Skip to content

Commit 6858c88

Browse files
dchinnerdgchinner
authored andcommitted
Merge tag 'scrub-btree-key-enhancements-6.4_2023-04-11' of git://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into guilt/xfs-for-next
xfs: enhance btree key scrubbing [v24.5] This series fixes the scrub btree block checker to ensure that the keys in the parent block accurately represent the block, and check the ordering of all interior key records. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Dave Chinner <david@fromorbit.com>
2 parents 1ee7550 + 2bea8df commit 6858c88

2 files changed

Lines changed: 63 additions & 8 deletions

File tree

fs/xfs/scrub/btree.c

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,12 @@ xchk_btree_rec(
151151

152152
trace_xchk_btree_rec(bs->sc, cur, 0);
153153

154-
/* If this isn't the first record, are they in order? */
155-
if (cur->bc_levels[0].ptr > 1 &&
154+
/* Are all records across all record blocks in order? */
155+
if (bs->lastrec_valid &&
156156
!cur->bc_ops->recs_inorder(cur, &bs->lastrec, rec))
157157
xchk_btree_set_corrupt(bs->sc, cur, 0);
158158
memcpy(&bs->lastrec, rec, cur->bc_ops->rec_len);
159+
bs->lastrec_valid = true;
159160

160161
if (cur->bc_nlevels == 1)
161162
return;
@@ -198,11 +199,12 @@ xchk_btree_key(
198199

199200
trace_xchk_btree_key(bs->sc, cur, level);
200201

201-
/* If this isn't the first key, are they in order? */
202-
if (cur->bc_levels[level].ptr > 1 &&
203-
!cur->bc_ops->keys_inorder(cur, &bs->lastkey[level - 1], key))
202+
/* Are all low keys across all node blocks in order? */
203+
if (bs->lastkey[level - 1].valid &&
204+
!cur->bc_ops->keys_inorder(cur, &bs->lastkey[level - 1].key, key))
204205
xchk_btree_set_corrupt(bs->sc, cur, level);
205-
memcpy(&bs->lastkey[level - 1], key, cur->bc_ops->key_len);
206+
memcpy(&bs->lastkey[level - 1].key, key, cur->bc_ops->key_len);
207+
bs->lastkey[level - 1].valid = true;
206208

207209
if (level + 1 >= cur->bc_nlevels)
208210
return;
@@ -529,6 +531,48 @@ xchk_btree_check_minrecs(
529531
xchk_btree_set_corrupt(bs->sc, cur, level);
530532
}
531533

534+
/*
535+
* If this btree block has a parent, make sure that the parent's keys capture
536+
* the keyspace contained in this block.
537+
*/
538+
STATIC void
539+
xchk_btree_block_check_keys(
540+
struct xchk_btree *bs,
541+
int level,
542+
struct xfs_btree_block *block)
543+
{
544+
union xfs_btree_key block_key;
545+
union xfs_btree_key *block_high_key;
546+
union xfs_btree_key *parent_low_key, *parent_high_key;
547+
struct xfs_btree_cur *cur = bs->cur;
548+
struct xfs_btree_block *parent_block;
549+
struct xfs_buf *bp;
550+
551+
if (level == cur->bc_nlevels - 1)
552+
return;
553+
554+
xfs_btree_get_keys(cur, block, &block_key);
555+
556+
/* Make sure the low key of this block matches the parent. */
557+
parent_block = xfs_btree_get_block(cur, level + 1, &bp);
558+
parent_low_key = xfs_btree_key_addr(cur, cur->bc_levels[level + 1].ptr,
559+
parent_block);
560+
if (cur->bc_ops->diff_two_keys(cur, &block_key, parent_low_key)) {
561+
xchk_btree_set_corrupt(bs->sc, bs->cur, level);
562+
return;
563+
}
564+
565+
if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
566+
return;
567+
568+
/* Make sure the high key of this block matches the parent. */
569+
parent_high_key = xfs_btree_high_key_addr(cur,
570+
cur->bc_levels[level + 1].ptr, parent_block);
571+
block_high_key = xfs_btree_high_key_from_key(cur, &block_key);
572+
if (cur->bc_ops->diff_two_keys(cur, block_high_key, parent_high_key))
573+
xchk_btree_set_corrupt(bs->sc, bs->cur, level);
574+
}
575+
532576
/*
533577
* Grab and scrub a btree block given a btree pointer. Returns block
534578
* and buffer pointers (if applicable) if they're ok to use.
@@ -580,7 +624,12 @@ xchk_btree_get_block(
580624
* Check the block's siblings; this function absorbs error codes
581625
* for us.
582626
*/
583-
return xchk_btree_block_check_siblings(bs, *pblock);
627+
error = xchk_btree_block_check_siblings(bs, *pblock);
628+
if (error)
629+
return error;
630+
631+
xchk_btree_block_check_keys(bs, level, *pblock);
632+
return 0;
584633
}
585634

586635
/*

fs/xfs/scrub/btree.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ typedef int (*xchk_btree_rec_fn)(
3131
struct xchk_btree *bs,
3232
const union xfs_btree_rec *rec);
3333

34+
struct xchk_btree_key {
35+
union xfs_btree_key key;
36+
bool valid;
37+
};
38+
3439
struct xchk_btree {
3540
/* caller-provided scrub state */
3641
struct xfs_scrub *sc;
@@ -40,11 +45,12 @@ struct xchk_btree {
4045
void *private;
4146

4247
/* internal scrub state */
48+
bool lastrec_valid;
4349
union xfs_btree_rec lastrec;
4450
struct list_head to_check;
4551

4652
/* this element must come last! */
47-
union xfs_btree_key lastkey[];
53+
struct xchk_btree_key lastkey[];
4854
};
4955

5056
/*

0 commit comments

Comments
 (0)