617 lines · c
1// SPDX-License-Identifier: GPL-2.02#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(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 21static 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 }84err:85 kfree(sorted);86 return ret;87}88 89void 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 113out:114 rcu_read_unlock();115 out->atomic--;116}117 118static 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 else136 prt_printf(out, "[parent %llu name %s]",137 BCH_GROUP_PARENT(g), g->label);138 }139}140 141const 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_text144};145 146int 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 199const 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_devices212 ? 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].deleted221 ? &g->entries[t.group].devs222 : NULL;223 break;224 }225 default:226 BUG();227 }228 229 rcu_read_unlock();230 231 return devs;232}233 234bool 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].deleted251 ? &g->entries[t.group].devs252 : 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 264static 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 289static 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 332int 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 352int 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 380void 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 }421out:422 rcu_read_unlock();423 out->atomic--;424 return;425invalid:426 prt_printf(out, "invalid label %u", v);427 goto out;428}429 430void 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;467inval:468 prt_printf(out, "invalid label %u", v);469}470 471int __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 492int 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 504int 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 541void 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_devices555 ? 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 579static 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 607void 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 else615 bch2_target_to_text_sb(out, sb, v);616}617