]> git-server-git.apps.pok.os.sepia.ceph.com Git - xfsprogs-dev.git/commitdiff
xfs_scrub: move failmap and other outputs into read_verify_pool
authorDarrick J. Wong <djwong@kernel.org>
Wed, 18 Mar 2026 16:45:04 +0000 (09:45 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Thu, 9 Apr 2026 22:30:19 +0000 (15:30 -0700)
Remove all the indirect verify IO error function calls and whatnot by
making the read_verify_pool track the ranges of failed media and other
problems.  Add some new helper functions that report on the outcome of
the read verifciation so that phase6 can report on what happened.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
scrub/phase6.c
scrub/read_verify.c
scrub/read_verify.h

index df907ea816fa0b65db38aa6f43b6c7dc6b28f2be..ae3b928df0f263cce63c049dc09e779f9a436eaf 100644 (file)
@@ -43,13 +43,6 @@ struct media_verify_state {
        struct ptvar            *verify_schedules;
 
        struct read_verify_pool *rvp[XFS_DEV_RT + 1];
-
-       struct bitmap           *d_bad;         /* bytes */
-       struct bitmap           *r_bad;         /* bytes */
-       struct bitmap           *l_bad;         /* bytes */
-       bool                    d_trunc:1;
-       bool                    r_trunc:1;
-       bool                    l_trunc:1;
 };
 
 /* Return XFS device index from fsmap device. */
@@ -94,24 +87,6 @@ to_fsmap_dev(
        }
 }
 
-/* Find the incore bad blocks bitmap for a given disk. */
-static struct bitmap *
-bitmap_for_disk(
-       enum xfs_device                 dev,
-       struct media_verify_state       *vs)
-{
-       switch (dev) {
-       case XFS_DEV_DATA:
-               return vs->d_bad;
-       case XFS_DEV_RT:
-               return vs->r_bad;
-       case XFS_DEV_LOG:
-               return vs->l_bad;
-       default:
-               return NULL;
-       }
-}
-
 struct disk_ioerr_report {
        struct scrub_ctx        *ctx;
        enum xfs_device         dev;
@@ -190,6 +165,13 @@ _("media error at data offset %llu length %llu."),
        return 0;
 }
 
+static inline enum xfs_device from_fsx(const struct fsxattr *fsx)
+{
+       if (fsx->fsx_xflags & FS_XFLAG_REALTIME)
+               return XFS_DEV_RT;
+       return XFS_DEV_DATA;
+}
+
 /* Report if this extent overlaps a bad region. */
 static int
 report_data_loss(
@@ -202,7 +184,6 @@ report_data_loss(
 {
        struct badfile_report           *br = arg;
        struct media_verify_state       *vs = br->vs;
-       struct bitmap                   *bmp;
 
        br->bmap = bmap;
 
@@ -210,13 +191,9 @@ report_data_loss(
        if (bmap->bm_flags & (BMV_OF_PREALLOC | BMV_OF_DELALLOC))
                return 0;
 
-       if (fsx->fsx_xflags & FS_XFLAG_REALTIME)
-               bmp = vs->r_bad;
-       else
-               bmp = vs->d_bad;
-
-       return -bitmap_iterate_range(bmp, bmap->bm_physical, bmap->bm_length,
-                       report_badfile, br);
+       return read_verify_iterate_failed_range(vs->rvp[from_fsx(fsx)],
+                       bmap->bm_physical, bmap->bm_length, report_badfile,
+                       br);
 }
 
 /* Report if the extended attribute data overlaps a bad region. */
@@ -231,7 +208,6 @@ report_attr_loss(
 {
        struct badfile_report           *br = arg;
        struct media_verify_state       *vs = br->vs;
-       struct bitmap                   *bmp = vs->d_bad;
 
        /* Complain about attr fork extents that don't look right. */
        if (bmap->bm_flags & (BMV_OF_PREALLOC | BMV_OF_DELALLOC)) {
@@ -246,7 +222,8 @@ _("found unexpected realtime attr fork extent."));
                return 0;
        }
 
-       if (bitmap_test(bmp, bmap->bm_physical, bmap->bm_length))
+       if (read_verify_has_failed(vs->rvp[XFS_DEV_DATA], bmap->bm_physical,
+                               bmap->bm_length))
                str_corrupt(ctx, br->descr,
 _("media error in extended attribute data."));
 
@@ -530,6 +507,19 @@ report_ioerr(
                        &fr);
 }
 
+static inline const char *trunc_msg(enum xfs_device dev)
+{
+       switch (dev) {
+       case XFS_DEV_DATA:
+               return _("data device truncated");
+       case XFS_DEV_LOG:
+               return _("log device truncated");
+       case XFS_DEV_RT:
+               return _("rt device truncated");
+       }
+       abort();
+}
+
 /* Report all the media errors found on a disk. */
 static int
 report_disk_ioerrs(
@@ -537,15 +527,18 @@ report_disk_ioerrs(
        struct media_verify_state       *vs,
        enum xfs_device                 dev)
 {
-       struct bitmap                   *tree = bitmap_for_disk(dev, vs);
        struct disk_ioerr_report        dioerr = {
                .ctx                    = ctx,
                .dev                    = dev,
        };
 
-       if (!tree)
+       if (!vs->rvp[dev])
                return 0;
-       return -bitmap_iterate(tree, report_ioerr, &dioerr);
+
+       if (read_verify_truncated(vs->rvp[dev]))
+               str_corrupt(ctx, ctx->mntpoint, trunc_msg(dev));
+
+       return read_verify_iterate_failed(vs->rvp[dev], report_ioerr, &dioerr);
 }
 
 /* Given bad extent lists for the data & rtdev, find bad files. */
@@ -556,13 +549,6 @@ report_all_media_errors(
 {
        int                             ret;
 
-       if (vs->d_trunc)
-               str_corrupt(ctx, ctx->mntpoint, _("data device truncated"));
-       if (vs->l_trunc)
-               str_corrupt(ctx, ctx->mntpoint, _("log device truncated"));
-       if (vs->r_trunc)
-               str_corrupt(ctx, ctx->mntpoint, _("rt device truncated"));
-
        ret = report_disk_ioerrs(ctx, vs, XFS_DEV_DATA);
        if (ret) {
                str_liberror(ctx, ret, _("walking datadev io errors"));
@@ -682,10 +668,10 @@ static int
 clean_pool(
        struct media_verify_state       *vs,
        enum xfs_device                 dev,
-       unsigned long long              *bytes_checked)
+       unsigned long long              *bytes_checked,
+       bool                            *ok)
 {
        struct read_verify_pool         *rvp = vs->rvp[dev];
-       uint64_t                        pool_checked;
        int                             ret;
 
        if (!rvp)
@@ -693,56 +679,12 @@ clean_pool(
 
        ret = read_verify_pool_flush(rvp);
        if (ret)
-               goto out_destroy;
-
-       ret = read_verify_bytes(rvp, &pool_checked);
-       if (ret)
-               goto out_destroy;
-
-       *bytes_checked += pool_checked;
-out_destroy:
-       read_verify_pool_destroy(rvp);
-       return ret;
-}
-
-/* Remember a media error for later. */
-static void
-remember_ioerr(
-       struct scrub_ctx                *ctx,
-       enum xfs_device                 dev,
-       uint64_t                        start,
-       uint64_t                        length,
-       int                             error,
-       void                            *arg)
-{
-       struct media_verify_state       *vs = arg;
-       struct bitmap                   *tree;
-       int                             ret;
-
-       if (!length) {
-               switch (dev) {
-               case XFS_DEV_DATA:
-                       vs->d_trunc = true;
-                       break;
-               case XFS_DEV_LOG:
-                       vs->l_trunc = true;
-                       break;
-               case XFS_DEV_RT:
-                       vs->r_trunc = true;
-                       break;
-               }
-               return;
-       }
-
-       tree = bitmap_for_disk(dev, vs);
-       if (!tree) {
-               str_liberror(ctx, ENOENT, _("finding bad block bitmap"));
-               return;
-       }
+               return ret;
 
-       ret = -bitmap_set(tree, start, length);
-       if (ret)
-               str_liberror(ctx, ret, _("setting bad block bitmap"));
+       *bytes_checked += read_verify_progress(rvp);
+       if (!read_verify_ok(rvp))
+               *ok = false;
+       return 0;
 }
 
 static inline int
@@ -751,8 +693,7 @@ alloc_pool(
        struct media_verify_state       *vs,
        enum xfs_device                 dev)
 {
-       return read_verify_pool_alloc(ctx, dev, remember_ioerr, vs,
-                       &vs->rvp[dev]);
+       return read_verify_pool_alloc(ctx, dev, &vs->rvp[dev]);
 }
 
 static inline void
@@ -779,30 +720,13 @@ phase6_func(
        struct scrub_ctx                *ctx)
 {
        struct media_verify_state       vs = { NULL };
+       bool                            ok = true;
        int                             ret, ret2, ret3;
 
-       ret = -bitmap_alloc(&vs.d_bad);
-       if (ret) {
-               str_liberror(ctx, ret, _("creating datadev badblock bitmap"));
-               return ret;
-       }
-
-       ret = -bitmap_alloc(&vs.r_bad);
-       if (ret) {
-               str_liberror(ctx, ret, _("creating realtime badblock bitmap"));
-               goto out_dbad;
-       }
-
-       ret = -bitmap_alloc(&vs.l_bad);
-       if (ret) {
-               str_liberror(ctx, ret, _("creating log badblock bitmap"));
-               goto out_rbad;
-       }
-
        ret = alloc_pool(ctx, &vs, XFS_DEV_DATA);
        if (ret) {
                str_liberror(ctx, ret, _("creating datadev media verifier"));
-               goto out_lbad;
+               return ret;
        }
        if (ctx->fsinfo.fs_log) {
                ret = alloc_pool(ctx, &vs, XFS_DEV_LOG);
@@ -839,15 +763,15 @@ phase6_func(
        ptvar_free(vs.verify_schedules);
        vs.verify_schedules = NULL;
 
-       ret = clean_pool(&vs, XFS_DEV_DATA, &ctx->bytes_checked);
+       ret = clean_pool(&vs, XFS_DEV_DATA, &ctx->bytes_checked, &ok);
        if (ret)
                str_liberror(ctx, ret, _("flushing datadev verify pool"));
 
-       ret2 = clean_pool(&vs, XFS_DEV_LOG, &ctx->bytes_checked);
+       ret2 = clean_pool(&vs, XFS_DEV_LOG, &ctx->bytes_checked, &ok);
        if (ret2)
                str_liberror(ctx, ret2, _("flushing logdev verify pool"));
 
-       ret3 = clean_pool(&vs, XFS_DEV_RT, &ctx->bytes_checked);
+       ret3 = clean_pool(&vs, XFS_DEV_RT, &ctx->bytes_checked, &ok);
        if (ret3)
                str_liberror(ctx, ret3, _("flushing rtdev verify pool"));
 
@@ -855,22 +779,14 @@ phase6_func(
         * If the verify flush didn't work or we found no bad blocks, we're
         * done!  No errors detected.
         */
-       if (ret || ret2 || ret3) {
+       if (ret || ret2 || ret3 || ok) {
                ret |= ret2 | ret3; /* caller only cares about non-zero/zero */
-               goto out_lbad;
+               goto out_rtpool;
        }
-       if (bitmap_empty(vs.d_bad) && !vs.d_trunc &&
-           bitmap_empty(vs.r_bad) && !vs.r_trunc &&
-           bitmap_empty(vs.l_bad) && !vs.l_trunc)
-               goto out_lbad;
 
        /* Scan the whole dir tree to see what matches the bad extents. */
        ret = report_all_media_errors(ctx, &vs);
-
-       bitmap_free(&vs.l_bad);
-       bitmap_free(&vs.r_bad);
-       bitmap_free(&vs.d_bad);
-       return ret;
+       goto out_rtpool;
 
 out_schedules:
        ptvar_free(vs.verify_schedules);
@@ -880,12 +796,6 @@ out_logpool:
        free_pool(&vs, XFS_DEV_LOG);
 out_datapool:
        free_pool(&vs, XFS_DEV_DATA);
-out_lbad:
-       bitmap_free(&vs.l_bad);
-out_rbad:
-       bitmap_free(&vs.r_bad);
-out_dbad:
-       bitmap_free(&vs.d_bad);
        return ret;
 }
 
index 66086d6fe2d56d40e5e2f70fe22d4131e9967833..3334f8d8c79ca22b2c7e8fe506f6b8aa8d388ea2 100644 (file)
@@ -9,6 +9,7 @@
 #include <sys/statvfs.h>
 #include "libfrog/workqueue.h"
 #include "libfrog/paths.h"
+#include "libfrog/bitmap.h"
 #include "xfs_scrub.h"
 #include "common.h"
 #include "counter.h"
@@ -58,8 +59,6 @@ struct read_verify_pool {
        struct scrub_ctx        *ctx;           /* scrub context */
        void                    *readbuf;       /* read buffer */
        struct ptcounter        *verified_bytes;
-       void                    *ioerr_arg;
-       read_verify_ioerr_fn_t  ioerr_fn;       /* io error callback */
        size_t                  miniosz;        /* minimum io size, bytes */
        enum xfs_device         dev;            /* which device? */
 
@@ -68,6 +67,10 @@ struct read_verify_pool {
         * return it to the caller.
         */
        int                     runtime_error;
+
+       /* outputs: a bad block bitmap and a truncated flag */
+       struct bitmap           *failmap;
+       bool                    truncated;
 };
 
 unsigned int
@@ -88,15 +91,11 @@ read_verify_nproc(
 
 /*
  * Create a thread pool to run read verifiers.
- *
- * @ioerr_fn will be called when IO errors occur.
  */
 int
 read_verify_pool_alloc(
        struct scrub_ctx                *ctx,
        enum xfs_device                 dev,
-       read_verify_ioerr_fn_t          ioerr_fn,
-       void                            *ioerr_arg,
        struct read_verify_pool         **prvp)
 {
        struct read_verify_pool         *rvp;
@@ -121,8 +120,6 @@ read_verify_pool_alloc(
        rvp->miniosz = ctx->mnt.fsgeom.blocksize;
        rvp->ctx = ctx;
        rvp->dev = dev;
-       rvp->ioerr_fn = ioerr_fn;
-       rvp->ioerr_arg = ioerr_arg;
        ret = -workqueue_create(&rvp->wq, (struct xfs_mount *)rvp,
                        verifier_threads == 1 ? 0 : verifier_threads);
        if (ret)
@@ -146,7 +143,8 @@ read_verify_pool_abort(
 {
        if (!rvp->runtime_error)
                rvp->runtime_error = ECANCELED;
-       workqueue_terminate(&rvp->wq);
+       if (!rvp->wq.terminated)
+               workqueue_terminate(&rvp->wq);
 }
 
 /* Finish up any read verification work. */
@@ -163,6 +161,7 @@ read_verify_pool_destroy(
        struct read_verify_pool         *rvp)
 {
        workqueue_destroy(&rvp->wq);
+       bitmap_free(&rvp->failmap);
        ptcounter_free(rvp->verified_bytes);
        free(rvp->readbuf);
        free(rvp);
@@ -286,6 +285,49 @@ read_verify_one(
                        single_step);
 }
 
+/* Remember a media error for later. */
+static int
+read_verify_error(
+       struct read_verify_pool         *rvp,
+       uint64_t                        start,
+       uint64_t                        length,
+       int                             error)
+{
+       static pthread_mutex_t          lock = PTHREAD_MUTEX_INITIALIZER;
+       int                             ret;
+
+       if (!length) {
+               rvp->truncated = true;
+               return 0;
+       }
+
+       if (!rvp->failmap) {
+               struct bitmap *failmap;
+
+               ret = -bitmap_alloc(&failmap);
+               if (ret) {
+                       str_liberror(rvp->ctx, ret,
+ _("allocating bad block bitmap"));
+                       return ret;
+               }
+
+               pthread_mutex_lock(&lock);
+               if (!rvp->failmap)
+                       rvp->failmap = failmap;
+               else
+                       bitmap_free(&failmap);
+               pthread_mutex_unlock(&lock);
+       }
+
+       ret = -bitmap_set(rvp->failmap, start, length);
+       if (ret) {
+               str_liberror(rvp->ctx, ret, _("setting bad block bitmap"));
+               return ret;
+       }
+
+       return 0;
+}
+
 /*
  * Issue a read-verify IO in big batches.
  */
@@ -302,7 +344,7 @@ read_verify(
        ssize_t                         sz;
        ssize_t                         len;
        int                             read_error;
-       int                             ret;
+       int                             ret = 0, ret2;
 
        rvp = (struct read_verify_pool *)wq->wq_ctx;
        if (rvp->runtime_error)
@@ -356,14 +398,18 @@ read_verify(
                        sz = rvp->miniosz - (rv->io_start % rvp->miniosz);
                        dbg_printf("IOERR %u @ %"PRIu64" %zu err %d\n",
                                        rvp->dev, rv->io_start, sz, read_error);
-                       rvp->ioerr_fn(rvp->ctx, rvp->dev, rv->io_start, sz,
-                                       read_error, rvp->ioerr_arg);
+                       ret = read_verify_error(rvp, rv->io_start, sz,
+                                       read_error);
+                       if (ret)
+                               goto out_err;
                } else if (sz == 0) {
                        /* No bytes at all?  Did we hit the end of the disk? */
                        dbg_printf("EOF %u @ %"PRIu64" %zu err %d\n",
                                        rvp->dev, rv->io_start, sz, read_error);
-                       rvp->ioerr_fn(rvp->ctx, rvp->dev, rv->io_start, sz,
-                                       read_error, rvp->ioerr_arg);
+                       ret = read_verify_error(rvp, rv->io_start, sz,
+                                       read_error);
+                       if (ret)
+                               goto out_err;
                        break;
                } else if (sz < len) {
                        /*
@@ -392,8 +438,11 @@ read_verify(
                background_sleep();
        }
 
+out_err:
        free(rv);
-       ret = ptcounter_add(rvp->verified_bytes, verified);
+       ret2 = ptcounter_add(rvp->verified_bytes, verified);
+       if (!ret && ret2)
+               ret = ret2;
        if (ret)
                rvp->runtime_error = ret;
 }
@@ -491,11 +540,69 @@ try_read_verify_schedule_io(
        return false;
 }
 
-/* How many bytes has this process verified? */
+/* Did read verification succeed? */
+bool
+read_verify_ok(
+       const struct read_verify_pool   *rvp)
+{
+       return rvp->failmap == NULL && !rvp->truncated;
+}
+
+/* Did the verification unexpectedly stop early due to short reads? */
+bool
+read_verify_truncated(
+       const struct read_verify_pool   *rvp)
+{
+       return rvp->truncated;
+}
+
+/* How many bytes has this pool verified? */
+uint64_t
+read_verify_progress(
+       const struct read_verify_pool   *rvp)
+{
+       uint64_t                        ret = 0;
+
+       ptcounter_value(rvp->verified_bytes, &ret);
+       return ret;
+}
+
+/* Call @fn for every media failure this pool observed. */
 int
-read_verify_bytes(
+read_verify_iterate_failed(
        struct read_verify_pool         *rvp,
-       uint64_t                        *bytes_checked)
+       int                             (*fn)(uint64_t, uint64_t, void *),
+       void                            *arg)
 {
-       return ptcounter_value(rvp->verified_bytes, bytes_checked);
+       if (!rvp->failmap)
+               return 0;
+
+       return -bitmap_iterate(rvp->failmap, fn, arg);
+}
+
+/* Call @fn for every media failure this pool observed in the given range. */
+int
+read_verify_iterate_failed_range(
+       struct read_verify_pool         *rvp,
+       uint64_t                        start,
+       uint64_t                        length,
+       int                             (*fn)(uint64_t, uint64_t, void *),
+       void                            *arg)
+{
+       if (!rvp->failmap)
+               return 0;
+
+       return -bitmap_iterate_range(rvp->failmap, start, length, fn, arg);
+}
+
+/* Were there any media failures within the given range? */
+bool
+read_verify_has_failed(
+       struct read_verify_pool         *rvp,
+       uint64_t                        start,
+       uint64_t                        length)
+{
+       if (rvp->failmap)
+               return bitmap_test(rvp->failmap, start, length);
+       return false;
 }
index 6a338abb896bc3cf578c52debc280317bc8eefc8..c46ba1e76a8d035bc4e6d8f1bbf75bf21a4f1d15 100644 (file)
@@ -21,7 +21,6 @@ typedef void (*read_verify_ioerr_fn_t)(struct scrub_ctx *ctx,
                int error, void *arg);
 
 int read_verify_pool_alloc(struct scrub_ctx *ctx, enum xfs_device dev,
-               read_verify_ioerr_fn_t ioerr_fn, void *ioerr_arg,
                struct read_verify_pool **prvp);
 void read_verify_pool_abort(struct read_verify_pool *rvp);
 int read_verify_pool_flush(struct read_verify_pool *rvp);
@@ -31,7 +30,17 @@ int read_verify_schedule_now(struct read_verify_schedule *rs);
 bool try_read_verify_schedule_io(struct read_verify_schedule *rs,
                struct read_verify_pool *rvp, uint64_t start, uint64_t length);
 
-int read_verify_bytes(struct read_verify_pool *rvp, uint64_t *bytes);
+bool read_verify_ok(const struct read_verify_pool *rvp);
+bool read_verify_truncated(const struct read_verify_pool *rvp);
+uint64_t read_verify_progress(const struct read_verify_pool *rvp);
+
+int read_verify_iterate_failed(struct read_verify_pool *rvp,
+               int (*fn)(uint64_t, uint64_t, void *), void *arg);
+int read_verify_iterate_failed_range(struct read_verify_pool *rvp,
+               uint64_t start, uint64_t length,
+               int (*fn)(uint64_t, uint64_t, void *), void *arg);
+bool read_verify_has_failed(struct read_verify_pool *rvp, uint64_t start,
+               uint64_t len);
 
 unsigned int read_verify_nproc(struct scrub_ctx *ctx);