43 44#include <sys/param.h> 45#include <sys/conf.h> 46#include <sys/kernel.h> 47#include <sys/libkern.h> 48#include <sys/malloc.h> 49#include <sys/systm.h> 50 51#include <geom/geom.h> 52#include <geom/geom_int.h> 53#include <geom/vinum/geom_vinum_var.h> 54#include <geom/vinum/geom_vinum.h> 55#include <geom/vinum/geom_vinum_share.h> 56 57/* Find the VINUM class and it's associated geom. */ 58struct g_geom * 59find_vinum_geom(void) 60{ 61 struct g_class *mp; 62 struct g_geom *gp; 63 64 g_topology_assert(); 65 66 gp = NULL; 67 68 LIST_FOREACH(mp, &g_classes, class) { 69 if (!strcmp(mp->name, "VINUM")) { 70 gp = LIST_FIRST(&mp->geom); 71 break; 72 } 73 } 74 75 return (gp); 76} 77 78/* 79 * Parse the vinum config provided in *buf and store it in *gp's softc. 80 * If parameter 'merge' is non-zero, then the given config is merged into 81 * *gp. 82 */ 83void 84gv_parse_config(struct gv_softc *sc, u_char *buf, int merge) 85{ 86 char *aptr, *bptr, *cptr; 87 struct gv_volume *v, *v2; 88 struct gv_plex *p, *p2; 89 struct gv_sd *s, *s2; 90 int tokens; 91 char *token[GV_MAXARGS]; 92 93 g_topology_assert(); 94 95 KASSERT(sc != NULL, ("gv_parse_config: NULL softc")); 96 97 /* Until the end of the string *buf. */ 98 for (aptr = buf; *aptr != '\0'; aptr = bptr) { 99 bptr = aptr; 100 cptr = aptr; 101 102 /* Seperate input lines. */ 103 while (*bptr != '\n') 104 bptr++; 105 *bptr = '\0'; 106 bptr++; 107 108 tokens = gv_tokenize(cptr, token, GV_MAXARGS); 109 110 if (tokens > 0) { 111 if (!strcmp(token[0], "volume")) { 112 v = gv_new_volume(tokens, token); 113 if (v == NULL) { 114 printf("geom_vinum: failed volume\n"); 115 break; 116 } 117 118 if (merge) { 119 v2 = gv_find_vol(sc, v->name); 120 if (v2 != NULL) { 121 g_free(v); 122 continue; 123 } 124 } 125 126 v->vinumconf = sc; 127 LIST_INIT(&v->plexes); 128 LIST_INSERT_HEAD(&sc->volumes, v, volume); 129 130 } else if (!strcmp(token[0], "plex")) { 131 p = gv_new_plex(tokens, token); 132 if (p == NULL) { 133 printf("geom_vinum: failed plex\n"); 134 break; 135 } 136 137 if (merge) { 138 p2 = gv_find_plex(sc, p->name); 139 if (p2 != NULL) { 140 g_free(p); 141 continue; 142 } 143 } 144 145 p->vinumconf = sc; 146 LIST_INIT(&p->subdisks); 147 LIST_INSERT_HEAD(&sc->plexes, p, plex); 148 149 } else if (!strcmp(token[0], "sd")) { 150 s = gv_new_sd(tokens, token); 151 152 if (s == NULL) { 153 printf("geom_vinum: failed subdisk\n"); 154 break; 155 } 156 157 if (merge) { 158 s2 = gv_find_sd(sc, s->name); 159 if (s2 != NULL) { 160 g_free(s); 161 continue; 162 } 163 } 164 165 s->vinumconf = sc; 166 LIST_INSERT_HEAD(&sc->subdisks, s, sd); 167 } 168 } 169 } 170} 171 172/* 173 * Format the vinum configuration properly. If ondisk is non-zero then the 174 * configuration is intended to be written to disk later. 175 */ 176void 177gv_format_config(struct gv_softc *sc, struct sbuf *sb, int ondisk, char *prefix) 178{ 179 struct gv_drive *d; 180 struct gv_sd *s; 181 struct gv_plex *p; 182 struct gv_volume *v; 183 184 g_topology_assert(); 185 186 /* 187 * We don't need the drive configuration if we're not writing the 188 * config to disk. 189 */ 190 if (!ondisk) { 191 LIST_FOREACH(d, &sc->drives, drive) { 192 sbuf_printf(sb, "%sdrive %s device %s\n", prefix, 193 d->name, d->device); 194 } 195 } 196 197 LIST_FOREACH(v, &sc->volumes, volume) { 198 if (!ondisk) 199 sbuf_printf(sb, "%s", prefix); 200 sbuf_printf(sb, "volume %s", v->name); 201 if (ondisk) 202 sbuf_printf(sb, " state %s", gv_volstate(v->state)); 203 sbuf_printf(sb, "\n"); 204 } 205 206 LIST_FOREACH(p, &sc->plexes, plex) { 207 if (!ondisk) 208 sbuf_printf(sb, "%s", prefix); 209 sbuf_printf(sb, "plex name %s org %s ", p->name, 210 gv_plexorg(p->org)); 211 if (gv_is_striped(p)) 212 sbuf_printf(sb, "%ds ", p->stripesize / 512); 213 if (p->vol_sc != NULL) 214 sbuf_printf(sb, "vol %s", p->volume); 215 if (ondisk) 216 sbuf_printf(sb, " state %s", gv_plexstate(p->state)); 217 sbuf_printf(sb, "\n"); 218 } 219 220 LIST_FOREACH(s, &sc->subdisks, sd) { 221 if (!ondisk) 222 sbuf_printf(sb, "%s", prefix); 223 sbuf_printf(sb, "sd name %s drive %s len %jds driveoffset " 224 "%jds", s->name, s->drive, s->size / 512, 225 s->drive_offset / 512); 226 if (s->plex_sc != NULL) { 227 sbuf_printf(sb, " plex %s plexoffset %jds", s->plex, 228 s->plex_offset / 512); 229 } 230 if (ondisk) 231 sbuf_printf(sb, " state %s", gv_sdstate(s->state)); 232 sbuf_printf(sb, "\n"); 233 } 234 235 return; 236} 237 238/* 239 * Take a size in bytes and return a pointer to a string which represents the 240 * size best. If lj is != 0, return left justified, otherwise in a fixed 10 241 * character field suitable for columnar printing. 242 * 243 * Note this uses a static string: it's only intended to be used immediately 244 * for printing. 245 */ 246const char * 247gv_roughlength(off_t bytes, int lj) 248{ 249 static char desc[16]; 250 251 /* Gigabytes. */ 252 if (bytes > (off_t)MEGABYTE * 10000) 253 snprintf(desc, sizeof(desc), lj ? "%jd GB" : "%10jd GB", 254 bytes / GIGABYTE); 255 256 /* Megabytes. */ 257 else if (bytes > KILOBYTE * 10000) 258 snprintf(desc, sizeof(desc), lj ? "%jd MB" : "%10jd MB", 259 bytes / MEGABYTE); 260 261 /* Kilobytes. */ 262 else if (bytes > 10000) 263 snprintf(desc, sizeof(desc), lj ? "%jd kB" : "%10jd kB", 264 bytes / KILOBYTE); 265 266 /* Bytes. */ 267 else 268 snprintf(desc, sizeof(desc), lj ? "%jd B" : "%10jd B", bytes); 269 270 return (desc); 271} 272 273int 274gv_sd_to_plex(struct gv_plex *p, struct gv_sd *s, int check) 275{ 276 struct gv_sd *s2; 277 278 g_topology_assert(); 279 280 /* If this subdisk was already given to this plex, do nothing. */ 281 if (s->plex_sc == p) 282 return (0); 283 284 /* Find the correct plex offset for this subdisk, if needed. */ 285 if (s->plex_offset == -1) { 286 if (p->sdcount) { 287 LIST_FOREACH(s2, &p->subdisks, in_plex) { 288 if (gv_is_striped(p)) 289 s->plex_offset = p->sdcount * 290 p->stripesize; 291 else 292 s->plex_offset = s2->plex_offset + 293 s2->size; 294 } 295 } else 296 s->plex_offset = 0; 297 } 298 299 p->sdcount++; 300 301 /* Adjust the size of our plex. */ 302 switch (p->org) { 303 case GV_PLEX_CONCAT: 304 case GV_PLEX_STRIPED: 305 p->size += s->size; 306 break; 307 308 case GV_PLEX_RAID5: 309 p->size = (p->sdcount - 1) * s->size; 310 break; 311 312 default: 313 break; 314 } 315 316 /* There are no subdisks for this plex yet, just insert it. */ 317 if (LIST_EMPTY(&p->subdisks)) { 318 LIST_INSERT_HEAD(&p->subdisks, s, in_plex); 319 320 /* Insert in correct order, depending on plex_offset. */ 321 } else { 322 LIST_FOREACH(s2, &p->subdisks, in_plex) { 323 if (s->plex_offset < s2->plex_offset) { 324 LIST_INSERT_BEFORE(s2, s, in_plex); 325 break; 326 } else if (LIST_NEXT(s2, in_plex) == NULL) { 327 LIST_INSERT_AFTER(s2, s, in_plex); 328 break; 329 } 330 } 331 } 332 333 s->plex_sc = p; 334 335 return (0); 336} 337 338void 339gv_update_vol_size(struct gv_volume *v, off_t size) 340{ 341 struct g_geom *gp; 342 struct g_provider *pp; 343 344 if (v == NULL) 345 return; 346 347 gp = v->geom; 348 if (gp == NULL) 349 return; 350 351 LIST_FOREACH(pp, &gp->provider, provider) { 352 pp->mediasize = size; 353 } 354 355 v->size = size; 356} 357 358void 359gv_update_plex_config(struct gv_plex *p) 360{ 361 struct gv_sd *s, *s2; 362 off_t remainder; 363 int required_sds, state; 364 365 KASSERT(p != NULL, ("gv_update_plex_config: NULL p")); 366 367 /* This is what we want the plex to be. */ 368 state = GV_PLEX_UP; 369 370 /* The plex was added to an already running volume. */ 371 if (p->flags & GV_PLEX_ADDED) 372 state = GV_PLEX_DOWN; 373 374 switch (p->org) { 375 case GV_PLEX_STRIPED: 376 required_sds = 2; 377 break; 378 case GV_PLEX_RAID5: 379 required_sds = 3; 380 break; 381 case GV_PLEX_CONCAT: 382 default: 383 required_sds = 0; 384 break; 385 } 386 387 if (required_sds) { 388 if (p->sdcount < required_sds) { 389 state = GV_PLEX_DOWN; 390 } 391 392 /* 393 * The subdisks in striped plexes must all have the same size. 394 */ 395 s = LIST_FIRST(&p->subdisks); 396 LIST_FOREACH(s2, &p->subdisks, in_plex) { 397 if (s->size != s2->size) { 398 printf("geom_vinum: subdisk size mismatch " 399 "%s (%jd) <> %s (%jd)\n", s->name, s->size, 400 s2->name, s2->size); 401 state = GV_PLEX_DOWN; 402 } 403 } 404 405 /* Trim subdisk sizes so that they match the stripe size. */ 406 LIST_FOREACH(s, &p->subdisks, in_plex) { 407 remainder = s->size % p->stripesize; 408 if (remainder) { 409 printf("gvinum: size of sd %s is not a " 410 "multiple of plex stripesize, taking off " 411 "%jd bytes\n", s->name, 412 (intmax_t)remainder); 413 gv_adjust_freespace(s, remainder); 414 } 415 } 416 } 417 418 /* Adjust the size of our plex. */ 419 if (p->sdcount > 0) { 420 p->size = 0; 421 switch (p->org) { 422 case GV_PLEX_CONCAT: 423 LIST_FOREACH(s, &p->subdisks, in_plex) 424 p->size += s->size; 425 break; 426 427 case GV_PLEX_STRIPED: 428 s = LIST_FIRST(&p->subdisks); 429 p->size = p->sdcount * s->size; 430 break; 431 432 case GV_PLEX_RAID5: 433 s = LIST_FIRST(&p->subdisks); 434 p->size = (p->sdcount - 1) * s->size; 435 break; 436 437 default: 438 break; 439 } 440 } 441 442 if (p->sdcount == 0) 443 state = GV_PLEX_DOWN; 444 else if ((p->flags & GV_PLEX_ADDED) || 445 ((p->org == GV_PLEX_RAID5) && (p->flags & GV_PLEX_NEWBORN))) { 446 LIST_FOREACH(s, &p->subdisks, in_plex) 447 s->state = GV_SD_STALE; 448 p->flags &= ~GV_PLEX_ADDED; 449 p->flags &= ~GV_PLEX_NEWBORN; 450 p->state = GV_PLEX_DOWN; 451 } 452} 453 454/* 455 * Give a subdisk to a drive, check and adjust several parameters, adjust 456 * freelist. 457 */ 458int 459gv_sd_to_drive(struct gv_softc *sc, struct gv_drive *d, struct gv_sd *s, 460 char *errstr, int errlen) 461{ 462 struct gv_sd *s2; 463 struct gv_freelist *fl, *fl2; 464 off_t tmp; 465 int i; 466 467 g_topology_assert(); 468 469 fl2 = NULL; 470 471 KASSERT(sc != NULL, ("gv_sd_to_drive: NULL softc")); 472 KASSERT(d != NULL, ("gv_sd_to_drive: NULL drive")); 473 KASSERT(s != NULL, ("gv_sd_to_drive: NULL subdisk")); 474 KASSERT(errstr != NULL, ("gv_sd_to_drive: NULL errstr")); 475 KASSERT(errlen >= ERRBUFSIZ, ("gv_sd_to_drive: short errlen (%d)", 476 errlen)); 477 478 /* Check if this subdisk was already given to this drive. */ 479 if (s->drive_sc == d) 480 return (0); 481 482 /* Preliminary checks. */ 483 if (s->size > d->avail || d->freelist_entries == 0) { 484 snprintf(errstr, errlen, "not enough space on '%s' for '%s'", 485 d->name, s->name); 486 return (-1); 487 } 488 489 /* No size given, autosize it. */ 490 if (s->size == -1) { 491 /* Find the largest available slot. */ 492 LIST_FOREACH(fl, &d->freelist, freelist) { 493 if (fl->size >= s->size) { 494 s->size = fl->size; 495 s->drive_offset = fl->offset; 496 fl2 = fl; 497 } 498 } 499 500 /* No good slot found? */ 501 if (s->size == -1) { 502 snprintf(errstr, errlen, "couldn't autosize '%s' on " 503 "'%s'", s->name, d->name); 504 return (-1); 505 } 506 507 /* 508 * Check if we have a free slot that's large enough for the given size. 509 */ 510 } else { 511 i = 0; 512 LIST_FOREACH(fl, &d->freelist, freelist) { 513 /* Yes, this subdisk fits. */ 514 if (fl->size >= s->size) { 515 i++; 516 /* Assign drive offset, if not given. */ 517 if (s->drive_offset == -1) 518 s->drive_offset = fl->offset; 519 fl2 = fl; 520 break; 521 } 522 } 523 524 /* Couldn't find a good free slot. */ 525 if (i == 0) { 526 snprintf(errstr, errlen, "free slots to small for '%s' " 527 "on '%s'", s->name, d->name); 528 return (-1); 529 } 530 } 531 532 /* No drive offset given, try to calculate it. */ 533 if (s->drive_offset == -1) { 534 535 /* Add offsets and sizes from other subdisks on this drive. */ 536 LIST_FOREACH(s2, &d->subdisks, from_drive) { 537 s->drive_offset = s2->drive_offset + s2->size; 538 } 539 540 /* 541 * If there are no other subdisks yet, then set the default 542 * offset to GV_DATA_START. 543 */ 544 if (s->drive_offset == -1) 545 s->drive_offset = GV_DATA_START; 546 547 /* Check if we have a free slot at the given drive offset. */ 548 } else { 549 i = 0; 550 LIST_FOREACH(fl, &d->freelist, freelist) { 551 /* Yes, this subdisk fits. */ 552 if ((fl->offset <= s->drive_offset) && 553 (fl->offset + fl->size >= 554 s->drive_offset + s->size)) { 555 i++; 556 fl2 = fl; 557 break; 558 } 559 } 560 561 /* Couldn't find a good free slot. */ 562 if (i == 0) { 563 snprintf(errstr, errlen, "given drive_offset for '%s' " 564 "won't fit on '%s'", s->name, d->name); 565 return (-1); 566 } 567 } 568 569 /* 570 * Now that all parameters are checked and set up, we can give the 571 * subdisk to the drive and adjust the freelist. 572 */ 573 574 /* First, adjust the freelist. */ 575 LIST_FOREACH(fl, &d->freelist, freelist) { 576 577 /* This is the free slot that we have found before. */ 578 if (fl == fl2) { 579 580 /* 581 * The subdisk starts at the beginning of the free 582 * slot. 583 */ 584 if (fl->offset == s->drive_offset) { 585 fl->offset += s->size; 586 fl->size -= s->size; 587 588 /* 589 * The subdisk uses the whole slot, so remove 590 * it. 591 */ 592 if (fl->size == 0) { 593 d->freelist_entries--; 594 LIST_REMOVE(fl, freelist); 595 } 596 /* 597 * The subdisk does not start at the beginning of the 598 * free slot. 599 */ 600 } else { 601 tmp = fl->offset + fl->size; 602 fl->size = s->drive_offset - fl->offset; 603 604 /* 605 * The subdisk didn't use the complete rest of 606 * the free slot, so we need to split it. 607 */ 608 if (s->drive_offset + s->size != tmp) { 609 fl2 = g_malloc(sizeof(*fl2), 610 M_WAITOK | M_ZERO); 611 fl2->offset = s->drive_offset + s->size; 612 fl2->size = tmp - fl2->offset; 613 LIST_INSERT_AFTER(fl, fl2, freelist); 614 d->freelist_entries++; 615 } 616 } 617 break; 618 } 619 } 620 621 /* 622 * This is the first subdisk on this drive, just insert it into the 623 * list. 624 */ 625 if (LIST_EMPTY(&d->subdisks)) { 626 LIST_INSERT_HEAD(&d->subdisks, s, from_drive); 627 628 /* There are other subdisks, so insert this one in correct order. */ 629 } else { 630 LIST_FOREACH(s2, &d->subdisks, from_drive) { 631 if (s->drive_offset < s2->drive_offset) { 632 LIST_INSERT_BEFORE(s2, s, from_drive); 633 break; 634 } else if (LIST_NEXT(s2, from_drive) == NULL) { 635 LIST_INSERT_AFTER(s2, s, from_drive); 636 break; 637 } 638 } 639 } 640 641 d->sdcount++; 642 d->avail -= s->size; 643 644 /* Link back from the subdisk to this drive. */ 645 s->drive_sc = d; 646 647 return (0); 648} 649 650void 651gv_adjust_freespace(struct gv_sd *s, off_t remainder) 652{ 653 struct gv_drive *d; 654 struct gv_freelist *fl, *fl2; 655 656 KASSERT(s != NULL, ("gv_adjust_freespace: NULL s")); 657 d = s->drive_sc; 658 KASSERT(d != NULL, ("gv_adjust_freespace: NULL d")); 659 660 /* First, find the free slot that's immediately after this subdisk. */ 661 fl = NULL; 662 LIST_FOREACH(fl, &d->freelist, freelist) { 663 if (fl->offset == s->drive_offset + s->size) 664 break; 665 } 666 667 /* If there is no free slot behind this subdisk, so create one. */ 668 if (fl == NULL) { 669 670 fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO); 671 fl->size = remainder; 672 fl->offset = s->drive_offset + s->size - remainder; 673 674 if (d->freelist_entries == 0) { 675 LIST_INSERT_HEAD(&d->freelist, fl, freelist); 676 } else { 677 LIST_FOREACH(fl2, &d->freelist, freelist) { 678 if (fl->offset < fl2->offset) { 679 LIST_INSERT_BEFORE(fl2, fl, freelist); 680 break; 681 } else if (LIST_NEXT(fl2, freelist) == NULL) { 682 LIST_INSERT_AFTER(fl2, fl, freelist); 683 break; 684 } 685 } 686 } 687 688 d->freelist_entries++; 689 690 /* Expand the free slot we just found. */ 691 } else { 692 fl->offset -= remainder; 693 fl->size += remainder; 694 } 695 696 s->size -= remainder; 697 d->avail += remainder; 698} 699 700/* Check if the given plex is a striped one. */ 701int 702gv_is_striped(struct gv_plex *p) 703{ 704 KASSERT(p != NULL, ("gv_is_striped: NULL p")); 705 switch(p->org) { 706 case GV_PLEX_STRIPED: 707 case GV_PLEX_RAID5: 708 return (1); 709 default: 710 return (0); 711 } 712} 713 714/* Find a volume by name. */ 715struct gv_volume * 716gv_find_vol(struct gv_softc *sc, char *name) 717{ 718 struct gv_volume *v; 719 720 LIST_FOREACH(v, &sc->volumes, volume) { 721 if (!strncmp(v->name, name, GV_MAXVOLNAME)) 722 return (v); 723 } 724 725 return (NULL); 726} 727 728/* Find a plex by name. */ 729struct gv_plex * 730gv_find_plex(struct gv_softc *sc, char *name) 731{ 732 struct gv_plex *p; 733 734 LIST_FOREACH(p, &sc->plexes, plex) { 735 if (!strncmp(p->name, name, GV_MAXPLEXNAME)) 736 return (p); 737 } 738 739 return (NULL); 740} 741 742/* Find a subdisk by name. */ 743struct gv_sd * 744gv_find_sd(struct gv_softc *sc, char *name) 745{ 746 struct gv_sd *s; 747 748 LIST_FOREACH(s, &sc->subdisks, sd) { 749 if (!strncmp(s->name, name, GV_MAXSDNAME)) 750 return (s); 751 } 752 753 return (NULL); 754} 755 756/* Find a drive by name. */ 757struct gv_drive * 758gv_find_drive(struct gv_softc *sc, char *name) 759{ 760 struct gv_drive *d; 761 762 LIST_FOREACH(d, &sc->drives, drive) { 763 if (!strncmp(d->name, name, GV_MAXDRIVENAME)) 764 return (d); 765 } 766 767 return (NULL); 768} 769 770/* Check if any consumer of the given geom is open. */ 771int 772gv_is_open(struct g_geom *gp) 773{ 774 struct g_consumer *cp; 775 776 if (gp == NULL) 777 return (0); 778 779 LIST_FOREACH(cp, &gp->consumer, consumer) { 780 if (cp->acr || cp->acw || cp->ace) 781 return (1); 782 } 783 784 return (0); 785} 786 787/* Return the type of object identified by string 'name'. */ 788int 789gv_object_type(struct gv_softc *sc, char *name) 790{ 791 struct gv_drive *d; 792 struct gv_plex *p; 793 struct gv_sd *s; 794 struct gv_volume *v; 795 796 LIST_FOREACH(v, &sc->volumes, volume) { 797 if (!strncmp(v->name, name, GV_MAXVOLNAME)) 798 return (GV_TYPE_VOL); 799 } 800 801 LIST_FOREACH(p, &sc->plexes, plex) { 802 if (!strncmp(p->name, name, GV_MAXPLEXNAME)) 803 return (GV_TYPE_PLEX); 804 } 805 806 LIST_FOREACH(s, &sc->subdisks, sd) { 807 if (!strncmp(s->name, name, GV_MAXSDNAME)) 808 return (GV_TYPE_SD); 809 } 810 811 LIST_FOREACH(d, &sc->drives, drive) { 812 if (!strncmp(d->name, name, GV_MAXDRIVENAME)) 813 return (GV_TYPE_DRIVE); 814 } 815 816 return (-1); 817} 818 819void
|