Skip to content

Commit b1bdab2

Browse files
dchinnerdgchinner
authored andcommitted
Merge tag 'scrub-detect-rmapbt-gaps-6.4_2023-04-11' of git://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into guilt/xfs-for-next
xfs: detect incorrect gaps in rmap btree [v24.5] Following in the theme of the last two patchsets, this one strengthens the rmap btree record checking so that scrub can count the number of space records that map to a given owner and that do not map to a given owner. This enables us to determine exclusive ownership of space that can't be shared. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Dave Chinner <david@fromorbit.com>
2 parents f1121b9 + 30f8ee5 commit b1bdab2

9 files changed

Lines changed: 195 additions & 94 deletions

File tree

fs/xfs/libxfs/xfs_rmap.c

Lines changed: 133 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2735,65 +2735,141 @@ xfs_rmap_has_records(
27352735
return xfs_btree_has_records(cur, &low, &high, &mask, outcome);
27362736
}
27372737

2738-
/*
2739-
* Is there a record for this owner completely covering a given physical
2740-
* extent? If so, *has_rmap will be set to true. If there is no record
2741-
* or the record only covers part of the range, we set *has_rmap to false.
2742-
* This function doesn't perform range lookups or offset checks, so it is
2743-
* not suitable for checking data fork blocks.
2744-
*/
2745-
int
2746-
xfs_rmap_record_exists(
2747-
struct xfs_btree_cur *cur,
2738+
struct xfs_rmap_ownercount {
2739+
/* Owner that we're looking for. */
2740+
struct xfs_rmap_irec good;
2741+
2742+
/* rmap search keys */
2743+
struct xfs_rmap_irec low;
2744+
struct xfs_rmap_irec high;
2745+
2746+
struct xfs_rmap_matches *results;
2747+
2748+
/* Stop early if we find a nonmatch? */
2749+
bool stop_on_nonmatch;
2750+
};
2751+
2752+
/* Does this rmap represent space that can have multiple owners? */
2753+
static inline bool
2754+
xfs_rmap_shareable(
2755+
struct xfs_mount *mp,
2756+
const struct xfs_rmap_irec *rmap)
2757+
{
2758+
if (!xfs_has_reflink(mp))
2759+
return false;
2760+
if (XFS_RMAP_NON_INODE_OWNER(rmap->rm_owner))
2761+
return false;
2762+
if (rmap->rm_flags & (XFS_RMAP_ATTR_FORK |
2763+
XFS_RMAP_BMBT_BLOCK))
2764+
return false;
2765+
return true;
2766+
}
2767+
2768+
static inline void
2769+
xfs_rmap_ownercount_init(
2770+
struct xfs_rmap_ownercount *roc,
27482771
xfs_agblock_t bno,
27492772
xfs_extlen_t len,
27502773
const struct xfs_owner_info *oinfo,
2751-
bool *has_rmap)
2774+
struct xfs_rmap_matches *results)
27522775
{
2753-
uint64_t owner;
2754-
uint64_t offset;
2755-
unsigned int flags;
2756-
int has_record;
2757-
struct xfs_rmap_irec irec;
2758-
int error;
2776+
memset(roc, 0, sizeof(*roc));
2777+
roc->results = results;
2778+
2779+
roc->low.rm_startblock = bno;
2780+
memset(&roc->high, 0xFF, sizeof(roc->high));
2781+
roc->high.rm_startblock = bno + len - 1;
2782+
2783+
memset(results, 0, sizeof(*results));
2784+
roc->good.rm_startblock = bno;
2785+
roc->good.rm_blockcount = len;
2786+
roc->good.rm_owner = oinfo->oi_owner;
2787+
roc->good.rm_offset = oinfo->oi_offset;
2788+
if (oinfo->oi_flags & XFS_OWNER_INFO_ATTR_FORK)
2789+
roc->good.rm_flags |= XFS_RMAP_ATTR_FORK;
2790+
if (oinfo->oi_flags & XFS_OWNER_INFO_BMBT_BLOCK)
2791+
roc->good.rm_flags |= XFS_RMAP_BMBT_BLOCK;
2792+
}
27592793

2760-
xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
2761-
ASSERT(XFS_RMAP_NON_INODE_OWNER(owner) ||
2762-
(flags & XFS_RMAP_BMBT_BLOCK));
2794+
/* Figure out if this is a match for the owner. */
2795+
STATIC int
2796+
xfs_rmap_count_owners_helper(
2797+
struct xfs_btree_cur *cur,
2798+
const struct xfs_rmap_irec *rec,
2799+
void *priv)
2800+
{
2801+
struct xfs_rmap_ownercount *roc = priv;
2802+
struct xfs_rmap_irec check = *rec;
2803+
unsigned int keyflags;
2804+
bool filedata;
2805+
int64_t delta;
2806+
2807+
filedata = !XFS_RMAP_NON_INODE_OWNER(check.rm_owner) &&
2808+
!(check.rm_flags & XFS_RMAP_BMBT_BLOCK);
2809+
2810+
/* Trim the part of check that comes before the comparison range. */
2811+
delta = (int64_t)roc->good.rm_startblock - check.rm_startblock;
2812+
if (delta > 0) {
2813+
check.rm_startblock += delta;
2814+
check.rm_blockcount -= delta;
2815+
if (filedata)
2816+
check.rm_offset += delta;
2817+
}
27632818

2764-
error = xfs_rmap_lookup_le(cur, bno, owner, offset, flags, &irec,
2765-
&has_record);
2766-
if (error)
2767-
return error;
2768-
if (!has_record) {
2769-
*has_rmap = false;
2770-
return 0;
2819+
/* Trim the part of check that comes after the comparison range. */
2820+
delta = (check.rm_startblock + check.rm_blockcount) -
2821+
(roc->good.rm_startblock + roc->good.rm_blockcount);
2822+
if (delta > 0)
2823+
check.rm_blockcount -= delta;
2824+
2825+
/* Don't care about unwritten status for establishing ownership. */
2826+
keyflags = check.rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK);
2827+
2828+
if (check.rm_startblock == roc->good.rm_startblock &&
2829+
check.rm_blockcount == roc->good.rm_blockcount &&
2830+
check.rm_owner == roc->good.rm_owner &&
2831+
check.rm_offset == roc->good.rm_offset &&
2832+
keyflags == roc->good.rm_flags) {
2833+
roc->results->matches++;
2834+
} else {
2835+
roc->results->non_owner_matches++;
2836+
if (xfs_rmap_shareable(cur->bc_mp, &roc->good) ^
2837+
xfs_rmap_shareable(cur->bc_mp, &check))
2838+
roc->results->bad_non_owner_matches++;
27712839
}
27722840

2773-
*has_rmap = (irec.rm_owner == owner && irec.rm_startblock <= bno &&
2774-
irec.rm_startblock + irec.rm_blockcount >= bno + len);
2841+
if (roc->results->non_owner_matches && roc->stop_on_nonmatch)
2842+
return -ECANCELED;
2843+
27752844
return 0;
27762845
}
27772846

2778-
struct xfs_rmap_key_state {
2779-
uint64_t owner;
2780-
uint64_t offset;
2781-
unsigned int flags;
2782-
};
2783-
2784-
/* For each rmap given, figure out if it doesn't match the key we want. */
2785-
STATIC int
2786-
xfs_rmap_has_other_keys_helper(
2847+
/* Count the number of owners and non-owners of this range of blocks. */
2848+
int
2849+
xfs_rmap_count_owners(
27872850
struct xfs_btree_cur *cur,
2788-
const struct xfs_rmap_irec *rec,
2789-
void *priv)
2851+
xfs_agblock_t bno,
2852+
xfs_extlen_t len,
2853+
const struct xfs_owner_info *oinfo,
2854+
struct xfs_rmap_matches *results)
27902855
{
2791-
struct xfs_rmap_key_state *rks = priv;
2856+
struct xfs_rmap_ownercount roc;
2857+
int error;
27922858

2793-
if (rks->owner == rec->rm_owner && rks->offset == rec->rm_offset &&
2794-
((rks->flags & rec->rm_flags) & XFS_RMAP_KEY_FLAGS) == rks->flags)
2795-
return 0;
2796-
return -ECANCELED;
2859+
xfs_rmap_ownercount_init(&roc, bno, len, oinfo, results);
2860+
error = xfs_rmap_query_range(cur, &roc.low, &roc.high,
2861+
xfs_rmap_count_owners_helper, &roc);
2862+
if (error)
2863+
return error;
2864+
2865+
/*
2866+
* There can't be any non-owner rmaps that conflict with the given
2867+
* owner if we didn't find any rmaps matching the owner.
2868+
*/
2869+
if (!results->matches)
2870+
results->bad_non_owner_matches = 0;
2871+
2872+
return 0;
27972873
}
27982874

27992875
/*
@@ -2806,28 +2882,26 @@ xfs_rmap_has_other_keys(
28062882
xfs_agblock_t bno,
28072883
xfs_extlen_t len,
28082884
const struct xfs_owner_info *oinfo,
2809-
bool *has_rmap)
2885+
bool *has_other)
28102886
{
2811-
struct xfs_rmap_irec low = {0};
2812-
struct xfs_rmap_irec high;
2813-
struct xfs_rmap_key_state rks;
2887+
struct xfs_rmap_matches res;
2888+
struct xfs_rmap_ownercount roc;
28142889
int error;
28152890

2816-
xfs_owner_info_unpack(oinfo, &rks.owner, &rks.offset, &rks.flags);
2817-
*has_rmap = false;
2818-
2819-
low.rm_startblock = bno;
2820-
memset(&high, 0xFF, sizeof(high));
2821-
high.rm_startblock = bno + len - 1;
2891+
xfs_rmap_ownercount_init(&roc, bno, len, oinfo, &res);
2892+
roc.stop_on_nonmatch = true;
28222893

2823-
error = xfs_rmap_query_range(cur, &low, &high,
2824-
xfs_rmap_has_other_keys_helper, &rks);
2894+
error = xfs_rmap_query_range(cur, &roc.low, &roc.high,
2895+
xfs_rmap_count_owners_helper, &roc);
28252896
if (error == -ECANCELED) {
2826-
*has_rmap = true;
2897+
*has_other = true;
28272898
return 0;
28282899
}
2900+
if (error)
2901+
return error;
28292902

2830-
return error;
2903+
*has_other = false;
2904+
return 0;
28312905
}
28322906

28332907
const struct xfs_owner_info XFS_RMAP_OINFO_SKIP_UPDATE = {

fs/xfs/libxfs/xfs_rmap.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,24 @@ xfs_failaddr_t xfs_rmap_check_irec(struct xfs_btree_cur *cur,
200200

201201
int xfs_rmap_has_records(struct xfs_btree_cur *cur, xfs_agblock_t bno,
202202
xfs_extlen_t len, enum xbtree_recpacking *outcome);
203-
int xfs_rmap_record_exists(struct xfs_btree_cur *cur, xfs_agblock_t bno,
203+
204+
struct xfs_rmap_matches {
205+
/* Number of owner matches. */
206+
unsigned long long matches;
207+
208+
/* Number of non-owner matches. */
209+
unsigned long long non_owner_matches;
210+
211+
/* Number of non-owner matches that conflict with the owner matches. */
212+
unsigned long long bad_non_owner_matches;
213+
};
214+
215+
int xfs_rmap_count_owners(struct xfs_btree_cur *cur, xfs_agblock_t bno,
204216
xfs_extlen_t len, const struct xfs_owner_info *oinfo,
205-
bool *has_rmap);
217+
struct xfs_rmap_matches *rmatch);
206218
int xfs_rmap_has_other_keys(struct xfs_btree_cur *cur, xfs_agblock_t bno,
207219
xfs_extlen_t len, const struct xfs_owner_info *oinfo,
208-
bool *has_rmap);
220+
bool *has_other);
209221
int xfs_rmap_map_raw(struct xfs_btree_cur *cur, struct xfs_rmap_irec *rmap);
210222

211223
extern const struct xfs_owner_info XFS_RMAP_OINFO_SKIP_UPDATE;

fs/xfs/scrub/agheader.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ xchk_superblock_xref(
5151

5252
xchk_xref_is_used_space(sc, agbno, 1);
5353
xchk_xref_is_not_inode_chunk(sc, agbno, 1);
54-
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
54+
xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
5555
xchk_xref_is_not_shared(sc, agbno, 1);
5656
xchk_xref_is_not_cow_staging(sc, agbno, 1);
5757

@@ -515,7 +515,7 @@ xchk_agf_xref(
515515
xchk_agf_xref_freeblks(sc);
516516
xchk_agf_xref_cntbt(sc);
517517
xchk_xref_is_not_inode_chunk(sc, agbno, 1);
518-
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
518+
xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
519519
xchk_agf_xref_btreeblks(sc);
520520
xchk_xref_is_not_shared(sc, agbno, 1);
521521
xchk_xref_is_not_cow_staging(sc, agbno, 1);
@@ -644,7 +644,7 @@ xchk_agfl_block_xref(
644644

645645
xchk_xref_is_used_space(sc, agbno, 1);
646646
xchk_xref_is_not_inode_chunk(sc, agbno, 1);
647-
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_AG);
647+
xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_AG);
648648
xchk_xref_is_not_shared(sc, agbno, 1);
649649
xchk_xref_is_not_cow_staging(sc, agbno, 1);
650650
}
@@ -701,7 +701,7 @@ xchk_agfl_xref(
701701

702702
xchk_xref_is_used_space(sc, agbno, 1);
703703
xchk_xref_is_not_inode_chunk(sc, agbno, 1);
704-
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
704+
xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
705705
xchk_xref_is_not_shared(sc, agbno, 1);
706706
xchk_xref_is_not_cow_staging(sc, agbno, 1);
707707

@@ -857,7 +857,7 @@ xchk_agi_xref(
857857
xchk_xref_is_used_space(sc, agbno, 1);
858858
xchk_xref_is_not_inode_chunk(sc, agbno, 1);
859859
xchk_agi_xref_icounts(sc);
860-
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
860+
xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
861861
xchk_xref_is_not_shared(sc, agbno, 1);
862862
xchk_xref_is_not_cow_staging(sc, agbno, 1);
863863
xchk_agi_xref_fiblocks(sc);

fs/xfs/scrub/bmap.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ xchk_bmap_iextent_xref(
308308
struct xchk_bmap_info *info,
309309
struct xfs_bmbt_irec *irec)
310310
{
311+
struct xfs_owner_info oinfo;
311312
struct xfs_mount *mp = info->sc->mp;
312313
xfs_agnumber_t agno;
313314
xfs_agblock_t agbno;
@@ -328,19 +329,30 @@ xchk_bmap_iextent_xref(
328329
xchk_bmap_xref_rmap(info, irec, agbno);
329330
switch (info->whichfork) {
330331
case XFS_DATA_FORK:
331-
if (!xfs_is_reflink_inode(info->sc->ip))
332+
if (!xfs_is_reflink_inode(info->sc->ip)) {
333+
xfs_rmap_ino_owner(&oinfo, info->sc->ip->i_ino,
334+
info->whichfork, irec->br_startoff);
335+
xchk_xref_is_only_owned_by(info->sc, agbno,
336+
irec->br_blockcount, &oinfo);
332337
xchk_xref_is_not_shared(info->sc, agbno,
333338
irec->br_blockcount);
339+
}
334340
xchk_xref_is_not_cow_staging(info->sc, agbno,
335341
irec->br_blockcount);
336342
break;
337343
case XFS_ATTR_FORK:
344+
xfs_rmap_ino_owner(&oinfo, info->sc->ip->i_ino,
345+
info->whichfork, irec->br_startoff);
346+
xchk_xref_is_only_owned_by(info->sc, agbno, irec->br_blockcount,
347+
&oinfo);
338348
xchk_xref_is_not_shared(info->sc, agbno,
339349
irec->br_blockcount);
340350
xchk_xref_is_not_cow_staging(info->sc, agbno,
341351
irec->br_blockcount);
342352
break;
343353
case XFS_COW_FORK:
354+
xchk_xref_is_only_owned_by(info->sc, agbno, irec->br_blockcount,
355+
&XFS_RMAP_OINFO_COW);
344356
xchk_xref_is_cow_staging(info->sc, agbno,
345357
irec->br_blockcount);
346358
xchk_xref_is_not_shared(info->sc, agbno,

fs/xfs/scrub/btree.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ xchk_btree_check_block_owner(
402402
if (!bs->sc->sa.bno_cur && btnum == XFS_BTNUM_BNO)
403403
bs->cur = NULL;
404404

405-
xchk_xref_is_owned_by(bs->sc, agbno, 1, bs->oinfo);
405+
xchk_xref_is_only_owned_by(bs->sc, agbno, 1, bs->oinfo);
406406
if (!bs->sc->sa.rmap_cur && btnum == XFS_BTNUM_RMAP)
407407
bs->cur = NULL;
408408

fs/xfs/scrub/ialloc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ xchk_iallocbt_chunk(
276276
xchk_inobt_chunk_xref_finobt(sc, irec, agino, nr_inodes);
277277
else
278278
xchk_finobt_chunk_xref_inobt(sc, irec, agino, nr_inodes);
279-
xchk_xref_is_owned_by(sc, agbno, len, &XFS_RMAP_OINFO_INODES);
279+
xchk_xref_is_only_owned_by(sc, agbno, len, &XFS_RMAP_OINFO_INODES);
280280
xchk_xref_is_not_shared(sc, agbno, len);
281281
xchk_xref_is_not_cow_staging(sc, agbno, len);
282282
return true;
@@ -428,7 +428,7 @@ xchk_iallocbt_check_cluster(
428428
return 0;
429429
}
430430

431-
xchk_xref_is_owned_by(bs->sc, agbno, M_IGEO(mp)->blocks_per_cluster,
431+
xchk_xref_is_only_owned_by(bs->sc, agbno, M_IGEO(mp)->blocks_per_cluster,
432432
&XFS_RMAP_OINFO_INODES);
433433

434434
/* Grab the inode cluster buffer. */

fs/xfs/scrub/inode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ xchk_inode_xref(
556556

557557
xchk_xref_is_used_space(sc, agbno, 1);
558558
xchk_inode_xref_finobt(sc, ino);
559-
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_INODES);
559+
xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_INODES);
560560
xchk_xref_is_not_shared(sc, agbno, 1);
561561
xchk_xref_is_not_cow_staging(sc, agbno, 1);
562562
xchk_inode_xref_bmap(sc, dip);

0 commit comments

Comments
 (0)