Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
[safe/jmp/linux-2.6] / fs / xfs / xfs_trans.c
index 4e1c22a..ce558ef 100644 (file)
 #include "xfs_trans_priv.h"
 #include "xfs_trans_space.h"
 #include "xfs_inode_item.h"
-
-
-STATIC void    xfs_trans_apply_sb_deltas(xfs_trans_t *);
-STATIC uint    xfs_trans_count_vecs(xfs_trans_t *);
-STATIC void    xfs_trans_fill_vecs(xfs_trans_t *, xfs_log_iovec_t *);
-STATIC void    xfs_trans_uncommit(xfs_trans_t *, uint);
-STATIC void    xfs_trans_committed(xfs_trans_t *, int);
-STATIC void    xfs_trans_chunk_committed(xfs_log_item_chunk_t *, xfs_lsn_t, int);
-STATIC void    xfs_trans_free(xfs_trans_t *);
+#include "xfs_trace.h"
 
 kmem_zone_t    *xfs_trans_zone;
 
-
 /*
  * Reservation functions here avoid a huge stack in xfs_trans_init
  * due to register overflow from temporaries in the calculations.
  */
-
 STATIC uint
 xfs_calc_write_reservation(xfs_mount_t *mp)
 {
@@ -236,30 +226,48 @@ xfs_trans_alloc(
        uint            type)
 {
        xfs_wait_for_freeze(mp, SB_FREEZE_TRANS);
-       return _xfs_trans_alloc(mp, type);
+       return _xfs_trans_alloc(mp, type, KM_SLEEP);
 }
 
 xfs_trans_t *
 _xfs_trans_alloc(
        xfs_mount_t     *mp,
-       uint            type)
+       uint            type,
+       uint            memflags)
 {
        xfs_trans_t     *tp;
 
        atomic_inc(&mp->m_active_trans);
 
-       tp = kmem_zone_zalloc(xfs_trans_zone, KM_SLEEP);
+       tp = kmem_zone_zalloc(xfs_trans_zone, memflags);
        tp->t_magic = XFS_TRANS_MAGIC;
        tp->t_type = type;
        tp->t_mountp = mp;
        tp->t_items_free = XFS_LIC_NUM_SLOTS;
-       tp->t_busy_free = XFS_LBC_NUM_SLOTS;
        xfs_lic_init(&(tp->t_items));
-       XFS_LBC_INIT(&(tp->t_busy));
+       INIT_LIST_HEAD(&tp->t_busy);
        return tp;
 }
 
 /*
+ * Free the transaction structure.  If there is more clean up
+ * to do when the structure is freed, add it here.
+ */
+STATIC void
+xfs_trans_free(
+       struct xfs_trans        *tp)
+{
+       struct xfs_busy_extent  *busyp, *n;
+
+       list_for_each_entry_safe(busyp, n, &tp->t_busy, list)
+               xfs_alloc_busy_clear(tp->t_mountp, busyp);
+
+       atomic_dec(&tp->t_mountp->m_active_trans);
+       xfs_trans_free_dqinfo(tp);
+       kmem_zone_free(xfs_trans_zone, tp);
+}
+
+/*
  * This is called to create a new transaction which will share the
  * permanent log reservation of the given transaction.  The remaining
  * unused block and rt extent reservations are also inherited.  This
@@ -282,22 +290,21 @@ xfs_trans_dup(
        ntp->t_type = tp->t_type;
        ntp->t_mountp = tp->t_mountp;
        ntp->t_items_free = XFS_LIC_NUM_SLOTS;
-       ntp->t_busy_free = XFS_LBC_NUM_SLOTS;
        xfs_lic_init(&(ntp->t_items));
-       XFS_LBC_INIT(&(ntp->t_busy));
+       INIT_LIST_HEAD(&ntp->t_busy);
 
        ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
        ASSERT(tp->t_ticket != NULL);
 
        ntp->t_flags = XFS_TRANS_PERM_LOG_RES | (tp->t_flags & XFS_TRANS_RESERVE);
-       ntp->t_ticket = tp->t_ticket;
+       ntp->t_ticket = xfs_log_ticket_get(tp->t_ticket);
        ntp->t_blk_res = tp->t_blk_res - tp->t_blk_res_used;
        tp->t_blk_res = tp->t_blk_res_used;
        ntp->t_rtx_res = tp->t_rtx_res - tp->t_rtx_res_used;
        tp->t_rtx_res = tp->t_rtx_res_used;
        ntp->t_pflags = tp->t_pflags;
 
-       XFS_TRANS_DUP_DQINFO(tp->t_mountp, tp, ntp);
+       xfs_trans_dup_dqinfo(tp, ntp);
 
        atomic_inc(&tp->t_mountp->m_active_trans);
        return ntp;
@@ -420,7 +427,6 @@ undo_blocks:
        return error;
 }
 
-
 /*
  * Record the indicated change to the given field for application
  * to the file system's superblock when the transaction commits.
@@ -628,8 +634,6 @@ xfs_trans_apply_sb_deltas(
                xfs_trans_log_buf(tp, bp, offsetof(xfs_dsb_t, sb_icount),
                                  offsetof(xfs_dsb_t, sb_frextents) +
                                  sizeof(sbp->sb_frextents) - 1);
-
-       tp->t_mountp->m_super->s_dirt = 1;
 }
 
 /*
@@ -651,7 +655,7 @@ xfs_trans_apply_sb_deltas(
  * XFS_TRANS_SB_DIRTY will not be set when the transaction is updated but we
  * still need to update the incore superblock with the changes.
  */
-STATIC void
+void
 xfs_trans_unreserve_and_mod_sb(
        xfs_trans_t     *tp)
 {
@@ -765,95 +769,256 @@ xfs_trans_unreserve_and_mod_sb(
        }
 }
 
+/*
+ * Total up the number of log iovecs needed to commit this
+ * transaction.  The transaction itself needs one for the
+ * transaction header.  Ask each dirty item in turn how many
+ * it needs to get the total.
+ */
+static uint
+xfs_trans_count_vecs(
+       struct xfs_trans        *tp)
+{
+       int                     nvecs;
+       xfs_log_item_desc_t     *lidp;
+
+       nvecs = 1;
+       lidp = xfs_trans_first_item(tp);
+       ASSERT(lidp != NULL);
+
+       /* In the non-debug case we need to start bailing out if we
+        * didn't find a log_item here, return zero and let trans_commit
+        * deal with it.
+        */
+       if (lidp == NULL)
+               return 0;
+
+       while (lidp != NULL) {
+               /*
+                * Skip items which aren't dirty in this transaction.
+                */
+               if (!(lidp->lid_flags & XFS_LID_DIRTY)) {
+                       lidp = xfs_trans_next_item(tp, lidp);
+                       continue;
+               }
+               lidp->lid_size = IOP_SIZE(lidp->lid_item);
+               nvecs += lidp->lid_size;
+               lidp = xfs_trans_next_item(tp, lidp);
+       }
+
+       return nvecs;
+}
 
 /*
- * xfs_trans_commit
- *
- * Commit the given transaction to the log a/synchronously.
+ * Fill in the vector with pointers to data to be logged
+ * by this transaction.  The transaction header takes
+ * the first vector, and then each dirty item takes the
+ * number of vectors it indicated it needed in xfs_trans_count_vecs().
  *
- * XFS disk error handling mechanism is not based on a typical
- * transaction abort mechanism. Logically after the filesystem
- * gets marked 'SHUTDOWN', we can't let any new transactions
- * be durable - ie. committed to disk - because some metadata might
- * be inconsistent. In such cases, this returns an error, and the
- * caller may assume that all locked objects joined to the transaction
- * have already been unlocked as if the commit had succeeded.
- * Do not reference the transaction structure after this call.
+ * As each item fills in the entries it needs, also pin the item
+ * so that it cannot be flushed out until the log write completes.
  */
- /*ARGSUSED*/
-int
-_xfs_trans_commit(
-       xfs_trans_t     *tp,
-       uint            flags,
-       int             *log_flushed)
+static void
+xfs_trans_fill_vecs(
+       struct xfs_trans        *tp,
+       struct xfs_log_iovec    *log_vector)
 {
-       xfs_log_iovec_t         *log_vector;
-       int                     nvec;
-       xfs_mount_t             *mp;
-       xfs_lsn_t               commit_lsn;
-       /* REFERENCED */
-       int                     error;
-       int                     log_flags;
-       int                     sync;
-#define        XFS_TRANS_LOGVEC_COUNT  16
-       xfs_log_iovec_t         log_vector_fast[XFS_TRANS_LOGVEC_COUNT];
-       void                    *commit_iclog;
-       int                     shutdown;
-
-       commit_lsn = -1;
+       xfs_log_item_desc_t     *lidp;
+       struct xfs_log_iovec    *vecp;
+       uint                    nitems;
 
        /*
-        * Determine whether this commit is releasing a permanent
-        * log reservation or not.
+        * Skip over the entry for the transaction header, we'll
+        * fill that in at the end.
         */
-       if (flags & XFS_TRANS_RELEASE_LOG_RES) {
-               ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
-               log_flags = XFS_LOG_REL_PERM_RESERV;
-       } else {
-               log_flags = 0;
+       vecp = log_vector + 1;
+
+       nitems = 0;
+       lidp = xfs_trans_first_item(tp);
+       ASSERT(lidp);
+       while (lidp) {
+               /* Skip items which aren't dirty in this transaction. */
+               if (!(lidp->lid_flags & XFS_LID_DIRTY)) {
+                       lidp = xfs_trans_next_item(tp, lidp);
+                       continue;
+               }
+
+               /*
+                * The item may be marked dirty but not log anything.  This can
+                * be used to get called when a transaction is committed.
+                */
+               if (lidp->lid_size)
+                       nitems++;
+               IOP_FORMAT(lidp->lid_item, vecp);
+               vecp += lidp->lid_size;
+               IOP_PIN(lidp->lid_item);
+               lidp = xfs_trans_next_item(tp, lidp);
        }
-       mp = tp->t_mountp;
 
        /*
-        * If there is nothing to be logged by the transaction,
-        * then unlock all of the items associated with the
-        * transaction and free the transaction structure.
-        * Also make sure to return any reserved blocks to
-        * the free pool.
+        * Now that we've counted the number of items in this transaction, fill
+        * in the transaction header. Note that the transaction header does not
+        * have a log item.
+        */
+       tp->t_header.th_magic = XFS_TRANS_HEADER_MAGIC;
+       tp->t_header.th_type = tp->t_type;
+       tp->t_header.th_num_items = nitems;
+       log_vector->i_addr = (xfs_caddr_t)&tp->t_header;
+       log_vector->i_len = sizeof(xfs_trans_header_t);
+       log_vector->i_type = XLOG_REG_TYPE_TRANSHDR;
+}
+
+/*
+ * The committed item processing consists of calling the committed routine of
+ * each logged item, updating the item's position in the AIL if necessary, and
+ * unpinning each item.  If the committed routine returns -1, then do nothing
+ * further with the item because it may have been freed.
+ *
+ * Since items are unlocked when they are copied to the incore log, it is
+ * possible for two transactions to be completing and manipulating the same
+ * item simultaneously.  The AIL lock will protect the lsn field of each item.
+ * The value of this field can never go backwards.
+ *
+ * We unpin the items after repositioning them in the AIL, because otherwise
+ * they could be immediately flushed and we'd have to race with the flusher
+ * trying to pull the item from the AIL as we add it.
+ */
+void
+xfs_trans_item_committed(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               commit_lsn,
+       int                     aborted)
+{
+       xfs_lsn_t               item_lsn;
+       struct xfs_ail          *ailp;
+
+       if (aborted)
+               lip->li_flags |= XFS_LI_ABORTED;
+       item_lsn = IOP_COMMITTED(lip, commit_lsn);
+
+       /* If the committed routine returns -1, item has been freed. */
+       if (XFS_LSN_CMP(item_lsn, (xfs_lsn_t)-1) == 0)
+               return;
+
+       /*
+        * If the returned lsn is greater than what it contained before, update
+        * the location of the item in the AIL.  If it is not, then do nothing.
+        * Items can never move backwards in the AIL.
+        *
+        * While the new lsn should usually be greater, it is possible that a
+        * later transaction completing simultaneously with an earlier one
+        * using the same item could complete first with a higher lsn.  This
+        * would cause the earlier transaction to fail the test below.
         */
-shut_us_down:
-       shutdown = XFS_FORCED_SHUTDOWN(mp) ? EIO : 0;
-       if (!(tp->t_flags & XFS_TRANS_DIRTY) || shutdown) {
-               xfs_trans_unreserve_and_mod_sb(tp);
+       ailp = lip->li_ailp;
+       spin_lock(&ailp->xa_lock);
+       if (XFS_LSN_CMP(item_lsn, lip->li_lsn) > 0) {
                /*
-                * It is indeed possible for the transaction to be
-                * not dirty but the dqinfo portion to be. All that
-                * means is that we have some (non-persistent) quota
-                * reservations that need to be unreserved.
+                * This will set the item's lsn to item_lsn and update the
+                * position of the item in the AIL.
+                *
+                * xfs_trans_ail_update() drops the AIL lock.
                 */
-               XFS_TRANS_UNRESERVE_AND_MOD_DQUOTS(mp, tp);
-               if (tp->t_ticket) {
-                       commit_lsn = xfs_log_done(mp, tp->t_ticket,
-                                                       NULL, log_flags);
-                       if (commit_lsn == -1 && !shutdown)
-                               shutdown = XFS_ERROR(EIO);
-               }
-               current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
-               xfs_trans_free_items(tp, shutdown? XFS_TRANS_ABORT : 0);
-               xfs_trans_free_busy(tp);
-               xfs_trans_free(tp);
-               XFS_STATS_INC(xs_trans_empty);
-               return (shutdown);
+               xfs_trans_ail_update(ailp, lip, item_lsn);
+       } else {
+               spin_unlock(&ailp->xa_lock);
        }
-       ASSERT(tp->t_ticket != NULL);
 
        /*
-        * If we need to update the superblock, then do it now.
+        * Now that we've repositioned the item in the AIL, unpin it so it can
+        * be flushed. Pass information about buffer stale state down from the
+        * log item flags, if anyone else stales the buffer we do not want to
+        * pay any attention to it.
         */
-       if (tp->t_flags & XFS_TRANS_SB_DIRTY) {
-               xfs_trans_apply_sb_deltas(tp);
+       IOP_UNPIN(lip);
+}
+
+/*
+ * This is typically called by the LM when a transaction has been fully
+ * committed to disk.  It needs to unpin the items which have
+ * been logged by the transaction and update their positions
+ * in the AIL if necessary.
+ *
+ * This also gets called when the transactions didn't get written out
+ * because of an I/O error. Abortflag & XFS_LI_ABORTED is set then.
+ */
+STATIC void
+xfs_trans_committed(
+       struct xfs_trans        *tp,
+       int                     abortflag)
+{
+       xfs_log_item_desc_t     *lidp;
+       xfs_log_item_chunk_t    *licp;
+       xfs_log_item_chunk_t    *next_licp;
+
+       /* Call the transaction's completion callback if there is one. */
+       if (tp->t_callback != NULL)
+               tp->t_callback(tp, tp->t_callarg);
+
+       for (lidp = xfs_trans_first_item(tp);
+            lidp != NULL;
+            lidp = xfs_trans_next_item(tp, lidp)) {
+               xfs_trans_item_committed(lidp->lid_item, tp->t_lsn, abortflag);
+       }
+
+       /* free the item chunks, ignoring the embedded chunk */
+       for (licp = tp->t_items.lic_next; licp != NULL; licp = next_licp) {
+               next_licp = licp->lic_next;
+               kmem_free(licp);
        }
-       XFS_TRANS_APPLY_DQUOT_DELTAS(mp, tp);
+
+       xfs_trans_free(tp);
+}
+
+/*
+ * Called from the trans_commit code when we notice that
+ * the filesystem is in the middle of a forced shutdown.
+ */
+STATIC void
+xfs_trans_uncommit(
+       struct xfs_trans        *tp,
+       uint                    flags)
+{
+       xfs_log_item_desc_t     *lidp;
+
+       for (lidp = xfs_trans_first_item(tp);
+            lidp != NULL;
+            lidp = xfs_trans_next_item(tp, lidp)) {
+               /*
+                * Unpin all but those that aren't dirty.
+                */
+               if (lidp->lid_flags & XFS_LID_DIRTY)
+                       IOP_UNPIN_REMOVE(lidp->lid_item, tp);
+       }
+
+       xfs_trans_unreserve_and_mod_sb(tp);
+       xfs_trans_unreserve_and_mod_dquots(tp);
+
+       xfs_trans_free_items(tp, NULLCOMMITLSN, flags);
+       xfs_trans_free(tp);
+}
+
+/*
+ * Format the transaction direct to the iclog. This isolates the physical
+ * transaction commit operation from the logical operation and hence allows
+ * other methods to be introduced without affecting the existing commit path.
+ */
+static int
+xfs_trans_commit_iclog(
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       xfs_lsn_t               *commit_lsn,
+       int                     flags)
+{
+       int                     shutdown;
+       int                     error;
+       int                     log_flags = 0;
+       struct xlog_in_core     *commit_iclog;
+#define XFS_TRANS_LOGVEC_COUNT  16
+       struct xfs_log_iovec    log_vector_fast[XFS_TRANS_LOGVEC_COUNT];
+       struct xfs_log_iovec    *log_vector;
+       uint                    nvec;
+
 
        /*
         * Ask each log item how many log_vector entries it will
@@ -863,8 +1028,7 @@ shut_us_down:
         */
        nvec = xfs_trans_count_vecs(tp);
        if (nvec == 0) {
-               xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);
-               goto shut_us_down;
+               return ENOMEM;  /* triggers a shutdown! */
        } else if (nvec <= XFS_TRANS_LOGVEC_COUNT) {
                log_vector = log_vector_fast;
        } else {
@@ -879,6 +1043,9 @@ shut_us_down:
         */
        xfs_trans_fill_vecs(tp, log_vector);
 
+       if (flags & XFS_TRANS_RELEASE_LOG_RES)
+               log_flags = XFS_LOG_REL_PERM_RESERV;
+
        error = xfs_log_write(mp, log_vector, nvec, tp->t_ticket, &(tp->t_lsn));
 
        /*
@@ -886,18 +1053,19 @@ shut_us_down:
         * at any time after this call.  However, all the items associated
         * with the transaction are still locked and pinned in memory.
         */
-       commit_lsn = xfs_log_done(mp, tp->t_ticket, &commit_iclog, log_flags);
+       *commit_lsn = xfs_log_done(mp, tp->t_ticket, &commit_iclog, log_flags);
+
+       tp->t_commit_lsn = *commit_lsn;
+       trace_xfs_trans_commit_lsn(tp);
 
-       tp->t_commit_lsn = commit_lsn;
-       if (nvec > XFS_TRANS_LOGVEC_COUNT) {
+       if (nvec > XFS_TRANS_LOGVEC_COUNT)
                kmem_free(log_vector);
-       }
 
        /*
         * If we got a log write error. Unpin the logitems that we
         * had pinned, clean up, free trans structure, and return error.
         */
-       if (error || commit_lsn == -1) {
+       if (error || *commit_lsn == -1) {
                current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
                xfs_trans_uncommit(tp, flags|XFS_TRANS_ABORT);
                return XFS_ERROR(EIO);
@@ -911,8 +1079,6 @@ shut_us_down:
         */
        xfs_trans_unreserve_and_mod_sb(tp);
 
-       sync = tp->t_flags & XFS_TRANS_SYNC;
-
        /*
         * Tell the LM to call the transaction completion routine
         * when the log write with LSN commit_lsn completes (e.g.
@@ -955,7 +1121,7 @@ shut_us_down:
         * the commit lsn of this transaction for dependency tracking
         * purposes.
         */
-       xfs_trans_unlock_items(tp, commit_lsn);
+       xfs_trans_unlock_items(tp, *commit_lsn);
 
        /*
         * If we detected a log error earlier, finish committing
@@ -975,157 +1141,204 @@ shut_us_down:
         * and the items are released we can finally allow the iclog to
         * go to disk.
         */
-       error = xfs_log_release_iclog(mp, commit_iclog);
-
-       /*
-        * If the transaction needs to be synchronous, then force the
-        * log out now and wait for it.
-        */
-       if (sync) {
-               if (!error) {
-                       error = _xfs_log_force(mp, commit_lsn,
-                                     XFS_LOG_FORCE | XFS_LOG_SYNC,
-                                     log_flushed);
-               }
-               XFS_STATS_INC(xs_trans_sync);
-       } else {
-               XFS_STATS_INC(xs_trans_async);
-       }
-
-       return (error);
+       return xfs_log_release_iclog(mp, commit_iclog);
 }
 
-
 /*
- * Total up the number of log iovecs needed to commit this
- * transaction.  The transaction itself needs one for the
- * transaction header.  Ask each dirty item in turn how many
- * it needs to get the total.
+ * Walk the log items and allocate log vector structures for
+ * each item large enough to fit all the vectors they require.
+ * Note that this format differs from the old log vector format in
+ * that there is no transaction header in these log vectors.
  */
-STATIC uint
-xfs_trans_count_vecs(
+STATIC struct xfs_log_vec *
+xfs_trans_alloc_log_vecs(
        xfs_trans_t     *tp)
 {
-       int                     nvecs;
        xfs_log_item_desc_t     *lidp;
+       struct xfs_log_vec      *lv = NULL;
+       struct xfs_log_vec      *ret_lv = NULL;
 
-       nvecs = 1;
        lidp = xfs_trans_first_item(tp);
-       ASSERT(lidp != NULL);
 
-       /* In the non-debug case we need to start bailing out if we
-        * didn't find a log_item here, return zero and let trans_commit
-        * deal with it.
-        */
-       if (lidp == NULL)
-               return 0;
+       /* Bail out if we didn't find a log item.  */
+       if (!lidp) {
+               ASSERT(0);
+               return NULL;
+       }
 
        while (lidp != NULL) {
-               /*
-                * Skip items which aren't dirty in this transaction.
-                */
+               struct xfs_log_vec *new_lv;
+
+               /* Skip items which aren't dirty in this transaction. */
                if (!(lidp->lid_flags & XFS_LID_DIRTY)) {
                        lidp = xfs_trans_next_item(tp, lidp);
                        continue;
                }
+
+               /* Skip items that do not have any vectors for writing */
                lidp->lid_size = IOP_SIZE(lidp->lid_item);
-               nvecs += lidp->lid_size;
+               if (!lidp->lid_size) {
+                       lidp = xfs_trans_next_item(tp, lidp);
+                       continue;
+               }
+
+               new_lv = kmem_zalloc(sizeof(*new_lv) +
+                               lidp->lid_size * sizeof(struct xfs_log_iovec),
+                               KM_SLEEP);
+
+               /* The allocated iovec region lies beyond the log vector. */
+               new_lv->lv_iovecp = (struct xfs_log_iovec *)&new_lv[1];
+               new_lv->lv_niovecs = lidp->lid_size;
+               new_lv->lv_item = lidp->lid_item;
+               if (!ret_lv)
+                       ret_lv = new_lv;
+               else
+                       lv->lv_next = new_lv;
+               lv = new_lv;
                lidp = xfs_trans_next_item(tp, lidp);
        }
 
-       return nvecs;
+       return ret_lv;
 }
 
-/*
- * Called from the trans_commit code when we notice that
- * the filesystem is in the middle of a forced shutdown.
- */
-STATIC void
-xfs_trans_uncommit(
-       xfs_trans_t     *tp,
-       uint            flags)
+static int
+xfs_trans_commit_cil(
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       xfs_lsn_t               *commit_lsn,
+       int                     flags)
 {
-       xfs_log_item_desc_t     *lidp;
+       struct xfs_log_vec      *log_vector;
+       int                     error;
 
-       for (lidp = xfs_trans_first_item(tp);
-            lidp != NULL;
-            lidp = xfs_trans_next_item(tp, lidp)) {
-               /*
-                * Unpin all but those that aren't dirty.
-                */
-               if (lidp->lid_flags & XFS_LID_DIRTY)
-                       IOP_UNPIN_REMOVE(lidp->lid_item, tp);
-       }
+       /*
+        * Get each log item to allocate a vector structure for
+        * the log item to to pass to the log write code. The
+        * CIL commit code will format the vector and save it away.
+        */
+       log_vector = xfs_trans_alloc_log_vecs(tp);
+       if (!log_vector)
+               return ENOMEM;
 
-       xfs_trans_unreserve_and_mod_sb(tp);
-       XFS_TRANS_UNRESERVE_AND_MOD_DQUOTS(tp->t_mountp, tp);
+       error = xfs_log_commit_cil(mp, tp, log_vector, commit_lsn, flags);
+       if (error)
+               return error;
 
-       xfs_trans_free_items(tp, flags);
-       xfs_trans_free_busy(tp);
+       current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
+
+       /* xfs_trans_free_items() unlocks them first */
+       xfs_trans_free_items(tp, *commit_lsn, 0);
        xfs_trans_free(tp);
+       return 0;
 }
 
 /*
- * Fill in the vector with pointers to data to be logged
- * by this transaction.  The transaction header takes
- * the first vector, and then each dirty item takes the
- * number of vectors it indicated it needed in xfs_trans_count_vecs().
+ * xfs_trans_commit
  *
- * As each item fills in the entries it needs, also pin the item
- * so that it cannot be flushed out until the log write completes.
+ * Commit the given transaction to the log a/synchronously.
+ *
+ * XFS disk error handling mechanism is not based on a typical
+ * transaction abort mechanism. Logically after the filesystem
+ * gets marked 'SHUTDOWN', we can't let any new transactions
+ * be durable - ie. committed to disk - because some metadata might
+ * be inconsistent. In such cases, this returns an error, and the
+ * caller may assume that all locked objects joined to the transaction
+ * have already been unlocked as if the commit had succeeded.
+ * Do not reference the transaction structure after this call.
  */
-STATIC void
-xfs_trans_fill_vecs(
-       xfs_trans_t             *tp,
-       xfs_log_iovec_t         *log_vector)
+int
+_xfs_trans_commit(
+       struct xfs_trans        *tp,
+       uint                    flags,
+       int                     *log_flushed)
 {
-       xfs_log_item_desc_t     *lidp;
-       xfs_log_iovec_t         *vecp;
-       uint                    nitems;
+       struct xfs_mount        *mp = tp->t_mountp;
+       xfs_lsn_t               commit_lsn = -1;
+       int                     error = 0;
+       int                     log_flags = 0;
+       int                     sync = tp->t_flags & XFS_TRANS_SYNC;
 
        /*
-        * Skip over the entry for the transaction header, we'll
-        * fill that in at the end.
+        * Determine whether this commit is releasing a permanent
+        * log reservation or not.
         */
-       vecp = log_vector + 1;          /* pointer arithmetic */
+       if (flags & XFS_TRANS_RELEASE_LOG_RES) {
+               ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
+               log_flags = XFS_LOG_REL_PERM_RESERV;
+       }
 
-       nitems = 0;
-       lidp = xfs_trans_first_item(tp);
-       ASSERT(lidp != NULL);
-       while (lidp != NULL) {
-               /*
-                * Skip items which aren't dirty in this transaction.
-                */
-               if (!(lidp->lid_flags & XFS_LID_DIRTY)) {
-                       lidp = xfs_trans_next_item(tp, lidp);
-                       continue;
-               }
-               /*
-                * The item may be marked dirty but not log anything.
-                * This can be used to get called when a transaction
-                * is committed.
-                */
-               if (lidp->lid_size) {
-                       nitems++;
+       /*
+        * If there is nothing to be logged by the transaction,
+        * then unlock all of the items associated with the
+        * transaction and free the transaction structure.
+        * Also make sure to return any reserved blocks to
+        * the free pool.
+        */
+       if (!(tp->t_flags & XFS_TRANS_DIRTY))
+               goto out_unreserve;
+
+       if (XFS_FORCED_SHUTDOWN(mp)) {
+               error = XFS_ERROR(EIO);
+               goto out_unreserve;
+       }
+
+       ASSERT(tp->t_ticket != NULL);
+
+       /*
+        * If we need to update the superblock, then do it now.
+        */
+       if (tp->t_flags & XFS_TRANS_SB_DIRTY)
+               xfs_trans_apply_sb_deltas(tp);
+       xfs_trans_apply_dquot_deltas(tp);
+
+       if (mp->m_flags & XFS_MOUNT_DELAYLOG)
+               error = xfs_trans_commit_cil(mp, tp, &commit_lsn, flags);
+       else
+               error = xfs_trans_commit_iclog(mp, tp, &commit_lsn, flags);
+
+       if (error == ENOMEM) {
+               xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);
+               error = XFS_ERROR(EIO);
+               goto out_unreserve;
+       }
+
+       /*
+        * If the transaction needs to be synchronous, then force the
+        * log out now and wait for it.
+        */
+       if (sync) {
+               if (!error) {
+                       error = _xfs_log_force_lsn(mp, commit_lsn,
+                                     XFS_LOG_SYNC, log_flushed);
                }
-               IOP_FORMAT(lidp->lid_item, vecp);
-               vecp += lidp->lid_size;         /* pointer arithmetic */
-               IOP_PIN(lidp->lid_item);
-               lidp = xfs_trans_next_item(tp, lidp);
+               XFS_STATS_INC(xs_trans_sync);
+       } else {
+               XFS_STATS_INC(xs_trans_async);
        }
 
+       return error;
+
+out_unreserve:
+       xfs_trans_unreserve_and_mod_sb(tp);
+
        /*
-        * Now that we've counted the number of items in this
-        * transaction, fill in the transaction header.
+        * It is indeed possible for the transaction to be not dirty but
+        * the dqinfo portion to be.  All that means is that we have some
+        * (non-persistent) quota reservations that need to be unreserved.
         */
-       tp->t_header.th_magic = XFS_TRANS_HEADER_MAGIC;
-       tp->t_header.th_type = tp->t_type;
-       tp->t_header.th_num_items = nitems;
-       log_vector->i_addr = (xfs_caddr_t)&tp->t_header;
-       log_vector->i_len = sizeof(xfs_trans_header_t);
-       XLOG_VEC_SET_TYPE(log_vector, XLOG_REG_TYPE_TRANSHDR);
-}
+       xfs_trans_unreserve_and_mod_dquots(tp);
+       if (tp->t_ticket) {
+               commit_lsn = xfs_log_done(mp, tp->t_ticket, NULL, log_flags);
+               if (commit_lsn == -1 && !error)
+                       error = XFS_ERROR(EIO);
+       }
+       current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
+       xfs_trans_free_items(tp, NULLCOMMITLSN, error ? XFS_TRANS_ABORT : 0);
+       xfs_trans_free(tp);
 
+       XFS_STATS_INC(xs_trans_empty);
+       return error;
+}
 
 /*
  * Unlock all of the transaction's items and free the transaction.
@@ -1183,7 +1396,7 @@ xfs_trans_cancel(
        }
 #endif
        xfs_trans_unreserve_and_mod_sb(tp);
-       XFS_TRANS_UNRESERVE_AND_MOD_DQUOTS(mp, tp);
+       xfs_trans_unreserve_and_mod_dquots(tp);
 
        if (tp->t_ticket) {
                if (flags & XFS_TRANS_RELEASE_LOG_RES) {
@@ -1198,25 +1411,10 @@ xfs_trans_cancel(
        /* mark this thread as no longer being in a transaction */
        current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
 
-       xfs_trans_free_items(tp, flags);
-       xfs_trans_free_busy(tp);
+       xfs_trans_free_items(tp, NULLCOMMITLSN, flags);
        xfs_trans_free(tp);
 }
 
-
-/*
- * Free the transaction structure.  If there is more clean up
- * to do when the structure is freed, add it here.
- */
-STATIC void
-xfs_trans_free(
-       xfs_trans_t     *tp)
-{
-       atomic_dec(&tp->t_mountp->m_active_trans);
-       XFS_TRANS_FREE_DQINFO(tp->t_mountp, tp);
-       kmem_zone_free(xfs_trans_zone, tp);
-}
-
 /*
  * Roll from one trans in the sequence of PERMANENT transactions to
  * the next: permanent transactions are only flushed out when
@@ -1260,6 +1458,13 @@ xfs_trans_roll(
        trans = *tpp;
 
        /*
+        * transaction commit worked ok so we can drop the extra ticket
+        * reference that we gained in xfs_trans_dup()
+        */
+       xfs_log_ticket_put(trans->t_ticket);
+
+
+       /*
         * Reserve space in the log for th next transaction.
         * This also pushes items in the "AIL", the list of logged items,
         * out to disk if they are taking up space at the tail of the log
@@ -1279,173 +1484,3 @@ xfs_trans_roll(
        xfs_trans_ihold(trans, dp);
        return 0;
 }
-
-/*
- * THIS SHOULD BE REWRITTEN TO USE xfs_trans_next_item().
- *
- * This is typically called by the LM when a transaction has been fully
- * committed to disk.  It needs to unpin the items which have
- * been logged by the transaction and update their positions
- * in the AIL if necessary.
- * This also gets called when the transactions didn't get written out
- * because of an I/O error. Abortflag & XFS_LI_ABORTED is set then.
- *
- * Call xfs_trans_chunk_committed() to process the items in
- * each chunk.
- */
-STATIC void
-xfs_trans_committed(
-       xfs_trans_t     *tp,
-       int             abortflag)
-{
-       xfs_log_item_chunk_t    *licp;
-       xfs_log_item_chunk_t    *next_licp;
-       xfs_log_busy_chunk_t    *lbcp;
-       xfs_log_busy_slot_t     *lbsp;
-       int                     i;
-
-       /*
-        * Call the transaction's completion callback if there
-        * is one.
-        */
-       if (tp->t_callback != NULL) {
-               tp->t_callback(tp, tp->t_callarg);
-       }
-
-       /*
-        * Special case the chunk embedded in the transaction.
-        */
-       licp = &(tp->t_items);
-       if (!(xfs_lic_are_all_free(licp))) {
-               xfs_trans_chunk_committed(licp, tp->t_lsn, abortflag);
-       }
-
-       /*
-        * Process the items in each chunk in turn.
-        */
-       licp = licp->lic_next;
-       while (licp != NULL) {
-               ASSERT(!xfs_lic_are_all_free(licp));
-               xfs_trans_chunk_committed(licp, tp->t_lsn, abortflag);
-               next_licp = licp->lic_next;
-               kmem_free(licp);
-               licp = next_licp;
-       }
-
-       /*
-        * Clear all the per-AG busy list items listed in this transaction
-        */
-       lbcp = &tp->t_busy;
-       while (lbcp != NULL) {
-               for (i = 0, lbsp = lbcp->lbc_busy; i < lbcp->lbc_unused; i++, lbsp++) {
-                       if (!XFS_LBC_ISFREE(lbcp, i)) {
-                               xfs_alloc_clear_busy(tp, lbsp->lbc_ag,
-                                                    lbsp->lbc_idx);
-                       }
-               }
-               lbcp = lbcp->lbc_next;
-       }
-       xfs_trans_free_busy(tp);
-
-       /*
-        * That's it for the transaction structure.  Free it.
-        */
-       xfs_trans_free(tp);
-}
-
-/*
- * This is called to perform the commit processing for each
- * item described by the given chunk.
- *
- * The commit processing consists of unlocking items which were
- * held locked with the SYNC_UNLOCK attribute, calling the committed
- * routine of each logged item, updating the item's position in the AIL
- * if necessary, and unpinning each item.  If the committed routine
- * returns -1, then do nothing further with the item because it
- * may have been freed.
- *
- * Since items are unlocked when they are copied to the incore
- * log, it is possible for two transactions to be completing
- * and manipulating the same item simultaneously.  The AIL lock
- * will protect the lsn field of each item.  The value of this
- * field can never go backwards.
- *
- * We unpin the items after repositioning them in the AIL, because
- * otherwise they could be immediately flushed and we'd have to race
- * with the flusher trying to pull the item from the AIL as we add it.
- */
-STATIC void
-xfs_trans_chunk_committed(
-       xfs_log_item_chunk_t    *licp,
-       xfs_lsn_t               lsn,
-       int                     aborted)
-{
-       xfs_log_item_desc_t     *lidp;
-       xfs_log_item_t          *lip;
-       xfs_lsn_t               item_lsn;
-       struct xfs_mount        *mp;
-       int                     i;
-
-       lidp = licp->lic_descs;
-       for (i = 0; i < licp->lic_unused; i++, lidp++) {
-               if (xfs_lic_isfree(licp, i)) {
-                       continue;
-               }
-
-               lip = lidp->lid_item;
-               if (aborted)
-                       lip->li_flags |= XFS_LI_ABORTED;
-
-               /*
-                * Send in the ABORTED flag to the COMMITTED routine
-                * so that it knows whether the transaction was aborted
-                * or not.
-                */
-               item_lsn = IOP_COMMITTED(lip, lsn);
-
-               /*
-                * If the committed routine returns -1, make
-                * no more references to the item.
-                */
-               if (XFS_LSN_CMP(item_lsn, (xfs_lsn_t)-1) == 0) {
-                       continue;
-               }
-
-               /*
-                * If the returned lsn is greater than what it
-                * contained before, update the location of the
-                * item in the AIL.  If it is not, then do nothing.
-                * Items can never move backwards in the AIL.
-                *
-                * While the new lsn should usually be greater, it
-                * is possible that a later transaction completing
-                * simultaneously with an earlier one using the
-                * same item could complete first with a higher lsn.
-                * This would cause the earlier transaction to fail
-                * the test below.
-                */
-               mp = lip->li_mountp;
-               spin_lock(&mp->m_ail_lock);
-               if (XFS_LSN_CMP(item_lsn, lip->li_lsn) > 0) {
-                       /*
-                        * This will set the item's lsn to item_lsn
-                        * and update the position of the item in
-                        * the AIL.
-                        *
-                        * xfs_trans_update_ail() drops the AIL lock.
-                        */
-                       xfs_trans_update_ail(mp, lip, item_lsn);
-               } else {
-                       spin_unlock(&mp->m_ail_lock);
-               }
-
-               /*
-                * Now that we've repositioned the item in the AIL,
-                * unpin it so it can be flushed. Pass information
-                * about buffer stale state down from the log item
-                * flags, if anyone else stales the buffer we do not
-                * want to pay any attention to it.
-                */
-               IOP_UNPIN(lip, lidp->lid_flags & XFS_LID_BUF_STALE);
-       }
-}