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 -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
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