* [PATCH CANDIDATE v6.1 1/5] xfs: hoist refcount record merge predicates
@ 2023-08-04 17:12 Theodore Ts'o
2023-08-04 17:12 ` [PATCH CANDIDATE v6.1 2/5] xfs: estimate post-merge refcounts correctly Theodore Ts'o
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Theodore Ts'o @ 2023-08-04 17:12 UTC (permalink / raw)
To: linux-xfs
Cc: amir73il, djwong, chandan.babu, leah.rumancik, Dave Chinner,
Xiao Yang
From: "Darrick J. Wong" <djwong@kernel.org>
commit 9d720a5a658f5135861773f26e927449bef93d61 upstream.
Hoist these multiline conditionals into separate static inline helpers
to improve readability and set the stage for corruption fixes that will
be introduced in the next patch.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Xiao Yang <yangx.jy@fujitsu.com>
---
fs/xfs/libxfs/xfs_refcount.c | 129 ++++++++++++++++++++++++++++++-----
1 file changed, 113 insertions(+), 16 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c
index 3f34bafe18dd..4408893333a6 100644
--- a/fs/xfs/libxfs/xfs_refcount.c
+++ b/fs/xfs/libxfs/xfs_refcount.c
@@ -815,11 +815,119 @@ xfs_refcount_find_right_extents(
/* Is this extent valid? */
static inline bool
xfs_refc_valid(
- struct xfs_refcount_irec *rc)
+ const struct xfs_refcount_irec *rc)
{
return rc->rc_startblock != NULLAGBLOCK;
}
+static inline bool
+xfs_refc_want_merge_center(
+ const struct xfs_refcount_irec *left,
+ const struct xfs_refcount_irec *cleft,
+ const struct xfs_refcount_irec *cright,
+ const struct xfs_refcount_irec *right,
+ bool cleft_is_cright,
+ enum xfs_refc_adjust_op adjust,
+ unsigned long long *ulenp)
+{
+ unsigned long long ulen = left->rc_blockcount;
+
+ /*
+ * To merge with a center record, both shoulder records must be
+ * adjacent to the record we want to adjust. This is only true if
+ * find_left and find_right made all four records valid.
+ */
+ if (!xfs_refc_valid(left) || !xfs_refc_valid(right) ||
+ !xfs_refc_valid(cleft) || !xfs_refc_valid(cright))
+ return false;
+
+ /* There must only be one record for the entire range. */
+ if (!cleft_is_cright)
+ return false;
+
+ /* The shoulder record refcounts must match the new refcount. */
+ if (left->rc_refcount != cleft->rc_refcount + adjust)
+ return false;
+ if (right->rc_refcount != cleft->rc_refcount + adjust)
+ return false;
+
+ /*
+ * The new record cannot exceed the max length. ulen is a ULL as the
+ * individual record block counts can be up to (u32 - 1) in length
+ * hence we need to catch u32 addition overflows here.
+ */
+ ulen += cleft->rc_blockcount + right->rc_blockcount;
+ if (ulen >= MAXREFCEXTLEN)
+ return false;
+
+ *ulenp = ulen;
+ return true;
+}
+
+static inline bool
+xfs_refc_want_merge_left(
+ const struct xfs_refcount_irec *left,
+ const struct xfs_refcount_irec *cleft,
+ enum xfs_refc_adjust_op adjust)
+{
+ unsigned long long ulen = left->rc_blockcount;
+
+ /*
+ * For a left merge, the left shoulder record must be adjacent to the
+ * start of the range. If this is true, find_left made left and cleft
+ * contain valid contents.
+ */
+ if (!xfs_refc_valid(left) || !xfs_refc_valid(cleft))
+ return false;
+
+ /* Left shoulder record refcount must match the new refcount. */
+ if (left->rc_refcount != cleft->rc_refcount + adjust)
+ return false;
+
+ /*
+ * The new record cannot exceed the max length. ulen is a ULL as the
+ * individual record block counts can be up to (u32 - 1) in length
+ * hence we need to catch u32 addition overflows here.
+ */
+ ulen += cleft->rc_blockcount;
+ if (ulen >= MAXREFCEXTLEN)
+ return false;
+
+ return true;
+}
+
+static inline bool
+xfs_refc_want_merge_right(
+ const struct xfs_refcount_irec *cright,
+ const struct xfs_refcount_irec *right,
+ enum xfs_refc_adjust_op adjust)
+{
+ unsigned long long ulen = right->rc_blockcount;
+
+ /*
+ * For a right merge, the right shoulder record must be adjacent to the
+ * end of the range. If this is true, find_right made cright and right
+ * contain valid contents.
+ */
+ if (!xfs_refc_valid(right) || !xfs_refc_valid(cright))
+ return false;
+
+ /* Right shoulder record refcount must match the new refcount. */
+ if (right->rc_refcount != cright->rc_refcount + adjust)
+ return false;
+
+ /*
+ * The new record cannot exceed the max length. ulen is a ULL as the
+ * individual record block counts can be up to (u32 - 1) in length
+ * hence we need to catch u32 addition overflows here.
+ */
+ ulen += cright->rc_blockcount;
+ if (ulen >= MAXREFCEXTLEN)
+ return false;
+
+ return true;
+}
+
/*
* Try to merge with any extents on the boundaries of the adjustment range.
*/
@@ -861,23 +969,15 @@ xfs_refcount_merge_extents(
(cleft.rc_blockcount == cright.rc_blockcount);
/* Try to merge left, cleft, and right. cleft must == cright. */
- ulen = (unsigned long long)left.rc_blockcount + cleft.rc_blockcount +
- right.rc_blockcount;
- if (xfs_refc_valid(&left) && xfs_refc_valid(&right) &&
- xfs_refc_valid(&cleft) && xfs_refc_valid(&cright) && cequal &&
- left.rc_refcount == cleft.rc_refcount + adjust &&
- right.rc_refcount == cleft.rc_refcount + adjust &&
- ulen < MAXREFCEXTLEN) {
+ if (xfs_refc_want_merge_center(&left, &cleft, &cright, &right, cequal,
+ adjust, &ulen)) {
*shape_changed = true;
return xfs_refcount_merge_center_extents(cur, &left, &cleft,
&right, ulen, aglen);
}
/* Try to merge left and cleft. */
- ulen = (unsigned long long)left.rc_blockcount + cleft.rc_blockcount;
- if (xfs_refc_valid(&left) && xfs_refc_valid(&cleft) &&
- left.rc_refcount == cleft.rc_refcount + adjust &&
- ulen < MAXREFCEXTLEN) {
+ if (xfs_refc_want_merge_left(&left, &cleft, adjust)) {
*shape_changed = true;
error = xfs_refcount_merge_left_extent(cur, &left, &cleft,
agbno, aglen);
@@ -893,10 +993,7 @@ xfs_refcount_merge_extents(
}
/* Try to merge cright and right. */
- ulen = (unsigned long long)right.rc_blockcount + cright.rc_blockcount;
- if (xfs_refc_valid(&right) && xfs_refc_valid(&cright) &&
- right.rc_refcount == cright.rc_refcount + adjust &&
- ulen < MAXREFCEXTLEN) {
+ if (xfs_refc_want_merge_right(&cright, &right, adjust)) {
*shape_changed = true;
return xfs_refcount_merge_right_extent(cur, &right, &cright,
aglen);
--
2.31.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH CANDIDATE v6.1 2/5] xfs: estimate post-merge refcounts correctly
2023-08-04 17:12 [PATCH CANDIDATE v6.1 1/5] xfs: hoist refcount record merge predicates Theodore Ts'o
@ 2023-08-04 17:12 ` Theodore Ts'o
2023-08-04 17:12 ` [PATCH CANDIDATE v6.1 3/5] xfs: stabilize the dirent name transformation function used for ascii-ci dir hash computation Theodore Ts'o
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Theodore Ts'o @ 2023-08-04 17:12 UTC (permalink / raw)
To: linux-xfs
Cc: amir73il, djwong, chandan.babu, leah.rumancik, Dave Chinner,
Xiao Yang
From: "Darrick J. Wong" <djwong@kernel.org>
commit b25d1984aa884fc91a73a5a407b9ac976d441e9b upstream.
Upon enabling fsdax + reflink for XFS, xfs/179 began to report refcount
metadata corruptions after being run. Specifically, xfs_repair noticed
single-block refcount records that could be combined but had not been.
The root cause of this is improper MAXREFCOUNT edge case handling in
xfs_refcount_merge_extents. When we're trying to find candidates for a
refcount btree record merge, we compute the refcount attribute of the
merged record, but we fail to account for the fact that once a record
hits rc_refcount == MAXREFCOUNT, it is pinned that way forever. Hence
the computed refcount is wrong, and we fail to merge the extents.
Fix this by adjusting the merge predicates to compute the adjusted
refcount correctly.
Fixes: 3172725814f9 ("xfs: adjust refcount of an extent of blocks in refcount btree")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Xiao Yang <yangx.jy@fujitsu.com>
---
fs/xfs/libxfs/xfs_refcount.c | 25 +++++++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c
index 4408893333a6..6f7ed9288fe4 100644
--- a/fs/xfs/libxfs/xfs_refcount.c
+++ b/fs/xfs/libxfs/xfs_refcount.c
@@ -820,6 +820,17 @@ xfs_refc_valid(
return rc->rc_startblock != NULLAGBLOCK;
}
+static inline xfs_nlink_t
+xfs_refc_merge_refcount(
+ const struct xfs_refcount_irec *irec,
+ enum xfs_refc_adjust_op adjust)
+{
+ /* Once a record hits MAXREFCOUNT, it is pinned there forever */
+ if (irec->rc_refcount == MAXREFCOUNT)
+ return MAXREFCOUNT;
+ return irec->rc_refcount + adjust;
+}
+
static inline bool
xfs_refc_want_merge_center(
const struct xfs_refcount_irec *left,
@@ -831,6 +842,7 @@ xfs_refc_want_merge_center(
unsigned long long *ulenp)
{
unsigned long long ulen = left->rc_blockcount;
+ xfs_nlink_t new_refcount;
/*
* To merge with a center record, both shoulder records must be
@@ -846,9 +858,10 @@ xfs_refc_want_merge_center(
return false;
/* The shoulder record refcounts must match the new refcount. */
- if (left->rc_refcount != cleft->rc_refcount + adjust)
+ new_refcount = xfs_refc_merge_refcount(cleft, adjust);
+ if (left->rc_refcount != new_refcount)
return false;
- if (right->rc_refcount != cleft->rc_refcount + adjust)
+ if (right->rc_refcount != new_refcount)
return false;
/*
@@ -871,6 +884,7 @@ xfs_refc_want_merge_left(
enum xfs_refc_adjust_op adjust)
{
unsigned long long ulen = left->rc_blockcount;
+ xfs_nlink_t new_refcount;
/*
* For a left merge, the left shoulder record must be adjacent to the
@@ -881,7 +895,8 @@ xfs_refc_want_merge_left(
return false;
/* Left shoulder record refcount must match the new refcount. */
- if (left->rc_refcount != cleft->rc_refcount + adjust)
+ new_refcount = xfs_refc_merge_refcount(cleft, adjust);
+ if (left->rc_refcount != new_refcount)
return false;
/*
@@ -903,6 +918,7 @@ xfs_refc_want_merge_right(
enum xfs_refc_adjust_op adjust)
{
unsigned long long ulen = right->rc_blockcount;
+ xfs_nlink_t new_refcount;
/*
* For a right merge, the right shoulder record must be adjacent to the
@@ -913,7 +929,8 @@ xfs_refc_want_merge_right(
return false;
/* Right shoulder record refcount must match the new refcount. */
- if (right->rc_refcount != cright->rc_refcount + adjust)
+ new_refcount = xfs_refc_merge_refcount(cright, adjust);
+ if (right->rc_refcount != new_refcount)
return false;
/*
--
2.31.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH CANDIDATE v6.1 3/5] xfs: stabilize the dirent name transformation function used for ascii-ci dir hash computation
2023-08-04 17:12 [PATCH CANDIDATE v6.1 1/5] xfs: hoist refcount record merge predicates Theodore Ts'o
2023-08-04 17:12 ` [PATCH CANDIDATE v6.1 2/5] xfs: estimate post-merge refcounts correctly Theodore Ts'o
@ 2023-08-04 17:12 ` Theodore Ts'o
2023-08-04 17:12 ` [PATCH CANDIDATE v6.1 4/5] xfs: use the directory name hash function for dir scrubbing Theodore Ts'o
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Theodore Ts'o @ 2023-08-04 17:12 UTC (permalink / raw)
To: linux-xfs
Cc: amir73il, djwong, chandan.babu, leah.rumancik, Christoph Hellwig
From: "Darrick J. Wong" <djwong@kernel.org>
Back in the old days, the "ascii-ci" feature was created to implement
case-insensitive directory entry lookups for latin1-encoded names and
remove the large overhead of Samba's case-insensitive lookup code. UTF8
names were not allowed, but nobody explicitly wrote in the documentation
that this was only expected to work if the system used latin1 names.
The kernel tolower function was selected to prepare names for hashed
lookups.
There's a major discrepancy in the function that computes directory entry
hashes for filesystems that have ASCII case-insensitive lookups enabled.
The root of this is that the kernel and glibc's tolower implementations
have differing behavior for extended ASCII accented characters. I wrote
a program to spit out characters for which the tolower() return value is
different from the input:
glibc tolower:
65:A 66:B 67:C 68:D 69:E 70:F 71:G 72:H 73:I 74:J 75:K 76:L 77:M 78:N
79:O 80:P 81:Q 82:R 83:S 84:T 85:U 86:V 87:W 88:X 89:Y 90:Z
kernel tolower:
65:A 66:B 67:C 68:D 69:E 70:F 71:G 72:H 73:I 74:J 75:K 76:L 77:M 78:N
79:O 80:P 81:Q 82:R 83:S 84:T 85:U 86:V 87:W 88:X 89:Y 90:Z 192:À 193:Á
194:Â 195:Ã 196:Ä 197:Å 198:Æ 199:Ç 200:È 201:É 202:Ê 203:Ë 204:Ì 205:Í
206:Î 207:Ï 208:Ð 209:Ñ 210:Ò 211:Ó 212:Ô 213:Õ 214:Ö 215:× 216:Ø 217:Ù
218:Ú 219:Û 220:Ü 221:Ý 222:Þ
Which means that the kernel and userspace do not agree on the hash value
for a directory filename that contains those higher values. The hash
values are written into the leaf index block of directories that are
larger than two blocks in size, which means that xfs_repair will flag
these directories as having corrupted hash indexes and rewrite the index
with hash values that the kernel now will not recognize.
Because the ascii-ci feature is not frequently enabled and the kernel
touches filesystems far more frequently than xfs_repair does, fix this
by encoding the kernel's toupper predicate and tolower functions into
libxfs. Give the new functions less provocative names to make it really
obvious that this is a pre-hash name preparation function, and nothing
else. This change makes userspace's behavior consistent with the
kernel.
Found by auditing obfuscate_name in xfs_metadump as part of working on
parent pointers, wondering how it could possibly work correctly with ci
filesystems, writing a test tool to create a directory with
hash-colliding names, and watching xfs_repair flag it.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
(cherry picked from commit a9248538facc3d9e769489e50a544509c2f9cebe)
---
fs/xfs/libxfs/xfs_dir2.c | 5 +++--
fs/xfs/libxfs/xfs_dir2.h | 31 +++++++++++++++++++++++++++++++
2 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c
index 92bac3373f1f..f5462fd582d5 100644
--- a/fs/xfs/libxfs/xfs_dir2.c
+++ b/fs/xfs/libxfs/xfs_dir2.c
@@ -64,7 +64,7 @@ xfs_ascii_ci_hashname(
int i;
for (i = 0, hash = 0; i < name->len; i++)
- hash = tolower(name->name[i]) ^ rol32(hash, 7);
+ hash = xfs_ascii_ci_xfrm(name->name[i]) ^ rol32(hash, 7);
return hash;
}
@@ -85,7 +85,8 @@ xfs_ascii_ci_compname(
for (i = 0; i < len; i++) {
if (args->name[i] == name[i])
continue;
- if (tolower(args->name[i]) != tolower(name[i]))
+ if (xfs_ascii_ci_xfrm(args->name[i]) !=
+ xfs_ascii_ci_xfrm(name[i]))
return XFS_CMP_DIFFERENT;
result = XFS_CMP_CASE;
}
diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
index dd39f17dd9a9..19af22a16c41 100644
--- a/fs/xfs/libxfs/xfs_dir2.h
+++ b/fs/xfs/libxfs/xfs_dir2.h
@@ -248,4 +248,35 @@ unsigned int xfs_dir3_data_end_offset(struct xfs_da_geometry *geo,
struct xfs_dir2_data_hdr *hdr);
bool xfs_dir2_namecheck(const void *name, size_t length);
+/*
+ * The "ascii-ci" feature was created to speed up case-insensitive lookups for
+ * a Samba product. Because of the inherent problems with CI and UTF-8
+ * encoding, etc, it was decided that Samba would be configured to export
+ * latin1/iso 8859-1 encodings as that covered >90% of the target markets for
+ * the product. Hence the "ascii-ci" casefolding code could be encoded into
+ * the XFS directory operations and remove all the overhead of casefolding from
+ * Samba.
+ *
+ * To provide consistent hashing behavior between the userspace and kernel,
+ * these functions prepare names for hashing by transforming specific bytes
+ * to other bytes. Robustness with other encodings is not guaranteed.
+ */
+static inline bool xfs_ascii_ci_need_xfrm(unsigned char c)
+{
+ if (c >= 0x41 && c <= 0x5a) /* A-Z */
+ return true;
+ if (c >= 0xc0 && c <= 0xd6) /* latin A-O with accents */
+ return true;
+ if (c >= 0xd8 && c <= 0xde) /* latin O-Y with accents */
+ return true;
+ return false;
+}
+
+static inline unsigned char xfs_ascii_ci_xfrm(unsigned char c)
+{
+ if (xfs_ascii_ci_need_xfrm(c))
+ c -= 'A' - 'a';
+ return c;
+}
+
#endif /* __XFS_DIR2_H__ */
--
2.31.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH CANDIDATE v6.1 4/5] xfs: use the directory name hash function for dir scrubbing
2023-08-04 17:12 [PATCH CANDIDATE v6.1 1/5] xfs: hoist refcount record merge predicates Theodore Ts'o
2023-08-04 17:12 ` [PATCH CANDIDATE v6.1 2/5] xfs: estimate post-merge refcounts correctly Theodore Ts'o
2023-08-04 17:12 ` [PATCH CANDIDATE v6.1 3/5] xfs: stabilize the dirent name transformation function used for ascii-ci dir hash computation Theodore Ts'o
@ 2023-08-04 17:12 ` Theodore Ts'o
2023-08-04 17:12 ` [PATCH CANDIDATE v6.1 5/5] xfs: get root inode correctly at bulkstat Theodore Ts'o
2023-08-04 18:05 ` [PATCH CANDIDATE v6.1 1/5] xfs: hoist refcount record merge predicates Darrick J. Wong
4 siblings, 0 replies; 6+ messages in thread
From: Theodore Ts'o @ 2023-08-04 17:12 UTC (permalink / raw)
To: linux-xfs
Cc: amir73il, djwong, chandan.babu, leah.rumancik, Christoph Hellwig,
Dave Chinner
From: "Darrick J. Wong" <djwong@kernel.org>
The directory code has a directory-specific hash computation function
that includes a modified hash function for case-insensitive lookups.
Hence we must use that function (and not the raw da_hashname) when
checking the dabtree structure.
Found by accidentally breaking xfs/188 to create an abnormally huge
case-insensitive directory and watching scrub break.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
(cherry picked from commit 9dceccc5822f2ecea12a89f24d7cad1f3e5eab7c)
---
fs/xfs/scrub/dir.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c
index 5c87800ab223..ee29084ec44e 100644
--- a/fs/xfs/scrub/dir.c
+++ b/fs/xfs/scrub/dir.c
@@ -201,6 +201,7 @@ xchk_dir_rec(
struct xchk_da_btree *ds,
int level)
{
+ struct xfs_name dname = { };
struct xfs_da_state_blk *blk = &ds->state->path.blk[level];
struct xfs_mount *mp = ds->state->mp;
struct xfs_inode *dp = ds->dargs.dp;
@@ -297,7 +298,11 @@ xchk_dir_rec(
xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
goto out_relse;
}
- calc_hash = xfs_da_hashname(dent->name, dent->namelen);
+
+ /* Does the directory hash match? */
+ dname.name = dent->name;
+ dname.len = dent->namelen;
+ calc_hash = xfs_dir2_hashname(mp, &dname);
if (calc_hash != hash)
xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
--
2.31.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH CANDIDATE v6.1 5/5] xfs: get root inode correctly at bulkstat
2023-08-04 17:12 [PATCH CANDIDATE v6.1 1/5] xfs: hoist refcount record merge predicates Theodore Ts'o
` (2 preceding siblings ...)
2023-08-04 17:12 ` [PATCH CANDIDATE v6.1 4/5] xfs: use the directory name hash function for dir scrubbing Theodore Ts'o
@ 2023-08-04 17:12 ` Theodore Ts'o
2023-08-04 18:05 ` [PATCH CANDIDATE v6.1 1/5] xfs: hoist refcount record merge predicates Darrick J. Wong
4 siblings, 0 replies; 6+ messages in thread
From: Theodore Ts'o @ 2023-08-04 17:12 UTC (permalink / raw)
To: linux-xfs
Cc: amir73il, djwong, chandan.babu, leah.rumancik, Hironori Shiina,
Hironori Shiina
From: Hironori Shiina <shiina.hironori@gmail.com>
The root inode number should be set to `breq->startino` for getting stat
information of the root when XFS_BULK_IREQ_SPECIAL_ROOT is used.
Otherwise, the inode search is started from 1
(XFS_BULK_IREQ_SPECIAL_ROOT) and the inode with the lowest number in a
filesystem is returned.
Fixes: bf3cb3944792 ("xfs: allow single bulkstat of special inodes")
Signed-off-by: Hironori Shiina <shiina.hironori@fujitsu.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
(cherry picked from commit 817644fa4525258992f17fecf4f1d6cdd2e1b731)
---
fs/xfs/xfs_ioctl.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 1f783e979629..85fbb3b71d1c 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -754,7 +754,7 @@ xfs_bulkstat_fmt(
static int
xfs_bulk_ireq_setup(
struct xfs_mount *mp,
- struct xfs_bulk_ireq *hdr,
+ const struct xfs_bulk_ireq *hdr,
struct xfs_ibulk *breq,
void __user *ubuffer)
{
@@ -780,7 +780,7 @@ xfs_bulk_ireq_setup(
switch (hdr->ino) {
case XFS_BULK_IREQ_SPECIAL_ROOT:
- hdr->ino = mp->m_sb.sb_rootino;
+ breq->startino = mp->m_sb.sb_rootino;
break;
default:
return -EINVAL;
--
2.31.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH CANDIDATE v6.1 1/5] xfs: hoist refcount record merge predicates
2023-08-04 17:12 [PATCH CANDIDATE v6.1 1/5] xfs: hoist refcount record merge predicates Theodore Ts'o
` (3 preceding siblings ...)
2023-08-04 17:12 ` [PATCH CANDIDATE v6.1 5/5] xfs: get root inode correctly at bulkstat Theodore Ts'o
@ 2023-08-04 18:05 ` Darrick J. Wong
4 siblings, 0 replies; 6+ messages in thread
From: Darrick J. Wong @ 2023-08-04 18:05 UTC (permalink / raw)
To: Theodore Ts'o
Cc: linux-xfs, amir73il, chandan.babu, leah.rumancik, Dave Chinner,
Xiao Yang
On Fri, Aug 04, 2023 at 01:12:19PM -0400, Theodore Ts'o wrote:
> From: "Darrick J. Wong" <djwong@kernel.org>
>
> commit 9d720a5a658f5135861773f26e927449bef93d61 upstream.
>
> Hoist these multiline conditionals into separate static inline helpers
> to improve readability and set the stage for corruption fixes that will
> be introduced in the next patch.
>
> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> Reviewed-by: Dave Chinner <dchinner@redhat.com>
> Reviewed-by: Xiao Yang <yangx.jy@fujitsu.com>
For the whole series,
Acked-by: Darrick J. Wong <djwong@kernel.org>
--D
> ---
> fs/xfs/libxfs/xfs_refcount.c | 129 ++++++++++++++++++++++++++++++-----
> 1 file changed, 113 insertions(+), 16 deletions(-)
>
> diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c
> index 3f34bafe18dd..4408893333a6 100644
> --- a/fs/xfs/libxfs/xfs_refcount.c
> +++ b/fs/xfs/libxfs/xfs_refcount.c
> @@ -815,11 +815,119 @@ xfs_refcount_find_right_extents(
> /* Is this extent valid? */
> static inline bool
> xfs_refc_valid(
> - struct xfs_refcount_irec *rc)
> + const struct xfs_refcount_irec *rc)
> {
> return rc->rc_startblock != NULLAGBLOCK;
> }
>
> +static inline bool
> +xfs_refc_want_merge_center(
> + const struct xfs_refcount_irec *left,
> + const struct xfs_refcount_irec *cleft,
> + const struct xfs_refcount_irec *cright,
> + const struct xfs_refcount_irec *right,
> + bool cleft_is_cright,
> + enum xfs_refc_adjust_op adjust,
> + unsigned long long *ulenp)
> +{
> + unsigned long long ulen = left->rc_blockcount;
> +
> + /*
> + * To merge with a center record, both shoulder records must be
> + * adjacent to the record we want to adjust. This is only true if
> + * find_left and find_right made all four records valid.
> + */
> + if (!xfs_refc_valid(left) || !xfs_refc_valid(right) ||
> + !xfs_refc_valid(cleft) || !xfs_refc_valid(cright))
> + return false;
> +
> + /* There must only be one record for the entire range. */
> + if (!cleft_is_cright)
> + return false;
> +
> + /* The shoulder record refcounts must match the new refcount. */
> + if (left->rc_refcount != cleft->rc_refcount + adjust)
> + return false;
> + if (right->rc_refcount != cleft->rc_refcount + adjust)
> + return false;
> +
> + /*
> + * The new record cannot exceed the max length. ulen is a ULL as the
> + * individual record block counts can be up to (u32 - 1) in length
> + * hence we need to catch u32 addition overflows here.
> + */
> + ulen += cleft->rc_blockcount + right->rc_blockcount;
> + if (ulen >= MAXREFCEXTLEN)
> + return false;
> +
> + *ulenp = ulen;
> + return true;
> +}
> +
> +static inline bool
> +xfs_refc_want_merge_left(
> + const struct xfs_refcount_irec *left,
> + const struct xfs_refcount_irec *cleft,
> + enum xfs_refc_adjust_op adjust)
> +{
> + unsigned long long ulen = left->rc_blockcount;
> +
> + /*
> + * For a left merge, the left shoulder record must be adjacent to the
> + * start of the range. If this is true, find_left made left and cleft
> + * contain valid contents.
> + */
> + if (!xfs_refc_valid(left) || !xfs_refc_valid(cleft))
> + return false;
> +
> + /* Left shoulder record refcount must match the new refcount. */
> + if (left->rc_refcount != cleft->rc_refcount + adjust)
> + return false;
> +
> + /*
> + * The new record cannot exceed the max length. ulen is a ULL as the
> + * individual record block counts can be up to (u32 - 1) in length
> + * hence we need to catch u32 addition overflows here.
> + */
> + ulen += cleft->rc_blockcount;
> + if (ulen >= MAXREFCEXTLEN)
> + return false;
> +
> + return true;
> +}
> +
> +static inline bool
> +xfs_refc_want_merge_right(
> + const struct xfs_refcount_irec *cright,
> + const struct xfs_refcount_irec *right,
> + enum xfs_refc_adjust_op adjust)
> +{
> + unsigned long long ulen = right->rc_blockcount;
> +
> + /*
> + * For a right merge, the right shoulder record must be adjacent to the
> + * end of the range. If this is true, find_right made cright and right
> + * contain valid contents.
> + */
> + if (!xfs_refc_valid(right) || !xfs_refc_valid(cright))
> + return false;
> +
> + /* Right shoulder record refcount must match the new refcount. */
> + if (right->rc_refcount != cright->rc_refcount + adjust)
> + return false;
> +
> + /*
> + * The new record cannot exceed the max length. ulen is a ULL as the
> + * individual record block counts can be up to (u32 - 1) in length
> + * hence we need to catch u32 addition overflows here.
> + */
> + ulen += cright->rc_blockcount;
> + if (ulen >= MAXREFCEXTLEN)
> + return false;
> +
> + return true;
> +}
> +
> /*
> * Try to merge with any extents on the boundaries of the adjustment range.
> */
> @@ -861,23 +969,15 @@ xfs_refcount_merge_extents(
> (cleft.rc_blockcount == cright.rc_blockcount);
>
> /* Try to merge left, cleft, and right. cleft must == cright. */
> - ulen = (unsigned long long)left.rc_blockcount + cleft.rc_blockcount +
> - right.rc_blockcount;
> - if (xfs_refc_valid(&left) && xfs_refc_valid(&right) &&
> - xfs_refc_valid(&cleft) && xfs_refc_valid(&cright) && cequal &&
> - left.rc_refcount == cleft.rc_refcount + adjust &&
> - right.rc_refcount == cleft.rc_refcount + adjust &&
> - ulen < MAXREFCEXTLEN) {
> + if (xfs_refc_want_merge_center(&left, &cleft, &cright, &right, cequal,
> + adjust, &ulen)) {
> *shape_changed = true;
> return xfs_refcount_merge_center_extents(cur, &left, &cleft,
> &right, ulen, aglen);
> }
>
> /* Try to merge left and cleft. */
> - ulen = (unsigned long long)left.rc_blockcount + cleft.rc_blockcount;
> - if (xfs_refc_valid(&left) && xfs_refc_valid(&cleft) &&
> - left.rc_refcount == cleft.rc_refcount + adjust &&
> - ulen < MAXREFCEXTLEN) {
> + if (xfs_refc_want_merge_left(&left, &cleft, adjust)) {
> *shape_changed = true;
> error = xfs_refcount_merge_left_extent(cur, &left, &cleft,
> agbno, aglen);
> @@ -893,10 +993,7 @@ xfs_refcount_merge_extents(
> }
>
> /* Try to merge cright and right. */
> - ulen = (unsigned long long)right.rc_blockcount + cright.rc_blockcount;
> - if (xfs_refc_valid(&right) && xfs_refc_valid(&cright) &&
> - right.rc_refcount == cright.rc_refcount + adjust &&
> - ulen < MAXREFCEXTLEN) {
> + if (xfs_refc_want_merge_right(&cright, &right, adjust)) {
> *shape_changed = true;
> return xfs_refcount_merge_right_extent(cur, &right, &cright,
> aglen);
> --
> 2.31.0
>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2023-08-04 18:05 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-08-04 17:12 [PATCH CANDIDATE v6.1 1/5] xfs: hoist refcount record merge predicates Theodore Ts'o
2023-08-04 17:12 ` [PATCH CANDIDATE v6.1 2/5] xfs: estimate post-merge refcounts correctly Theodore Ts'o
2023-08-04 17:12 ` [PATCH CANDIDATE v6.1 3/5] xfs: stabilize the dirent name transformation function used for ascii-ci dir hash computation Theodore Ts'o
2023-08-04 17:12 ` [PATCH CANDIDATE v6.1 4/5] xfs: use the directory name hash function for dir scrubbing Theodore Ts'o
2023-08-04 17:12 ` [PATCH CANDIDATE v6.1 5/5] xfs: get root inode correctly at bulkstat Theodore Ts'o
2023-08-04 18:05 ` [PATCH CANDIDATE v6.1 1/5] xfs: hoist refcount record merge predicates Darrick J. Wong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox