diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2019-08-07 13:51:28 -0700 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2019-08-10 09:58:10 -0700 |
commit | e744ed92731440350ce23c58c6aa22e894b521f7 (patch) | |
tree | 5c6b4e7ac558289d7c57492de117115156a51439 /fs | |
parent | 6b35d437d9b8c2447f74f96228be30890ae94c44 (diff) |
convert refcount repair to bulk load
Diffstat (limited to 'fs')
-rw-r--r-- | fs/xfs/libxfs/xfs_refcount_btree.c | 13 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_refcount_btree.h | 4 | ||||
-rw-r--r-- | fs/xfs/scrub/refcount_repair.c | 191 |
3 files changed, 116 insertions, 92 deletions
diff --git a/fs/xfs/libxfs/xfs_refcount_btree.c b/fs/xfs/libxfs/xfs_refcount_btree.c index ce7633a953f5..60c043f1f81a 100644 --- a/fs/xfs/libxfs/xfs_refcount_btree.c +++ b/fs/xfs/libxfs/xfs_refcount_btree.c @@ -405,18 +405,21 @@ xfs_refcountbt_stage_cursor( */ void xfs_refcountbt_commit_staged_btree( - struct xfs_trans *tp, - struct xbtree_afakeroot *afake, + struct xfs_btree_cur *cur, struct xfs_buf *agbp) { struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp); + struct xbtree_afakeroot *afake = cur->bc_private.a.afake; + + ASSERT(cur->bc_flags & XFS_BTREE_STAGING); agf->agf_refcount_root = cpu_to_be32(afake->af_root); agf->agf_refcount_level = cpu_to_be32(afake->af_levels); agf->agf_refcount_blocks = cpu_to_be32(afake->af_blocks); - xfs_alloc_log_agf(tp, agbp, XFS_AGF_REFCOUNT_BLOCKS | - XFS_AGF_REFCOUNT_ROOT | - XFS_AGF_REFCOUNT_LEVEL); + xfs_alloc_log_agf(cur->bc_tp, agbp, XFS_AGF_REFCOUNT_BLOCKS | + XFS_AGF_REFCOUNT_ROOT | + XFS_AGF_REFCOUNT_LEVEL); + xfs_btree_commit_afakeroot(cur, agbp, &xfs_refcountbt_ops); } /* diff --git a/fs/xfs/libxfs/xfs_refcount_btree.h b/fs/xfs/libxfs/xfs_refcount_btree.h index b66ba2bd6985..978b714be9f4 100644 --- a/fs/xfs/libxfs/xfs_refcount_btree.h +++ b/fs/xfs/libxfs/xfs_refcount_btree.h @@ -62,7 +62,7 @@ extern int xfs_refcountbt_calc_reserves(struct xfs_mount *mp, struct xfs_trans *tp, xfs_agnumber_t agno, xfs_extlen_t *ask, xfs_extlen_t *used); -void xfs_refcountbt_commit_staged_btree(struct xfs_trans *tp, - struct xbtree_afakeroot *afake, struct xfs_buf *agbp); +void xfs_refcountbt_commit_staged_btree(struct xfs_btree_cur *cur, + struct xfs_buf *agbp); #endif /* __XFS_REFCOUNT_BTREE_H__ */ diff --git a/fs/xfs/scrub/refcount_repair.c b/fs/xfs/scrub/refcount_repair.c index 6193191dd76a..f5d6a4b37cbe 100644 --- a/fs/xfs/scrub/refcount_repair.c +++ b/fs/xfs/scrub/refcount_repair.c @@ -107,6 +107,9 @@ struct xrep_refc { /* refcount extents */ struct xfbma *refcount_records; + /* new refcountbt information */ + struct xrep_newbt new_btree_info; + /* old refcountbt blocks */ struct xfs_bitmap old_refcountbt_blocks; @@ -115,8 +118,8 @@ struct xrep_refc { /* # of refcountbt blocks */ xfs_extlen_t btblocks; - /* Fake root for new btree. */ - struct xbtree_afakeroot refc_root; + /* Iterator */ + uint64_t iter; }; /* Grab the next (abbreviated) rmap record from the rmapbt. */ @@ -389,76 +392,53 @@ out_cur: } #undef RRM_NEXT -/* - * Initialize new refcountbt root block and set up a fake root so we can build - * a new btree and only swap it if we're successful. - */ +/* Retrieve refcountbt data for bulk load. */ STATIC int -xrep_refc_stage_btree( - struct xrep_refc *rr) +xrep_refc_get_data( + struct xfs_btree_cur *cur, + void *priv) { - struct xfs_scrub *sc = rr->sc; - struct xfs_mount *mp = sc->mp; - struct xfs_buf *bp; - xfs_fsblock_t btfsb; - xfs_extlen_t blocks; - int error; + struct xfs_refcount_irec *refc = &cur->bc_rec.rc; + struct xrep_refc *rr = priv; + int error; - /* Do we actually have enough space to do this? */ - blocks = xfs_refcountbt_calc_size(mp, - xfbma_length(rr->refcount_records)); - if (!xrep_ag_has_space(sc->sa.pag, blocks, XFS_AG_RESV_METADATA)) - return -ENOSPC; + do { + error = xfbma_get(rr->refcount_records, rr->iter++, refc); + } while (error == 0 && xfbma_is_null(rr->refcount_records, refc)); - /* Initialize a new refcountbt root. */ - error = xrep_alloc_ag_block(sc, &XFS_RMAP_OINFO_REFC, &btfsb, - XFS_AG_RESV_METADATA); - if (error) - return error; - error = xrep_init_btblock(sc, btfsb, &bp, XFS_BTNUM_REFC, - &xfs_refcountbt_buf_ops); - if (error) - return error; - xbtree_afakeroot_init(sc->mp, &rr->refc_root, - XFS_FSB_TO_AGBNO(sc->mp, btfsb)); - return 0; + return error; } -/* Insert a single record into the refcount btree. */ +/* Feed one of the new btree blocks to the bulk loader. */ STATIC int -xrep_refc_insert_rec( - const void *item, - void *priv) +xrep_refc_bload_alloc( + struct xfs_btree_cur *cur, + union xfs_btree_ptr *ptr, + void *priv) { - struct xrep_refc *rr = priv; - const struct xrep_refc_extent *rre = item; - struct xfs_refcount_irec refc = { - .rc_startblock = rre->startblock, - .rc_blockcount = rre->blockcount, - .rc_refcount = rre->refcount, - }; - struct xfs_scrub *sc = rr->sc; - struct xfs_mount *mp = sc->mp; - struct xfs_btree_cur *cur; - int have_gt; - int error; + struct xrep_refc *rr = priv; - /* Insert into the refcountbt. */ - cur = xfs_refcountbt_stage_cursor(mp, sc->tp, &rr->refc_root, - sc->sa.agno); - error = xfs_refcount_lookup_eq(cur, rre->startblock, &have_gt); - if (error) - goto out; - XFS_WANT_CORRUPTED_GOTO(mp, have_gt == 0, out); - error = xfs_refcount_insert(cur, &refc, &have_gt); - if (error) - goto out; - XFS_WANT_CORRUPTED_GOTO(mp, have_gt == 1, out); - xfs_btree_del_cursor(cur, error); - return xrep_roll_ag_trans(sc); -out: - xfs_btree_del_cursor(cur, error); - return error; + return xrep_newbt_alloc_block(cur, &rr->new_btree_info, ptr); +} + +/* Update the AGF counters. */ +STATIC int +xrep_refc_reset_counters( + struct xrep_refc *rr) +{ + struct xfs_scrub *sc = rr->sc; + struct xfs_perag *pag = sc->sa.pag; + struct xfs_buf *bp; + + /* + * Mark the pagf information stale and use the accessor function to + * forcibly reload it from the values we just logged. We still own the + * AGF bp so we can safely ignore bp. + */ + ASSERT(pag->pagf_init); + pag->pagf_init = 0; + + return xfs_alloc_read_agf(sc->mp, sc->tp, sc->sa.agno, 0, &bp); } /* @@ -470,50 +450,91 @@ STATIC int xrep_refc_build_new_tree( struct xrep_refc *rr) { + struct xfs_btree_bload refc_bload; struct xfs_scrub *sc = rr->sc; + struct xfs_btree_cur *refc_cur; int error; /* - * Sort the refcount extents by startblock to avoid btree splits when - * we rebuild the refcount btree. + * Sort the refcount extents by startblock or else the btree records + * will be in the wrong order. */ error = xfbma_sort(rr->refcount_records, xrep_refc_extent_cmp); if (error) return error; /* - * Create a new btree for staging all the refcount records we collected - * earlier. This btree will not be rooted in the AGF until we've - * succesfully reloaded the tree. + * Prepare to construct the new btree by reserving disk space for the + * new btree and setting up all the accounting information we'll need + * to root the new btree while it's under construction and before we + * attach it to the AG header. */ - error = xrep_refc_stage_btree(rr); - if (error) - return error; + xrep_newbt_init(&rr->new_btree_info, sc, &XFS_RMAP_OINFO_REFC, + XFS_AGB_TO_FSB(sc->mp, sc->sa.agno, + xfs_refc_block(sc->mp)), + XFS_AG_RESV_METADATA); - /* Add all records. */ - error = xfbma_iter_del(rr->refcount_records, xrep_refc_insert_rec, rr); + /* Compute how many blocks we'll need. */ + refc_cur = xfs_refcountbt_stage_cursor(sc->mp, sc->tp, + &rr->new_btree_info.afake, sc->sa.agno); + error = xfs_btree_bload_init(refc_cur, &refc_bload, + xfbma_length(rr->refcount_records), 0, 0); if (error) - return error; + goto err_cur; + xfs_btree_del_cursor(refc_cur, error); - /* Clean transaction ahead of installing the new btree root. */ - error = xrep_roll_ag_trans(sc); + /* + * Reserve the space we'll need for the new btree. Drop the cursor + * while we do this because that can roll the transaction and cursors + * can't handle that. + */ + error = xrep_newbt_reserve_space(&rr->new_btree_info, + refc_bload.nr_blocks); if (error) - return error; + goto err_newbt; + + /* Add all observed refcount records. */ + rr->iter = 0; + refc_cur = xfs_refcountbt_stage_cursor(sc->mp, sc->tp, + &rr->new_btree_info.afake, sc->sa.agno); + error = xfs_btree_bload(refc_cur, &refc_bload, xrep_refc_get_data, + xrep_refc_bload_alloc, rr); + if (error) + goto err_cur; /* - * Re-read the AGF so that the buffer type is set properly. Since we - * built a new tree without dirtying the AGF, the buffer item may have - * fallen off the buffer. This ought to succeed since the AGF is held - * across transaction rolls. + * Install the new btree in the AG header. After this point the old + * btree is no longer accessible and the new tree is live. + * + * Note: We re-read the AGF here to ensure the buffer type is set + * properly. Since we built a new tree without attaching to the AGF + * buffer, the buffer item may have fallen off the buffer. This ought + * to succeed since the AGF is held across transaction rolls. */ error = xfs_read_agf(sc->mp, sc->tp, sc->sa.agno, 0, &sc->sa.agf_bp); if (error) + goto err_cur; + + /* Commit our new btree. */ + xfs_refcountbt_commit_staged_btree(refc_cur, sc->sa.agf_bp); + xfs_btree_del_cursor(refc_cur, 0); + + /* Reset the AGF counters now that we've changed the btree shape. */ + error = xrep_refc_reset_counters(rr); + if (error) + goto err_newbt; + + /* Dispose of any unused blocks and the accounting infomation. */ + error = xrep_newbt_unreserve_space(&rr->new_btree_info); + if (error) return error; - /* Install new btree root. */ - xfs_refcountbt_commit_staged_btree(sc->tp, &rr->refc_root, - sc->sa.agf_bp); - return 0; + return xrep_roll_ag_trans(sc); +err_cur: + xfs_btree_del_cursor(refc_cur, error); +err_newbt: + xrep_newbt_unreserve_space(&rr->new_btree_info); + return error; } /* |