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