From: Darrick J. Wong Date: Mon, 24 Feb 2025 18:22:05 +0000 (-0800) Subject: xfs_repair: check existing realtime refcountbt entries against observed refcounts X-Git-Tag: v6.14.0~48 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bd212fd416c446881b1f686136f7a109fc50bef5;p=thirdparty%2Fxfsprogs-dev.git xfs_repair: check existing realtime refcountbt entries against observed refcounts Once we've finished collecting reverse mapping observations from the metadata scan, check those observations against the realtime refcount btree (particularly if we're in -n mode) to detect rtrefcountbt problems. Signed-off-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig --- diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h index 0dc6f035..b5a39856 100644 --- a/libxfs/libxfs_api_defs.h +++ b/libxfs/libxfs_api_defs.h @@ -322,6 +322,7 @@ #define xfs_rtrefcountbt_calc_reserves libxfs_rtrefcountbt_calc_reserves #define xfs_rtrefcountbt_droot_maxrecs libxfs_rtrefcountbt_droot_maxrecs +#define xfs_rtrefcountbt_init_cursor libxfs_rtrefcountbt_init_cursor #define xfs_rtrefcountbt_maxlevels_ondisk libxfs_rtrefcountbt_maxlevels_ondisk #define xfs_rtrefcountbt_maxrecs libxfs_rtrefcountbt_maxrecs diff --git a/repair/agbtree.c b/repair/agbtree.c index 2135147f..01066130 100644 --- a/repair/agbtree.c +++ b/repair/agbtree.c @@ -747,7 +747,7 @@ build_refcount_tree( { int error; - error = init_refcount_cursor(agno, &btr->slab_cursor); + error = init_refcount_cursor(false, agno, &btr->slab_cursor); if (error) do_error( _("Insufficient memory to construct refcount cursor.\n")); diff --git a/repair/phase4.c b/repair/phase4.c index 4cfad1a6..b752b4c8 100644 --- a/repair/phase4.c +++ b/repair/phase4.c @@ -223,6 +223,15 @@ check_refcount_btrees( check_refcounts(wq->wq_ctx, agno); } +static void +check_rt_refcount_btrees( + struct workqueue *wq, + xfs_agnumber_t agno, + void *arg) +{ + check_rtrefcounts(wq->wq_ctx, agno); +} + static void process_rmap_data( struct xfs_mount *mp) @@ -259,6 +268,10 @@ process_rmap_data( queue_work(&wq, process_inode_reflink_flags, i, NULL); queue_work(&wq, check_refcount_btrees, i, NULL); } + if (xfs_has_rtreflink(mp)) { + for (i = 0; i < mp->m_sb.sb_rgcount; i++) + queue_work(&wq, check_rt_refcount_btrees, i, NULL); + } destroy_work_queue(&wq); } diff --git a/repair/rmap.c b/repair/rmap.c index 64a9f06d..638e5ea9 100644 --- a/repair/rmap.c +++ b/repair/rmap.c @@ -1694,10 +1694,11 @@ refcount_record_count( */ int init_refcount_cursor( + bool isrt, xfs_agnumber_t agno, struct xfs_slab_cursor **cur) { - struct xfs_ag_rmap *x = rmaps_for_group(false, agno); + struct xfs_ag_rmap *x = rmaps_for_group(isrt, agno); return init_slab_cursor(x->ar_refcount_items, NULL, cur); } @@ -1714,56 +1715,18 @@ refcount_avoid_check( refcbt_suspect = true; } -/* - * Compare the observed reference counts against what's in the ag btree. - */ -void -check_refcounts( - struct xfs_mount *mp, +static int +check_refcount_records( + struct xfs_slab_cursor *rl_cur, + struct xfs_btree_cur *bt_cur, xfs_agnumber_t agno) { struct xfs_refcount_irec tmp; - struct xfs_slab_cursor *rl_cur; - struct xfs_btree_cur *bt_cur = NULL; - struct xfs_buf *agbp = NULL; - struct xfs_perag *pag = NULL; struct xfs_refcount_irec *rl_rec; - int have; int i; + int have; int error; - if (!xfs_has_reflink(mp)) - return; - if (refcbt_suspect) { - if (no_modify && agno == 0) - do_warn(_("would rebuild corrupt refcount btrees.\n")); - return; - } - - /* Create cursors to refcount structures */ - error = init_refcount_cursor(agno, &rl_cur); - if (error) { - do_warn(_("Not enough memory to check refcount data.\n")); - return; - } - - pag = libxfs_perag_get(mp, agno); - error = -libxfs_alloc_read_agf(pag, NULL, 0, &agbp); - if (error) { - do_warn(_("Could not read AGF %u to check refcount btree.\n"), - agno); - goto err_pag; - } - - /* Leave the per-ag data "uninitialized" since we rewrite it later */ - clear_bit(XFS_AGSTATE_AGF_INIT, &pag->pag_opstate); - - bt_cur = libxfs_refcountbt_init_cursor(mp, NULL, agbp, pag); - if (!bt_cur) { - do_warn(_("Not enough memory to check refcount data.\n")); - goto err_agf; - } - rl_rec = pop_slab_cursor(rl_cur); while (rl_rec) { /* Look for a refcount record in the btree */ @@ -1774,7 +1737,7 @@ check_refcounts( do_warn( _("Could not read reference count record for (%u/%u).\n"), agno, rl_rec->rc_startblock); - goto err_cur; + return error; } if (!have) { do_warn( @@ -1789,7 +1752,7 @@ _("Missing reference count record for (%u/%u) len %u count %u\n"), do_warn( _("Could not read reference count record for (%u/%u).\n"), agno, rl_rec->rc_startblock); - goto err_cur; + return error; } if (!i) { do_warn( @@ -1819,6 +1782,59 @@ next_loop: rl_rec = pop_slab_cursor(rl_cur); } + return 0; +} + +/* + * Compare the observed reference counts against what's in the ag btree. + */ +void +check_refcounts( + struct xfs_mount *mp, + xfs_agnumber_t agno) +{ + struct xfs_slab_cursor *rl_cur; + struct xfs_btree_cur *bt_cur = NULL; + struct xfs_buf *agbp = NULL; + struct xfs_perag *pag = NULL; + int error; + + if (!xfs_has_reflink(mp)) + return; + if (refcbt_suspect) { + if (no_modify && agno == 0) + do_warn(_("would rebuild corrupt refcount btrees.\n")); + return; + } + + /* Create cursors to refcount structures */ + error = init_refcount_cursor(false, agno, &rl_cur); + if (error) { + do_warn(_("Not enough memory to check refcount data.\n")); + return; + } + + pag = libxfs_perag_get(mp, agno); + error = -libxfs_alloc_read_agf(pag, NULL, 0, &agbp); + if (error) { + do_warn(_("Could not read AGF %u to check refcount btree.\n"), + agno); + goto err_pag; + } + + /* Leave the per-ag data "uninitialized" since we rewrite it later */ + clear_bit(XFS_AGSTATE_AGF_INIT, &pag->pag_opstate); + + bt_cur = libxfs_refcountbt_init_cursor(mp, NULL, agbp, pag); + if (!bt_cur) { + do_warn(_("Not enough memory to check refcount data.\n")); + goto err_agf; + } + + error = check_refcount_records(rl_cur, bt_cur, agno); + if (error) + goto err_cur; + err_cur: libxfs_btree_del_cursor(bt_cur, error); err_agf: @@ -1828,6 +1844,95 @@ err_pag: free_slab_cursor(&rl_cur); } +/* + * Compare the observed reference counts against what's in the ondisk btree. + */ +void +check_rtrefcounts( + struct xfs_mount *mp, + xfs_rgnumber_t rgno) +{ + struct xfs_slab_cursor *rl_cur; + struct xfs_btree_cur *bt_cur = NULL; + struct xfs_rtgroup *rtg = NULL; + struct xfs_inode *ip = NULL; + int error; + + if (!xfs_has_reflink(mp)) + return; + if (refcbt_suspect) { + if (no_modify && rgno == 0) + do_warn(_("would rebuild corrupt refcount btrees.\n")); + return; + } + if (mp->m_sb.sb_rblocks == 0) { + if (rmap_record_count(mp, true, rgno) != 0) + do_error(_("realtime refcounts but no rtdev?\n")); + return; + } + + /* Create cursors to refcount structures */ + error = init_refcount_cursor(true, rgno, &rl_cur); + if (error) { + do_warn(_("Not enough memory to check refcount data.\n")); + return; + } + + rtg = libxfs_rtgroup_get(mp, rgno); + if (!rtg) { + do_warn(_("Could not load rtgroup %u.\n"), rgno); + goto err_rcur; + } + + ip = rtg_refcount(rtg); + if (!ip) { + do_warn(_("Could not find rtgroup %u refcount inode.\n"), + rgno); + goto err_rtg; + } + + if (ip->i_df.if_format != XFS_DINODE_FMT_META_BTREE) { + do_warn( +_("rtgroup %u refcount inode has wrong format 0x%x, expected 0x%x\n"), + rgno, ip->i_df.if_format, + XFS_DINODE_FMT_META_BTREE); + goto err_rtg; + } + + if (ip->i_metatype != XFS_METAFILE_RTREFCOUNT) { + do_warn( +_("rtgroup %u refcount inode has wrong metatype 0x%x, expected 0x%x\n"), + rgno, ip->i_df.if_format, + XFS_METAFILE_RTREFCOUNT); + goto err_rtg; + } + + if (xfs_inode_has_attr_fork(ip) && + !(xfs_has_metadir(mp) && xfs_has_parent(mp))) { + do_warn( +_("rtgroup %u refcount inode should not have extended attributes\n"), + rgno); + goto err_rtg; + } + + bt_cur = libxfs_rtrefcountbt_init_cursor(NULL, rtg); + if (!bt_cur) { + do_warn(_("Not enough memory to check refcount data.\n")); + goto err_rtg; + } + + error = check_refcount_records(rl_cur, bt_cur, rgno); + if (error) + goto err_cur; + +err_cur: + libxfs_btree_del_cursor(bt_cur, error); +err_rtg: + libxfs_rtgroup_put(rtg); +err_rcur: + free_slab_cursor(&rl_cur); +} + /* * Regenerate the AGFL so that we don't run out of it while rebuilding the * rmap btree. If skip_rmapbt is true, don't update the rmapbt (most probably diff --git a/repair/rmap.h b/repair/rmap.h index 98f28916..80e82a4a 100644 --- a/repair/rmap.h +++ b/repair/rmap.h @@ -40,9 +40,11 @@ extern void rmap_high_key_from_rec(struct xfs_rmap_irec *rec, int compute_refcounts(struct xfs_mount *mp, bool isrt, xfs_agnumber_t agno); uint64_t refcount_record_count(struct xfs_mount *mp, xfs_agnumber_t agno); -extern int init_refcount_cursor(xfs_agnumber_t, struct xfs_slab_cursor **); +int init_refcount_cursor(bool isrt, xfs_agnumber_t agno, + struct xfs_slab_cursor **pcur); extern void refcount_avoid_check(struct xfs_mount *mp); void check_refcounts(struct xfs_mount *mp, xfs_agnumber_t agno); +void check_rtrefcounts(struct xfs_mount *mp, xfs_rgnumber_t rgno); extern void record_inode_reflink_flag(struct xfs_mount *, struct xfs_dinode *, xfs_agnumber_t, xfs_agino_t, xfs_ino_t);