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, 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				   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
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, 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
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(&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	unsigned i;
115
116	mutex_lock(&c->fsck_error_counts_lock);
117
118	dst = bch2_sb_field_resize(&c->disk_sb, errors,
119				   bch2_sb_field_errors_u64s(src->nr));
120
121	if (!dst)
122		goto err;
123
124	for (i = 0; i < src->nr; i++) {
125		SET_BCH_SB_ERROR_ENTRY_ID(&dst->entries[i], src->data[i].id);
126		SET_BCH_SB_ERROR_ENTRY_NR(&dst->entries[i], src->data[i].nr);
127		dst->entries[i].last_error_time = cpu_to_le64(src->data[i].last_error_time);
128	}
129
130err:
131	mutex_unlock(&c->fsck_error_counts_lock);
132}
133
134static int bch2_sb_errors_to_cpu(struct bch_fs *c)
135{
136	struct bch_sb_field_errors *src = bch2_sb_field_get(c->disk_sb.sb, errors);
137	bch_sb_errors_cpu *dst = &c->fsck_error_counts;
138	unsigned i, nr = bch2_sb_field_errors_nr_entries(src);
139	int ret;
140
141	if (!nr)
142		return 0;
143
144	mutex_lock(&c->fsck_error_counts_lock);
145	ret = darray_make_room(dst, nr);
146	if (ret)
147		goto err;
148
149	dst->nr = nr;
150
151	for (i = 0; i < nr; i++) {
152		dst->data[i].id = BCH_SB_ERROR_ENTRY_ID(&src->entries[i]);
153		dst->data[i].nr = BCH_SB_ERROR_ENTRY_NR(&src->entries[i]);
154		dst->data[i].last_error_time = le64_to_cpu(src->entries[i].last_error_time);
155	}
156err:
157	mutex_unlock(&c->fsck_error_counts_lock);
158
159	return ret;
160}
161
162void bch2_fs_sb_errors_exit(struct bch_fs *c)
163{
164	darray_exit(&c->fsck_error_counts);
165}
166
167void bch2_fs_sb_errors_init_early(struct bch_fs *c)
168{
169	mutex_init(&c->fsck_error_counts_lock);
170	darray_init(&c->fsck_error_counts);
171}
172
173int bch2_fs_sb_errors_init(struct bch_fs *c)
174{
175	return bch2_sb_errors_to_cpu(c);
176}
177