]>
Commit | Line | Data |
---|---|---|
25be2e5d KO |
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); | |
1241df58 | 24 | struct bch_member m = bch2_sb_member_get(sb, sb->dev_idx); |
78c0b75c | 25 | int ret = -BCH_ERR_invalid_sb_journal; |
25be2e5d KO |
26 | unsigned nr; |
27 | unsigned i; | |
28 | u64 *b; | |
29 | ||
30 | nr = bch2_nr_journal_buckets(journal); | |
31 | if (!nr) | |
32 | return 0; | |
33 | ||
3e3e02e6 | 34 | b = kmalloc_array(nr, sizeof(u64), GFP_KERNEL); |
25be2e5d | 35 | if (!b) |
65d48e35 | 36 | return -BCH_ERR_ENOMEM_sb_journal_validate; |
25be2e5d KO |
37 | |
38 | for (i = 0; i < nr; i++) | |
39 | b[i] = le64_to_cpu(journal->buckets[i]); | |
40 | ||
41 | sort(b, nr, sizeof(u64), u64_cmp, NULL); | |
42 | ||
43 | if (!b[0]) { | |
401ec4db | 44 | prt_printf(err, "journal bucket at sector 0"); |
25be2e5d KO |
45 | goto err; |
46 | } | |
47 | ||
1241df58 | 48 | if (b[0] < le16_to_cpu(m.first_bucket)) { |
401ec4db | 49 | prt_printf(err, "journal bucket %llu before first bucket %u", |
1241df58 | 50 | b[0], le16_to_cpu(m.first_bucket)); |
25be2e5d KO |
51 | goto err; |
52 | } | |
53 | ||
1241df58 | 54 | if (b[nr - 1] >= le64_to_cpu(m.nbuckets)) { |
401ec4db | 55 | prt_printf(err, "journal bucket %llu past end of device (nbuckets %llu)", |
1241df58 | 56 | b[nr - 1], le64_to_cpu(m.nbuckets)); |
25be2e5d KO |
57 | goto err; |
58 | } | |
59 | ||
60 | for (i = 0; i + 1 < nr; i++) | |
61 | if (b[i] == b[i + 1]) { | |
401ec4db | 62 | prt_printf(err, "duplicate journal buckets %llu", b[i]); |
25be2e5d KO |
63 | goto err; |
64 | } | |
65 | ||
66 | ret = 0; | |
67 | err: | |
68 | kfree(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(journal); | |
77 | ||
401ec4db | 78 | prt_printf(out, "Buckets: "); |
25be2e5d | 79 | for (i = 0; i < nr; i++) |
401ec4db KO |
80 | prt_printf(out, " %llu", le64_to_cpu(journal->buckets[i])); |
81 | prt_newline(out); | |
25be2e5d KO |
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); | |
1241df58 | 107 | struct bch_member m = bch2_sb_member_get(sb, sb->dev_idx); |
78c0b75c | 108 | int ret = -BCH_ERR_invalid_sb_journal; |
25be2e5d KO |
109 | unsigned nr; |
110 | unsigned i; | |
111 | struct u64_range *b; | |
112 | ||
113 | nr = bch2_sb_field_journal_v2_nr_entries(journal); | |
114 | if (!nr) | |
115 | return 0; | |
116 | ||
3e3e02e6 | 117 | b = kmalloc_array(nr, sizeof(*b), GFP_KERNEL); |
25be2e5d | 118 | if (!b) |
65d48e35 | 119 | return -BCH_ERR_ENOMEM_sb_journal_v2_validate; |
25be2e5d KO |
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(b, nr, sizeof(*b), u64_range_cmp, NULL); | |
127 | ||
128 | if (!b[0].start) { | |
401ec4db | 129 | prt_printf(err, "journal bucket at sector 0"); |
25be2e5d KO |
130 | goto err; |
131 | } | |
132 | ||
1241df58 | 133 | if (b[0].start < le16_to_cpu(m.first_bucket)) { |
401ec4db | 134 | prt_printf(err, "journal bucket %llu before first bucket %u", |
1241df58 | 135 | b[0].start, le16_to_cpu(m.first_bucket)); |
25be2e5d KO |
136 | goto err; |
137 | } | |
138 | ||
1241df58 | 139 | if (b[nr - 1].end > le64_to_cpu(m.nbuckets)) { |
401ec4db | 140 | prt_printf(err, "journal bucket %llu past end of device (nbuckets %llu)", |
1241df58 | 141 | b[nr - 1].end - 1, le64_to_cpu(m.nbuckets)); |
25be2e5d KO |
142 | goto err; |
143 | } | |
144 | ||
145 | for (i = 0; i + 1 < nr; i++) { | |
146 | if (b[i].end > b[i + 1].start) { | |
401ec4db | 147 | prt_printf(err, "duplicate journal buckets in ranges %llu-%llu, %llu-%llu", |
25be2e5d KO |
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(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(journal); | |
164 | ||
401ec4db | 165 | prt_printf(out, "Buckets: "); |
25be2e5d | 166 | for (i = 0; i < nr; i++) |
401ec4db | 167 | prt_printf(out, " %llu-%llu", |
25be2e5d KO |
168 | le64_to_cpu(journal->d[i].start), |
169 | le64_to_cpu(journal->d[i].start) + le64_to_cpu(journal->d[i].nr)); | |
401ec4db | 170 | prt_newline(out); |
25be2e5d KO |
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 | ||
2640faeb KO |
178 | int bch2_journal_buckets_to_sb(struct bch_fs *c, struct bch_dev *ca, |
179 | u64 *buckets, unsigned nr) | |
25be2e5d | 180 | { |
25be2e5d | 181 | struct bch_sb_field_journal_v2 *j; |
2640faeb | 182 | unsigned i, dst = 0, nr_compacted = 1; |
25be2e5d KO |
183 | |
184 | if (c) | |
185 | lockdep_assert_held(&c->sb_lock); | |
186 | ||
2640faeb | 187 | if (!nr) { |
25be2e5d KO |
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 | ||
2640faeb KO |
193 | for (i = 0; i + 1 < nr; i++) |
194 | if (buckets[i] + 1 != buckets[i + 1]) | |
195 | nr_compacted++; | |
25be2e5d KO |
196 | |
197 | j = bch2_sb_resize_journal_v2(&ca->disk_sb, | |
2640faeb | 198 | (sizeof(*j) + sizeof(j->d[0]) * nr_compacted) / sizeof(u64)); |
25be2e5d | 199 | if (!j) |
098ef98d | 200 | return -BCH_ERR_ENOSPC_sb_journal; |
25be2e5d KO |
201 | |
202 | bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal); | |
203 | ||
73bd774d KO |
204 | j->d[dst].start = cpu_to_le64(buckets[0]); |
205 | j->d[dst].nr = cpu_to_le64(1); | |
25be2e5d | 206 | |
2640faeb KO |
207 | for (i = 1; i < nr; i++) { |
208 | if (buckets[i] == buckets[i - 1] + 1) { | |
25be2e5d KO |
209 | le64_add_cpu(&j->d[dst].nr, 1); |
210 | } else { | |
211 | dst++; | |
73bd774d KO |
212 | j->d[dst].start = cpu_to_le64(buckets[i]); |
213 | j->d[dst].nr = cpu_to_le64(1); | |
25be2e5d KO |
214 | } |
215 | } | |
216 | ||
2640faeb | 217 | BUG_ON(dst + 1 != nr_compacted); |
25be2e5d KO |
218 | return 0; |
219 | } |