]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bcachefs: Make sure __bch2_run_explicit_recovery_pass() signals to rewind
authorKent Overstreet <kent.overstreet@linux.dev>
Thu, 5 Dec 2024 00:46:35 +0000 (19:46 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Sat, 21 Dec 2024 06:36:22 +0000 (01:36 -0500)
We should always signal to rewind if the requested pass hasn't been run,
even if called multiple times.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/bcachefs.h
fs/bcachefs/recovery_passes.c

index b12c9c78beec3212604b6133d7c446136e635351..e6cd93e1ed0f35027a35d91f840cc3f12951dcd5 100644 (file)
@@ -1044,6 +1044,7 @@ struct bch_fs {
         * for signaling to the toplevel code which pass we want to run now.
         */
        enum bch_recovery_pass  curr_recovery_pass;
+       enum bch_recovery_pass  next_recovery_pass;
        /* bitmask of recovery passes that we actually ran */
        u64                     recovery_passes_complete;
        /* never rewinds version of curr_recovery_pass */
index f6d3a99cb63ee7c6a1b5fb2c3bcda1cca8804f1c..0b3c951c32da9dc8ffad0b80df02cb8e51efa60a 100644 (file)
@@ -103,27 +103,31 @@ u64 bch2_recovery_passes_from_stable(u64 v)
 static int __bch2_run_explicit_recovery_pass(struct bch_fs *c,
                                             enum bch_recovery_pass pass)
 {
-       if (c->opts.recovery_passes & BIT_ULL(pass))
-               return 0;
-
        if (c->curr_recovery_pass == ARRAY_SIZE(recovery_pass_fns))
                return -BCH_ERR_not_in_recovery;
 
+       if (c->recovery_passes_complete & BIT_ULL(pass))
+               return 0;
+
+       bool print = !(c->opts.recovery_passes & BIT_ULL(pass));
+
        if (pass < BCH_RECOVERY_PASS_set_may_go_rw &&
            c->curr_recovery_pass >= BCH_RECOVERY_PASS_set_may_go_rw) {
-               bch_info(c, "need recovery pass %s (%u), but already rw",
-                        bch2_recovery_passes[pass], pass);
+               if (print)
+                       bch_info(c, "need recovery pass %s (%u), but already rw",
+                                bch2_recovery_passes[pass], pass);
                return -BCH_ERR_cannot_rewind_recovery;
        }
 
-       bch_info(c, "running explicit recovery pass %s (%u), currently at %s (%u)",
-                bch2_recovery_passes[pass], pass,
-                bch2_recovery_passes[c->curr_recovery_pass], c->curr_recovery_pass);
+       if (print)
+               bch_info(c, "running explicit recovery pass %s (%u), currently at %s (%u)",
+                        bch2_recovery_passes[pass], pass,
+                        bch2_recovery_passes[c->curr_recovery_pass], c->curr_recovery_pass);
 
        c->opts.recovery_passes |= BIT_ULL(pass);
 
-       if (c->curr_recovery_pass >= pass) {
-               c->curr_recovery_pass = pass;
+       if (c->curr_recovery_pass > pass) {
+               c->next_recovery_pass = pass;
                c->recovery_passes_complete &= (1ULL << pass) >> 1;
                return -BCH_ERR_restart_recovery;
        } else {
@@ -264,7 +268,9 @@ int bch2_run_recovery_passes(struct bch_fs *c)
         */
        c->opts.recovery_passes_exclude &= ~BCH_RECOVERY_PASS_set_may_go_rw;
 
-       while (c->curr_recovery_pass < ARRAY_SIZE(recovery_pass_fns)) {
+       while (c->curr_recovery_pass < ARRAY_SIZE(recovery_pass_fns) && !ret) {
+               c->next_recovery_pass = c->curr_recovery_pass + 1;
+
                spin_lock_irq(&c->recovery_pass_lock);
                unsigned pass = c->curr_recovery_pass;
 
@@ -285,31 +291,25 @@ int bch2_run_recovery_passes(struct bch_fs *c)
                ret =   bch2_run_recovery_pass(c, pass) ?:
                        bch2_journal_flush(&c->journal);
 
+               if (!ret && !test_bit(BCH_FS_error, &c->flags))
+                       bch2_clear_recovery_pass_required(c, pass);
+
                spin_lock_irq(&c->recovery_pass_lock);
-               if (c->curr_recovery_pass < pass) {
+               if (c->next_recovery_pass < c->curr_recovery_pass) {
                        /*
                         * bch2_run_explicit_recovery_pass() was called: we
                         * can't always catch -BCH_ERR_restart_recovery because
                         * it may have been called from another thread (btree
                         * node read completion)
                         */
-                       spin_unlock_irq(&c->recovery_pass_lock);
-                       continue;
-               } else if (c->curr_recovery_pass == pass) {
-                       c->curr_recovery_pass++;
+                       ret = 0;
+                       c->recovery_passes_complete &= ~(~0ULL << c->curr_recovery_pass);
                } else {
-                       BUG();
+                       c->recovery_passes_complete |= BIT_ULL(pass);
+                       c->recovery_pass_done = max(c->recovery_pass_done, pass);
                }
+               c->curr_recovery_pass = c->next_recovery_pass;
                spin_unlock_irq(&c->recovery_pass_lock);
-
-               if (ret)
-                       break;
-
-               c->recovery_passes_complete |= BIT_ULL(pass);
-               c->recovery_pass_done = max(c->recovery_pass_done, pass);
-
-               if (!test_bit(BCH_FS_error, &c->flags))
-                       bch2_clear_recovery_pass_required(c, pass);
        }
 
        return ret;