During metadata btree scrub, we should cross-reference with the reference counts. Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx> --- fs/xfs/libxfs/xfs_refcount.c | 19 ++++ fs/xfs/libxfs/xfs_refcount.h | 3 + fs/xfs/xfs_scrub.c | 184 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+) diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c index 9136745..af82ea3 100644 --- a/fs/xfs/libxfs/xfs_refcount.c +++ b/fs/xfs/libxfs/xfs_refcount.c @@ -1535,3 +1535,22 @@ xfs_refcount_free_cow_extent( return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_FREE_COW, fsb, len); } + +/* Is there a record covering a given extent? */ +int +xfs_refcount_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.rc.rc_startblock = bno; + memset(&high, 0xFF, sizeof(high)); + high.rc.rc_startblock = bno + len - 1; + + return xfs_btree_has_record(cur, &low, &high, exists); +} diff --git a/fs/xfs/libxfs/xfs_refcount.h b/fs/xfs/libxfs/xfs_refcount.h index 105c246..a00400f 100644 --- a/fs/xfs/libxfs/xfs_refcount.h +++ b/fs/xfs/libxfs/xfs_refcount.h @@ -64,4 +64,7 @@ extern int xfs_refcount_free_cow_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops, xfs_fsblock_t fsb, xfs_extlen_t len); +extern int xfs_refcount_has_record(struct xfs_btree_cur *cur, + xfs_agblock_t bno, xfs_extlen_t len, bool *exists); + #endif /* __XFS_REFCOUNT_H__ */ diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c index 34c23f7..ff55d8c 100644 --- a/fs/xfs/xfs_scrub.c +++ b/fs/xfs/xfs_scrub.c @@ -867,6 +867,7 @@ xfs_scrub_sb( bool is_freesp; bool has_inodes; bool has_rmap; + bool has_refcount; int error; int err2; @@ -973,6 +974,17 @@ btree_xref: XFS_BTREE_NOERROR); } + /* Cross-reference with the refcountbt. */ + if (xfs_sb_version_hasreflink(&mp->m_sb)) { + xcur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL); + err2 = xfs_refcount_has_record(xcur, XFS_SB_BLOCK(mp), 1, + &has_refcount); + if (!err2) + XFS_SCRUB_CHECK(mp, bp, "superblock", !has_refcount); + 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); @@ -1000,6 +1012,7 @@ xfs_scrub_agf( bool is_freesp; bool has_inodes; bool has_rmap; + bool has_refcount; int error; int err2; @@ -1103,6 +1116,23 @@ skip_rmap_xref: XFS_BTREE_NOERROR); } + /* Cross-reference with the refcountbt. */ + if (xfs_sb_version_hasreflink(&mp->m_sb)) { + xcur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL); + err2 = xfs_refcount_has_record(xcur, XFS_AGF_BLOCK(mp), 1, + &has_refcount); + if (err2) + goto skip_refc_xref; + XFS_SCRUB_CHECK(mp, agf_bp, "AGF", !has_refcount); + err2 = xfs_btree_count_blocks(xcur, &blocks); + if (!err2) + XFS_SCRUB_CHECK(mp, agf_bp, "AGF", blocks == + be32_to_cpu(agf->agf_refcount_blocks)); +skip_refc_xref: + xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : + XFS_BTREE_NOERROR); + } + xfs_scrub_put_ag_headers(&agi_bp, &agf_bp); return error; } @@ -1123,6 +1153,7 @@ xfs_scrub_agfl( struct xfs_btree_cur *icur = NULL; struct xfs_btree_cur *fcur = NULL; struct xfs_btree_cur *rcur = NULL; + struct xfs_btree_cur *ccur = NULL; struct xfs_owner_info oinfo; xfs_agnumber_t agno; xfs_agblock_t agbno; @@ -1131,6 +1162,7 @@ xfs_scrub_agfl( bool is_freesp; bool has_inodes; bool has_rmap; + bool has_refcount; int i; int error; int err2; @@ -1185,6 +1217,15 @@ xfs_scrub_agfl( XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", has_rmap); } + /* Set up cross-reference with refcountbt. */ + if (xfs_sb_version_hasreflink(&mp->m_sb)) { + ccur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL); + err2 = xfs_refcount_has_record(ccur, XFS_AGFL_BLOCK(mp), 1, + &has_refcount); + if (!err2) + XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", !has_refcount); + } + xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_AG); agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agfl_bp); for (i = be32_to_cpu(agf->agf_flfirst); @@ -1229,8 +1270,19 @@ xfs_scrub_agfl( if (!err2) XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", has_rmap); } + + /* Cross-reference with the refcountbt. */ + if (ccur) { + err2 = xfs_refcount_has_record(ccur, agbno, 1, + &has_refcount); + if (!err2) + XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", + !has_refcount); + } } + if (ccur) + xfs_btree_del_cursor(ccur, XFS_BTREE_ERROR); if (rcur) xfs_btree_del_cursor(rcur, XFS_BTREE_ERROR); if (fcur) @@ -1264,6 +1316,7 @@ xfs_scrub_agi( bool is_freesp; bool has_inodes; bool has_rmap; + bool has_refcount; int error; int err2; @@ -1334,6 +1387,17 @@ xfs_scrub_agi( XFS_BTREE_NOERROR); } + /* Cross-reference with the refcountbt. */ + if (xfs_sb_version_hasreflink(&mp->m_sb)) { + xcur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL); + err2 = xfs_refcount_has_record(xcur, XFS_AGI_BLOCK(mp), 1, + &has_refcount); + if (!err2) + XFS_SCRUB_CHECK(mp, agi_bp, "AGI", !has_refcount); + xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : + XFS_BTREE_NOERROR); + } + xfs_scrub_put_ag_headers(&agi_bp, &agf_bp); return error; } @@ -1355,6 +1419,7 @@ xfs_scrub_allocbt_helper( xfs_extlen_t len; bool has_rmap; bool has_inodes; + bool has_refcount; int has_otherrec; int error = 0; int err2; @@ -1415,6 +1480,14 @@ skip_freesp_xref: XFS_BTREC_SCRUB_CHECK(bs, !has_rmap); } + /* Cross-reference with the refcountbt. */ + if (bs->refc_cur) { + err2 = xfs_refcount_has_record(bs->refc_cur, bno, len, + &has_refcount); + if (!err2) + XFS_BTREC_SCRUB_CHECK(bs, !has_refcount); + } + return error; } @@ -1488,6 +1561,7 @@ xfs_scrub_iallocbt_helper( bool is_freesp; bool has_inodes; bool has_rmap; + bool has_refcount; int holecount; int i; int error = 0; @@ -1547,6 +1621,14 @@ xfs_scrub_iallocbt_helper( XFS_BTREC_SCRUB_CHECK(bs, has_rmap); } + /* Cross-reference with the refcountbt. */ + if (bs->refc_cur) { + err2 = xfs_refcount_has_record(bs->refc_cur, bno, + len, &has_refcount); + if (!err2) + XFS_BTREC_SCRUB_CHECK(bs, !has_refcount); + } + goto out; } @@ -1604,6 +1686,14 @@ xfs_scrub_iallocbt_helper( if (!err2) XFS_BTREC_SCRUB_CHECK(bs, has_rmap); } + + /* Cross-reference with the refcountbt. */ + if (bs->refc_cur) { + err2 = xfs_refcount_has_record(bs->refc_cur, bno, + len, &has_refcount); + if (!err2) + XFS_BTREC_SCRUB_CHECK(bs, !has_refcount); + } } XFS_BTREC_SCRUB_CHECK(bs, holecount <= XFS_INODES_PER_CHUNK); @@ -1674,13 +1764,17 @@ xfs_scrub_rmapbt_helper( struct xfs_mount *mp = bs->cur->bc_mp; struct xfs_agf *agf; struct xfs_rmap_irec irec; + struct xfs_refcount_irec crec; xfs_agblock_t eoag; + xfs_agblock_t fbno; + xfs_extlen_t flen; bool is_freesp; bool non_inode; bool is_unwritten; bool is_bmbt; bool is_attr; bool has_inodes; + int has_refcount; int error = 0; int err2; @@ -1741,6 +1835,45 @@ xfs_scrub_rmapbt_helper( !has_inodes); } + /* Cross-reference with the refcount btree. */ + if (bs->refc_cur) { + if (irec.rm_owner == XFS_RMAP_OWN_COW) { + /* Check this CoW staging extent. */ + err2 = xfs_refcount_lookup_le(bs->refc_cur, + irec.rm_startblock, &has_refcount); + if (err2) + goto skip_refc_xref; + XFS_BTREC_SCRUB_GOTO(bs, has_refcount, skip_refc_xref); + + err2 = xfs_refcount_get_rec(bs->refc_cur, &crec, + &has_refcount); + if (err2) + goto skip_refc_xref; + XFS_BTREC_SCRUB_GOTO(bs, has_refcount, skip_refc_xref); + XFS_BTREC_SCRUB_CHECK(bs, crec.rc_startblock <= + irec.rm_startblock); + XFS_BTREC_SCRUB_CHECK(bs, crec.rc_startblock + + crec.rc_blockcount > + crec.rc_startblock); + XFS_BTREC_SCRUB_CHECK(bs, crec.rc_startblock + + crec.rc_blockcount >= + irec.rm_startblock + + irec.rm_blockcount); + XFS_BTREC_SCRUB_CHECK(bs, + crec.rc_refcount == 1); + } else { + /* If this is shared, the inode flag must be set. */ + err2 = xfs_refcount_find_shared(bs->refc_cur, + irec.rm_startblock, irec.rm_blockcount, + &fbno, &flen, false); + if (!err2) + XFS_BTREC_SCRUB_CHECK(bs, flen == 0 || + (!non_inode && !is_attr && + !is_bmbt && !is_unwritten)); + } +skip_refc_xref:; + } + return error; } @@ -2164,12 +2297,16 @@ xfs_scrub_bmap_extent( xfs_agnumber_t agno; xfs_fsblock_t bno; struct xfs_rmap_irec rmap; + struct xfs_refcount_irec crec; uint64_t owner; xfs_fileoff_t offset; + xfs_agblock_t fbno; + xfs_extlen_t flen; bool is_freesp; bool has_inodes; unsigned int rflags; int has_rmap; + int has_refcount; int error = 0; int err2 = 0; @@ -2348,6 +2485,53 @@ skip_rmap_xref: XFS_BTREE_NOERROR); } + /* + * If this is a non-shared file on a reflink filesystem, + * check the refcountbt to see if the flag is wrong. + */ + if (xfs_sb_version_hasreflink(&mp->m_sb) && !info->is_rt) { + xcur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL); + + if (info->whichfork == XFS_COW_FORK) { + /* Check this CoW staging extent. */ + err2 = xfs_refcount_lookup_le(xcur, bno, &has_refcount); + if (err2) + goto skip_refc_xref; + XFS_INO_SCRUB_GOTO(ip, NULL, info->type, has_refcount, + skip_refc_xref); + + err2 = xfs_refcount_get_rec(xcur, &crec, &has_refcount); + if (err2) + goto skip_refc_xref; + XFS_INO_SCRUB_GOTO(ip, NULL, info->type, has_refcount, + skip_refc_xref); + + XFS_INO_SCRUB_CHECK(ip, NULL, info->type, + crec.rc_startblock <= bno); + XFS_INO_SCRUB_CHECK(ip, NULL, info->type, + crec.rc_startblock + + crec.rc_blockcount > + crec.rc_startblock); + XFS_INO_SCRUB_CHECK(ip, NULL, info->type, + crec.rc_startblock + + crec.rc_blockcount >= + bno + irec->br_blockcount); + XFS_INO_SCRUB_CHECK(ip, NULL, info->type, + crec.rc_refcount == 1); + } else { + /* If this is shared, the inode flag must be set. */ + err2 = xfs_refcount_find_shared(xcur, bno, + irec->br_blockcount, &fbno, &flen, + false); + if (!err2) + XFS_INO_SCRUB_CHECK(ip, bp, info->type, + flen == 0 || + xfs_is_reflink_inode(ip)); + } +skip_refc_xref: + xfs_btree_del_cursor(xcur, XFS_BTREE_NOERROR); + } + xfs_scrub_put_ag_headers(&agi_bp, &agf_bp); out: info->lastoff = irec->br_startoff + irec->br_blockcount; _______________________________________________ xfs mailing list xfs@xxxxxxxxxxx http://oss.sgi.com/mailman/listinfo/xfs