|
@@ -47,6 +47,28 @@ xfs_efi_item_free(
|
|
|
kmem_zone_free(xfs_efi_zone, efip);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Freeing the efi requires that we remove it from the AIL if it has already
|
|
|
+ * been placed there. However, the EFI may not yet have been placed in the AIL
|
|
|
+ * when called by xfs_efi_release() from EFD processing due to the ordering of
|
|
|
+ * committed vs unpin operations in bulk insert operations. Hence the
|
|
|
+ * test_and_clear_bit(XFS_EFI_COMMITTED) to ensure only the last caller frees
|
|
|
+ * the EFI.
|
|
|
+ */
|
|
|
+STATIC void
|
|
|
+__xfs_efi_release(
|
|
|
+ struct xfs_efi_log_item *efip)
|
|
|
+{
|
|
|
+ struct xfs_ail *ailp = efip->efi_item.li_ailp;
|
|
|
+
|
|
|
+ if (!test_and_clear_bit(XFS_EFI_COMMITTED, &efip->efi_flags)) {
|
|
|
+ spin_lock(&ailp->xa_lock);
|
|
|
+ /* xfs_trans_ail_delete() drops the AIL lock. */
|
|
|
+ xfs_trans_ail_delete(ailp, &efip->efi_item);
|
|
|
+ xfs_efi_item_free(efip);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* This returns the number of iovecs needed to log the given efi item.
|
|
|
* We only need 1 iovec for an efi item. It just logs the efi_log_format
|
|
@@ -74,7 +96,8 @@ xfs_efi_item_format(
|
|
|
struct xfs_efi_log_item *efip = EFI_ITEM(lip);
|
|
|
uint size;
|
|
|
|
|
|
- ASSERT(efip->efi_next_extent == efip->efi_format.efi_nextents);
|
|
|
+ ASSERT(atomic_read(&efip->efi_next_extent) ==
|
|
|
+ efip->efi_format.efi_nextents);
|
|
|
|
|
|
efip->efi_format.efi_type = XFS_LI_EFI;
|
|
|
|
|
@@ -103,7 +126,8 @@ xfs_efi_item_pin(
|
|
|
* which the EFI is manipulated during a transaction. If we are being asked to
|
|
|
* remove the EFI it's because the transaction has been cancelled and by
|
|
|
* definition that means the EFI cannot be in the AIL so remove it from the
|
|
|
- * transaction and free it.
|
|
|
+ * transaction and free it. Otherwise coordinate with xfs_efi_release() (via
|
|
|
+ * XFS_EFI_COMMITTED) to determine who gets to free the EFI.
|
|
|
*/
|
|
|
STATIC void
|
|
|
xfs_efi_item_unpin(
|
|
@@ -111,17 +135,14 @@ xfs_efi_item_unpin(
|
|
|
int remove)
|
|
|
{
|
|
|
struct xfs_efi_log_item *efip = EFI_ITEM(lip);
|
|
|
- struct xfs_ail *ailp = lip->li_ailp;
|
|
|
|
|
|
- spin_lock(&ailp->xa_lock);
|
|
|
if (remove) {
|
|
|
ASSERT(!(lip->li_flags & XFS_LI_IN_AIL));
|
|
|
xfs_trans_del_item(lip);
|
|
|
xfs_efi_item_free(efip);
|
|
|
- } else {
|
|
|
- efip->efi_flags |= XFS_EFI_COMMITTED;
|
|
|
+ return;
|
|
|
}
|
|
|
- spin_unlock(&ailp->xa_lock);
|
|
|
+ __xfs_efi_release(efip);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -150,16 +171,20 @@ xfs_efi_item_unlock(
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * The EFI is logged only once and cannot be moved in the log, so
|
|
|
- * simply return the lsn at which it's been logged. The canceled
|
|
|
- * flag is not paid any attention here. Checking for that is delayed
|
|
|
- * until the EFI is unpinned.
|
|
|
+ * The EFI is logged only once and cannot be moved in the log, so simply return
|
|
|
+ * the lsn at which it's been logged. For bulk transaction committed
|
|
|
+ * processing, the EFI may be processed but not yet unpinned prior to the EFD
|
|
|
+ * being processed. Set the XFS_EFI_COMMITTED flag so this case can be detected
|
|
|
+ * when processing the EFD.
|
|
|
*/
|
|
|
STATIC xfs_lsn_t
|
|
|
xfs_efi_item_committed(
|
|
|
struct xfs_log_item *lip,
|
|
|
xfs_lsn_t lsn)
|
|
|
{
|
|
|
+ struct xfs_efi_log_item *efip = EFI_ITEM(lip);
|
|
|
+
|
|
|
+ set_bit(XFS_EFI_COMMITTED, &efip->efi_flags);
|
|
|
return lsn;
|
|
|
}
|
|
|
|
|
@@ -228,6 +253,7 @@ xfs_efi_init(
|
|
|
xfs_log_item_init(mp, &efip->efi_item, XFS_LI_EFI, &xfs_efi_item_ops);
|
|
|
efip->efi_format.efi_nextents = nextents;
|
|
|
efip->efi_format.efi_id = (__psint_t)(void*)efip;
|
|
|
+ atomic_set(&efip->efi_next_extent, 0);
|
|
|
|
|
|
return efip;
|
|
|
}
|
|
@@ -287,37 +313,18 @@ xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt)
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * This is called by the efd item code below to release references to
|
|
|
- * the given efi item. Each efd calls this with the number of
|
|
|
- * extents that it has logged, and when the sum of these reaches
|
|
|
- * the total number of extents logged by this efi item we can free
|
|
|
- * the efi item.
|
|
|
- *
|
|
|
- * Freeing the efi item requires that we remove it from the AIL.
|
|
|
- * We'll use the AIL lock to protect our counters as well as
|
|
|
- * the removal from the AIL.
|
|
|
+ * This is called by the efd item code below to release references to the given
|
|
|
+ * efi item. Each efd calls this with the number of extents that it has
|
|
|
+ * logged, and when the sum of these reaches the total number of extents logged
|
|
|
+ * by this efi item we can free the efi item.
|
|
|
*/
|
|
|
void
|
|
|
xfs_efi_release(xfs_efi_log_item_t *efip,
|
|
|
uint nextents)
|
|
|
{
|
|
|
- struct xfs_ail *ailp = efip->efi_item.li_ailp;
|
|
|
- int extents_left;
|
|
|
-
|
|
|
- ASSERT(efip->efi_next_extent > 0);
|
|
|
- ASSERT(efip->efi_flags & XFS_EFI_COMMITTED);
|
|
|
-
|
|
|
- spin_lock(&ailp->xa_lock);
|
|
|
- ASSERT(efip->efi_next_extent >= nextents);
|
|
|
- efip->efi_next_extent -= nextents;
|
|
|
- extents_left = efip->efi_next_extent;
|
|
|
- if (extents_left == 0) {
|
|
|
- /* xfs_trans_ail_delete() drops the AIL lock. */
|
|
|
- xfs_trans_ail_delete(ailp, (xfs_log_item_t *)efip);
|
|
|
- xfs_efi_item_free(efip);
|
|
|
- } else {
|
|
|
- spin_unlock(&ailp->xa_lock);
|
|
|
- }
|
|
|
+ ASSERT(atomic_read(&efip->efi_next_extent) >= nextents);
|
|
|
+ if (atomic_sub_and_test(nextents, &efip->efi_next_extent))
|
|
|
+ __xfs_efi_release(efip);
|
|
|
}
|
|
|
|
|
|
static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip)
|