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