diff options
author | Darrick J. Wong <djwong@kernel.org> | 2021-05-03 11:45:02 -0700 |
---|---|---|
committer | Darrick J. Wong <djwong@kernel.org> | 2021-08-25 22:25:51 -0700 |
commit | 9ce1de42246798b74f6521f403713706e2300775 (patch) | |
tree | 4d29a6f0329faa25261f1a463ac85d32e81930fe | |
parent | 686a06fb9c336ab369fc737fb33f71aed1cd6961 (diff) |
xfs: reap large extents when possible
When we're freeing extents that have been set in a bitmap, break the
bitmap extent into multiple sub-extents organized by fate, and reap the
extents. This enables us to dispose of old resources more efficiently
than doing them block by block.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
-rw-r--r-- | fs/xfs/scrub/bitmap.c | 37 | ||||
-rw-r--r-- | fs/xfs/scrub/bitmap.h | 4 | ||||
-rw-r--r-- | fs/xfs/scrub/repair.c | 205 |
3 files changed, 157 insertions, 89 deletions
diff --git a/fs/xfs/scrub/bitmap.c b/fs/xfs/scrub/bitmap.c index 58eadc20e8f8..d19dddd9401c 100644 --- a/fs/xfs/scrub/bitmap.c +++ b/fs/xfs/scrub/bitmap.c @@ -333,40 +333,3 @@ xbitmap_walk( return error; } - -struct xbitmap_walk_bits { - xbitmap_walk_bits_fn fn; - void *priv; -}; - -/* Walk all the bits in a run. */ -static int -xbitmap_walk_bits_in_run( - uint64_t start, - uint64_t len, - void *priv) -{ - struct xbitmap_walk_bits *wb = priv; - uint64_t i; - int error = 0; - - for (i = start; i < start + len; i++) { - error = wb->fn(i, wb->priv); - if (error) - break; - } - - return error; -} - -/* Call a function for every set bit in this bitmap. */ -int -xbitmap_walk_bits( - struct xbitmap *bitmap, - xbitmap_walk_bits_fn fn, - void *priv) -{ - struct xbitmap_walk_bits wb = {.fn = fn, .priv = priv}; - - return xbitmap_walk(bitmap, xbitmap_walk_bits_in_run, &wb); -} diff --git a/fs/xfs/scrub/bitmap.h b/fs/xfs/scrub/bitmap.h index 53601d281ffb..6cba89c50a38 100644 --- a/fs/xfs/scrub/bitmap.h +++ b/fs/xfs/scrub/bitmap.h @@ -38,8 +38,4 @@ typedef int (*xbitmap_walk_fn)(uint64_t start, uint64_t len, void *priv); int xbitmap_walk(struct xbitmap *bitmap, xbitmap_walk_fn fn, void *priv); -typedef int (*xbitmap_walk_bits_fn)(uint64_t bit, void *priv); -int xbitmap_walk_bits(struct xbitmap *bitmap, xbitmap_walk_bits_fn fn, - void *priv); - #endif /* __XFS_SCRUB_BITMAP_H__ */ diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c index 545fb47e771d..2de384a26a00 100644 --- a/fs/xfs/scrub/repair.c +++ b/fs/xfs/scrub/repair.c @@ -26,6 +26,10 @@ #include "xfs_ag_resv.h" #include "xfs_quota.h" #include "xfs_bmap.h" +#include "xfs_da_format.h" +#include "xfs_da_btree.h" +#include "xfs_attr.h" +#include "xfs_attr_remote.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" @@ -490,29 +494,53 @@ xrep_put_freelist( /* Try to invalidate the incore buffer for a block that we're about to free. */ STATIC void -xrep_reap_invalidate_block( +xrep_reap_invalidate_extent( struct xfs_scrub *sc, - xfs_fsblock_t fsbno) + xfs_agblock_t agbno, + xfs_extlen_t len) { + struct xfs_mount *mp = sc->mp; struct xfs_buf *bp; + xfs_agblock_t agbno_next = agbno + len; + xfs_agblock_t i; /* - * If there's an incore buffer for exactly this block, invalidate it. * Avoid invalidating AG headers and post-EOFS blocks because we never - * own those; and if we can't TRYLOCK the buffer we assume it's owned - * by someone else. + * own those. */ - if (!xfs_verify_fsbno(sc->mp, fsbno)) + if (!xfs_verify_agbno(mp, sc->sa.pag->pag_agno, agbno) || + !xfs_verify_agbno(mp, sc->sa.pag->pag_agno, agbno_next - 1)) return; - bp = xfs_buf_incore(sc->mp->m_ddev_targp, - XFS_FSB_TO_DADDR(sc->mp, fsbno), - XFS_FSB_TO_BB(sc->mp, 1), XBF_TRYLOCK); - if (!bp) - return; + /* + * If there are incore buffers for these blocks, invalidate them. If + * we can't TRYLOCK the buffer we assume it's owned by someone else. + * Double loops are required here because the buffer cache cannot + * detect aliasing. + */ + for (i = agbno; i < agbno_next; i++) { + xfs_agblock_t j; + xfs_agblock_t max_len; - xfs_trans_bjoin(sc->tp, bp); - xfs_trans_binval(sc->tp, bp); + /* + * Max buffer size is the max remote xattr buffer size, which + * is one fs block larger than 64k. + */ + max_len = min_t(xfs_agblock_t, agbno_next - i, + xfs_attr3_rmt_blocks(mp, XFS_XATTR_SIZE_MAX)); + + for (j = i; j < max_len; j++) { + bp = xfs_buf_incore(mp->m_ddev_targp, + XFS_AGB_TO_DADDR(mp, sc->sa.pag->pag_agno, i), + XFS_FSB_TO_BB(mp, j), + XBF_TRYLOCK); + if (!bp) + continue; + + xfs_trans_bjoin(sc->tp, bp); + xfs_trans_binval(sc->tp, bp); + } + } } struct xrep_reap_state { @@ -522,38 +550,21 @@ struct xrep_reap_state { unsigned int deferred; }; -/* Dispose of a single block. */ +/* Dispose of a single extent. */ STATIC int -xrep_reap_block( - uint64_t fsbno, - void *priv) +xrep_reap_ag_extent( + struct xrep_reap_state *rs, + xfs_agblock_t agbno, + xfs_extlen_t aglen, + bool crosslinked) { - struct xrep_reap_state *rs = priv; - struct xfs_scrub *sc = rs->sc; - struct xfs_btree_cur *cur; - xfs_agnumber_t agno; - xfs_agblock_t agbno; - bool has_other_rmap; - bool need_roll = true; - int error; - - agno = XFS_FSB_TO_AGNO(sc->mp, fsbno); - agbno = XFS_FSB_TO_AGBNO(sc->mp, fsbno); - - /* We don't support reaping file extents yet. */ - if (sc->ip != NULL || sc->sa.pag->pag_agno != agno) { - ASSERT(0); - return -EFSCORRUPTED; - } + struct xfs_scrub *sc = rs->sc; + xfs_fsblock_t fsbno; + bool need_roll = true; + int error = 0; - cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, sc->sa.agf_bp, sc->sa.pag); - - /* Can we find any other rmappings? */ - error = xfs_rmap_has_other_keys(cur, agbno, 1, rs->oinfo, - &has_other_rmap); - xfs_btree_del_cursor(cur, error); - if (error) - return error; + fsbno = XFS_AGB_TO_FSB(sc->mp, sc->sa.pag->pag_agno, agbno); + trace_xrep_dispose_btree_extent(sc->mp, sc->sa.pag->pag_agno, agbno, aglen); /* * If there are other rmappings, this block is cross linked and must @@ -568,11 +579,13 @@ xrep_reap_block( * blow on writeout, the filesystem will shut down, and the admin gets * to run xfs_repair. */ - if (has_other_rmap) { + if (!crosslinked) + xrep_reap_invalidate_extent(sc, agbno, aglen); + + if (crosslinked) { error = xfs_rmap_free(sc->tp, sc->sa.agf_bp, sc->sa.pag, agbno, - 1, rs->oinfo); + aglen, rs->oinfo); } else if (rs->resv == XFS_AG_RESV_AGFL) { - xrep_reap_invalidate_block(sc, fsbno); error = xrep_put_freelist(sc, agbno); } else { /* @@ -582,8 +595,7 @@ xrep_reap_block( * every 100 or so EFIs so that we don't exceed the log * reservation. */ - xrep_reap_invalidate_block(sc, fsbno); - __xfs_bmap_add_free(sc->tp, fsbno, 1, rs->oinfo, false); + __xfs_bmap_add_free(sc->tp, fsbno, aglen, rs->oinfo, false); rs->deferred++; need_roll = rs->deferred > 100; } @@ -594,6 +606,103 @@ xrep_reap_block( return xrep_roll_ag_trans(sc); } +/* + * Figure out the longest run of blocks that we can dispose of with a single + * call. Cross-linked blocks should have their reverse mappings removed, but + * single-owner extents can be freed. AGFL blocks can only be put back one at + * a time. + */ +STATIC int +xrep_reap_find_longest( + struct xrep_reap_state *rs, + struct xfs_btree_cur *cur, + xfs_agblock_t agbno, + xfs_agblock_t agbno_next, + bool agbno_crosslinked, + xfs_extlen_t *len) +{ + int error; + + if (rs->resv == XFS_AG_RESV_AGFL) + return 0; + + for (agbno++; agbno < agbno_next; agbno++) { + bool crosslinked; + + error = xfs_rmap_has_other_keys(cur, agbno, 1, rs->oinfo, + &crosslinked); + if (error) + return error; + + if (crosslinked != agbno_crosslinked) + return 0; + (*len)++; + } + + return 0; +} + +/* + * Break a bitmap extent into sub-extents by fate, and dispose of each + * sub-extent separately. + */ +STATIC int +xrep_reap_extent( + uint64_t fsbno, + uint64_t len, + void *priv) +{ + struct xrep_reap_state *rs = priv; + struct xfs_scrub *sc = rs->sc; + struct xfs_btree_cur *cur = NULL; + xfs_agnumber_t agno = XFS_FSB_TO_AGNO(sc->mp, fsbno); + xfs_agblock_t agbno = XFS_FSB_TO_AGBNO(sc->mp, fsbno); + xfs_agblock_t agbno_next = agbno + len; + int error = 0; + + ASSERT(len <= MAXEXTLEN); + + /* We don't support reaping file extents yet. */ + if (sc->ip != NULL || sc->sa.pag->pag_agno != agno) { + ASSERT(0); + return -EFSCORRUPTED; + } + + while (agbno < agbno_next) { + xfs_extlen_t len = 1; + bool agbno_crosslinked; + + cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, sc->sa.agf_bp, + sc->sa.pag); + + /* Find the longest extent we can reap all at once. */ + error = xfs_rmap_has_other_keys(cur, agbno, 1, rs->oinfo, + &agbno_crosslinked); + if (error) + goto out_cur; + + error = xrep_reap_find_longest(rs, cur, agbno, agbno_next, + agbno_crosslinked, &len); + if (error) + goto out_cur; + + /* Free the cursor because reap rolls the transaction. */ + xfs_btree_del_cursor(cur, 0); + cur = NULL; + + error = xrep_reap_ag_extent(rs, agbno, len, agbno_crosslinked); + if (error) + goto out_cur; + + agbno += len; + } + +out_cur: + if (cur) + xfs_btree_del_cursor(cur, error); + return error; +} + /* Dispose of every block of every extent in the bitmap. */ int xrep_reap_extents( @@ -611,7 +720,7 @@ xrep_reap_extents( ASSERT(xfs_sb_version_hasrmapbt(&sc->mp->m_sb)); - error = xbitmap_walk_bits(bitmap, xrep_reap_block, &rs); + error = xbitmap_walk(bitmap, xrep_reap_extent, &rs); if (error || rs.deferred == 0) return error; |