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