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
11static 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
19static 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;
67err:
68	kfree(b);
69	return ret;
70}
71
72static 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
84const 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
89struct u64_range {
90	u64	start;
91	u64	end;
92};
93
94static 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
102static 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;
154err:
155	kfree(b);
156	return ret;
157}
158
159static 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
173const 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
178int 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