1// SPDX-License-Identifier: GPL-2.0
2
3#include "bcachefs.h"
4#include "sb-errors.h"
5#include "super-io.h"
6
7const char * const bch2_sb_error_strs[] = {
8#define x(t, n, ...) [n] = #t,
9 BCH_SB_ERRS()
10 NULL
11};
12
13static 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
21static 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
26static 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
32static 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
57static 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
76const 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
81void 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);
106out:
107 mutex_unlock(lock: &c->fsck_error_counts_lock);
108}
109
110void 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
128static 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 }
150err:
151 mutex_unlock(lock: &c->fsck_error_counts_lock);
152
153 return ret;
154}
155
156void bch2_fs_sb_errors_exit(struct bch_fs *c)
157{
158 darray_exit(&c->fsck_error_counts);
159}
160
161void 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
167int bch2_fs_sb_errors_init(struct bch_fs *c)
168{
169 return bch2_sb_errors_to_cpu(c);
170}
171

source code of linux/fs/bcachefs/sb-errors.c