mirror of
https://github.com/torvalds/linux.git
synced 2026-06-07 05:55:44 +02:00
bcachefs: bch_sb_field_recovery_passes
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>
This commit is contained in:
parent
20a4b7f3b8
commit
e21f997721
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user