|
@@ -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)
|
|
|
+ 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)
|
|
|
- xlog_cil_insert(log, ticket, lv->lv_item, lv);
|
|
|
+ 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
|
|
@@ -638,7 +657,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)
|