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