xfs: remove XFS_INO64_OFFSET
[safe/jmp/linux-2.6] / fs / xfs / xfs_trans_ail.c
index 5b2ff59..f31271c 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
 #include "xfs_trans_priv.h"
 #include "xfs_error.h"
 
-STATIC void xfs_ail_insert(xfs_ail_entry_t *, xfs_log_item_t *);
-STATIC xfs_log_item_t * xfs_ail_delete(xfs_ail_entry_t *, xfs_log_item_t *);
-STATIC xfs_log_item_t * xfs_ail_min(xfs_ail_entry_t *);
-STATIC xfs_log_item_t * xfs_ail_next(xfs_ail_entry_t *, xfs_log_item_t *);
+STATIC void xfs_ail_insert(struct xfs_ail *, xfs_log_item_t *);
+STATIC xfs_log_item_t * xfs_ail_delete(struct xfs_ail *, xfs_log_item_t *);
+STATIC xfs_log_item_t * xfs_ail_min(struct xfs_ail *);
+STATIC xfs_log_item_t * xfs_ail_next(struct xfs_ail *, xfs_log_item_t *);
 
 #ifdef DEBUG
-STATIC void xfs_ail_check(xfs_ail_entry_t *);
+STATIC void xfs_ail_check(struct xfs_ail *, xfs_log_item_t *);
 #else
-#define        xfs_ail_check(a)
+#define        xfs_ail_check(a,l)
 #endif /* DEBUG */
 
 
@@ -50,21 +51,20 @@ STATIC void xfs_ail_check(xfs_ail_entry_t *);
  * lsn of the last item in the AIL.
  */
 xfs_lsn_t
-xfs_trans_tail_ail(
-       xfs_mount_t     *mp)
+xfs_trans_ail_tail(
+       struct xfs_ail  *ailp)
 {
        xfs_lsn_t       lsn;
        xfs_log_item_t  *lip;
-       SPLDECL(s);
 
-       AIL_LOCK(mp,s);
-       lip = xfs_ail_min(&(mp->m_ail));
+       spin_lock(&ailp->xa_lock);
+       lip = xfs_ail_min(ailp);
        if (lip == NULL) {
                lsn = (xfs_lsn_t)0;
        } else {
                lsn = lip->li_lsn;
        }
-       AIL_UNLOCK(mp, s);
+       spin_unlock(&ailp->xa_lock);
 
        return lsn;
 }
@@ -72,120 +72,297 @@ xfs_trans_tail_ail(
 /*
  * xfs_trans_push_ail
  *
- * This routine is called to move the tail of the AIL
- * forward.  It does this by trying to flush items in the AIL
- * whose lsns are below the given threshold_lsn.
+ * This routine is called to move the tail of the AIL forward.  It does this by
+ * trying to flush items in the AIL whose lsns are below the given
+ * threshold_lsn.
+ *
+ * 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 its work.
  *
- * The routine returns the lsn of the tail of the log.
+ * 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.
  */
-xfs_lsn_t
-xfs_trans_push_ail(
-       xfs_mount_t             *mp,
-       xfs_lsn_t               threshold_lsn)
+void
+xfs_trans_ail_push(
+       struct xfs_ail  *ailp,
+       xfs_lsn_t       threshold_lsn)
+{
+       xfs_log_item_t  *lip;
+
+       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);
+       }
+}
+
+/*
+ * AIL traversal cursor initialisation.
+ *
+ * The cursor keeps track of where our current traversal is up
+ * to by tracking the next ƣtem in the list for us. However, for
+ * this to be safe, removing an object from the AIL needs to invalidate
+ * any cursor that points to it. hence the traversal cursor needs to
+ * be linked to the struct xfs_ail so that deletion can search all the
+ * active cursors for invalidation.
+ *
+ * We don't link the push cursor because it is embedded in the struct
+ * xfs_ail and hence easily findable.
+ */
+STATIC void
+xfs_trans_ail_cursor_init(
+       struct xfs_ail          *ailp,
+       struct xfs_ail_cursor   *cur)
+{
+       cur->item = NULL;
+       if (cur == &ailp->xa_cursors)
+               return;
+
+       cur->next = ailp->xa_cursors.next;
+       ailp->xa_cursors.next = cur;
+}
+
+/*
+ * Set the cursor to the next item, because when we look
+ * up the cursor the current item may have been freed.
+ */
+STATIC void
+xfs_trans_ail_cursor_set(
+       struct xfs_ail          *ailp,
+       struct xfs_ail_cursor   *cur,
+       struct xfs_log_item     *lip)
+{
+       if (lip)
+               cur->item = xfs_ail_next(ailp, lip);
+}
+
+/*
+ * Get the next item in the traversal and advance the cursor.
+ * If the cursor was invalidated (inidicated by a lip of 1),
+ * restart the traversal.
+ */
+struct xfs_log_item *
+xfs_trans_ail_cursor_next(
+       struct xfs_ail          *ailp,
+       struct xfs_ail_cursor   *cur)
+{
+       struct xfs_log_item     *lip = cur->item;
+
+       if ((__psint_t)lip & 1)
+               lip = xfs_ail_min(ailp);
+       xfs_trans_ail_cursor_set(ailp, cur, lip);
+       return lip;
+}
+
+/*
+ * 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 always present to make the
+ * list deletion simple.
+ */
+void
+xfs_trans_ail_cursor_done(
+       struct xfs_ail          *ailp,
+       struct xfs_ail_cursor   *done)
+{
+       struct xfs_ail_cursor   *prev = NULL;
+       struct xfs_ail_cursor   *cur;
+
+       done->item = NULL;
+       if (done == &ailp->xa_cursors)
+               return;
+       prev = &ailp->xa_cursors;
+       for (cur = prev->next; cur; prev = cur, cur = prev->next) {
+               if (cur == done) {
+                       prev->next = cur->next;
+                       break;
+               }
+       }
+       ASSERT(cur);
+}
+
+/*
+ * Invalidate any cursor that is pointing to this item. This is
+ * called when an item is removed from the AIL. Any cursor pointing
+ * to this object is now invalid and the traversal needs to be
+ * terminated so it doesn't reference a freed object. We set the
+ * cursor item to a value of 1 so we can distinguish between an
+ * invalidation and the end of the list when getting the next item
+ * from the cursor.
+ */
+STATIC void
+xfs_trans_ail_cursor_clear(
+       struct xfs_ail          *ailp,
+       struct xfs_log_item     *lip)
+{
+       struct xfs_ail_cursor   *cur;
+
+       /* need to search all cursors */
+       for (cur = &ailp->xa_cursors; cur; cur = cur->next) {
+               if (cur->item == lip)
+                       cur->item = (struct xfs_log_item *)
+                                       ((__psint_t)cur->item | 1);
+       }
+}
+
+/*
+ * Return the item in the AIL with the current lsn.
+ * Return the current tree generation number for use
+ * in calls to xfs_trans_next_ail().
+ */
+xfs_log_item_t *
+xfs_trans_ail_cursor_first(
+       struct xfs_ail          *ailp,
+       struct xfs_ail_cursor   *cur,
+       xfs_lsn_t               lsn)
 {
-       xfs_lsn_t               lsn;
        xfs_log_item_t          *lip;
-       int                     gen;
-       int                     restarts;
-       int                     lock_result;
-       int                     flush_log;
-       SPLDECL(s);
 
-#define        XFS_TRANS_PUSH_AIL_RESTARTS     1000
+       xfs_trans_ail_cursor_init(ailp, cur);
+       lip = xfs_ail_min(ailp);
+       if (lsn == 0)
+               goto out;
 
-       AIL_LOCK(mp,s);
-       lip = xfs_trans_first_ail(mp, &gen);
-       if (lip == NULL || XFS_FORCED_SHUTDOWN(mp)) {
+       list_for_each_entry(lip, &ailp->xa_ail, li_ail) {
+               if (XFS_LSN_CMP(lip->li_lsn, lsn) >= 0)
+                       goto out;
+       }
+       lip = NULL;
+out:
+       xfs_trans_ail_cursor_set(ailp, cur, lip);
+       return lip;
+}
+
+/*
+ * Function that does the work of pushing on the AIL
+ */
+long
+xfsaild_push(
+       struct xfs_ail  *ailp,
+       xfs_lsn_t       *last_lsn)
+{
+       long            tout = 1000; /* milliseconds */
+       xfs_lsn_t       last_pushed_lsn = *last_lsn;
+       xfs_lsn_t       target =  ailp->xa_target;
+       xfs_lsn_t       lsn;
+       xfs_log_item_t  *lip;
+       int             flush_log, count, stuck;
+       xfs_mount_t     *mp = ailp->xa_mount;
+       struct xfs_ail_cursor   *cur = &ailp->xa_cursors;
+
+       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)) {
                /*
-                * Just return if the AIL is empty.
+                * AIL is empty or our push has reached the end.
                 */
-               AIL_UNLOCK(mp, s);
-               return (xfs_lsn_t)0;
+               xfs_trans_ail_cursor_done(ailp, cur);
+               spin_unlock(&ailp->xa_lock);
+               last_pushed_lsn = 0;
+               return tout;
        }
 
        XFS_STATS_INC(xs_push_ail);
 
        /*
         * While the item we are looking at is below the given threshold
-        * try to flush it out.  Make sure to limit the number of times
-        * we allow xfs_trans_next_ail() to restart scanning from the
-        * beginning of the list.  We'd like not to stop until we've at least
+        * try to flush it out. We'd like not to stop until we've at least
         * tried to push on everything in the AIL with an LSN less than
-        * the given threshold. However, we may give up before that if
-        * we realize that we've been holding the AIL_LOCK for 'too long',
-        * blocking interrupts. Currently, too long is < 500us roughly.
+        * the given threshold.
+        *
+        * However, we will stop after a certain number of pushes and wait
+        * for a reduced timeout to fire before pushing further. This
+        * prevents use from spinning when we can't do anything or there is
+        * lots of contention on the AIL lists.
         */
-       flush_log = 0;
-       restarts = 0;
-       while (((restarts < XFS_TRANS_PUSH_AIL_RESTARTS) &&
-               (XFS_LSN_CMP(lip->li_lsn, threshold_lsn) < 0))) {
+       tout = 10;
+       lsn = lip->li_lsn;
+       flush_log = stuck = count = 0;
+       while ((XFS_LSN_CMP(lip->li_lsn, target) < 0)) {
+               int     lock_result;
                /*
-                * If we can lock the item without sleeping, unlock
-                * the AIL lock and flush the item.  Then re-grab the
-                * AIL lock so we can look for the next item on the
-                * AIL.  Since we unlock the AIL while we flush the
-                * item, the next routine may start over again at the
-                * the beginning of the list if anything has changed.
-                * That is what the generation count is for.
+                * If we can lock the item without sleeping, unlock the AIL
+                * lock and flush the item.  Then re-grab the AIL lock so we
+                * can look for the next item on the AIL. List changes are
+                * handled by the AIL lookup functions internally
                 *
-                * If we can't lock the item, either its holder will flush
-                * it or it is already being flushed or it is being relogged.
-                * In any of these case it is being taken care of and we
-                * can just skip to the next item in the list.
+                * If we can't lock the item, either its holder will flush it
+                * or it is already being flushed or it is being relogged.  In
+                * any of these case it is being taken care of and we can just
+                * skip to the next item in the list.
                 */
                lock_result = IOP_TRYLOCK(lip);
+               spin_unlock(&ailp->xa_lock);
                switch (lock_result) {
-                     case XFS_ITEM_SUCCESS:
-                       AIL_UNLOCK(mp, s);
+               case XFS_ITEM_SUCCESS:
                        XFS_STATS_INC(xs_push_ail_success);
                        IOP_PUSH(lip);
-                       AIL_LOCK(mp,s);
+                       last_pushed_lsn = lsn;
                        break;
 
-                     case XFS_ITEM_PUSHBUF:
-                       AIL_UNLOCK(mp, s);
+               case XFS_ITEM_PUSHBUF:
                        XFS_STATS_INC(xs_push_ail_pushbuf);
-#ifdef XFSRACEDEBUG
-                       delay_for_intr();
-                       delay(300);
-#endif
-                       ASSERT(lip->li_ops->iop_pushbuf);
-                       ASSERT(lip);
                        IOP_PUSHBUF(lip);
-                       AIL_LOCK(mp,s);
+                       last_pushed_lsn = lsn;
                        break;
 
-                     case XFS_ITEM_PINNED:
+               case XFS_ITEM_PINNED:
                        XFS_STATS_INC(xs_push_ail_pinned);
+                       stuck++;
                        flush_log = 1;
                        break;
 
-                     case XFS_ITEM_LOCKED:
+               case XFS_ITEM_LOCKED:
                        XFS_STATS_INC(xs_push_ail_locked);
+                       last_pushed_lsn = lsn;
+                       stuck++;
                        break;
 
-                     case XFS_ITEM_FLUSHING:
+               case XFS_ITEM_FLUSHING:
                        XFS_STATS_INC(xs_push_ail_flushing);
+                       last_pushed_lsn = lsn;
+                       stuck++;
                        break;
 
-                     default:
+               default:
                        ASSERT(0);
                        break;
                }
 
-               lip = xfs_trans_next_ail(mp, lip, &gen, &restarts);
-               if (lip == NULL) {
+               spin_lock(&ailp->xa_lock);
+               /* should we bother continuing? */
+               if (XFS_FORCED_SHUTDOWN(mp))
+                       break;
+               ASSERT(mp->m_log);
+
+               count++;
+
+               /*
+                * Are there too many items we can't do anything with?
+                * If we we are skipping too many items because we can't flush
+                * them or they are already being flushed, we back off and
+                * given them time to complete whatever operation is being
+                * done. i.e. remove pressure from the AIL while we can't make
+                * progress so traversals don't slow down further inserts and
+                * removals to/from the AIL.
+                *
+                * The value of 100 is an arbitrary magic number based on
+                * observation.
+                */
+               if (stuck > 100)
                        break;
-               }
-               if (XFS_FORCED_SHUTDOWN(mp)) {
-                       /*
-                        * Just return if we shut down during the last try.
-                        */
-                       AIL_UNLOCK(mp, s);
-                       return (xfs_lsn_t)0;
-               }
 
+               lip = xfs_trans_ail_cursor_next(ailp, cur);
+               if (lip == NULL)
+                       break;
+               lsn = lip->li_lsn;
        }
+       xfs_trans_ail_cursor_done(ailp, cur);
+       spin_unlock(&ailp->xa_lock);
 
        if (flush_log) {
                /*
@@ -193,22 +370,36 @@ xfs_trans_push_ail(
                 * push out the log so it will become unpinned and
                 * move forward in the AIL.
                 */
-               AIL_UNLOCK(mp, s);
                XFS_STATS_INC(xs_push_ail_flush);
                xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE);
-               AIL_LOCK(mp, s);
        }
 
-       lip = xfs_ail_min(&(mp->m_ail));
-       if (lip == NULL) {
-               lsn = (xfs_lsn_t)0;
-       } else {
-               lsn = lip->li_lsn;
+       if (!count) {
+               /* We're past our target or empty, so idle */
+               tout = 1000;
+       } else if (XFS_LSN_CMP(lsn, target) >= 0) {
+               /*
+                * We reached the target so wait a bit longer for I/O to
+                * complete and remove pushed items from the AIL before we
+                * start the next scan from the start of the AIL.
+                */
+               tout += 20;
+               last_pushed_lsn = 0;
+       } else if ((stuck * 100) / count > 90) {
+               /*
+                * Either there is a lot of contention on the AIL or we
+                * are stuck due to operations in progress. "Stuck" in this
+                * case is defined as >90% of the items we tried to push
+                * were stuck.
+                *
+                * Backoff a bit more to allow some I/O to complete before
+                * continuing from where we were.
+                */
+               tout += 10;
        }
-
-       AIL_UNLOCK(mp, s);
-       return lsn;
-}      /* xfs_trans_push_ail */
+       *last_lsn = last_pushed_lsn;
+       return tout;
+}      /* xfsaild_push */
 
 
 /*
@@ -221,7 +412,7 @@ xfs_trans_push_ail(
  */
 void
 xfs_trans_unlocked_item(
-       xfs_mount_t     *mp,
+       struct xfs_ail  *ailp,
        xfs_log_item_t  *lip)
 {
        xfs_log_item_t  *min_lip;
@@ -233,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;
        }
 
@@ -249,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 */
 
 
@@ -265,45 +456,37 @@ xfs_trans_unlocked_item(
  * we move in the AIL is the minimum one, update the tail lsn in the
  * log manager.
  *
- * Increment the AIL's generation count to indicate that the tree
- * has changed.
- *
  * This function must be called with the AIL lock held.  The lock
- * is dropped before returning, so the caller must pass in the
- * cookie returned by AIL_LOCK.
+ * 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,
-       unsigned long   s) __releases(mp->m_ail_lock)
+       xfs_lsn_t       lsn) __releases(ailp->xa_lock)
 {
-       xfs_ail_entry_t         *ailp;
-       xfs_log_item_t          *dlip=NULL;
+       xfs_log_item_t          *dlip = NULL;
        xfs_log_item_t          *mlip;  /* ptr to minimum lip */
 
-       ailp = &(mp->m_ail);
        mlip = xfs_ail_min(ailp);
 
        if (lip->li_flags & XFS_LI_IN_AIL) {
                dlip = xfs_ail_delete(ailp, lip);
                ASSERT(dlip == lip);
+               xfs_trans_ail_cursor_clear(ailp, dlip);
        } else {
                lip->li_flags |= XFS_LI_IN_AIL;
        }
 
        lip->li_lsn = lsn;
-
        xfs_ail_insert(ailp, lip);
-       mp->m_ail_gen++;
 
        if (mlip == dlip) {
-               mlip = xfs_ail_min(&(mp->m_ail));
-               AIL_UNLOCK(mp, s);
-               xfs_log_move_tail(mp, mlip->li_lsn);
+               mlip = xfs_ail_min(ailp);
+               spin_unlock(&ailp->xa_lock);
+               xfs_log_move_tail(ailp->xa_mount, mlip->li_lsn);
        } else {
-               AIL_UNLOCK(mp, s);
+               spin_unlock(&ailp->xa_lock);
        }
 
 
@@ -322,36 +505,33 @@ xfs_trans_update_ail(
  * has changed.
  *
  * This function must be called with the AIL lock held.  The lock
- * is dropped before returning, so the caller must pass in the
- * cookie returned by AIL_LOCK.
+ * is dropped before returning.
  */
 void
-xfs_trans_delete_ail(
-       xfs_mount_t     *mp,
-       xfs_log_item_t  *lip,
-       unsigned long   s) __releases(mp->m_ail_lock)
+xfs_trans_ail_delete(
+       struct xfs_ail  *ailp,
+       xfs_log_item_t  *lip) __releases(ailp->xa_lock)
 {
-       xfs_ail_entry_t         *ailp;
        xfs_log_item_t          *dlip;
        xfs_log_item_t          *mlip;
 
        if (lip->li_flags & XFS_LI_IN_AIL) {
-               ailp = &(mp->m_ail);
                mlip = xfs_ail_min(ailp);
                dlip = xfs_ail_delete(ailp, lip);
                ASSERT(dlip == lip);
+               xfs_trans_ail_cursor_clear(ailp, dlip);
 
 
                lip->li_flags &= ~XFS_LI_IN_AIL;
                lip->li_lsn = 0;
-               mp->m_ail_gen++;
 
                if (mlip == dlip) {
-                       mlip = xfs_ail_min(&(mp->m_ail));
-                       AIL_UNLOCK(mp, s);
-                       xfs_log_move_tail(mp, (mlip ? mlip->li_lsn : 0));
+                       mlip = xfs_ail_min(ailp);
+                       spin_unlock(&ailp->xa_lock);
+                       xfs_log_move_tail(ailp->xa_mount,
+                                               (mlip ? mlip->li_lsn : 0));
                } else {
-                       AIL_UNLOCK(mp, s);
+                       spin_unlock(&ailp->xa_lock);
                }
        }
        else {
@@ -359,13 +539,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))
-                       AIL_UNLOCK(mp, s);
-               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",
-                                       __FUNCTION__);
-                       AIL_UNLOCK(mp, s);
+                                       __func__);
                        xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
                }
        }
@@ -374,56 +554,6 @@ xfs_trans_delete_ail(
 
 
 /*
- * Return the item in the AIL with the smallest lsn.
- * Return the current tree generation number for use
- * in calls to xfs_trans_next_ail().
- */
-xfs_log_item_t *
-xfs_trans_first_ail(
-       xfs_mount_t     *mp,
-       int             *gen)
-{
-       xfs_log_item_t  *lip;
-
-       lip = xfs_ail_min(&(mp->m_ail));
-       *gen = (int)mp->m_ail_gen;
-
-       return (lip);
-}
-
-/*
- * If the generation count of the tree has not changed since the
- * caller last took something from the AIL, then return the elmt
- * in the tree which follows the one given.  If the count has changed,
- * then return the minimum elmt of the AIL and bump the restarts counter
- * if one is given.
- */
-xfs_log_item_t *
-xfs_trans_next_ail(
-       xfs_mount_t     *mp,
-       xfs_log_item_t  *lip,
-       int             *gen,
-       int             *restarts)
-{
-       xfs_log_item_t  *nlip;
-
-       ASSERT(mp && lip && gen);
-       if (mp->m_ail_gen == *gen) {
-               nlip = xfs_ail_next(&(mp->m_ail), lip);
-       } else {
-               nlip = xfs_ail_min(&(mp->m_ail));
-               *gen = (int)mp->m_ail_gen;
-               if (restarts != NULL) {
-                       XFS_STATS_INC(xs_push_ail_restarts);
-                       (*restarts)++;
-               }
-       }
-
-       return (nlip);
-}
-
-
-/*
  * The active item list (AIL) is a doubly linked list of log
  * items sorted by ascending lsn.  The base of the list is
  * a forw/back pointer pair embedded in the xfs mount structure.
@@ -437,12 +567,39 @@ xfs_trans_next_ail(
 /*
  * Initialize the doubly linked list to point only to itself.
  */
-void
+int
 xfs_trans_ail_init(
        xfs_mount_t     *mp)
 {
-       mp->m_ail.ail_forw = (xfs_log_item_t*)&(mp->m_ail);
-       mp->m_ail.ail_back = (xfs_log_item_t*)&(mp->m_ail);
+       struct xfs_ail  *ailp;
+       int             error;
+
+       ailp = kmem_zalloc(sizeof(struct xfs_ail), KM_MAYFAIL);
+       if (!ailp)
+               return ENOMEM;
+
+       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;
+       mp->m_ail = ailp;
+       return 0;
+
+out_free_ailp:
+       kmem_free(ailp);
+       return error;
+}
+
+void
+xfs_trans_ail_destroy(
+       xfs_mount_t     *mp)
+{
+       struct xfs_ail  *ailp = mp->m_ail;
+
+       xfsaild_stop(ailp);
+       kmem_free(ailp);
 }
 
 /*
@@ -453,7 +610,7 @@ xfs_trans_ail_init(
  */
 STATIC void
 xfs_ail_insert(
-       xfs_ail_entry_t *base,
+       struct xfs_ail  *ailp,
        xfs_log_item_t  *lip)
 /* ARGSUSED */
 {
@@ -462,27 +619,22 @@ xfs_ail_insert(
        /*
         * If the list is empty, just insert the item.
         */
-       if (base->ail_back == (xfs_log_item_t*)base) {
-               base->ail_forw = lip;
-               base->ail_back = lip;
-               lip->li_ail.ail_forw = (xfs_log_item_t*)base;
-               lip->li_ail.ail_back = (xfs_log_item_t*)base;
+       if (list_empty(&ailp->xa_ail)) {
+               list_add(&lip->li_ail, &ailp->xa_ail);
                return;
        }
 
-       next_lip = base->ail_back;
-       while ((next_lip != (xfs_log_item_t*)base) &&
-              (XFS_LSN_CMP(next_lip->li_lsn, lip->li_lsn) > 0)) {
-               next_lip = next_lip->li_ail.ail_back;
+       list_for_each_entry_reverse(next_lip, &ailp->xa_ail, li_ail) {
+               if (XFS_LSN_CMP(next_lip->li_lsn, lip->li_lsn) <= 0)
+                       break;
        }
-       ASSERT((next_lip == (xfs_log_item_t*)base) ||
+
+       ASSERT((&next_lip->li_ail == &ailp->xa_ail) ||
               (XFS_LSN_CMP(next_lip->li_lsn, lip->li_lsn) <= 0));
-       lip->li_ail.ail_forw = next_lip->li_ail.ail_forw;
-       lip->li_ail.ail_back = next_lip;
-       next_lip->li_ail.ail_forw = lip;
-       lip->li_ail.ail_forw->li_ail.ail_back = lip;
 
-       xfs_ail_check(base);
+       list_add(&lip->li_ail, &next_lip->li_ail);
+
+       xfs_ail_check(ailp, lip);
        return;
 }
 
@@ -492,16 +644,14 @@ xfs_ail_insert(
 /*ARGSUSED*/
 STATIC xfs_log_item_t *
 xfs_ail_delete(
-       xfs_ail_entry_t *base,
+       struct xfs_ail  *ailp,
        xfs_log_item_t  *lip)
 /* ARGSUSED */
 {
-       lip->li_ail.ail_forw->li_ail.ail_back = lip->li_ail.ail_back;
-       lip->li_ail.ail_back->li_ail.ail_forw = lip->li_ail.ail_forw;
-       lip->li_ail.ail_forw = NULL;
-       lip->li_ail.ail_back = NULL;
+       xfs_ail_check(ailp, lip);
+
+       list_del(&lip->li_ail);
 
-       xfs_ail_check(base);
        return lip;
 }
 
@@ -511,14 +661,13 @@ xfs_ail_delete(
  */
 STATIC xfs_log_item_t *
 xfs_ail_min(
-       xfs_ail_entry_t *base)
+       struct xfs_ail  *ailp)
 /* ARGSUSED */
 {
-       register xfs_log_item_t *forw = base->ail_forw;
-       if (forw == (xfs_log_item_t*)base) {
+       if (list_empty(&ailp->xa_ail))
                return NULL;
-       }
-       return forw;
+
+       return list_first_entry(&ailp->xa_ail, xfs_log_item_t, li_ail);
 }
 
 /*
@@ -528,15 +677,14 @@ xfs_ail_min(
  */
 STATIC xfs_log_item_t *
 xfs_ail_next(
-       xfs_ail_entry_t *base,
+       struct xfs_ail  *ailp,
        xfs_log_item_t  *lip)
 /* ARGSUSED */
 {
-       if (lip->li_ail.ail_forw == (xfs_log_item_t*)base) {
+       if (lip->li_ail.next == &ailp->xa_ail)
                return NULL;
-       }
-       return lip->li_ail.ail_forw;
 
+       return list_first_entry(&lip->li_ail, xfs_log_item_t, li_ail);
 }
 
 #ifdef DEBUG
@@ -545,38 +693,40 @@ xfs_ail_next(
  */
 STATIC void
 xfs_ail_check(
-       xfs_ail_entry_t *base)
+       struct xfs_ail  *ailp,
+       xfs_log_item_t  *lip)
 {
-       xfs_log_item_t  *lip;
        xfs_log_item_t  *prev_lip;
 
-       lip = base->ail_forw;
-       if (lip == (xfs_log_item_t*)base) {
-               /*
-                * Make sure the pointers are correct when the list
-                * is empty.
-                */
-               ASSERT(base->ail_back == (xfs_log_item_t*)base);
+       if (list_empty(&ailp->xa_ail))
                return;
-       }
 
        /*
-        * Walk the list checking forward and backward pointers,
-        * lsn ordering, and that every entry has the XFS_LI_IN_AIL
-        * flag set.
+        * Check the next and previous entries are valid.
         */
-       prev_lip = (xfs_log_item_t*)base;
-       while (lip != (xfs_log_item_t*)base) {
-               if (prev_lip != (xfs_log_item_t*)base) {
-                       ASSERT(prev_lip->li_ail.ail_forw == lip);
+       ASSERT((lip->li_flags & XFS_LI_IN_AIL) != 0);
+       prev_lip = list_entry(lip->li_ail.prev, xfs_log_item_t, li_ail);
+       if (&prev_lip->li_ail != &ailp->xa_ail)
+               ASSERT(XFS_LSN_CMP(prev_lip->li_lsn, lip->li_lsn) <= 0);
+
+       prev_lip = list_entry(lip->li_ail.next, xfs_log_item_t, li_ail);
+       if (&prev_lip->li_ail != &ailp->xa_ail)
+               ASSERT(XFS_LSN_CMP(prev_lip->li_lsn, lip->li_lsn) >= 0);
+
+
+#ifdef XFS_TRANS_DEBUG
+       /*
+        * Walk the list checking lsn ordering, and that every entry has the
+        * XFS_LI_IN_AIL flag set. This is really expensive, so only do it
+        * when specifically debugging the transaction subsystem.
+        */
+       prev_lip = list_entry(&ailp->xa_ail, xfs_log_item_t, li_ail);
+       list_for_each_entry(lip, &ailp->xa_ail, li_ail) {
+               if (&prev_lip->li_ail != &ailp->xa_ail)
                        ASSERT(XFS_LSN_CMP(prev_lip->li_lsn, lip->li_lsn) <= 0);
-               }
-               ASSERT(lip->li_ail.ail_back == prev_lip);
                ASSERT((lip->li_flags & XFS_LI_IN_AIL) != 0);
                prev_lip = lip;
-               lip = lip->li_ail.ail_forw;
        }
-       ASSERT(lip == (xfs_log_item_t*)base);
-       ASSERT(base->ail_back == prev_lip);
+#endif /* XFS_TRANS_DEBUG */
 }
 #endif /* DEBUG */