Commit 6abc7aef authored by Darrick J. Wong's avatar Darrick J. Wong

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: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
parent bd7e7951
...@@ -3745,13 +3745,16 @@ xfs_alloc_query_all( ...@@ -3745,13 +3745,16 @@ xfs_alloc_query_all(
return xfs_btree_query_all(cur, xfs_alloc_query_range_helper, &query); return xfs_btree_query_all(cur, xfs_alloc_query_range_helper, &query);
} }
/* Is there a record covering a given extent? */ /*
* Scan part of the keyspace of the free space and tell us if the area has no
* records, is fully mapped by records, or is partially filled.
*/
int int
xfs_alloc_has_record( xfs_alloc_has_records(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
xfs_agblock_t bno, xfs_agblock_t bno,
xfs_extlen_t len, xfs_extlen_t len,
bool *exists) enum xbtree_recpacking *outcome)
{ {
union xfs_btree_irec low; union xfs_btree_irec low;
union xfs_btree_irec high; union xfs_btree_irec high;
...@@ -3761,7 +3764,7 @@ xfs_alloc_has_record( ...@@ -3761,7 +3764,7 @@ xfs_alloc_has_record(
memset(&high, 0xFF, sizeof(high)); memset(&high, 0xFF, sizeof(high));
high.a.ar_startblock = bno + len - 1; high.a.ar_startblock = bno + len - 1;
return xfs_btree_has_record(cur, &low, &high, exists); return xfs_btree_has_records(cur, &low, &high, outcome);
} }
/* /*
......
...@@ -213,8 +213,8 @@ int xfs_alloc_query_range(struct xfs_btree_cur *cur, ...@@ -213,8 +213,8 @@ int xfs_alloc_query_range(struct xfs_btree_cur *cur,
int xfs_alloc_query_all(struct xfs_btree_cur *cur, xfs_alloc_query_range_fn fn, int xfs_alloc_query_all(struct xfs_btree_cur *cur, xfs_alloc_query_range_fn fn,
void *priv); void *priv);
int xfs_alloc_has_record(struct xfs_btree_cur *cur, xfs_agblock_t bno, int xfs_alloc_has_records(struct xfs_btree_cur *cur, xfs_agblock_t bno,
xfs_extlen_t len, bool *exist); xfs_extlen_t len, enum xbtree_recpacking *outcome);
typedef int (*xfs_agfl_walk_fn)(struct xfs_mount *mp, xfs_agblock_t bno, typedef int (*xfs_agfl_walk_fn)(struct xfs_mount *mp, xfs_agblock_t bno,
void *priv); void *priv);
......
...@@ -423,6 +423,16 @@ xfs_cntbt_recs_inorder( ...@@ -423,6 +423,16 @@ xfs_cntbt_recs_inorder(
be32_to_cpu(r2->alloc.ar_startblock)); be32_to_cpu(r2->alloc.ar_startblock));
} }
STATIC enum xbtree_key_contig
xfs_allocbt_keys_contiguous(
struct xfs_btree_cur *cur,
const union xfs_btree_key *key1,
const union xfs_btree_key *key2)
{
return xbtree_key_contig(be32_to_cpu(key1->alloc.ar_startblock),
be32_to_cpu(key2->alloc.ar_startblock));
}
static const struct xfs_btree_ops xfs_bnobt_ops = { static const struct xfs_btree_ops xfs_bnobt_ops = {
.rec_len = sizeof(xfs_alloc_rec_t), .rec_len = sizeof(xfs_alloc_rec_t),
.key_len = sizeof(xfs_alloc_key_t), .key_len = sizeof(xfs_alloc_key_t),
...@@ -443,6 +453,7 @@ static const struct xfs_btree_ops xfs_bnobt_ops = { ...@@ -443,6 +453,7 @@ static const struct xfs_btree_ops xfs_bnobt_ops = {
.diff_two_keys = xfs_bnobt_diff_two_keys, .diff_two_keys = xfs_bnobt_diff_two_keys,
.keys_inorder = xfs_bnobt_keys_inorder, .keys_inorder = xfs_bnobt_keys_inorder,
.recs_inorder = xfs_bnobt_recs_inorder, .recs_inorder = xfs_bnobt_recs_inorder,
.keys_contiguous = xfs_allocbt_keys_contiguous,
}; };
static const struct xfs_btree_ops xfs_cntbt_ops = { static const struct xfs_btree_ops xfs_cntbt_ops = {
...@@ -465,6 +476,7 @@ static const struct xfs_btree_ops xfs_cntbt_ops = { ...@@ -465,6 +476,7 @@ static const struct xfs_btree_ops xfs_cntbt_ops = {
.diff_two_keys = xfs_cntbt_diff_two_keys, .diff_two_keys = xfs_cntbt_diff_two_keys,
.keys_inorder = xfs_cntbt_keys_inorder, .keys_inorder = xfs_cntbt_keys_inorder,
.recs_inorder = xfs_cntbt_recs_inorder, .recs_inorder = xfs_cntbt_recs_inorder,
.keys_contiguous = NULL, /* not needed right now */
}; };
/* Allocate most of a new allocation btree cursor. */ /* Allocate most of a new allocation btree cursor. */
......
...@@ -500,6 +500,16 @@ xfs_bmbt_recs_inorder( ...@@ -500,6 +500,16 @@ xfs_bmbt_recs_inorder(
xfs_bmbt_disk_get_startoff(&r2->bmbt); xfs_bmbt_disk_get_startoff(&r2->bmbt);
} }
STATIC enum xbtree_key_contig
xfs_bmbt_keys_contiguous(
struct xfs_btree_cur *cur,
const union xfs_btree_key *key1,
const union xfs_btree_key *key2)
{
return xbtree_key_contig(be64_to_cpu(key1->bmbt.br_startoff),
be64_to_cpu(key2->bmbt.br_startoff));
}
static const struct xfs_btree_ops xfs_bmbt_ops = { static const struct xfs_btree_ops xfs_bmbt_ops = {
.rec_len = sizeof(xfs_bmbt_rec_t), .rec_len = sizeof(xfs_bmbt_rec_t),
.key_len = sizeof(xfs_bmbt_key_t), .key_len = sizeof(xfs_bmbt_key_t),
...@@ -520,6 +530,7 @@ static const struct xfs_btree_ops xfs_bmbt_ops = { ...@@ -520,6 +530,7 @@ static const struct xfs_btree_ops xfs_bmbt_ops = {
.buf_ops = &xfs_bmbt_buf_ops, .buf_ops = &xfs_bmbt_buf_ops,
.keys_inorder = xfs_bmbt_keys_inorder, .keys_inorder = xfs_bmbt_keys_inorder,
.recs_inorder = xfs_bmbt_recs_inorder, .recs_inorder = xfs_bmbt_recs_inorder,
.keys_contiguous = xfs_bmbt_keys_contiguous,
}; };
/* /*
......
...@@ -5025,34 +5025,116 @@ xfs_btree_diff_two_ptrs( ...@@ -5025,34 +5025,116 @@ xfs_btree_diff_two_ptrs(
return (int64_t)be32_to_cpu(a->s) - be32_to_cpu(b->s); return (int64_t)be32_to_cpu(a->s) - be32_to_cpu(b->s);
} }
/* If there's an extent, we're done. */ struct xfs_btree_has_records {
/* Keys for the start and end of the range we want to know about. */
union xfs_btree_key start_key;
union xfs_btree_key end_key;
/* Highest record key we've seen so far. */
union xfs_btree_key high_key;
enum xbtree_recpacking outcome;
};
STATIC int STATIC int
xfs_btree_has_record_helper( xfs_btree_has_records_helper(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
const union xfs_btree_rec *rec, const union xfs_btree_rec *rec,
void *priv) void *priv)
{ {
return -ECANCELED; union xfs_btree_key rec_key;
union xfs_btree_key rec_high_key;
struct xfs_btree_has_records *info = priv;
enum xbtree_key_contig key_contig;
cur->bc_ops->init_key_from_rec(&rec_key, rec);
if (info->outcome == XBTREE_RECPACKING_EMPTY) {
info->outcome = XBTREE_RECPACKING_SPARSE;
/*
* If the first record we find does not overlap the start key,
* then there is a hole at the start of the search range.
* Classify this as sparse and stop immediately.
*/
if (xfs_btree_keycmp_lt(cur, &info->start_key, &rec_key))
return -ECANCELED;
} else {
/*
* If a subsequent record does not overlap with the any record
* we've seen so far, there is a hole in the middle of the
* search range. Classify this as sparse and stop.
* If the keys overlap and this btree does not allow overlap,
* signal corruption.
*/
key_contig = cur->bc_ops->keys_contiguous(cur, &info->high_key,
&rec_key);
if (key_contig == XBTREE_KEY_OVERLAP &&
!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
return -EFSCORRUPTED;
if (key_contig == XBTREE_KEY_GAP)
return -ECANCELED;
}
/*
* If high_key(rec) is larger than any other high key we've seen,
* remember it for later.
*/
cur->bc_ops->init_high_key_from_rec(&rec_high_key, rec);
if (xfs_btree_keycmp_gt(cur, &rec_high_key, &info->high_key))
info->high_key = rec_high_key; /* struct copy */
return 0;
} }
/* Is there a record covering a given range of keys? */ /*
* Scan part of the keyspace of a btree and tell us if that keyspace does not
* map to any records; is fully mapped to records; or is partially mapped to
* records. This is the btree record equivalent to determining if a file is
* sparse.
*/
int int
xfs_btree_has_record( xfs_btree_has_records(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
const union xfs_btree_irec *low, const union xfs_btree_irec *low,
const union xfs_btree_irec *high, const union xfs_btree_irec *high,
bool *exists) enum xbtree_recpacking *outcome)
{ {
struct xfs_btree_has_records info = {
.outcome = XBTREE_RECPACKING_EMPTY,
};
int error; int error;
error = xfs_btree_query_range(cur, low, high, /* Not all btrees support this operation. */
&xfs_btree_has_record_helper, NULL); if (!cur->bc_ops->keys_contiguous) {
if (error == -ECANCELED) { ASSERT(0);
*exists = true; return -EOPNOTSUPP;
return 0;
} }
*exists = false;
return error; xfs_btree_key_from_irec(cur, &info.start_key, low);
xfs_btree_key_from_irec(cur, &info.end_key, high);
error = xfs_btree_query_range(cur, low, high,
xfs_btree_has_records_helper, &info);
if (error == -ECANCELED)
goto out;
if (error)
return error;
if (info.outcome == XBTREE_RECPACKING_EMPTY)
goto out;
/*
* If the largest high_key(rec) we saw during the walk is greater than
* the end of the search range, classify this as full. Otherwise,
* there is a hole at the end of the search range.
*/
if (xfs_btree_keycmp_ge(cur, &info.high_key, &info.end_key))
info.outcome = XBTREE_RECPACKING_FULL;
out:
*outcome = info.outcome;
return 0;
} }
/* Are there more records in this btree? */ /* Are there more records in this btree? */
......
...@@ -90,6 +90,27 @@ uint32_t xfs_btree_magic(int crc, xfs_btnum_t btnum); ...@@ -90,6 +90,27 @@ uint32_t xfs_btree_magic(int crc, xfs_btnum_t btnum);
#define XFS_BTREE_STATS_ADD(cur, stat, val) \ #define XFS_BTREE_STATS_ADD(cur, stat, val) \
XFS_STATS_ADD_OFF((cur)->bc_mp, (cur)->bc_statoff + __XBTS_ ## stat, val) XFS_STATS_ADD_OFF((cur)->bc_mp, (cur)->bc_statoff + __XBTS_ ## stat, val)
enum xbtree_key_contig {
XBTREE_KEY_GAP = 0,
XBTREE_KEY_CONTIGUOUS,
XBTREE_KEY_OVERLAP,
};
/*
* Decide if these two numeric btree key fields are contiguous, overlapping,
* or if there's a gap between them. @x should be the field from the high
* key and @y should be the field from the low key.
*/
static inline enum xbtree_key_contig xbtree_key_contig(uint64_t x, uint64_t y)
{
x++;
if (x < y)
return XBTREE_KEY_GAP;
if (x == y)
return XBTREE_KEY_CONTIGUOUS;
return XBTREE_KEY_OVERLAP;
}
struct xfs_btree_ops { struct xfs_btree_ops {
/* size of the key and record structures */ /* size of the key and record structures */
size_t key_len; size_t key_len;
...@@ -157,6 +178,19 @@ struct xfs_btree_ops { ...@@ -157,6 +178,19 @@ struct xfs_btree_ops {
int (*recs_inorder)(struct xfs_btree_cur *cur, int (*recs_inorder)(struct xfs_btree_cur *cur,
const union xfs_btree_rec *r1, const union xfs_btree_rec *r1,
const union xfs_btree_rec *r2); const union xfs_btree_rec *r2);
/*
* Are these two btree keys immediately adjacent?
*
* Given two btree keys @key1 and @key2, decide if it is impossible for
* there to be a third btree key K satisfying the relationship
* @key1 < K < @key2. To determine if two btree records are
* immediately adjacent, @key1 should be the high key of the first
* record and @key2 should be the low key of the second record.
*/
enum xbtree_key_contig (*keys_contiguous)(struct xfs_btree_cur *cur,
const union xfs_btree_key *key1,
const union xfs_btree_key *key2);
}; };
/* /*
...@@ -540,9 +574,15 @@ void xfs_btree_get_keys(struct xfs_btree_cur *cur, ...@@ -540,9 +574,15 @@ void xfs_btree_get_keys(struct xfs_btree_cur *cur,
struct xfs_btree_block *block, union xfs_btree_key *key); struct xfs_btree_block *block, union xfs_btree_key *key);
union xfs_btree_key *xfs_btree_high_key_from_key(struct xfs_btree_cur *cur, union xfs_btree_key *xfs_btree_high_key_from_key(struct xfs_btree_cur *cur,
union xfs_btree_key *key); union xfs_btree_key *key);
int xfs_btree_has_record(struct xfs_btree_cur *cur, typedef bool (*xfs_btree_key_gap_fn)(struct xfs_btree_cur *cur,
const union xfs_btree_key *key1,
const union xfs_btree_key *key2);
int xfs_btree_has_records(struct xfs_btree_cur *cur,
const union xfs_btree_irec *low, const union xfs_btree_irec *low,
const union xfs_btree_irec *high, bool *exists); const union xfs_btree_irec *high,
enum xbtree_recpacking *outcome);
bool xfs_btree_has_more_records(struct xfs_btree_cur *cur); bool xfs_btree_has_more_records(struct xfs_btree_cur *cur);
struct xfs_ifork *xfs_btree_ifork_ptr(struct xfs_btree_cur *cur); struct xfs_ifork *xfs_btree_ifork_ptr(struct xfs_btree_cur *cur);
......
...@@ -383,6 +383,16 @@ xfs_inobt_recs_inorder( ...@@ -383,6 +383,16 @@ xfs_inobt_recs_inorder(
be32_to_cpu(r2->inobt.ir_startino); be32_to_cpu(r2->inobt.ir_startino);
} }
STATIC enum xbtree_key_contig
xfs_inobt_keys_contiguous(
struct xfs_btree_cur *cur,
const union xfs_btree_key *key1,
const union xfs_btree_key *key2)
{
return xbtree_key_contig(be32_to_cpu(key1->inobt.ir_startino),
be32_to_cpu(key2->inobt.ir_startino));
}
static const struct xfs_btree_ops xfs_inobt_ops = { static const struct xfs_btree_ops xfs_inobt_ops = {
.rec_len = sizeof(xfs_inobt_rec_t), .rec_len = sizeof(xfs_inobt_rec_t),
.key_len = sizeof(xfs_inobt_key_t), .key_len = sizeof(xfs_inobt_key_t),
...@@ -402,6 +412,7 @@ static const struct xfs_btree_ops xfs_inobt_ops = { ...@@ -402,6 +412,7 @@ static const struct xfs_btree_ops xfs_inobt_ops = {
.diff_two_keys = xfs_inobt_diff_two_keys, .diff_two_keys = xfs_inobt_diff_two_keys,
.keys_inorder = xfs_inobt_keys_inorder, .keys_inorder = xfs_inobt_keys_inorder,
.recs_inorder = xfs_inobt_recs_inorder, .recs_inorder = xfs_inobt_recs_inorder,
.keys_contiguous = xfs_inobt_keys_contiguous,
}; };
static const struct xfs_btree_ops xfs_finobt_ops = { static const struct xfs_btree_ops xfs_finobt_ops = {
...@@ -423,6 +434,7 @@ static const struct xfs_btree_ops xfs_finobt_ops = { ...@@ -423,6 +434,7 @@ static const struct xfs_btree_ops xfs_finobt_ops = {
.diff_two_keys = xfs_inobt_diff_two_keys, .diff_two_keys = xfs_inobt_diff_two_keys,
.keys_inorder = xfs_inobt_keys_inorder, .keys_inorder = xfs_inobt_keys_inorder,
.recs_inorder = xfs_inobt_recs_inorder, .recs_inorder = xfs_inobt_recs_inorder,
.keys_contiguous = xfs_inobt_keys_contiguous,
}; };
/* /*
......
...@@ -1998,14 +1998,17 @@ xfs_refcount_recover_cow_leftovers( ...@@ -1998,14 +1998,17 @@ xfs_refcount_recover_cow_leftovers(
return error; return error;
} }
/* Is there a record covering a given extent? */ /*
* Scan part of the keyspace of the refcount records and tell us if the area
* has no records, is fully mapped by records, or is partially filled.
*/
int int
xfs_refcount_has_record( xfs_refcount_has_records(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
enum xfs_refc_domain domain, enum xfs_refc_domain domain,
xfs_agblock_t bno, xfs_agblock_t bno,
xfs_extlen_t len, xfs_extlen_t len,
bool *exists) enum xbtree_recpacking *outcome)
{ {
union xfs_btree_irec low; union xfs_btree_irec low;
union xfs_btree_irec high; union xfs_btree_irec high;
...@@ -2016,7 +2019,7 @@ xfs_refcount_has_record( ...@@ -2016,7 +2019,7 @@ xfs_refcount_has_record(
high.rc.rc_startblock = bno + len - 1; high.rc.rc_startblock = bno + len - 1;
low.rc.rc_domain = high.rc.rc_domain = domain; low.rc.rc_domain = high.rc.rc_domain = domain;
return xfs_btree_has_record(cur, &low, &high, exists); return xfs_btree_has_records(cur, &low, &high, outcome);
} }
int __init int __init
......
...@@ -111,9 +111,9 @@ extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp, ...@@ -111,9 +111,9 @@ extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp,
*/ */
#define XFS_REFCOUNT_ITEM_OVERHEAD 32 #define XFS_REFCOUNT_ITEM_OVERHEAD 32
extern int xfs_refcount_has_record(struct xfs_btree_cur *cur, extern int xfs_refcount_has_records(struct xfs_btree_cur *cur,
enum xfs_refc_domain domain, xfs_agblock_t bno, enum xfs_refc_domain domain, xfs_agblock_t bno,
xfs_extlen_t len, bool *exists); xfs_extlen_t len, enum xbtree_recpacking *outcome);
union xfs_btree_rec; union xfs_btree_rec;
extern void xfs_refcount_btrec_to_irec(const union xfs_btree_rec *rec, extern void xfs_refcount_btrec_to_irec(const union xfs_btree_rec *rec,
struct xfs_refcount_irec *irec); struct xfs_refcount_irec *irec);
......
...@@ -300,6 +300,16 @@ xfs_refcountbt_recs_inorder( ...@@ -300,6 +300,16 @@ xfs_refcountbt_recs_inorder(
be32_to_cpu(r2->refc.rc_startblock); be32_to_cpu(r2->refc.rc_startblock);
} }
STATIC enum xbtree_key_contig
xfs_refcountbt_keys_contiguous(
struct xfs_btree_cur *cur,
const union xfs_btree_key *key1,
const union xfs_btree_key *key2)
{
return xbtree_key_contig(be32_to_cpu(key1->refc.rc_startblock),
be32_to_cpu(key2->refc.rc_startblock));
}
static const struct xfs_btree_ops xfs_refcountbt_ops = { static const struct xfs_btree_ops xfs_refcountbt_ops = {
.rec_len = sizeof(struct xfs_refcount_rec), .rec_len = sizeof(struct xfs_refcount_rec),
.key_len = sizeof(struct xfs_refcount_key), .key_len = sizeof(struct xfs_refcount_key),
...@@ -319,6 +329,7 @@ static const struct xfs_btree_ops xfs_refcountbt_ops = { ...@@ -319,6 +329,7 @@ static const struct xfs_btree_ops xfs_refcountbt_ops = {
.diff_two_keys = xfs_refcountbt_diff_two_keys, .diff_two_keys = xfs_refcountbt_diff_two_keys,
.keys_inorder = xfs_refcountbt_keys_inorder, .keys_inorder = xfs_refcountbt_keys_inorder,
.recs_inorder = xfs_refcountbt_recs_inorder, .recs_inorder = xfs_refcountbt_recs_inorder,
.keys_contiguous = xfs_refcountbt_keys_contiguous,
}; };
/* /*
......
...@@ -2709,13 +2709,17 @@ xfs_rmap_compare( ...@@ -2709,13 +2709,17 @@ xfs_rmap_compare(
return 0; return 0;
} }
/* Is there a record covering a given extent? */ /*
* Scan the physical storage part of the keyspace of the reverse mapping index
* and tell us if the area has no records, is fully mapped by records, or is
* partially filled.
*/
int int
xfs_rmap_has_record( xfs_rmap_has_records(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
xfs_agblock_t bno, xfs_agblock_t bno,
xfs_extlen_t len, xfs_extlen_t len,
bool *exists) enum xbtree_recpacking *outcome)
{ {
union xfs_btree_irec low; union xfs_btree_irec low;
union xfs_btree_irec high; union xfs_btree_irec high;
...@@ -2725,7 +2729,7 @@ xfs_rmap_has_record( ...@@ -2725,7 +2729,7 @@ xfs_rmap_has_record(
memset(&high, 0xFF, sizeof(high)); memset(&high, 0xFF, sizeof(high));
high.r.rm_startblock = bno + len - 1; high.r.rm_startblock = bno + len - 1;
return xfs_btree_has_record(cur, &low, &high, exists); return xfs_btree_has_records(cur, &low, &high, outcome);
} }
/* /*
......
...@@ -198,8 +198,8 @@ xfs_failaddr_t xfs_rmap_btrec_to_irec(const union xfs_btree_rec *rec, ...@@ -198,8 +198,8 @@ xfs_failaddr_t xfs_rmap_btrec_to_irec(const union xfs_btree_rec *rec,
xfs_failaddr_t xfs_rmap_check_irec(struct xfs_btree_cur *cur, xfs_failaddr_t xfs_rmap_check_irec(struct xfs_btree_cur *cur,
const struct xfs_rmap_irec *irec); const struct xfs_rmap_irec *irec);
int xfs_rmap_has_record(struct xfs_btree_cur *cur, xfs_agblock_t bno, int xfs_rmap_has_records(struct xfs_btree_cur *cur, xfs_agblock_t bno,
xfs_extlen_t len, bool *exists); xfs_extlen_t len, enum xbtree_recpacking *outcome);
int xfs_rmap_record_exists(struct xfs_btree_cur *cur, xfs_agblock_t bno, int xfs_rmap_record_exists(struct xfs_btree_cur *cur, xfs_agblock_t bno,
xfs_extlen_t len, const struct xfs_owner_info *oinfo, xfs_extlen_t len, const struct xfs_owner_info *oinfo,
bool *has_rmap); bool *has_rmap);
......
...@@ -444,6 +444,21 @@ xfs_rmapbt_recs_inorder( ...@@ -444,6 +444,21 @@ xfs_rmapbt_recs_inorder(
return 0; return 0;
} }
STATIC enum xbtree_key_contig
xfs_rmapbt_keys_contiguous(
struct xfs_btree_cur *cur,
const union xfs_btree_key *key1,
const union xfs_btree_key *key2)
{
/*
* We only support checking contiguity of the physical space component.
* If any callers ever need more specificity than that, they'll have to
* implement it here.
*/
return xbtree_key_contig(be32_to_cpu(key1->rmap.rm_startblock),
be32_to_cpu(key2->rmap.rm_startblock));
}
static const struct xfs_btree_ops xfs_rmapbt_ops = { static const struct xfs_btree_ops xfs_rmapbt_ops = {
.rec_len = sizeof(struct xfs_rmap_rec), .rec_len = sizeof(struct xfs_rmap_rec),
.key_len = 2 * sizeof(struct xfs_rmap_key), .key_len = 2 * sizeof(struct xfs_rmap_key),
...@@ -463,6 +478,7 @@ static const struct xfs_btree_ops xfs_rmapbt_ops = { ...@@ -463,6 +478,7 @@ static const struct xfs_btree_ops xfs_rmapbt_ops = {
.diff_two_keys = xfs_rmapbt_diff_two_keys, .diff_two_keys = xfs_rmapbt_diff_two_keys,
.keys_inorder = xfs_rmapbt_keys_inorder, .keys_inorder = xfs_rmapbt_keys_inorder,
.recs_inorder = xfs_rmapbt_recs_inorder, .recs_inorder = xfs_rmapbt_recs_inorder,
.keys_contiguous = xfs_rmapbt_keys_contiguous,
}; };
static struct xfs_btree_cur * static struct xfs_btree_cur *
......
...@@ -204,6 +204,18 @@ enum xfs_ag_resv_type { ...@@ -204,6 +204,18 @@ enum xfs_ag_resv_type {
XFS_AG_RESV_RMAPBT, XFS_AG_RESV_RMAPBT,
}; };
/* Results of scanning a btree keyspace to check occupancy. */
enum xbtree_recpacking {
/* None of the keyspace maps to records. */
XBTREE_RECPACKING_EMPTY = 0,
/* Some, but not all, of the keyspace maps to records. */
XBTREE_RECPACKING_SPARSE,
/* The entire keyspace maps to records. */
XBTREE_RECPACKING_FULL,
};
/* /*
* Type verifier functions * Type verifier functions
*/ */
......
...@@ -144,15 +144,15 @@ xchk_xref_is_used_space( ...@@ -144,15 +144,15 @@ xchk_xref_is_used_space(
xfs_agblock_t agbno, xfs_agblock_t agbno,
xfs_extlen_t len) xfs_extlen_t len)
{ {
bool is_freesp; enum xbtree_recpacking outcome;
int error; int error;
if (!sc->sa.bno_cur || xchk_skip_xref(sc->sm)) if (!sc->sa.bno_cur || xchk_skip_xref(sc->sm))
return; return;
error = xfs_alloc_has_record(sc->sa.bno_cur, agbno, len, &is_freesp); error = xfs_alloc_has_records(sc->sa.bno_cur, agbno, len, &outcome);
if (!xchk_should_check_xref(sc, &error, &sc->sa.bno_cur)) if (!xchk_should_check_xref(sc, &error, &sc->sa.bno_cur))
return; return;
if (is_freesp) if (outcome != XBTREE_RECPACKING_EMPTY)
xchk_btree_xref_set_corrupt(sc, sc->sa.bno_cur, 0); xchk_btree_xref_set_corrupt(sc, sc->sa.bno_cur, 0);
} }
...@@ -457,16 +457,16 @@ xchk_xref_is_not_shared( ...@@ -457,16 +457,16 @@ xchk_xref_is_not_shared(
xfs_agblock_t agbno, xfs_agblock_t agbno,
xfs_extlen_t len) xfs_extlen_t len)
{ {
bool shared; enum xbtree_recpacking outcome;
int error; int error;
if (!sc->sa.refc_cur || xchk_skip_xref(sc->sm)) if (!sc->sa.refc_cur || xchk_skip_xref(sc->sm))
return; return;
error = xfs_refcount_has_record(sc->sa.refc_cur, XFS_REFC_DOMAIN_SHARED, error = xfs_refcount_has_records(sc->sa.refc_cur,
agbno, len, &shared); XFS_REFC_DOMAIN_SHARED, agbno, len, &outcome);
if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur)) if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur))
return; return;
if (shared) if (outcome != XBTREE_RECPACKING_EMPTY)
xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0); xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
} }
...@@ -219,15 +219,15 @@ xchk_xref_has_no_owner( ...@@ -219,15 +219,15 @@ xchk_xref_has_no_owner(
xfs_agblock_t bno, xfs_agblock_t bno,
xfs_extlen_t len) xfs_extlen_t len)
{ {
bool has_rmap; enum xbtree_recpacking outcome;
int error; int error;
if (!sc->sa.rmap_cur || xchk_skip_xref(sc->sm)) if (!sc->sa.rmap_cur || xchk_skip_xref(sc->sm))
return; return;
error = xfs_rmap_has_record(sc->sa.rmap_cur, bno, len, &has_rmap); error = xfs_rmap_has_records(sc->sa.rmap_cur, bno, len, &outcome);
if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur)) if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur))
return; return;
if (has_rmap) if (outcome != XBTREE_RECPACKING_EMPTY)
xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0); xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment