Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh...
[pandora-kernel.git] / fs / xfs / xfs_extfree_item.c
index a55e687..75f2ef6 100644 (file)
@@ -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;
 
@@ -99,10 +122,12 @@ xfs_efi_item_pin(
 }
 
 /*
- * While EFIs cannot really be pinned, the unpin operation is the
- * last place at which the EFI is manipulated during a transaction.
- * Here we coordinate with xfs_efi_cancel() to determine who gets to
- * free the EFI.
+ * While EFIs cannot really be pinned, the unpin operation is the last place at
+ * 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.  Otherwise coordinate with xfs_efi_release() (via
+ * XFS_EFI_COMMITTED) to determine who gets to free the EFI.
  */
 STATIC void
 xfs_efi_item_unpin(
@@ -110,20 +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 (efip->efi_flags & XFS_EFI_CANCELED) {
-               if (remove)
-                       xfs_trans_del_item(lip);
 
-               /* xfs_trans_ail_delete() drops the AIL lock. */
-               xfs_trans_ail_delete(ailp, lip);
+       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;
-               spin_unlock(&ailp->xa_lock);
+               return;
        }
+       __xfs_efi_release(efip);
 }
 
 /*
@@ -152,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;
 }
 
@@ -230,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;
 }
@@ -289,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)