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 | NULL |
11 | }; |
12 | |
13 | static void bch2_sb_error_id_to_text(struct printbuf *out, enum bch_sb_error_id id) |
14 | { |
15 | if (id < BCH_SB_ERR_MAX) |
16 | prt_str(out, str: 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 | 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(k: &e->entries[i])) { |
40 | prt_printf(err, "entry with count 0 (id " ); |
41 | bch2_sb_error_id_to_text(out: err, id: BCH_SB_ERROR_ENTRY_ID(k: &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(k: &e->entries[i]) >= |
48 | BCH_SB_ERROR_ENTRY_ID(k: &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, id: BCH_SB_ERROR_ENTRY_ID(k: &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_sb_error_count(struct bch_fs *c, enum bch_sb_error_id err) |
82 | { |
83 | bch_sb_errors_cpu *e = &c->fsck_error_counts; |
84 | struct bch_sb_error_entry_cpu n = { |
85 | .id = err, |
86 | .nr = 1, |
87 | .last_error_time = ktime_get_real_seconds() |
88 | }; |
89 | unsigned i; |
90 | |
91 | mutex_lock(&c->fsck_error_counts_lock); |
92 | for (i = 0; i < e->nr; i++) { |
93 | if (err == e->data[i].id) { |
94 | e->data[i].nr++; |
95 | e->data[i].last_error_time = n.last_error_time; |
96 | goto out; |
97 | } |
98 | if (err < e->data[i].id) |
99 | break; |
100 | } |
101 | |
102 | if (darray_make_room(e, 1)) |
103 | goto out; |
104 | |
105 | darray_insert_item(e, i, n); |
106 | out: |
107 | mutex_unlock(lock: &c->fsck_error_counts_lock); |
108 | } |
109 | |
110 | void bch2_sb_errors_from_cpu(struct bch_fs *c) |
111 | { |
112 | bch_sb_errors_cpu *src = &c->fsck_error_counts; |
113 | struct bch_sb_field_errors *dst = |
114 | bch2_sb_field_resize(&c->disk_sb, errors, |
115 | bch2_sb_field_errors_u64s(src->nr)); |
116 | unsigned i; |
117 | |
118 | if (!dst) |
119 | return; |
120 | |
121 | for (i = 0; i < src->nr; i++) { |
122 | SET_BCH_SB_ERROR_ENTRY_ID(k: &dst->entries[i], v: src->data[i].id); |
123 | SET_BCH_SB_ERROR_ENTRY_NR(k: &dst->entries[i], v: src->data[i].nr); |
124 | dst->entries[i].last_error_time = cpu_to_le64(src->data[i].last_error_time); |
125 | } |
126 | } |
127 | |
128 | static int bch2_sb_errors_to_cpu(struct bch_fs *c) |
129 | { |
130 | struct bch_sb_field_errors *src = bch2_sb_field_get(c->disk_sb.sb, errors); |
131 | bch_sb_errors_cpu *dst = &c->fsck_error_counts; |
132 | unsigned i, nr = bch2_sb_field_errors_nr_entries(e: src); |
133 | int ret; |
134 | |
135 | if (!nr) |
136 | return 0; |
137 | |
138 | mutex_lock(&c->fsck_error_counts_lock); |
139 | ret = darray_make_room(dst, nr); |
140 | if (ret) |
141 | goto err; |
142 | |
143 | dst->nr = nr; |
144 | |
145 | for (i = 0; i < nr; i++) { |
146 | dst->data[i].id = BCH_SB_ERROR_ENTRY_ID(k: &src->entries[i]); |
147 | dst->data[i].nr = BCH_SB_ERROR_ENTRY_NR(k: &src->entries[i]); |
148 | dst->data[i].last_error_time = le64_to_cpu(src->entries[i].last_error_time); |
149 | } |
150 | err: |
151 | mutex_unlock(lock: &c->fsck_error_counts_lock); |
152 | |
153 | return ret; |
154 | } |
155 | |
156 | void bch2_fs_sb_errors_exit(struct bch_fs *c) |
157 | { |
158 | darray_exit(&c->fsck_error_counts); |
159 | } |
160 | |
161 | void bch2_fs_sb_errors_init_early(struct bch_fs *c) |
162 | { |
163 | mutex_init(&c->fsck_error_counts_lock); |
164 | darray_init(&c->fsck_error_counts); |
165 | } |
166 | |
167 | int bch2_fs_sb_errors_init(struct bch_fs *c) |
168 | { |
169 | return bch2_sb_errors_to_cpu(c); |
170 | } |
171 | |