From: "Darrick J. Wong" <djwong@kernel.org>
To: linux-xfs@vger.kernel.org, hch@lst.de, dchinner@redhat.com,
christian.brauner@ubuntu.com
Subject: [PATCH v2.1 2/4] xfs: avoid buffer deadlocks when walking fs inodes
Date: Sun, 7 Mar 2021 12:36:38 -0800 [thread overview]
Message-ID: <20210307203638.GJ3419940@magnolia> (raw)
In-Reply-To: <161514875165.698643.17020544838073213424.stgit@magnolia>
From: Darrick J. Wong <djwong@kernel.org>
When we're servicing an INUMBERS or BULKSTAT request or running
quotacheck, grab an empty transaction so that we can use its inherent
recursive buffer locking abilities to detect inode btree cycles without
hitting ABBA buffer deadlocks.
Found by fuzzing an inode btree pointer to introduce a cycle into the
tree (xfs/365).
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
v2.1: actually pass tp in the bulkstat_single case
---
fs/xfs/xfs_itable.c | 42 +++++++++++++++++++++++++++++++++++++-----
fs/xfs/xfs_iwalk.c | 32 +++++++++++++++++++++++++++-----
2 files changed, 64 insertions(+), 10 deletions(-)
diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c
index ca310a125d1e..326495c8910e 100644
--- a/fs/xfs/xfs_itable.c
+++ b/fs/xfs/xfs_itable.c
@@ -19,6 +19,7 @@
#include "xfs_error.h"
#include "xfs_icache.h"
#include "xfs_health.h"
+#include "xfs_trans.h"
/*
* Bulk Stat
@@ -166,6 +167,7 @@ xfs_bulkstat_one(
.formatter = formatter,
.breq = breq,
};
+ struct xfs_trans *tp;
int error;
ASSERT(breq->icount == 1);
@@ -175,9 +177,18 @@ xfs_bulkstat_one(
if (!bc.buf)
return -ENOMEM;
- error = xfs_bulkstat_one_int(breq->mp, breq->mnt_userns, NULL,
- breq->startino, &bc);
+ /*
+ * Grab an empty transaction so that we can use its recursive buffer
+ * locking abilities to detect cycles in the inobt without deadlocking.
+ */
+ error = xfs_trans_alloc_empty(breq->mp, &tp);
+ if (error)
+ goto out;
+ error = xfs_bulkstat_one_int(breq->mp, breq->mnt_userns, tp,
+ breq->startino, &bc);
+ xfs_trans_cancel(tp);
+out:
kmem_free(bc.buf);
/*
@@ -241,6 +252,7 @@ xfs_bulkstat(
.formatter = formatter,
.breq = breq,
};
+ struct xfs_trans *tp;
int error;
if (breq->mnt_userns != &init_user_ns) {
@@ -256,9 +268,18 @@ xfs_bulkstat(
if (!bc.buf)
return -ENOMEM;
- error = xfs_iwalk(breq->mp, NULL, breq->startino, breq->flags,
+ /*
+ * Grab an empty transaction so that we can use its recursive buffer
+ * locking abilities to detect cycles in the inobt without deadlocking.
+ */
+ error = xfs_trans_alloc_empty(breq->mp, &tp);
+ if (error)
+ goto out;
+
+ error = xfs_iwalk(breq->mp, tp, breq->startino, breq->flags,
xfs_bulkstat_iwalk, breq->icount, &bc);
-
+ xfs_trans_cancel(tp);
+out:
kmem_free(bc.buf);
/*
@@ -371,13 +392,24 @@ xfs_inumbers(
.formatter = formatter,
.breq = breq,
};
+ struct xfs_trans *tp;
int error = 0;
if (xfs_bulkstat_already_done(breq->mp, breq->startino))
return 0;
- error = xfs_inobt_walk(breq->mp, NULL, breq->startino, breq->flags,
+ /*
+ * Grab an empty transaction so that we can use its recursive buffer
+ * locking abilities to detect cycles in the inobt without deadlocking.
+ */
+ error = xfs_trans_alloc_empty(breq->mp, &tp);
+ if (error)
+ goto out;
+
+ error = xfs_inobt_walk(breq->mp, tp, breq->startino, breq->flags,
xfs_inumbers_walk, breq->icount, &ic);
+ xfs_trans_cancel(tp);
+out:
/*
* We found some inode groups, so clear the error status and return
diff --git a/fs/xfs/xfs_iwalk.c b/fs/xfs/xfs_iwalk.c
index c4a340f1f1e1..e1e889f3647f 100644
--- a/fs/xfs/xfs_iwalk.c
+++ b/fs/xfs/xfs_iwalk.c
@@ -81,6 +81,9 @@ struct xfs_iwalk_ag {
/* Skip empty inobt records? */
unsigned int skip_empty:1;
+
+ /* Drop the (hopefully empty) transaction when calling iwalk_fn. */
+ unsigned int drop_trans:1;
};
/*
@@ -351,7 +354,6 @@ xfs_iwalk_run_callbacks(
int *has_more)
{
struct xfs_mount *mp = iwag->mp;
- struct xfs_trans *tp = iwag->tp;
struct xfs_inobt_rec_incore *irec;
xfs_agino_t next_agino;
int error;
@@ -361,10 +363,15 @@ xfs_iwalk_run_callbacks(
ASSERT(iwag->nr_recs > 0);
/* Delete cursor but remember the last record we cached... */
- xfs_iwalk_del_inobt(tp, curpp, agi_bpp, 0);
+ xfs_iwalk_del_inobt(iwag->tp, curpp, agi_bpp, 0);
irec = &iwag->recs[iwag->nr_recs - 1];
ASSERT(next_agino >= irec->ir_startino + XFS_INODES_PER_CHUNK);
+ if (iwag->drop_trans) {
+ xfs_trans_cancel(iwag->tp);
+ iwag->tp = NULL;
+ }
+
error = xfs_iwalk_ag_recs(iwag);
if (error)
return error;
@@ -375,8 +382,14 @@ xfs_iwalk_run_callbacks(
if (!has_more)
return 0;
+ if (iwag->drop_trans) {
+ error = xfs_trans_alloc_empty(mp, &iwag->tp);
+ if (error)
+ return error;
+ }
+
/* ...and recreate the cursor just past where we left off. */
- error = xfs_inobt_cur(mp, tp, agno, XFS_BTNUM_INO, curpp, agi_bpp);
+ error = xfs_inobt_cur(mp, iwag->tp, agno, XFS_BTNUM_INO, curpp, agi_bpp);
if (error)
return error;
@@ -389,7 +402,6 @@ xfs_iwalk_ag(
struct xfs_iwalk_ag *iwag)
{
struct xfs_mount *mp = iwag->mp;
- struct xfs_trans *tp = iwag->tp;
struct xfs_buf *agi_bp = NULL;
struct xfs_btree_cur *cur = NULL;
xfs_agnumber_t agno;
@@ -469,7 +481,7 @@ xfs_iwalk_ag(
error = xfs_iwalk_run_callbacks(iwag, agno, &cur, &agi_bp, &has_more);
out:
- xfs_iwalk_del_inobt(tp, &cur, &agi_bp, error);
+ xfs_iwalk_del_inobt(iwag->tp, &cur, &agi_bp, error);
return error;
}
@@ -594,8 +606,18 @@ xfs_iwalk_ag_work(
error = xfs_iwalk_alloc(iwag);
if (error)
goto out;
+ /*
+ * Grab an empty transaction so that we can use its recursive buffer
+ * locking abilities to detect cycles in the inobt without deadlocking.
+ */
+ error = xfs_trans_alloc_empty(mp, &iwag->tp);
+ if (error)
+ goto out;
+ iwag->drop_trans = 1;
error = xfs_iwalk_ag(iwag);
+ if (iwag->tp)
+ xfs_trans_cancel(iwag->tp);
xfs_iwalk_free(iwag);
out:
kmem_free(iwag);
next prev parent reply other threads:[~2021-03-07 20:37 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-03-07 20:25 [PATCHSET v2 0/4] xfs: small fixes for 5.12 Darrick J. Wong
2021-03-07 20:25 ` [PATCH 1/4] xfs: fix quota accounting when a mount is idmapped Darrick J. Wong
2021-03-07 22:28 ` Dave Chinner
2021-03-07 20:25 ` [PATCH 2/4] xfs: avoid buffer deadlocks when walking fs inodes Darrick J. Wong
2021-03-07 20:36 ` Darrick J. Wong [this message]
2021-03-07 22:37 ` [PATCH v2.1 " Dave Chinner
2021-03-08 3:56 ` Darrick J. Wong
2021-03-08 7:56 ` Christoph Hellwig
2021-03-07 20:25 ` [PATCH 3/4] xfs: force log and push AIL to clear pinned inodes when aborting mount Darrick J. Wong
2021-03-07 23:01 ` Dave Chinner
2021-03-08 4:47 ` Darrick J. Wong
2021-03-08 4:48 ` [PATCH v2.1 " Darrick J. Wong
2021-03-08 9:16 ` Christoph Hellwig
2021-03-08 23:42 ` Dave Chinner
2021-03-08 7:53 ` [PATCH " Christoph Hellwig
2021-03-07 20:26 ` [PATCH 4/4] xfs: drop freeze protection when running GETFSMAP Darrick J. Wong
2021-03-07 23:05 ` Dave Chinner
2021-03-08 4:45 ` Darrick J. Wong
2021-03-08 7:55 ` Christoph Hellwig
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=20210307203638.GJ3419940@magnolia \
--to=djwong@kernel.org \
--cc=christian.brauner@ubuntu.com \
--cc=dchinner@redhat.com \
--cc=hch@lst.de \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.