diff options
-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 | 208 |
3 files changed, 158 insertions, 91 deletions
diff --git a/fs/xfs/scrub/bitmap.c b/fs/xfs/scrub/bitmap.c index 547fba952c76..9942f8f249f2 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 5a6933f41c71..b02cfae9ecaf 100644 --- a/fs/xfs/scrub/repair.c +++ b/fs/xfs/scrub/repair.c @@ -27,6 +27,10 @@ #include "xfs_quota.h" #include "xfs_qm.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" @@ -491,30 +495,55 @@ 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_agnumber_t agno = sc->sa.pag->pag_agno; + xfs_agblock_t agbno_next = agbno + len; /* - * 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, agno, agbno) || + !xfs_verify_agbno(mp, 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 | _XBF_IGNORE_STALE); - if (!bp) - return; + /* + * If there are incore buffers for these blocks, invalidate them. If + * we can't (try)lock the buffer we assume it's owned by someone else + * and leave it alone. The buffer cache cannot detect aliasing, so + * employ nested loops to detect incore buffers of any plausible size. + */ + for (; agbno < agbno_next; agbno++) { + xfs_agblock_t fsbcount; + xfs_agblock_t max_fsbs; + + /* + * Max buffer size is the max remote xattr buffer size, which + * is one fs block larger than 64k. + */ + max_fsbs = min_t(xfs_agblock_t, agbno_next - agbno, + xfs_attr3_rmt_blocks(mp, XFS_XATTR_SIZE_MAX)); + + for (fsbcount = 1; fsbcount < max_fsbs; fsbcount++) { + xfs_daddr_t daddr; + + daddr = XFS_AGB_TO_DADDR(mp, agno, agbno); + bp = xfs_buf_incore(mp->m_ddev_targp, daddr, + XFS_FSB_TO_BB(mp, fsbcount), + XBF_TRYLOCK | _XBF_IGNORE_STALE); + if (!bp) + continue; - xfs_trans_bjoin(sc->tp, bp); - xfs_trans_binval(sc->tp, bp); + xfs_trans_bjoin(sc->tp, bp); + xfs_trans_binval(sc->tp, bp); + } + } } struct xrep_reap_state { @@ -524,38 +553,20 @@ 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); /* * If there are other rmappings, this block is cross linked and must @@ -570,20 +581,20 @@ xrep_reap_block( * blow on writeout, the filesystem will shut down, and the admin gets * to run xfs_repair. */ - if (has_other_rmap) { - trace_xrep_dispose_unmap_extent(sc->sa.pag, agbno, 1); + if (crosslinked) { + trace_xrep_dispose_unmap_extent(sc->sa.pag, agbno, aglen); error = xfs_rmap_free(sc->tp, sc->sa.agf_bp, sc->sa.pag, agbno, - 1, rs->oinfo); + aglen, rs->oinfo); if (error) return error; goto roll_out; } - trace_xrep_dispose_free_extent(sc->sa.pag, agbno, 1); + trace_xrep_dispose_free_extent(sc->sa.pag, agbno, aglen); - xrep_reap_invalidate_block(sc, fsbno); + xrep_reap_invalidate_extent(sc, agbno, aglen); if (rs->resv == XFS_AG_RESV_AGFL) { error = xrep_put_freelist(sc, agbno); @@ -595,7 +606,7 @@ xrep_reap_block( * every 100 or so EFIs so that we don't exceed the log * reservation. */ - __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; } @@ -607,6 +618,103 @@ roll_out: 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( @@ -624,7 +732,7 @@ xrep_reap_extents( ASSERT(xfs_has_rmapbt(sc->mp)); - error = xbitmap_walk_bits(bitmap, xrep_reap_block, &rs); + error = xbitmap_walk(bitmap, xrep_reap_extent, &rs); if (error || rs.deferred == 0) return error; |