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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.