~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/bcachefs/disk_groups.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php