]> git.apps.os.sepia.ceph.com Git - xfsprogs-dev.git/commitdiff
xfs_scrub: try to repair space metadata before file metadata
authorDarrick J. Wong <djwong@kernel.org>
Mon, 29 Jul 2024 23:23:09 +0000 (16:23 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 30 Jul 2024 00:01:08 +0000 (17:01 -0700)
Phase 4 (metadata repairs) of xfs_scrub has suffered a mild race
condition since the beginning of its existence.  Repair functions for
higher level metadata such as directories build the new directory blocks
in an unlinked temporary file and use atomic extent swapping to commit
the corrected directory contents into the existing directory.  Atomic
extent swapping requires consistent filesystem space metadata, but phase
4 has never enforced correctness dependencies between space and file
metadata repairs.

Before the previous patch eliminated the per-AG repair lists, this error
was not often hit in testing scenarios because the allocator generally
succeeds in placing file data blocks in the same AG as the inode.  With
pool threads now able to pop file repairs from the repair list before
space repairs complete, this error became much more obvious.

Fortunately, the new phase 4 design makes it easy to try to enforce the
consistency requirements of higher level file metadata repairs.  Split
the repair list into one for space metadata and another for file
metadata.  Phase 4 will now try to fix the space metadata until it stops
making progress on that, and only then will it try to fix file metadata.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
scrub/phase1.c
scrub/phase2.c
scrub/phase3.c
scrub/phase4.c
scrub/xfs_scrub.h

index 78769a57bf1fd8102b74425f2bb1054cd5684f8d..1b3f6e8eb4f337b8c3b426b7a2ae365543c301c7 100644 (file)
@@ -89,7 +89,8 @@ scrub_cleanup(
        if (error)
                return error;
 
-       action_list_free(&ctx->action_list);
+       action_list_free(&ctx->file_repair_list);
+       action_list_free(&ctx->fs_repair_list);
 
        if (ctx->fshandle)
                free_handle(ctx->fshandle, ctx->fshandle_len);
@@ -186,9 +187,15 @@ _("Not an XFS filesystem."));
                return error;
        }
 
-       error = action_list_alloc(&ctx->action_list);
+       error = action_list_alloc(&ctx->fs_repair_list);
        if (error) {
-               str_liberror(ctx, error, _("allocating repair list"));
+               str_liberror(ctx, error, _("allocating fs repair list"));
+               return error;
+       }
+
+       error = action_list_alloc(&ctx->file_repair_list);
+       if (error) {
+               str_liberror(ctx, error, _("allocating file repair list"));
                return error;
        }
 
index 5803d8c645ac77cbaa07975966e2e35d1bb40101..57c6d0ef2137c3ab75268e49def3b017887a3454 100644 (file)
@@ -64,7 +64,7 @@ defer_fs_repair(
                return error;
 
        pthread_mutex_lock(&ctx->lock);
-       action_list_add(ctx->action_list, aitem);
+       action_list_add(ctx->fs_repair_list, aitem);
        pthread_mutex_unlock(&ctx->lock);
        return 0;
 }
index 1a71d4ace486a56af043669de82478488b4207a6..98e5c5a1f9f4629b5af3fda15c3a384a331ec2bc 100644 (file)
@@ -234,7 +234,7 @@ collect_repairs(
        struct scrub_ctx        *ctx = foreach_arg;
        struct action_list      *alist = data;
 
-       action_list_merge(ctx->action_list, alist);
+       action_list_merge(ctx->file_repair_list, alist);
        return 0;
 }
 
@@ -278,7 +278,7 @@ phase3_func(
         * to repair the space metadata.
         */
        for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) {
-               if (!action_list_empty(ctx->action_list))
+               if (!action_list_empty(ctx->fs_repair_list))
                        ictx.always_defer_repairs = true;
        }
 
index 564ccb82704907d24bdc16f040ffeedbc95a7e15..9080d38818f7e426a58563230d295e29a76a99ce 100644 (file)
@@ -198,7 +198,13 @@ repair_everything(
        do {
                fixed_anything = 0;
 
-               ret = repair_list_schedule(ctx, &wq, ctx->action_list);
+               ret = repair_list_schedule(ctx, &wq, ctx->fs_repair_list);
+               if (ret < 0)
+                       break;
+               if (ret == 1)
+                       fixed_anything++;
+
+               ret = repair_list_schedule(ctx, &wq, ctx->file_repair_list);
                if (ret < 0)
                        break;
                if (ret == 1)
@@ -213,8 +219,12 @@ repair_everything(
        if (ret < 0)
                return ret;
 
-       /* Repair everything serially.  Last chance to fix things. */
-       return action_list_process(ctx, ctx->action_list, XRM_FINAL_WARNING);
+       /*
+        * Combine both repair lists and repair everything serially.  This is
+        * the last chance to fix things.
+        */
+       action_list_merge(ctx->fs_repair_list, ctx->file_repair_list);
+       return action_list_process(ctx, ctx->fs_repair_list, XRM_FINAL_WARNING);
 }
 
 /* Trim the unused areas of the filesystem if the caller asked us to. */
@@ -236,7 +246,8 @@ phase4_func(
        struct scrub_item       sri;
        int                     ret;
 
-       if (action_list_empty(ctx->action_list))
+       if (action_list_empty(ctx->fs_repair_list) &&
+           action_list_empty(ctx->file_repair_list))
                goto maybe_trim;
 
        /*
@@ -297,7 +308,8 @@ phase4_estimate(
        unsigned long long      need_fixing;
 
        /* Everything on the repair list plus FSTRIM. */
-       need_fixing = action_list_length(ctx->action_list);
+       need_fixing = action_list_length(ctx->fs_repair_list) +
+                     action_list_length(ctx->file_repair_list);
        need_fixing++;
 
        *items = need_fixing;
index a339c4d6348ce819e624e0e1a9acadbb59ba8d79..ed86d0093db3f681eda01b3d2b710c0255fd0d24 100644 (file)
@@ -72,7 +72,8 @@ struct scrub_ctx {
 
        /* Mutable scrub state; use lock. */
        pthread_mutex_t         lock;
-       struct action_list      *action_list;
+       struct action_list      *fs_repair_list;
+       struct action_list      *file_repair_list;
        unsigned long long      max_errors;
        unsigned long long      runtime_errors;
        unsigned long long      corruptions_found;