#include <pthread.h>
#include <stdbool.h>
#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
#include "platform_defs.h"
#include "xfs.h"
#include "input.h"
exit(SCRUB_RET_SYNTAX);
}
+#ifndef RUSAGE_BOTH
+# define RUSAGE_BOTH (-2)
+#endif
+
+/* Get resource usage for ourselves and all children. */
+static int
+scrub_getrusage(
+ struct rusage *usage)
+{
+ struct rusage cusage;
+ int err;
+
+ err = getrusage(RUSAGE_BOTH, usage);
+ if (!err)
+ return err;
+
+ err = getrusage(RUSAGE_SELF, usage);
+ if (err)
+ return err;
+
+ err = getrusage(RUSAGE_CHILDREN, &cusage);
+ if (err)
+ return err;
+
+ usage->ru_minflt += cusage.ru_minflt;
+ usage->ru_majflt += cusage.ru_majflt;
+ usage->ru_nswap += cusage.ru_nswap;
+ usage->ru_inblock += cusage.ru_inblock;
+ usage->ru_oublock += cusage.ru_oublock;
+ usage->ru_msgsnd += cusage.ru_msgsnd;
+ usage->ru_msgrcv += cusage.ru_msgrcv;
+ usage->ru_nsignals += cusage.ru_nsignals;
+ usage->ru_nvcsw += cusage.ru_nvcsw;
+ usage->ru_nivcsw += cusage.ru_nivcsw;
+ return 0;
+}
+
+/*
+ * Scrub Phase Dispatch
+ *
+ * The operations of the scrub program are split up into several
+ * different phases. Each phase builds upon the metadata checked in the
+ * previous phase, which is to say that we may skip phase (X + 1) if our
+ * scans in phase (X) reveal corruption. A phase may be skipped
+ * entirely.
+ */
+
+/* Resource usage for each phase. */
+struct phase_rusage {
+ struct rusage ruse;
+ struct timeval time;
+ unsigned long long verified_bytes;
+ void *brk_start;
+ const char *descr;
+};
+
+/* Operations for each phase. */
+#define DATASCAN_DUMMY_FN ((void *)1)
+#define REPAIR_DUMMY_FN ((void *)2)
+struct phase_ops {
+ char *descr;
+ bool (*fn)(struct scrub_ctx *);
+ bool must_run;
+};
+
+/* Start tracking resource usage for a phase. */
+static bool
+phase_start(
+ struct phase_rusage *pi,
+ unsigned int phase,
+ const char *descr)
+{
+ int error;
+
+ memset(pi, 0, sizeof(*pi));
+ error = scrub_getrusage(&pi->ruse);
+ if (error) {
+ perror(_("getrusage"));
+ return false;
+ }
+ pi->brk_start = sbrk(0);
+
+ error = gettimeofday(&pi->time, NULL);
+ if (error) {
+ perror(_("gettimeofday"));
+ return false;
+ }
+
+ pi->descr = descr;
+ if ((verbose || display_rusage) && descr) {
+ fprintf(stdout, _("Phase %u: %s\n"), phase, descr);
+ fflush(stdout);
+ }
+ return true;
+}
+
+/* Report usage stats. */
+static bool
+phase_end(
+ struct phase_rusage *pi,
+ unsigned int phase)
+{
+ struct rusage ruse_now;
+#ifdef HAVE_MALLINFO
+ struct mallinfo mall_now;
+#endif
+ struct timeval time_now;
+ char phasebuf[DESCR_BUFSZ];
+ double dt;
+ unsigned long long in, out;
+ unsigned long long io;
+ double i, o, t;
+ double din, dout, dtot;
+ char *iu, *ou, *tu, *dinu, *doutu, *dtotu;
+ int error;
+
+ if (!display_rusage)
+ return true;
+
+ error = gettimeofday(&time_now, NULL);
+ if (error) {
+ perror(_("gettimeofday"));
+ return false;
+ }
+ dt = timeval_subtract(&time_now, &pi->time);
+
+ error = scrub_getrusage(&ruse_now);
+ if (error) {
+ perror(_("getrusage"));
+ return false;
+ }
+
+ if (phase)
+ snprintf(phasebuf, DESCR_BUFSZ, _("Phase %u: "), phase);
+ else
+ phasebuf[0] = 0;
+
+#define kbytes(x) (((unsigned long)(x) + 1023) / 1024)
+#ifdef HAVE_MALLINFO
+
+ mall_now = mallinfo();
+ fprintf(stdout, _("%sMemory used: %luk/%luk (%luk/%luk), "),
+ phasebuf,
+ kbytes(mall_now.arena), kbytes(mall_now.hblkhd),
+ kbytes(mall_now.uordblks), kbytes(mall_now.fordblks));
+#else
+ fprintf(stdout, _("%sMemory used: %luk, "),
+ phasebuf,
+ (unsigned long) kbytes(((char *) sbrk(0)) -
+ ((char *) pi->brk_start)));
+#endif
+#undef kbytes
+
+ fprintf(stdout, _("time: %5.2f/%5.2f/%5.2fs\n"),
+ timeval_subtract(&time_now, &pi->time),
+ timeval_subtract(&ruse_now.ru_utime, &pi->ruse.ru_utime),
+ timeval_subtract(&ruse_now.ru_stime, &pi->ruse.ru_stime));
+
+ /* I/O usage */
+ in = ((unsigned long long)ruse_now.ru_inblock -
+ pi->ruse.ru_inblock) << BBSHIFT;
+ out = ((unsigned long long)ruse_now.ru_oublock -
+ pi->ruse.ru_oublock) << BBSHIFT;
+ io = in + out;
+ if (io) {
+ i = auto_space_units(in, &iu);
+ o = auto_space_units(out, &ou);
+ t = auto_space_units(io, &tu);
+ din = auto_space_units(in / dt, &dinu);
+ dout = auto_space_units(out / dt, &doutu);
+ dtot = auto_space_units(io / dt, &dtotu);
+ fprintf(stdout,
+_("%sI/O: %.1f%s in, %.1f%s out, %.1f%s tot\n"),
+ phasebuf, i, iu, o, ou, t, tu);
+ fprintf(stdout,
+_("%sI/O rate: %.1f%s/s in, %.1f%s/s out, %.1f%s/s tot\n"),
+ phasebuf, din, dinu, dout, doutu, dtot, dtotu);
+ }
+ fflush(stdout);
+
+ return true;
+}
+
+/* Run all the phases of the scrubber. */
+static bool
+run_scrub_phases(
+ struct scrub_ctx *ctx)
+{
+ struct phase_ops phases[] =
+ {
+ {
+ .descr = _("Find filesystem geometry."),
+ },
+ {
+ .descr = _("Check internal metadata."),
+ },
+ {
+ .descr = _("Scan all inodes."),
+ },
+ {
+ .descr = _("Defer filesystem repairs."),
+ .fn = REPAIR_DUMMY_FN,
+ },
+ {
+ .descr = _("Check directory tree."),
+ },
+ {
+ .descr = _("Verify data file integrity."),
+ .fn = DATASCAN_DUMMY_FN,
+ },
+ {
+ .descr = _("Check summary counters."),
+ },
+ {
+ NULL
+ },
+ };
+ struct phase_rusage pi;
+ struct phase_ops *sp;
+ bool moveon = true;
+ unsigned int debug_phase = 0;
+ unsigned int phase;
+
+ if (debug && debug_tweak_on("XFS_SCRUB_PHASE"))
+ debug_phase = atoi(getenv("XFS_SCRUB_PHASE"));
+
+ /* Run all phases of the scrub tool. */
+ for (phase = 1, sp = phases; sp->fn; sp++, phase++) {
+ /* Skip certain phases unless they're turned on. */
+ if (sp->fn == REPAIR_DUMMY_FN ||
+ sp->fn == DATASCAN_DUMMY_FN)
+ continue;
+
+ /* Allow debug users to force a particular phase. */
+ if (debug_phase && phase != debug_phase && !sp->must_run)
+ continue;
+
+ /* Run this phase. */
+ moveon = phase_start(&pi, phase, sp->descr);
+ if (!moveon)
+ break;
+ moveon = sp->fn(ctx);
+ if (!moveon) {
+ str_info(ctx, ctx->mntpoint,
+_("Scrub aborted after phase %d."),
+ phase);
+ break;
+ }
+ moveon = phase_end(&pi, phase);
+ if (!moveon)
+ break;
+
+ /* Too many errors? */
+ moveon = !xfs_scrub_excessive_errors(ctx);
+ if (!moveon)
+ break;
+ }
+
+ return moveon;
+}
+
int
main(
int argc,
char **argv)
{
struct scrub_ctx ctx = {0};
+ struct phase_rusage all_pi;
char *mtab = NULL;
char *repairstr = "";
unsigned long long total_errors;
mtab = _PATH_MOUNTED;
}
+ /* Initialize overall phase stats. */
+ moveon = phase_start(&all_pi, 0, NULL);
+ if (!moveon)
+ goto out;
+
/* How many CPUs? */
nproc = sysconf(_SC_NPROCESSORS_ONLN);
if (nproc < 1)
if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
ctx.mode = SCRUB_MODE_REPAIR;
+ /* Scrub a filesystem. */
+ moveon = run_scrub_phases(&ctx);
+ if (!moveon && ctx.runtime_errors == 0)
+ ctx.runtime_errors++;
+
+ /*
+ * Excessive errors will cause the scrub phases to bail out early.
+ * We don't want every thread yelling that into the output, so check
+ * if we hit the threshold and tell the user *once*.
+ */
if (xfs_scrub_excessive_errors(&ctx))
str_info(&ctx, ctx.mntpoint, _("Too many errors; aborting."));
ret |= SCRUB_RET_UNOPTIMIZED;
if (ctx.runtime_errors)
ret |= SCRUB_RET_OPERROR;
+ phase_end(&all_pi, 0);
free(ctx.mntpoint);
return ret;