xfs: Ensure we force all busy extents in range to disk
[safe/jmp/linux-2.6] / fs / xfs / xfs_trans_ail.c
index 286934d..2ffc570 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2008 Dave Chinner
  * All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or
@@ -56,14 +57,14 @@ xfs_trans_ail_tail(
        xfs_lsn_t       lsn;
        xfs_log_item_t  *lip;
 
-       spin_lock(&ailp->xa_mount->m_ail_lock);
+       spin_lock(&ailp->xa_lock);
        lip = xfs_ail_min(ailp);
        if (lip == NULL) {
                lsn = (xfs_lsn_t)0;
        } else {
                lsn = lip->li_lsn;
        }
-       spin_unlock(&ailp->xa_mount->m_ail_lock);
+       spin_unlock(&ailp->xa_lock);
 
        return lsn;
 }
@@ -78,23 +79,23 @@ xfs_trans_ail_tail(
  * the push is run asynchronously in a separate thread, so we return the tail
  * of the log right now instead of the tail after the push. This means we will
  * either continue right away, or we will sleep waiting on the async thread to
- * do it's work.
+ * do its work.
  *
  * We do this unlocked - we only need to know whether there is anything in the
  * AIL at the time we are called. We don't need to access the contents of
  * any of the objects, so the lock is not needed.
  */
 void
-xfs_trans_push_ail(
-       xfs_mount_t             *mp,
-       xfs_lsn_t               threshold_lsn)
+xfs_trans_ail_push(
+       struct xfs_ail  *ailp,
+       xfs_lsn_t       threshold_lsn)
 {
-       xfs_log_item_t          *lip;
+       xfs_log_item_t  *lip;
 
-       lip = xfs_ail_min(mp->m_ail);
-       if (lip && !XFS_FORCED_SHUTDOWN(mp)) {
-               if (XFS_LSN_CMP(threshold_lsn, mp->m_ail->xa_target) > 0)
-                       xfsaild_wakeup(mp->m_ail, threshold_lsn);
+       lip = xfs_ail_min(ailp);
+       if (lip && !XFS_FORCED_SHUTDOWN(ailp->xa_mount)) {
+               if (XFS_LSN_CMP(threshold_lsn, ailp->xa_target) > 0)
+                       xfsaild_wakeup(ailp, threshold_lsn);
        }
 }
 
@@ -159,7 +160,7 @@ xfs_trans_ail_cursor_next(
 /*
  * Now that the traversal is complete, we need to remove the cursor
  * from the list of traversing cursors. Avoid removing the embedded
- * push cursor, but use the fact it is alway present to make the
+ * push cursor, but use the fact it is always present to make the
  * list deletion simple.
  */
 void
@@ -227,7 +228,7 @@ xfs_trans_ail_cursor_first(
 
        list_for_each_entry(lip, &ailp->xa_ail, li_ail) {
                if (XFS_LSN_CMP(lip->li_lsn, lsn) >= 0)
-                       break;
+                       goto out;
        }
        lip = NULL;
 out:
@@ -252,7 +253,7 @@ xfsaild_push(
        xfs_mount_t     *mp = ailp->xa_mount;
        struct xfs_ail_cursor   *cur = &ailp->xa_cursors;
 
-       spin_lock(&mp->m_ail_lock);
+       spin_lock(&ailp->xa_lock);
        xfs_trans_ail_cursor_init(ailp, cur);
        lip = xfs_trans_ail_cursor_first(ailp, cur, *last_lsn);
        if (!lip || XFS_FORCED_SHUTDOWN(mp)) {
@@ -260,7 +261,7 @@ xfsaild_push(
                 * AIL is empty or our push has reached the end.
                 */
                xfs_trans_ail_cursor_done(ailp, cur);
-               spin_unlock(&mp->m_ail_lock);
+               spin_unlock(&ailp->xa_lock);
                last_pushed_lsn = 0;
                return tout;
        }
@@ -295,7 +296,7 @@ xfsaild_push(
                 * skip to the next item in the list.
                 */
                lock_result = IOP_TRYLOCK(lip);
-               spin_unlock(&mp->m_ail_lock);
+               spin_unlock(&ailp->xa_lock);
                switch (lock_result) {
                case XFS_ITEM_SUCCESS:
                        XFS_STATS_INC(xs_push_ail_success);
@@ -332,7 +333,7 @@ xfsaild_push(
                        break;
                }
 
-               spin_lock(&mp->m_ail_lock);
+               spin_lock(&ailp->xa_lock);
                /* should we bother continuing? */
                if (XFS_FORCED_SHUTDOWN(mp))
                        break;
@@ -361,7 +362,7 @@ xfsaild_push(
                lsn = lip->li_lsn;
        }
        xfs_trans_ail_cursor_done(ailp, cur);
-       spin_unlock(&mp->m_ail_lock);
+       spin_unlock(&ailp->xa_lock);
 
        if (flush_log) {
                /*
@@ -411,7 +412,7 @@ xfsaild_push(
  */
 void
 xfs_trans_unlocked_item(
-       xfs_mount_t     *mp,
+       struct xfs_ail  *ailp,
        xfs_log_item_t  *lip)
 {
        xfs_log_item_t  *min_lip;
@@ -423,7 +424,7 @@ xfs_trans_unlocked_item(
         * over some potentially valid data.
         */
        if (!(lip->li_flags & XFS_LI_IN_AIL) ||
-           XFS_FORCED_SHUTDOWN(mp)) {
+           XFS_FORCED_SHUTDOWN(ailp->xa_mount)) {
                return;
        }
 
@@ -439,10 +440,10 @@ xfs_trans_unlocked_item(
         * the call to xfs_log_move_tail() doesn't do anything if there's
         * not enough free space to wake people up so we're safe calling it.
         */
-       min_lip = xfs_ail_min(mp->m_ail);
+       min_lip = xfs_ail_min(ailp);
 
        if (min_lip == lip)
-               xfs_log_move_tail(mp, 1);
+               xfs_log_move_tail(ailp->xa_mount, 1);
 }      /* xfs_trans_unlocked_item */
 
 
@@ -459,33 +460,42 @@ xfs_trans_unlocked_item(
  * is dropped before returning.
  */
 void
-xfs_trans_update_ail(
-       xfs_mount_t     *mp,
+xfs_trans_ail_update(
+       struct xfs_ail  *ailp,
        xfs_log_item_t  *lip,
-       xfs_lsn_t       lsn) __releases(mp->m_ail_lock)
+       xfs_lsn_t       lsn) __releases(ailp->xa_lock)
 {
-       xfs_log_item_t          *dlip=NULL;
+       xfs_log_item_t          *dlip = NULL;
        xfs_log_item_t          *mlip;  /* ptr to minimum lip */
+       xfs_lsn_t               tail_lsn;
 
-       mlip = xfs_ail_min(mp->m_ail);
+       mlip = xfs_ail_min(ailp);
 
        if (lip->li_flags & XFS_LI_IN_AIL) {
-               dlip = xfs_ail_delete(mp->m_ail, lip);
+               dlip = xfs_ail_delete(ailp, lip);
                ASSERT(dlip == lip);
-               xfs_trans_ail_cursor_clear(mp->m_ail, dlip);
+               xfs_trans_ail_cursor_clear(ailp, dlip);
        } else {
                lip->li_flags |= XFS_LI_IN_AIL;
        }
 
        lip->li_lsn = lsn;
-       xfs_ail_insert(mp->m_ail, lip);
+       xfs_ail_insert(ailp, lip);
 
        if (mlip == dlip) {
-               mlip = xfs_ail_min(mp->m_ail);
-               spin_unlock(&mp->m_ail_lock);
-               xfs_log_move_tail(mp, mlip->li_lsn);
+               mlip = xfs_ail_min(ailp);
+               /*
+                * It is not safe to access mlip after the AIL lock is
+                * dropped, so we must get a copy of li_lsn before we do
+                * so.  This is especially important on 32-bit platforms
+                * where accessing and updating 64-bit values like li_lsn
+                * is not atomic.
+                */
+               tail_lsn = mlip->li_lsn;
+               spin_unlock(&ailp->xa_lock);
+               xfs_log_move_tail(ailp->xa_mount, tail_lsn);
        } else {
-               spin_unlock(&mp->m_ail_lock);
+               spin_unlock(&ailp->xa_lock);
        }
 
 
@@ -507,29 +517,38 @@ xfs_trans_update_ail(
  * is dropped before returning.
  */
 void
-xfs_trans_delete_ail(
-       xfs_mount_t     *mp,
-       xfs_log_item_t  *lip) __releases(mp->m_ail_lock)
+xfs_trans_ail_delete(
+       struct xfs_ail  *ailp,
+       xfs_log_item_t  *lip) __releases(ailp->xa_lock)
 {
        xfs_log_item_t          *dlip;
        xfs_log_item_t          *mlip;
+       xfs_lsn_t               tail_lsn;
 
        if (lip->li_flags & XFS_LI_IN_AIL) {
-               mlip = xfs_ail_min(mp->m_ail);
-               dlip = xfs_ail_delete(mp->m_ail, lip);
+               mlip = xfs_ail_min(ailp);
+               dlip = xfs_ail_delete(ailp, lip);
                ASSERT(dlip == lip);
-               xfs_trans_ail_cursor_clear(mp->m_ail, dlip);
+               xfs_trans_ail_cursor_clear(ailp, dlip);
 
 
                lip->li_flags &= ~XFS_LI_IN_AIL;
                lip->li_lsn = 0;
 
                if (mlip == dlip) {
-                       mlip = xfs_ail_min(mp->m_ail);
-                       spin_unlock(&mp->m_ail_lock);
-                       xfs_log_move_tail(mp, (mlip ? mlip->li_lsn : 0));
+                       mlip = xfs_ail_min(ailp);
+                       /*
+                        * It is not safe to access mlip after the AIL lock
+                        * is dropped, so we must get a copy of li_lsn
+                        * before we do so.  This is especially important
+                        * on 32-bit platforms where accessing and updating
+                        * 64-bit values like li_lsn is not atomic.
+                        */
+                       tail_lsn = mlip ? mlip->li_lsn : 0;
+                       spin_unlock(&ailp->xa_lock);
+                       xfs_log_move_tail(ailp->xa_mount, tail_lsn);
                } else {
-                       spin_unlock(&mp->m_ail_lock);
+                       spin_unlock(&ailp->xa_lock);
                }
        }
        else {
@@ -537,13 +556,13 @@ xfs_trans_delete_ail(
                 * If the file system is not being shutdown, we are in
                 * serious trouble if we get to this stage.
                 */
-               if (XFS_FORCED_SHUTDOWN(mp))
-                       spin_unlock(&mp->m_ail_lock);
-               else {
+               struct xfs_mount        *mp = ailp->xa_mount;
+
+               spin_unlock(&ailp->xa_lock);
+               if (!XFS_FORCED_SHUTDOWN(mp)) {
                        xfs_cmn_err(XFS_PTAG_AILDELETE, CE_ALERT, mp,
                "%s: attempting to delete a log item that is not in the AIL",
                                        __func__);
-                       spin_unlock(&mp->m_ail_lock);
                        xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
                }
        }
@@ -578,6 +597,7 @@ xfs_trans_ail_init(
 
        ailp->xa_mount = mp;
        INIT_LIST_HEAD(&ailp->xa_ail);
+       spin_lock_init(&ailp->xa_lock);
        error = xfsaild_start(ailp);
        if (error)
                goto out_free_ailp;