aboutsummaryrefslogtreecommitdiffstats
diff options
authorCarlos Maiolino <cem@kernel.org>2025-02-20 13:32:44 +0100
committerCarlos Maiolino <cem@kernel.org>2025-02-20 13:32:44 +0100
commit90430e39422c2efc34b3dbe3e27a9260a54eba39 (patch)
tree8ab669640ccd2c11280fd4570c7c8648e35c8d79
parent53652c0c79b90ae49ef5dfd113f83111d7df8671 (diff)
downloadxfs-linux-testing.tar.gz
patch move_chechpoint_to_ailtesting
-rw-r--r--fs/xfs/xfs_log_cil.c212
-rw-r--r--fs/xfs/xfs_trans_ail.c172
-rw-r--r--fs/xfs/xfs_trans_priv.h5
3 files changed, 194 insertions, 195 deletions
diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c
index b797586958c998..7e47cb74c6a31a 100644
--- a/fs/xfs/xfs_log_cil.c
+++ b/fs/xfs/xfs_log_cil.c
@@ -689,193 +689,6 @@ xlog_cil_insert_items(
}
}
-static inline void
-xlog_cil_ail_insert_batch(
- struct xfs_ail *ailp,
- struct xfs_ail_cursor *cur,
- struct xfs_log_item **log_items,
- int nr_items,
- xfs_lsn_t commit_lsn)
-{
- int i;
-
- spin_lock(&ailp->ail_lock);
- /* xfs_trans_ail_update_bulk drops ailp->ail_lock */
- xfs_trans_ail_update_bulk(ailp, cur, log_items, nr_items, commit_lsn);
-
- for (i = 0; i < nr_items; i++) {
- struct xfs_log_item *lip = log_items[i];
-
- if (lip->li_ops->iop_unpin)
- lip->li_ops->iop_unpin(lip, 0);
- }
-}
-
-/*
- * Take the checkpoint's log vector chain of items and insert the attached log
- * items into the AIL. This uses bulk insertion techniques to minimise AIL lock
- * traffic.
- *
- * The AIL tracks log items via the start record LSN of the checkpoint,
- * not the commit record LSN. This is because we can pipeline multiple
- * checkpoints, and so the start record of checkpoint N+1 can be
- * written before the commit record of checkpoint N. i.e:
- *
- * start N commit N
- * +-------------+------------+----------------+
- * start N+1 commit N+1
- *
- * The tail of the log cannot be moved to the LSN of commit N when all
- * the items of that checkpoint are written back, because then the
- * start record for N+1 is no longer in the active portion of the log
- * and recovery will fail/corrupt the filesystem.
- *
- * Hence when all the log items in checkpoint N are written back, the
- * tail of the log most now only move as far forwards as the start LSN
- * of checkpoint N+1.
- *
- * If we are called with the aborted flag set, it is because a log write during
- * a CIL checkpoint commit has failed. In this case, all the items in the
- * checkpoint have already gone through iop_committed and iop_committing, which
- * means that checkpoint commit abort handling is treated exactly the same as an
- * iclog write error even though we haven't started any IO yet. Hence in this
- * case all we need to do is iop_committed processing, followed by an
- * iop_unpin(aborted) call.
- *
- * The AIL cursor is used to optimise the insert process. If commit_lsn is not
- * at the end of the AIL, the insert cursor avoids the need to walk the AIL to
- * find the insertion point on every xfs_log_item_batch_insert() call. This
- * saves a lot of needless list walking and is a net win, even though it
- * slightly increases that amount of AIL lock traffic to set it up and tear it
- * down.
- */
-static void
-xlog_cil_ail_insert(
- struct xlog_chkpt *ctx,
- bool aborted)
-{
-#define LOG_ITEM_BATCH_SIZE 32
- struct xfs_ail *ailp = ctx->cil->xc_log->l_ailp;
- struct xfs_log_item *log_items[LOG_ITEM_BATCH_SIZE];
- struct xfs_log_vec *lv;
- struct xfs_ail_cursor cur;
- xfs_lsn_t old_head;
- int i = 0;
-
- /*
- * Update the AIL head LSN with the commit record LSN of this
- * checkpoint. As iclogs are always completed in order, this should
- * always be the same (as iclogs can contain multiple commit records) or
- * higher LSN than the current head. We do this before insertion of the
- * items so that log space checks during insertion will reflect the
- * space that this checkpoint has already consumed. We call
- * xfs_ail_update_finish() so that tail space and space-based wakeups
- * will be recalculated appropriately.
- */
- ASSERT(XFS_LSN_CMP(ctx->commit_lsn, ailp->ail_head_lsn) >= 0 ||
- aborted);
- spin_lock(&ailp->ail_lock);
- xfs_trans_ail_cursor_last(ailp, &cur, ctx->start_lsn);
- old_head = ailp->ail_head_lsn;
- ailp->ail_head_lsn = ctx->commit_lsn;
- /* xfs_ail_update_finish() drops the ail_lock */
- xfs_ail_update_finish(ailp, NULLCOMMITLSN);
-
- /*
- * We move the AIL head forwards to account for the space used in the
- * log before we remove that space from the grant heads. This prevents a
- * transient condition where reservation space appears to become
- * available on return, only for it to disappear again immediately as
- * the AIL head update accounts in the log tail space.
- */
- smp_wmb(); /* paired with smp_rmb in xlog_grant_space_left */
- xlog_grant_return_space(ailp->ail_log, old_head, ailp->ail_head_lsn);
-
- /* unpin all the log items */
- list_for_each_entry(lv, &ctx->lv_chain, lv_list) {
- struct xfs_log_item *lip = lv->lv_item;
- xfs_lsn_t item_lsn;
-
- if (aborted)
- set_bit(XFS_LI_ABORTED, &lip->li_flags);
-
- if (lip->li_ops->flags & XFS_ITEM_RELEASE_WHEN_COMMITTED) {
- lip->li_ops->iop_release(lip);
- continue;
- }
-
- if (lip->li_ops->iop_committed)
- item_lsn = lip->li_ops->iop_committed(lip,
- ctx->start_lsn);
- else
- item_lsn = ctx->start_lsn;
-
- /* item_lsn of -1 means the item needs no further processing */
- if (XFS_LSN_CMP(item_lsn, (xfs_lsn_t)-1) == 0)
- continue;
-
- /*
- * if we are aborting the operation, no point in inserting the
- * object into the AIL as we are in a shutdown situation.
- */
- if (aborted) {
- ASSERT(xlog_is_shutdown(ailp->ail_log));
- if (lip->li_ops->iop_unpin)
- lip->li_ops->iop_unpin(lip, 1);
- continue;
- }
-
- if (item_lsn != ctx->start_lsn) {
-
- /*
- * Not a bulk update option due to unusual item_lsn.
- * Push into AIL immediately, rechecking the lsn once
- * we have the ail lock. Then unpin the item. This does
- * not affect the AIL cursor the bulk insert path is
- * using.
- */
- spin_lock(&ailp->ail_lock);
- if (XFS_LSN_CMP(item_lsn, lip->li_lsn) > 0)
- xfs_trans_ail_update(ailp, lip, item_lsn);
- else
- spin_unlock(&ailp->ail_lock);
- if (lip->li_ops->iop_unpin)
- lip->li_ops->iop_unpin(lip, 0);
- continue;
- }
-
- /* Item is a candidate for bulk AIL insert. */
- log_items[i++] = lv->lv_item;
- if (i >= LOG_ITEM_BATCH_SIZE) {
- xlog_cil_ail_insert_batch(ailp, &cur, log_items,
- LOG_ITEM_BATCH_SIZE, ctx->start_lsn);
- i = 0;
- }
- }
-
- /* make sure we insert the remainder! */
- if (i)
- xlog_cil_ail_insert_batch(ailp, &cur, log_items, i,
- ctx->start_lsn);
-
- spin_lock(&ailp->ail_lock);
- xfs_trans_ail_cursor_done(&cur);
- spin_unlock(&ailp->ail_lock);
-}
-
-static void
-xlog_cil_free_logvec(
- struct list_head *lv_chain)
-{
- struct xfs_log_vec *lv;
-
- while (!list_empty(lv_chain)) {
- lv = list_first_entry(lv_chain, struct xfs_log_vec, lv_list);
- list_del_init(&lv->lv_list);
- kvfree(lv);
- }
-}
-
/*
* Mark all items committed and clear busy extents. We free the log vector
* chains in a separate pass so that we unpin the log items as quickly as
@@ -912,20 +725,29 @@ xlog_cil_committed(
spin_unlock(&ctx->cil->xc_push_lock);
}
- xlog_cil_ail_insert(ctx, abort);
+ /*
+ * Remove the checkpoint context from the committing list while we still
+ * owns it. I.e. before we transfer it to the AIL code.
+ *
+ * XXX: By code analysis, removing the context from the committing list
+ * doesn't seem to have any bad impact on it, and we prevent messing
+ * around with the context after the transfer.
+ * I don't think it's a good idea to do this in the AIL code, as I'd
+ * rather keep all cil push lock usage self contained within cil code.
+ * Otherwise, a helper for the AIL to call in after all the transfer
+ * has been done looks pertinent.
+ */
+ spin_lock(&ctx->cil->xc_push_lock);
+ list_del(&ctx->committing);
+ spin_unlock(&ctx->cil->xc_push_lock);
+
+ xfs_trans_ail_chkpt_transfer(ctx, abort);
xfs_extent_busy_sort(&extents->extent_list);
xfs_extent_busy_clear(&extents->extent_list,
xfs_has_discard(mp) && !abort);
- spin_lock(&ctx->cil->xc_push_lock);
- list_del(&ctx->committing);
- spin_unlock(&ctx->cil->xc_push_lock);
-
- xlog_cil_free_logvec(&ctx->lv_chain);
xfs_discard_extents(mp, extents);
-
- kfree(ctx);
}
void
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c
index 0fcb1828e598fb..6a896c63c5d411 100644
--- a/fs/xfs/xfs_trans_ail.c
+++ b/fs/xfs/xfs_trans_ail.c
@@ -863,6 +863,178 @@ xfs_trans_ail_insert(
xfs_trans_ail_update_bulk(ailp, NULL, &lip, 1, lsn);
}
+static inline void
+xfs_trans_ail_insert_batch(
+ struct xfs_ail *ailp,
+ struct xfs_ail_cursor *cur,
+ struct xfs_log_item **log_items,
+ int nr_items,
+ xfs_lsn_t commit_lsn)
+{
+ int i;
+
+ spin_lock(&ailp->ail_lock);
+ xfs_trans_ail_update_bulk(ailp, cur, log_items, nr_items, commit_lsn);
+
+ for (i = 0; i < nr_items; i++) {
+ struct xfs_log_item *lip = log_items[i];
+
+ if (lip->li_ops->iop_unpin)
+ lip->li_ops->iop_unpin(lip, 0);
+ }
+}
+
+/*
+ * Take the checkpoint's context from CIL and transfer the log items
+ * (linked through their log vectors) to the AIL.
+ *
+ * Once it's called, the checkpoint context can't be access by the CIL
+ * anymore.
+ *
+ * This uses bulk insertion techniques to minimise AIL lock traffic.
+ *
+ * The AIL tracks log items via the start record LSN of the checkpoint,
+ * not the commit record LSN. This is because we can pipeline multiple
+ * checkpoints, and so the start record of checkpoint N+1 can be written
+ * before the commit record of checkpoint N. i.e:
+ *
+ * start N commit N
+ * +-------------+------------+----------------+
+ * start N+1 commit N+1
+ *
+ * The tail of the log cannot be moved to the LSN of commit N when all
+ * the items of that checkpoint are written back, because then, the
+ * start record for N+1 is no longer in the active portion of the log
+ * and recovery will fail/corrupt the filesystem.
+ *
+ * Hence when all the log items in checkpoint N are written back, the
+ * tail of the log must now only move as far forwards as the start LSN
+ * of checkpoint N+1.
+ *
+ * If we are called with the aborted flag set, it is because a log write during
+ * a CIL checkpoint commit has failed. In this case, all the items in the
+ * checkpoint have already gone through iop_committed and iop_committing, which
+ * means that checkpoint commit abort handling is treated exactly the same as an
+ * iclog write error, even though we haven't started any IO yet. Hence in this
+ * case, all we need to do is iop_committed processing, followed by an
+ * iop_unpin(aborted) call.
+ *
+ * The AIL cursor is used to optimise the insert process. If commit_lsn is not
+ * at the end of the AIL, the insert cursor avoids the need to walk the AIL to
+ * find the insertion point on every xfs_trans_ail_insert_batch() call. This
+ * saves a lot of needless list walking and is a net win, even though it
+ * slightly increases the amount of AIL lock traffic to set it up and tear it
+ * down.
+ */
+void
+xfs_trans_ail_chkpt_transfer(
+ struct xlog_chkpt *chkpt,
+ bool aborted)
+{
+# define LOG_ITEM_BATCH_SIZE 32
+ struct xfs_ail *ailp = chkpt->cil->xc_log->l_ailp;
+ struct xfs_log_item *log_items[LOG_ITEM_BATCH_SIZE];
+ struct xfs_log_vec *lv;
+ struct xfs_ail_cursor cur;
+ xfs_lsn_t old_head;
+ int i = 0;
+
+ ASSERT(XFS_LSN_CMP(chkpt->commit_lsn, ailp->ail_head_lsn) >= 0 ||
+ aborted);
+
+ spin_lock(&ailp->ail_lock);
+ xfs_trans_ail_cursor_last(ailp, &cur, chkpt->start_lsn);
+ old_head = ailp->ail_head_lsn;
+ ailp->ail_head_lsn = chkpt->commit_lsn;
+
+ /* xfs_ail_update_finish() dropts the ail_lock */
+ xfs_ail_update_finish(ailp, NULLCOMMITLSN);
+
+ /* XXX: If we are returning the space here, where we took it? */
+ smp_wmb(); /* paired with smp_rmb in xlog_grant_space_left */
+ xlog_grant_return_space(ailp->ail_log, old_head, ailp->ail_head_lsn);
+
+ /* unpin all the log items with log vectors in this checkpoint */
+ list_for_each_entry(lv, &chkpt->lv_chain, lv_list) {
+ struct xfs_log_item *lip = lv->lv_item;
+ xfs_lsn_t item_lsn;
+
+ if (aborted)
+ set_bit(XFS_LI_ABORTED, &lip->li_flags);
+
+ if (lip->li_ops->flags & XFS_ITEM_RELEASE_WHEN_COMMITTED) {
+ lip->li_ops->iop_release(lip);
+ continue;
+ }
+
+ if (lip->li_ops->iop_committed)
+ item_lsn = lip->li_ops->iop_committed(lip,
+ chkpt->start_lsn);
+ else
+ item_lsn = chkpt->start_lsn;
+
+ if (XFS_LSN_CMP(item_lsn, (xfs_lsn_t)-1) == 0)
+ continue;
+
+ if (aborted) {
+ ASSERT(xlog_is_shutdown(ailp->ail_log));
+ if (lip->li_ops->iop_unpin)
+ lip->li_ops->iop_unpin(lip, 1);
+ continue;
+ }
+
+ if (item_lsn != chkpt->start_lsn) {
+ spin_lock(&ailp->ail_lock);
+ if (XFS_LSN_CMP(item_lsn, lip->li_lsn) > 0)
+ xfs_trans_ail_update(ailp, lip, item_lsn);
+ else
+ spin_unlock(&ailp->ail_lock);
+
+ if (lip->li_ops->iop_unpin)
+ lip->li_ops->iop_unpin(lip, 0);
+
+ continue;
+ }
+
+ log_items[i++] = lv->lv_item;
+ if (i >= LOG_ITEM_BATCH_SIZE) {
+ xfs_trans_ail_insert_batch(ailp, &cur, log_items,
+ LOG_ITEM_BATCH_SIZE, chkpt->start_lsn);
+ i = 0;
+ }
+ }
+
+ if (i)
+ xfs_trans_ail_insert_batch(ailp, &cur, log_items, i,
+ chkpt->start_lsn);
+
+ spin_lock(&ailp->ail_lock);
+ xfs_trans_ail_cursor_done(&cur);
+ spin_unlock(&ailp->ail_lock);
+
+ /*
+ * We are done with the checkpoint context, clean up the lv_chain.
+ *
+ * This opencode the old xlog_cil_free_logvec()
+ *
+ * Reusing lv pointer here as has no usage anymore, instead of
+ * defining a new pointer just for it
+ *
+ * XXX: We are freeing the log vector here, is this safe? Don't AIL code
+ * uses it to push the item to the log?
+ */
+
+ while (!list_empty(&chkpt->lv_chain)) {
+ lv = list_first_entry(&chkpt->lv_chain, struct xfs_log_vec,
+ lv_list);
+
+ list_del_init(&lv->lv_list);
+ kvfree(lv);
+ }
+
+ kfree(chkpt);
+}
+
/*
* Delete one log item from the AIL.
*
diff --git a/fs/xfs/xfs_trans_priv.h b/fs/xfs/xfs_trans_priv.h
index bd841df93021ff..7c26b6c592e5e8 100644
--- a/fs/xfs/xfs_trans_priv.h
+++ b/fs/xfs/xfs_trans_priv.h
@@ -12,6 +12,7 @@ struct xfs_mount;
struct xfs_trans;
struct xfs_ail;
struct xfs_log_vec;
+struct xlog_chkpt;
void xfs_trans_init(struct xfs_mount *);
@@ -73,6 +74,7 @@ void xfs_trans_ail_update_bulk(struct xfs_ail *ailp,
struct xfs_ail_cursor *cur,
struct xfs_log_item **log_items, int nr_items,
xfs_lsn_t lsn) __releases(ailp->ail_lock);
+
/*
* Return a pointer to the first item in the AIL. If the AIL is empty, then
* return NULL.
@@ -102,6 +104,9 @@ void xfs_ail_update_finish(struct xfs_ail *ailp, xfs_lsn_t old_lsn)
__releases(ailp->ail_lock);
void xfs_trans_ail_delete(struct xfs_log_item *lip, int shutdown_type);
+/* Move the checkpoint context into the AIL code */
+void xfs_trans_ail_chkpt_transfer(struct xlog_chkpt *chkpt, bool aborted);
+
static inline void xfs_ail_push(struct xfs_ail *ailp)
{
wake_up_process(ailp->ail_task);