]> Git Repo - J-linux.git/blob - fs/bcachefs/disk_groups.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / fs / bcachefs / disk_groups.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
This page took 0.06112 seconds and 4 git commands to generate.