xref: /linux/fs/bcachefs/journal_sb.c (revision 70ab9ec9166db90ab8980aff4f7083512ecddd1f)
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, 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(journal);
31 	if (!nr)
32 		return 0;
33 
34 	b = kmalloc_array(nr, 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(b, nr, sizeof(u64), 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(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 
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, 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(journal);
114 	if (!nr)
115 		return 0;
116 
117 	b = kmalloc_array(nr, 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(b, nr, sizeof(*b), 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(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 
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(&j->d[dst].nr, 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