1 // SPDX-License-Identifier: GPL-2.0
2
3 #include "bcachefs.h"
4 #include "journal_sb.h"
5 #include "darray.h"
6
7 #include <linux/sort.h>
8
9 /* BCH_SB_FIELD_journal: */
10
u64_cmp(const void * _l,const void * _r)11 static int u64_cmp(const void *_l, const void *_r)
12 {
13 const u64 *l = _l;
14 const u64 *r = _r;
15
16 return cmp_int(*l, *r);
17 }
18
bch2_sb_journal_validate(struct bch_sb * sb,struct bch_sb_field * f,enum bch_validate_flags flags,struct printbuf * err)19 static int bch2_sb_journal_validate(struct bch_sb *sb, struct bch_sb_field *f,
20 enum bch_validate_flags flags, struct printbuf *err)
21 {
22 struct bch_sb_field_journal *journal = field_to_type(f, journal);
23 struct bch_member m = bch2_sb_member_get(sb, sb->dev_idx);
24 int ret = -BCH_ERR_invalid_sb_journal;
25 unsigned nr;
26 unsigned i;
27 u64 *b;
28
29 nr = bch2_nr_journal_buckets(journal);
30 if (!nr)
31 return 0;
32
33 b = kmalloc_array(nr, sizeof(u64), GFP_KERNEL);
34 if (!b)
35 return -BCH_ERR_ENOMEM_sb_journal_validate;
36
37 for (i = 0; i < nr; i++)
38 b[i] = le64_to_cpu(journal->buckets[i]);
39
40 sort(b, nr, sizeof(u64), u64_cmp, NULL);
41
42 if (!b[0]) {
43 prt_printf(err, "journal bucket at sector 0");
44 goto err;
45 }
46
47 if (b[0] < le16_to_cpu(m.first_bucket)) {
48 prt_printf(err, "journal bucket %llu before first bucket %u",
49 b[0], le16_to_cpu(m.first_bucket));
50 goto err;
51 }
52
53 if (b[nr - 1] >= le64_to_cpu(m.nbuckets)) {
54 prt_printf(err, "journal bucket %llu past end of device (nbuckets %llu)",
55 b[nr - 1], le64_to_cpu(m.nbuckets));
56 goto err;
57 }
58
59 for (i = 0; i + 1 < nr; i++)
60 if (b[i] == b[i + 1]) {
61 prt_printf(err, "duplicate journal buckets %llu", b[i]);
62 goto err;
63 }
64
65 ret = 0;
66 err:
67 kfree(b);
68 return ret;
69 }
70
bch2_sb_journal_to_text(struct printbuf * out,struct bch_sb * sb,struct bch_sb_field * f)71 static void bch2_sb_journal_to_text(struct printbuf *out, struct bch_sb *sb,
72 struct bch_sb_field *f)
73 {
74 struct bch_sb_field_journal *journal = field_to_type(f, journal);
75 unsigned i, nr = bch2_nr_journal_buckets(journal);
76
77 prt_printf(out, "Buckets: ");
78 for (i = 0; i < nr; i++)
79 prt_printf(out, " %llu", le64_to_cpu(journal->buckets[i]));
80 prt_newline(out);
81 }
82
83 const struct bch_sb_field_ops bch_sb_field_ops_journal = {
84 .validate = bch2_sb_journal_validate,
85 .to_text = bch2_sb_journal_to_text,
86 };
87
88 struct u64_range {
89 u64 start;
90 u64 end;
91 };
92
u64_range_cmp(const void * _l,const void * _r)93 static int u64_range_cmp(const void *_l, const void *_r)
94 {
95 const struct u64_range *l = _l;
96 const struct u64_range *r = _r;
97
98 return cmp_int(l->start, r->start);
99 }
100
bch2_sb_journal_v2_validate(struct bch_sb * sb,struct bch_sb_field * f,enum bch_validate_flags flags,struct printbuf * err)101 static int bch2_sb_journal_v2_validate(struct bch_sb *sb, struct bch_sb_field *f,
102 enum bch_validate_flags flags, struct printbuf *err)
103 {
104 struct bch_sb_field_journal_v2 *journal = field_to_type(f, journal_v2);
105 struct bch_member m = bch2_sb_member_get(sb, sb->dev_idx);
106 int ret = -BCH_ERR_invalid_sb_journal;
107 u64 sum = 0;
108 unsigned nr;
109 unsigned i;
110 struct u64_range *b;
111
112 nr = bch2_sb_field_journal_v2_nr_entries(journal);
113 if (!nr)
114 return 0;
115
116 b = kmalloc_array(nr, sizeof(*b), GFP_KERNEL);
117 if (!b)
118 return -BCH_ERR_ENOMEM_sb_journal_v2_validate;
119
120 for (i = 0; i < nr; i++) {
121 b[i].start = le64_to_cpu(journal->d[i].start);
122 b[i].end = b[i].start + le64_to_cpu(journal->d[i].nr);
123
124 if (b[i].end <= b[i].start) {
125 prt_printf(err, "journal buckets entry with bad nr: %llu+%llu",
126 le64_to_cpu(journal->d[i].start),
127 le64_to_cpu(journal->d[i].nr));
128 goto err;
129 }
130
131 sum += le64_to_cpu(journal->d[i].nr);
132 }
133
134 sort(b, nr, sizeof(*b), u64_range_cmp, NULL);
135
136 if (!b[0].start) {
137 prt_printf(err, "journal bucket at sector 0");
138 goto err;
139 }
140
141 if (b[0].start < le16_to_cpu(m.first_bucket)) {
142 prt_printf(err, "journal bucket %llu before first bucket %u",
143 b[0].start, le16_to_cpu(m.first_bucket));
144 goto err;
145 }
146
147 if (b[nr - 1].end > le64_to_cpu(m.nbuckets)) {
148 prt_printf(err, "journal bucket %llu past end of device (nbuckets %llu)",
149 b[nr - 1].end - 1, le64_to_cpu(m.nbuckets));
150 goto err;
151 }
152
153 for (i = 0; i + 1 < nr; i++) {
154 if (b[i].end > b[i + 1].start) {
155 prt_printf(err, "duplicate journal buckets in ranges %llu-%llu, %llu-%llu",
156 b[i].start, b[i].end, b[i + 1].start, b[i + 1].end);
157 goto err;
158 }
159 }
160
161 if (sum > UINT_MAX) {
162 prt_printf(err, "too many journal buckets: %llu > %u", sum, UINT_MAX);
163 goto err;
164 }
165
166 ret = 0;
167 err:
168 kfree(b);
169 return ret;
170 }
171
bch2_sb_journal_v2_to_text(struct printbuf * out,struct bch_sb * sb,struct bch_sb_field * f)172 static void bch2_sb_journal_v2_to_text(struct printbuf *out, struct bch_sb *sb,
173 struct bch_sb_field *f)
174 {
175 struct bch_sb_field_journal_v2 *journal = field_to_type(f, journal_v2);
176 unsigned i, nr = bch2_sb_field_journal_v2_nr_entries(journal);
177
178 prt_printf(out, "Buckets: ");
179 for (i = 0; i < nr; i++)
180 prt_printf(out, " %llu-%llu",
181 le64_to_cpu(journal->d[i].start),
182 le64_to_cpu(journal->d[i].start) + le64_to_cpu(journal->d[i].nr));
183 prt_newline(out);
184 }
185
186 const struct bch_sb_field_ops bch_sb_field_ops_journal_v2 = {
187 .validate = bch2_sb_journal_v2_validate,
188 .to_text = bch2_sb_journal_v2_to_text,
189 };
190
bch2_journal_buckets_to_sb(struct bch_fs * c,struct bch_dev * ca,u64 * buckets,unsigned nr)191 int bch2_journal_buckets_to_sb(struct bch_fs *c, struct bch_dev *ca,
192 u64 *buckets, unsigned nr)
193 {
194 struct bch_sb_field_journal_v2 *j;
195 unsigned i, dst = 0, nr_compacted = 1;
196
197 if (c)
198 lockdep_assert_held(&c->sb_lock);
199
200 if (!nr) {
201 bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal);
202 bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal_v2);
203 return 0;
204 }
205
206 for (i = 0; i + 1 < nr; i++)
207 if (buckets[i] + 1 != buckets[i + 1])
208 nr_compacted++;
209
210 j = bch2_sb_field_resize(&ca->disk_sb, journal_v2,
211 (sizeof(*j) + sizeof(j->d[0]) * nr_compacted) / sizeof(u64));
212 if (!j)
213 return -BCH_ERR_ENOSPC_sb_journal;
214
215 bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal);
216
217 j->d[dst].start = cpu_to_le64(buckets[0]);
218 j->d[dst].nr = cpu_to_le64(1);
219
220 for (i = 1; i < nr; i++) {
221 if (buckets[i] == buckets[i - 1] + 1) {
222 le64_add_cpu(&j->d[dst].nr, 1);
223 } else {
224 dst++;
225 j->d[dst].start = cpu_to_le64(buckets[i]);
226 j->d[dst].nr = cpu_to_le64(1);
227 }
228 }
229
230 BUG_ON(dst + 1 != nr_compacted);
231 return 0;
232 }
233