From: Darrick J. Wong Date: Fri, 10 Jul 2020 19:35:46 +0000 (-0400) Subject: xfs_repair: rebuild reverse mapping btrees with bulk loader X-Git-Tag: xfsprogs-5.7-fixes_2020-07-14~7 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=dc9f4f5e3442c97855b133bac522660c51b3f4cd;p=thirdparty%2Fxfsprogs-dev.git xfs_repair: rebuild reverse mapping btrees with bulk loader Use the btree bulk loading functions to rebuild the reverse mapping btrees and drop the open-coded implementation. Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster [sandeen: adjust for prior inclusion of "fix rebuilding btree block..."] Signed-off-by: Eric Sandeen --- diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h index 5d0868c2a..0026ca45d 100644 --- a/libxfs/libxfs_api_defs.h +++ b/libxfs/libxfs_api_defs.h @@ -142,6 +142,7 @@ #define xfs_rmapbt_calc_reserves libxfs_rmapbt_calc_reserves #define xfs_rmapbt_init_cursor libxfs_rmapbt_init_cursor #define xfs_rmapbt_maxrecs libxfs_rmapbt_maxrecs +#define xfs_rmapbt_stage_cursor libxfs_rmapbt_stage_cursor #define xfs_rmap_compare libxfs_rmap_compare #define xfs_rmap_get_rec libxfs_rmap_get_rec #define xfs_rmap_irec_offset_pack libxfs_rmap_irec_offset_pack diff --git a/repair/agbtree.c b/repair/agbtree.c index 874e6ff92..1f07b63c9 100644 --- a/repair/agbtree.c +++ b/repair/agbtree.c @@ -515,3 +515,73 @@ _("Error %d while creating finobt btree for AG %u.\n"), error, agno); /* Since we're not writing the AGI yet, no need to commit the cursor */ libxfs_btree_del_cursor(btr_fino->cur, 0); } + +/* rebuild the rmap tree */ + +/* Grab one rmap record. */ +static int +get_rmapbt_record( + struct xfs_btree_cur *cur, + void *priv) +{ + struct xfs_rmap_irec *rec; + struct bt_rebuild *btr = priv; + + rec = pop_slab_cursor(btr->slab_cursor); + memcpy(&cur->bc_rec.r, rec, sizeof(struct xfs_rmap_irec)); + return 0; +} + +/* Set up the rmap rebuild parameters. */ +void +init_rmapbt_cursor( + struct repair_ctx *sc, + xfs_agnumber_t agno, + unsigned int free_space, + struct bt_rebuild *btr) +{ + int error; + + if (!xfs_sb_version_hasrmapbt(&sc->mp->m_sb)) + return; + + init_rebuild(sc, &XFS_RMAP_OINFO_AG, free_space, btr); + btr->cur = libxfs_rmapbt_stage_cursor(sc->mp, &btr->newbt.afake, agno); + + btr->bload.get_record = get_rmapbt_record; + btr->bload.claim_block = rebuild_claim_block; + + /* Compute how many blocks we'll need. */ + error = -libxfs_btree_bload_compute_geometry(btr->cur, &btr->bload, + rmap_record_count(sc->mp, agno)); + if (error) + do_error( +_("Unable to compute rmap btree geometry, error %d.\n"), error); + + reserve_btblocks(sc->mp, agno, btr, btr->bload.nr_blocks); +} + +/* Rebuild a rmap btree. */ +void +build_rmap_tree( + struct repair_ctx *sc, + xfs_agnumber_t agno, + struct bt_rebuild *btr) +{ + int error; + + error = rmap_init_cursor(agno, &btr->slab_cursor); + if (error) + do_error( +_("Insufficient memory to construct rmap cursor.\n")); + + /* Add all observed rmap records. */ + error = -libxfs_btree_bload(btr->cur, &btr->bload, btr); + if (error) + do_error( +_("Error %d while creating rmap btree for AG %u.\n"), error, agno); + + /* Since we're not writing the AGF yet, no need to commit the cursor */ + libxfs_btree_del_cursor(btr->cur, 0); + free_slab_cursor(&btr->slab_cursor); +} diff --git a/repair/agbtree.h b/repair/agbtree.h index 3cad2a8e9..ca6e70de9 100644 --- a/repair/agbtree.h +++ b/repair/agbtree.h @@ -49,4 +49,9 @@ void init_ino_cursors(struct repair_ctx *sc, xfs_agnumber_t agno, void build_inode_btrees(struct repair_ctx *sc, xfs_agnumber_t agno, struct bt_rebuild *btr_ino, struct bt_rebuild *btr_fino); +void init_rmapbt_cursor(struct repair_ctx *sc, xfs_agnumber_t agno, + unsigned int free_space, struct bt_rebuild *btr); +void build_rmap_tree(struct repair_ctx *sc, xfs_agnumber_t agno, + struct bt_rebuild *btr); + #endif /* __XFS_REPAIR_AG_BTREE_H__ */ diff --git a/repair/phase5.c b/repair/phase5.c index 82b430ee4..1c6448f4b 100644 --- a/repair/phase5.c +++ b/repair/phase5.c @@ -336,45 +336,6 @@ finish_cursor(bt_status_t *curs) free(curs->btree_blocks); } -/* - * We need to leave some free records in the tree for the corner case of - * setting up the AGFL. This may require allocation of blocks, and as - * such can require insertion of new records into the tree (e.g. moving - * a record in the by-count tree when a long extent is shortened). If we - * pack the records into the leaves with no slack space, this requires a - * leaf split to occur and a block to be allocated from the free list. - * If we don't have any blocks on the free list (because we are setting - * it up!), then we fail, and the filesystem will fail with the same - * failure at runtime. Hence leave a couple of records slack space in - * each block to allow immediate modification of the tree without - * requiring splits to be done. - */ -static void -compute_level_geometry( - struct xfs_mount *mp, - struct bt_stat_level *lptr, - uint64_t nr_this_level, - int slack, - bool leaf) -{ - unsigned int maxrecs = mp->m_alloc_mxr[!leaf]; - unsigned int desired_npb; - - desired_npb = max(mp->m_alloc_mnr[!leaf], maxrecs - slack); - lptr->num_recs_tot = nr_this_level; - lptr->num_blocks = max(1ULL, nr_this_level / desired_npb); - - lptr->num_recs_pb = nr_this_level / lptr->num_blocks; - lptr->modulo = nr_this_level % lptr->num_blocks; - if (lptr->num_recs_pb > maxrecs || - (lptr->num_recs_pb == maxrecs && lptr->modulo)) { - lptr->num_blocks++; - - lptr->num_recs_pb = nr_this_level / lptr->num_blocks; - lptr->modulo = nr_this_level % lptr->num_blocks; - } -} - /* Map btnum to buffer ops for the types that need it. */ static const struct xfs_buf_ops * btnum_to_ops( @@ -456,364 +417,6 @@ build_agi( libxfs_buf_relse(agi_buf); } -/* rebuild the rmap tree */ - -/* - * we don't have to worry here about how chewing up free extents - * may perturb things because rmap tree building happens before - * freespace tree building. - */ -static void -init_rmapbt_cursor( - struct xfs_mount *mp, - xfs_agnumber_t agno, - struct bt_status *btree_curs) -{ - size_t num_recs; - int level; - struct bt_stat_level *lptr; - struct bt_stat_level *p_lptr; - xfs_extlen_t blocks_allocated; - - if (!xfs_sb_version_hasrmapbt(&mp->m_sb)) { - memset(btree_curs, 0, sizeof(struct bt_status)); - return; - } - - lptr = &btree_curs->level[0]; - btree_curs->init = 1; - btree_curs->owner = XFS_RMAP_OWN_AG; - - /* - * build up statistics - */ - num_recs = rmap_record_count(mp, agno); - if (num_recs == 0) { - /* - * easy corner-case -- no rmap records - */ - lptr->num_blocks = 1; - lptr->modulo = 0; - lptr->num_recs_pb = 0; - lptr->num_recs_tot = 0; - - btree_curs->num_levels = 1; - btree_curs->num_tot_blocks = btree_curs->num_free_blocks = 1; - - setup_cursor(mp, agno, btree_curs); - - return; - } - - /* - * Leave enough slack in the rmapbt that we can insert the - * metadata AG entries without too many splits. - */ - compute_level_geometry(mp, lptr, num_recs, - num_recs > mp->m_rmap_mxr[0] ? 10 : 0, true); - blocks_allocated = lptr->num_blocks; - level = 1; - - while (lptr->num_blocks > 1) { - p_lptr = lptr; - lptr = &btree_curs->level[level++]; - - compute_level_geometry(mp, lptr, - p_lptr->num_blocks, 0, false); - blocks_allocated += lptr->num_blocks; - } - ASSERT(level < XFS_BTREE_MAXLEVELS); - ASSERT(lptr->num_blocks == 1); - btree_curs->num_levels = level; - - btree_curs->num_tot_blocks = btree_curs->num_free_blocks - = blocks_allocated; - - setup_cursor(mp, agno, btree_curs); -} - -static void -prop_rmap_cursor( - struct xfs_mount *mp, - xfs_agnumber_t agno, - struct bt_status *btree_curs, - struct xfs_rmap_irec *rm_rec, - int level) -{ - struct xfs_btree_block *bt_hdr; - struct xfs_rmap_key *bt_key; - xfs_rmap_ptr_t *bt_ptr; - xfs_agblock_t agbno; - struct bt_stat_level *lptr; - const struct xfs_buf_ops *ops = btnum_to_ops(XFS_BTNUM_RMAP); - int error; - - level++; - - if (level >= btree_curs->num_levels) - return; - - lptr = &btree_curs->level[level]; - bt_hdr = XFS_BUF_TO_BLOCK(lptr->buf_p); - - if (be16_to_cpu(bt_hdr->bb_numrecs) == 0) { - /* - * this only happens once to initialize the - * first path up the left side of the tree - * where the agbno's are already set up - */ - prop_rmap_cursor(mp, agno, btree_curs, rm_rec, level); - } - - if (be16_to_cpu(bt_hdr->bb_numrecs) == - lptr->num_recs_pb + (lptr->modulo > 0)) { - /* - * write out current prev block, grab us a new block, - * and set the rightsib pointer of current block - */ -#ifdef XR_BLD_INO_TRACE - fprintf(stderr, " rmap prop agbno %d ", lptr->prev_agbno); -#endif - if (lptr->prev_agbno != NULLAGBLOCK) { - ASSERT(lptr->prev_buf_p != NULL); - libxfs_buf_mark_dirty(lptr->prev_buf_p); - libxfs_buf_relse(lptr->prev_buf_p); - } - lptr->prev_agbno = lptr->agbno; - lptr->prev_buf_p = lptr->buf_p; - agbno = get_next_blockaddr(agno, level, btree_curs); - - bt_hdr->bb_u.s.bb_rightsib = cpu_to_be32(agbno); - - error = -libxfs_buf_get(mp->m_dev, - XFS_AGB_TO_DADDR(mp, agno, agbno), - XFS_FSB_TO_BB(mp, 1), &lptr->buf_p); - if (error) - do_error(_("Cannot grab rmapbt buffer, err=%d"), - error); - lptr->agbno = agbno; - - if (lptr->modulo) - lptr->modulo--; - - /* - * initialize block header - */ - lptr->buf_p->b_ops = ops; - bt_hdr = XFS_BUF_TO_BLOCK(lptr->buf_p); - memset(bt_hdr, 0, mp->m_sb.sb_blocksize); - libxfs_btree_init_block(mp, lptr->buf_p, XFS_BTNUM_RMAP, - level, 0, agno); - - bt_hdr->bb_u.s.bb_leftsib = cpu_to_be32(lptr->prev_agbno); - - /* - * propagate extent record for first extent in new block up - */ - prop_rmap_cursor(mp, agno, btree_curs, rm_rec, level); - } - /* - * add rmap info to current block - */ - be16_add_cpu(&bt_hdr->bb_numrecs, 1); - - bt_key = XFS_RMAP_KEY_ADDR(bt_hdr, - be16_to_cpu(bt_hdr->bb_numrecs)); - bt_ptr = XFS_RMAP_PTR_ADDR(bt_hdr, - be16_to_cpu(bt_hdr->bb_numrecs), - mp->m_rmap_mxr[1]); - - bt_key->rm_startblock = cpu_to_be32(rm_rec->rm_startblock); - bt_key->rm_owner = cpu_to_be64(rm_rec->rm_owner); - bt_key->rm_offset = cpu_to_be64(rm_rec->rm_offset); - - *bt_ptr = cpu_to_be32(btree_curs->level[level-1].agbno); -} - -static void -prop_rmap_highkey( - struct xfs_mount *mp, - xfs_agnumber_t agno, - struct bt_status *btree_curs, - struct xfs_rmap_irec *rm_highkey) -{ - struct xfs_btree_block *bt_hdr; - struct xfs_rmap_key *bt_key; - struct bt_stat_level *lptr; - struct xfs_rmap_irec key = {0}; - struct xfs_rmap_irec high_key; - int level; - int i; - int numrecs; - - high_key = *rm_highkey; - for (level = 1; level < btree_curs->num_levels; level++) { - lptr = &btree_curs->level[level]; - bt_hdr = XFS_BUF_TO_BLOCK(lptr->buf_p); - numrecs = be16_to_cpu(bt_hdr->bb_numrecs); - bt_key = XFS_RMAP_HIGH_KEY_ADDR(bt_hdr, numrecs); - - bt_key->rm_startblock = cpu_to_be32(high_key.rm_startblock); - bt_key->rm_owner = cpu_to_be64(high_key.rm_owner); - bt_key->rm_offset = cpu_to_be64( - libxfs_rmap_irec_offset_pack(&high_key)); - - for (i = 1; i <= numrecs; i++) { - bt_key = XFS_RMAP_HIGH_KEY_ADDR(bt_hdr, i); - key.rm_startblock = be32_to_cpu(bt_key->rm_startblock); - key.rm_owner = be64_to_cpu(bt_key->rm_owner); - key.rm_offset = be64_to_cpu(bt_key->rm_offset); - if (rmap_diffkeys(&key, &high_key) > 0) - high_key = key; - } - } -} - -/* - * rebuilds a rmap btree given a cursor. - */ -static void -build_rmap_tree( - struct xfs_mount *mp, - xfs_agnumber_t agno, - struct bt_status *btree_curs) -{ - xfs_agnumber_t i; - xfs_agblock_t j; - xfs_agblock_t agbno; - struct xfs_btree_block *bt_hdr; - struct xfs_rmap_irec *rm_rec; - struct xfs_slab_cursor *rmap_cur; - struct xfs_rmap_rec *bt_rec; - struct xfs_rmap_irec highest_key = {0}; - struct xfs_rmap_irec hi_key = {0}; - struct bt_stat_level *lptr; - const struct xfs_buf_ops *ops = btnum_to_ops(XFS_BTNUM_RMAP); - int numrecs; - int level = btree_curs->num_levels; - int error; - - highest_key.rm_flags = 0; - for (i = 0; i < level; i++) { - lptr = &btree_curs->level[i]; - - agbno = get_next_blockaddr(agno, i, btree_curs); - error = -libxfs_buf_get(mp->m_dev, - XFS_AGB_TO_DADDR(mp, agno, agbno), - XFS_FSB_TO_BB(mp, 1), &lptr->buf_p); - if (error) - do_error(_("Cannot grab rmapbt buffer, err=%d"), - error); - - if (i == btree_curs->num_levels - 1) - btree_curs->root = agbno; - - lptr->agbno = agbno; - lptr->prev_agbno = NULLAGBLOCK; - lptr->prev_buf_p = NULL; - /* - * initialize block header - */ - - lptr->buf_p->b_ops = ops; - bt_hdr = XFS_BUF_TO_BLOCK(lptr->buf_p); - memset(bt_hdr, 0, mp->m_sb.sb_blocksize); - libxfs_btree_init_block(mp, lptr->buf_p, XFS_BTNUM_RMAP, - i, 0, agno); - } - - /* - * run along leaf, setting up records. as we have to switch - * blocks, call the prop_rmap_cursor routine to set up the new - * pointers for the parent. that can recurse up to the root - * if required. set the sibling pointers for leaf level here. - */ - error = rmap_init_cursor(agno, &rmap_cur); - if (error) - do_error( -_("Insufficient memory to construct reverse-map cursor.")); - rm_rec = pop_slab_cursor(rmap_cur); - lptr = &btree_curs->level[0]; - - for (i = 0; i < lptr->num_blocks; i++) { - numrecs = lptr->num_recs_pb + (lptr->modulo > 0); - ASSERT(rm_rec != NULL || numrecs == 0); - - /* - * block initialization, lay in block header - */ - lptr->buf_p->b_ops = ops; - bt_hdr = XFS_BUF_TO_BLOCK(lptr->buf_p); - memset(bt_hdr, 0, mp->m_sb.sb_blocksize); - libxfs_btree_init_block(mp, lptr->buf_p, XFS_BTNUM_RMAP, - 0, 0, agno); - - bt_hdr->bb_u.s.bb_leftsib = cpu_to_be32(lptr->prev_agbno); - bt_hdr->bb_numrecs = cpu_to_be16(numrecs); - - if (lptr->modulo > 0) - lptr->modulo--; - - if (lptr->num_recs_pb > 0) { - ASSERT(rm_rec != NULL); - prop_rmap_cursor(mp, agno, btree_curs, rm_rec, 0); - } - - bt_rec = (struct xfs_rmap_rec *) - ((char *)bt_hdr + XFS_RMAP_BLOCK_LEN); - highest_key.rm_startblock = 0; - highest_key.rm_owner = 0; - highest_key.rm_offset = 0; - for (j = 0; j < be16_to_cpu(bt_hdr->bb_numrecs); j++) { - ASSERT(rm_rec != NULL); - bt_rec[j].rm_startblock = - cpu_to_be32(rm_rec->rm_startblock); - bt_rec[j].rm_blockcount = - cpu_to_be32(rm_rec->rm_blockcount); - bt_rec[j].rm_owner = cpu_to_be64(rm_rec->rm_owner); - bt_rec[j].rm_offset = cpu_to_be64( - libxfs_rmap_irec_offset_pack(rm_rec)); - rmap_high_key_from_rec(rm_rec, &hi_key); - if (rmap_diffkeys(&hi_key, &highest_key) > 0) - highest_key = hi_key; - - rm_rec = pop_slab_cursor(rmap_cur); - } - - /* Now go set the parent key */ - prop_rmap_highkey(mp, agno, btree_curs, &highest_key); - - if (rm_rec != NULL) { - /* - * get next leaf level block - */ - if (lptr->prev_buf_p != NULL) { -#ifdef XR_BLD_RL_TRACE - fprintf(stderr, "writing rmapbt agbno %u\n", - lptr->prev_agbno); -#endif - ASSERT(lptr->prev_agbno != NULLAGBLOCK); - libxfs_buf_mark_dirty(lptr->prev_buf_p); - libxfs_buf_relse(lptr->prev_buf_p); - } - lptr->prev_buf_p = lptr->buf_p; - lptr->prev_agbno = lptr->agbno; - lptr->agbno = get_next_blockaddr(agno, 0, btree_curs); - bt_hdr->bb_u.s.bb_rightsib = cpu_to_be32(lptr->agbno); - - error = -libxfs_buf_get(mp->m_dev, - XFS_AGB_TO_DADDR(mp, agno, lptr->agbno), - XFS_FSB_TO_BB(mp, 1), - &lptr->buf_p); - if (error) - do_error( - _("Cannot grab rmapbt buffer, err=%d"), - error); - } - } - free_slab_cursor(&rmap_cur); -} - /* rebuild the refcount tree */ /* @@ -1155,7 +758,7 @@ build_agf_agfl( xfs_agnumber_t agno, struct bt_rebuild *btr_bno, struct bt_rebuild *btr_cnt, - struct bt_status *rmap_bt, + struct bt_rebuild *btr_rmap, struct bt_status *refcnt_bt, struct xfs_slab *lost_fsb) { @@ -1203,11 +806,17 @@ build_agf_agfl( cpu_to_be32(btr_cnt->newbt.afake.af_root); agf->agf_levels[XFS_BTNUM_CNT] = cpu_to_be32(btr_cnt->newbt.afake.af_levels); - agf->agf_roots[XFS_BTNUM_RMAP] = cpu_to_be32(rmap_bt->root); - agf->agf_levels[XFS_BTNUM_RMAP] = cpu_to_be32(rmap_bt->num_levels); agf->agf_freeblks = cpu_to_be32(btr_bno->freeblks); - agf->agf_rmap_blocks = cpu_to_be32(rmap_bt->num_tot_blocks - - rmap_bt->num_free_blocks); + + if (xfs_sb_version_hasrmapbt(&mp->m_sb)) { + agf->agf_roots[XFS_BTNUM_RMAP] = + cpu_to_be32(btr_rmap->newbt.afake.af_root); + agf->agf_levels[XFS_BTNUM_RMAP] = + cpu_to_be32(btr_rmap->newbt.afake.af_levels); + agf->agf_rmap_blocks = + cpu_to_be32(btr_rmap->newbt.afake.af_blocks); + } + agf->agf_refcount_root = cpu_to_be32(refcnt_bt->root); agf->agf_refcount_level = cpu_to_be32(refcnt_bt->num_levels); agf->agf_refcount_blocks = cpu_to_be32(refcnt_bt->num_tot_blocks - @@ -1225,7 +834,7 @@ build_agf_agfl( blks = btr_bno->newbt.afake.af_blocks + btr_cnt->newbt.afake.af_blocks - 2; if (xfs_sb_version_hasrmapbt(&mp->m_sb)) - blks += rmap_bt->num_tot_blocks - rmap_bt->num_free_blocks - 1; + blks += btr_rmap->newbt.afake.af_blocks - 1; agf->agf_btreeblks = cpu_to_be32(blks); #ifdef XR_BLD_FREE_TRACE fprintf(stderr, "agf->agf_btreeblks = %u\n", @@ -1270,6 +879,8 @@ build_agf_agfl( freelist = xfs_buf_to_agfl_bno(agfl_buf); fill_agfl(btr_bno, freelist, &agfl_idx); fill_agfl(btr_cnt, freelist, &agfl_idx); + if (xfs_sb_version_hasrmapbt(&mp->m_sb)) + fill_agfl(btr_rmap, freelist, &agfl_idx); /* Set the AGF counters for the AGFL. */ if (agfl_idx > 0) { @@ -1369,7 +980,7 @@ phase5_func( struct bt_rebuild btr_cnt; struct bt_rebuild btr_ino; struct bt_rebuild btr_fino; - bt_status_t rmap_btree_curs; + struct bt_rebuild btr_rmap; bt_status_t refcnt_btree_curs; int extra_blocks = 0; uint num_freeblocks; @@ -1404,11 +1015,7 @@ _("unable to rebuild AG %u. Not enough free space in on-disk AG.\n"), init_ino_cursors(&sc, agno, num_freeblocks, &sb_icount_ag[agno], &sb_ifree_ag[agno], &btr_ino, &btr_fino); - /* - * Set up the btree cursors for the on-disk rmap btrees, which includes - * pre-allocating all required blocks. - */ - init_rmapbt_cursor(mp, agno, &rmap_btree_curs); + init_rmapbt_cursor(&sc, agno, num_freeblocks, &btr_rmap); /* * Set up the btree cursors for the on-disk refcount btrees, @@ -1474,10 +1081,8 @@ _("unable to rebuild AG %u. Not enough free space in on-disk AG.\n"), ASSERT(btr_bno.freeblks == btr_cnt.freeblks); if (xfs_sb_version_hasrmapbt(&mp->m_sb)) { - build_rmap_tree(mp, agno, &rmap_btree_curs); - write_cursor(&rmap_btree_curs); - sb_fdblocks_ag[agno] += (rmap_btree_curs.num_tot_blocks - - rmap_btree_curs.num_free_blocks) - 1; + build_rmap_tree(&sc, agno, &btr_rmap); + sb_fdblocks_ag[agno] += btr_rmap.newbt.afake.af_blocks - 1; } if (xfs_sb_version_hasreflink(&mp->m_sb)) { @@ -1488,7 +1093,7 @@ _("unable to rebuild AG %u. Not enough free space in on-disk AG.\n"), /* * set up agf and agfl */ - build_agf_agfl(mp, agno, &btr_bno, &btr_cnt, &rmap_btree_curs, + build_agf_agfl(mp, agno, &btr_bno, &btr_cnt, &btr_rmap, &refcnt_btree_curs, lost_fsb); build_inode_btrees(&sc, agno, &btr_ino, &btr_fino); @@ -1505,7 +1110,7 @@ _("unable to rebuild AG %u. Not enough free space in on-disk AG.\n"), if (xfs_sb_version_hasfinobt(&mp->m_sb)) finish_rebuild(mp, &btr_fino, lost_fsb); if (xfs_sb_version_hasrmapbt(&mp->m_sb)) - finish_cursor(&rmap_btree_curs); + finish_rebuild(mp, &btr_rmap, lost_fsb); if (xfs_sb_version_hasreflink(&mp->m_sb)) finish_cursor(&refcnt_btree_curs);