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 /* Find the VINUM class and it's associated geom. */ 58 struct g_geom * 59 find_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 */ 83 void 84 gv_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 */ 176 void 177 gv_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 /dev/%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 int 239 gv_sd_to_plex(struct gv_plex *p, struct gv_sd *s, int check) 240 { 241 struct gv_sd *s2; 242 243 g_topology_assert(); 244 245 /* If this subdisk was already given to this plex, do nothing. */ 246 if (s->plex_sc == p) 247 return (0); 248 249 /* Find the correct plex offset for this subdisk, if needed. */ 250 if (s->plex_offset == -1) { 251 if (p->sdcount) { 252 LIST_FOREACH(s2, &p->subdisks, in_plex) { 253 if (gv_is_striped(p)) 254 s->plex_offset = p->sdcount * 255 p->stripesize; 256 else 257 s->plex_offset = s2->plex_offset + 258 s2->size; 259 } 260 } else 261 s->plex_offset = 0; 262 } 263 264 p->sdcount++; 265 266 /* Adjust the size of our plex. */ 267 switch (p->org) { 268 case GV_PLEX_CONCAT: 269 case GV_PLEX_STRIPED: 270 p->size += s->size; 271 break; 272 273 case GV_PLEX_RAID5: 274 p->size = (p->sdcount - 1) * s->size; 275 break; 276 277 default: 278 break; 279 } 280 281 /* There are no subdisks for this plex yet, just insert it. */ 282 if (LIST_EMPTY(&p->subdisks)) { 283 LIST_INSERT_HEAD(&p->subdisks, s, in_plex); 284 285 /* Insert in correct order, depending on plex_offset. */ 286 } else { 287 LIST_FOREACH(s2, &p->subdisks, in_plex) { 288 if (s->plex_offset < s2->plex_offset) { 289 LIST_INSERT_BEFORE(s2, s, in_plex); 290 break; 291 } else if (LIST_NEXT(s2, in_plex) == NULL) { 292 LIST_INSERT_AFTER(s2, s, in_plex); 293 break; 294 } 295 } 296 } 297 298 s->plex_sc = p; 299 300 return (0); 301 } 302 303 void 304 gv_update_vol_size(struct gv_volume *v, off_t size) 305 { 306 struct g_geom *gp; 307 struct g_provider *pp; 308 309 if (v == NULL) 310 return; 311 312 gp = v->geom; 313 if (gp == NULL) 314 return; 315 316 LIST_FOREACH(pp, &gp->provider, provider) { 317 pp->mediasize = size; 318 } 319 320 v->size = size; 321 } 322 323 void 324 gv_update_plex_config(struct gv_plex *p) 325 { 326 struct gv_sd *s, *s2; 327 off_t remainder; 328 int required_sds, state; 329 330 KASSERT(p != NULL, ("gv_update_plex_config: NULL p")); 331 332 /* This is what we want the plex to be. */ 333 state = GV_PLEX_UP; 334 335 /* The plex was added to an already running volume. */ 336 if (p->flags & GV_PLEX_ADDED) 337 state = GV_PLEX_DOWN; 338 339 switch (p->org) { 340 case GV_PLEX_STRIPED: 341 required_sds = 2; 342 break; 343 case GV_PLEX_RAID5: 344 required_sds = 3; 345 break; 346 case GV_PLEX_CONCAT: 347 default: 348 required_sds = 0; 349 break; 350 } 351 352 if (required_sds) { 353 if (p->sdcount < required_sds) { 354 state = GV_PLEX_DOWN; 355 } 356 357 /* 358 * The subdisks in striped plexes must all have the same size. 359 */ 360 s = LIST_FIRST(&p->subdisks); 361 LIST_FOREACH(s2, &p->subdisks, in_plex) { 362 if (s->size != s2->size) { 363 printf("geom_vinum: subdisk size mismatch " 364 "%s (%jd) <> %s (%jd)\n", s->name, s->size, 365 s2->name, s2->size); 366 state = GV_PLEX_DOWN; 367 } 368 } 369 370 /* Trim subdisk sizes so that they match the stripe size. */ 371 LIST_FOREACH(s, &p->subdisks, in_plex) { 372 remainder = s->size % p->stripesize; 373 if (remainder) { 374 printf("gvinum: size of sd %s is not a " 375 "multiple of plex stripesize, taking off " 376 "%jd bytes\n", s->name, 377 (intmax_t)remainder); 378 gv_adjust_freespace(s, remainder); 379 } 380 } 381 } 382 383 /* Adjust the size of our plex. */ 384 if (p->sdcount > 0) { 385 p->size = 0; 386 switch (p->org) { 387 case GV_PLEX_CONCAT: 388 LIST_FOREACH(s, &p->subdisks, in_plex) 389 p->size += s->size; 390 break; 391 392 case GV_PLEX_STRIPED: 393 s = LIST_FIRST(&p->subdisks); 394 p->size = p->sdcount * s->size; 395 break; 396 397 case GV_PLEX_RAID5: 398 s = LIST_FIRST(&p->subdisks); 399 p->size = (p->sdcount - 1) * s->size; 400 break; 401 402 default: 403 break; 404 } 405 } 406 407 if (p->sdcount == 0) 408 state = GV_PLEX_DOWN; 409 else if ((p->flags & GV_PLEX_ADDED) || 410 ((p->org == GV_PLEX_RAID5) && (p->flags & GV_PLEX_NEWBORN))) { 411 LIST_FOREACH(s, &p->subdisks, in_plex) 412 s->state = GV_SD_STALE; 413 p->flags &= ~GV_PLEX_ADDED; 414 p->flags &= ~GV_PLEX_NEWBORN; 415 p->state = GV_PLEX_DOWN; 416 } 417 } 418 419 /* 420 * Give a subdisk to a drive, check and adjust several parameters, adjust 421 * freelist. 422 */ 423 int 424 gv_sd_to_drive(struct gv_softc *sc, struct gv_drive *d, struct gv_sd *s, 425 char *errstr, int errlen) 426 { 427 struct gv_sd *s2; 428 struct gv_freelist *fl, *fl2; 429 off_t tmp; 430 int i; 431 432 g_topology_assert(); 433 434 fl2 = NULL; 435 436 KASSERT(sc != NULL, ("gv_sd_to_drive: NULL softc")); 437 KASSERT(d != NULL, ("gv_sd_to_drive: NULL drive")); 438 KASSERT(s != NULL, ("gv_sd_to_drive: NULL subdisk")); 439 KASSERT(errstr != NULL, ("gv_sd_to_drive: NULL errstr")); 440 KASSERT(errlen >= ERRBUFSIZ, ("gv_sd_to_drive: short errlen (%d)", 441 errlen)); 442 443 /* Check if this subdisk was already given to this drive. */ 444 if (s->drive_sc == d) 445 return (0); 446 447 /* Preliminary checks. */ 448 if (s->size > d->avail || d->freelist_entries == 0) { 449 snprintf(errstr, errlen, "not enough space on '%s' for '%s'", 450 d->name, s->name); 451 return (-1); 452 } 453 454 /* No size given, autosize it. */ 455 if (s->size == -1) { 456 /* Find the largest available slot. */ 457 LIST_FOREACH(fl, &d->freelist, freelist) { 458 if (fl->size >= s->size) { 459 s->size = fl->size; 460 s->drive_offset = fl->offset; 461 fl2 = fl; 462 } 463 } 464 465 /* No good slot found? */ 466 if (s->size == -1) { 467 snprintf(errstr, errlen, "couldn't autosize '%s' on " 468 "'%s'", s->name, d->name); 469 return (-1); 470 } 471 472 /* 473 * Check if we have a free slot that's large enough for the given size. 474 */ 475 } else { 476 i = 0; 477 LIST_FOREACH(fl, &d->freelist, freelist) { 478 /* Yes, this subdisk fits. */ 479 if (fl->size >= s->size) { 480 i++; 481 /* Assign drive offset, if not given. */ 482 if (s->drive_offset == -1) 483 s->drive_offset = fl->offset; 484 fl2 = fl; 485 break; 486 } 487 } 488 489 /* Couldn't find a good free slot. */ 490 if (i == 0) { 491 snprintf(errstr, errlen, "free slots to small for '%s' " 492 "on '%s'", s->name, d->name); 493 return (-1); 494 } 495 } 496 497 /* No drive offset given, try to calculate it. */ 498 if (s->drive_offset == -1) { 499 500 /* Add offsets and sizes from other subdisks on this drive. */ 501 LIST_FOREACH(s2, &d->subdisks, from_drive) { 502 s->drive_offset = s2->drive_offset + s2->size; 503 } 504 505 /* 506 * If there are no other subdisks yet, then set the default 507 * offset to GV_DATA_START. 508 */ 509 if (s->drive_offset == -1) 510 s->drive_offset = GV_DATA_START; 511 512 /* Check if we have a free slot at the given drive offset. */ 513 } else { 514 i = 0; 515 LIST_FOREACH(fl, &d->freelist, freelist) { 516 /* Yes, this subdisk fits. */ 517 if ((fl->offset <= s->drive_offset) && 518 (fl->offset + fl->size >= 519 s->drive_offset + s->size)) { 520 i++; 521 fl2 = fl; 522 break; 523 } 524 } 525 526 /* Couldn't find a good free slot. */ 527 if (i == 0) { 528 snprintf(errstr, errlen, "given drive_offset for '%s' " 529 "won't fit on '%s'", s->name, d->name); 530 return (-1); 531 } 532 } 533 534 /* 535 * Now that all parameters are checked and set up, we can give the 536 * subdisk to the drive and adjust the freelist. 537 */ 538 539 /* First, adjust the freelist. */ 540 LIST_FOREACH(fl, &d->freelist, freelist) { 541 542 /* This is the free slot that we have found before. */ 543 if (fl == fl2) { 544 545 /* 546 * The subdisk starts at the beginning of the free 547 * slot. 548 */ 549 if (fl->offset == s->drive_offset) { 550 fl->offset += s->size; 551 fl->size -= s->size; 552 553 /* 554 * The subdisk uses the whole slot, so remove 555 * it. 556 */ 557 if (fl->size == 0) { 558 d->freelist_entries--; 559 LIST_REMOVE(fl, freelist); 560 } 561 /* 562 * The subdisk does not start at the beginning of the 563 * free slot. 564 */ 565 } else { 566 tmp = fl->offset + fl->size; 567 fl->size = s->drive_offset - fl->offset; 568 569 /* 570 * The subdisk didn't use the complete rest of 571 * the free slot, so we need to split it. 572 */ 573 if (s->drive_offset + s->size != tmp) { 574 fl2 = g_malloc(sizeof(*fl2), 575 M_WAITOK | M_ZERO); 576 fl2->offset = s->drive_offset + s->size; 577 fl2->size = tmp - fl2->offset; 578 LIST_INSERT_AFTER(fl, fl2, freelist); 579 d->freelist_entries++; 580 } 581 } 582 break; 583 } 584 } 585 586 /* 587 * This is the first subdisk on this drive, just insert it into the 588 * list. 589 */ 590 if (LIST_EMPTY(&d->subdisks)) { 591 LIST_INSERT_HEAD(&d->subdisks, s, from_drive); 592 593 /* There are other subdisks, so insert this one in correct order. */ 594 } else { 595 LIST_FOREACH(s2, &d->subdisks, from_drive) { 596 if (s->drive_offset < s2->drive_offset) { 597 LIST_INSERT_BEFORE(s2, s, from_drive); 598 break; 599 } else if (LIST_NEXT(s2, from_drive) == NULL) { 600 LIST_INSERT_AFTER(s2, s, from_drive); 601 break; 602 } 603 } 604 } 605 606 d->sdcount++; 607 d->avail -= s->size; 608 609 /* Link back from the subdisk to this drive. */ 610 s->drive_sc = d; 611 612 return (0); 613 } 614 615 void 616 gv_adjust_freespace(struct gv_sd *s, off_t remainder) 617 { 618 struct gv_drive *d; 619 struct gv_freelist *fl, *fl2; 620 621 KASSERT(s != NULL, ("gv_adjust_freespace: NULL s")); 622 d = s->drive_sc; 623 KASSERT(d != NULL, ("gv_adjust_freespace: NULL d")); 624 625 /* First, find the free slot that's immediately after this subdisk. */ 626 fl = NULL; 627 LIST_FOREACH(fl, &d->freelist, freelist) { 628 if (fl->offset == s->drive_offset + s->size) 629 break; 630 } 631 632 /* If there is no free slot behind this subdisk, so create one. */ 633 if (fl == NULL) { 634 635 fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO); 636 fl->size = remainder; 637 fl->offset = s->drive_offset + s->size - remainder; 638 639 if (d->freelist_entries == 0) { 640 LIST_INSERT_HEAD(&d->freelist, fl, freelist); 641 } else { 642 LIST_FOREACH(fl2, &d->freelist, freelist) { 643 if (fl->offset < fl2->offset) { 644 LIST_INSERT_BEFORE(fl2, fl, freelist); 645 break; 646 } else if (LIST_NEXT(fl2, freelist) == NULL) { 647 LIST_INSERT_AFTER(fl2, fl, freelist); 648 break; 649 } 650 } 651 } 652 653 d->freelist_entries++; 654 655 /* Expand the free slot we just found. */ 656 } else { 657 fl->offset -= remainder; 658 fl->size += remainder; 659 } 660 661 s->size -= remainder; 662 d->avail += remainder; 663 } 664 665 /* Check if the given plex is a striped one. */ 666 int 667 gv_is_striped(struct gv_plex *p) 668 { 669 KASSERT(p != NULL, ("gv_is_striped: NULL p")); 670 switch(p->org) { 671 case GV_PLEX_STRIPED: 672 case GV_PLEX_RAID5: 673 return (1); 674 default: 675 return (0); 676 } 677 } 678 679 /* Find a volume by name. */ 680 struct gv_volume * 681 gv_find_vol(struct gv_softc *sc, char *name) 682 { 683 struct gv_volume *v; 684 685 LIST_FOREACH(v, &sc->volumes, volume) { 686 if (!strncmp(v->name, name, GV_MAXVOLNAME)) 687 return (v); 688 } 689 690 return (NULL); 691 } 692 693 /* Find a plex by name. */ 694 struct gv_plex * 695 gv_find_plex(struct gv_softc *sc, char *name) 696 { 697 struct gv_plex *p; 698 699 LIST_FOREACH(p, &sc->plexes, plex) { 700 if (!strncmp(p->name, name, GV_MAXPLEXNAME)) 701 return (p); 702 } 703 704 return (NULL); 705 } 706 707 /* Find a subdisk by name. */ 708 struct gv_sd * 709 gv_find_sd(struct gv_softc *sc, char *name) 710 { 711 struct gv_sd *s; 712 713 LIST_FOREACH(s, &sc->subdisks, sd) { 714 if (!strncmp(s->name, name, GV_MAXSDNAME)) 715 return (s); 716 } 717 718 return (NULL); 719 } 720 721 /* Find a drive by name. */ 722 struct gv_drive * 723 gv_find_drive(struct gv_softc *sc, char *name) 724 { 725 struct gv_drive *d; 726 727 LIST_FOREACH(d, &sc->drives, drive) { 728 if (!strncmp(d->name, name, GV_MAXDRIVENAME)) 729 return (d); 730 } 731 732 return (NULL); 733 } 734 735 /* Check if any consumer of the given geom is open. */ 736 int 737 gv_is_open(struct g_geom *gp) 738 { 739 struct g_consumer *cp; 740 741 if (gp == NULL) 742 return (0); 743 744 LIST_FOREACH(cp, &gp->consumer, consumer) { 745 if (cp->acr || cp->acw || cp->ace) 746 return (1); 747 } 748 749 return (0); 750 } 751 752 /* Return the type of object identified by string 'name'. */ 753 int 754 gv_object_type(struct gv_softc *sc, char *name) 755 { 756 struct gv_drive *d; 757 struct gv_plex *p; 758 struct gv_sd *s; 759 struct gv_volume *v; 760 761 LIST_FOREACH(v, &sc->volumes, volume) { 762 if (!strncmp(v->name, name, GV_MAXVOLNAME)) 763 return (GV_TYPE_VOL); 764 } 765 766 LIST_FOREACH(p, &sc->plexes, plex) { 767 if (!strncmp(p->name, name, GV_MAXPLEXNAME)) 768 return (GV_TYPE_PLEX); 769 } 770 771 LIST_FOREACH(s, &sc->subdisks, sd) { 772 if (!strncmp(s->name, name, GV_MAXSDNAME)) 773 return (GV_TYPE_SD); 774 } 775 776 LIST_FOREACH(d, &sc->drives, drive) { 777 if (!strncmp(d->name, name, GV_MAXDRIVENAME)) 778 return (GV_TYPE_DRIVE); 779 } 780 781 return (-1); 782 } 783 784 void 785 gv_kill_drive_thread(struct gv_drive *d) 786 { 787 if (d->flags & GV_DRIVE_THREAD_ACTIVE) { 788 d->flags |= GV_DRIVE_THREAD_DIE; 789 wakeup(d); 790 while (!(d->flags & GV_DRIVE_THREAD_DEAD)) 791 tsleep(d, PRIBIO, "gv_die", hz); 792 d->flags &= ~GV_DRIVE_THREAD_ACTIVE; 793 d->flags &= ~GV_DRIVE_THREAD_DIE; 794 d->flags &= ~GV_DRIVE_THREAD_DEAD; 795 mtx_destroy(&d->bqueue_mtx); 796 } 797 } 798 799 void 800 gv_kill_plex_thread(struct gv_plex *p) 801 { 802 if (p->flags & GV_PLEX_THREAD_ACTIVE) { 803 p->flags |= GV_PLEX_THREAD_DIE; 804 wakeup(p); 805 while (!(p->flags & GV_PLEX_THREAD_DEAD)) 806 tsleep(p, PRIBIO, "gv_die", hz); 807 p->flags &= ~GV_PLEX_THREAD_ACTIVE; 808 p->flags &= ~GV_PLEX_THREAD_DIE; 809 p->flags &= ~GV_PLEX_THREAD_DEAD; 810 mtx_destroy(&p->bqueue_mtx); 811 } 812 } 813 814 void 815 gv_kill_vol_thread(struct gv_volume *v) 816 { 817 if (v->flags & GV_VOL_THREAD_ACTIVE) { 818 v->flags |= GV_VOL_THREAD_DIE; 819 wakeup(v); 820 while (!(v->flags & GV_VOL_THREAD_DEAD)) 821 tsleep(v, PRIBIO, "gv_die", hz); 822 v->flags &= ~GV_VOL_THREAD_ACTIVE; 823 v->flags &= ~GV_VOL_THREAD_DIE; 824 v->flags &= ~GV_VOL_THREAD_DEAD; 825 mtx_destroy(&v->bqueue_mtx); 826 } 827 } 828