Commit d852657c authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: cross-reference reverse-mapping btree

When scrubbing various btrees, we should cross-reference the records
with the reverse mapping btree and ensure that traversing the btree
finds the same number of blocks that the rmapbt thinks are owned by
that btree.
Signed-off-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
parent 2e6f2756
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "xfs_inode.h" #include "xfs_inode.h"
#include "xfs_alloc.h" #include "xfs_alloc.h"
#include "xfs_ialloc.h" #include "xfs_ialloc.h"
#include "xfs_rmap.h"
#include "scrub/xfs_scrub.h" #include "scrub/xfs_scrub.h"
#include "scrub/scrub.h" #include "scrub/scrub.h"
#include "scrub/common.h" #include "scrub/common.h"
...@@ -107,6 +108,7 @@ xfs_scrub_superblock_xref( ...@@ -107,6 +108,7 @@ xfs_scrub_superblock_xref(
struct xfs_scrub_context *sc, struct xfs_scrub_context *sc,
struct xfs_buf *bp) struct xfs_buf *bp)
{ {
struct xfs_owner_info oinfo;
struct xfs_mount *mp = sc->mp; struct xfs_mount *mp = sc->mp;
xfs_agnumber_t agno = sc->sm->sm_agno; xfs_agnumber_t agno = sc->sm->sm_agno;
xfs_agblock_t agbno; xfs_agblock_t agbno;
...@@ -123,6 +125,8 @@ xfs_scrub_superblock_xref( ...@@ -123,6 +125,8 @@ xfs_scrub_superblock_xref(
xfs_scrub_xref_is_used_space(sc, agbno, 1); xfs_scrub_xref_is_used_space(sc, agbno, 1);
xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1); xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
/* scrub teardown will take care of sc->sa for us */ /* scrub teardown will take care of sc->sa for us */
} }
...@@ -487,11 +491,58 @@ xfs_scrub_agf_xref_cntbt( ...@@ -487,11 +491,58 @@ xfs_scrub_agf_xref_cntbt(
xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp); xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
} }
/* Check the btree block counts in the AGF against the btrees. */
STATIC void
xfs_scrub_agf_xref_btreeblks(
struct xfs_scrub_context *sc)
{
struct xfs_agf *agf = XFS_BUF_TO_AGF(sc->sa.agf_bp);
struct xfs_mount *mp = sc->mp;
xfs_agblock_t blocks;
xfs_agblock_t btreeblks;
int error;
/* Check agf_rmap_blocks; set up for agf_btreeblks check */
if (sc->sa.rmap_cur) {
error = xfs_btree_count_blocks(sc->sa.rmap_cur, &blocks);
if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur))
return;
btreeblks = blocks - 1;
if (blocks != be32_to_cpu(agf->agf_rmap_blocks))
xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
} else {
btreeblks = 0;
}
/*
* No rmap cursor; we can't xref if we have the rmapbt feature.
* We also can't do it if we're missing the free space btree cursors.
*/
if ((xfs_sb_version_hasrmapbt(&mp->m_sb) && !sc->sa.rmap_cur) ||
!sc->sa.bno_cur || !sc->sa.cnt_cur)
return;
/* Check agf_btreeblks */
error = xfs_btree_count_blocks(sc->sa.bno_cur, &blocks);
if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.bno_cur))
return;
btreeblks += blocks - 1;
error = xfs_btree_count_blocks(sc->sa.cnt_cur, &blocks);
if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.cnt_cur))
return;
btreeblks += blocks - 1;
if (btreeblks != be32_to_cpu(agf->agf_btreeblks))
xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
}
/* Cross-reference with the other btrees. */ /* Cross-reference with the other btrees. */
STATIC void STATIC void
xfs_scrub_agf_xref( xfs_scrub_agf_xref(
struct xfs_scrub_context *sc) struct xfs_scrub_context *sc)
{ {
struct xfs_owner_info oinfo;
struct xfs_mount *mp = sc->mp; struct xfs_mount *mp = sc->mp;
xfs_agblock_t agbno; xfs_agblock_t agbno;
int error; int error;
...@@ -509,6 +560,9 @@ xfs_scrub_agf_xref( ...@@ -509,6 +560,9 @@ xfs_scrub_agf_xref(
xfs_scrub_agf_xref_freeblks(sc); xfs_scrub_agf_xref_freeblks(sc);
xfs_scrub_agf_xref_cntbt(sc); xfs_scrub_agf_xref_cntbt(sc);
xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1); xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
xfs_scrub_agf_xref_btreeblks(sc);
/* scrub teardown will take care of sc->sa for us */ /* scrub teardown will take care of sc->sa for us */
} }
...@@ -599,6 +653,7 @@ xfs_scrub_agf( ...@@ -599,6 +653,7 @@ xfs_scrub_agf(
/* AGFL */ /* AGFL */
struct xfs_scrub_agfl_info { struct xfs_scrub_agfl_info {
struct xfs_owner_info oinfo;
unsigned int sz_entries; unsigned int sz_entries;
unsigned int nr_entries; unsigned int nr_entries;
xfs_agblock_t *entries; xfs_agblock_t *entries;
...@@ -608,13 +663,15 @@ struct xfs_scrub_agfl_info { ...@@ -608,13 +663,15 @@ struct xfs_scrub_agfl_info {
STATIC void STATIC void
xfs_scrub_agfl_block_xref( xfs_scrub_agfl_block_xref(
struct xfs_scrub_context *sc, struct xfs_scrub_context *sc,
xfs_agblock_t agbno) xfs_agblock_t agbno,
struct xfs_owner_info *oinfo)
{ {
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
return; return;
xfs_scrub_xref_is_used_space(sc, agbno, 1); xfs_scrub_xref_is_used_space(sc, agbno, 1);
xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1); xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
xfs_scrub_xref_is_owned_by(sc, agbno, 1, oinfo);
} }
/* Scrub an AGFL block. */ /* Scrub an AGFL block. */
...@@ -634,7 +691,7 @@ xfs_scrub_agfl_block( ...@@ -634,7 +691,7 @@ xfs_scrub_agfl_block(
else else
xfs_scrub_block_set_corrupt(sc, sc->sa.agfl_bp); xfs_scrub_block_set_corrupt(sc, sc->sa.agfl_bp);
xfs_scrub_agfl_block_xref(sc, agbno); xfs_scrub_agfl_block_xref(sc, agbno, priv);
return 0; return 0;
} }
...@@ -655,6 +712,7 @@ STATIC void ...@@ -655,6 +712,7 @@ STATIC void
xfs_scrub_agfl_xref( xfs_scrub_agfl_xref(
struct xfs_scrub_context *sc) struct xfs_scrub_context *sc)
{ {
struct xfs_owner_info oinfo;
struct xfs_mount *mp = sc->mp; struct xfs_mount *mp = sc->mp;
xfs_agblock_t agbno; xfs_agblock_t agbno;
int error; int error;
...@@ -670,6 +728,8 @@ xfs_scrub_agfl_xref( ...@@ -670,6 +728,8 @@ xfs_scrub_agfl_xref(
xfs_scrub_xref_is_used_space(sc, agbno, 1); xfs_scrub_xref_is_used_space(sc, agbno, 1);
xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1); xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
/* /*
* Scrub teardown will take care of sc->sa for us. Leave sc->sa * Scrub teardown will take care of sc->sa for us. Leave sc->sa
...@@ -717,6 +777,7 @@ xfs_scrub_agfl( ...@@ -717,6 +777,7 @@ xfs_scrub_agfl(
} }
/* Check the blocks in the AGFL. */ /* Check the blocks in the AGFL. */
xfs_rmap_ag_owner(&sai.oinfo, XFS_RMAP_OWN_AG);
error = xfs_scrub_walk_agfl(sc, xfs_scrub_agfl_block, &sai); error = xfs_scrub_walk_agfl(sc, xfs_scrub_agfl_block, &sai);
if (error) if (error)
goto out_free; goto out_free;
...@@ -770,6 +831,7 @@ STATIC void ...@@ -770,6 +831,7 @@ STATIC void
xfs_scrub_agi_xref( xfs_scrub_agi_xref(
struct xfs_scrub_context *sc) struct xfs_scrub_context *sc)
{ {
struct xfs_owner_info oinfo;
struct xfs_mount *mp = sc->mp; struct xfs_mount *mp = sc->mp;
xfs_agblock_t agbno; xfs_agblock_t agbno;
int error; int error;
...@@ -786,6 +848,8 @@ xfs_scrub_agi_xref( ...@@ -786,6 +848,8 @@ xfs_scrub_agi_xref(
xfs_scrub_xref_is_used_space(sc, agbno, 1); xfs_scrub_xref_is_used_space(sc, agbno, 1);
xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1); xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
xfs_scrub_agi_xref_icounts(sc); xfs_scrub_agi_xref_icounts(sc);
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
/* scrub teardown will take care of sc->sa for us */ /* scrub teardown will take care of sc->sa for us */
} }
......
...@@ -105,6 +105,7 @@ xfs_scrub_allocbt_xref( ...@@ -105,6 +105,7 @@ xfs_scrub_allocbt_xref(
xfs_scrub_allocbt_xref_other(sc, agbno, len); xfs_scrub_allocbt_xref_other(sc, agbno, len);
xfs_scrub_xref_is_not_inode_chunk(sc, agbno, len); xfs_scrub_xref_is_not_inode_chunk(sc, agbno, len);
xfs_scrub_xref_has_no_owner(sc, agbno, len);
} }
/* Scrub a bnobt/cntbt record. */ /* Scrub a bnobt/cntbt record. */
......
...@@ -99,6 +99,139 @@ struct xfs_scrub_bmap_info { ...@@ -99,6 +99,139 @@ struct xfs_scrub_bmap_info {
int whichfork; int whichfork;
}; };
/* Look for a corresponding rmap for this irec. */
static inline bool
xfs_scrub_bmap_get_rmap(
struct xfs_scrub_bmap_info *info,
struct xfs_bmbt_irec *irec,
xfs_agblock_t agbno,
uint64_t owner,
struct xfs_rmap_irec *rmap)
{
xfs_fileoff_t offset;
unsigned int rflags = 0;
int has_rmap;
int error;
if (info->whichfork == XFS_ATTR_FORK)
rflags |= XFS_RMAP_ATTR_FORK;
/*
* CoW staging extents are owned (on disk) by the refcountbt, so
* their rmaps do not have offsets.
*/
if (info->whichfork == XFS_COW_FORK)
offset = 0;
else
offset = irec->br_startoff;
/*
* If the caller thinks this could be a shared bmbt extent (IOWs,
* any data fork extent of a reflink inode) then we have to use the
* range rmap lookup to make sure we get the correct owner/offset.
*/
if (info->is_shared) {
error = xfs_rmap_lookup_le_range(info->sc->sa.rmap_cur, agbno,
owner, offset, rflags, rmap, &has_rmap);
if (!xfs_scrub_should_check_xref(info->sc, &error,
&info->sc->sa.rmap_cur))
return false;
goto out;
}
/*
* Otherwise, use the (faster) regular lookup.
*/
error = xfs_rmap_lookup_le(info->sc->sa.rmap_cur, agbno, 0, owner,
offset, rflags, &has_rmap);
if (!xfs_scrub_should_check_xref(info->sc, &error,
&info->sc->sa.rmap_cur))
return false;
if (!has_rmap)
goto out;
error = xfs_rmap_get_rec(info->sc->sa.rmap_cur, rmap, &has_rmap);
if (!xfs_scrub_should_check_xref(info->sc, &error,
&info->sc->sa.rmap_cur))
return false;
out:
if (!has_rmap)
xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
return has_rmap;
}
/* Make sure that we have rmapbt records for this extent. */
STATIC void
xfs_scrub_bmap_xref_rmap(
struct xfs_scrub_bmap_info *info,
struct xfs_bmbt_irec *irec,
xfs_agblock_t agbno)
{
struct xfs_rmap_irec rmap;
unsigned long long rmap_end;
uint64_t owner;
if (!info->sc->sa.rmap_cur)
return;
if (info->whichfork == XFS_COW_FORK)
owner = XFS_RMAP_OWN_COW;
else
owner = info->sc->ip->i_ino;
/* Find the rmap record for this irec. */
if (!xfs_scrub_bmap_get_rmap(info, irec, agbno, owner, &rmap))
return;
/* Check the rmap. */
rmap_end = (unsigned long long)rmap.rm_startblock + rmap.rm_blockcount;
if (rmap.rm_startblock > agbno ||
agbno + irec->br_blockcount > rmap_end)
xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
/*
* Check the logical offsets if applicable. CoW staging extents
* don't track logical offsets since the mappings only exist in
* memory.
*/
if (info->whichfork != XFS_COW_FORK) {
rmap_end = (unsigned long long)rmap.rm_offset +
rmap.rm_blockcount;
if (rmap.rm_offset > irec->br_startoff ||
irec->br_startoff + irec->br_blockcount > rmap_end)
xfs_scrub_fblock_xref_set_corrupt(info->sc,
info->whichfork, irec->br_startoff);
}
if (rmap.rm_owner != owner)
xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
/*
* Check for discrepancies between the unwritten flag in the irec and
* the rmap. Note that the (in-memory) CoW fork distinguishes between
* unwritten and written extents, but we don't track that in the rmap
* records because the blocks are owned (on-disk) by the refcountbt,
* which doesn't track unwritten state.
*/
if (owner != XFS_RMAP_OWN_COW &&
irec->br_state == XFS_EXT_UNWRITTEN &&
!(rmap.rm_flags & XFS_RMAP_UNWRITTEN))
xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
if (info->whichfork == XFS_ATTR_FORK &&
!(rmap.rm_flags & XFS_RMAP_ATTR_FORK))
xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
if (rmap.rm_flags & XFS_RMAP_BMBT_BLOCK)
xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
}
/* Cross-reference a single rtdev extent record. */ /* Cross-reference a single rtdev extent record. */
STATIC void STATIC void
xfs_scrub_bmap_rt_extent_xref( xfs_scrub_bmap_rt_extent_xref(
...@@ -139,6 +272,7 @@ xfs_scrub_bmap_extent_xref( ...@@ -139,6 +272,7 @@ xfs_scrub_bmap_extent_xref(
xfs_scrub_xref_is_used_space(info->sc, agbno, len); xfs_scrub_xref_is_used_space(info->sc, agbno, len);
xfs_scrub_xref_is_not_inode_chunk(info->sc, agbno, len); xfs_scrub_xref_is_not_inode_chunk(info->sc, agbno, len);
xfs_scrub_bmap_xref_rmap(info, irec, agbno);
xfs_scrub_ag_free(info->sc, &info->sc->sa); xfs_scrub_ag_free(info->sc, &info->sc->sa);
} }
......
...@@ -407,6 +407,10 @@ xfs_scrub_btree_check_block_owner( ...@@ -407,6 +407,10 @@ xfs_scrub_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;
xfs_scrub_xref_is_owned_by(bs->sc, agbno, 1, bs->oinfo);
if (!bs->sc->sa.rmap_cur && btnum == XFS_BTNUM_RMAP)
bs->cur = NULL;
if (init_sa) if (init_sa)
xfs_scrub_ag_free(bs->sc, &bs->sc->sa); xfs_scrub_ag_free(bs->sc, &bs->sc->sa);
......
...@@ -324,6 +324,59 @@ xfs_scrub_set_incomplete( ...@@ -324,6 +324,59 @@ xfs_scrub_set_incomplete(
trace_xfs_scrub_incomplete(sc, __return_address); trace_xfs_scrub_incomplete(sc, __return_address);
} }
/*
* rmap scrubbing -- compute the number of blocks with a given owner,
* at least according to the reverse mapping data.
*/
struct xfs_scrub_rmap_ownedby_info {
struct xfs_owner_info *oinfo;
xfs_filblks_t *blocks;
};
STATIC int
xfs_scrub_count_rmap_ownedby_irec(
struct xfs_btree_cur *cur,
struct xfs_rmap_irec *rec,
void *priv)
{
struct xfs_scrub_rmap_ownedby_info *sroi = priv;
bool irec_attr;
bool oinfo_attr;
irec_attr = rec->rm_flags & XFS_RMAP_ATTR_FORK;
oinfo_attr = sroi->oinfo->oi_flags & XFS_OWNER_INFO_ATTR_FORK;
if (rec->rm_owner != sroi->oinfo->oi_owner)
return 0;
if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) || irec_attr == oinfo_attr)
(*sroi->blocks) += rec->rm_blockcount;
return 0;
}
/*
* Calculate the number of blocks the rmap thinks are owned by something.
* The caller should pass us an rmapbt cursor.
*/
int
xfs_scrub_count_rmap_ownedby_ag(
struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur,
struct xfs_owner_info *oinfo,
xfs_filblks_t *blocks)
{
struct xfs_scrub_rmap_ownedby_info sroi;
sroi.oinfo = oinfo;
*blocks = 0;
sroi.blocks = blocks;
return xfs_rmap_query_all(cur, xfs_scrub_count_rmap_ownedby_irec,
&sroi);
}
/* /*
* AG scrubbing * AG scrubbing
* *
......
...@@ -148,6 +148,10 @@ int xfs_scrub_walk_agfl(struct xfs_scrub_context *sc, ...@@ -148,6 +148,10 @@ int xfs_scrub_walk_agfl(struct xfs_scrub_context *sc,
int (*fn)(struct xfs_scrub_context *, xfs_agblock_t bno, int (*fn)(struct xfs_scrub_context *, xfs_agblock_t bno,
void *), void *),
void *priv); void *priv);
int xfs_scrub_count_rmap_ownedby_ag(struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur,
struct xfs_owner_info *oinfo,
xfs_filblks_t *blocks);
int xfs_scrub_setup_ag_btree(struct xfs_scrub_context *sc, int xfs_scrub_setup_ag_btree(struct xfs_scrub_context *sc,
struct xfs_inode *ip, bool force_log); struct xfs_inode *ip, bool force_log);
......
...@@ -96,11 +96,15 @@ xfs_scrub_iallocbt_chunk_xref( ...@@ -96,11 +96,15 @@ xfs_scrub_iallocbt_chunk_xref(
xfs_agblock_t agbno, xfs_agblock_t agbno,
xfs_extlen_t len) xfs_extlen_t len)
{ {
struct xfs_owner_info oinfo;
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
return; return;
xfs_scrub_xref_is_used_space(sc, agbno, len); xfs_scrub_xref_is_used_space(sc, agbno, len);
xfs_scrub_iallocbt_chunk_xref_other(sc, irec, agino); xfs_scrub_iallocbt_chunk_xref_other(sc, irec, agino);
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
xfs_scrub_xref_is_owned_by(sc, agbno, len, &oinfo);
} }
/* Is this chunk worth checking? */ /* Is this chunk worth checking? */
...@@ -237,8 +241,14 @@ xfs_scrub_iallocbt_check_freemask( ...@@ -237,8 +241,14 @@ xfs_scrub_iallocbt_check_freemask(
} }
/* If any part of this is a hole, skip it. */ /* If any part of this is a hole, skip it. */
if (ir_holemask) if (ir_holemask) {
xfs_scrub_xref_is_not_owned_by(bs->sc, agbno,
blks_per_cluster, &oinfo);
continue; continue;
}
xfs_scrub_xref_is_owned_by(bs->sc, agbno, blks_per_cluster,
&oinfo);
/* Grab the inode cluster buffer. */ /* Grab the inode cluster buffer. */
imap.im_blkno = XFS_AGB_TO_DADDR(mp, bs->cur->bc_private.a.agno, imap.im_blkno = XFS_AGB_TO_DADDR(mp, bs->cur->bc_private.a.agno,
...@@ -274,6 +284,7 @@ xfs_scrub_iallocbt_rec( ...@@ -274,6 +284,7 @@ xfs_scrub_iallocbt_rec(
union xfs_btree_rec *rec) union xfs_btree_rec *rec)
{ {
struct xfs_mount *mp = bs->cur->bc_mp; struct xfs_mount *mp = bs->cur->bc_mp;
xfs_filblks_t *inode_blocks = bs->private;
struct xfs_inobt_rec_incore irec; struct xfs_inobt_rec_incore irec;
uint64_t holes; uint64_t holes;
xfs_agnumber_t agno = bs->cur->bc_private.a.agno; xfs_agnumber_t agno = bs->cur->bc_private.a.agno;
...@@ -311,6 +322,9 @@ xfs_scrub_iallocbt_rec( ...@@ -311,6 +322,9 @@ xfs_scrub_iallocbt_rec(
(agbno & (xfs_icluster_size_fsb(mp) - 1))) (agbno & (xfs_icluster_size_fsb(mp) - 1)))
xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0); xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0);
*inode_blocks += XFS_B_TO_FSB(mp,
irec.ir_count * mp->m_sb.sb_inodesize);
/* Handle non-sparse inodes */ /* Handle non-sparse inodes */
if (!xfs_inobt_issparse(irec.ir_holemask)) { if (!xfs_inobt_issparse(irec.ir_holemask)) {
len = XFS_B_TO_FSB(mp, len = XFS_B_TO_FSB(mp,
...@@ -355,6 +369,72 @@ xfs_scrub_iallocbt_rec( ...@@ -355,6 +369,72 @@ xfs_scrub_iallocbt_rec(
return error; return error;
} }
/*
* Make sure the inode btrees are as large as the rmap thinks they are.
* Don't bother if we're missing btree cursors, as we're already corrupt.
*/
STATIC void
xfs_scrub_iallocbt_xref_rmap_btreeblks(
struct xfs_scrub_context *sc,
int which)
{
struct xfs_owner_info oinfo;
xfs_filblks_t blocks;
xfs_extlen_t inobt_blocks = 0;
xfs_extlen_t finobt_blocks = 0;
int error;
if (!sc->sa.ino_cur || !sc->sa.rmap_cur ||
(xfs_sb_version_hasfinobt(&sc->mp->m_sb) && !sc->sa.fino_cur))
return;
/* Check that we saw as many inobt blocks as the rmap says. */
error = xfs_btree_count_blocks(sc->sa.ino_cur, &inobt_blocks);
if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.ino_cur))
return;
if (sc->sa.fino_cur) {
error = xfs_btree_count_blocks(sc->sa.fino_cur, &finobt_blocks);
if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.fino_cur))
return;
}
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INOBT);
error = xfs_scrub_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, &oinfo,
&blocks);
if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur))
return;
if (blocks != inobt_blocks + finobt_blocks)
xfs_scrub_btree_set_corrupt(sc, sc->sa.ino_cur, 0);
}
/*
* Make sure that the inobt records point to the same number of blocks as
* the rmap says are owned by inodes.
*/
STATIC void
xfs_scrub_iallocbt_xref_rmap_inodes(
struct xfs_scrub_context *sc,
int which,
xfs_filblks_t inode_blocks)
{
struct xfs_owner_info oinfo;
xfs_filblks_t blocks;
int error;
if (!sc->sa.rmap_cur)
return;
/* Check that we saw as many inode blocks as the rmap knows about. */
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
error = xfs_scrub_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, &oinfo,
&blocks);
if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur))
return;
if (blocks != inode_blocks)
xfs_scrub_btree_set_corrupt(sc, sc->sa.ino_cur, 0);
}
/* Scrub the inode btrees for some AG. */ /* Scrub the inode btrees for some AG. */
STATIC int STATIC int
xfs_scrub_iallocbt( xfs_scrub_iallocbt(
...@@ -363,10 +443,29 @@ xfs_scrub_iallocbt( ...@@ -363,10 +443,29 @@ xfs_scrub_iallocbt(
{ {
struct xfs_btree_cur *cur; struct xfs_btree_cur *cur;
struct xfs_owner_info oinfo; struct xfs_owner_info oinfo;
xfs_filblks_t inode_blocks = 0;
int error;
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INOBT); xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INOBT);
cur = which == XFS_BTNUM_INO ? sc->sa.ino_cur : sc->sa.fino_cur; cur = which == XFS_BTNUM_INO ? sc->sa.ino_cur : sc->sa.fino_cur;
return xfs_scrub_btree(sc, cur, xfs_scrub_iallocbt_rec, &oinfo, NULL); error = xfs_scrub_btree(sc, cur, xfs_scrub_iallocbt_rec, &oinfo,
&inode_blocks);
if (error)
return error;
xfs_scrub_iallocbt_xref_rmap_btreeblks(sc, which);
/*
* If we're scrubbing the inode btree, inode_blocks is the number of
* blocks pointed to by all the inode chunk records. Therefore, we
* should compare to the number of inode chunk blocks that the rmap
* knows about. We can't do this for the finobt since it only points
* to inode chunks with free inodes.
*/
if (which == XFS_BTNUM_INO)
xfs_scrub_iallocbt_xref_rmap_inodes(sc, which, inode_blocks);
return error;
} }
int int
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "xfs_ialloc.h" #include "xfs_ialloc.h"
#include "xfs_da_format.h" #include "xfs_da_format.h"
#include "xfs_reflink.h" #include "xfs_reflink.h"
#include "xfs_rmap.h"
#include "scrub/xfs_scrub.h" #include "scrub/xfs_scrub.h"
#include "scrub/scrub.h" #include "scrub/scrub.h"
#include "scrub/common.h" #include "scrub/common.h"
...@@ -632,6 +633,7 @@ xfs_scrub_inode_xref( ...@@ -632,6 +633,7 @@ xfs_scrub_inode_xref(
xfs_ino_t ino, xfs_ino_t ino,
struct xfs_dinode *dip) struct xfs_dinode *dip)
{ {
struct xfs_owner_info oinfo;
xfs_agnumber_t agno; xfs_agnumber_t agno;
xfs_agblock_t agbno; xfs_agblock_t agbno;
int error; int error;
...@@ -648,6 +650,8 @@ xfs_scrub_inode_xref( ...@@ -648,6 +650,8 @@ xfs_scrub_inode_xref(
xfs_scrub_xref_is_used_space(sc, agbno, 1); xfs_scrub_xref_is_used_space(sc, agbno, 1);
xfs_scrub_inode_xref_finobt(sc, ino); xfs_scrub_inode_xref_finobt(sc, ino);
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
xfs_scrub_ag_free(sc, &sc->sa); xfs_scrub_ag_free(sc, &sc->sa);
} }
......
...@@ -157,3 +157,68 @@ xfs_scrub_rmapbt( ...@@ -157,3 +157,68 @@ xfs_scrub_rmapbt(
return xfs_scrub_btree(sc, sc->sa.rmap_cur, xfs_scrub_rmapbt_rec, return xfs_scrub_btree(sc, sc->sa.rmap_cur, xfs_scrub_rmapbt_rec,
&oinfo, NULL); &oinfo, NULL);
} }
/* xref check that the extent is owned by a given owner */
static inline void
xfs_scrub_xref_check_owner(
struct xfs_scrub_context *sc,
xfs_agblock_t bno,
xfs_extlen_t len,
struct xfs_owner_info *oinfo,
bool should_have_rmap)
{
bool has_rmap;
int error;
if (!sc->sa.rmap_cur)
return;
error = xfs_rmap_record_exists(sc->sa.rmap_cur, bno, len, oinfo,
&has_rmap);
if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur))
return;
if (has_rmap != should_have_rmap)
xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
}
/* xref check that the extent is owned by a given owner */
void
xfs_scrub_xref_is_owned_by(
struct xfs_scrub_context *sc,
xfs_agblock_t bno,
xfs_extlen_t len,
struct xfs_owner_info *oinfo)
{
xfs_scrub_xref_check_owner(sc, bno, len, oinfo, true);
}
/* xref check that the extent is not owned by a given owner */
void
xfs_scrub_xref_is_not_owned_by(
struct xfs_scrub_context *sc,
xfs_agblock_t bno,
xfs_extlen_t len,
struct xfs_owner_info *oinfo)
{
xfs_scrub_xref_check_owner(sc, bno, len, oinfo, false);
}
/* xref check that the extent has no reverse mapping at all */
void
xfs_scrub_xref_has_no_owner(
struct xfs_scrub_context *sc,
xfs_agblock_t bno,
xfs_extlen_t len)
{
bool has_rmap;
int error;
if (!sc->sa.rmap_cur)
return;
error = xfs_rmap_has_record(sc->sa.rmap_cur, bno, len, &has_rmap);
if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur))
return;
if (has_rmap)
xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
}
...@@ -130,5 +130,13 @@ void xfs_scrub_xref_is_not_inode_chunk(struct xfs_scrub_context *sc, ...@@ -130,5 +130,13 @@ void xfs_scrub_xref_is_not_inode_chunk(struct xfs_scrub_context *sc,
xfs_agblock_t agbno, xfs_extlen_t len); xfs_agblock_t agbno, xfs_extlen_t len);
void xfs_scrub_xref_is_inode_chunk(struct xfs_scrub_context *sc, void xfs_scrub_xref_is_inode_chunk(struct xfs_scrub_context *sc,
xfs_agblock_t agbno, xfs_extlen_t len); xfs_agblock_t agbno, xfs_extlen_t len);
void xfs_scrub_xref_is_owned_by(struct xfs_scrub_context *sc,
xfs_agblock_t agbno, xfs_extlen_t len,
struct xfs_owner_info *oinfo);
void xfs_scrub_xref_is_not_owned_by(struct xfs_scrub_context *sc,
xfs_agblock_t agbno, xfs_extlen_t len,
struct xfs_owner_info *oinfo);
void xfs_scrub_xref_has_no_owner(struct xfs_scrub_context *sc,
xfs_agblock_t agbno, xfs_extlen_t len);
#endif /* __XFS_SCRUB_SCRUB_H__ */ #endif /* __XFS_SCRUB_SCRUB_H__ */
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