Plumb in the pieces necessary to check the refcount btree. If rmap is available, check the reference count by performing an interval query against the rmapbt. v2: Handle the case where the rmap records are not all at least the length of the refcount extent. Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx> --- fs/xfs/libxfs/xfs_refcount.c | 224 ++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_refcount.h | 2 fs/xfs/libxfs/xfs_refcount_btree.c | 16 ++- fs/xfs/xfs_scrub_sysfs.c | 2 4 files changed, 240 insertions(+), 4 deletions(-) diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c index e8d8702..126dd57 100644 --- a/fs/xfs/libxfs/xfs_refcount.c +++ b/fs/xfs/libxfs/xfs_refcount.c @@ -37,6 +37,7 @@ #include "xfs_bit.h" #include "xfs_refcount.h" #include "xfs_rmap_btree.h" +#include "xfs_scrub.h" /* Allowable refcount adjustment amounts. */ enum xfs_refc_adjust_op { @@ -1578,3 +1579,226 @@ xfs_refcount_free_cow_extent( return __xfs_refcount_add(mp, dfops, &ri); } + +struct xfs_refcountbt_scrub_fragment { + struct xfs_rmap_irec rm; + struct list_head list; +}; + +struct xfs_refcountbt_scrub_rmap_check_info { + xfs_nlink_t nr; + struct xfs_refcount_irec rc; + struct list_head fragments; +}; + +static int +xfs_refcountbt_scrub_rmap_check( + struct xfs_btree_cur *cur, + struct xfs_rmap_irec *rec, + void *priv) +{ + struct xfs_refcountbt_scrub_rmap_check_info *rsrci = priv; + struct xfs_refcountbt_scrub_fragment *frag; + xfs_agblock_t rm_last; + xfs_agblock_t rc_last; + + rm_last = rec->rm_startblock + rec->rm_blockcount; + rc_last = rsrci->rc.rc_startblock + rsrci->rc.rc_blockcount; + if (rec->rm_startblock <= rsrci->rc.rc_startblock && rm_last >= rc_last) + rsrci->nr++; + else { + frag = kmem_zalloc(sizeof(struct xfs_refcountbt_scrub_fragment), + KM_SLEEP); + frag->rm = *rec; + list_add_tail(&frag->list, &rsrci->fragments); + } + + return 0; +} + +STATIC void +xfs_refcountbt_process_rmap_fragments( + struct xfs_mount *mp, + struct xfs_refcountbt_scrub_rmap_check_info *rsrci) +{ + struct list_head worklist; + struct xfs_refcountbt_scrub_fragment *cur; + struct xfs_refcountbt_scrub_fragment *n; + xfs_agblock_t bno; + xfs_agblock_t rbno; + xfs_agblock_t next_rbno; + xfs_nlink_t nr; + xfs_nlink_t target_nr; + + target_nr = rsrci->rc.rc_refcount - rsrci->nr; + if (target_nr == 0) + return; + + /* + * There are (rsrci->rc.rc_refcount - rsrci->nr refcount) + * references we haven't found yet. Pull that many off the + * fragment list and figure out where the smallest rmap ends + * (and therefore the next rmap should start). All the rmaps + * we pull off should start at or before the beginning of the + * refcount record's range. + */ + INIT_LIST_HEAD(&worklist); + rbno = NULLAGBLOCK; + nr = 1; + list_for_each_entry_safe(cur, n, &rsrci->fragments, list) { + if (cur->rm.rm_startblock > rsrci->rc.rc_startblock) + goto fail; + bno = cur->rm.rm_startblock + cur->rm.rm_blockcount; + if (rbno > bno) + rbno = bno; + list_del(&cur->list); + list_add_tail(&cur->list, &worklist); + if (nr == target_nr) + break; + nr++; + } + + if (nr != target_nr) + goto fail; + + while (!list_empty(&rsrci->fragments)) { + /* Discard any fragments ending at rbno. */ + nr = 0; + next_rbno = NULLAGBLOCK; + list_for_each_entry_safe(cur, n, &worklist, list) { + bno = cur->rm.rm_startblock + cur->rm.rm_blockcount; + if (bno != rbno) { + if (next_rbno > bno) + next_rbno = bno; + continue; + } + list_del(&cur->list); + kmem_free(cur); + nr++; + } + + /* Empty list? We're done. */ + if (list_empty(&rsrci->fragments)) + break; + + /* Try to add nr rmaps starting at rbno to the worklist. */ + list_for_each_entry_safe(cur, n, &rsrci->fragments, list) { + bno = cur->rm.rm_startblock + cur->rm.rm_blockcount; + if (cur->rm.rm_startblock != rbno) + goto fail; + list_del(&cur->list); + list_add_tail(&cur->list, &worklist); + if (next_rbno > bno) + next_rbno = bno; + nr--; + if (nr == 0) + break; + } + + rbno = next_rbno; + } + + /* + * Make sure the last extent we processed ends at or beyond + * the end of the refcount extent. + */ + if (rbno < rsrci->rc.rc_startblock + rsrci->rc.rc_blockcount) + goto fail; + + rsrci->nr = rsrci->rc.rc_refcount; +fail: + /* Delete fragments and work list. */ + while (!list_empty(&worklist)) { + cur = list_first_entry(&worklist, + struct xfs_refcountbt_scrub_fragment, list); + list_del(&cur->list); + kmem_free(cur); + } + while (!list_empty(&rsrci->fragments)) { + cur = list_first_entry(&rsrci->fragments, + struct xfs_refcountbt_scrub_fragment, list); + list_del(&cur->list); + kmem_free(cur); + } +} + +STATIC int +xfs_refcountbt_scrub_helper( + struct xfs_btree_scrub *bs, + union xfs_btree_rec *rec) +{ + struct xfs_mount *mp = bs->cur->bc_mp; + struct xfs_rmap_irec low; + struct xfs_rmap_irec high; + struct xfs_refcount_irec irec; + struct xfs_refcountbt_scrub_rmap_check_info rsrci; + struct xfs_refcountbt_scrub_fragment *cur; + int error; + + irec.rc_startblock = be32_to_cpu(rec->refc.rc_startblock); + irec.rc_blockcount = be32_to_cpu(rec->refc.rc_blockcount); + irec.rc_refcount = be32_to_cpu(rec->refc.rc_refcount); + + XFS_BTREC_SCRUB_CHECK(bs, irec.rc_startblock < mp->m_sb.sb_agblocks); + XFS_BTREC_SCRUB_CHECK(bs, irec.rc_startblock < irec.rc_startblock + + irec.rc_blockcount); + XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)irec.rc_startblock + + irec.rc_blockcount <= mp->m_sb.sb_agblocks); + XFS_BTREC_SCRUB_CHECK(bs, irec.rc_refcount >= 1); + + /* confirm the refcount */ + if (!bs->rmap_cur) + return 0; + + memset(&low, 0, sizeof(low)); + low.rm_startblock = irec.rc_startblock; + memset(&high, 0xFF, sizeof(high)); + high.rm_startblock = irec.rc_startblock + irec.rc_blockcount - 1; + + rsrci.nr = 0; + rsrci.rc = irec; + INIT_LIST_HEAD(&rsrci.fragments); + error = xfs_rmapbt_query_range(bs->rmap_cur, &low, &high, + &xfs_refcountbt_scrub_rmap_check, &rsrci); + if (error && error != XFS_BTREE_QUERY_RANGE_ABORT) + goto err; + error = 0; + xfs_refcountbt_process_rmap_fragments(mp, &rsrci); + XFS_BTREC_SCRUB_CHECK(bs, irec.rc_refcount == rsrci.nr); + +err: + while (!list_empty(&rsrci.fragments)) { + cur = list_first_entry(&rsrci.fragments, + struct xfs_refcountbt_scrub_fragment, list); + list_del(&cur->list); + kmem_free(cur); + } + return error; +} + +/* Scrub the refcount btree for some AG. */ +int +xfs_refcountbt_scrub( + struct xfs_mount *mp, + xfs_agnumber_t agno) +{ + struct xfs_btree_scrub bs; + int error; + + error = xfs_alloc_read_agf(mp, NULL, agno, 0, &bs.agf_bp); + if (error) + return error; + + bs.cur = xfs_refcountbt_init_cursor(mp, NULL, bs.agf_bp, agno, NULL); + bs.scrub_rec = xfs_refcountbt_scrub_helper; + xfs_rmap_ag_owner(&bs.oinfo, XFS_RMAP_OWN_REFC); + error = xfs_btree_scrub(&bs); + xfs_btree_del_cursor(bs.cur, + error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR); + xfs_trans_brelse(NULL, bs.agf_bp); + + if (!error && bs.error) + error = bs.error; + + return error; +} diff --git a/fs/xfs/libxfs/xfs_refcount.h b/fs/xfs/libxfs/xfs_refcount.h index 44b0346..d2317f1 100644 --- a/fs/xfs/libxfs/xfs_refcount.h +++ b/fs/xfs/libxfs/xfs_refcount.h @@ -68,4 +68,6 @@ 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_refcountbt_scrub(struct xfs_mount *mp, xfs_agnumber_t agno); + #endif /* __XFS_REFCOUNT_H__ */ diff --git a/fs/xfs/libxfs/xfs_refcount_btree.c b/fs/xfs/libxfs/xfs_refcount_btree.c index abf1ebf..c5d2942 100644 --- a/fs/xfs/libxfs/xfs_refcount_btree.c +++ b/fs/xfs/libxfs/xfs_refcount_btree.c @@ -197,6 +197,16 @@ xfs_refcountbt_key_diff( return (__int64_t)be32_to_cpu(kp->rc_startblock) - rec->rc_startblock; } +STATIC __int64_t +xfs_refcountbt_diff_two_keys( + struct xfs_btree_cur *cur, + union xfs_btree_key *k1, + union xfs_btree_key *k2) +{ + return (__int64_t)be32_to_cpu(k2->refc.rc_startblock) - + be32_to_cpu(k1->refc.rc_startblock); +} + STATIC bool xfs_refcountbt_verify( struct xfs_buf *bp) @@ -259,7 +269,6 @@ const struct xfs_buf_ops xfs_refcountbt_buf_ops = { .verify_write = xfs_refcountbt_write_verify, }; -#if defined(DEBUG) || defined(XFS_WARN) STATIC int xfs_refcountbt_keys_inorder( struct xfs_btree_cur *cur, @@ -288,13 +297,13 @@ xfs_refcountbt_recs_inorder( b.rc_startblock = be32_to_cpu(r2->refc.rc_startblock); b.rc_blockcount = be32_to_cpu(r2->refc.rc_blockcount); b.rc_refcount = be32_to_cpu(r2->refc.rc_refcount); + a = a; b = b; trace_xfs_refcount_rec_order_error(cur->bc_mp, cur->bc_private.a.agno, &a, &b); } return ret; } -#endif /* DEBUG */ static const struct xfs_btree_ops xfs_refcountbt_ops = { .rec_len = sizeof(struct xfs_refcount_rec), @@ -311,10 +320,9 @@ static const struct xfs_btree_ops xfs_refcountbt_ops = { .init_ptr_from_cur = xfs_refcountbt_init_ptr_from_cur, .key_diff = xfs_refcountbt_key_diff, .buf_ops = &xfs_refcountbt_buf_ops, -#if defined(DEBUG) || defined(XFS_WARN) + .diff_two_keys = xfs_refcountbt_diff_two_keys, .keys_inorder = xfs_refcountbt_keys_inorder, .recs_inorder = xfs_refcountbt_recs_inorder, -#endif }; /* diff --git a/fs/xfs/xfs_scrub_sysfs.c b/fs/xfs/xfs_scrub_sysfs.c index c2256f3..ad51e05 100644 --- a/fs/xfs/xfs_scrub_sysfs.c +++ b/fs/xfs/xfs_scrub_sysfs.c @@ -179,6 +179,7 @@ XFS_AGDATA_SCRUB_ATTR(cntbt, NULL); XFS_AGDATA_SCRUB_ATTR(inobt, NULL); XFS_AGDATA_SCRUB_ATTR(finobt, xfs_sb_version_hasfinobt); XFS_AGDATA_SCRUB_ATTR(rmapbt, xfs_sb_version_hasrmapbt); +XFS_AGDATA_SCRUB_ATTR(refcountbt, xfs_sb_version_hasreflink); static struct attribute *xfs_agdata_scrub_attrs[] = { XFS_AGDATA_SCRUB_LIST(bnobt), @@ -186,6 +187,7 @@ static struct attribute *xfs_agdata_scrub_attrs[] = { XFS_AGDATA_SCRUB_LIST(inobt), XFS_AGDATA_SCRUB_LIST(finobt), XFS_AGDATA_SCRUB_LIST(rmapbt), + XFS_AGDATA_SCRUB_LIST(refcountbt), NULL, }; -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html