Commit 69115f77 authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: teach scrub to check for sole ownership of metadata objects

Strengthen online scrub's checking even further by enabling us to check
that a range of blocks are owned solely by a given owner.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
parent efc0845f
...@@ -2735,65 +2735,141 @@ xfs_rmap_has_records( ...@@ -2735,65 +2735,141 @@ xfs_rmap_has_records(
return xfs_btree_has_records(cur, &low, &high, &mask, outcome); return xfs_btree_has_records(cur, &low, &high, &mask, outcome);
} }
/* struct xfs_rmap_ownercount {
* Is there a record for this owner completely covering a given physical /* Owner that we're looking for. */
* extent? If so, *has_rmap will be set to true. If there is no record struct xfs_rmap_irec good;
* or the record only covers part of the range, we set *has_rmap to false.
* This function doesn't perform range lookups or offset checks, so it is /* rmap search keys */
* not suitable for checking data fork blocks. struct xfs_rmap_irec low;
*/ struct xfs_rmap_irec high;
int
xfs_rmap_record_exists( struct xfs_rmap_matches *results;
struct xfs_btree_cur *cur,
/* Stop early if we find a nonmatch? */
bool stop_on_nonmatch;
};
/* Does this rmap represent space that can have multiple owners? */
static inline bool
xfs_rmap_shareable(
struct xfs_mount *mp,
const struct xfs_rmap_irec *rmap)
{
if (!xfs_has_reflink(mp))
return false;
if (XFS_RMAP_NON_INODE_OWNER(rmap->rm_owner))
return false;
if (rmap->rm_flags & (XFS_RMAP_ATTR_FORK |
XFS_RMAP_BMBT_BLOCK))
return false;
return true;
}
static inline void
xfs_rmap_ownercount_init(
struct xfs_rmap_ownercount *roc,
xfs_agblock_t bno, xfs_agblock_t bno,
xfs_extlen_t len, xfs_extlen_t len,
const struct xfs_owner_info *oinfo, const struct xfs_owner_info *oinfo,
bool *has_rmap) struct xfs_rmap_matches *results)
{ {
uint64_t owner; memset(roc, 0, sizeof(*roc));
uint64_t offset; roc->results = results;
unsigned int flags;
int has_record; roc->low.rm_startblock = bno;
struct xfs_rmap_irec irec; memset(&roc->high, 0xFF, sizeof(roc->high));
int error; roc->high.rm_startblock = bno + len - 1;
memset(results, 0, sizeof(*results));
roc->good.rm_startblock = bno;
roc->good.rm_blockcount = len;
roc->good.rm_owner = oinfo->oi_owner;
roc->good.rm_offset = oinfo->oi_offset;
if (oinfo->oi_flags & XFS_OWNER_INFO_ATTR_FORK)
roc->good.rm_flags |= XFS_RMAP_ATTR_FORK;
if (oinfo->oi_flags & XFS_OWNER_INFO_BMBT_BLOCK)
roc->good.rm_flags |= XFS_RMAP_BMBT_BLOCK;
}
xfs_owner_info_unpack(oinfo, &owner, &offset, &flags); /* Figure out if this is a match for the owner. */
ASSERT(XFS_RMAP_NON_INODE_OWNER(owner) || STATIC int
(flags & XFS_RMAP_BMBT_BLOCK)); xfs_rmap_count_owners_helper(
struct xfs_btree_cur *cur,
const struct xfs_rmap_irec *rec,
void *priv)
{
struct xfs_rmap_ownercount *roc = priv;
struct xfs_rmap_irec check = *rec;
unsigned int keyflags;
bool filedata;
int64_t delta;
filedata = !XFS_RMAP_NON_INODE_OWNER(check.rm_owner) &&
!(check.rm_flags & XFS_RMAP_BMBT_BLOCK);
/* Trim the part of check that comes before the comparison range. */
delta = (int64_t)roc->good.rm_startblock - check.rm_startblock;
if (delta > 0) {
check.rm_startblock += delta;
check.rm_blockcount -= delta;
if (filedata)
check.rm_offset += delta;
}
error = xfs_rmap_lookup_le(cur, bno, owner, offset, flags, &irec, /* Trim the part of check that comes after the comparison range. */
&has_record); delta = (check.rm_startblock + check.rm_blockcount) -
if (error) (roc->good.rm_startblock + roc->good.rm_blockcount);
return error; if (delta > 0)
if (!has_record) { check.rm_blockcount -= delta;
*has_rmap = false;
return 0; /* Don't care about unwritten status for establishing ownership. */
keyflags = check.rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK);
if (check.rm_startblock == roc->good.rm_startblock &&
check.rm_blockcount == roc->good.rm_blockcount &&
check.rm_owner == roc->good.rm_owner &&
check.rm_offset == roc->good.rm_offset &&
keyflags == roc->good.rm_flags) {
roc->results->matches++;
} else {
roc->results->non_owner_matches++;
if (xfs_rmap_shareable(cur->bc_mp, &roc->good) ^
xfs_rmap_shareable(cur->bc_mp, &check))
roc->results->bad_non_owner_matches++;
} }
*has_rmap = (irec.rm_owner == owner && irec.rm_startblock <= bno && if (roc->results->non_owner_matches && roc->stop_on_nonmatch)
irec.rm_startblock + irec.rm_blockcount >= bno + len); return -ECANCELED;
return 0; return 0;
} }
struct xfs_rmap_key_state { /* Count the number of owners and non-owners of this range of blocks. */
uint64_t owner; int
uint64_t offset; xfs_rmap_count_owners(
unsigned int flags;
};
/* For each rmap given, figure out if it doesn't match the key we want. */
STATIC int
xfs_rmap_has_other_keys_helper(
struct xfs_btree_cur *cur, struct xfs_btree_cur *cur,
const struct xfs_rmap_irec *rec, xfs_agblock_t bno,
void *priv) xfs_extlen_t len,
const struct xfs_owner_info *oinfo,
struct xfs_rmap_matches *results)
{ {
struct xfs_rmap_key_state *rks = priv; struct xfs_rmap_ownercount roc;
int error;
if (rks->owner == rec->rm_owner && rks->offset == rec->rm_offset && xfs_rmap_ownercount_init(&roc, bno, len, oinfo, results);
((rks->flags & rec->rm_flags) & XFS_RMAP_KEY_FLAGS) == rks->flags) error = xfs_rmap_query_range(cur, &roc.low, &roc.high,
return 0; xfs_rmap_count_owners_helper, &roc);
return -ECANCELED; if (error)
return error;
/*
* There can't be any non-owner rmaps that conflict with the given
* owner if we didn't find any rmaps matching the owner.
*/
if (!results->matches)
results->bad_non_owner_matches = 0;
return 0;
} }
/* /*
...@@ -2806,28 +2882,26 @@ xfs_rmap_has_other_keys( ...@@ -2806,28 +2882,26 @@ xfs_rmap_has_other_keys(
xfs_agblock_t bno, xfs_agblock_t bno,
xfs_extlen_t len, xfs_extlen_t len,
const struct xfs_owner_info *oinfo, const struct xfs_owner_info *oinfo,
bool *has_rmap) bool *has_other)
{ {
struct xfs_rmap_irec low = {0}; struct xfs_rmap_matches res;
struct xfs_rmap_irec high; struct xfs_rmap_ownercount roc;
struct xfs_rmap_key_state rks;
int error; int error;
xfs_owner_info_unpack(oinfo, &rks.owner, &rks.offset, &rks.flags); xfs_rmap_ownercount_init(&roc, bno, len, oinfo, &res);
*has_rmap = false; roc.stop_on_nonmatch = true;
low.rm_startblock = bno;
memset(&high, 0xFF, sizeof(high));
high.rm_startblock = bno + len - 1;
error = xfs_rmap_query_range(cur, &low, &high, error = xfs_rmap_query_range(cur, &roc.low, &roc.high,
xfs_rmap_has_other_keys_helper, &rks); xfs_rmap_count_owners_helper, &roc);
if (error == -ECANCELED) { if (error == -ECANCELED) {
*has_rmap = true; *has_other = true;
return 0; return 0;
} }
if (error)
return error;
return error; *has_other = false;
return 0;
} }
const struct xfs_owner_info XFS_RMAP_OINFO_SKIP_UPDATE = { const struct xfs_owner_info XFS_RMAP_OINFO_SKIP_UPDATE = {
......
...@@ -200,12 +200,24 @@ xfs_failaddr_t xfs_rmap_check_irec(struct xfs_btree_cur *cur, ...@@ -200,12 +200,24 @@ xfs_failaddr_t xfs_rmap_check_irec(struct xfs_btree_cur *cur,
int xfs_rmap_has_records(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, enum xbtree_recpacking *outcome); xfs_extlen_t len, enum xbtree_recpacking *outcome);
int xfs_rmap_record_exists(struct xfs_btree_cur *cur, xfs_agblock_t bno,
struct xfs_rmap_matches {
/* Number of owner matches. */
unsigned long long matches;
/* Number of non-owner matches. */
unsigned long long non_owner_matches;
/* Number of non-owner matches that conflict with the owner matches. */
unsigned long long bad_non_owner_matches;
};
int xfs_rmap_count_owners(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); struct xfs_rmap_matches *rmatch);
int xfs_rmap_has_other_keys(struct xfs_btree_cur *cur, xfs_agblock_t bno, int xfs_rmap_has_other_keys(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_other);
int xfs_rmap_map_raw(struct xfs_btree_cur *cur, struct xfs_rmap_irec *rmap); int xfs_rmap_map_raw(struct xfs_btree_cur *cur, struct xfs_rmap_irec *rmap);
extern const struct xfs_owner_info XFS_RMAP_OINFO_SKIP_UPDATE; extern const struct xfs_owner_info XFS_RMAP_OINFO_SKIP_UPDATE;
......
...@@ -51,7 +51,7 @@ xchk_superblock_xref( ...@@ -51,7 +51,7 @@ xchk_superblock_xref(
xchk_xref_is_used_space(sc, agbno, 1); xchk_xref_is_used_space(sc, agbno, 1);
xchk_xref_is_not_inode_chunk(sc, agbno, 1); xchk_xref_is_not_inode_chunk(sc, agbno, 1);
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS); xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
xchk_xref_is_not_shared(sc, agbno, 1); xchk_xref_is_not_shared(sc, agbno, 1);
xchk_xref_is_not_cow_staging(sc, agbno, 1); xchk_xref_is_not_cow_staging(sc, agbno, 1);
...@@ -515,7 +515,7 @@ xchk_agf_xref( ...@@ -515,7 +515,7 @@ xchk_agf_xref(
xchk_agf_xref_freeblks(sc); xchk_agf_xref_freeblks(sc);
xchk_agf_xref_cntbt(sc); xchk_agf_xref_cntbt(sc);
xchk_xref_is_not_inode_chunk(sc, agbno, 1); xchk_xref_is_not_inode_chunk(sc, agbno, 1);
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS); xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
xchk_agf_xref_btreeblks(sc); xchk_agf_xref_btreeblks(sc);
xchk_xref_is_not_shared(sc, agbno, 1); xchk_xref_is_not_shared(sc, agbno, 1);
xchk_xref_is_not_cow_staging(sc, agbno, 1); xchk_xref_is_not_cow_staging(sc, agbno, 1);
...@@ -644,7 +644,7 @@ xchk_agfl_block_xref( ...@@ -644,7 +644,7 @@ xchk_agfl_block_xref(
xchk_xref_is_used_space(sc, agbno, 1); xchk_xref_is_used_space(sc, agbno, 1);
xchk_xref_is_not_inode_chunk(sc, agbno, 1); xchk_xref_is_not_inode_chunk(sc, agbno, 1);
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_AG); xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_AG);
xchk_xref_is_not_shared(sc, agbno, 1); xchk_xref_is_not_shared(sc, agbno, 1);
xchk_xref_is_not_cow_staging(sc, agbno, 1); xchk_xref_is_not_cow_staging(sc, agbno, 1);
} }
...@@ -701,7 +701,7 @@ xchk_agfl_xref( ...@@ -701,7 +701,7 @@ xchk_agfl_xref(
xchk_xref_is_used_space(sc, agbno, 1); xchk_xref_is_used_space(sc, agbno, 1);
xchk_xref_is_not_inode_chunk(sc, agbno, 1); xchk_xref_is_not_inode_chunk(sc, agbno, 1);
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS); xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
xchk_xref_is_not_shared(sc, agbno, 1); xchk_xref_is_not_shared(sc, agbno, 1);
xchk_xref_is_not_cow_staging(sc, agbno, 1); xchk_xref_is_not_cow_staging(sc, agbno, 1);
...@@ -857,7 +857,7 @@ xchk_agi_xref( ...@@ -857,7 +857,7 @@ xchk_agi_xref(
xchk_xref_is_used_space(sc, agbno, 1); xchk_xref_is_used_space(sc, agbno, 1);
xchk_xref_is_not_inode_chunk(sc, agbno, 1); xchk_xref_is_not_inode_chunk(sc, agbno, 1);
xchk_agi_xref_icounts(sc); xchk_agi_xref_icounts(sc);
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS); xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
xchk_xref_is_not_shared(sc, agbno, 1); xchk_xref_is_not_shared(sc, agbno, 1);
xchk_xref_is_not_cow_staging(sc, agbno, 1); xchk_xref_is_not_cow_staging(sc, agbno, 1);
xchk_agi_xref_fiblocks(sc); xchk_agi_xref_fiblocks(sc);
......
...@@ -402,7 +402,7 @@ xchk_btree_check_block_owner( ...@@ -402,7 +402,7 @@ xchk_btree_check_block_owner(
if (!bs->sc->sa.bno_cur && btnum == XFS_BTNUM_BNO) if (!bs->sc->sa.bno_cur && btnum == XFS_BTNUM_BNO)
bs->cur = NULL; bs->cur = NULL;
xchk_xref_is_owned_by(bs->sc, agbno, 1, bs->oinfo); xchk_xref_is_only_owned_by(bs->sc, agbno, 1, bs->oinfo);
if (!bs->sc->sa.rmap_cur && btnum == XFS_BTNUM_RMAP) if (!bs->sc->sa.rmap_cur && btnum == XFS_BTNUM_RMAP)
bs->cur = NULL; bs->cur = NULL;
......
...@@ -276,7 +276,7 @@ xchk_iallocbt_chunk( ...@@ -276,7 +276,7 @@ xchk_iallocbt_chunk(
xchk_inobt_chunk_xref_finobt(sc, irec, agino, nr_inodes); xchk_inobt_chunk_xref_finobt(sc, irec, agino, nr_inodes);
else else
xchk_finobt_chunk_xref_inobt(sc, irec, agino, nr_inodes); xchk_finobt_chunk_xref_inobt(sc, irec, agino, nr_inodes);
xchk_xref_is_owned_by(sc, agbno, len, &XFS_RMAP_OINFO_INODES); xchk_xref_is_only_owned_by(sc, agbno, len, &XFS_RMAP_OINFO_INODES);
xchk_xref_is_not_shared(sc, agbno, len); xchk_xref_is_not_shared(sc, agbno, len);
xchk_xref_is_not_cow_staging(sc, agbno, len); xchk_xref_is_not_cow_staging(sc, agbno, len);
return true; return true;
...@@ -428,7 +428,7 @@ xchk_iallocbt_check_cluster( ...@@ -428,7 +428,7 @@ xchk_iallocbt_check_cluster(
return 0; return 0;
} }
xchk_xref_is_owned_by(bs->sc, agbno, M_IGEO(mp)->blocks_per_cluster, xchk_xref_is_only_owned_by(bs->sc, agbno, M_IGEO(mp)->blocks_per_cluster,
&XFS_RMAP_OINFO_INODES); &XFS_RMAP_OINFO_INODES);
/* Grab the inode cluster buffer. */ /* Grab the inode cluster buffer. */
......
...@@ -556,7 +556,7 @@ xchk_inode_xref( ...@@ -556,7 +556,7 @@ xchk_inode_xref(
xchk_xref_is_used_space(sc, agbno, 1); xchk_xref_is_used_space(sc, agbno, 1);
xchk_inode_xref_finobt(sc, ino); xchk_inode_xref_finobt(sc, ino);
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_INODES); xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_INODES);
xchk_xref_is_not_shared(sc, agbno, 1); xchk_xref_is_not_shared(sc, agbno, 1);
xchk_xref_is_not_cow_staging(sc, agbno, 1); xchk_xref_is_not_cow_staging(sc, agbno, 1);
xchk_inode_xref_bmap(sc, dip); xchk_inode_xref_bmap(sc, dip);
......
...@@ -167,38 +167,29 @@ xchk_rmapbt( ...@@ -167,38 +167,29 @@ xchk_rmapbt(
&XFS_RMAP_OINFO_AG, NULL); &XFS_RMAP_OINFO_AG, NULL);
} }
/* xref check that the extent is owned by a given owner */ /* xref check that the extent is owned only by a given owner */
static inline void void
xchk_xref_check_owner( xchk_xref_is_only_owned_by(
struct xfs_scrub *sc, struct xfs_scrub *sc,
xfs_agblock_t bno, xfs_agblock_t bno,
xfs_extlen_t len, xfs_extlen_t len,
const struct xfs_owner_info *oinfo, const struct xfs_owner_info *oinfo)
bool should_have_rmap)
{ {
bool has_rmap; struct xfs_rmap_matches res;
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_record_exists(sc->sa.rmap_cur, bno, len, oinfo, error = xfs_rmap_count_owners(sc->sa.rmap_cur, bno, len, oinfo, &res);
&has_rmap);
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 != should_have_rmap) if (res.matches != 1)
xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
if (res.bad_non_owner_matches)
xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
if (res.non_owner_matches)
xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0); xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
}
/* xref check that the extent is owned by a given owner */
void
xchk_xref_is_owned_by(
struct xfs_scrub *sc,
xfs_agblock_t bno,
xfs_extlen_t len,
const struct xfs_owner_info *oinfo)
{
xchk_xref_check_owner(sc, bno, len, oinfo, true);
} }
/* xref check that the extent is not owned by a given owner */ /* xref check that the extent is not owned by a given owner */
...@@ -209,7 +200,19 @@ xchk_xref_is_not_owned_by( ...@@ -209,7 +200,19 @@ xchk_xref_is_not_owned_by(
xfs_extlen_t len, xfs_extlen_t len,
const struct xfs_owner_info *oinfo) const struct xfs_owner_info *oinfo)
{ {
xchk_xref_check_owner(sc, bno, len, oinfo, false); struct xfs_rmap_matches res;
int error;
if (!sc->sa.rmap_cur || xchk_skip_xref(sc->sm))
return;
error = xfs_rmap_count_owners(sc->sa.rmap_cur, bno, len, oinfo, &res);
if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur))
return;
if (res.matches != 0)
xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
if (res.bad_non_owner_matches)
xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
} }
/* xref check that the extent has no reverse mapping at all */ /* xref check that the extent has no reverse mapping at all */
......
...@@ -162,7 +162,7 @@ void xchk_xref_is_not_inode_chunk(struct xfs_scrub *sc, xfs_agblock_t agbno, ...@@ -162,7 +162,7 @@ void xchk_xref_is_not_inode_chunk(struct xfs_scrub *sc, xfs_agblock_t agbno,
xfs_extlen_t len); xfs_extlen_t len);
void xchk_xref_is_inode_chunk(struct xfs_scrub *sc, xfs_agblock_t agbno, void xchk_xref_is_inode_chunk(struct xfs_scrub *sc, xfs_agblock_t agbno,
xfs_extlen_t len); xfs_extlen_t len);
void xchk_xref_is_owned_by(struct xfs_scrub *sc, xfs_agblock_t agbno, void xchk_xref_is_only_owned_by(struct xfs_scrub *sc, xfs_agblock_t agbno,
xfs_extlen_t len, const struct xfs_owner_info *oinfo); xfs_extlen_t len, const struct xfs_owner_info *oinfo);
void xchk_xref_is_not_owned_by(struct xfs_scrub *sc, xfs_agblock_t agbno, void xchk_xref_is_not_owned_by(struct xfs_scrub *sc, xfs_agblock_t agbno,
xfs_extlen_t len, const struct xfs_owner_info *oinfo); xfs_extlen_t len, const struct xfs_owner_info *oinfo);
......
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