1 // SPDX-License-Identifier: GPL-2.0 2 3 #include "bcachefs.h" 4 #include "sb-errors.h" 5 #include "super-io.h" 6 7 const char * const bch2_sb_error_strs[] = { 8 #define x(t, n, ...) [n] = #t, 9 BCH_SB_ERRS() 10 #undef x 11 }; 12 13 void bch2_sb_error_id_to_text(struct printbuf *out, enum bch_sb_error_id id) 14 { 15 if (id < BCH_FSCK_ERR_MAX) 16 prt_str(out, bch2_sb_error_strs[id]); 17 else 18 prt_printf(out, "(unknown error %u)", id); 19 } 20 21 static inline unsigned bch2_sb_field_errors_nr_entries(struct bch_sb_field_errors *e) 22 { 23 return bch2_sb_field_nr_entries(e); 24 } 25 26 static inline unsigned bch2_sb_field_errors_u64s(unsigned nr) 27 { 28 return (sizeof(struct bch_sb_field_errors) + 29 sizeof(struct bch_sb_field_error_entry) * nr) / sizeof(u64); 30 } 31 32 static int bch2_sb_errors_validate(struct bch_sb *sb, struct bch_sb_field *f, 33 enum bch_validate_flags flags, struct printbuf *err) 34 { 35 struct bch_sb_field_errors *e = field_to_type(f, errors); 36 unsigned i, nr = bch2_sb_field_errors_nr_entries(e); 37 38 for (i = 0; i < nr; i++) { 39 if (!BCH_SB_ERROR_ENTRY_NR(&e->entries[i])) { 40 prt_printf(err, "entry with count 0 (id "); 41 bch2_sb_error_id_to_text(err, BCH_SB_ERROR_ENTRY_ID(&e->entries[i])); 42 prt_printf(err, ")"); 43 return -BCH_ERR_invalid_sb_errors; 44 } 45 46 if (i + 1 < nr && 47 BCH_SB_ERROR_ENTRY_ID(&e->entries[i]) >= 48 BCH_SB_ERROR_ENTRY_ID(&e->entries[i + 1])) { 49 prt_printf(err, "entries out of order"); 50 return -BCH_ERR_invalid_sb_errors; 51 } 52 } 53 54 return 0; 55 } 56 57 static void bch2_sb_errors_to_text(struct printbuf *out, struct bch_sb *sb, 58 struct bch_sb_field *f) 59 { 60 struct bch_sb_field_errors *e = field_to_type(f, errors); 61 unsigned i, nr = bch2_sb_field_errors_nr_entries(e); 62 63 if (out->nr_tabstops <= 1) 64 printbuf_tabstop_push(out, 16); 65 66 for (i = 0; i < nr; i++) { 67 bch2_sb_error_id_to_text(out, BCH_SB_ERROR_ENTRY_ID(&e->entries[i])); 68 prt_tab(out); 69 prt_u64(out, BCH_SB_ERROR_ENTRY_NR(&e->entries[i])); 70 prt_tab(out); 71 bch2_prt_datetime(out, le64_to_cpu(e->entries[i].last_error_time)); 72 prt_newline(out); 73 } 74 } 75 76 const struct bch_sb_field_ops bch_sb_field_ops_errors = { 77 .validate = bch2_sb_errors_validate, 78 .to_text = bch2_sb_errors_to_text, 79 }; 80 81 void bch2_fs_errors_to_text(struct printbuf *out, struct bch_fs *c) 82 { 83 if (out->nr_tabstops < 1) 84 printbuf_tabstop_push(out, 48); 85 if (out->nr_tabstops < 2) 86 printbuf_tabstop_push(out, 8); 87 if (out->nr_tabstops < 3) 88 printbuf_tabstop_push(out, 16); 89 90 guard(mutex)(&c->fsck_error_counts_lock); 91 92 bch_sb_errors_cpu *e = &c->fsck_error_counts; 93 darray_for_each(*e, i) { 94 bch2_sb_error_id_to_text(out, i->id); 95 prt_tab(out); 96 prt_u64(out, i->nr); 97 prt_tab(out); 98 bch2_prt_datetime(out, i->last_error_time); 99 prt_newline(out); 100 } 101 } 102 103 void bch2_sb_error_count(struct bch_fs *c, enum bch_sb_error_id err) 104 { 105 bch_sb_errors_cpu *e = &c->fsck_error_counts; 106 struct bch_sb_error_entry_cpu n = { 107 .id = err, 108 .nr = 1, 109 .last_error_time = ktime_get_real_seconds() 110 }; 111 unsigned i; 112 113 mutex_lock(&c->fsck_error_counts_lock); 114 for (i = 0; i < e->nr; i++) { 115 if (err == e->data[i].id) { 116 e->data[i].nr++; 117 e->data[i].last_error_time = n.last_error_time; 118 goto out; 119 } 120 if (err < e->data[i].id) 121 break; 122 } 123 124 if (darray_make_room(e, 1)) 125 goto out; 126 127 darray_insert_item(e, i, n); 128 out: 129 mutex_unlock(&c->fsck_error_counts_lock); 130 } 131 132 void bch2_sb_errors_from_cpu(struct bch_fs *c) 133 { 134 bch_sb_errors_cpu *src = &c->fsck_error_counts; 135 struct bch_sb_field_errors *dst; 136 unsigned i; 137 138 mutex_lock(&c->fsck_error_counts_lock); 139 140 dst = bch2_sb_field_resize(&c->disk_sb, errors, 141 bch2_sb_field_errors_u64s(src->nr)); 142 143 if (!dst) 144 goto err; 145 146 for (i = 0; i < src->nr; i++) { 147 SET_BCH_SB_ERROR_ENTRY_ID(&dst->entries[i], src->data[i].id); 148 SET_BCH_SB_ERROR_ENTRY_NR(&dst->entries[i], src->data[i].nr); 149 dst->entries[i].last_error_time = cpu_to_le64(src->data[i].last_error_time); 150 } 151 152 err: 153 mutex_unlock(&c->fsck_error_counts_lock); 154 } 155 156 static int bch2_sb_errors_to_cpu(struct bch_fs *c) 157 { 158 struct bch_sb_field_errors *src = bch2_sb_field_get(c->disk_sb.sb, errors); 159 bch_sb_errors_cpu *dst = &c->fsck_error_counts; 160 unsigned i, nr = bch2_sb_field_errors_nr_entries(src); 161 int ret; 162 163 if (!nr) 164 return 0; 165 166 mutex_lock(&c->fsck_error_counts_lock); 167 ret = darray_make_room(dst, nr); 168 if (ret) 169 goto err; 170 171 dst->nr = nr; 172 173 for (i = 0; i < nr; i++) { 174 dst->data[i].id = BCH_SB_ERROR_ENTRY_ID(&src->entries[i]); 175 dst->data[i].nr = BCH_SB_ERROR_ENTRY_NR(&src->entries[i]); 176 dst->data[i].last_error_time = le64_to_cpu(src->entries[i].last_error_time); 177 } 178 err: 179 mutex_unlock(&c->fsck_error_counts_lock); 180 181 return ret; 182 } 183 184 void bch2_fs_sb_errors_exit(struct bch_fs *c) 185 { 186 darray_exit(&c->fsck_error_counts); 187 } 188 189 void bch2_fs_sb_errors_init_early(struct bch_fs *c) 190 { 191 mutex_init(&c->fsck_error_counts_lock); 192 darray_init(&c->fsck_error_counts); 193 } 194 195 int bch2_fs_sb_errors_init(struct bch_fs *c) 196 { 197 return bch2_sb_errors_to_cpu(c); 198 } 199