xref: /linux/fs/bcachefs/sb-errors.c (revision 9a95c5bfbf02a0a7f5983280fe284a0ff0836c34)
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, 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 				   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 
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 
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(&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(&dst->entries[i], src->data[i].id);
123 		SET_BCH_SB_ERROR_ENTRY_NR(&dst->entries[i], 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(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(&src->entries[i]);
147 		dst->data[i].nr = BCH_SB_ERROR_ENTRY_NR(&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(&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