summaryrefslogtreecommitdiff
path: root/fs/xfs/scrub/reap.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/scrub/reap.c')
-rw-r--r--fs/xfs/scrub/reap.c121
1 files changed, 117 insertions, 4 deletions
diff --git a/fs/xfs/scrub/reap.c b/fs/xfs/scrub/reap.c
index bfc3583132ac..0d2e32fbb51a 100644
--- a/fs/xfs/scrub/reap.c
+++ b/fs/xfs/scrub/reap.c
@@ -38,6 +38,7 @@
#include "scrub/repair.h"
#include "scrub/bitmap.h"
#include "scrub/agb_bitmap.h"
+#include "scrub/fsb_bitmap.h"
#include "scrub/reap.h"
/*
@@ -75,10 +76,10 @@
* with only the same rmap owner but the block is not owned by something with
* the same rmap owner, the block will be freed.
*
- * The caller is responsible for locking the AG headers for the entire rebuild
- * operation so that nothing else can sneak in and change the AG state while
- * we're not looking. We must also invalidate any buffers associated with
- * @bitmap.
+ * The caller is responsible for locking the AG headers/inode for the entire
+ * rebuild operation so that nothing else can sneak in and change the incore
+ * state while we're not looking. We must also invalidate any buffers
+ * associated with @bitmap.
*/
/* Information about reaping extents after a repair. */
@@ -501,3 +502,115 @@ xrep_reap_agblocks(
return 0;
}
+
+/*
+ * Break a file metadata extent into sub-extents by fate (crosslinked, not
+ * crosslinked), and dispose of each sub-extent separately. The extent must
+ * not cross an AG boundary.
+ */
+STATIC int
+xreap_fsmeta_extent(
+ uint64_t fsbno,
+ uint64_t len,
+ void *priv)
+{
+ struct xreap_state *rs = priv;
+ struct xfs_scrub *sc = rs->sc;
+ 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 <= XFS_MAX_BMBT_EXTLEN);
+ ASSERT(sc->ip != NULL);
+ ASSERT(!sc->sa.pag);
+
+ /*
+ * We're reaping blocks after repairing file metadata, which means that
+ * we have to init the xchk_ag structure ourselves.
+ */
+ sc->sa.pag = xfs_perag_get(sc->mp, agno);
+ if (!sc->sa.pag)
+ return -EFSCORRUPTED;
+
+ error = xfs_alloc_read_agf(sc->sa.pag, sc->tp, 0, &sc->sa.agf_bp);
+ if (error)
+ goto out_pag;
+
+ while (agbno < agbno_next) {
+ xfs_extlen_t aglen;
+ bool crosslinked;
+
+ error = xreap_agextent_select(rs, agbno, agbno_next,
+ &crosslinked, &aglen);
+ if (error)
+ goto out_agf;
+
+ error = xreap_agextent_iter(rs, agbno, &aglen, crosslinked);
+ if (error)
+ goto out_agf;
+
+ if (xreap_want_defer_finish(rs)) {
+ /*
+ * Holds the AGF buffer across the deferred chain
+ * processing.
+ */
+ error = xrep_defer_finish(sc);
+ if (error)
+ goto out_agf;
+ xreap_defer_finish_reset(rs);
+ } else if (xreap_want_roll(rs)) {
+ /*
+ * Hold the AGF buffer across the transaction roll so
+ * that we don't have to reattach it to the scrub
+ * context.
+ */
+ xfs_trans_bhold(sc->tp, sc->sa.agf_bp);
+ error = xfs_trans_roll_inode(&sc->tp, sc->ip);
+ xfs_trans_bjoin(sc->tp, sc->sa.agf_bp);
+ if (error)
+ goto out_agf;
+ xreap_reset(rs);
+ }
+
+ agbno += aglen;
+ }
+
+out_agf:
+ xfs_trans_brelse(sc->tp, sc->sa.agf_bp);
+ sc->sa.agf_bp = NULL;
+out_pag:
+ xfs_perag_put(sc->sa.pag);
+ sc->sa.pag = NULL;
+ return error;
+}
+
+/*
+ * Dispose of every block of every fs metadata extent in the bitmap.
+ * Do not use this to dispose of the mappings in an ondisk inode fork.
+ */
+int
+xrep_reap_fsblocks(
+ struct xfs_scrub *sc,
+ struct xfsb_bitmap *bitmap,
+ const struct xfs_owner_info *oinfo)
+{
+ struct xreap_state rs = {
+ .sc = sc,
+ .oinfo = oinfo,
+ .resv = XFS_AG_RESV_NONE,
+ };
+ int error;
+
+ ASSERT(xfs_has_rmapbt(sc->mp));
+ ASSERT(sc->ip != NULL);
+
+ error = xfsb_bitmap_walk(bitmap, xreap_fsmeta_extent, &rs);
+ if (error)
+ return error;
+
+ if (xreap_dirty(&rs))
+ return xrep_defer_finish(sc);
+
+ return 0;
+}