1 /*- 2 * Copyright (c) 2004 Lukas Ertl 3 * Copyright (c) 1997, 1998, 1999 4 * Nan Yang Computer Services Limited. All rights reserved. 5 * 6 * Parts written by Greg Lehey 7 * 8 * This software is distributed under the so-called ``Berkeley 9 * License'': 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by Nan Yang Computer 22 * Services Limited. 23 * 4. Neither the name of the Company nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * This software is provided ``as is'', and any express or implied 28 * warranties, including, but not limited to, the implied warranties of 29 * merchantability and fitness for a particular purpose are disclaimed. 30 * In no event shall the company or contributors be liable for any 31 * direct, indirect, incidental, special, exemplary, or consequential 32 * damages (including, but not limited to, procurement of substitute 33 * goods or services; loss of use, data, or profits; or business 34 * interruption) however caused and on any theory of liability, whether 35 * in contract, strict liability, or tort (including negligence or 36 * otherwise) arising in any way out of the use of this software, even if 37 * advised of the possibility of such damage. 38 * 39 */ 40 41 #include <sys/cdefs.h> 42 __FBSDID("$FreeBSD$"); 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 static off_t gv_plex_smallest_sd(struct gv_plex *, off_t); 58 59 /* Find the VINUM class and it's associated geom. */ 60 struct g_geom * 61 find_vinum_geom(void) 62 { 63 struct g_class *mp; 64 struct g_geom *gp; 65 66 g_topology_assert(); 67 68 gp = NULL; 69 70 LIST_FOREACH(mp, &g_classes, class) { 71 if (!strcmp(mp->name, "VINUM")) { 72 gp = LIST_FIRST(&mp->geom); 73 break; 74 } 75 } 76 77 return (gp); 78 } 79 80 /* 81 * Parse the vinum config provided in *buf and store it in *gp's softc. 82 * If parameter 'merge' is non-zero, then the given config is merged into 83 * *gp. 84 */ 85 void 86 gv_parse_config(struct gv_softc *sc, u_char *buf, int merge) 87 { 88 char *aptr, *bptr, *cptr; 89 struct gv_volume *v, *v2; 90 struct gv_plex *p, *p2; 91 struct gv_sd *s, *s2; 92 int tokens; 93 char *token[GV_MAXARGS]; 94 95 g_topology_assert(); 96 97 KASSERT(sc != NULL, ("gv_parse_config: NULL softc")); 98 99 /* Until the end of the string *buf. */ 100 for (aptr = buf; *aptr != '\0'; aptr = bptr) { 101 bptr = aptr; 102 cptr = aptr; 103 104 /* Seperate input lines. */ 105 while (*bptr != '\n') 106 bptr++; 107 *bptr = '\0'; 108 bptr++; 109 110 tokens = gv_tokenize(cptr, token, GV_MAXARGS); 111 112 if (tokens > 0) { 113 if (!strcmp(token[0], "volume")) { 114 v = gv_new_volume(tokens, token); 115 if (v == NULL) { 116 printf("geom_vinum: failed volume\n"); 117 break; 118 } 119 120 if (merge) { 121 v2 = gv_find_vol(sc, v->name); 122 if (v2 != NULL) { 123 g_free(v); 124 continue; 125 } 126 } 127 128 v->vinumconf = sc; 129 LIST_INIT(&v->plexes); 130 LIST_INSERT_HEAD(&sc->volumes, v, volume); 131 132 } else if (!strcmp(token[0], "plex")) { 133 p = gv_new_plex(tokens, token); 134 if (p == NULL) { 135 printf("geom_vinum: failed plex\n"); 136 break; 137 } 138 139 if (merge) { 140 p2 = gv_find_plex(sc, p->name); 141 if (p2 != NULL) { 142 g_free(p); 143 continue; 144 } 145 } 146 147 p->vinumconf = sc; 148 LIST_INIT(&p->subdisks); 149 LIST_INSERT_HEAD(&sc->plexes, p, plex); 150 151 } else if (!strcmp(token[0], "sd")) { 152 s = gv_new_sd(tokens, token); 153 154 if (s == NULL) { 155 printf("geom_vinum: failed subdisk\n"); 156 break; 157 } 158 159 if (merge) { 160 s2 = gv_find_sd(sc, s->name); 161 if (s2 != NULL) { 162 g_free(s); 163 continue; 164 } 165 } 166 167 s->vinumconf = sc; 168 LIST_INSERT_HEAD(&sc->subdisks, s, sd); 169 } 170 } 171 } 172 } 173 174 /* 175 * Format the vinum configuration properly. If ondisk is non-zero then the 176 * configuration is intended to be written to disk later. 177 */ 178 void 179 gv_format_config(struct gv_softc *sc, struct sbuf *sb, int ondisk, char *prefix) 180 { 181 struct gv_drive *d; 182 struct gv_sd *s; 183 struct gv_plex *p; 184 struct gv_volume *v; 185 186 g_topology_assert(); 187 188 /* 189 * We don't need the drive configuration if we're not writing the 190 * config to disk. 191 */ 192 if (!ondisk) { 193 LIST_FOREACH(d, &sc->drives, drive) { 194 sbuf_printf(sb, "%sdrive %s device /dev/%s\n", prefix, 195 d->name, d->device); 196 } 197 } 198 199 LIST_FOREACH(v, &sc->volumes, volume) { 200 if (!ondisk) 201 sbuf_printf(sb, "%s", prefix); 202 sbuf_printf(sb, "volume %s", v->name); 203 if (ondisk) 204 sbuf_printf(sb, " state %s", gv_volstate(v->state)); 205 sbuf_printf(sb, "\n"); 206 } 207 208 LIST_FOREACH(p, &sc->plexes, plex) { 209 if (!ondisk) 210 sbuf_printf(sb, "%s", prefix); 211 sbuf_printf(sb, "plex name %s org %s ", p->name, 212 gv_plexorg(p->org)); 213 if (gv_is_striped(p)) 214 sbuf_printf(sb, "%ds ", p->stripesize / 512); 215 if (p->vol_sc != NULL) 216 sbuf_printf(sb, "vol %s", p->volume); 217 if (ondisk) 218 sbuf_printf(sb, " state %s", gv_plexstate(p->state)); 219 sbuf_printf(sb, "\n"); 220 } 221 222 LIST_FOREACH(s, &sc->subdisks, sd) { 223 if (!ondisk) 224 sbuf_printf(sb, "%s", prefix); 225 sbuf_printf(sb, "sd name %s drive %s len %jds driveoffset " 226 "%jds", s->name, s->drive, s->size / 512, 227 s->drive_offset / 512); 228 if (s->plex_sc != NULL) { 229 sbuf_printf(sb, " plex %s plexoffset %jds", s->plex, 230 s->plex_offset / 512); 231 } 232 if (ondisk) 233 sbuf_printf(sb, " state %s", gv_sdstate(s->state)); 234 sbuf_printf(sb, "\n"); 235 } 236 237 return; 238 } 239 240 static off_t 241 gv_plex_smallest_sd(struct gv_plex *p, off_t smallest) 242 { 243 struct gv_sd *s; 244 245 KASSERT(p != NULL, ("gv_plex_smallest_sd: NULL p")); 246 247 LIST_FOREACH(s, &p->subdisks, in_plex) { 248 if (s->size < smallest) 249 smallest = s->size; 250 } 251 return (smallest); 252 } 253 254 int 255 gv_sd_to_plex(struct gv_plex *p, struct gv_sd *s, int check) 256 { 257 struct gv_sd *s2; 258 259 g_topology_assert(); 260 261 /* If this subdisk was already given to this plex, do nothing. */ 262 if (s->plex_sc == p) 263 return (0); 264 265 /* Check correct size of this subdisk. */ 266 s2 = LIST_FIRST(&p->subdisks); 267 if (s2 != NULL && gv_is_striped(p) && (s2->size != s->size)) { 268 printf("GEOM_VINUM: need equal sized subdisks for " 269 "this plex organisation - %s (%jd) <-> %s (%jd)\n", 270 s2->name, s2->size, s->name, s->size); 271 return (-1); 272 } 273 274 /* Find the correct plex offset for this subdisk, if needed. */ 275 if (s->plex_offset == -1) { 276 if (p->sdcount) { 277 LIST_FOREACH(s2, &p->subdisks, in_plex) { 278 if (gv_is_striped(p)) 279 s->plex_offset = p->sdcount * 280 p->stripesize; 281 else 282 s->plex_offset = s2->plex_offset + 283 s2->size; 284 } 285 } else 286 s->plex_offset = 0; 287 } 288 289 p->sdcount++; 290 291 /* Adjust the size of our plex. */ 292 switch (p->org) { 293 case GV_PLEX_CONCAT: 294 case GV_PLEX_STRIPED: 295 p->size += s->size; 296 break; 297 298 case GV_PLEX_RAID5: 299 p->size = (p->sdcount - 1) * gv_plex_smallest_sd(p, s->size); 300 break; 301 302 default: 303 break; 304 } 305 306 /* There are no subdisks for this plex yet, just insert it. */ 307 if (LIST_EMPTY(&p->subdisks)) { 308 LIST_INSERT_HEAD(&p->subdisks, s, in_plex); 309 310 /* Insert in correct order, depending on plex_offset. */ 311 } else { 312 LIST_FOREACH(s2, &p->subdisks, in_plex) { 313 if (s->plex_offset < s2->plex_offset) { 314 LIST_INSERT_BEFORE(s2, s, in_plex); 315 break; 316 } else if (LIST_NEXT(s2, in_plex) == NULL) { 317 LIST_INSERT_AFTER(s2, s, in_plex); 318 break; 319 } 320 } 321 } 322 323 s->plex_sc = p; 324 325 return (0); 326 } 327 328 void 329 gv_update_vol_size(struct gv_volume *v, off_t size) 330 { 331 struct g_geom *gp; 332 struct g_provider *pp; 333 334 if (v == NULL) 335 return; 336 337 gp = v->geom; 338 if (gp == NULL) 339 return; 340 341 LIST_FOREACH(pp, &gp->provider, provider) { 342 pp->mediasize = size; 343 } 344 345 v->size = size; 346 } 347 348 /* Calculates the plex size. */ 349 off_t 350 gv_plex_size(struct gv_plex *p) 351 { 352 struct gv_sd *s; 353 off_t size; 354 355 KASSERT(p != NULL, ("gv_plex_size: NULL p")); 356 357 if (p->sdcount == 0) 358 return (0); 359 360 /* Adjust the size of our plex. */ 361 size = 0; 362 switch (p->org) { 363 case GV_PLEX_CONCAT: 364 LIST_FOREACH(s, &p->subdisks, in_plex) 365 size += s->size; 366 break; 367 case GV_PLEX_STRIPED: 368 s = LIST_FIRST(&p->subdisks); 369 size = p->sdcount * s->size; 370 break; 371 case GV_PLEX_RAID5: 372 s = LIST_FIRST(&p->subdisks); 373 size = (p->sdcount - 1) * s->size; 374 break; 375 } 376 377 return (size); 378 } 379 380 /* Returns the size of a volume. */ 381 off_t 382 gv_vol_size(struct gv_volume *v) 383 { 384 struct gv_plex *p; 385 off_t minplexsize; 386 387 KASSERT(v != NULL, ("gv_vol_size: NULL v")); 388 389 p = LIST_FIRST(&v->plexes); 390 if (p == NULL) 391 return (0); 392 393 minplexsize = p->size; 394 LIST_FOREACH(p, &v->plexes, plex) { 395 if (p->size < minplexsize) { 396 minplexsize = p->size; 397 } 398 } 399 return (minplexsize); 400 } 401 402 void 403 gv_update_plex_config(struct gv_plex *p) 404 { 405 struct gv_sd *s, *s2; 406 off_t remainder; 407 int required_sds, state; 408 409 KASSERT(p != NULL, ("gv_update_plex_config: NULL p")); 410 411 /* This is what we want the plex to be. */ 412 state = GV_PLEX_UP; 413 414 /* The plex was added to an already running volume. */ 415 if (p->flags & GV_PLEX_ADDED) 416 state = GV_PLEX_DOWN; 417 418 switch (p->org) { 419 case GV_PLEX_STRIPED: 420 required_sds = 2; 421 break; 422 case GV_PLEX_RAID5: 423 required_sds = 3; 424 break; 425 case GV_PLEX_CONCAT: 426 default: 427 required_sds = 0; 428 break; 429 } 430 431 if (required_sds) { 432 if (p->sdcount < required_sds) { 433 state = GV_PLEX_DOWN; 434 } 435 436 /* 437 * The subdisks in striped plexes must all have the same size. 438 */ 439 s = LIST_FIRST(&p->subdisks); 440 LIST_FOREACH(s2, &p->subdisks, in_plex) { 441 if (s->size != s2->size) { 442 printf("geom_vinum: subdisk size mismatch " 443 "%s (%jd) <> %s (%jd)\n", s->name, s->size, 444 s2->name, s2->size); 445 state = GV_PLEX_DOWN; 446 } 447 } 448 449 /* Trim subdisk sizes so that they match the stripe size. */ 450 LIST_FOREACH(s, &p->subdisks, in_plex) { 451 remainder = s->size % p->stripesize; 452 if (remainder) { 453 printf("gvinum: size of sd %s is not a " 454 "multiple of plex stripesize, taking off " 455 "%jd bytes\n", s->name, 456 (intmax_t)remainder); 457 gv_adjust_freespace(s, remainder); 458 } 459 } 460 } 461 462 /* Adjust the size of our plex. */ 463 if (p->sdcount > 0) { 464 p->size = 0; 465 switch (p->org) { 466 case GV_PLEX_CONCAT: 467 LIST_FOREACH(s, &p->subdisks, in_plex) 468 p->size += s->size; 469 break; 470 471 case GV_PLEX_STRIPED: 472 s = LIST_FIRST(&p->subdisks); 473 p->size = p->sdcount * s->size; 474 break; 475 476 case GV_PLEX_RAID5: 477 s = LIST_FIRST(&p->subdisks); 478 p->size = (p->sdcount - 1) * s->size; 479 break; 480 481 default: 482 break; 483 } 484 } 485 486 if (p->sdcount == 0) 487 state = GV_PLEX_DOWN; 488 else if ((p->flags & GV_PLEX_ADDED) || 489 ((p->org == GV_PLEX_RAID5) && (p->flags & GV_PLEX_NEWBORN))) { 490 LIST_FOREACH(s, &p->subdisks, in_plex) 491 s->state = GV_SD_STALE; 492 p->flags &= ~GV_PLEX_ADDED; 493 p->flags &= ~GV_PLEX_NEWBORN; 494 state = GV_PLEX_DOWN; 495 } 496 p->state = state; 497 } 498 499 /* 500 * Give a subdisk to a drive, check and adjust several parameters, adjust 501 * freelist. 502 */ 503 int 504 gv_sd_to_drive(struct gv_softc *sc, struct gv_drive *d, struct gv_sd *s, 505 char *errstr, int errlen) 506 { 507 struct gv_sd *s2; 508 struct gv_freelist *fl, *fl2; 509 off_t tmp; 510 int i; 511 512 g_topology_assert(); 513 514 fl2 = NULL; 515 516 KASSERT(sc != NULL, ("gv_sd_to_drive: NULL softc")); 517 KASSERT(d != NULL, ("gv_sd_to_drive: NULL drive")); 518 KASSERT(s != NULL, ("gv_sd_to_drive: NULL subdisk")); 519 KASSERT(errstr != NULL, ("gv_sd_to_drive: NULL errstr")); 520 KASSERT(errlen >= ERRBUFSIZ, ("gv_sd_to_drive: short errlen (%d)", 521 errlen)); 522 523 /* Check if this subdisk was already given to this drive. */ 524 if (s->drive_sc == d) 525 return (0); 526 527 /* Preliminary checks. */ 528 if (s->size > d->avail || d->freelist_entries == 0) { 529 snprintf(errstr, errlen, "not enough space on '%s' for '%s'", 530 d->name, s->name); 531 return (-1); 532 } 533 534 /* No size given, autosize it. */ 535 if (s->size == -1) { 536 /* Find the largest available slot. */ 537 LIST_FOREACH(fl, &d->freelist, freelist) { 538 if (fl->size >= s->size) { 539 s->size = fl->size; 540 s->drive_offset = fl->offset; 541 fl2 = fl; 542 } 543 } 544 545 /* No good slot found? */ 546 if (s->size == -1) { 547 snprintf(errstr, errlen, "couldn't autosize '%s' on " 548 "'%s'", s->name, d->name); 549 return (-1); 550 } 551 552 /* 553 * Check if we have a free slot that's large enough for the given size. 554 */ 555 } else { 556 i = 0; 557 LIST_FOREACH(fl, &d->freelist, freelist) { 558 /* Yes, this subdisk fits. */ 559 if (fl->size >= s->size) { 560 i++; 561 /* Assign drive offset, if not given. */ 562 if (s->drive_offset == -1) 563 s->drive_offset = fl->offset; 564 fl2 = fl; 565 break; 566 } 567 } 568 569 /* Couldn't find a good free slot. */ 570 if (i == 0) { 571 snprintf(errstr, errlen, "free slots to small for '%s' " 572 "on '%s'", s->name, d->name); 573 return (-1); 574 } 575 } 576 577 /* No drive offset given, try to calculate it. */ 578 if (s->drive_offset == -1) { 579 580 /* Add offsets and sizes from other subdisks on this drive. */ 581 LIST_FOREACH(s2, &d->subdisks, from_drive) { 582 s->drive_offset = s2->drive_offset + s2->size; 583 } 584 585 /* 586 * If there are no other subdisks yet, then set the default 587 * offset to GV_DATA_START. 588 */ 589 if (s->drive_offset == -1) 590 s->drive_offset = GV_DATA_START; 591 592 /* Check if we have a free slot at the given drive offset. */ 593 } else { 594 i = 0; 595 LIST_FOREACH(fl, &d->freelist, freelist) { 596 /* Yes, this subdisk fits. */ 597 if ((fl->offset <= s->drive_offset) && 598 (fl->offset + fl->size >= 599 s->drive_offset + s->size)) { 600 i++; 601 fl2 = fl; 602 break; 603 } 604 } 605 606 /* Couldn't find a good free slot. */ 607 if (i == 0) { 608 snprintf(errstr, errlen, "given drive_offset for '%s' " 609 "won't fit on '%s'", s->name, d->name); 610 return (-1); 611 } 612 } 613 614 /* 615 * Now that all parameters are checked and set up, we can give the 616 * subdisk to the drive and adjust the freelist. 617 */ 618 619 /* First, adjust the freelist. */ 620 LIST_FOREACH(fl, &d->freelist, freelist) { 621 622 /* This is the free slot that we have found before. */ 623 if (fl == fl2) { 624 625 /* 626 * The subdisk starts at the beginning of the free 627 * slot. 628 */ 629 if (fl->offset == s->drive_offset) { 630 fl->offset += s->size; 631 fl->size -= s->size; 632 633 /* 634 * The subdisk uses the whole slot, so remove 635 * it. 636 */ 637 if (fl->size == 0) { 638 d->freelist_entries--; 639 LIST_REMOVE(fl, freelist); 640 } 641 /* 642 * The subdisk does not start at the beginning of the 643 * free slot. 644 */ 645 } else { 646 tmp = fl->offset + fl->size; 647 fl->size = s->drive_offset - fl->offset; 648 649 /* 650 * The subdisk didn't use the complete rest of 651 * the free slot, so we need to split it. 652 */ 653 if (s->drive_offset + s->size != tmp) { 654 fl2 = g_malloc(sizeof(*fl2), 655 M_WAITOK | M_ZERO); 656 fl2->offset = s->drive_offset + s->size; 657 fl2->size = tmp - fl2->offset; 658 LIST_INSERT_AFTER(fl, fl2, freelist); 659 d->freelist_entries++; 660 } 661 } 662 break; 663 } 664 } 665 666 /* 667 * This is the first subdisk on this drive, just insert it into the 668 * list. 669 */ 670 if (LIST_EMPTY(&d->subdisks)) { 671 LIST_INSERT_HEAD(&d->subdisks, s, from_drive); 672 673 /* There are other subdisks, so insert this one in correct order. */ 674 } else { 675 LIST_FOREACH(s2, &d->subdisks, from_drive) { 676 if (s->drive_offset < s2->drive_offset) { 677 LIST_INSERT_BEFORE(s2, s, from_drive); 678 break; 679 } else if (LIST_NEXT(s2, from_drive) == NULL) { 680 LIST_INSERT_AFTER(s2, s, from_drive); 681 break; 682 } 683 } 684 } 685 686 d->sdcount++; 687 d->avail -= s->size; 688 689 /* Link back from the subdisk to this drive. */ 690 s->drive_sc = d; 691 692 return (0); 693 } 694 695 void 696 gv_free_sd(struct gv_sd *s) 697 { 698 struct gv_drive *d; 699 struct gv_freelist *fl, *fl2; 700 701 KASSERT(s != NULL, ("gv_free_sd: NULL s")); 702 703 d = s->drive_sc; 704 if (d == NULL) 705 return; 706 707 /* 708 * First, find the free slot that's immediately before or after this 709 * subdisk. 710 */ 711 fl = NULL; 712 LIST_FOREACH(fl, &d->freelist, freelist) { 713 if (fl->offset == s->drive_offset + s->size) 714 break; 715 if (fl->offset + fl->size == s->drive_offset) 716 break; 717 } 718 719 /* If there is no free slot behind this subdisk, so create one. */ 720 if (fl == NULL) { 721 722 fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO); 723 fl->size = s->size; 724 fl->offset = s->drive_offset; 725 726 if (d->freelist_entries == 0) { 727 LIST_INSERT_HEAD(&d->freelist, fl, freelist); 728 } else { 729 LIST_FOREACH(fl2, &d->freelist, freelist) { 730 if (fl->offset < fl2->offset) { 731 LIST_INSERT_BEFORE(fl2, fl, freelist); 732 break; 733 } else if (LIST_NEXT(fl2, freelist) == NULL) { 734 LIST_INSERT_AFTER(fl2, fl, freelist); 735 break; 736 } 737 } 738 } 739 740 d->freelist_entries++; 741 742 /* Expand the free slot we just found. */ 743 } else { 744 fl->size += s->size; 745 if (fl->offset > s->drive_offset) 746 fl->offset = s->drive_offset; 747 } 748 749 d->avail += s->size; 750 d->sdcount--; 751 } 752 753 void 754 gv_adjust_freespace(struct gv_sd *s, off_t remainder) 755 { 756 struct gv_drive *d; 757 struct gv_freelist *fl, *fl2; 758 759 KASSERT(s != NULL, ("gv_adjust_freespace: NULL s")); 760 d = s->drive_sc; 761 KASSERT(d != NULL, ("gv_adjust_freespace: NULL d")); 762 763 /* First, find the free slot that's immediately after this subdisk. */ 764 fl = NULL; 765 LIST_FOREACH(fl, &d->freelist, freelist) { 766 if (fl->offset == s->drive_offset + s->size) 767 break; 768 } 769 770 /* If there is no free slot behind this subdisk, so create one. */ 771 if (fl == NULL) { 772 773 fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO); 774 fl->size = remainder; 775 fl->offset = s->drive_offset + s->size - remainder; 776 777 if (d->freelist_entries == 0) { 778 LIST_INSERT_HEAD(&d->freelist, fl, freelist); 779 } else { 780 LIST_FOREACH(fl2, &d->freelist, freelist) { 781 if (fl->offset < fl2->offset) { 782 LIST_INSERT_BEFORE(fl2, fl, freelist); 783 break; 784 } else if (LIST_NEXT(fl2, freelist) == NULL) { 785 LIST_INSERT_AFTER(fl2, fl, freelist); 786 break; 787 } 788 } 789 } 790 791 d->freelist_entries++; 792 793 /* Expand the free slot we just found. */ 794 } else { 795 fl->offset -= remainder; 796 fl->size += remainder; 797 } 798 799 s->size -= remainder; 800 d->avail += remainder; 801 } 802 803 /* Check if the given plex is a striped one. */ 804 int 805 gv_is_striped(struct gv_plex *p) 806 { 807 KASSERT(p != NULL, ("gv_is_striped: NULL p")); 808 switch(p->org) { 809 case GV_PLEX_STRIPED: 810 case GV_PLEX_RAID5: 811 return (1); 812 default: 813 return (0); 814 } 815 } 816 817 /* Find a volume by name. */ 818 struct gv_volume * 819 gv_find_vol(struct gv_softc *sc, char *name) 820 { 821 struct gv_volume *v; 822 823 LIST_FOREACH(v, &sc->volumes, volume) { 824 if (!strncmp(v->name, name, GV_MAXVOLNAME)) 825 return (v); 826 } 827 828 return (NULL); 829 } 830 831 /* Find a plex by name. */ 832 struct gv_plex * 833 gv_find_plex(struct gv_softc *sc, char *name) 834 { 835 struct gv_plex *p; 836 837 LIST_FOREACH(p, &sc->plexes, plex) { 838 if (!strncmp(p->name, name, GV_MAXPLEXNAME)) 839 return (p); 840 } 841 842 return (NULL); 843 } 844 845 /* Find a subdisk by name. */ 846 struct gv_sd * 847 gv_find_sd(struct gv_softc *sc, char *name) 848 { 849 struct gv_sd *s; 850 851 LIST_FOREACH(s, &sc->subdisks, sd) { 852 if (!strncmp(s->name, name, GV_MAXSDNAME)) 853 return (s); 854 } 855 856 return (NULL); 857 } 858 859 /* Find a drive by name. */ 860 struct gv_drive * 861 gv_find_drive(struct gv_softc *sc, char *name) 862 { 863 struct gv_drive *d; 864 865 LIST_FOREACH(d, &sc->drives, drive) { 866 if (!strncmp(d->name, name, GV_MAXDRIVENAME)) 867 return (d); 868 } 869 870 return (NULL); 871 } 872 873 /* Check if any consumer of the given geom is open. */ 874 int 875 gv_is_open(struct g_geom *gp) 876 { 877 struct g_consumer *cp; 878 879 if (gp == NULL) 880 return (0); 881 882 LIST_FOREACH(cp, &gp->consumer, consumer) { 883 if (cp->acr || cp->acw || cp->ace) 884 return (1); 885 } 886 887 return (0); 888 } 889 890 /* Return the type of object identified by string 'name'. */ 891 int 892 gv_object_type(struct gv_softc *sc, char *name) 893 { 894 struct gv_drive *d; 895 struct gv_plex *p; 896 struct gv_sd *s; 897 struct gv_volume *v; 898 899 LIST_FOREACH(v, &sc->volumes, volume) { 900 if (!strncmp(v->name, name, GV_MAXVOLNAME)) 901 return (GV_TYPE_VOL); 902 } 903 904 LIST_FOREACH(p, &sc->plexes, plex) { 905 if (!strncmp(p->name, name, GV_MAXPLEXNAME)) 906 return (GV_TYPE_PLEX); 907 } 908 909 LIST_FOREACH(s, &sc->subdisks, sd) { 910 if (!strncmp(s->name, name, GV_MAXSDNAME)) 911 return (GV_TYPE_SD); 912 } 913 914 LIST_FOREACH(d, &sc->drives, drive) { 915 if (!strncmp(d->name, name, GV_MAXDRIVENAME)) 916 return (GV_TYPE_DRIVE); 917 } 918 919 return (-1); 920 } 921 922 void 923 gv_kill_drive_thread(struct gv_drive *d) 924 { 925 if (d->flags & GV_DRIVE_THREAD_ACTIVE) { 926 d->flags |= GV_DRIVE_THREAD_DIE; 927 wakeup(d); 928 while (!(d->flags & GV_DRIVE_THREAD_DEAD)) 929 tsleep(d, PRIBIO, "gv_die", hz); 930 d->flags &= ~GV_DRIVE_THREAD_ACTIVE; 931 d->flags &= ~GV_DRIVE_THREAD_DIE; 932 d->flags &= ~GV_DRIVE_THREAD_DEAD; 933 g_free(d->bqueue); 934 d->bqueue = NULL; 935 mtx_destroy(&d->bqueue_mtx); 936 } 937 } 938 939 void 940 gv_kill_plex_thread(struct gv_plex *p) 941 { 942 if (p->flags & GV_PLEX_THREAD_ACTIVE) { 943 p->flags |= GV_PLEX_THREAD_DIE; 944 wakeup(p); 945 while (!(p->flags & GV_PLEX_THREAD_DEAD)) 946 tsleep(p, PRIBIO, "gv_die", hz); 947 p->flags &= ~GV_PLEX_THREAD_ACTIVE; 948 p->flags &= ~GV_PLEX_THREAD_DIE; 949 p->flags &= ~GV_PLEX_THREAD_DEAD; 950 g_free(p->bqueue); 951 g_free(p->wqueue); 952 p->bqueue = NULL; 953 p->wqueue = NULL; 954 mtx_destroy(&p->bqueue_mtx); 955 } 956 } 957 958 void 959 gv_kill_vol_thread(struct gv_volume *v) 960 { 961 if (v->flags & GV_VOL_THREAD_ACTIVE) { 962 v->flags |= GV_VOL_THREAD_DIE; 963 wakeup(v); 964 while (!(v->flags & GV_VOL_THREAD_DEAD)) 965 tsleep(v, PRIBIO, "gv_die", hz); 966 v->flags &= ~GV_VOL_THREAD_ACTIVE; 967 v->flags &= ~GV_VOL_THREAD_DIE; 968 v->flags &= ~GV_VOL_THREAD_DEAD; 969 g_free(v->bqueue); 970 v->bqueue = NULL; 971 mtx_destroy(&v->bqueue_mtx); 972 } 973 } 974