--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ */
+#include "xfs.h"
+#include <assert.h>
+#include <sys/statvfs.h>
+#include "platform_defs.h"
+#include "input.h"
+#include "libfrog/paths.h"
+#include "libfrog/ptvar.h"
+#include "xfs_scrub.h"
+#include "common.h"
+#include "descr.h"
+
+/*
+ * Deferred String Description Renderer
+ * ====================================
+ * There are many places in xfs_scrub where some event occurred and we'd like
+ * to be able to print some sort of message describing what happened, and
+ * where. However, we don't know whether we're going to need the description
+ * of where ahead of time and there's little point in spending any time looking
+ * up gettext strings and formatting buffers until we actually need to.
+ *
+ * This code provides enough of a function closure that we are able to record
+ * some information about the program status but defer rendering the textual
+ * description until we know that we need it. Once we've rendered the string
+ * we can skip it for subsequent calls. We use per-thread storage for the
+ * message buffer to amortize the memory allocation across calls.
+ *
+ * On a clean filesystem this can reduce the xfs_scrub runtime by 7-10% by
+ * avoiding unnecessary work.
+ */
+
+static struct ptvar *descr_ptvar;
+
+/* Global buffer for when we aren't running in threaded mode. */
+static char global_dsc_buf[DESCR_BUFSZ];
+
+/*
+ * Render a textual description string using the function and location stored
+ * in the description context.
+ */
+const char *
+__descr_render(
+ struct descr *dsc,
+ const char *file,
+ int line)
+{
+ char *dsc_buf;
+ int ret;
+
+ if (descr_ptvar) {
+ dsc_buf = ptvar_get(descr_ptvar, &ret);
+ if (ret)
+ return _("error finding description buffer");
+ } else
+ dsc_buf = global_dsc_buf;
+
+ ret = dsc->fn(dsc->ctx, dsc_buf, DESCR_BUFSZ, dsc->where);
+ if (ret < 0) {
+ snprintf(dsc_buf, DESCR_BUFSZ,
+_("error %d while rendering description at %s line %d\n"),
+ ret, file, line);
+ }
+
+ return dsc_buf;
+}
+
+/*
+ * Set a new location context for this deferred-rendering string.
+ * The caller is responsible for freeing the old context, if any.
+ */
+void
+descr_set(
+ struct descr *dsc,
+ void *where)
+{
+ dsc->where = where;
+}
+
+/* Allocate all the description string buffers. */
+int
+descr_init_phase(
+ struct scrub_ctx *ctx,
+ unsigned int nr_threads)
+{
+ int ret;
+
+ assert(descr_ptvar == NULL);
+ ret = ptvar_alloc(nr_threads, DESCR_BUFSZ, &descr_ptvar);
+ if (ret)
+ str_liberror(ctx, ret, _("creating description buffer"));
+
+ return ret;
+}
+
+/* Free all the description string buffers. */
+void
+descr_end_phase(void)
+{
+ if (descr_ptvar)
+ ptvar_free(descr_ptvar);
+ descr_ptvar = NULL;
+}
#include "scrub.h"
#include "xfs_errortag.h"
#include "repair.h"
+#include "descr.h"
/* Online scrub and repair wrappers. */
/* Format a scrub description. */
-static void
+static int
format_scrub_descr(
struct scrub_ctx *ctx,
char *buf,
size_t buflen,
- struct xfs_scrub_metadata *meta)
+ void *where)
{
+ struct xfs_scrub_metadata *meta = where;
const struct xfrog_scrub_descr *sc = &xfrog_scrubbers[meta->sm_type];
switch (sc->type) {
case XFROG_SCRUB_TYPE_AGHEADER:
case XFROG_SCRUB_TYPE_PERAG:
- snprintf(buf, buflen, _("AG %u %s"), meta->sm_agno,
+ return snprintf(buf, buflen, _("AG %u %s"), meta->sm_agno,
_(sc->descr));
break;
case XFROG_SCRUB_TYPE_INODE:
- scrub_render_ino_descr(ctx, buf, buflen,
+ return scrub_render_ino_descr(ctx, buf, buflen,
meta->sm_ino, meta->sm_gen, "%s",
_(sc->descr));
break;
case XFROG_SCRUB_TYPE_FS:
- snprintf(buf, buflen, _("%s"), _(sc->descr));
+ return snprintf(buf, buflen, _("%s"), _(sc->descr));
break;
case XFROG_SCRUB_TYPE_NONE:
assert(0);
break;
}
+ return -1;
}
/* Predicates for scrub flag state. */
static inline void
xfs_scrub_warn_incomplete_scrub(
struct scrub_ctx *ctx,
- const char *descr,
+ struct descr *dsc,
struct xfs_scrub_metadata *meta)
{
if (is_incomplete(meta))
- str_info(ctx, descr, _("Check incomplete."));
+ str_info(ctx, descr_render(dsc), _("Check incomplete."));
if (is_suspicious(meta)) {
if (debug)
- str_info(ctx, descr, _("Possibly suspect metadata."));
+ str_info(ctx, descr_render(dsc),
+ _("Possibly suspect metadata."));
else
- str_warn(ctx, descr, _("Possibly suspect metadata."));
+ str_warn(ctx, descr_render(dsc),
+ _("Possibly suspect metadata."));
}
if (xref_failed(meta))
- str_info(ctx, descr, _("Cross-referencing failed."));
+ str_info(ctx, descr_render(dsc),
+ _("Cross-referencing failed."));
}
/* Do a read-only check of some metadata. */
struct xfs_scrub_metadata *meta,
bool is_inode)
{
- char buf[DESCR_BUFSZ];
+ DEFINE_DESCR(dsc, ctx, format_scrub_descr);
unsigned int tries = 0;
int code;
int error;
assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
assert(meta->sm_type < XFS_SCRUB_TYPE_NR);
- format_scrub_descr(ctx, buf, DESCR_BUFSZ, meta);
+ descr_set(&dsc, meta);
- dbg_printf("check %s flags %xh\n", buf, meta->sm_flags);
+ dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta->sm_flags);
retry:
error = xfrog_scrub_metadata(&ctx->mnt, meta);
if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR") && !error)
return CHECK_DONE;
case ESHUTDOWN:
/* FS already crashed, give up. */
- str_error(ctx, buf,
+ str_error(ctx, descr_render(&dsc),
_("Filesystem is shut down, aborting."));
return CHECK_ABORT;
case EIO:
case ENOMEM:
/* Abort on I/O errors or insufficient memory. */
- str_errno(ctx, buf);
+ str_errno(ctx, descr_render(&dsc));
return CHECK_ABORT;
case EDEADLOCK:
case EBUSY:
/* fall through */
default:
/* Operational error. */
- str_errno(ctx, buf);
+ str_errno(ctx, descr_render(&dsc));
return CHECK_DONE;
}
}
}
/* Complain about incomplete or suspicious metadata. */
- xfs_scrub_warn_incomplete_scrub(ctx, buf, meta);
+ xfs_scrub_warn_incomplete_scrub(ctx, &dsc, meta);
/*
* If we need repairs or there were discrepancies, schedule a
*/
if (is_corrupt(meta) || xref_disagrees(meta)) {
if (ctx->mode < SCRUB_MODE_REPAIR) {
- str_corrupt(ctx, buf,
+ str_corrupt(ctx, descr_render(&dsc),
_("Repairs are required."));
return CHECK_DONE;
}
if (ctx->mode != SCRUB_MODE_REPAIR) {
if (!is_inode) {
/* AG or FS metadata, always warn. */
- str_info(ctx, buf,
+ str_info(ctx, descr_render(&dsc),
_("Optimization is possible."));
} else if (!ctx->preen_triggers[meta->sm_type]) {
/* File metadata, only warn once per type. */
struct action_item *aitem,
unsigned int repair_flags)
{
- char buf[DESCR_BUFSZ];
struct xfs_scrub_metadata meta = { 0 };
struct xfs_scrub_metadata oldm;
+ DEFINE_DESCR(dsc, ctx, format_scrub_descr);
int error;
assert(aitem->type < XFS_SCRUB_TYPE_NR);
return CHECK_RETRY;
memcpy(&oldm, &meta, sizeof(oldm));
- format_scrub_descr(ctx, buf, DESCR_BUFSZ, &meta);
+ descr_set(&dsc, &oldm);
if (needs_repair(&meta))
- str_info(ctx, buf, _("Attempting repair."));
+ str_info(ctx, descr_render(&dsc), _("Attempting repair."));
else if (debug || verbose)
- str_info(ctx, buf, _("Attempting optimization."));
+ str_info(ctx, descr_render(&dsc),
+ _("Attempting optimization."));
error = xfrog_scrub_metadata(&ctx->mnt, &meta);
if (error) {
case EBUSY:
/* Filesystem is busy, try again later. */
if (debug || verbose)
- str_info(ctx, buf,
+ str_info(ctx, descr_render(&dsc),
_("Filesystem is busy, deferring repair."));
return CHECK_RETRY;
case ESHUTDOWN:
/* Filesystem is already shut down, abort. */
- str_error(ctx, buf,
+ str_error(ctx, descr_render(&dsc),
_("Filesystem is shut down, aborting."));
return CHECK_ABORT;
case ENOTTY:
/* fall through */
case EINVAL:
/* Kernel doesn't know how to repair this? */
- str_corrupt(ctx, buf,
+ str_corrupt(ctx, descr_render(&dsc),
_("Don't know how to fix; offline repair required."));
return CHECK_DONE;
case EROFS:
/* Read-only filesystem, can't fix. */
if (verbose || debug || needs_repair(&oldm))
- str_error(ctx, buf,
+ str_error(ctx, descr_render(&dsc),
_("Read-only filesystem; cannot make changes."));
return CHECK_ABORT;
case ENOENT:
*/
if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
return CHECK_RETRY;
- str_errno(ctx, buf);
+ str_errno(ctx, descr_render(&dsc));
return CHECK_DONE;
}
}
if (repair_flags & XRM_COMPLAIN_IF_UNFIXED)
- xfs_scrub_warn_incomplete_scrub(ctx, buf, &meta);
+ xfs_scrub_warn_incomplete_scrub(ctx, &dsc, &meta);
if (needs_repair(&meta)) {
/*
* Still broken; if we've been told not to complain then we
*/
if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
return CHECK_RETRY;
- str_corrupt(ctx, buf,
+ str_corrupt(ctx, descr_render(&dsc),
_("Repair unsuccessful; offline repair required."));
} else {
/* Clean operation, no corruption detected. */
if (needs_repair(&oldm))
- record_repair(ctx, buf, _("Repairs successful."));
+ record_repair(ctx, descr_render(&dsc),
+ _("Repairs successful."));
else
- record_preen(ctx, buf, _("Optimization successful."));
+ record_preen(ctx, descr_render(&dsc),
+ _("Optimization successful."));
}
return CHECK_DONE;
}