xref: /linux/fs/bcachefs/journal_sb.c (revision 55d0969c451159cff86949b38c39171cab962069)
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, 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 
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 
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 
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 
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 
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