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 p->state = GV_PLEX_DOWN; 495 } 496 } 497 498 /* 499 * Give a subdisk to a drive, check and adjust several parameters, adjust 500 * freelist. 501 */ 502 int 503 gv_sd_to_drive(struct gv_softc *sc, struct gv_drive *d, struct gv_sd *s, 504 char *errstr, int errlen) 505 { 506 struct gv_sd *s2; 507 struct gv_freelist *fl, *fl2; 508 off_t tmp; 509 int i; 510 511 g_topology_assert(); 512 513 fl2 = NULL; 514 515 KASSERT(sc != NULL, ("gv_sd_to_drive: NULL softc")); 516 KASSERT(d != NULL, ("gv_sd_to_drive: NULL drive")); 517 KASSERT(s != NULL, ("gv_sd_to_drive: NULL subdisk")); 518 KASSERT(errstr != NULL, ("gv_sd_to_drive: NULL errstr")); 519 KASSERT(errlen >= ERRBUFSIZ, ("gv_sd_to_drive: short errlen (%d)", 520 errlen)); 521 522 /* Check if this subdisk was already given to this drive. */ 523 if (s->drive_sc == d) 524 return (0); 525 526 /* Preliminary checks. */ 527 if (s->size > d->avail || d->freelist_entries == 0) { 528 snprintf(errstr, errlen, "not enough space on '%s' for '%s'", 529 d->name, s->name); 530 return (-1); 531 } 532 533 /* No size given, autosize it. */ 534 if (s->size == -1) { 535 /* Find the largest available slot. */ 536 LIST_FOREACH(fl, &d->freelist, freelist) { 537 if (fl->size >= s->size) { 538 s->size = fl->size; 539 s->drive_offset = fl->offset; 540 fl2 = fl; 541 } 542 } 543 544 /* No good slot found? */ 545 if (s->size == -1) { 546 snprintf(errstr, errlen, "couldn't autosize '%s' on " 547 "'%s'", s->name, d->name); 548 return (-1); 549 } 550 551 /* 552 * Check if we have a free slot that's large enough for the given size. 553 */ 554 } else { 555 i = 0; 556 LIST_FOREACH(fl, &d->freelist, freelist) { 557 /* Yes, this subdisk fits. */ 558 if (fl->size >= s->size) { 559 i++; 560 /* Assign drive offset, if not given. */ 561 if (s->drive_offset == -1) 562 s->drive_offset = fl->offset; 563 fl2 = fl; 564 break; 565 } 566 } 567 568 /* Couldn't find a good free slot. */ 569 if (i == 0) { 570 snprintf(errstr, errlen, "free slots to small for '%s' " 571 "on '%s'", s->name, d->name); 572 return (-1); 573 } 574 } 575 576 /* No drive offset given, try to calculate it. */ 577 if (s->drive_offset == -1) { 578 579 /* Add offsets and sizes from other subdisks on this drive. */ 580 LIST_FOREACH(s2, &d->subdisks, from_drive) { 581 s->drive_offset = s2->drive_offset + s2->size; 582 } 583 584 /* 585 * If there are no other subdisks yet, then set the default 586 * offset to GV_DATA_START. 587 */ 588 if (s->drive_offset == -1) 589 s->drive_offset = GV_DATA_START; 590 591 /* Check if we have a free slot at the given drive offset. */ 592 } else { 593 i = 0; 594 LIST_FOREACH(fl, &d->freelist, freelist) { 595 /* Yes, this subdisk fits. */ 596 if ((fl->offset <= s->drive_offset) && 597 (fl->offset + fl->size >= 598 s->drive_offset + s->size)) { 599 i++; 600 fl2 = fl; 601 break; 602 } 603 } 604 605 /* Couldn't find a good free slot. */ 606 if (i == 0) { 607 snprintf(errstr, errlen, "given drive_offset for '%s' " 608 "won't fit on '%s'", s->name, d->name); 609 return (-1); 610 } 611 } 612 613 /* 614 * Now that all parameters are checked and set up, we can give the 615 * subdisk to the drive and adjust the freelist. 616 */ 617 618 /* First, adjust the freelist. */ 619 LIST_FOREACH(fl, &d->freelist, freelist) { 620 621 /* This is the free slot that we have found before. */ 622 if (fl == fl2) { 623 624 /* 625 * The subdisk starts at the beginning of the free 626 * slot. 627 */ 628 if (fl->offset == s->drive_offset) { 629 fl->offset += s->size; 630 fl->size -= s->size; 631 632 /* 633 * The subdisk uses the whole slot, so remove 634 * it. 635 */ 636 if (fl->size == 0) { 637 d->freelist_entries--; 638 LIST_REMOVE(fl, freelist); 639 } 640 /* 641 * The subdisk does not start at the beginning of the 642 * free slot. 643 */ 644 } else { 645 tmp = fl->offset + fl->size; 646 fl->size = s->drive_offset - fl->offset; 647 648 /* 649 * The subdisk didn't use the complete rest of 650 * the free slot, so we need to split it. 651 */ 652 if (s->drive_offset + s->size != tmp) { 653 fl2 = g_malloc(sizeof(*fl2), 654 M_WAITOK | M_ZERO); 655 fl2->offset = s->drive_offset + s->size; 656 fl2->size = tmp - fl2->offset; 657 LIST_INSERT_AFTER(fl, fl2, freelist); 658 d->freelist_entries++; 659 } 660 } 661 break; 662 } 663 } 664 665 /* 666 * This is the first subdisk on this drive, just insert it into the 667 * list. 668 */ 669 if (LIST_EMPTY(&d->subdisks)) { 670 LIST_INSERT_HEAD(&d->subdisks, s, from_drive); 671 672 /* There are other subdisks, so insert this one in correct order. */ 673 } else { 674 LIST_FOREACH(s2, &d->subdisks, from_drive) { 675 if (s->drive_offset < s2->drive_offset) { 676 LIST_INSERT_BEFORE(s2, s, from_drive); 677 break; 678 } else if (LIST_NEXT(s2, from_drive) == NULL) { 679 LIST_INSERT_AFTER(s2, s, from_drive); 680 break; 681 } 682 } 683 } 684 685 d->sdcount++; 686 d->avail -= s->size; 687 688 /* Link back from the subdisk to this drive. */ 689 s->drive_sc = d; 690 691 return (0); 692 } 693 694 void 695 gv_free_sd(struct gv_sd *s) 696 { 697 struct gv_drive *d; 698 struct gv_freelist *fl, *fl2; 699 700 KASSERT(s != NULL, ("gv_free_sd: NULL s")); 701 702 d = s->drive_sc; 703 if (d == NULL) 704 return; 705 706 /* 707 * First, find the free slot that's immediately before or after this 708 * subdisk. 709 */ 710 fl = NULL; 711 LIST_FOREACH(fl, &d->freelist, freelist) { 712 if (fl->offset == s->drive_offset + s->size) 713 break; 714 if (fl->offset + fl->size == s->drive_offset) 715 break; 716 } 717 718 /* If there is no free slot behind this subdisk, so create one. */ 719 if (fl == NULL) { 720 721 fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO); 722 fl->size = s->size; 723 fl->offset = s->drive_offset; 724 725 if (d->freelist_entries == 0) { 726 LIST_INSERT_HEAD(&d->freelist, fl, freelist); 727 } else { 728 LIST_FOREACH(fl2, &d->freelist, freelist) { 729 if (fl->offset < fl2->offset) { 730 LIST_INSERT_BEFORE(fl2, fl, freelist); 731 break; 732 } else if (LIST_NEXT(fl2, freelist) == NULL) { 733 LIST_INSERT_AFTER(fl2, fl, freelist); 734 break; 735 } 736 } 737 } 738 739 d->freelist_entries++; 740 741 /* Expand the free slot we just found. */ 742 } else { 743 fl->size += s->size; 744 if (fl->offset > s->drive_offset) 745 fl->offset = s->drive_offset; 746 } 747 748 d->avail += s->size; 749 d->sdcount--; 750 } 751 752 void 753 gv_adjust_freespace(struct gv_sd *s, off_t remainder) 754 { 755 struct gv_drive *d; 756 struct gv_freelist *fl, *fl2; 757 758 KASSERT(s != NULL, ("gv_adjust_freespace: NULL s")); 759 d = s->drive_sc; 760 KASSERT(d != NULL, ("gv_adjust_freespace: NULL d")); 761 762 /* First, find the free slot that's immediately after this subdisk. */ 763 fl = NULL; 764 LIST_FOREACH(fl, &d->freelist, freelist) { 765 if (fl->offset == s->drive_offset + s->size) 766 break; 767 } 768 769 /* If there is no free slot behind this subdisk, so create one. */ 770 if (fl == NULL) { 771 772 fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO); 773 fl->size = remainder; 774 fl->offset = s->drive_offset + s->size - remainder; 775 776 if (d->freelist_entries == 0) { 777 LIST_INSERT_HEAD(&d->freelist, fl, freelist); 778 } else { 779 LIST_FOREACH(fl2, &d->freelist, freelist) { 780 if (fl->offset < fl2->offset) { 781 LIST_INSERT_BEFORE(fl2, fl, freelist); 782 break; 783 } else if (LIST_NEXT(fl2, freelist) == NULL) { 784 LIST_INSERT_AFTER(fl2, fl, freelist); 785 break; 786 } 787 } 788 } 789 790 d->freelist_entries++; 791 792 /* Expand the free slot we just found. */ 793 } else { 794 fl->offset -= remainder; 795 fl->size += remainder; 796 } 797 798 s->size -= remainder; 799 d->avail += remainder; 800 } 801 802 /* Check if the given plex is a striped one. */ 803 int 804 gv_is_striped(struct gv_plex *p) 805 { 806 KASSERT(p != NULL, ("gv_is_striped: NULL p")); 807 switch(p->org) { 808 case GV_PLEX_STRIPED: 809 case GV_PLEX_RAID5: 810 return (1); 811 default: 812 return (0); 813 } 814 } 815 816 /* Find a volume by name. */ 817 struct gv_volume * 818 gv_find_vol(struct gv_softc *sc, char *name) 819 { 820 struct gv_volume *v; 821 822 LIST_FOREACH(v, &sc->volumes, volume) { 823 if (!strncmp(v->name, name, GV_MAXVOLNAME)) 824 return (v); 825 } 826 827 return (NULL); 828 } 829 830 /* Find a plex by name. */ 831 struct gv_plex * 832 gv_find_plex(struct gv_softc *sc, char *name) 833 { 834 struct gv_plex *p; 835 836 LIST_FOREACH(p, &sc->plexes, plex) { 837 if (!strncmp(p->name, name, GV_MAXPLEXNAME)) 838 return (p); 839 } 840 841 return (NULL); 842 } 843 844 /* Find a subdisk by name. */ 845 struct gv_sd * 846 gv_find_sd(struct gv_softc *sc, char *name) 847 { 848 struct gv_sd *s; 849 850 LIST_FOREACH(s, &sc->subdisks, sd) { 851 if (!strncmp(s->name, name, GV_MAXSDNAME)) 852 return (s); 853 } 854 855 return (NULL); 856 } 857 858 /* Find a drive by name. */ 859 struct gv_drive * 860 gv_find_drive(struct gv_softc *sc, char *name) 861 { 862 struct gv_drive *d; 863 864 LIST_FOREACH(d, &sc->drives, drive) { 865 if (!strncmp(d->name, name, GV_MAXDRIVENAME)) 866 return (d); 867 } 868 869 return (NULL); 870 } 871 872 /* Check if any consumer of the given geom is open. */ 873 int 874 gv_is_open(struct g_geom *gp) 875 { 876 struct g_consumer *cp; 877 878 if (gp == NULL) 879 return (0); 880 881 LIST_FOREACH(cp, &gp->consumer, consumer) { 882 if (cp->acr || cp->acw || cp->ace) 883 return (1); 884 } 885 886 return (0); 887 } 888 889 /* Return the type of object identified by string 'name'. */ 890 int 891 gv_object_type(struct gv_softc *sc, char *name) 892 { 893 struct gv_drive *d; 894 struct gv_plex *p; 895 struct gv_sd *s; 896 struct gv_volume *v; 897 898 LIST_FOREACH(v, &sc->volumes, volume) { 899 if (!strncmp(v->name, name, GV_MAXVOLNAME)) 900 return (GV_TYPE_VOL); 901 } 902 903 LIST_FOREACH(p, &sc->plexes, plex) { 904 if (!strncmp(p->name, name, GV_MAXPLEXNAME)) 905 return (GV_TYPE_PLEX); 906 } 907 908 LIST_FOREACH(s, &sc->subdisks, sd) { 909 if (!strncmp(s->name, name, GV_MAXSDNAME)) 910 return (GV_TYPE_SD); 911 } 912 913 LIST_FOREACH(d, &sc->drives, drive) { 914 if (!strncmp(d->name, name, GV_MAXDRIVENAME)) 915 return (GV_TYPE_DRIVE); 916 } 917 918 return (-1); 919 } 920 921 void 922 gv_kill_drive_thread(struct gv_drive *d) 923 { 924 if (d->flags & GV_DRIVE_THREAD_ACTIVE) { 925 d->flags |= GV_DRIVE_THREAD_DIE; 926 wakeup(d); 927 while (!(d->flags & GV_DRIVE_THREAD_DEAD)) 928 tsleep(d, PRIBIO, "gv_die", hz); 929 d->flags &= ~GV_DRIVE_THREAD_ACTIVE; 930 d->flags &= ~GV_DRIVE_THREAD_DIE; 931 d->flags &= ~GV_DRIVE_THREAD_DEAD; 932 g_free(d->bqueue); 933 d->bqueue = NULL; 934 mtx_destroy(&d->bqueue_mtx); 935 } 936 } 937 938 void 939 gv_kill_plex_thread(struct gv_plex *p) 940 { 941 if (p->flags & GV_PLEX_THREAD_ACTIVE) { 942 p->flags |= GV_PLEX_THREAD_DIE; 943 wakeup(p); 944 while (!(p->flags & GV_PLEX_THREAD_DEAD)) 945 tsleep(p, PRIBIO, "gv_die", hz); 946 p->flags &= ~GV_PLEX_THREAD_ACTIVE; 947 p->flags &= ~GV_PLEX_THREAD_DIE; 948 p->flags &= ~GV_PLEX_THREAD_DEAD; 949 g_free(p->bqueue); 950 g_free(p->wqueue); 951 p->bqueue = NULL; 952 p->wqueue = NULL; 953 mtx_destroy(&p->bqueue_mtx); 954 } 955 } 956 957 void 958 gv_kill_vol_thread(struct gv_volume *v) 959 { 960 if (v->flags & GV_VOL_THREAD_ACTIVE) { 961 v->flags |= GV_VOL_THREAD_DIE; 962 wakeup(v); 963 while (!(v->flags & GV_VOL_THREAD_DEAD)) 964 tsleep(v, PRIBIO, "gv_die", hz); 965 v->flags &= ~GV_VOL_THREAD_ACTIVE; 966 v->flags &= ~GV_VOL_THREAD_DIE; 967 v->flags &= ~GV_VOL_THREAD_DEAD; 968 g_free(v->bqueue); 969 v->bqueue = NULL; 970 mtx_destroy(&v->bqueue_mtx); 971 } 972 } 973