1// SPDX-License-Identifier: GPL-2.0
2#include "bcachefs.h"
3#include "disk_groups.h"
4#include "sb-members.h"
5#include "super-io.h"
6
7#include <linux/sort.h>
8
9static int group_cmp(const void *_l, const void *_r)
10{
11	const struct bch_disk_group *l = _l;
12	const struct bch_disk_group *r = _r;
13
14	return ((BCH_GROUP_DELETED(l) > BCH_GROUP_DELETED(r)) -
15		(BCH_GROUP_DELETED(l) < BCH_GROUP_DELETED(r))) ?:
16		((BCH_GROUP_PARENT(l) > BCH_GROUP_PARENT(r)) -
17		 (BCH_GROUP_PARENT(l) < BCH_GROUP_PARENT(r))) ?:
18		strncmp(l->label, r->label, sizeof(l->label));
19}
20
21static int bch2_sb_disk_groups_validate(struct bch_sb *sb,
22					struct bch_sb_field *f,
23					struct printbuf *err)
24{
25	struct bch_sb_field_disk_groups *groups =
26		field_to_type(f, disk_groups);
27	struct bch_disk_group *g, *sorted = NULL;
28	unsigned nr_groups = disk_groups_nr(groups);
29	unsigned i, len;
30	int ret = 0;
31
32	for (i = 0; i < sb->nr_devices; i++) {
33		struct bch_member m = bch2_sb_member_get(sb, i);
34		unsigned group_id;
35
36		if (!BCH_MEMBER_GROUP(&m))
37			continue;
38
39		group_id = BCH_MEMBER_GROUP(&m) - 1;
40
41		if (group_id >= nr_groups) {
42			prt_printf(err, "disk %u has invalid label %u (have %u)",
43				   i, group_id, nr_groups);
44			return -BCH_ERR_invalid_sb_disk_groups;
45		}
46
47		if (BCH_GROUP_DELETED(&groups->entries[group_id])) {
48			prt_printf(err, "disk %u has deleted label %u", i, group_id);
49			return -BCH_ERR_invalid_sb_disk_groups;
50		}
51	}
52
53	if (!nr_groups)
54		return 0;
55
56	for (i = 0; i < nr_groups; i++) {
57		g = groups->entries + i;
58
59		if (BCH_GROUP_DELETED(g))
60			continue;
61
62		len = strnlen(g->label, sizeof(g->label));
63		if (!len) {
64			prt_printf(err, "label %u empty", i);
65			return -BCH_ERR_invalid_sb_disk_groups;
66		}
67	}
68
69	sorted = kmalloc_array(nr_groups, sizeof(*sorted), GFP_KERNEL);
70	if (!sorted)
71		return -BCH_ERR_ENOMEM_disk_groups_validate;
72
73	memcpy(sorted, groups->entries, nr_groups * sizeof(*sorted));
74	sort(sorted, nr_groups, sizeof(*sorted), group_cmp, NULL);
75
76	for (g = sorted; g + 1 < sorted + nr_groups; g++)
77		if (!BCH_GROUP_DELETED(g) &&
78		    !group_cmp(&g[0], &g[1])) {
79			prt_printf(err, "duplicate label %llu.%.*s",
80			       BCH_GROUP_PARENT(g),
81			       (int) sizeof(g->label), g->label);
82			ret = -BCH_ERR_invalid_sb_disk_groups;
83			goto err;
84		}
85err:
86	kfree(sorted);
87	return ret;
88}
89
90void bch2_disk_groups_to_text(struct printbuf *out, struct bch_fs *c)
91{
92	out->atomic++;
93	rcu_read_lock();
94
95	struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups);
96	if (!g)
97		goto out;
98
99	for (unsigned i = 0; i < g->nr; i++) {
100		if (i)
101			prt_printf(out, " ");
102
103		if (g->entries[i].deleted) {
104			prt_printf(out, "[deleted]");
105			continue;
106		}
107
108		prt_printf(out, "[parent %d devs", g->entries[i].parent);
109		for_each_member_device_rcu(c, ca, &g->entries[i].devs)
110			prt_printf(out, " %s", ca->name);
111		prt_printf(out, "]");
112	}
113
114out:
115	rcu_read_unlock();
116	out->atomic--;
117}
118
119static void bch2_sb_disk_groups_to_text(struct printbuf *out,
120					struct bch_sb *sb,
121					struct bch_sb_field *f)
122{
123	struct bch_sb_field_disk_groups *groups =
124		field_to_type(f, disk_groups);
125	struct bch_disk_group *g;
126	unsigned nr_groups = disk_groups_nr(groups);
127
128	for (g = groups->entries;
129	     g < groups->entries + nr_groups;
130	     g++) {
131		if (g != groups->entries)
132			prt_printf(out, " ");
133
134		if (BCH_GROUP_DELETED(g))
135			prt_printf(out, "[deleted]");
136		else
137			prt_printf(out, "[parent %llu name %s]",
138			       BCH_GROUP_PARENT(g), g->label);
139	}
140}
141
142const struct bch_sb_field_ops bch_sb_field_ops_disk_groups = {
143	.validate	= bch2_sb_disk_groups_validate,
144	.to_text	= bch2_sb_disk_groups_to_text
145};
146
147int bch2_sb_disk_groups_to_cpu(struct bch_fs *c)
148{
149	struct bch_sb_field_disk_groups *groups;
150	struct bch_disk_groups_cpu *cpu_g, *old_g;
151	unsigned i, g, nr_groups;
152
153	lockdep_assert_held(&c->sb_lock);
154
155	groups		= bch2_sb_field_get(c->disk_sb.sb, disk_groups);
156	nr_groups	= disk_groups_nr(groups);
157
158	if (!groups)
159		return 0;
160
161	cpu_g = kzalloc(struct_size(cpu_g, entries, nr_groups), GFP_KERNEL);
162	if (!cpu_g)
163		return -BCH_ERR_ENOMEM_disk_groups_to_cpu;
164
165	cpu_g->nr = nr_groups;
166
167	for (i = 0; i < nr_groups; i++) {
168		struct bch_disk_group *src	= &groups->entries[i];
169		struct bch_disk_group_cpu *dst	= &cpu_g->entries[i];
170
171		dst->deleted	= BCH_GROUP_DELETED(src);
172		dst->parent	= BCH_GROUP_PARENT(src);
173		memcpy(dst->label, src->label, sizeof(dst->label));
174	}
175
176	for (i = 0; i < c->disk_sb.sb->nr_devices; i++) {
177		struct bch_member m = bch2_sb_member_get(c->disk_sb.sb, i);
178		struct bch_disk_group_cpu *dst;
179
180		if (!bch2_member_exists(&m))
181			continue;
182
183		g = BCH_MEMBER_GROUP(&m);
184		while (g) {
185			dst = &cpu_g->entries[g - 1];
186			__set_bit(i, dst->devs.d);
187			g = dst->parent;
188		}
189	}
190
191	old_g = rcu_dereference_protected(c->disk_groups,
192				lockdep_is_held(&c->sb_lock));
193	rcu_assign_pointer(c->disk_groups, cpu_g);
194	if (old_g)
195		kfree_rcu(old_g, rcu);
196
197	return 0;
198}
199
200const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *c, unsigned target)
201{
202	struct target t = target_decode(target);
203	struct bch_devs_mask *devs;
204
205	rcu_read_lock();
206
207	switch (t.type) {
208	case TARGET_NULL:
209		devs = NULL;
210		break;
211	case TARGET_DEV: {
212		struct bch_dev *ca = t.dev < c->sb.nr_devices
213			? rcu_dereference(c->devs[t.dev])
214			: NULL;
215		devs = ca ? &ca->self : NULL;
216		break;
217	}
218	case TARGET_GROUP: {
219		struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups);
220
221		devs = g && t.group < g->nr && !g->entries[t.group].deleted
222			? &g->entries[t.group].devs
223			: NULL;
224		break;
225	}
226	default:
227		BUG();
228	}
229
230	rcu_read_unlock();
231
232	return devs;
233}
234
235bool bch2_dev_in_target(struct bch_fs *c, unsigned dev, unsigned target)
236{
237	struct target t = target_decode(target);
238
239	switch (t.type) {
240	case TARGET_NULL:
241		return false;
242	case TARGET_DEV:
243		return dev == t.dev;
244	case TARGET_GROUP: {
245		struct bch_disk_groups_cpu *g;
246		const struct bch_devs_mask *m;
247		bool ret;
248
249		rcu_read_lock();
250		g = rcu_dereference(c->disk_groups);
251		m = g && t.group < g->nr && !g->entries[t.group].deleted
252			? &g->entries[t.group].devs
253			: NULL;
254
255		ret = m ? test_bit(dev, m->d) : false;
256		rcu_read_unlock();
257
258		return ret;
259	}
260	default:
261		BUG();
262	}
263}
264
265static int __bch2_disk_group_find(struct bch_sb_field_disk_groups *groups,
266				  unsigned parent,
267				  const char *name, unsigned namelen)
268{
269	unsigned i, nr_groups = disk_groups_nr(groups);
270
271	if (!namelen || namelen > BCH_SB_LABEL_SIZE)
272		return -EINVAL;
273
274	for (i = 0; i < nr_groups; i++) {
275		struct bch_disk_group *g = groups->entries + i;
276
277		if (BCH_GROUP_DELETED(g))
278			continue;
279
280		if (!BCH_GROUP_DELETED(g) &&
281		    BCH_GROUP_PARENT(g) == parent &&
282		    strnlen(g->label, sizeof(g->label)) == namelen &&
283		    !memcmp(name, g->label, namelen))
284			return i;
285	}
286
287	return -1;
288}
289
290static int __bch2_disk_group_add(struct bch_sb_handle *sb, unsigned parent,
291				 const char *name, unsigned namelen)
292{
293	struct bch_sb_field_disk_groups *groups =
294		bch2_sb_field_get(sb->sb, disk_groups);
295	unsigned i, nr_groups = disk_groups_nr(groups);
296	struct bch_disk_group *g;
297
298	if (!namelen || namelen > BCH_SB_LABEL_SIZE)
299		return -EINVAL;
300
301	for (i = 0;
302	     i < nr_groups && !BCH_GROUP_DELETED(&groups->entries[i]);
303	     i++)
304		;
305
306	if (i == nr_groups) {
307		unsigned u64s =
308			(sizeof(struct bch_sb_field_disk_groups) +
309			 sizeof(struct bch_disk_group) * (nr_groups + 1)) /
310			sizeof(u64);
311
312		groups = bch2_sb_field_resize(sb, disk_groups, u64s);
313		if (!groups)
314			return -BCH_ERR_ENOSPC_disk_label_add;
315
316		nr_groups = disk_groups_nr(groups);
317	}
318
319	BUG_ON(i >= nr_groups);
320
321	g = &groups->entries[i];
322
323	memcpy(g->label, name, namelen);
324	if (namelen < sizeof(g->label))
325		g->label[namelen] = '\0';
326	SET_BCH_GROUP_DELETED(g, 0);
327	SET_BCH_GROUP_PARENT(g, parent);
328	SET_BCH_GROUP_DATA_ALLOWED(g, ~0);
329
330	return i;
331}
332
333int bch2_disk_path_find(struct bch_sb_handle *sb, const char *name)
334{
335	struct bch_sb_field_disk_groups *groups =
336		bch2_sb_field_get(sb->sb, disk_groups);
337	int v = -1;
338
339	do {
340		const char *next = strchrnul(name, '.');
341		unsigned len = next - name;
342
343		if (*next == '.')
344			next++;
345
346		v = __bch2_disk_group_find(groups, v + 1, name, len);
347		name = next;
348	} while (*name && v >= 0);
349
350	return v;
351}
352
353int bch2_disk_path_find_or_create(struct bch_sb_handle *sb, const char *name)
354{
355	struct bch_sb_field_disk_groups *groups;
356	unsigned parent = 0;
357	int v = -1;
358
359	do {
360		const char *next = strchrnul(name, '.');
361		unsigned len = next - name;
362
363		if (*next == '.')
364			next++;
365
366		groups = bch2_sb_field_get(sb->sb, disk_groups);
367
368		v = __bch2_disk_group_find(groups, parent, name, len);
369		if (v < 0)
370			v = __bch2_disk_group_add(sb, parent, name, len);
371		if (v < 0)
372			return v;
373
374		parent = v + 1;
375		name = next;
376	} while (*name && v >= 0);
377
378	return v;
379}
380
381void bch2_disk_path_to_text(struct printbuf *out, struct bch_fs *c, unsigned v)
382{
383	struct bch_disk_groups_cpu *groups;
384	struct bch_disk_group_cpu *g;
385	unsigned nr = 0;
386	u16 path[32];
387
388	out->atomic++;
389	rcu_read_lock();
390	groups = rcu_dereference(c->disk_groups);
391	if (!groups)
392		goto invalid;
393
394	while (1) {
395		if (nr == ARRAY_SIZE(path))
396			goto invalid;
397
398		if (v >= groups->nr)
399			goto invalid;
400
401		g = groups->entries + v;
402
403		if (g->deleted)
404			goto invalid;
405
406		path[nr++] = v;
407
408		if (!g->parent)
409			break;
410
411		v = g->parent - 1;
412	}
413
414	while (nr) {
415		v = path[--nr];
416		g = groups->entries + v;
417
418		prt_printf(out, "%.*s", (int) sizeof(g->label), g->label);
419		if (nr)
420			prt_printf(out, ".");
421	}
422out:
423	rcu_read_unlock();
424	out->atomic--;
425	return;
426invalid:
427	prt_printf(out, "invalid label %u", v);
428	goto out;
429}
430
431void bch2_disk_path_to_text_sb(struct printbuf *out, struct bch_sb *sb, unsigned v)
432{
433	struct bch_sb_field_disk_groups *groups =
434		bch2_sb_field_get(sb, disk_groups);
435	struct bch_disk_group *g;
436	unsigned nr = 0;
437	u16 path[32];
438
439	while (1) {
440		if (nr == ARRAY_SIZE(path))
441			goto inval;
442
443		if (v >= disk_groups_nr(groups))
444			goto inval;
445
446		g = groups->entries + v;
447
448		if (BCH_GROUP_DELETED(g))
449			goto inval;
450
451		path[nr++] = v;
452
453		if (!BCH_GROUP_PARENT(g))
454			break;
455
456		v = BCH_GROUP_PARENT(g) - 1;
457	}
458
459	while (nr) {
460		v = path[--nr];
461		g = groups->entries + v;
462
463		prt_printf(out, "%.*s", (int) sizeof(g->label), g->label);
464		if (nr)
465			prt_printf(out, ".");
466	}
467	return;
468inval:
469	prt_printf(out, "invalid label %u", v);
470}
471
472int __bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)
473{
474	struct bch_member *mi;
475	int ret, v = -1;
476
477	if (!strlen(name) || !strcmp(name, "none"))
478		return 0;
479
480	v = bch2_disk_path_find_or_create(&c->disk_sb, name);
481	if (v < 0)
482		return v;
483
484	ret = bch2_sb_disk_groups_to_cpu(c);
485	if (ret)
486		return ret;
487
488	mi = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx);
489	SET_BCH_MEMBER_GROUP(mi, v + 1);
490	return 0;
491}
492
493int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)
494{
495	int ret;
496
497	mutex_lock(&c->sb_lock);
498	ret = __bch2_dev_group_set(c, ca, name) ?:
499		bch2_write_super(c);
500	mutex_unlock(&c->sb_lock);
501
502	return ret;
503}
504
505int bch2_opt_target_parse(struct bch_fs *c, const char *val, u64 *res,
506			  struct printbuf *err)
507{
508	struct bch_dev *ca;
509	int g;
510
511	if (!val)
512		return -EINVAL;
513
514	if (!c)
515		return 0;
516
517	if (!strlen(val) || !strcmp(val, "none")) {
518		*res = 0;
519		return 0;
520	}
521
522	/* Is it a device? */
523	ca = bch2_dev_lookup(c, val);
524	if (!IS_ERR(ca)) {
525		*res = dev_to_target(ca->dev_idx);
526		percpu_ref_put(&ca->ref);
527		return 0;
528	}
529
530	mutex_lock(&c->sb_lock);
531	g = bch2_disk_path_find(&c->disk_sb, val);
532	mutex_unlock(&c->sb_lock);
533
534	if (g >= 0) {
535		*res = group_to_target(g);
536		return 0;
537	}
538
539	return -EINVAL;
540}
541
542void bch2_target_to_text(struct printbuf *out, struct bch_fs *c, unsigned v)
543{
544	struct target t = target_decode(v);
545
546	switch (t.type) {
547	case TARGET_NULL:
548		prt_printf(out, "none");
549		break;
550	case TARGET_DEV: {
551		struct bch_dev *ca;
552
553		out->atomic++;
554		rcu_read_lock();
555		ca = t.dev < c->sb.nr_devices
556			? rcu_dereference(c->devs[t.dev])
557			: NULL;
558
559		if (ca && percpu_ref_tryget(&ca->io_ref)) {
560			prt_printf(out, "/dev/%s", ca->name);
561			percpu_ref_put(&ca->io_ref);
562		} else if (ca) {
563			prt_printf(out, "offline device %u", t.dev);
564		} else {
565			prt_printf(out, "invalid device %u", t.dev);
566		}
567
568		rcu_read_unlock();
569		out->atomic--;
570		break;
571	}
572	case TARGET_GROUP:
573		bch2_disk_path_to_text(out, c, t.group);
574		break;
575	default:
576		BUG();
577	}
578}
579
580static void bch2_target_to_text_sb(struct printbuf *out, struct bch_sb *sb, unsigned v)
581{
582	struct target t = target_decode(v);
583
584	switch (t.type) {
585	case TARGET_NULL:
586		prt_printf(out, "none");
587		break;
588	case TARGET_DEV: {
589		struct bch_member m = bch2_sb_member_get(sb, t.dev);
590
591		if (bch2_dev_exists(sb, t.dev)) {
592			prt_printf(out, "Device ");
593			pr_uuid(out, m.uuid.b);
594			prt_printf(out, " (%u)", t.dev);
595		} else {
596			prt_printf(out, "Bad device %u", t.dev);
597		}
598		break;
599	}
600	case TARGET_GROUP:
601		bch2_disk_path_to_text_sb(out, sb, t.group);
602		break;
603	default:
604		BUG();
605	}
606}
607
608void bch2_opt_target_to_text(struct printbuf *out,
609			     struct bch_fs *c,
610			     struct bch_sb *sb,
611			     u64 v)
612{
613	if (c)
614		bch2_target_to_text(out, c, v);
615	else
616		bch2_target_to_text_sb(out, sb, v);
617}
618