@@ -20,11 +20,6 @@
#include "descr.h"
#include "scrub_private.h"
-static int repair_epilogue(struct scrub_ctx *ctx, struct descr *dsc,
- struct scrub_item *sri, unsigned int repair_flags,
- const struct xfs_scrub_vec *oldm,
- const struct xfs_scrub_vec *meta);
-
/* General repair routines. */
/*
@@ -83,64 +78,14 @@ repair_want_service_downgrade(
return false;
}
-/* Repair some metadata. */
-static int
-xfs_repair_metadata(
- struct scrub_ctx *ctx,
- struct xfs_fd *xfdp,
- unsigned int scrub_type,
- struct scrub_item *sri,
- unsigned int repair_flags)
+static inline void
+restore_oldvec(
+ struct xfs_scrub_vec *oldvec,
+ const struct scrub_item *sri,
+ unsigned int scrub_type)
{
- struct xfs_scrub_metadata meta = { 0 };
- struct xfs_scrub_vec oldm, vec;
- DEFINE_DESCR(dsc, ctx, format_scrub_descr);
- bool repair_only;
-
- /*
- * If the caller boosted the priority of this scrub type on behalf of a
- * higher level repair by setting IFLAG_REPAIR, turn off REPAIR_ONLY.
- */
- repair_only = (repair_flags & XRM_REPAIR_ONLY) &&
- scrub_item_type_boosted(sri, scrub_type);
-
- assert(scrub_type < XFS_SCRUB_TYPE_NR);
- assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
- meta.sm_type = scrub_type;
- meta.sm_flags = XFS_SCRUB_IFLAG_REPAIR;
- if (use_force_rebuild)
- meta.sm_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD;
- switch (xfrog_scrubbers[scrub_type].group) {
- case XFROG_SCRUB_GROUP_AGHEADER:
- case XFROG_SCRUB_GROUP_PERAG:
- meta.sm_agno = sri->sri_agno;
- break;
- case XFROG_SCRUB_GROUP_INODE:
- meta.sm_ino = sri->sri_ino;
- meta.sm_gen = sri->sri_gen;
- break;
- default:
- break;
- }
-
- vec.sv_type = scrub_type;
- vec.sv_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY;
- memcpy(&oldm, &vec, sizeof(struct xfs_scrub_vec));
- if (!is_corrupt(&vec) && repair_only)
- return 0;
-
- descr_set(&dsc, &meta);
-
- if (needs_repair(&vec))
- str_info(ctx, descr_render(&dsc), _("Attempting repair."));
- else if (debug || verbose)
- str_info(ctx, descr_render(&dsc),
- _("Attempting optimization."));
-
- vec.sv_ret = xfrog_scrub_metadata(xfdp, &meta);
- vec.sv_flags = meta.sm_flags;
-
- return repair_epilogue(ctx, &dsc, sri, repair_flags, &oldm, &vec);
+ oldvec->sv_type = scrub_type;
+ oldvec->sv_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY;
}
static int
@@ -149,12 +94,15 @@ repair_epilogue(
struct descr *dsc,
struct scrub_item *sri,
unsigned int repair_flags,
- const struct xfs_scrub_vec *oldm,
const struct xfs_scrub_vec *meta)
{
+ struct xfs_scrub_vec oldv;
+ struct xfs_scrub_vec *oldm = &oldv;
unsigned int scrub_type = meta->sv_type;
int error = -meta->sv_ret;
+ restore_oldvec(oldm, sri, meta->sv_type);
+
switch (error) {
case 0:
/* No operational errors encountered. */
@@ -296,6 +244,133 @@ _("Repair unsuccessful; offline repair required."));
return 0;
}
+/* Decide if the dependent scrub types of the given scrub type are ok. */
+static bool
+repair_item_dependencies_ok(
+ const struct scrub_item *sri,
+ unsigned int scrub_type)
+{
+ unsigned int dep_mask = repair_deps[scrub_type];
+ unsigned int b;
+
+ for (b = 0; dep_mask && b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) {
+ if (!(dep_mask & 1))
+ continue;
+ /*
+ * If this lower level object also needs repair, we can't fix
+ * the higher level item.
+ */
+ if (sri->sri_state[b] & SCRUB_ITEM_NEEDSREPAIR)
+ return false;
+ }
+
+ return true;
+}
+
+/* Decide if we want to repair a particular type of metadata. */
+static bool
+can_repair_now(
+ const struct scrub_item *sri,
+ unsigned int scrub_type,
+ __u32 repair_mask,
+ unsigned int repair_flags)
+{
+ struct xfs_scrub_vec oldvec;
+ bool repair_only;
+
+ /* Do we even need to repair this thing? */
+ if (!(sri->sri_state[scrub_type] & repair_mask))
+ return false;
+
+ restore_oldvec(&oldvec, sri, scrub_type);
+
+ /*
+ * If the caller boosted the priority of this scrub type on behalf of a
+ * higher level repair by setting IFLAG_REPAIR, ignore REPAIR_ONLY.
+ */
+ repair_only = (repair_flags & XRM_REPAIR_ONLY) &&
+ !(sri->sri_state[scrub_type] & SCRUB_ITEM_BOOST_REPAIR);
+ if (!is_corrupt(&oldvec) && repair_only)
+ return false;
+
+ /*
+ * Don't try to repair higher level items if their lower-level
+ * dependencies haven't been verified, unless this is our last chance
+ * to fix things without complaint.
+ */
+ if (!(repair_flags & XRM_FINAL_WARNING) &&
+ !repair_item_dependencies_ok(sri, scrub_type))
+ return false;
+
+ return true;
+}
+
+/*
+ * Repair some metadata.
+ *
+ * Returns 0 for success (or repair item deferral), or ECANCELED to abort the
+ * program.
+ */
+static int
+repair_call_kernel(
+ struct scrub_ctx *ctx,
+ struct xfs_fd *xfdp,
+ struct scrub_item *sri,
+ __u32 repair_mask,
+ unsigned int repair_flags)
+{
+ DEFINE_DESCR(dsc, ctx, format_scrubv_descr);
+ struct xfrog_scrubv scrubv = { };
+ struct scrubv_descr vdesc = SCRUBV_DESCR(&scrubv);
+ struct xfs_scrub_vec *v;
+ unsigned int scrub_type;
+ int error;
+
+ assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
+
+ xfrog_scrubv_from_item(&scrubv, sri);
+ descr_set(&dsc, &vdesc);
+
+ foreach_scrub_type(scrub_type) {
+ if (scrub_excessive_errors(ctx))
+ return ECANCELED;
+
+ if (!can_repair_now(sri, scrub_type, repair_mask,
+ repair_flags))
+ continue;
+
+ xfrog_scrubv_add_item(&scrubv, sri, scrub_type, true);
+
+ if (sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSREPAIR)
+ str_info(ctx, descr_render(&dsc),
+ _("Attempting repair."));
+ else if (debug || verbose)
+ str_info(ctx, descr_render(&dsc),
+ _("Attempting optimization."));
+
+ dbg_printf("repair %s flags %xh tries %u\n", descr_render(&dsc),
+ sri->sri_state[scrub_type],
+ sri->sri_tries[scrub_type]);
+ }
+
+ error = -xfrog_scrubv_metadata(xfdp, &scrubv);
+ if (error)
+ return error;
+
+ foreach_xfrog_scrubv_vec(&scrubv, vdesc.idx, v) {
+ error = repair_epilogue(ctx, &dsc, sri, repair_flags, v);
+ if (error)
+ return error;
+
+ /* Maybe update progress if we fixed the problem. */
+ if (!(repair_flags & XRM_NOPROGRESS) &&
+ !(sri->sri_state[v->sv_type] & SCRUB_ITEM_REPAIR_ANY))
+ progress_add(1);
+ }
+
+ return 0;
+}
+
/*
* Prioritize action items in order of how long we can wait.
*
@@ -632,29 +707,6 @@ action_list_process(
return ret;
}
-/* Decide if the dependent scrub types of the given scrub type are ok. */
-static bool
-repair_item_dependencies_ok(
- const struct scrub_item *sri,
- unsigned int scrub_type)
-{
- unsigned int dep_mask = repair_deps[scrub_type];
- unsigned int b;
-
- for (b = 0; dep_mask && b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) {
- if (!(dep_mask & 1))
- continue;
- /*
- * If this lower level object also needs repair, we can't fix
- * the higher level item.
- */
- if (sri->sri_state[b] & SCRUB_ITEM_NEEDSREPAIR)
- return false;
- }
-
- return true;
-}
-
/*
* For a given filesystem object, perform all repairs of a given class
* (corrupt, xcorrupt, xfail, preen) if the repair item says it's needed.
@@ -670,13 +722,14 @@ repair_item_class(
struct xfs_fd xfd;
struct scrub_item old_sri;
struct xfs_fd *xfdp = &ctx->mnt;
- unsigned int scrub_type;
int error = 0;
if (ctx->mode == SCRUB_MODE_DRY_RUN)
return 0;
if (ctx->mode == SCRUB_MODE_PREEN && !(repair_mask & SCRUB_ITEM_PREEN))
return 0;
+ if (!scrub_item_schedule_work(sri, repair_mask))
+ return 0;
/*
* If the caller passed us a file descriptor for a scrub, use it
@@ -689,39 +742,14 @@ repair_item_class(
xfdp = &xfd;
}
- foreach_scrub_type(scrub_type) {
- if (scrub_excessive_errors(ctx))
- return ECANCELED;
-
- if (!(sri->sri_state[scrub_type] & repair_mask))
- continue;
-
- /*
- * Don't try to repair higher level items if their lower-level
- * dependencies haven't been verified, unless this is our last
- * chance to fix things without complaint.
- */
- if (!(flags & XRM_FINAL_WARNING) &&
- !repair_item_dependencies_ok(sri, scrub_type))
- continue;
-
- sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES;
- do {
- memcpy(&old_sri, sri, sizeof(old_sri));
- error = xfs_repair_metadata(ctx, xfdp, scrub_type, sri,
- flags);
- if (error)
- return error;
- } while (scrub_item_call_kernel_again(sri, scrub_type,
- repair_mask, &old_sri));
-
- /* Maybe update progress if we fixed the problem. */
- if (!(flags & XRM_NOPROGRESS) &&
- !(sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY))
- progress_add(1);
- }
-
- return error;
+ do {
+ memcpy(&old_sri, sri, sizeof(struct scrub_item));
+ error = repair_call_kernel(ctx, xfdp, sri, repair_mask, flags);
+ if (error)
+ return error;
+ } while (scrub_item_call_kernel_again(sri, repair_mask, &old_sri));
+
+ return 0;
}
/*
@@ -64,35 +64,6 @@ format_scrubv_descr(
return -1;
}
-/* Format a scrub description. */
-int
-format_scrub_descr(
- struct scrub_ctx *ctx,
- char *buf,
- size_t buflen,
- void *where)
-{
- struct xfs_scrub_metadata *meta = where;
- const struct xfrog_scrub_descr *sc = &xfrog_scrubbers[meta->sm_type];
-
- switch (sc->group) {
- case XFROG_SCRUB_GROUP_AGHEADER:
- case XFROG_SCRUB_GROUP_PERAG:
- return snprintf(buf, buflen, _("AG %u %s"), meta->sm_agno,
- _(sc->descr));
- case XFROG_SCRUB_GROUP_INODE:
- return scrub_render_ino_descr(ctx, buf, buflen,
- meta->sm_ino, meta->sm_gen, "%s",
- _(sc->descr));
- case XFROG_SCRUB_GROUP_FS:
- case XFROG_SCRUB_GROUP_SUMMARY:
- case XFROG_SCRUB_GROUP_ISCAN:
- case XFROG_SCRUB_GROUP_NONE:
- return snprintf(buf, buflen, _("%s"), _(sc->descr));
- }
- return -1;
-}
-
/* Warn about strange circumstances after scrub. */
void
scrub_warn_incomplete_scrub(
@@ -271,12 +242,17 @@ void
xfrog_scrubv_add_item(
struct xfrog_scrubv *scrubv,
const struct scrub_item *sri,
- unsigned int scrub_type)
+ unsigned int scrub_type,
+ bool want_repair)
{
struct xfs_scrub_vec *v;
v = xfrog_scrubv_next_vector(scrubv);
v->sv_type = scrub_type;
+ if (want_repair)
+ v->sv_flags |= XFS_SCRUB_IFLAG_REPAIR;
+ if (want_repair && use_force_rebuild)
+ v->sv_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD;
}
/* Do a read-only check of some metadata. */
@@ -301,7 +277,7 @@ scrub_call_kernel(
foreach_scrub_type(scrub_type) {
if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK))
continue;
- xfrog_scrubv_add_item(&scrubv, sri, scrub_type);
+ xfrog_scrubv_add_item(&scrubv, sri, scrub_type, false);
dbg_printf("check %s flags %xh tries %u\n", descr_render(&dsc),
sri->sri_state[scrub_type],
@@ -365,8 +341,8 @@ scrub_item_schedule_group(
}
/* Decide if we call the kernel again to finish scrub/repair activity. */
-static inline bool
-scrub_item_call_kernel_again_future(
+bool
+scrub_item_call_kernel_again(
struct scrub_item *sri,
uint8_t work_mask,
const struct scrub_item *old)
@@ -382,6 +358,11 @@ scrub_item_call_kernel_again_future(
if (!nr)
return false;
+ /*
+ * We are willing to go again if the last call had any effect on the
+ * state of the scrub item that the caller cares about or if the kernel
+ * asked us to try again.
+ */
foreach_scrub_type(scrub_type) {
uint8_t statex = sri->sri_state[scrub_type] ^
old->sri_state[scrub_type];
@@ -395,34 +376,6 @@ scrub_item_call_kernel_again_future(
return false;
}
-/* Decide if we call the kernel again to finish scrub/repair activity. */
-bool
-scrub_item_call_kernel_again(
- struct scrub_item *sri,
- unsigned int scrub_type,
- uint8_t work_mask,
- const struct scrub_item *old)
-{
- uint8_t statex;
-
- /* If there's nothing to do, we're done. */
- if (!(sri->sri_state[scrub_type] & work_mask))
- return false;
-
- /*
- * We are willing to go again if the last call had any effect on the
- * state of the scrub item that the caller cares about, if the freeze
- * flag got set, or if the kernel asked us to try again...
- */
- statex = sri->sri_state[scrub_type] ^ old->sri_state[scrub_type];
- if (statex & work_mask)
- return true;
- if (sri->sri_tries[scrub_type] != old->sri_tries[scrub_type])
- return true;
-
- return false;
-}
-
/*
* For each scrub item whose state matches the state_flags, set up the item
* state for a kernel call. Returns true if any work was scheduled.
@@ -477,7 +430,7 @@ scrub_item_check_file(
error = scrub_call_kernel(ctx, xfdp, sri);
if (error)
return error;
- } while (scrub_item_call_kernel_again_future(sri, SCRUB_ITEM_NEEDSCHECK,
+ } while (scrub_item_call_kernel_again(sri, SCRUB_ITEM_NEEDSCHECK,
&old_sri));
return 0;
@@ -11,10 +11,8 @@
void xfrog_scrubv_from_item(struct xfrog_scrubv *scrubv,
const struct scrub_item *sri);
void xfrog_scrubv_add_item(struct xfrog_scrubv *scrubv,
- const struct scrub_item *sri, unsigned int scrub_type);
-
-int format_scrub_descr(struct scrub_ctx *ctx, char *buf, size_t buflen,
- void *where);
+ const struct scrub_item *sri, unsigned int scrub_type,
+ bool want_repair);
struct scrubv_descr {
struct xfrog_scrubv *scrubv;
@@ -116,8 +114,7 @@ scrub_item_schedule_retry(struct scrub_item *sri, unsigned int scrub_type)
return true;
}
-bool scrub_item_call_kernel_again(struct scrub_item *sri,
- unsigned int scrub_type, uint8_t work_mask,
+bool scrub_item_call_kernel_again(struct scrub_item *sri, uint8_t work_mask,
const struct scrub_item *old);
bool scrub_item_schedule_work(struct scrub_item *sri, uint8_t state_flags);