@@ -2974,3 +2974,22 @@ xfs_alloc_query_range(
return xfs_btree_query_range(cur, &low_brec, &high_brec,
xfs_alloc_query_range_helper, &query);
}
+
+/* Is there a record covering a given extent? */
+int
+xfs_alloc_has_record(
+ struct xfs_btree_cur *cur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ bool *exists)
+{
+ union xfs_btree_irec low;
+ union xfs_btree_irec high;
+
+ memset(&low, 0, sizeof(low));
+ low.a.ar_startblock = bno;
+ memset(&high, 0xFF, sizeof(high));
+ high.a.ar_startblock = bno + len - 1;
+
+ return xfs_btree_has_record(cur, &low, &high, exists);
+}
@@ -222,4 +222,7 @@ int xfs_alloc_query_range(struct xfs_btree_cur *cur,
struct xfs_alloc_rec_incore *high_rec,
xfs_alloc_query_range_fn fn, void *priv);
+int xfs_alloc_has_record(struct xfs_btree_cur *cur, xfs_agblock_t bno,
+ xfs_extlen_t len, bool *exist);
+
#endif /* __XFS_ALLOC_H__ */
@@ -515,6 +515,67 @@ xfs_scrub_should_terminate(
}
/*
+ * Make sure this btree block isn't in the free list and that there's
+ * an rmap record for it.
+ */
+STATIC int
+xfs_scrub_btree_check_block_owner(
+ struct xfs_scrub_btree *bs,
+ xfs_fsblock_t fsb)
+{
+ xfs_agnumber_t agno;
+ xfs_agblock_t bno;
+ bool is_freesp;
+ struct xfs_buf *agf_bp = NULL;
+ struct xfs_btree_cur *bcur = NULL;
+ int error = 0;
+ int err2;
+
+ agno = XFS_FSB_TO_AGNO(bs->cur->bc_mp, fsb);
+ bno = XFS_FSB_TO_AGBNO(bs->cur->bc_mp, fsb);
+
+ if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+ err2 = xfs_alloc_read_agf(bs->cur->bc_mp, NULL, agno,
+ 0, &agf_bp);
+ if (err2)
+ return error;
+ bcur = xfs_allocbt_init_cursor(bs->cur->bc_mp, NULL,
+ agf_bp, agno, XFS_BTNUM_BNO);
+ } else {
+ bcur = bs->bno_cur;
+ }
+
+ /* Check that this block isn't free. */
+ err2 = xfs_alloc_has_record(bcur, bno, 1, &is_freesp);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
+
+ if (agf_bp) {
+ xfs_btree_del_cursor(bcur, XFS_BTREE_ERROR);
+ xfs_buf_relse(agf_bp);
+ }
+
+ return error;
+}
+
+/* Check the owner of a btree block. */
+STATIC int
+xfs_scrub_btree_check_owner(
+ struct xfs_scrub_btree *bs,
+ struct xfs_buf *bp)
+{
+ struct xfs_btree_cur *cur = bs->cur;
+ xfs_fsblock_t fsbno;
+
+ if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) && bp == NULL)
+ return 0;
+
+ fsbno = XFS_DADDR_TO_FSB(cur->bc_mp, bp->b_bn);
+
+ return xfs_scrub_btree_check_block_owner(bs, fsbno);
+}
+
+/*
* Visit all nodes and leaves of a btree. Check that all pointers and
* records are in order, that the keys reflect the records, and use a callback
* so that the caller can verify individual records. The callback is the same
@@ -604,6 +665,9 @@ xfs_scrub_btree(
error = xfs_btree_check_block(cur, block, level, bp);
if (error)
goto out;
+ error = xfs_scrub_btree_check_owner(bs, bp);
+ if (error)
+ goto out;
cur->bc_ptrs[level] = 1;
@@ -664,6 +728,10 @@ xfs_scrub_btree(
if (error)
goto out;
+ error = xfs_scrub_btree_check_owner(bs, bp);
+ if (error)
+ goto out;
+
cur->bc_ptrs[level] = 1;
}
@@ -716,9 +784,14 @@ xfs_scrub_sb(
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_buf *bp;
+ struct xfs_buf *agi_bp = NULL;
+ struct xfs_buf *agf_bp = NULL;
+ struct xfs_btree_cur *xcur = NULL;
struct xfs_sb sb;
xfs_agnumber_t agno;
+ bool is_freesp;
int error;
+ int err2;
if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
return -EINVAL;
@@ -732,7 +805,7 @@ xfs_scrub_sb(
return error;
if (agno == 0)
- goto out;
+ goto btree_xref;
xfs_sb_from_disk(&sb, XFS_BUF_TO_SBP(bp));
@@ -776,6 +849,22 @@ xfs_scrub_sb(
XFS_SCRUB_SB_FEAT(realtime);
#undef XFS_SCRUB_SB_FEAT
+ if (error)
+ goto out;
+
+btree_xref:
+ error = xfs_scrub_get_ag_headers(mp, agno, &agi_bp, &agf_bp);
+ if (error)
+ goto out;
+
+ /* Cross-reference with bnobt. */
+ xcur = xfs_allocbt_init_cursor(mp, NULL, agf_bp, agno, XFS_BTNUM_BNO);
+ err2 = xfs_alloc_has_record(xcur, XFS_SB_BLOCK(mp), 1, &is_freesp);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, bp, "superblock", !is_freesp);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+
+ xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
out:
xfs_buf_relse(bp);
return error;
@@ -791,12 +880,15 @@ xfs_scrub_agf(
struct xfs_agf *agf;
struct xfs_buf *agi_bp = NULL;
struct xfs_buf *agf_bp = NULL;
+ struct xfs_btree_cur *xcur = NULL;
xfs_agnumber_t agno;
xfs_agblock_t agbno;
xfs_agblock_t eoag;
xfs_daddr_t daddr;
xfs_daddr_t eofs;
+ bool is_freesp;
int error;
+ int err2;
if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
return -EINVAL;
@@ -853,6 +945,13 @@ xfs_scrub_agf(
XFS_SCRUB_CHECK(mp, agf_bp, "AGF", daddr < eofs);
}
+ /* Cross-reference with the bnobt. */
+ xcur = xfs_allocbt_init_cursor(mp, NULL, agf_bp, agno, XFS_BTNUM_BNO);
+ err2 = xfs_alloc_has_record(xcur, XFS_AGF_BLOCK(mp), 1, &is_freesp);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", !is_freesp);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
return error;
}
@@ -869,12 +968,15 @@ xfs_scrub_agfl(
struct xfs_buf *agf_bp = NULL;
struct xfs_buf *agfl_bp;
__be32 *agfl_bno;
+ struct xfs_btree_cur *xcur = NULL;
xfs_agnumber_t agno;
xfs_agblock_t agbno;
xfs_agblock_t eoag;
xfs_daddr_t eofs;
+ bool is_freesp;
int i;
int error;
+ int err2;
if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
return -EINVAL;
@@ -893,6 +995,12 @@ xfs_scrub_agfl(
eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
eoag = be32_to_cpu(agf->agf_length);
+ /* Cross-reference with the bnobt. */
+ xcur = xfs_allocbt_init_cursor(mp, NULL, agf_bp, agno, XFS_BTNUM_BNO);
+ err2 = xfs_alloc_has_record(xcur, XFS_AGFL_BLOCK(mp), 1, &is_freesp);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", !is_freesp);
+
agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agfl_bp);
for (i = be32_to_cpu(agf->agf_flfirst);
i <= be32_to_cpu(agf->agf_fllast);
@@ -904,8 +1012,14 @@ xfs_scrub_agfl(
agbno < mp->m_sb.sb_agblocks);
XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
agbno < eoag);
+
+ /* Cross-reference with the bnobt. */
+ err2 = xfs_alloc_has_record(xcur, agbno, 1, &is_freesp);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", !is_freesp);
}
+ xfs_btree_del_cursor(xcur, XFS_BTREE_ERROR);
xfs_buf_relse(agfl_bp);
err_no_agfl:
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
@@ -923,12 +1037,15 @@ xfs_scrub_agi(
struct xfs_agf *agf;
struct xfs_buf *agi_bp = NULL;
struct xfs_buf *agf_bp = NULL;
+ struct xfs_btree_cur *xcur = NULL;
xfs_agnumber_t agno;
xfs_agblock_t agbno;
xfs_agblock_t eoag;
xfs_daddr_t daddr;
xfs_daddr_t eofs;
+ bool is_freesp;
int error;
+ int err2;
if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
return -EINVAL;
@@ -958,6 +1075,13 @@ xfs_scrub_agi(
XFS_SCRUB_CHECK(mp, agi_bp, "AGI", daddr < eofs);
}
+ /* Cross-reference with bnobt. */
+ xcur = xfs_allocbt_init_cursor(mp, NULL, agf_bp, agno, XFS_BTNUM_BNO);
+ err2 = xfs_alloc_has_record(xcur, XFS_AGI_BLOCK(mp), 1, &is_freesp);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agi_bp, "AGI", !is_freesp);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
return error;
}
@@ -1056,9 +1180,11 @@ xfs_scrub_iallocbt_helper(
xfs_agblock_t bno;
xfs_agblock_t eoag;
xfs_extlen_t len;
+ bool is_freesp;
int holecount;
int i;
int error = 0;
+ int err2;
uint64_t holes;
xfs_inobt_btrec_to_irec(mp, rec, &irec);
@@ -1082,7 +1208,14 @@ xfs_scrub_iallocbt_helper(
mp->m_sb.sb_agblocks);
XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
eoag);
- return error;
+
+ /* Cross-reference with the bnobt. */
+ err2 = xfs_alloc_has_record(bs->bno_cur, bno, len,
+ &is_freesp);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
+
+ goto out;
}
/* Check each chunk of a sparse inode cluster. */
@@ -1109,12 +1242,19 @@ xfs_scrub_iallocbt_helper(
mp->m_sb.sb_agblocks);
XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
eoag);
+
+ /* Cross-reference with the bnobt. */
+ err2 = xfs_alloc_has_record(bs->bno_cur, bno, len,
+ &is_freesp);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
}
XFS_BTREC_SCRUB_CHECK(bs, holecount <= XFS_INODES_PER_CHUNK);
XFS_BTREC_SCRUB_CHECK(bs, holecount + irec.ir_count ==
XFS_INODES_PER_CHUNK);
+out:
return error;
}
@@ -1179,11 +1319,13 @@ xfs_scrub_rmapbt_helper(
struct xfs_agf *agf;
struct xfs_rmap_irec irec;
xfs_agblock_t eoag;
+ bool is_freesp;
bool non_inode;
bool is_unwritten;
bool is_bmbt;
bool is_attr;
- int error;
+ int error = 0;
+ int err2;
error = xfs_rmap_btrec_to_irec(rec, &irec);
if (error)
@@ -1212,7 +1354,11 @@ xfs_scrub_rmapbt_helper(
XFS_BTREC_SCRUB_CHECK(bs, !non_inode || !(is_bmbt || is_unwritten ||
is_attr));
- /* XXX: check with the owner */
+ /* check there's no record in freesp btrees */
+ err2 = xfs_alloc_has_record(bs->bno_cur, irec.rm_startblock,
+ irec.rm_blockcount, &is_freesp);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
return error;
}
@@ -1262,7 +1408,9 @@ xfs_scrub_refcountbt_helper(
struct xfs_agf *agf;
struct xfs_refcount_irec irec;
xfs_agblock_t eoag;
+ bool is_freesp;
int error = 0;
+ int err2;
irec.rc_startblock = be32_to_cpu(rec->refc.rc_startblock);
irec.rc_blockcount = be32_to_cpu(rec->refc.rc_blockcount);
@@ -1280,6 +1428,12 @@ xfs_scrub_refcountbt_helper(
irec.rc_blockcount <= eoag);
XFS_BTREC_SCRUB_CHECK(bs, irec.rc_refcount >= 1);
+ /* Cross-reference with the bnobt. */
+ err2 = xfs_alloc_has_record(bs->bno_cur, irec.rc_startblock,
+ irec.rc_blockcount, &is_freesp);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
+
return error;
}
@@ -1412,10 +1566,12 @@ xfs_scrub_bmap_extent(
struct xfs_buf *bp = NULL;
struct xfs_buf *agi_bp = NULL;
struct xfs_buf *agf_bp = NULL;
+ struct xfs_btree_cur *xcur = NULL;
xfs_daddr_t daddr;
xfs_daddr_t dlen;
xfs_agnumber_t agno;
xfs_fsblock_t bno;
+ bool is_freesp;
int error = 0;
int err2 = 0;
@@ -1450,19 +1606,29 @@ xfs_scrub_bmap_extent(
XFS_INO_SCRUB_CHECK(ip, bp, info->type,
irec->br_state != XFS_EXT_UNWRITTEN ||
xfs_sb_version_hasextflgbit(&mp->m_sb));
+ if (error)
+ goto out;
/* Set ourselves up for cross-referencing later. */
if (!info->is_rt) {
- err2 = xfs_scrub_get_ag_headers(mp, agno, &agi_bp, &agf_bp);
- if (err2)
+ error = xfs_scrub_get_ag_headers(mp, agno, &agi_bp, &agf_bp);
+ if (error)
goto out;
+
+ /* Cross-reference with the bnobt. */
+ xcur = xfs_allocbt_init_cursor(mp, NULL, agf_bp, agno,
+ XFS_BTNUM_BNO);
+ err2 = xfs_alloc_has_record(xcur, bno,
+ irec->br_blockcount, &is_freesp);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(&info->bs, !is_freesp);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
}
-out:
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
+out:
info->lastoff = irec->br_startoff + irec->br_blockcount;
- if (!error && err2)
- error = err2;
return error;
}
When we're scrubbing various btrees, cross-reference the records with the bnobt to ensure that we don't also think the space is free. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> --- fs/xfs/libxfs/xfs_alloc.c | 19 +++++ fs/xfs/libxfs/xfs_alloc.h | 3 + fs/xfs/xfs_scrub.c | 184 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 197 insertions(+), 9 deletions(-)