From: Brian Foster <bfoster@redhat.com>
To: linux-xfs@vger.kernel.org
Subject: [PATCH 09/12] xfs: elide the AIL lock on log item failure tracking
Date: Fri, 17 Apr 2020 11:08:56 -0400 [thread overview]
Message-ID: <20200417150859.14734-10-bfoster@redhat.com> (raw)
In-Reply-To: <20200417150859.14734-1-bfoster@redhat.com>
The log item failed flag is used to indicate a log item was flushed
but the underlying buffer was not successfully written to disk. If
the error configuration allows for writeback retries, xfsaild uses
the flag to resubmit the underlying buffer without acquiring the
flush lock of the item.
The flag is currently set and cleared under the AIL lock and buffer
lock in the ->iop_error() callback, invoked via ->b_iodone() at I/O
completion time. The flag is checked at xfsaild push time under AIL
lock and cleared under buffer lock before resubmission. If I/O
eventually succeeds, the flag is cleared in the _done() handler for
the associated item type, again under AIL lock and buffer lock.
As far as I can tell, the only reason for holding the AIL lock
across sets/clears is to manage consistency between the log item
bitop state and the temporary buffer pointer that is attached to the
log item. The bit itself is used to manage consistency of the
attached buffer, but is not enough to guarantee the buffer is still
attached by the time xfsaild attempts to access it. However since
failure state is always set or cleared under buffer lock (either via
I/O completion or xfsaild), this particular case can be handled at
item push time by verifying failure state once under buffer lock.
Remove the AIL lock protection from the various bits of log item
failure state management and simplify the surrounding code where
applicable. Use the buffer lock in the xfsaild resubmit code to
detect failure state changes and temporarily treat the item as
locked.
Signed-off-by: Brian Foster <bfoster@redhat.com>
---
fs/xfs/xfs_buf_item.c | 4 ----
fs/xfs/xfs_dquot.c | 41 +++++++++++++++--------------------------
fs/xfs/xfs_inode_item.c | 11 +++--------
fs/xfs/xfs_trans_ail.c | 12 ++++++++++--
fs/xfs/xfs_trans_priv.h | 5 -----
5 files changed, 28 insertions(+), 45 deletions(-)
diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c
index 72d37a4609d8..6b000f855e13 100644
--- a/fs/xfs/xfs_buf_item.c
+++ b/fs/xfs/xfs_buf_item.c
@@ -1046,7 +1046,6 @@ xfs_buf_do_callbacks_fail(
struct xfs_buf *bp)
{
struct xfs_log_item *lip;
- struct xfs_ail *ailp;
/*
* Buffer log item errors are handled directly by xfs_buf_item_push()
@@ -1058,13 +1057,10 @@ xfs_buf_do_callbacks_fail(
lip = list_first_entry(&bp->b_li_list, struct xfs_log_item,
li_bio_list);
- ailp = lip->li_ailp;
- spin_lock(&ailp->ail_lock);
list_for_each_entry(lip, &bp->b_li_list, li_bio_list) {
if (lip->li_ops->iop_error)
lip->li_ops->iop_error(lip, bp);
}
- spin_unlock(&ailp->ail_lock);
}
static bool
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
index 41750f797861..953059235130 100644
--- a/fs/xfs/xfs_dquot.c
+++ b/fs/xfs/xfs_dquot.c
@@ -1023,34 +1023,23 @@ xfs_qm_dqflush_done(
struct xfs_ail *ailp = lip->li_ailp;
/*
- * We only want to pull the item from the AIL if its
- * location in the log has not changed since we started the flush.
- * Thus, we only bother if the dquot's lsn has
- * not changed. First we check the lsn outside the lock
- * since it's cheaper, and then we recheck while
- * holding the lock before removing the dquot from the AIL.
+ * Only pull the item from the AIL if its location in the log has not
+ * changed since it was flushed. Do a lockless check first to reduce
+ * lock traffic.
*/
- if (test_bit(XFS_LI_IN_AIL, &lip->li_flags) &&
- ((lip->li_lsn == qip->qli_flush_lsn) ||
- test_bit(XFS_LI_FAILED, &lip->li_flags))) {
-
- /* xfs_trans_ail_delete() drops the AIL lock. */
- spin_lock(&ailp->ail_lock);
- if (lip->li_lsn == qip->qli_flush_lsn) {
- xfs_trans_ail_delete(ailp, lip, SHUTDOWN_CORRUPT_INCORE);
- } else {
- /*
- * Clear the failed state since we are about to drop the
- * flush lock
- */
- xfs_clear_li_failed(lip);
- spin_unlock(&ailp->ail_lock);
- }
- }
+ if (!test_bit(XFS_LI_IN_AIL, &lip->li_flags) ||
+ lip->li_lsn != qip->qli_flush_lsn)
+ goto out;
- /*
- * Release the dq's flush lock since we're done with it.
- */
+ spin_lock(&ailp->ail_lock);
+ if (lip->li_lsn == qip->qli_flush_lsn)
+ /* xfs_trans_ail_delete() drops the AIL lock */
+ xfs_trans_ail_delete(ailp, lip, SHUTDOWN_CORRUPT_INCORE);
+ else
+ spin_unlock(&ailp->ail_lock);
+
+out:
+ xfs_clear_li_failed(lip);
xfs_dqfunlock(dqp);
}
diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c
index 1d4d256a2e96..0ae61844b224 100644
--- a/fs/xfs/xfs_inode_item.c
+++ b/fs/xfs/xfs_inode_item.c
@@ -682,9 +682,7 @@ xfs_iflush_done(
* Scan the buffer IO completions for other inodes being completed and
* attach them to the current inode log item.
*/
-
list_add_tail(&lip->li_bio_list, &tmp);
-
list_for_each_entry_safe(blip, n, &bp->b_li_list, li_bio_list) {
if (lip->li_cb != xfs_iflush_done)
continue;
@@ -695,15 +693,13 @@ xfs_iflush_done(
* the AIL lock.
*/
iip = INODE_ITEM(blip);
- if ((iip->ili_logged && blip->li_lsn == iip->ili_flush_lsn) ||
- test_bit(XFS_LI_FAILED, &blip->li_flags))
+ if (iip->ili_logged && blip->li_lsn == iip->ili_flush_lsn)
need_ail++;
}
/* make sure we capture the state of the initial inode. */
iip = INODE_ITEM(lip);
- if ((iip->ili_logged && lip->li_lsn == iip->ili_flush_lsn) ||
- test_bit(XFS_LI_FAILED, &lip->li_flags))
+ if (iip->ili_logged && lip->li_lsn == iip->ili_flush_lsn)
need_ail++;
/*
@@ -732,8 +728,6 @@ xfs_iflush_done(
xfs_lsn_t lsn = xfs_ail_delete_one(ailp, blip);
if (!tail_lsn && lsn)
tail_lsn = lsn;
- } else {
- xfs_clear_li_failed(blip);
}
}
xfs_ail_update_finish(ailp, tail_lsn);
@@ -746,6 +740,7 @@ xfs_iflush_done(
*/
list_for_each_entry_safe(blip, n, &tmp, li_bio_list) {
list_del_init(&blip->li_bio_list);
+ xfs_clear_li_failed(blip);
iip = INODE_ITEM(blip);
iip->ili_logged = 0;
iip->ili_last_fields = 0;
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c
index 0c709651a2c6..6af609070143 100644
--- a/fs/xfs/xfs_trans_ail.c
+++ b/fs/xfs/xfs_trans_ail.c
@@ -368,15 +368,23 @@ xfsaild_push_failed(
{
struct xfs_buf *bp = lip->li_buf;
- if (!xfs_buf_trylock(bp))
+ /*
+ * Log item state bits are racy so we cannot assume the temporary buffer
+ * pointer is set. Treat the item as locked if the pointer is clear or
+ * the failure state has changed once we've locked out I/O completion.
+ */
+ if (!bp || !xfs_buf_trylock(bp))
return XFS_ITEM_LOCKED;
+ if (!test_bit(XFS_LI_FAILED, &lip->li_flags)) {
+ xfs_buf_unlock(bp);
+ return XFS_ITEM_LOCKED;
+ }
if (!xfs_buf_delwri_queue(bp, buffer_list)) {
xfs_buf_unlock(bp);
return XFS_ITEM_FLUSHING;
}
- /* protected by ail_lock */
list_for_each_entry(lip, &bp->b_li_list, li_bio_list)
xfs_clear_li_failed(lip);
diff --git a/fs/xfs/xfs_trans_priv.h b/fs/xfs/xfs_trans_priv.h
index 35655eac01a6..9135afdcee9d 100644
--- a/fs/xfs/xfs_trans_priv.h
+++ b/fs/xfs/xfs_trans_priv.h
@@ -158,9 +158,6 @@ xfs_clear_li_failed(
{
struct xfs_buf *bp = lip->li_buf;
- ASSERT(test_bit(XFS_LI_IN_AIL, &lip->li_flags));
- lockdep_assert_held(&lip->li_ailp->ail_lock);
-
if (test_and_clear_bit(XFS_LI_FAILED, &lip->li_flags)) {
lip->li_buf = NULL;
xfs_buf_rele(bp);
@@ -172,8 +169,6 @@ xfs_set_li_failed(
struct xfs_log_item *lip,
struct xfs_buf *bp)
{
- lockdep_assert_held(&lip->li_ailp->ail_lock);
-
if (!test_and_set_bit(XFS_LI_FAILED, &lip->li_flags)) {
xfs_buf_hold(bp);
lip->li_buf = bp;
--
2.21.1
next prev parent reply other threads:[~2020-04-17 15:09 UTC|newest]
Thread overview: 48+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-04-17 15:08 [PATCH 00/12] xfs: flush related error handling cleanups Brian Foster
2020-04-17 15:08 ` [PATCH 01/12] xfs: refactor failed buffer resubmission into xfsaild Brian Foster
2020-04-17 22:37 ` Allison Collins
2020-04-20 2:45 ` Dave Chinner
2020-04-20 13:58 ` Brian Foster
2020-04-20 22:19 ` Dave Chinner
2020-04-17 15:08 ` [PATCH 02/12] xfs: factor out buffer I/O failure simulation code Brian Foster
2020-04-17 22:37 ` Allison Collins
2020-04-20 2:48 ` Dave Chinner
2020-04-20 13:58 ` Brian Foster
2020-04-17 15:08 ` [PATCH 03/12] xfs: always attach iflush_done and simplify error handling Brian Foster
2020-04-18 0:07 ` Allison Collins
2020-04-20 13:59 ` Brian Foster
2020-04-20 3:08 ` Dave Chinner
2020-04-20 14:00 ` Brian Foster
2020-04-17 15:08 ` [PATCH 04/12] xfs: remove unnecessary shutdown check from xfs_iflush() Brian Foster
2020-04-18 0:27 ` Allison Collins
2020-04-20 3:10 ` Dave Chinner
2020-04-17 15:08 ` [PATCH 05/12] xfs: ratelimit unmount time per-buffer I/O error warning Brian Foster
2020-04-20 3:19 ` Dave Chinner
2020-04-20 14:02 ` Brian Foster
2020-04-20 22:23 ` Dave Chinner
2020-04-21 12:13 ` Brian Foster
2020-04-20 18:50 ` Allison Collins
2020-04-17 15:08 ` [PATCH 06/12] xfs: remove duplicate verification from xfs_qm_dqflush() Brian Foster
2020-04-20 3:53 ` Dave Chinner
2020-04-20 14:02 ` Brian Foster
2020-04-20 22:31 ` Dave Chinner
2020-04-17 15:08 ` [PATCH 07/12] xfs: abort consistently on dquot flush failure Brian Foster
2020-04-20 3:54 ` Dave Chinner
2020-04-20 18:50 ` Allison Collins
2020-04-17 15:08 ` [PATCH 08/12] xfs: remove unnecessary quotaoff intent item push handler Brian Foster
2020-04-20 3:58 ` Dave Chinner
2020-04-20 14:02 ` Brian Foster
2020-04-17 15:08 ` Brian Foster [this message]
2020-04-17 15:08 ` [PATCH 10/12] xfs: clean up AIL log item removal functions Brian Foster
2020-04-20 4:32 ` Dave Chinner
2020-04-20 14:03 ` Brian Foster
2020-04-17 15:08 ` [PATCH 11/12] xfs: remove unused iflush stale parameter Brian Foster
2020-04-20 4:34 ` Dave Chinner
2020-04-20 19:19 ` Allison Collins
2020-04-17 15:08 ` [PATCH 12/12] xfs: random buffer write failure errortag Brian Foster
2020-04-20 4:37 ` Dave Chinner
2020-04-20 14:04 ` Brian Foster
2020-04-20 22:42 ` Allison Collins
2020-04-19 22:53 ` [PATCH 00/12] xfs: flush related error handling cleanups Dave Chinner
2020-04-20 14:06 ` Brian Foster
2020-04-20 22:53 ` Dave Chinner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200417150859.14734-10-bfoster@redhat.com \
--to=bfoster@redhat.com \
--cc=linux-xfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).