Merge branch 'devel-stable' of master.kernel.org:/home/rmk/linux-2.6-arm
[pandora-kernel.git] / fs / xfs / xfs_log_cil.c
index 7e206fc..9dc8125 100644 (file)
@@ -61,7 +61,7 @@ xlog_cil_init(
        INIT_LIST_HEAD(&cil->xc_committing);
        spin_lock_init(&cil->xc_cil_lock);
        init_rwsem(&cil->xc_ctx_lock);
-       sv_init(&cil->xc_commit_wait, SV_DEFAULT, "cilwait");
+       init_waitqueue_head(&cil->xc_commit_wait);
 
        INIT_LIST_HEAD(&ctx->committing);
        INIT_LIST_HEAD(&ctx->busy_extents);
@@ -145,102 +145,6 @@ xlog_cil_init_post_recovery(
                                                                log->l_curr_block);
 }
 
-/*
- * Insert the log item into the CIL and calculate the difference in space
- * consumed by the item. Add the space to the checkpoint ticket and calculate
- * if the change requires additional log metadata. If it does, take that space
- * as well. Remove the amount of space we addded to the checkpoint ticket from
- * the current transaction ticket so that the accounting works out correctly.
- *
- * If this is the first time the item is being placed into the CIL in this
- * context, pin it so it can't be written to disk until the CIL is flushed to
- * the iclog and the iclog written to disk.
- */
-static void
-xlog_cil_insert(
-       struct log              *log,
-       struct xlog_ticket      *ticket,
-       struct xfs_log_item     *item,
-       struct xfs_log_vec      *lv)
-{
-       struct xfs_cil          *cil = log->l_cilp;
-       struct xfs_log_vec      *old = lv->lv_item->li_lv;
-       struct xfs_cil_ctx      *ctx = cil->xc_ctx;
-       int                     len;
-       int                     diff_iovecs;
-       int                     iclog_space;
-
-       if (old) {
-               /* existing lv on log item, space used is a delta */
-               ASSERT(!list_empty(&item->li_cil));
-               ASSERT(old->lv_buf && old->lv_buf_len && old->lv_niovecs);
-
-               len = lv->lv_buf_len - old->lv_buf_len;
-               diff_iovecs = lv->lv_niovecs - old->lv_niovecs;
-               kmem_free(old->lv_buf);
-               kmem_free(old);
-       } else {
-               /* new lv, must pin the log item */
-               ASSERT(!lv->lv_item->li_lv);
-               ASSERT(list_empty(&item->li_cil));
-
-               len = lv->lv_buf_len;
-               diff_iovecs = lv->lv_niovecs;
-               IOP_PIN(lv->lv_item);
-
-       }
-       len += diff_iovecs * sizeof(xlog_op_header_t);
-
-       /* attach new log vector to log item */
-       lv->lv_item->li_lv = lv;
-
-       spin_lock(&cil->xc_cil_lock);
-       list_move_tail(&item->li_cil, &cil->xc_cil);
-       ctx->nvecs += diff_iovecs;
-
-       /*
-        * If this is the first time the item is being committed to the CIL,
-        * store the sequence number on the log item so we can tell
-        * in future commits whether this is the first checkpoint the item is
-        * being committed into.
-        */
-       if (!item->li_seq)
-               item->li_seq = ctx->sequence;
-
-       /*
-        * Now transfer enough transaction reservation to the context ticket
-        * for the checkpoint. The context ticket is special - the unit
-        * reservation has to grow as well as the current reservation as we
-        * steal from tickets so we can correctly determine the space used
-        * during the transaction commit.
-        */
-       if (ctx->ticket->t_curr_res == 0) {
-               /* first commit in checkpoint, steal the header reservation */
-               ASSERT(ticket->t_curr_res >= ctx->ticket->t_unit_res + len);
-               ctx->ticket->t_curr_res = ctx->ticket->t_unit_res;
-               ticket->t_curr_res -= ctx->ticket->t_unit_res;
-       }
-
-       /* do we need space for more log record headers? */
-       iclog_space = log->l_iclog_size - log->l_iclog_hsize;
-       if (len > 0 && (ctx->space_used / iclog_space !=
-                               (ctx->space_used + len) / iclog_space)) {
-               int hdrs;
-
-               hdrs = (len + iclog_space - 1) / iclog_space;
-               /* need to take into account split region headers, too */
-               hdrs *= log->l_iclog_hsize + sizeof(struct xlog_op_header);
-               ctx->ticket->t_unit_res += hdrs;
-               ctx->ticket->t_curr_res += hdrs;
-               ticket->t_curr_res -= hdrs;
-               ASSERT(ticket->t_curr_res >= len);
-       }
-       ticket->t_curr_res -= len;
-       ctx->space_used += len;
-
-       spin_unlock(&cil->xc_cil_lock);
-}
-
 /*
  * Format log item into a flat buffers
  *
@@ -286,7 +190,7 @@ xlog_cil_format_items(
                        len += lv->lv_iovecp[index].i_len;
 
                lv->lv_buf_len = len;
-               lv->lv_buf = kmem_zalloc(lv->lv_buf_len, KM_SLEEP|KM_NOFS);
+               lv->lv_buf = kmem_alloc(lv->lv_buf_len, KM_SLEEP|KM_NOFS);
                ptr = lv->lv_buf;
 
                for (index = 0; index < lv->lv_niovecs; index++) {
@@ -300,21 +204,136 @@ xlog_cil_format_items(
        }
 }
 
+/*
+ * Prepare the log item for insertion into the CIL. Calculate the difference in
+ * log space and vectors it will consume, and if it is a new item pin it as
+ * well.
+ */
+STATIC void
+xfs_cil_prepare_item(
+       struct log              *log,
+       struct xfs_log_vec      *lv,
+       int                     *len,
+       int                     *diff_iovecs)
+{
+       struct xfs_log_vec      *old = lv->lv_item->li_lv;
+
+       if (old) {
+               /* existing lv on log item, space used is a delta */
+               ASSERT(!list_empty(&lv->lv_item->li_cil));
+               ASSERT(old->lv_buf && old->lv_buf_len && old->lv_niovecs);
+
+               *len += lv->lv_buf_len - old->lv_buf_len;
+               *diff_iovecs += lv->lv_niovecs - old->lv_niovecs;
+               kmem_free(old->lv_buf);
+               kmem_free(old);
+       } else {
+               /* new lv, must pin the log item */
+               ASSERT(!lv->lv_item->li_lv);
+               ASSERT(list_empty(&lv->lv_item->li_cil));
+
+               *len += lv->lv_buf_len;
+               *diff_iovecs += lv->lv_niovecs;
+               IOP_PIN(lv->lv_item);
+
+       }
+
+       /* attach new log vector to log item */
+       lv->lv_item->li_lv = lv;
+
+       /*
+        * If this is the first time the item is being committed to the
+        * CIL, store the sequence number on the log item so we can
+        * tell in future commits whether this is the first checkpoint
+        * the item is being committed into.
+        */
+       if (!lv->lv_item->li_seq)
+               lv->lv_item->li_seq = log->l_cilp->xc_ctx->sequence;
+}
+
+/*
+ * Insert the log items into the CIL and calculate the difference in space
+ * consumed by the item. Add the space to the checkpoint ticket and calculate
+ * if the change requires additional log metadata. If it does, take that space
+ * as well. Remove the amount of space we addded to the checkpoint ticket from
+ * the current transaction ticket so that the accounting works out correctly.
+ */
 static void
 xlog_cil_insert_items(
        struct log              *log,
        struct xfs_log_vec      *log_vector,
-       struct xlog_ticket      *ticket,
-       xfs_lsn_t               *start_lsn)
+       struct xlog_ticket      *ticket)
 {
-       struct xfs_log_vec *lv;
-
-       if (start_lsn)
-               *start_lsn = log->l_cilp->xc_ctx->sequence;
+       struct xfs_cil          *cil = log->l_cilp;
+       struct xfs_cil_ctx      *ctx = cil->xc_ctx;
+       struct xfs_log_vec      *lv;
+       int                     len = 0;
+       int                     diff_iovecs = 0;
+       int                     iclog_space;
 
        ASSERT(log_vector);
+
+       /*
+        * Do all the accounting aggregation and switching of log vectors
+        * around in a separate loop to the insertion of items into the CIL.
+        * Then we can do a separate loop to update the CIL within a single
+        * lock/unlock pair. This reduces the number of round trips on the CIL
+        * lock from O(nr_logvectors) to O(1) and greatly reduces the overall
+        * hold time for the transaction commit.
+        *
+        * If this is the first time the item is being placed into the CIL in
+        * this context, pin it so it can't be written to disk until the CIL is
+        * flushed to the iclog and the iclog written to disk.
+        *
+        * We can do this safely because the context can't checkpoint until we
+        * are done so it doesn't matter exactly how we update the CIL.
+        */
        for (lv = log_vector; lv; lv = lv->lv_next)
-               xlog_cil_insert(log, ticket, lv->lv_item, lv);
+               xfs_cil_prepare_item(log, lv, &len, &diff_iovecs);
+
+       /* account for space used by new iovec headers  */
+       len += diff_iovecs * sizeof(xlog_op_header_t);
+
+       spin_lock(&cil->xc_cil_lock);
+
+       /* move the items to the tail of the CIL */
+       for (lv = log_vector; lv; lv = lv->lv_next)
+               list_move_tail(&lv->lv_item->li_cil, &cil->xc_cil);
+
+       ctx->nvecs += diff_iovecs;
+
+       /*
+        * Now transfer enough transaction reservation to the context ticket
+        * for the checkpoint. The context ticket is special - the unit
+        * reservation has to grow as well as the current reservation as we
+        * steal from tickets so we can correctly determine the space used
+        * during the transaction commit.
+        */
+       if (ctx->ticket->t_curr_res == 0) {
+               /* first commit in checkpoint, steal the header reservation */
+               ASSERT(ticket->t_curr_res >= ctx->ticket->t_unit_res + len);
+               ctx->ticket->t_curr_res = ctx->ticket->t_unit_res;
+               ticket->t_curr_res -= ctx->ticket->t_unit_res;
+       }
+
+       /* do we need space for more log record headers? */
+       iclog_space = log->l_iclog_size - log->l_iclog_hsize;
+       if (len > 0 && (ctx->space_used / iclog_space !=
+                               (ctx->space_used + len) / iclog_space)) {
+               int hdrs;
+
+               hdrs = (len + iclog_space - 1) / iclog_space;
+               /* need to take into account split region headers, too */
+               hdrs *= log->l_iclog_hsize + sizeof(struct xlog_op_header);
+               ctx->ticket->t_unit_res += hdrs;
+               ctx->ticket->t_curr_res += hdrs;
+               ticket->t_curr_res -= hdrs;
+               ASSERT(ticket->t_curr_res >= len);
+       }
+       ticket->t_curr_res -= len;
+       ctx->space_used += len;
+
+       spin_unlock(&cil->xc_cil_lock);
 }
 
 static void
@@ -342,15 +361,10 @@ xlog_cil_committed(
        int     abort)
 {
        struct xfs_cil_ctx      *ctx = args;
-       struct xfs_log_vec      *lv;
-       int                     abortflag = abort ? XFS_LI_ABORTED : 0;
        struct xfs_busy_extent  *busyp, *n;
 
-       /* unpin all the log items */
-       for (lv = ctx->lv_chain; lv; lv = lv->lv_next ) {
-               xfs_trans_item_committed(lv->lv_item, ctx->start_lsn,
-                                                       abortflag);
-       }
+       xfs_trans_committed_bulk(ctx->cil->xc_log->l_ailp, ctx->lv_chain,
+                                       ctx->start_lsn, abort);
 
        list_for_each_entry_safe(busyp, n, &ctx->busy_extents, list)
                xfs_alloc_busy_clear(ctx->cil->xc_log->l_mp, busyp);
@@ -549,7 +563,7 @@ restart:
                         * It is still being pushed! Wait for the push to
                         * complete, then start again from the beginning.
                         */
-                       sv_wait(&cil->xc_commit_wait, 0, &cil->xc_cil_lock, 0);
+                       xlog_wait(&cil->xc_commit_wait, &cil->xc_cil_lock);
                        goto restart;
                }
        }
@@ -573,7 +587,7 @@ restart:
         */
        spin_lock(&cil->xc_cil_lock);
        ctx->commit_lsn = commit_lsn;
-       sv_broadcast(&cil->xc_commit_wait);
+       wake_up_all(&cil->xc_commit_wait);
        spin_unlock(&cil->xc_cil_lock);
 
        /* release the hounds! */
@@ -638,7 +652,10 @@ xfs_log_commit_cil(
 
        /* lock out background commit */
        down_read(&log->l_cilp->xc_ctx_lock);
-       xlog_cil_insert_items(log, log_vector, tp->t_ticket, commit_lsn);
+       if (commit_lsn)
+               *commit_lsn = log->l_cilp->xc_ctx->sequence;
+
+       xlog_cil_insert_items(log, log_vector, tp->t_ticket);
 
        /* check we didn't blow the reservation */
        if (tp->t_ticket->t_curr_res < 0)
@@ -735,7 +752,7 @@ restart:
                         * It is still being pushed! Wait for the push to
                         * complete, then start again from the beginning.
                         */
-                       sv_wait(&cil->xc_commit_wait, 0, &cil->xc_cil_lock, 0);
+                       xlog_wait(&cil->xc_commit_wait, &cil->xc_cil_lock);
                        goto restart;
                }
                if (ctx->sequence != sequence)