X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libbcachefs%2Ferror.c;h=655e3ba9bfd2c09f221011ae16c38ba9f101e6ff;hb=596ec1050d0ce55f5e2d38aa8cdcb46d92178140;hp=c2882c599896cc694c9e0180bf4abf4e90f05e0a;hpb=a104f0407b7f5de54972389ef10e11dd8c525a96;p=bcachefs-tools-debian diff --git a/libbcachefs/error.c b/libbcachefs/error.c index c2882c5..655e3ba 100644 --- a/libbcachefs/error.c +++ b/libbcachefs/error.c @@ -1,14 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 #include "bcachefs.h" #include "error.h" -#include "io.h" #include "super.h" #define FSCK_ERR_RATELIMIT_NR 10 bool bch2_inconsistent_error(struct bch_fs *c) { - set_bit(BCH_FS_ERROR, &c->flags); + set_bit(BCH_FS_error, &c->flags); switch (c->opts.errors) { case BCH_ON_ERROR_continue: @@ -27,11 +26,8 @@ bool bch2_inconsistent_error(struct bch_fs *c) void bch2_topology_error(struct bch_fs *c) { - if (!test_bit(BCH_FS_TOPOLOGY_REPAIR_DONE, &c->flags)) - return; - - set_bit(BCH_FS_TOPOLOGY_ERROR, &c->flags); - if (test_bit(BCH_FS_FSCK_DONE, &c->flags)) + set_bit(BCH_FS_topology_error, &c->flags); + if (test_bit(BCH_FS_fsck_done, &c->flags)) bch2_inconsistent_error(c); } @@ -60,50 +56,95 @@ void bch2_io_error_work(struct work_struct *work) up_write(&c->state_lock); } -void bch2_io_error(struct bch_dev *ca) +void bch2_io_error(struct bch_dev *ca, enum bch_member_error_type type) { + atomic64_inc(&ca->errors[type]); //queue_work(system_long_wq, &ca->io_error_work); } +enum ask_yn { + YN_NO, + YN_YES, + YN_ALLNO, + YN_ALLYES, +}; + #ifdef __KERNEL__ -#define ask_yn() false +#define bch2_fsck_ask_yn() YN_NO #else + #include "tools-util.h" + +enum ask_yn bch2_fsck_ask_yn(void) +{ + char *buf = NULL; + size_t buflen = 0; + bool ret; + + while (true) { + fputs(" (y,n, or Y,N for all errors of this type) ", stdout); + fflush(stdout); + + if (getline(&buf, &buflen, stdin) < 0) + die("error reading from standard input"); + + strim(buf); + if (strlen(buf) != 1) + continue; + + switch (buf[0]) { + case 'n': + return YN_NO; + case 'y': + return YN_YES; + case 'N': + return YN_ALLNO; + case 'Y': + return YN_ALLYES; + } + } + + free(buf); + return ret; +} + #endif static struct fsck_err_state *fsck_err_get(struct bch_fs *c, const char *fmt) { struct fsck_err_state *s; - if (test_bit(BCH_FS_FSCK_DONE, &c->flags)) + if (test_bit(BCH_FS_fsck_done, &c->flags)) return NULL; - list_for_each_entry(s, &c->fsck_errors, list) + list_for_each_entry(s, &c->fsck_error_msgs, list) if (s->fmt == fmt) { /* * move it to the head of the list: repeated fsck errors * are common */ - list_move(&s->list, &c->fsck_errors); + list_move(&s->list, &c->fsck_error_msgs); return s; } s = kzalloc(sizeof(*s), GFP_NOFS); if (!s) { - if (!c->fsck_alloc_err) + if (!c->fsck_alloc_msgs_err) bch_err(c, "kmalloc err, cannot ratelimit fsck errs"); - c->fsck_alloc_err = true; + c->fsck_alloc_msgs_err = true; return NULL; } INIT_LIST_HEAD(&s->list); s->fmt = fmt; - s->buf = PRINTBUF; - list_add(&s->list, &c->fsck_errors); + list_add(&s->list, &c->fsck_error_msgs); return s; } -int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...) +int bch2_fsck_err(struct bch_fs *c, + enum bch_fsck_flags flags, + enum bch_sb_error_id err, + const char *fmt, ...) { struct fsck_err_state *s = NULL; va_list args; @@ -111,9 +152,30 @@ int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...) struct printbuf buf = PRINTBUF, *out = &buf; int ret = -BCH_ERR_fsck_ignore; - mutex_lock(&c->fsck_error_lock); + bch2_sb_error_count(c, err); + + va_start(args, fmt); + prt_vprintf(out, fmt, args); + va_end(args); + + mutex_lock(&c->fsck_error_msgs_lock); s = fsck_err_get(c, fmt); if (s) { + /* + * We may be called multiple times for the same error on + * transaction restart - this memoizes instead of asking the user + * multiple times for the same error: + */ + if (s->last_msg && !strcmp(buf.buf, s->last_msg)) { + ret = s->ret; + mutex_unlock(&c->fsck_error_msgs_lock); + printbuf_exit(&buf); + return ret; + } + + kfree(s->last_msg); + s->last_msg = kstrdup(buf.buf, GFP_KERNEL); + if (c->opts.ratelimit_errors && !(flags & FSCK_NO_RATELIMIT) && s->nr >= FSCK_ERR_RATELIMIT_NR) { @@ -123,8 +185,6 @@ int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...) print = false; } - printbuf_reset(&s->buf); - out = &s->buf; s->nr++; } @@ -133,11 +193,7 @@ int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...) prt_printf(out, bch2_log_msg(c, "")); #endif - va_start(args, fmt); - prt_vprintf(out, fmt, args); - va_end(args); - - if (test_bit(BCH_FS_FSCK_DONE, &c->flags)) { + if (test_bit(BCH_FS_fsck_done, &c->flags)) { if (c->opts.errors != BCH_ON_ERROR_continue || !(flags & (FSCK_CAN_FIX|FSCK_CAN_IGNORE))) { prt_str(out, ", shutting down"); @@ -150,18 +206,32 @@ int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...) prt_str(out, ", continuing"); ret = -BCH_ERR_fsck_ignore; } - } else if (c->opts.fix_errors == FSCK_OPT_EXIT) { + } else if (c->opts.fix_errors == FSCK_FIX_exit) { prt_str(out, ", exiting"); ret = -BCH_ERR_fsck_errors_not_fixed; } else if (flags & FSCK_CAN_FIX) { - if (c->opts.fix_errors == FSCK_OPT_ASK) { + int fix = s && s->fix + ? s->fix + : c->opts.fix_errors; + + if (fix == FSCK_FIX_ask) { + int ask; + prt_str(out, ": fix?"); bch2_print_string_as_lines(KERN_ERR, out->buf); print = false; - ret = ask_yn() + + ask = bch2_fsck_ask_yn(); + + if (ask >= YN_ALLNO && s) + s->fix = ask == YN_ALLNO + ? FSCK_FIX_no + : FSCK_FIX_yes; + + ret = ask & 1 ? -BCH_ERR_fsck_fix : -BCH_ERR_fsck_ignore; - } else if (c->opts.fix_errors == FSCK_OPT_YES || + } else if (fix == FSCK_FIX_yes || (c->opts.nochanges && !(flags & FSCK_CAN_IGNORE))) { prt_str(out, ", fixing"); @@ -176,21 +246,24 @@ int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...) } if (ret == -BCH_ERR_fsck_ignore && - (c->opts.fix_errors == FSCK_OPT_EXIT || + (c->opts.fix_errors == FSCK_FIX_exit || !(flags & FSCK_CAN_IGNORE))) ret = -BCH_ERR_fsck_errors_not_fixed; if (print) bch2_print_string_as_lines(KERN_ERR, out->buf); - if (!test_bit(BCH_FS_FSCK_DONE, &c->flags) && + if (!test_bit(BCH_FS_fsck_done, &c->flags) && (ret != -BCH_ERR_fsck_fix && ret != -BCH_ERR_fsck_ignore)) bch_err(c, "Unable to continue, halting"); else if (suppressing) bch_err(c, "Ratelimiting new instances of previous error"); - mutex_unlock(&c->fsck_error_lock); + if (s) + s->ret = ret; + + mutex_unlock(&c->fsck_error_msgs_lock); printbuf_exit(&buf); @@ -198,10 +271,10 @@ int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...) bch2_inconsistent_error(c); if (ret == -BCH_ERR_fsck_fix) { - set_bit(BCH_FS_ERRORS_FIXED, &c->flags); + set_bit(BCH_FS_errors_fixed, &c->flags); } else { - set_bit(BCH_FS_ERRORS_NOT_FIXED, &c->flags); - set_bit(BCH_FS_ERROR, &c->flags); + set_bit(BCH_FS_errors_not_fixed, &c->flags); + set_bit(BCH_FS_error, &c->flags); } return ret; @@ -211,16 +284,16 @@ void bch2_flush_fsck_errs(struct bch_fs *c) { struct fsck_err_state *s, *n; - mutex_lock(&c->fsck_error_lock); + mutex_lock(&c->fsck_error_msgs_lock); - list_for_each_entry_safe(s, n, &c->fsck_errors, list) { - if (s->ratelimited) - bch_err(c, "Saw %llu errors like:\n %s", s->nr, s->buf.buf); + list_for_each_entry_safe(s, n, &c->fsck_error_msgs, list) { + if (s->ratelimited && s->last_msg) + bch_err(c, "Saw %llu errors like:\n %s", s->nr, s->last_msg); list_del(&s->list); - printbuf_exit(&s->buf); + kfree(s->last_msg); kfree(s); } - mutex_unlock(&c->fsck_error_lock); + mutex_unlock(&c->fsck_error_msgs_lock); }