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(k: l) > BCH_GROUP_DELETED(k: r)) -
15 (BCH_GROUP_DELETED(k: l) < BCH_GROUP_DELETED(k: r))) ?:
16 ((BCH_GROUP_PARENT(k: l) > BCH_GROUP_PARENT(k: r)) -
17 (BCH_GROUP_PARENT(k: l) < BCH_GROUP_PARENT(k: 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(k: &m))
37 continue;
38
39 group_id = BCH_MEMBER_GROUP(k: &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(k: &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(k: g))
60 continue;
61
62 len = strnlen(p: g->label, maxlen: 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(n: nr_groups, size: 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(base: sorted, num: nr_groups, size: sizeof(*sorted), cmp_func: group_cmp, NULL);
75
76 for (g = sorted; g + 1 < sorted + nr_groups; g++)
77 if (!BCH_GROUP_DELETED(k: g) &&
78 !group_cmp(l: &g[0], r: &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(objp: 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(k: 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(k: src);
172 dst->parent = BCH_GROUP_PARENT(k: 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(sb: c->disk_sb.sb, i);
178 struct bch_disk_group_cpu *dst;
179
180 if (!bch2_member_exists(m: &m))
181 continue;
182
183 g = BCH_MEMBER_GROUP(k: &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(k: g))
278 continue;
279
280 if (!BCH_GROUP_DELETED(k: g) &&
281 BCH_GROUP_PARENT(k: g) == parent &&
282 strnlen(p: g->label, maxlen: sizeof(g->label)) == namelen &&
283 !memcmp(p: name, q: g->label, size: 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(k: &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(k: g, v: 0);
327 SET_BCH_GROUP_PARENT(k: g, v: parent);
328 SET_BCH_GROUP_DATA_ALLOWED(k: g, v: ~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, parent: v + 1, name, namelen: 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, namelen: len);
369 if (v < 0)
370 v = __bch2_disk_group_add(sb, parent, name, namelen: 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(k: g))
449 goto inval;
450
451 path[nr++] = v;
452
453 if (!BCH_GROUP_PARENT(k: g))
454 break;
455
456 v = BCH_GROUP_PARENT(k: 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(sb: &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(sb: c->disk_sb.sb, i: ca->dev_idx);
489 SET_BCH_MEMBER_GROUP(k: mi, v: 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(lock: &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(ptr: ca)) {
525 *res = dev_to_target(dev: ca->dev_idx);
526 percpu_ref_put(ref: &ca->ref);
527 return 0;
528 }
529
530 mutex_lock(&c->sb_lock);
531 g = bch2_disk_path_find(sb: &c->disk_sb, name: val);
532 mutex_unlock(lock: &c->sb_lock);
533
534 if (g >= 0) {
535 *res = group_to_target(group: 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(target: 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(ref: &ca->io_ref)) {
560 prt_printf(out, "/dev/%s", ca->name);
561 percpu_ref_put(ref: &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, v: 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(target: 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, i: t.dev);
590
591 if (bch2_dev_exists(sb, dev: t.dev)) {
592 prt_printf(out, "Device ");
593 pr_uuid(out, uuid: 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, v: 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

source code of linux/fs/bcachefs/disk_groups.c