@@ -114,9 +114,27 @@ Instructing the underlying storage to discard unused extents via the
.B FITRIM
ioctl.
.SH REPAIRS
-This program currently does not support making any repairs.
-Corruptions can only be fixed by unmounting the filesystem and running
-.BR xfs_repair (8).
+Repairs are performed by calling into the kernel.
+This limits the scope of repair activities to rebuilding primary data
+structures from secondary data structures, or secondary structures from
+primary structures.
+The existence of secondary data structures may require features that can
+only be turned on from
+.BR mkfs.xfs (8).
+If errors cannot be repaired, the filesystem must be
+unmounted and
+.BR xfs_repair (8)
+run.
+Repairs supported by the kernel include, but are not limited to:
+.IP \[bu] 2
+Reconstructing extent allocation data from the reverse mapping data.
+.IP \[bu]
+Reconstructing reverse mapping data from primary extent allocation data.
+.IP \[bu]
+Scheduling a quotacheck for the next mount.
+.PP
+If corrupt metadata is successfully repaired, this program will log that
+a repair has succeeded instead of a corruption report.
.SH EXIT CODE
The exit code returned by
.B xfs_scrub
@@ -140,8 +158,5 @@ This program takes advantage of in-kernel scrubbing to verify a given
data structure with locks held and can keep the filesystem busy for a
long time.
The kernel must be new enough to support the SCRUB_METADATA ioctl.
-.PP
-If errors are found and cannot be repaired, the filesystem must be
-unmounted and repaired.
.SH SEE ALSO
.BR xfs_repair (8).
@@ -37,6 +37,7 @@ fscounters.h \
inodes.h \
progress.h \
read_verify.h \
+repair.h \
scrub.h \
spacemap.h \
unicrash.h \
@@ -60,6 +61,7 @@ phase6.c \
phase7.c \
progress.c \
read_verify.c \
+repair.c \
scrub.c \
spacemap.c \
vfs.c \
@@ -47,6 +47,7 @@
#include "common.h"
#include "disk.h"
#include "scrub.h"
+#include "repair.h"
/* Phase 1: Find filesystem geometry (and clean up after) */
@@ -68,6 +69,7 @@ bool
xfs_cleanup_fs(
struct scrub_ctx *ctx)
{
+ xfs_repair_lists_free(&ctx->repair_lists);
if (ctx->fshandle)
free_handle(ctx->fshandle, ctx->fshandle_len);
if (ctx->rtdev)
@@ -157,6 +159,11 @@ _("Does not appear to be an XFS filesystem!"));
return false;
}
+ if (!xfs_repair_lists_alloc(ctx->geo.agcount, &ctx->repair_lists)) {
+ str_error(ctx, ctx->mntpoint, _("Not enough memory."));
+ return false;
+ }
+
ctx->agblklog = log2_roundup(ctx->geo.agblocks);
ctx->blocklog = highbit32(ctx->geo.blocksize);
ctx->inodelog = highbit32(ctx->geo.inodesize);
@@ -30,6 +30,7 @@
#include "xfs_scrub.h"
#include "common.h"
#include "scrub.h"
+#include "repair.h"
/* Phase 2: Check internal metadata. */
@@ -42,24 +43,65 @@ xfs_scan_ag_metadata(
{
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
bool *pmoveon = arg;
+ struct xfs_repair_list repairs;
+ struct xfs_repair_list repair_now;
+ unsigned long long broken_primaries;
+ unsigned long long broken_secondaries;
bool moveon;
char descr[DESCR_BUFSZ];
+ xfs_repair_list_init(&repairs);
+ xfs_repair_list_init(&repair_now);
snprintf(descr, DESCR_BUFSZ, _("AG %u"), agno);
/*
* First we scrub and fix the AG headers, because we need
* them to work well enough to check the AG btrees.
*/
- moveon = xfs_scrub_ag_headers(ctx, agno);
+ moveon = xfs_scrub_ag_headers(ctx, agno, &repairs);
+ if (!moveon)
+ goto err;
+
+ /* Repair header damage. */
+ moveon = xfs_quick_repair(ctx, agno, &repairs);
if (!moveon)
goto err;
/* Now scrub the AG btrees. */
- moveon = xfs_scrub_ag_metadata(ctx, agno);
+ moveon = xfs_scrub_ag_metadata(ctx, agno, &repairs);
+ if (!moveon)
+ goto err;
+
+ /*
+ * Figure out if we need to perform early fixing. The only
+ * reason we need to do this is if the inobt is broken, which
+ * prevents phase 3 (inode scan) from running. We can rebuild
+ * the inobt from rmapbt data, but if the rmapbt is broken even
+ * at this early phase then we are sunk.
+ */
+ broken_secondaries = 0;
+ broken_primaries = 0;
+ xfs_repair_find_mustfix(&repairs, &repair_now,
+ &broken_primaries, &broken_secondaries);
+ if (broken_secondaries && !debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) {
+ if (broken_primaries)
+ str_info(ctx, descr,
+_("Corrupt primary and secondary block mapping metadata."));
+ else
+ str_info(ctx, descr,
+_("Corrupt secondary block mapping metadata."));
+ str_info(ctx, descr,
+_("Filesystem might not be repairable."));
+ }
+
+ /* Repair (inode) btree damage. */
+ moveon = xfs_quick_repair(ctx, agno, &repair_now);
if (!moveon)
goto err;
+ /* Everything else gets fixed during phase 4. */
+ xfs_defer_repairs(ctx, agno, &repairs);
+
return;
err:
*pmoveon = false;
@@ -74,11 +116,15 @@ xfs_scan_fs_metadata(
{
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
bool *pmoveon = arg;
+ struct xfs_repair_list repairs;
bool moveon;
- moveon = xfs_scrub_fs_metadata(ctx);
+ xfs_repair_list_init(&repairs);
+ moveon = xfs_scrub_fs_metadata(ctx, &repairs);
if (!moveon)
*pmoveon = false;
+
+ xfs_defer_repairs(ctx, agno, &repairs);
}
/* Scan all filesystem metadata. */
@@ -86,6 +132,7 @@ bool
xfs_scan_metadata(
struct scrub_ctx *ctx)
{
+ struct xfs_repair_list repairs;
struct workqueue wq;
xfs_agnumber_t agno;
bool moveon = true;
@@ -103,7 +150,11 @@ xfs_scan_metadata(
* upgrades (followed by a full scrub), do that before we launch
* anything else.
*/
- moveon = xfs_scrub_primary_super(ctx);
+ xfs_repair_list_init(&repairs);
+ moveon = xfs_scrub_primary_super(ctx, &repairs);
+ if (!moveon)
+ return moveon;
+ moveon = xfs_quick_repair(ctx, 0, &repairs);
if (!moveon)
return moveon;
@@ -33,6 +33,7 @@
#include "inodes.h"
#include "progress.h"
#include "scrub.h"
+#include "repair.h"
/* Phase 3: Scan all inodes. */
@@ -45,10 +46,11 @@ static bool
xfs_scrub_fd(
struct scrub_ctx *ctx,
bool (*fn)(struct scrub_ctx *, uint64_t,
- uint32_t, int),
- struct xfs_bstat *bs)
+ uint32_t, int, struct xfs_repair_list *),
+ struct xfs_bstat *bs,
+ struct xfs_repair_list *rl)
{
- return fn(ctx, bs->bs_ino, bs->bs_gen, ctx->mnt_fd);
+ return fn(ctx, bs->bs_ino, bs->bs_gen, ctx->mnt_fd, rl);
}
struct scrub_inode_ctx {
@@ -64,11 +66,15 @@ xfs_scrub_inode(
struct xfs_bstat *bstat,
void *arg)
{
+ struct xfs_repair_list repairs;
struct scrub_inode_ctx *ictx = arg;
struct ptcounter *icount = ictx->icount;
+ xfs_agnumber_t agno;
bool moveon = true;
int fd = -1;
+ xfs_repair_list_init(&repairs);
+ agno = bstat->bs_ino / (1ULL << (ctx->inopblog + ctx->agblklog));
background_sleep();
/* Try to open the inode to pin it. */
@@ -80,45 +86,59 @@ xfs_scrub_inode(
}
/* Scrub the inode. */
- moveon = xfs_scrub_fd(ctx, xfs_scrub_inode_fields, bstat);
+ moveon = xfs_scrub_fd(ctx, xfs_scrub_inode_fields, bstat, &repairs);
+ if (!moveon)
+ goto out;
+
+ moveon = xfs_quick_repair(ctx, agno, &repairs);
if (!moveon)
goto out;
/* Scrub all block mappings. */
- moveon = xfs_scrub_fd(ctx, xfs_scrub_data_fork, bstat);
+ moveon = xfs_scrub_fd(ctx, xfs_scrub_data_fork, bstat, &repairs);
if (!moveon)
goto out;
- moveon = xfs_scrub_fd(ctx, xfs_scrub_attr_fork, bstat);
+ moveon = xfs_scrub_fd(ctx, xfs_scrub_attr_fork, bstat, &repairs);
if (!moveon)
goto out;
- moveon = xfs_scrub_fd(ctx, xfs_scrub_cow_fork, bstat);
+ moveon = xfs_scrub_fd(ctx, xfs_scrub_cow_fork, bstat, &repairs);
+ if (!moveon)
+ goto out;
+
+ moveon = xfs_quick_repair(ctx, agno, &repairs);
if (!moveon)
goto out;
if (S_ISLNK(bstat->bs_mode)) {
/* Check symlink contents. */
moveon = xfs_scrub_symlink(ctx, bstat->bs_ino,
- bstat->bs_gen, ctx->mnt_fd);
+ bstat->bs_gen, ctx->mnt_fd, &repairs);
} else if (S_ISDIR(bstat->bs_mode)) {
/* Check the directory entries. */
- moveon = xfs_scrub_fd(ctx, xfs_scrub_dir, bstat);
+ moveon = xfs_scrub_fd(ctx, xfs_scrub_dir, bstat, &repairs);
}
if (!moveon)
goto out;
/* Check all the extended attributes. */
- moveon = xfs_scrub_fd(ctx, xfs_scrub_attr, bstat);
+ moveon = xfs_scrub_fd(ctx, xfs_scrub_attr, bstat, &repairs);
if (!moveon)
goto out;
/* Check parent pointers. */
- moveon = xfs_scrub_fd(ctx, xfs_scrub_parent, bstat);
+ moveon = xfs_scrub_fd(ctx, xfs_scrub_parent, bstat, &repairs);
+ if (!moveon)
+ goto out;
+
+ /* Try to repair the file while it's open. */
+ moveon = xfs_quick_repair(ctx, agno, &repairs);
if (!moveon)
goto out;
out:
ptcounter_add(icount, 1);
progress_add(1);
+ xfs_defer_repairs(ctx, agno, &repairs);
if (fd >= 0)
close(fd);
if (!moveon)
@@ -33,16 +33,82 @@
#include "common.h"
#include "progress.h"
#include "scrub.h"
+#include "repair.h"
#include "vfs.h"
/* Phase 4: Repair filesystem. */
+/* Fix all the problems in our per-AG list. */
+static void
+xfs_repair_ag(
+ struct workqueue *wq,
+ xfs_agnumber_t agno,
+ void *priv)
+{
+ struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
+ bool *pmoveon = priv;
+ struct xfs_repair_list *repairs;
+ size_t unfixed;
+ size_t new_unfixed;
+ unsigned int flags = 0;
+ bool moveon;
+
+ repairs = &ctx->repair_lists[agno];
+ unfixed = xfs_repair_list_length(repairs);
+
+ /* Repair anything broken until we fail to make progress. */
+ do {
+ moveon = xfs_repair_list_now(ctx, ctx->mnt_fd, repairs, flags);
+ if (!moveon) {
+ *pmoveon = false;
+ return;
+ }
+ new_unfixed = xfs_repair_list_length(repairs);
+ if (new_unfixed == unfixed)
+ break;
+ unfixed = new_unfixed;
+ } while (unfixed > 0 && *pmoveon);
+
+ if (!*pmoveon)
+ return;
+
+ /* Try once more, but this time complain if we can't fix things. */
+ flags |= XRML_NOFIX_COMPLAIN;
+ moveon = xfs_repair_list_now(ctx, ctx->mnt_fd, repairs, flags);
+ if (!moveon)
+ *pmoveon = false;
+}
+
/* Fix everything that needs fixing. */
bool
xfs_repair_fs(
struct scrub_ctx *ctx)
{
+ struct workqueue wq;
+ xfs_agnumber_t agno;
bool moveon = true;
+ int ret;
+
+ ret = workqueue_create(&wq, (struct xfs_mount *)ctx,
+ scrub_nproc_workqueue(ctx));
+ if (ret) {
+ str_error(ctx, ctx->mntpoint, _("Could not create workqueue."));
+ return false;
+ }
+ for (agno = 0; agno < ctx->geo.agcount; agno++) {
+ if (xfs_repair_list_length(&ctx->repair_lists[agno]) > 0) {
+ ret = workqueue_add(&wq, xfs_repair_ag, agno, &moveon);
+ if (ret) {
+ moveon = false;
+ str_error(ctx, ctx->mntpoint,
+_("Could not queue repair work."));
+ break;
+ }
+ }
+ if (!moveon)
+ break;
+ }
+ workqueue_destroy(&wq);
pthread_mutex_lock(&ctx->lock);
if (moveon && ctx->errors_found == 0 && want_fstrim) {
@@ -62,8 +128,14 @@ xfs_estimate_repair_work(
unsigned int *nr_threads,
int *rshift)
{
- *items = 1;
- *nr_threads = 1;
+ xfs_agnumber_t agno;
+ size_t need_fixing = 0;
+
+ for (agno = 0; agno < ctx->geo.agcount; agno++)
+ need_fixing += xfs_repair_list_length(&ctx->repair_lists[agno]);
+ need_fixing++;
+ *items = need_fixing;
+ *nr_threads = scrub_nproc(ctx) + 1;
*rshift = 0;
return true;
}
new file mode 100644
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2018 Oracle. All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "list.h"
+#include "path.h"
+#include "xfs_scrub.h"
+#include "common.h"
+#include "scrub.h"
+#include "repair.h"
+
+/*
+ * Prioritize repair items in order of how long we can wait.
+ * 0 = do it now, 10000 = do it later.
+ *
+ * To minimize the amount of repair work, we want to prioritize metadata
+ * objects by perceived corruptness. If CORRUPT is set, the fields are
+ * just plain bad; try fixing that first. Otherwise if XCORRUPT is set,
+ * the fields could be bad, but the xref data could also be bad; we'll
+ * try fixing that next. Finally, if XFAIL is set, some other metadata
+ * structure failed validation during xref, so we'll recheck this
+ * metadata last since it was probably fine.
+ *
+ * For metadata that lie in the critical path of checking other metadata
+ * (superblock, AG{F,I,FL}, inobt) we scrub and fix those things before
+ * we even get to handling their dependencies, so things should progress
+ * in order.
+ */
+
+/* Sort repair items in severity order. */
+static int
+PRIO(
+ struct repair_item *ri,
+ int order)
+{
+ if (ri->flags & XFS_SCRUB_OFLAG_CORRUPT)
+ return order;
+ else if (ri->flags & XFS_SCRUB_OFLAG_XCORRUPT)
+ return 100 + order;
+ else if (ri->flags & XFS_SCRUB_OFLAG_XFAIL)
+ return 200 + order;
+ else if (ri->flags & XFS_SCRUB_OFLAG_PREEN)
+ return 300 + order;
+ abort();
+}
+
+/* Sort the repair items in dependency order. */
+static int
+xfs_repair_item_priority(
+ struct repair_item *ri)
+{
+ switch (ri->type) {
+ case XFS_SCRUB_TYPE_SB:
+ case XFS_SCRUB_TYPE_AGF:
+ case XFS_SCRUB_TYPE_AGFL:
+ case XFS_SCRUB_TYPE_AGI:
+ case XFS_SCRUB_TYPE_BNOBT:
+ case XFS_SCRUB_TYPE_CNTBT:
+ case XFS_SCRUB_TYPE_INOBT:
+ case XFS_SCRUB_TYPE_FINOBT:
+ case XFS_SCRUB_TYPE_REFCNTBT:
+ case XFS_SCRUB_TYPE_RMAPBT:
+ case XFS_SCRUB_TYPE_INODE:
+ case XFS_SCRUB_TYPE_BMBTD:
+ case XFS_SCRUB_TYPE_BMBTA:
+ case XFS_SCRUB_TYPE_BMBTC:
+ return PRIO(ri, ri->type - 1);
+ case XFS_SCRUB_TYPE_DIR:
+ case XFS_SCRUB_TYPE_XATTR:
+ case XFS_SCRUB_TYPE_SYMLINK:
+ case XFS_SCRUB_TYPE_PARENT:
+ return PRIO(ri, XFS_SCRUB_TYPE_DIR);
+ case XFS_SCRUB_TYPE_RTBITMAP:
+ case XFS_SCRUB_TYPE_RTSUM:
+ return PRIO(ri, XFS_SCRUB_TYPE_RTBITMAP);
+ case XFS_SCRUB_TYPE_UQUOTA:
+ case XFS_SCRUB_TYPE_GQUOTA:
+ case XFS_SCRUB_TYPE_PQUOTA:
+ return PRIO(ri, XFS_SCRUB_TYPE_UQUOTA);
+ }
+ abort();
+}
+
+/* Make sure that btrees get repaired before headers. */
+static int
+xfs_repair_item_compare(
+ void *priv,
+ struct list_head *a,
+ struct list_head *b)
+{
+ struct repair_item *ra;
+ struct repair_item *rb;
+
+ ra = container_of(a, struct repair_item, list);
+ rb = container_of(b, struct repair_item, list);
+
+ return xfs_repair_item_priority(ra) - xfs_repair_item_priority(rb);
+}
+
+/*
+ * Figure out which AG metadata must be fixed before we can move on
+ * to the inode scan.
+ */
+void
+xfs_repair_find_mustfix(
+ struct xfs_repair_list *repairs,
+ struct xfs_repair_list *repair_now,
+ unsigned long long *broken_primaries,
+ unsigned long long *broken_secondaries)
+{
+ struct repair_item *n;
+ struct repair_item *ri;
+
+ list_for_each_entry_safe(ri, n, &repairs->list, list) {
+ switch (ri->type) {
+ case XFS_SCRUB_TYPE_RMAPBT:
+ (*broken_secondaries)++;
+ break;
+ case XFS_SCRUB_TYPE_FINOBT:
+ case XFS_SCRUB_TYPE_INOBT:
+ repairs->nr--;
+ list_del(&ri->list);
+ list_add_tail(&ri->list, &repair_now->list);
+ repair_now->nr++;
+ /* fall through */
+ case XFS_SCRUB_TYPE_BNOBT:
+ case XFS_SCRUB_TYPE_CNTBT:
+ case XFS_SCRUB_TYPE_REFCNTBT:
+ (*broken_primaries)++;
+ break;
+ default:
+ abort();
+ break;
+ }
+ }
+}
+
+/* Allocate a certain number of repair lists for the scrub context. */
+bool
+xfs_repair_lists_alloc(
+ size_t nr,
+ struct xfs_repair_list **listsp)
+{
+ struct xfs_repair_list *lists;
+ xfs_agnumber_t agno;
+
+ lists = calloc(nr, sizeof(struct xfs_repair_list));
+ if (!lists)
+ return false;
+
+ for (agno = 0; agno < nr; agno++)
+ xfs_repair_list_init(&lists[agno]);
+ *listsp = lists;
+
+ return true;
+}
+
+/* Free the repair lists. */
+void
+xfs_repair_lists_free(
+ struct xfs_repair_list **listsp)
+{
+ free(*listsp);
+ *listsp = NULL;
+}
+
+/* Initialize repair list */
+void
+xfs_repair_list_init(
+ struct xfs_repair_list *rl)
+{
+ INIT_LIST_HEAD(&rl->list);
+ rl->nr = 0;
+ rl->sorted = false;
+}
+
+/* Number of repairs in this list. */
+size_t
+xfs_repair_list_length(
+ struct xfs_repair_list *rl)
+{
+ return rl->nr;
+};
+
+/* Add to the list of repairs. */
+void
+xfs_repair_list_add(
+ struct xfs_repair_list *rl,
+ struct repair_item *ri)
+{
+ list_add_tail(&ri->list, &rl->list);
+ rl->nr++;
+ rl->sorted = false;
+}
+
+/* Splice two repair lists. */
+void
+xfs_repair_list_splice(
+ struct xfs_repair_list *dest,
+ struct xfs_repair_list *src)
+{
+ if (src->nr == 0)
+ return;
+
+ list_splice_tail_init(&src->list, &dest->list);
+ dest->nr += src->nr;
+ src->nr = 0;
+ dest->sorted = false;
+}
+
+/* Repair everything on this list. */
+bool
+xfs_repair_list_now(
+ struct scrub_ctx *ctx,
+ int fd,
+ struct xfs_repair_list *rl,
+ unsigned int repair_flags)
+{
+ struct repair_item *ri;
+ struct repair_item *n;
+ enum check_outcome fix;
+
+ if (!rl->sorted) {
+ list_sort(NULL, &rl->list, xfs_repair_item_compare);
+ rl->sorted = true;
+ }
+
+ list_for_each_entry_safe(ri, n, &rl->list, list) {
+ fix = xfs_repair_metadata(ctx, fd, ri, repair_flags);
+ switch (fix) {
+ case CHECK_DONE:
+ rl->nr--;
+ list_del(&ri->list);
+ free(ri);
+ continue;
+ case CHECK_ABORT:
+ return false;
+ case CHECK_RETRY:
+ continue;
+ case CHECK_REPAIR:
+ abort();
+ }
+ }
+
+ return !xfs_scrub_excessive_errors(ctx);
+}
+
+/* Defer all the repairs until phase 4. */
+void
+xfs_defer_repairs(
+ struct scrub_ctx *ctx,
+ xfs_agnumber_t agno,
+ struct xfs_repair_list *rl)
+{
+ ASSERT(agno < ctx->geo.agcount);
+
+ xfs_repair_list_splice(&ctx->repair_lists[agno], rl);
+}
+
+/* Quickly try to repair AG metadata; broken things are remembered for later. */
+bool
+xfs_quick_repair(
+ struct scrub_ctx *ctx,
+ xfs_agnumber_t agno,
+ struct xfs_repair_list *rl)
+{
+ bool moveon;
+
+ moveon = xfs_repair_list_now(ctx, ctx->mnt_fd, rl, XRML_REPAIR_ONLY);
+ if (!moveon)
+ return moveon;
+
+ xfs_defer_repairs(ctx, agno, rl);
+ return true;
+}
new file mode 100644
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 Oracle. All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef XFS_SCRUB_REPAIR_H_
+#define XFS_SCRUB_REPAIR_H_
+
+struct xfs_repair_list {
+ struct list_head list;
+ size_t nr;
+ bool sorted;
+};
+
+bool xfs_repair_lists_alloc(size_t nr, struct xfs_repair_list **listsp);
+void xfs_repair_lists_free(struct xfs_repair_list **listsp);
+
+void xfs_repair_list_init(struct xfs_repair_list *rl);
+size_t xfs_repair_list_length(struct xfs_repair_list *rl);
+void xfs_repair_list_add(struct xfs_repair_list *dest,
+ struct repair_item *item);
+void xfs_repair_list_splice(struct xfs_repair_list *dest,
+ struct xfs_repair_list *src);
+
+void xfs_repair_find_mustfix(struct xfs_repair_list *repairs,
+ struct xfs_repair_list *repair_now,
+ unsigned long long *broken_primaries,
+ unsigned long long *broken_secondaries);
+
+/* Passed through to xfs_repair_metadata() */
+#define XRML_REPAIR_ONLY (XRM_REPAIR_ONLY)
+#define XRML_NOFIX_COMPLAIN (XRM_NOFIX_COMPLAIN)
+
+bool xfs_repair_list_now(struct scrub_ctx *ctx, int fd,
+ struct xfs_repair_list *repair_list, unsigned int repair_flags);
+void xfs_defer_repairs(struct scrub_ctx *ctx, xfs_agnumber_t agno,
+ struct xfs_repair_list *rl);
+bool xfs_quick_repair(struct scrub_ctx *ctx, xfs_agnumber_t agno,
+ struct xfs_repair_list *rl);
+
+#endif /* XFS_SCRUB_REPAIR_H_ */
@@ -35,6 +35,7 @@
#include "progress.h"
#include "scrub.h"
#include "xfs_errortag.h"
+#include "repair.h"
/* Online scrub and repair wrappers. */
@@ -321,12 +322,47 @@ _("Optimizations of %s are possible."), scrubbers[i].name);
}
}
+/* Save a scrub context for later repairs. */
+bool
+xfs_scrub_save_repair(
+ struct scrub_ctx *ctx,
+ struct xfs_repair_list *rl,
+ struct xfs_scrub_metadata *meta)
+{
+ struct repair_item *ri;
+
+ /* Schedule this item for later repairs. */
+ ri = malloc(sizeof(struct repair_item));
+ if (!ri) {
+ str_errno(ctx, _("repair list"));
+ return false;
+ }
+ ri->type = meta->sm_type;
+ ri->flags = meta->sm_flags;
+ switch (scrubbers[meta->sm_type].type) {
+ case ST_AGHEADER:
+ case ST_PERAG:
+ ri->agno = meta->sm_agno;
+ break;
+ case ST_INODE:
+ ri->ino = meta->sm_ino;
+ ri->gen = meta->sm_gen;
+ break;
+ default:
+ break;
+ }
+
+ xfs_repair_list_add(rl, ri);
+ return true;
+}
+
/* Scrub metadata, saving corruption reports for later. */
static bool
xfs_scrub_metadata(
struct scrub_ctx *ctx,
enum scrub_type scrub_type,
- xfs_agnumber_t agno)
+ xfs_agnumber_t agno,
+ struct xfs_repair_list *rl)
{
struct xfs_scrub_metadata meta = {0};
const struct scrub_descr *sc;
@@ -350,6 +386,8 @@ xfs_scrub_metadata(
case CHECK_ABORT:
return false;
case CHECK_REPAIR:
+ if (!xfs_scrub_save_repair(ctx, rl, &meta))
+ return false;
/* fall through */
case CHECK_DONE:
continue;
@@ -369,7 +407,8 @@ xfs_scrub_metadata(
*/
bool
xfs_scrub_primary_super(
- struct scrub_ctx *ctx)
+ struct scrub_ctx *ctx,
+ struct xfs_repair_list *repair_list)
{
struct xfs_scrub_metadata meta = {
.sm_type = XFS_SCRUB_TYPE_SB,
@@ -382,6 +421,8 @@ xfs_scrub_primary_super(
case CHECK_ABORT:
return false;
case CHECK_REPAIR:
+ if (!xfs_scrub_save_repair(ctx, repair_list, &meta))
+ return false;
/* fall through */
case CHECK_DONE:
return true;
@@ -397,26 +438,29 @@ xfs_scrub_primary_super(
bool
xfs_scrub_ag_headers(
struct scrub_ctx *ctx,
- xfs_agnumber_t agno)
+ xfs_agnumber_t agno,
+ struct xfs_repair_list *rl)
{
- return xfs_scrub_metadata(ctx, ST_AGHEADER, agno);
+ return xfs_scrub_metadata(ctx, ST_AGHEADER, agno, rl);
}
/* Scrub each AG's metadata btrees. */
bool
xfs_scrub_ag_metadata(
struct scrub_ctx *ctx,
- xfs_agnumber_t agno)
+ xfs_agnumber_t agno,
+ struct xfs_repair_list *rl)
{
- return xfs_scrub_metadata(ctx, ST_PERAG, agno);
+ return xfs_scrub_metadata(ctx, ST_PERAG, agno, rl);
}
/* Scrub whole-FS metadata btrees. */
bool
xfs_scrub_fs_metadata(
- struct scrub_ctx *ctx)
+ struct scrub_ctx *ctx,
+ struct xfs_repair_list *rl)
{
- return xfs_scrub_metadata(ctx, ST_FS, 0);
+ return xfs_scrub_metadata(ctx, ST_FS, 0, rl);
}
/* How many items do we have to check? */
@@ -452,7 +496,8 @@ __xfs_scrub_file(
uint64_t ino,
uint32_t gen,
int fd,
- unsigned int type)
+ unsigned int type,
+ struct xfs_repair_list *rl)
{
struct xfs_scrub_metadata meta = {0};
enum check_outcome fix;
@@ -471,7 +516,7 @@ __xfs_scrub_file(
if (fix == CHECK_DONE)
return true;
- return true;
+ return xfs_scrub_save_repair(ctx, rl, &meta);
}
bool
@@ -479,9 +524,10 @@ xfs_scrub_inode_fields(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ int fd,
+ struct xfs_repair_list *rl)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_INODE);
+ return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_INODE, rl);
}
bool
@@ -489,9 +535,10 @@ xfs_scrub_data_fork(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ int fd,
+ struct xfs_repair_list *rl)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_BMBTD);
+ return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_BMBTD, rl);
}
bool
@@ -499,9 +546,10 @@ xfs_scrub_attr_fork(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ int fd,
+ struct xfs_repair_list *rl)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_BMBTA);
+ return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_BMBTA, rl);
}
bool
@@ -509,9 +557,10 @@ xfs_scrub_cow_fork(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ int fd,
+ struct xfs_repair_list *rl)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_BMBTC);
+ return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_BMBTC, rl);
}
bool
@@ -519,9 +568,10 @@ xfs_scrub_dir(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ int fd,
+ struct xfs_repair_list *rl)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_DIR);
+ return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_DIR, rl);
}
bool
@@ -529,9 +579,10 @@ xfs_scrub_attr(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ int fd,
+ struct xfs_repair_list *rl)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_XATTR);
+ return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_XATTR, rl);
}
bool
@@ -539,9 +590,10 @@ xfs_scrub_symlink(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ int fd,
+ struct xfs_repair_list *rl)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_SYMLINK);
+ return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_SYMLINK, rl);
}
bool
@@ -549,9 +601,10 @@ xfs_scrub_parent(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ int fd,
+ struct xfs_repair_list *rl)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_PARENT);
+ return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_PARENT, rl);
}
/* Test the availability of a kernel scrub command. */
@@ -773,7 +826,7 @@ _("Read-only filesystem; cannot make changes."));
xfs_scrub_warn_incomplete_scrub(ctx, buf, &meta);
if (needs_repair(&meta)) {
/* Still broken, try again or fix offline. */
- if (repair_flags & XRM_NOFIX_COMPLAIN)
+ if ((repair_flags & XRM_NOFIX_COMPLAIN) || debug)
str_error(ctx, buf,
_("Repair unsuccessful; offline repair required."));
} else {
@@ -28,11 +28,19 @@ enum check_outcome {
CHECK_RETRY, /* repair failed, try again later */
};
+struct repair_item;
+
void xfs_scrub_report_preen_triggers(struct scrub_ctx *ctx);
-bool xfs_scrub_primary_super(struct scrub_ctx *ctx);
-bool xfs_scrub_ag_headers(struct scrub_ctx *ctx, xfs_agnumber_t agno);
-bool xfs_scrub_ag_metadata(struct scrub_ctx *ctx, xfs_agnumber_t agno);
-bool xfs_scrub_fs_metadata(struct scrub_ctx *ctx);
+bool xfs_scrub_primary_super(struct scrub_ctx *ctx,
+ struct xfs_repair_list *repair_list);
+bool xfs_scrub_ag_headers(struct scrub_ctx *ctx, xfs_agnumber_t agno,
+ struct xfs_repair_list *repair_list);
+bool xfs_scrub_ag_metadata(struct scrub_ctx *ctx, xfs_agnumber_t agno,
+ struct xfs_repair_list *repair_list);
+bool xfs_scrub_fs_metadata(struct scrub_ctx *ctx,
+ struct xfs_repair_list *repair_list);
+enum check_outcome xfs_repair_metadata(struct scrub_ctx *ctx, int fd,
+ struct repair_item *ri, unsigned int flags);
bool xfs_can_scrub_fs_metadata(struct scrub_ctx *ctx);
bool xfs_can_scrub_inode(struct scrub_ctx *ctx);
@@ -44,21 +52,21 @@ bool xfs_can_scrub_parent(struct scrub_ctx *ctx);
bool xfs_can_repair(struct scrub_ctx *ctx);
bool xfs_scrub_inode_fields(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
- int fd);
+ int fd, struct xfs_repair_list *repair_list);
bool xfs_scrub_data_fork(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
- int fd);
+ int fd, struct xfs_repair_list *repair_list);
bool xfs_scrub_attr_fork(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
- int fd);
+ int fd, struct xfs_repair_list *repair_list);
bool xfs_scrub_cow_fork(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
- int fd);
+ int fd, struct xfs_repair_list *repair_list);
bool xfs_scrub_dir(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
- int fd);
+ int fd, struct xfs_repair_list *repair_list);
bool xfs_scrub_attr(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
- int fd);
+ int fd, struct xfs_repair_list *repair_list);
bool xfs_scrub_symlink(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
- int fd);
+ int fd, struct xfs_repair_list *repair_list);
bool xfs_scrub_parent(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
- int fd);
+ int fd, struct xfs_repair_list *repair_list);
/* Repair parameters are the scrub inputs and retry count. */
struct repair_item {
@@ -88,6 +88,15 @@
* the previous two phases are retried here; if there are uncorrectable
* errors, xfs_scrub stops here.
*
+ * To perform the actual repairs, we iterate all the items on the per-AG
+ * repair list and ask the kernel to repair them. Items which are
+ * successfully repaired are removed from the list. If an item is not
+ * repaired successfully (or the kernel asks us to try again), we retry
+ * the repairs until there is nothing left to fix or we fail to make
+ * forward progress. In that event, the unrepaired items are recorded
+ * as errors. If there are no errors at this point, we call FSTRIM on
+ * the filesystem.
+ *
* The next phase is the "check directory tree" phase. In this phase,
* every directory is opened (via file handle) to confirm that each
* directory is connected to the root. Directory entries are checked
@@ -707,6 +716,19 @@ _("%s: Not a XFS mount point or block device.\n"),
ret |= 8;
out:
+ if (ctx.repairs && ctx.preens)
+ fprintf(stdout,
+_("%s: %llu repairs and %llu optimizations made.\n"),
+ ctx.mntpoint, ctx.repairs, ctx.preens);
+ else if (ctx.repairs && ctx.preens == 0)
+ fprintf(stdout,
+_("%s: %llu repairs made.\n"),
+ ctx.mntpoint, ctx.repairs);
+ else if (ctx.repairs == 0 && ctx.preens)
+ fprintf(stdout,
+_("%s: %llu optimizations made.\n"),
+ ctx.mntpoint, ctx.preens);
+
total_errors = ctx.errors_found + ctx.runtime_errors;
if (ctx.need_repair)
repairstr = _(" Unmount and run xfs_repair.");
@@ -90,6 +90,7 @@ struct scrub_ctx {
/* Mutable scrub state; use lock. */
pthread_mutex_t lock;
+ struct xfs_repair_list *repair_lists;
unsigned long long max_errors;
unsigned long long runtime_errors;
unsigned long long errors_found;