xref: /linux/fs/bcachefs/sb-errors.c (revision da42b5229b27bb5c0eff3408c92f025e6041dad3)
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