]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bcachefs: bch_sb_field_recovery_passes
authorKent Overstreet <kent.overstreet@linux.dev>
Fri, 9 May 2025 22:24:20 +0000 (18:24 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 22 May 2025 00:14:53 +0000 (20:14 -0400)
New superblock section for statistics on recovery passes - last time
ran (successfully), last runtime.

This will be used by self healing code to determine when to kick off
potentially expensive recovery passes.

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

index df5a4d4df64041df3050aa96ef43ecce5aaded81..5900ff3715c6244f24a628eeaf31b36cf41f68f3 100644 (file)
@@ -497,7 +497,8 @@ struct bch_sb_field {
        x(members_v2,                   11)     \
        x(errors,                       12)     \
        x(ext,                          13)     \
-       x(downgrade,                    14)
+       x(downgrade,                    14)     \
+       x(recovery_passes,              15)
 
 #include "alloc_background_format.h"
 #include "dirent_format.h"
index e14aca00cb7de4f341324786d41aa1967cedf388..4da3f6463db89c7b41d4f48561a1f86cbc5c7a0a 100644 (file)
@@ -28,6 +28,121 @@ const char * const bch2_recovery_passes[] = {
        NULL
 };
 
+static const u8 passes_to_stable_map[] = {
+#define x(n, id, ...)  [BCH_RECOVERY_PASS_##n] = BCH_RECOVERY_PASS_STABLE_##n,
+       BCH_RECOVERY_PASSES()
+#undef x
+};
+
+static const u8 passes_from_stable_map[] = {
+#define x(n, id, ...)  [BCH_RECOVERY_PASS_STABLE_##n] = BCH_RECOVERY_PASS_##n,
+       BCH_RECOVERY_PASSES()
+#undef x
+};
+
+static enum bch_recovery_pass_stable bch2_recovery_pass_to_stable(enum bch_recovery_pass pass)
+{
+       return passes_to_stable_map[pass];
+}
+
+u64 bch2_recovery_passes_to_stable(u64 v)
+{
+       u64 ret = 0;
+       for (unsigned i = 0; i < ARRAY_SIZE(passes_to_stable_map); i++)
+               if (v & BIT_ULL(i))
+                       ret |= BIT_ULL(passes_to_stable_map[i]);
+       return ret;
+}
+
+static enum bch_recovery_pass bch2_recovery_pass_from_stable(enum bch_recovery_pass_stable pass)
+{
+       return pass < ARRAY_SIZE(passes_from_stable_map)
+               ? passes_from_stable_map[pass]
+               : 0;
+}
+
+u64 bch2_recovery_passes_from_stable(u64 v)
+{
+       u64 ret = 0;
+       for (unsigned i = 0; i < ARRAY_SIZE(passes_from_stable_map); i++)
+               if (v & BIT_ULL(i))
+                       ret |= BIT_ULL(passes_from_stable_map[i]);
+       return ret;
+}
+
+static int bch2_sb_recovery_passes_validate(struct bch_sb *sb, struct bch_sb_field *f,
+                                           enum bch_validate_flags flags, struct printbuf *err)
+{
+       return 0;
+}
+
+static void bch2_sb_recovery_passes_to_text(struct printbuf *out,
+                                           struct bch_sb *sb,
+                                           struct bch_sb_field *f)
+{
+       struct bch_sb_field_recovery_passes *r =
+               field_to_type(f, recovery_passes);
+       unsigned nr = recovery_passes_nr_entries(r);
+
+       if (out->nr_tabstops < 1)
+               printbuf_tabstop_push(out, 32);
+       if (out->nr_tabstops < 2)
+               printbuf_tabstop_push(out, 16);
+
+       prt_printf(out, "Pass\tLast run\tLast runtime\n");
+
+       for (struct recovery_pass_entry *i = r->start; i < r->start + nr; i++) {
+               if (!i->last_run)
+                       continue;
+
+               unsigned idx = i - r->start;
+
+               prt_printf(out, "%s\t", bch2_recovery_passes[bch2_recovery_pass_from_stable(idx)]);
+
+               bch2_prt_datetime(out, le64_to_cpu(i->last_run));
+               prt_tab(out);
+
+               bch2_pr_time_units(out, le32_to_cpu(i->last_runtime) * NSEC_PER_SEC);
+               prt_newline(out);
+       }
+}
+
+static void bch2_sb_recovery_pass_complete(struct bch_fs *c,
+                                          enum bch_recovery_pass pass,
+                                          s64 start_time)
+{
+       enum bch_recovery_pass_stable stable = bch2_recovery_pass_to_stable(pass);
+       s64 end_time = ktime_get_real_seconds();
+
+       mutex_lock(&c->sb_lock);
+       struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext);
+       __clear_bit_le64(stable, ext->recovery_passes_required);
+
+       struct bch_sb_field_recovery_passes *r =
+               bch2_sb_field_get(c->disk_sb.sb, recovery_passes);
+
+       if (stable >= recovery_passes_nr_entries(r)) {
+               unsigned u64s = struct_size(r, start, stable + 1) / sizeof(u64);
+
+               r = bch2_sb_field_resize(&c->disk_sb, recovery_passes, u64s);
+               if (!r) {
+                       bch_err(c, "error creating recovery_passes sb section");
+                       goto out;
+               }
+       }
+
+       r->start[stable].last_run       = cpu_to_le64(end_time);
+       r->start[stable].last_runtime   = cpu_to_le32(max(0, end_time - start_time));
+out:
+       bch2_write_super(c);
+       mutex_unlock(&c->sb_lock);
+}
+
+const struct bch_sb_field_ops bch_sb_field_ops_recovery_passes = {
+       .validate       = bch2_sb_recovery_passes_validate,
+       .to_text        = bch2_sb_recovery_passes_to_text
+};
+
 /* Fake recovery pass, so that scan_for_btree_nodes isn't 0: */
 static int bch2_recovery_pass_empty(struct bch_fs *c)
 {
@@ -88,41 +203,6 @@ static struct recovery_pass_fn recovery_pass_fns[] = {
 #undef x
 };
 
-static const u8 passes_to_stable_map[] = {
-#define x(n, id, ...)  [BCH_RECOVERY_PASS_##n] = BCH_RECOVERY_PASS_STABLE_##n,
-       BCH_RECOVERY_PASSES()
-#undef x
-};
-
-static enum bch_recovery_pass_stable bch2_recovery_pass_to_stable(enum bch_recovery_pass pass)
-{
-       return passes_to_stable_map[pass];
-}
-
-u64 bch2_recovery_passes_to_stable(u64 v)
-{
-       u64 ret = 0;
-       for (unsigned i = 0; i < ARRAY_SIZE(passes_to_stable_map); i++)
-               if (v & BIT_ULL(i))
-                       ret |= BIT_ULL(passes_to_stable_map[i]);
-       return ret;
-}
-
-u64 bch2_recovery_passes_from_stable(u64 v)
-{
-       static const u8 map[] = {
-#define x(n, id, ...)  [BCH_RECOVERY_PASS_STABLE_##n] = BCH_RECOVERY_PASS_##n,
-       BCH_RECOVERY_PASSES()
-#undef x
-       };
-
-       u64 ret = 0;
-       for (unsigned i = 0; i < ARRAY_SIZE(map); i++)
-               if (v & BIT_ULL(i))
-                       ret |= BIT_ULL(map[i]);
-       return ret;
-}
-
 /*
  * For when we need to rewind recovery passes and run a pass we skipped:
  */
@@ -219,21 +299,6 @@ int bch2_run_explicit_recovery_pass_persistent(struct bch_fs *c,
        return ret;
 }
 
-static void bch2_clear_recovery_pass_required(struct bch_fs *c,
-                                             enum bch_recovery_pass pass)
-{
-       enum bch_recovery_pass_stable s = bch2_recovery_pass_to_stable(pass);
-
-       mutex_lock(&c->sb_lock);
-       struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext);
-
-       if (test_bit_le64(s, ext->recovery_passes_required)) {
-               __clear_bit_le64(s, ext->recovery_passes_required);
-               bch2_write_super(c);
-       }
-       mutex_unlock(&c->sb_lock);
-}
-
 u64 bch2_fsck_recovery_passes(void)
 {
        u64 ret = 0;
@@ -266,14 +331,19 @@ static bool should_run_recovery_pass(struct bch_fs *c, enum bch_recovery_pass pa
 static int bch2_run_recovery_pass(struct bch_fs *c, enum bch_recovery_pass pass)
 {
        struct recovery_pass_fn *p = recovery_pass_fns + pass;
-       int ret;
 
        if (!(p->when & PASS_SILENT))
                bch2_print(c, KERN_INFO bch2_log_msg(c, "%s..."),
                           bch2_recovery_passes[pass]);
-       ret = p->fn(c);
+
+       s64 start_time = ktime_get_real_seconds();
+       int ret = p->fn(c);
        if (ret)
                return ret;
+
+       if (!test_bit(BCH_FS_error, &c->flags))
+               bch2_sb_recovery_pass_complete(c, pass, start_time);
+
        if (!(p->when & PASS_SILENT))
                bch2_print(c, KERN_CONT " done\n");
 
@@ -326,9 +396,6 @@ int bch2_run_recovery_passes(struct bch_fs *c)
                        spin_unlock_irq(&c->recovery_pass_lock);
                        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->next_recovery_pass < c->curr_recovery_pass) {
index f33dd005beb438e0f30d0358851d84407996be73..d39856f908da4535ae750502560e359b42c06401 100644 (file)
@@ -3,6 +3,8 @@
 
 extern const char * const bch2_recovery_passes[];
 
+extern const struct bch_sb_field_ops bch_sb_field_ops_recovery_passes;
+
 u64 bch2_recovery_passes_to_stable(u64 v);
 u64 bch2_recovery_passes_from_stable(u64 v);
 
index 291f58dfbd2453c04d64c7f799d8f7a04d146e52..c434eafbca196f58f60aa61d55d40ac66bc9201d 100644 (file)
@@ -81,4 +81,24 @@ enum bch_recovery_pass_stable {
 #undef x
 };
 
+struct recovery_pass_entry {
+       __le64                  last_run;
+       __le32                  last_runtime;
+       __le32                  flags;
+};
+
+struct bch_sb_field_recovery_passes {
+       struct bch_sb_field     field;
+       struct recovery_pass_entry start[];
+};
+
+static inline unsigned
+recovery_passes_nr_entries(struct bch_sb_field_recovery_passes *r)
+{
+       return r
+               ? ((vstruct_end(&r->field) - (void *) &r->start[0]) /
+                  sizeof(struct recovery_pass_entry))
+               : 0;
+}
+
 #endif /* _BCACHEFS_RECOVERY_PASSES_FORMAT_H */