Skip to content

Commit 6abc7ae

Browse files
author
Darrick J. Wong
committed
xfs: replace xfs_btree_has_record with a general keyspace scanner
The current implementation of xfs_btree_has_record returns true if it finds /any/ record within the given range. Unfortunately, that's not sufficient for scrub. We want to be able to tell if a range of keyspace for a btree is devoid of records, is totally mapped to records, or is somewhere in between. By forcing this to be a boolean, we conflated sparseness and fullness, which caused scrub to return incorrect results. Fix the API so that we can tell the caller which of those three is the current state. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com>
1 parent bd7e795 commit 6abc7ae

17 files changed

Lines changed: 249 additions & 43 deletions

fs/xfs/libxfs/xfs_alloc.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3745,13 +3745,16 @@ xfs_alloc_query_all(
37453745
return xfs_btree_query_all(cur, xfs_alloc_query_range_helper, &query);
37463746
}
37473747

3748-
/* Is there a record covering a given extent? */
3748+
/*
3749+
* Scan part of the keyspace of the free space and tell us if the area has no
3750+
* records, is fully mapped by records, or is partially filled.
3751+
*/
37493752
int
3750-
xfs_alloc_has_record(
3753+
xfs_alloc_has_records(
37513754
struct xfs_btree_cur *cur,
37523755
xfs_agblock_t bno,
37533756
xfs_extlen_t len,
3754-
bool *exists)
3757+
enum xbtree_recpacking *outcome)
37553758
{
37563759
union xfs_btree_irec low;
37573760
union xfs_btree_irec high;
@@ -3761,7 +3764,7 @@ xfs_alloc_has_record(
37613764
memset(&high, 0xFF, sizeof(high));
37623765
high.a.ar_startblock = bno + len - 1;
37633766

3764-
return xfs_btree_has_record(cur, &low, &high, exists);
3767+
return xfs_btree_has_records(cur, &low, &high, outcome);
37653768
}
37663769

37673770
/*

fs/xfs/libxfs/xfs_alloc.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@ int xfs_alloc_query_range(struct xfs_btree_cur *cur,
213213
int xfs_alloc_query_all(struct xfs_btree_cur *cur, xfs_alloc_query_range_fn fn,
214214
void *priv);
215215

216-
int xfs_alloc_has_record(struct xfs_btree_cur *cur, xfs_agblock_t bno,
217-
xfs_extlen_t len, bool *exist);
216+
int xfs_alloc_has_records(struct xfs_btree_cur *cur, xfs_agblock_t bno,
217+
xfs_extlen_t len, enum xbtree_recpacking *outcome);
218218

219219
typedef int (*xfs_agfl_walk_fn)(struct xfs_mount *mp, xfs_agblock_t bno,
220220
void *priv);

fs/xfs/libxfs/xfs_alloc_btree.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,16 @@ xfs_cntbt_recs_inorder(
423423
be32_to_cpu(r2->alloc.ar_startblock));
424424
}
425425

426+
STATIC enum xbtree_key_contig
427+
xfs_allocbt_keys_contiguous(
428+
struct xfs_btree_cur *cur,
429+
const union xfs_btree_key *key1,
430+
const union xfs_btree_key *key2)
431+
{
432+
return xbtree_key_contig(be32_to_cpu(key1->alloc.ar_startblock),
433+
be32_to_cpu(key2->alloc.ar_startblock));
434+
}
435+
426436
static const struct xfs_btree_ops xfs_bnobt_ops = {
427437
.rec_len = sizeof(xfs_alloc_rec_t),
428438
.key_len = sizeof(xfs_alloc_key_t),
@@ -443,6 +453,7 @@ static const struct xfs_btree_ops xfs_bnobt_ops = {
443453
.diff_two_keys = xfs_bnobt_diff_two_keys,
444454
.keys_inorder = xfs_bnobt_keys_inorder,
445455
.recs_inorder = xfs_bnobt_recs_inorder,
456+
.keys_contiguous = xfs_allocbt_keys_contiguous,
446457
};
447458

448459
static const struct xfs_btree_ops xfs_cntbt_ops = {
@@ -465,6 +476,7 @@ static const struct xfs_btree_ops xfs_cntbt_ops = {
465476
.diff_two_keys = xfs_cntbt_diff_two_keys,
466477
.keys_inorder = xfs_cntbt_keys_inorder,
467478
.recs_inorder = xfs_cntbt_recs_inorder,
479+
.keys_contiguous = NULL, /* not needed right now */
468480
};
469481

470482
/* Allocate most of a new allocation btree cursor. */

fs/xfs/libxfs/xfs_bmap_btree.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,16 @@ xfs_bmbt_recs_inorder(
500500
xfs_bmbt_disk_get_startoff(&r2->bmbt);
501501
}
502502

503+
STATIC enum xbtree_key_contig
504+
xfs_bmbt_keys_contiguous(
505+
struct xfs_btree_cur *cur,
506+
const union xfs_btree_key *key1,
507+
const union xfs_btree_key *key2)
508+
{
509+
return xbtree_key_contig(be64_to_cpu(key1->bmbt.br_startoff),
510+
be64_to_cpu(key2->bmbt.br_startoff));
511+
}
512+
503513
static const struct xfs_btree_ops xfs_bmbt_ops = {
504514
.rec_len = sizeof(xfs_bmbt_rec_t),
505515
.key_len = sizeof(xfs_bmbt_key_t),
@@ -520,6 +530,7 @@ static const struct xfs_btree_ops xfs_bmbt_ops = {
520530
.buf_ops = &xfs_bmbt_buf_ops,
521531
.keys_inorder = xfs_bmbt_keys_inorder,
522532
.recs_inorder = xfs_bmbt_recs_inorder,
533+
.keys_contiguous = xfs_bmbt_keys_contiguous,
523534
};
524535

525536
/*

fs/xfs/libxfs/xfs_btree.c

Lines changed: 95 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5025,34 +5025,116 @@ xfs_btree_diff_two_ptrs(
50255025
return (int64_t)be32_to_cpu(a->s) - be32_to_cpu(b->s);
50265026
}
50275027

5028-
/* If there's an extent, we're done. */
5028+
struct xfs_btree_has_records {
5029+
/* Keys for the start and end of the range we want to know about. */
5030+
union xfs_btree_key start_key;
5031+
union xfs_btree_key end_key;
5032+
5033+
/* Highest record key we've seen so far. */
5034+
union xfs_btree_key high_key;
5035+
5036+
enum xbtree_recpacking outcome;
5037+
};
5038+
50295039
STATIC int
5030-
xfs_btree_has_record_helper(
5040+
xfs_btree_has_records_helper(
50315041
struct xfs_btree_cur *cur,
50325042
const union xfs_btree_rec *rec,
50335043
void *priv)
50345044
{
5035-
return -ECANCELED;
5045+
union xfs_btree_key rec_key;
5046+
union xfs_btree_key rec_high_key;
5047+
struct xfs_btree_has_records *info = priv;
5048+
enum xbtree_key_contig key_contig;
5049+
5050+
cur->bc_ops->init_key_from_rec(&rec_key, rec);
5051+
5052+
if (info->outcome == XBTREE_RECPACKING_EMPTY) {
5053+
info->outcome = XBTREE_RECPACKING_SPARSE;
5054+
5055+
/*
5056+
* If the first record we find does not overlap the start key,
5057+
* then there is a hole at the start of the search range.
5058+
* Classify this as sparse and stop immediately.
5059+
*/
5060+
if (xfs_btree_keycmp_lt(cur, &info->start_key, &rec_key))
5061+
return -ECANCELED;
5062+
} else {
5063+
/*
5064+
* If a subsequent record does not overlap with the any record
5065+
* we've seen so far, there is a hole in the middle of the
5066+
* search range. Classify this as sparse and stop.
5067+
* If the keys overlap and this btree does not allow overlap,
5068+
* signal corruption.
5069+
*/
5070+
key_contig = cur->bc_ops->keys_contiguous(cur, &info->high_key,
5071+
&rec_key);
5072+
if (key_contig == XBTREE_KEY_OVERLAP &&
5073+
!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
5074+
return -EFSCORRUPTED;
5075+
if (key_contig == XBTREE_KEY_GAP)
5076+
return -ECANCELED;
5077+
}
5078+
5079+
/*
5080+
* If high_key(rec) is larger than any other high key we've seen,
5081+
* remember it for later.
5082+
*/
5083+
cur->bc_ops->init_high_key_from_rec(&rec_high_key, rec);
5084+
if (xfs_btree_keycmp_gt(cur, &rec_high_key, &info->high_key))
5085+
info->high_key = rec_high_key; /* struct copy */
5086+
5087+
return 0;
50365088
}
50375089

5038-
/* Is there a record covering a given range of keys? */
5090+
/*
5091+
* Scan part of the keyspace of a btree and tell us if that keyspace does not
5092+
* map to any records; is fully mapped to records; or is partially mapped to
5093+
* records. This is the btree record equivalent to determining if a file is
5094+
* sparse.
5095+
*/
50395096
int
5040-
xfs_btree_has_record(
5097+
xfs_btree_has_records(
50415098
struct xfs_btree_cur *cur,
50425099
const union xfs_btree_irec *low,
50435100
const union xfs_btree_irec *high,
5044-
bool *exists)
5101+
enum xbtree_recpacking *outcome)
50455102
{
5103+
struct xfs_btree_has_records info = {
5104+
.outcome = XBTREE_RECPACKING_EMPTY,
5105+
};
50465106
int error;
50475107

5048-
error = xfs_btree_query_range(cur, low, high,
5049-
&xfs_btree_has_record_helper, NULL);
5050-
if (error == -ECANCELED) {
5051-
*exists = true;
5052-
return 0;
5108+
/* Not all btrees support this operation. */
5109+
if (!cur->bc_ops->keys_contiguous) {
5110+
ASSERT(0);
5111+
return -EOPNOTSUPP;
50535112
}
5054-
*exists = false;
5055-
return error;
5113+
5114+
xfs_btree_key_from_irec(cur, &info.start_key, low);
5115+
xfs_btree_key_from_irec(cur, &info.end_key, high);
5116+
5117+
error = xfs_btree_query_range(cur, low, high,
5118+
xfs_btree_has_records_helper, &info);
5119+
if (error == -ECANCELED)
5120+
goto out;
5121+
if (error)
5122+
return error;
5123+
5124+
if (info.outcome == XBTREE_RECPACKING_EMPTY)
5125+
goto out;
5126+
5127+
/*
5128+
* If the largest high_key(rec) we saw during the walk is greater than
5129+
* the end of the search range, classify this as full. Otherwise,
5130+
* there is a hole at the end of the search range.
5131+
*/
5132+
if (xfs_btree_keycmp_ge(cur, &info.high_key, &info.end_key))
5133+
info.outcome = XBTREE_RECPACKING_FULL;
5134+
5135+
out:
5136+
*outcome = info.outcome;
5137+
return 0;
50565138
}
50575139

50585140
/* Are there more records in this btree? */

fs/xfs/libxfs/xfs_btree.h

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,27 @@ uint32_t xfs_btree_magic(int crc, xfs_btnum_t btnum);
9090
#define XFS_BTREE_STATS_ADD(cur, stat, val) \
9191
XFS_STATS_ADD_OFF((cur)->bc_mp, (cur)->bc_statoff + __XBTS_ ## stat, val)
9292

93+
enum xbtree_key_contig {
94+
XBTREE_KEY_GAP = 0,
95+
XBTREE_KEY_CONTIGUOUS,
96+
XBTREE_KEY_OVERLAP,
97+
};
98+
99+
/*
100+
* Decide if these two numeric btree key fields are contiguous, overlapping,
101+
* or if there's a gap between them. @x should be the field from the high
102+
* key and @y should be the field from the low key.
103+
*/
104+
static inline enum xbtree_key_contig xbtree_key_contig(uint64_t x, uint64_t y)
105+
{
106+
x++;
107+
if (x < y)
108+
return XBTREE_KEY_GAP;
109+
if (x == y)
110+
return XBTREE_KEY_CONTIGUOUS;
111+
return XBTREE_KEY_OVERLAP;
112+
}
113+
93114
struct xfs_btree_ops {
94115
/* size of the key and record structures */
95116
size_t key_len;
@@ -157,6 +178,19 @@ struct xfs_btree_ops {
157178
int (*recs_inorder)(struct xfs_btree_cur *cur,
158179
const union xfs_btree_rec *r1,
159180
const union xfs_btree_rec *r2);
181+
182+
/*
183+
* Are these two btree keys immediately adjacent?
184+
*
185+
* Given two btree keys @key1 and @key2, decide if it is impossible for
186+
* there to be a third btree key K satisfying the relationship
187+
* @key1 < K < @key2. To determine if two btree records are
188+
* immediately adjacent, @key1 should be the high key of the first
189+
* record and @key2 should be the low key of the second record.
190+
*/
191+
enum xbtree_key_contig (*keys_contiguous)(struct xfs_btree_cur *cur,
192+
const union xfs_btree_key *key1,
193+
const union xfs_btree_key *key2);
160194
};
161195

162196
/*
@@ -540,9 +574,15 @@ void xfs_btree_get_keys(struct xfs_btree_cur *cur,
540574
struct xfs_btree_block *block, union xfs_btree_key *key);
541575
union xfs_btree_key *xfs_btree_high_key_from_key(struct xfs_btree_cur *cur,
542576
union xfs_btree_key *key);
543-
int xfs_btree_has_record(struct xfs_btree_cur *cur,
577+
typedef bool (*xfs_btree_key_gap_fn)(struct xfs_btree_cur *cur,
578+
const union xfs_btree_key *key1,
579+
const union xfs_btree_key *key2);
580+
581+
int xfs_btree_has_records(struct xfs_btree_cur *cur,
544582
const union xfs_btree_irec *low,
545-
const union xfs_btree_irec *high, bool *exists);
583+
const union xfs_btree_irec *high,
584+
enum xbtree_recpacking *outcome);
585+
546586
bool xfs_btree_has_more_records(struct xfs_btree_cur *cur);
547587
struct xfs_ifork *xfs_btree_ifork_ptr(struct xfs_btree_cur *cur);
548588

fs/xfs/libxfs/xfs_ialloc_btree.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,16 @@ xfs_inobt_recs_inorder(
383383
be32_to_cpu(r2->inobt.ir_startino);
384384
}
385385

386+
STATIC enum xbtree_key_contig
387+
xfs_inobt_keys_contiguous(
388+
struct xfs_btree_cur *cur,
389+
const union xfs_btree_key *key1,
390+
const union xfs_btree_key *key2)
391+
{
392+
return xbtree_key_contig(be32_to_cpu(key1->inobt.ir_startino),
393+
be32_to_cpu(key2->inobt.ir_startino));
394+
}
395+
386396
static const struct xfs_btree_ops xfs_inobt_ops = {
387397
.rec_len = sizeof(xfs_inobt_rec_t),
388398
.key_len = sizeof(xfs_inobt_key_t),
@@ -402,6 +412,7 @@ static const struct xfs_btree_ops xfs_inobt_ops = {
402412
.diff_two_keys = xfs_inobt_diff_two_keys,
403413
.keys_inorder = xfs_inobt_keys_inorder,
404414
.recs_inorder = xfs_inobt_recs_inorder,
415+
.keys_contiguous = xfs_inobt_keys_contiguous,
405416
};
406417

407418
static const struct xfs_btree_ops xfs_finobt_ops = {
@@ -423,6 +434,7 @@ static const struct xfs_btree_ops xfs_finobt_ops = {
423434
.diff_two_keys = xfs_inobt_diff_two_keys,
424435
.keys_inorder = xfs_inobt_keys_inorder,
425436
.recs_inorder = xfs_inobt_recs_inorder,
437+
.keys_contiguous = xfs_inobt_keys_contiguous,
426438
};
427439

428440
/*

fs/xfs/libxfs/xfs_refcount.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1998,14 +1998,17 @@ xfs_refcount_recover_cow_leftovers(
19981998
return error;
19991999
}
20002000

2001-
/* Is there a record covering a given extent? */
2001+
/*
2002+
* Scan part of the keyspace of the refcount records and tell us if the area
2003+
* has no records, is fully mapped by records, or is partially filled.
2004+
*/
20022005
int
2003-
xfs_refcount_has_record(
2006+
xfs_refcount_has_records(
20042007
struct xfs_btree_cur *cur,
20052008
enum xfs_refc_domain domain,
20062009
xfs_agblock_t bno,
20072010
xfs_extlen_t len,
2008-
bool *exists)
2011+
enum xbtree_recpacking *outcome)
20092012
{
20102013
union xfs_btree_irec low;
20112014
union xfs_btree_irec high;
@@ -2016,7 +2019,7 @@ xfs_refcount_has_record(
20162019
high.rc.rc_startblock = bno + len - 1;
20172020
low.rc.rc_domain = high.rc.rc_domain = domain;
20182021

2019-
return xfs_btree_has_record(cur, &low, &high, exists);
2022+
return xfs_btree_has_records(cur, &low, &high, outcome);
20202023
}
20212024

20222025
int __init

fs/xfs/libxfs/xfs_refcount.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp,
111111
*/
112112
#define XFS_REFCOUNT_ITEM_OVERHEAD 32
113113

114-
extern int xfs_refcount_has_record(struct xfs_btree_cur *cur,
114+
extern int xfs_refcount_has_records(struct xfs_btree_cur *cur,
115115
enum xfs_refc_domain domain, xfs_agblock_t bno,
116-
xfs_extlen_t len, bool *exists);
116+
xfs_extlen_t len, enum xbtree_recpacking *outcome);
117117
union xfs_btree_rec;
118118
extern void xfs_refcount_btrec_to_irec(const union xfs_btree_rec *rec,
119119
struct xfs_refcount_irec *irec);

fs/xfs/libxfs/xfs_refcount_btree.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,16 @@ xfs_refcountbt_recs_inorder(
300300
be32_to_cpu(r2->refc.rc_startblock);
301301
}
302302

303+
STATIC enum xbtree_key_contig
304+
xfs_refcountbt_keys_contiguous(
305+
struct xfs_btree_cur *cur,
306+
const union xfs_btree_key *key1,
307+
const union xfs_btree_key *key2)
308+
{
309+
return xbtree_key_contig(be32_to_cpu(key1->refc.rc_startblock),
310+
be32_to_cpu(key2->refc.rc_startblock));
311+
}
312+
303313
static const struct xfs_btree_ops xfs_refcountbt_ops = {
304314
.rec_len = sizeof(struct xfs_refcount_rec),
305315
.key_len = sizeof(struct xfs_refcount_key),
@@ -319,6 +329,7 @@ static const struct xfs_btree_ops xfs_refcountbt_ops = {
319329
.diff_two_keys = xfs_refcountbt_diff_two_keys,
320330
.keys_inorder = xfs_refcountbt_keys_inorder,
321331
.recs_inorder = xfs_refcountbt_recs_inorder,
332+
.keys_contiguous = xfs_refcountbt_keys_contiguous,
322333
};
323334

324335
/*

0 commit comments

Comments
 (0)