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
bch2_sb_error_id_to_text(struct printbuf * out,enum bch_sb_error_id id)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
bch2_sb_field_errors_nr_entries(struct bch_sb_field_errors * e)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
bch2_sb_field_errors_u64s(unsigned nr)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
bch2_sb_errors_validate(struct bch_sb * sb,struct bch_sb_field * f,enum bch_validate_flags flags,struct printbuf * err)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
bch2_sb_errors_to_text(struct printbuf * out,struct bch_sb * sb,struct bch_sb_field * f)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
bch2_fs_errors_to_text(struct printbuf * out,struct bch_fs * c)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
bch2_sb_error_count(struct bch_fs * c,enum bch_sb_error_id err)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
bch2_sb_errors_from_cpu(struct bch_fs * c)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
bch2_sb_errors_to_cpu(struct bch_fs * c)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
bch2_fs_sb_errors_exit(struct bch_fs * c)184 void bch2_fs_sb_errors_exit(struct bch_fs *c)
185 {
186 darray_exit(&c->fsck_error_counts);
187 }
188
bch2_fs_sb_errors_init_early(struct bch_fs * c)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
bch2_fs_sb_errors_init(struct bch_fs * c)195 int bch2_fs_sb_errors_init(struct bch_fs *c)
196 {
197 return bch2_sb_errors_to_cpu(c);
198 }
199