xref: /linux/fs/bcachefs/disk_groups.c (revision a5c3e265)
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 
group_cmp(const void * _l,const void * _r)9 static 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 
bch2_sb_disk_groups_validate(struct bch_sb * sb,struct bch_sb_field * f,enum bch_validate_flags flags,struct printbuf * err)21 static 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 		}
84 err:
85 	kfree(sorted);
86 	return ret;
87 }
88 
bch2_disk_groups_to_text(struct printbuf * out,struct bch_fs * c)89 void 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 
113 out:
114 	rcu_read_unlock();
115 	out->atomic--;
116 }
117 
bch2_sb_disk_groups_to_text(struct printbuf * out,struct bch_sb * sb,struct bch_sb_field * f)118 static 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 
141 const 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 
bch2_sb_disk_groups_to_cpu(struct bch_fs * c)146 int 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 
bch2_target_to_mask(struct bch_fs * c,unsigned target)199 const 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 
bch2_dev_in_target(struct bch_fs * c,unsigned dev,unsigned target)234 bool 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 
__bch2_disk_group_find(struct bch_sb_field_disk_groups * groups,unsigned parent,const char * name,unsigned namelen)264 static 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 
__bch2_disk_group_add(struct bch_sb_handle * sb,unsigned parent,const char * name,unsigned namelen)289 static 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 
bch2_disk_path_find(struct bch_sb_handle * sb,const char * name)332 int 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 
bch2_disk_path_find_or_create(struct bch_sb_handle * sb,const char * name)352 int 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 
bch2_disk_path_to_text(struct printbuf * out,struct bch_fs * c,unsigned v)380 void 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 	}
421 out:
422 	rcu_read_unlock();
423 	out->atomic--;
424 	return;
425 invalid:
426 	prt_printf(out, "invalid label %u", v);
427 	goto out;
428 }
429 
bch2_disk_path_to_text_sb(struct printbuf * out,struct bch_sb * sb,unsigned v)430 void 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;
467 inval:
468 	prt_printf(out, "invalid label %u", v);
469 }
470 
__bch2_dev_group_set(struct bch_fs * c,struct bch_dev * ca,const char * name)471 int __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 
bch2_dev_group_set(struct bch_fs * c,struct bch_dev * ca,const char * name)492 int 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 
bch2_opt_target_parse(struct bch_fs * c,const char * val,u64 * res,struct printbuf * err)504 int 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 0;
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 
bch2_target_to_text(struct printbuf * out,struct bch_fs * c,unsigned v)541 void 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 
bch2_target_to_text_sb(struct printbuf * out,struct bch_sb * sb,unsigned v)579 static 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 
bch2_opt_target_to_text(struct printbuf * out,struct bch_fs * c,struct bch_sb * sb,u64 v)607 void 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 }
617