xref: /linux/fs/bcachefs/journal_sb.c (revision a5c3e265)
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 	unsigned nr;
108 	unsigned i;
109 	struct u64_range *b;
110 
111 	nr = bch2_sb_field_journal_v2_nr_entries(journal);
112 	if (!nr)
113 		return 0;
114 
115 	b = kmalloc_array(nr, sizeof(*b), GFP_KERNEL);
116 	if (!b)
117 		return -BCH_ERR_ENOMEM_sb_journal_v2_validate;
118 
119 	for (i = 0; i < nr; i++) {
120 		b[i].start = le64_to_cpu(journal->d[i].start);
121 		b[i].end = b[i].start + le64_to_cpu(journal->d[i].nr);
122 	}
123 
124 	sort(b, nr, sizeof(*b), u64_range_cmp, NULL);
125 
126 	if (!b[0].start) {
127 		prt_printf(err, "journal bucket at sector 0");
128 		goto err;
129 	}
130 
131 	if (b[0].start < le16_to_cpu(m.first_bucket)) {
132 		prt_printf(err, "journal bucket %llu before first bucket %u",
133 		       b[0].start, le16_to_cpu(m.first_bucket));
134 		goto err;
135 	}
136 
137 	if (b[nr - 1].end > le64_to_cpu(m.nbuckets)) {
138 		prt_printf(err, "journal bucket %llu past end of device (nbuckets %llu)",
139 		       b[nr - 1].end - 1, le64_to_cpu(m.nbuckets));
140 		goto err;
141 	}
142 
143 	for (i = 0; i + 1 < nr; i++) {
144 		if (b[i].end > b[i + 1].start) {
145 			prt_printf(err, "duplicate journal buckets in ranges %llu-%llu, %llu-%llu",
146 			       b[i].start, b[i].end, b[i + 1].start, b[i + 1].end);
147 			goto err;
148 		}
149 	}
150 
151 	ret = 0;
152 err:
153 	kfree(b);
154 	return ret;
155 }
156 
bch2_sb_journal_v2_to_text(struct printbuf * out,struct bch_sb * sb,struct bch_sb_field * f)157 static void bch2_sb_journal_v2_to_text(struct printbuf *out, struct bch_sb *sb,
158 				    struct bch_sb_field *f)
159 {
160 	struct bch_sb_field_journal_v2 *journal = field_to_type(f, journal_v2);
161 	unsigned i, nr = bch2_sb_field_journal_v2_nr_entries(journal);
162 
163 	prt_printf(out, "Buckets: ");
164 	for (i = 0; i < nr; i++)
165 		prt_printf(out, " %llu-%llu",
166 		       le64_to_cpu(journal->d[i].start),
167 		       le64_to_cpu(journal->d[i].start) + le64_to_cpu(journal->d[i].nr));
168 	prt_newline(out);
169 }
170 
171 const struct bch_sb_field_ops bch_sb_field_ops_journal_v2 = {
172 	.validate	= bch2_sb_journal_v2_validate,
173 	.to_text	= bch2_sb_journal_v2_to_text,
174 };
175 
bch2_journal_buckets_to_sb(struct bch_fs * c,struct bch_dev * ca,u64 * buckets,unsigned nr)176 int bch2_journal_buckets_to_sb(struct bch_fs *c, struct bch_dev *ca,
177 			       u64 *buckets, unsigned nr)
178 {
179 	struct bch_sb_field_journal_v2 *j;
180 	unsigned i, dst = 0, nr_compacted = 1;
181 
182 	if (c)
183 		lockdep_assert_held(&c->sb_lock);
184 
185 	if (!nr) {
186 		bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal);
187 		bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal_v2);
188 		return 0;
189 	}
190 
191 	for (i = 0; i + 1 < nr; i++)
192 		if (buckets[i] + 1 != buckets[i + 1])
193 			nr_compacted++;
194 
195 	j = bch2_sb_field_resize(&ca->disk_sb, journal_v2,
196 			 (sizeof(*j) + sizeof(j->d[0]) * nr_compacted) / sizeof(u64));
197 	if (!j)
198 		return -BCH_ERR_ENOSPC_sb_journal;
199 
200 	bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal);
201 
202 	j->d[dst].start = cpu_to_le64(buckets[0]);
203 	j->d[dst].nr	= cpu_to_le64(1);
204 
205 	for (i = 1; i < nr; i++) {
206 		if (buckets[i] == buckets[i - 1] + 1) {
207 			le64_add_cpu(&j->d[dst].nr, 1);
208 		} else {
209 			dst++;
210 			j->d[dst].start = cpu_to_le64(buckets[i]);
211 			j->d[dst].nr	= cpu_to_le64(1);
212 		}
213 	}
214 
215 	BUG_ON(dst + 1 != nr_compacted);
216 	return 0;
217 }
218