Linux Audio

Check our new training course

Yocto distribution development and maintenance

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