xref: /linux/fs/bcachefs/sb-errors.c (revision 16e5ac127d8d18adf85fe5ba847d77b58d1ed418)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include "bcachefs.h"
4 #include "sb-errors.h"
5 #include "super-io.h"
6 
7 static 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, 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 e
24 		? (bch2_sb_field_bytes(&e->field) - sizeof(*e)) / sizeof(e->entries[0])
25 		: 0;
26 }
27 
28 static inline unsigned bch2_sb_field_errors_u64s(unsigned nr)
29 {
30 	return (sizeof(struct bch_sb_field_errors) +
31 		sizeof(struct bch_sb_field_error_entry) * nr) / sizeof(u64);
32 }
33 
34 static int bch2_sb_errors_validate(struct bch_sb *sb, struct bch_sb_field *f,
35 				   struct printbuf *err)
36 {
37 	struct bch_sb_field_errors *e = field_to_type(f, errors);
38 	unsigned i, nr = bch2_sb_field_errors_nr_entries(e);
39 
40 	for (i = 0; i < nr; i++) {
41 		if (!BCH_SB_ERROR_ENTRY_NR(&e->entries[i])) {
42 			prt_printf(err, "entry with count 0 (id ");
43 			bch2_sb_error_id_to_text(err, BCH_SB_ERROR_ENTRY_ID(&e->entries[i]));
44 			prt_printf(err, ")");
45 			return -BCH_ERR_invalid_sb_errors;
46 		}
47 
48 		if (i + 1 < nr &&
49 		    BCH_SB_ERROR_ENTRY_ID(&e->entries[i]) >=
50 		    BCH_SB_ERROR_ENTRY_ID(&e->entries[i + 1])) {
51 			prt_printf(err, "entries out of order");
52 			return -BCH_ERR_invalid_sb_errors;
53 		}
54 	}
55 
56 	return 0;
57 }
58 
59 static void bch2_sb_errors_to_text(struct printbuf *out, struct bch_sb *sb,
60 				   struct bch_sb_field *f)
61 {
62 	struct bch_sb_field_errors *e = field_to_type(f, errors);
63 	unsigned i, nr = bch2_sb_field_errors_nr_entries(e);
64 
65 	if (out->nr_tabstops <= 1)
66 		printbuf_tabstop_push(out, 16);
67 
68 	for (i = 0; i < nr; i++) {
69 		bch2_sb_error_id_to_text(out, BCH_SB_ERROR_ENTRY_ID(&e->entries[i]));
70 		prt_tab(out);
71 		prt_u64(out, BCH_SB_ERROR_ENTRY_NR(&e->entries[i]));
72 		prt_tab(out);
73 		bch2_prt_datetime(out, le64_to_cpu(e->entries[i].last_error_time));
74 		prt_newline(out);
75 	}
76 }
77 
78 const struct bch_sb_field_ops bch_sb_field_ops_errors = {
79 	.validate	= bch2_sb_errors_validate,
80 	.to_text	= bch2_sb_errors_to_text,
81 };
82 
83 void bch2_sb_error_count(struct bch_fs *c, enum bch_sb_error_id err)
84 {
85 	bch_sb_errors_cpu *e = &c->fsck_error_counts;
86 	struct bch_sb_error_entry_cpu n = {
87 		.id = err,
88 		.nr = 1,
89 		.last_error_time = ktime_get_real_seconds()
90 	};
91 	unsigned i;
92 
93 	mutex_lock(&c->fsck_error_counts_lock);
94 	for (i = 0; i < e->nr; i++) {
95 		if (err == e->data[i].id) {
96 			e->data[i].nr++;
97 			e->data[i].last_error_time = n.last_error_time;
98 			goto out;
99 		}
100 		if (err < e->data[i].id)
101 			break;
102 	}
103 
104 	if (darray_make_room(e, 1))
105 		goto out;
106 
107 	darray_insert_item(e, i, n);
108 out:
109 	mutex_unlock(&c->fsck_error_counts_lock);
110 }
111 
112 void bch2_sb_errors_from_cpu(struct bch_fs *c)
113 {
114 	bch_sb_errors_cpu *src = &c->fsck_error_counts;
115 	struct bch_sb_field_errors *dst =
116 		bch2_sb_field_resize(&c->disk_sb, errors,
117 				     bch2_sb_field_errors_u64s(src->nr));
118 	unsigned i;
119 
120 	if (!dst)
121 		return;
122 
123 	for (i = 0; i < src->nr; i++) {
124 		SET_BCH_SB_ERROR_ENTRY_ID(&dst->entries[i], src->data[i].id);
125 		SET_BCH_SB_ERROR_ENTRY_NR(&dst->entries[i], src->data[i].nr);
126 		dst->entries[i].last_error_time = cpu_to_le64(src->data[i].last_error_time);
127 	}
128 }
129 
130 static int bch2_sb_errors_to_cpu(struct bch_fs *c)
131 {
132 	struct bch_sb_field_errors *src = bch2_sb_field_get(c->disk_sb.sb, errors);
133 	bch_sb_errors_cpu *dst = &c->fsck_error_counts;
134 	unsigned i, nr = bch2_sb_field_errors_nr_entries(src);
135 	int ret;
136 
137 	if (!nr)
138 		return 0;
139 
140 	mutex_lock(&c->fsck_error_counts_lock);
141 	ret = darray_make_room(dst, nr);
142 	if (ret)
143 		goto err;
144 
145 	dst->nr = nr;
146 
147 	for (i = 0; i < nr; i++) {
148 		dst->data[i].id = BCH_SB_ERROR_ENTRY_ID(&src->entries[i]);
149 		dst->data[i].nr = BCH_SB_ERROR_ENTRY_NR(&src->entries[i]);
150 		dst->data[i].last_error_time = le64_to_cpu(src->entries[i].last_error_time);
151 	}
152 err:
153 	mutex_unlock(&c->fsck_error_counts_lock);
154 
155 	return ret;
156 }
157 
158 void bch2_fs_sb_errors_exit(struct bch_fs *c)
159 {
160 	darray_exit(&c->fsck_error_counts);
161 }
162 
163 void bch2_fs_sb_errors_init_early(struct bch_fs *c)
164 {
165 	mutex_init(&c->fsck_error_counts_lock);
166 	darray_init(&c->fsck_error_counts);
167 }
168 
169 int bch2_fs_sb_errors_init(struct bch_fs *c)
170 {
171 	return bch2_sb_errors_to_cpu(c);
172 }
173