Skip to content

Commit 37f3fa7

Browse files
committed
xfs: scrub btree keys and records
Add to the btree scrubber the ability to check that the keys and records are in the right order and actually call out to our record iterator to do actual checking of the records. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Dave Chinner <dchinner@redhat.com>
1 parent cc3e094 commit 37f3fa7

File tree

2 files changed

+154
-1
lines changed

2 files changed

+154
-1
lines changed

fs/xfs/scrub/btree.c

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,101 @@ xfs_scrub_btree_set_corrupt(
9292
__return_address);
9393
}
9494

95+
/*
96+
* Make sure this record is in order and doesn't stray outside of the parent
97+
* keys.
98+
*/
99+
STATIC void
100+
xfs_scrub_btree_rec(
101+
struct xfs_scrub_btree *bs)
102+
{
103+
struct xfs_btree_cur *cur = bs->cur;
104+
union xfs_btree_rec *rec;
105+
union xfs_btree_key key;
106+
union xfs_btree_key hkey;
107+
union xfs_btree_key *keyp;
108+
struct xfs_btree_block *block;
109+
struct xfs_btree_block *keyblock;
110+
struct xfs_buf *bp;
111+
112+
block = xfs_btree_get_block(cur, 0, &bp);
113+
rec = xfs_btree_rec_addr(cur, cur->bc_ptrs[0], block);
114+
115+
trace_xfs_scrub_btree_rec(bs->sc, cur, 0);
116+
117+
/* If this isn't the first record, are they in order? */
118+
if (!bs->firstrec && !cur->bc_ops->recs_inorder(cur, &bs->lastrec, rec))
119+
xfs_scrub_btree_set_corrupt(bs->sc, cur, 0);
120+
bs->firstrec = false;
121+
memcpy(&bs->lastrec, rec, cur->bc_ops->rec_len);
122+
123+
if (cur->bc_nlevels == 1)
124+
return;
125+
126+
/* Is this at least as large as the parent low key? */
127+
cur->bc_ops->init_key_from_rec(&key, rec);
128+
keyblock = xfs_btree_get_block(cur, 1, &bp);
129+
keyp = xfs_btree_key_addr(cur, cur->bc_ptrs[1], keyblock);
130+
if (cur->bc_ops->diff_two_keys(cur, &key, keyp) < 0)
131+
xfs_scrub_btree_set_corrupt(bs->sc, cur, 1);
132+
133+
if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
134+
return;
135+
136+
/* Is this no larger than the parent high key? */
137+
cur->bc_ops->init_high_key_from_rec(&hkey, rec);
138+
keyp = xfs_btree_high_key_addr(cur, cur->bc_ptrs[1], keyblock);
139+
if (cur->bc_ops->diff_two_keys(cur, keyp, &hkey) < 0)
140+
xfs_scrub_btree_set_corrupt(bs->sc, cur, 1);
141+
}
142+
143+
/*
144+
* Make sure this key is in order and doesn't stray outside of the parent
145+
* keys.
146+
*/
147+
STATIC void
148+
xfs_scrub_btree_key(
149+
struct xfs_scrub_btree *bs,
150+
int level)
151+
{
152+
struct xfs_btree_cur *cur = bs->cur;
153+
union xfs_btree_key *key;
154+
union xfs_btree_key *keyp;
155+
struct xfs_btree_block *block;
156+
struct xfs_btree_block *keyblock;
157+
struct xfs_buf *bp;
158+
159+
block = xfs_btree_get_block(cur, level, &bp);
160+
key = xfs_btree_key_addr(cur, cur->bc_ptrs[level], block);
161+
162+
trace_xfs_scrub_btree_key(bs->sc, cur, level);
163+
164+
/* If this isn't the first key, are they in order? */
165+
if (!bs->firstkey[level] &&
166+
!cur->bc_ops->keys_inorder(cur, &bs->lastkey[level], key))
167+
xfs_scrub_btree_set_corrupt(bs->sc, cur, level);
168+
bs->firstkey[level] = false;
169+
memcpy(&bs->lastkey[level], key, cur->bc_ops->key_len);
170+
171+
if (level + 1 >= cur->bc_nlevels)
172+
return;
173+
174+
/* Is this at least as large as the parent low key? */
175+
keyblock = xfs_btree_get_block(cur, level + 1, &bp);
176+
keyp = xfs_btree_key_addr(cur, cur->bc_ptrs[level + 1], keyblock);
177+
if (cur->bc_ops->diff_two_keys(cur, key, keyp) < 0)
178+
xfs_scrub_btree_set_corrupt(bs->sc, cur, level);
179+
180+
if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
181+
return;
182+
183+
/* Is this no larger than the parent high key? */
184+
key = xfs_btree_high_key_addr(cur, cur->bc_ptrs[level], block);
185+
keyp = xfs_btree_high_key_addr(cur, cur->bc_ptrs[level + 1], keyblock);
186+
if (cur->bc_ops->diff_two_keys(cur, keyp, key) < 0)
187+
xfs_scrub_btree_set_corrupt(bs->sc, cur, level);
188+
}
189+
95190
/*
96191
* Check a btree pointer. Returns true if it's ok to use this pointer.
97192
* Callers do not need to set the corrupt flag.
@@ -278,6 +373,7 @@ xfs_scrub_btree(
278373
struct xfs_scrub_btree bs = {0};
279374
union xfs_btree_ptr ptr;
280375
union xfs_btree_ptr *pp;
376+
union xfs_btree_rec *recp;
281377
struct xfs_btree_block *block;
282378
int level;
283379
struct xfs_buf *bp;
@@ -328,7 +424,16 @@ xfs_scrub_btree(
328424
continue;
329425
}
330426

331-
if (xfs_scrub_should_terminate(sc, &error))
427+
/* Records in order for scrub? */
428+
xfs_scrub_btree_rec(&bs);
429+
430+
/* Call out to the record checker. */
431+
recp = xfs_btree_rec_addr(cur, cur->bc_ptrs[0], block);
432+
error = bs.scrub_rec(&bs, recp);
433+
if (error)
434+
break;
435+
if (xfs_scrub_should_terminate(sc, &error) ||
436+
(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
332437
break;
333438

334439
cur->bc_ptrs[level]++;
@@ -343,6 +448,9 @@ xfs_scrub_btree(
343448
continue;
344449
}
345450

451+
/* Keys in order for scrub? */
452+
xfs_scrub_btree_key(&bs, level);
453+
346454
/* Drill another level deeper. */
347455
pp = xfs_btree_ptr_addr(cur, cur->bc_ptrs[level], block);
348456
if (!xfs_scrub_btree_ptr_ok(&bs, level, pp)) {

fs/xfs/scrub/trace.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,51 @@ TRACE_EVENT(xfs_scrub_ifork_btree_error,
446446
__entry->ret_ip)
447447
);
448448

449+
DECLARE_EVENT_CLASS(xfs_scrub_sbtree_class,
450+
TP_PROTO(struct xfs_scrub_context *sc, struct xfs_btree_cur *cur,
451+
int level),
452+
TP_ARGS(sc, cur, level),
453+
TP_STRUCT__entry(
454+
__field(dev_t, dev)
455+
__field(int, type)
456+
__field(xfs_btnum_t, btnum)
457+
__field(xfs_agnumber_t, agno)
458+
__field(xfs_agblock_t, bno)
459+
__field(int, level)
460+
__field(int, nlevels)
461+
__field(int, ptr)
462+
),
463+
TP_fast_assign(
464+
xfs_fsblock_t fsbno = xfs_scrub_btree_cur_fsbno(cur, level);
465+
466+
__entry->dev = sc->mp->m_super->s_dev;
467+
__entry->type = sc->sm->sm_type;
468+
__entry->btnum = cur->bc_btnum;
469+
__entry->agno = XFS_FSB_TO_AGNO(cur->bc_mp, fsbno);
470+
__entry->bno = XFS_FSB_TO_AGBNO(cur->bc_mp, fsbno);
471+
__entry->level = level;
472+
__entry->nlevels = cur->bc_nlevels;
473+
__entry->ptr = cur->bc_ptrs[level];
474+
),
475+
TP_printk("dev %d:%d type %u btnum %d agno %u agbno %u level %d nlevels %d ptr %d",
476+
MAJOR(__entry->dev), MINOR(__entry->dev),
477+
__entry->type,
478+
__entry->btnum,
479+
__entry->agno,
480+
__entry->bno,
481+
__entry->level,
482+
__entry->nlevels,
483+
__entry->ptr)
484+
)
485+
#define DEFINE_SCRUB_SBTREE_EVENT(name) \
486+
DEFINE_EVENT(xfs_scrub_sbtree_class, name, \
487+
TP_PROTO(struct xfs_scrub_context *sc, struct xfs_btree_cur *cur, \
488+
int level), \
489+
TP_ARGS(sc, cur, level))
490+
491+
DEFINE_SCRUB_SBTREE_EVENT(xfs_scrub_btree_rec);
492+
DEFINE_SCRUB_SBTREE_EVENT(xfs_scrub_btree_key);
493+
449494
#endif /* _TRACE_XFS_SCRUB_TRACE_H */
450495

451496
#undef TRACE_INCLUDE_PATH

0 commit comments

Comments
 (0)