1 /*- 2 * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/kernel.h> 33 #include <sys/module.h> 34 #include <sys/lock.h> 35 #include <sys/mutex.h> 36 #include <sys/bio.h> 37 #include <sys/sysctl.h> 38 #include <sys/malloc.h> 39 #include <geom/geom.h> 40 #include <geom/concat/g_concat.h> 41 42 43 static MALLOC_DEFINE(M_CONCAT, "concat data", "GEOM_CONCAT Data"); 44 45 SYSCTL_DECL(_kern_geom); 46 SYSCTL_NODE(_kern_geom, OID_AUTO, concat, CTLFLAG_RW, 0, "GEOM_CONCAT stuff"); 47 static u_int g_concat_debug = 0; 48 SYSCTL_UINT(_kern_geom_concat, OID_AUTO, debug, CTLFLAG_RW, &g_concat_debug, 0, 49 "Debug level"); 50 51 static int g_concat_destroy(struct g_concat_softc *sc, boolean_t force); 52 static int g_concat_destroy_geom(struct gctl_req *req, struct g_class *mp, 53 struct g_geom *gp); 54 55 static g_taste_t g_concat_taste; 56 static g_ctl_req_t g_concat_config; 57 static g_dumpconf_t g_concat_dumpconf; 58 59 struct g_class g_concat_class = { 60 .name = G_CONCAT_CLASS_NAME, 61 .ctlreq = g_concat_config, 62 .taste = g_concat_taste, 63 .destroy_geom = g_concat_destroy_geom 64 }; 65 66 67 /* 68 * Greatest Common Divisor. 69 */ 70 static u_int 71 gcd(u_int a, u_int b) 72 { 73 u_int c; 74 75 while (b != 0) { 76 c = a; 77 a = b; 78 b = (c % b); 79 } 80 return (a); 81 } 82 83 /* 84 * Least Common Multiple. 85 */ 86 static u_int 87 lcm(u_int a, u_int b) 88 { 89 90 return ((a * b) / gcd(a, b)); 91 } 92 93 /* 94 * Return the number of valid disks. 95 */ 96 static u_int 97 g_concat_nvalid(struct g_concat_softc *sc) 98 { 99 u_int i, no; 100 101 no = 0; 102 for (i = 0; i < sc->sc_ndisks; i++) { 103 if (sc->sc_disks[i].d_consumer != NULL) 104 no++; 105 } 106 107 return (no); 108 } 109 110 static void 111 g_concat_remove_disk(struct g_concat_disk *disk) 112 { 113 struct g_consumer *cp; 114 struct g_concat_softc *sc; 115 116 KASSERT(disk->d_consumer != NULL, ("Non-valid disk in %s.", __func__)); 117 sc = disk->d_softc; 118 cp = disk->d_consumer; 119 120 G_CONCAT_DEBUG(0, "Disk %s removed from %s.", cp->provider->name, 121 sc->sc_name); 122 123 disk->d_consumer = NULL; 124 if (sc->sc_provider != NULL) { 125 g_orphan_provider(sc->sc_provider, ENXIO); 126 sc->sc_provider = NULL; 127 G_CONCAT_DEBUG(0, "Device %s removed.", sc->sc_name); 128 } 129 130 if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) 131 g_access(cp, -cp->acr, -cp->acw, -cp->ace); 132 g_detach(cp); 133 g_destroy_consumer(cp); 134 } 135 136 static void 137 g_concat_orphan(struct g_consumer *cp) 138 { 139 struct g_concat_softc *sc; 140 struct g_concat_disk *disk; 141 struct g_geom *gp; 142 143 g_topology_assert(); 144 gp = cp->geom; 145 sc = gp->softc; 146 if (sc == NULL) 147 return; 148 149 disk = cp->private; 150 if (disk == NULL) /* Possible? */ 151 return; 152 g_concat_remove_disk(disk); 153 154 /* If there are no valid disks anymore, remove device. */ 155 if (g_concat_nvalid(sc) == 0) 156 g_concat_destroy(sc, 1); 157 } 158 159 static int 160 g_concat_access(struct g_provider *pp, int dr, int dw, int de) 161 { 162 struct g_consumer *cp1, *cp2; 163 struct g_concat_softc *sc; 164 struct g_geom *gp; 165 int error; 166 167 gp = pp->geom; 168 sc = gp->softc; 169 170 if (sc == NULL) { 171 /* 172 * It looks like geom is being withered. 173 * In that case we allow only negative requests. 174 */ 175 KASSERT(dr <= 0 && dw <= 0 && de <= 0, 176 ("Positive access request (device=%s).", pp->name)); 177 if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && 178 (pp->ace + de) == 0) { 179 G_CONCAT_DEBUG(0, "Device %s definitely destroyed.", 180 gp->name); 181 } 182 return (0); 183 } 184 185 /* On first open, grab an extra "exclusive" bit */ 186 if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) 187 de++; 188 /* ... and let go of it on last close */ 189 if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && (pp->ace + de) == 0) 190 de--; 191 192 error = ENXIO; 193 LIST_FOREACH(cp1, &gp->consumer, consumer) { 194 error = g_access(cp1, dr, dw, de); 195 if (error == 0) 196 continue; 197 /* 198 * If we fail here, backout all previous changes. 199 */ 200 LIST_FOREACH(cp2, &gp->consumer, consumer) { 201 if (cp1 == cp2) 202 return (error); 203 g_access(cp2, -dr, -dw, -de); 204 } 205 /* NOTREACHED */ 206 } 207 208 return (error); 209 } 210 211 static void 212 g_concat_start(struct bio *bp) 213 { 214 struct g_concat_softc *sc; 215 struct g_concat_disk *disk; 216 struct g_provider *pp; 217 off_t offset, end, length, off, len; 218 struct bio *cbp; 219 char *addr; 220 u_int no; 221 222 pp = bp->bio_to; 223 sc = pp->geom->softc; 224 /* 225 * If sc == NULL, provider's error should be set and g_concat_start() 226 * should not be called at all. 227 */ 228 KASSERT(sc != NULL, 229 ("Provider's error should be set (error=%d)(device=%s).", 230 bp->bio_to->error, bp->bio_to->name)); 231 232 G_CONCAT_LOGREQ(bp, "Request received."); 233 234 switch (bp->bio_cmd) { 235 case BIO_READ: 236 case BIO_WRITE: 237 case BIO_DELETE: 238 break; 239 case BIO_GETATTR: 240 /* To which provider it should be delivered? */ 241 default: 242 g_io_deliver(bp, EOPNOTSUPP); 243 return; 244 } 245 246 offset = bp->bio_offset; 247 length = bp->bio_length; 248 addr = bp->bio_data; 249 end = offset + length; 250 251 for (no = 0; no < sc->sc_ndisks; no++) { 252 disk = &sc->sc_disks[no]; 253 if (disk->d_end <= offset) 254 continue; 255 if (disk->d_start >= end) 256 break; 257 258 off = offset - disk->d_start; 259 len = MIN(length, disk->d_end - offset); 260 length -= len; 261 offset += len; 262 263 cbp = g_clone_bio(bp); 264 if (cbp == NULL) { 265 if (bp->bio_error == 0) 266 bp->bio_error = ENOMEM; 267 return; 268 } 269 /* 270 * Fill in the component buf structure. 271 */ 272 cbp->bio_done = g_std_done; 273 cbp->bio_offset = off; 274 cbp->bio_data = addr; 275 addr += len; 276 cbp->bio_length = len; 277 cbp->bio_to = disk->d_consumer->provider; 278 G_CONCAT_LOGREQ(cbp, "Sending request."); 279 g_io_request(cbp, disk->d_consumer); 280 281 if (length == 0) 282 break; 283 } 284 285 KASSERT(length == 0, 286 ("Length is still greater than 0 (class=%s, name=%s).", 287 bp->bio_to->geom->class->name, bp->bio_to->geom->name)); 288 } 289 290 static void 291 g_concat_check_and_run(struct g_concat_softc *sc) 292 { 293 struct g_concat_disk *disk; 294 u_int no, sectorsize = 0; 295 off_t start; 296 297 if (g_concat_nvalid(sc) != sc->sc_ndisks) 298 return; 299 300 sc->sc_provider = g_new_providerf(sc->sc_geom, "concat/%s", 301 sc->sc_name); 302 start = 0; 303 for (no = 0; no < sc->sc_ndisks; no++) { 304 disk = &sc->sc_disks[no]; 305 disk->d_start = start; 306 disk->d_end = disk->d_start + 307 disk->d_consumer->provider->mediasize; 308 if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) 309 disk->d_end -= disk->d_consumer->provider->sectorsize; 310 start = disk->d_end; 311 if (no == 0) 312 sectorsize = disk->d_consumer->provider->sectorsize; 313 else { 314 sectorsize = lcm(sectorsize, 315 disk->d_consumer->provider->sectorsize); 316 } 317 } 318 sc->sc_provider->sectorsize = sectorsize; 319 /* We have sc->sc_disks[sc->sc_ndisks - 1].d_end in 'start'. */ 320 sc->sc_provider->mediasize = start; 321 g_error_provider(sc->sc_provider, 0); 322 323 G_CONCAT_DEBUG(0, "Device %s activated.", sc->sc_name); 324 } 325 326 static int 327 g_concat_read_metadata(struct g_consumer *cp, struct g_concat_metadata *md) 328 { 329 struct g_provider *pp; 330 u_char *buf; 331 int error; 332 333 g_topology_assert(); 334 335 error = g_access(cp, 1, 0, 0); 336 if (error != 0) 337 return (error); 338 pp = cp->provider; 339 g_topology_unlock(); 340 buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 341 &error); 342 g_topology_lock(); 343 g_access(cp, -1, 0, 0); 344 if (buf == NULL) 345 return (error); 346 347 /* Decode metadata. */ 348 concat_metadata_decode(buf, md); 349 g_free(buf); 350 351 return (0); 352 } 353 354 /* 355 * Add disk to given device. 356 */ 357 static int 358 g_concat_add_disk(struct g_concat_softc *sc, struct g_provider *pp, u_int no) 359 { 360 struct g_concat_disk *disk; 361 struct g_consumer *cp, *fcp; 362 struct g_geom *gp; 363 int error; 364 365 /* Metadata corrupted? */ 366 if (no >= sc->sc_ndisks) 367 return (EINVAL); 368 369 disk = &sc->sc_disks[no]; 370 /* Check if disk is not already attached. */ 371 if (disk->d_consumer != NULL) 372 return (EEXIST); 373 374 gp = sc->sc_geom; 375 fcp = LIST_FIRST(&gp->consumer); 376 377 cp = g_new_consumer(gp); 378 error = g_attach(cp, pp); 379 if (error != 0) { 380 g_destroy_consumer(cp); 381 return (error); 382 } 383 384 if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) { 385 error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); 386 if (error != 0) { 387 g_detach(cp); 388 g_destroy_consumer(cp); 389 return (error); 390 } 391 } 392 if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) { 393 struct g_concat_metadata md; 394 395 /* Re-read metadata. */ 396 error = g_concat_read_metadata(cp, &md); 397 if (error != 0) 398 goto fail; 399 400 if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0 || 401 strcmp(md.md_name, sc->sc_name) != 0 || 402 md.md_id != sc->sc_id) { 403 G_CONCAT_DEBUG(0, "Metadata on %s changed.", pp->name); 404 goto fail; 405 } 406 } 407 408 cp->private = disk; 409 disk->d_consumer = cp; 410 disk->d_softc = sc; 411 disk->d_start = 0; /* not yet */ 412 disk->d_end = 0; /* not yet */ 413 414 G_CONCAT_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name); 415 416 g_concat_check_and_run(sc); 417 418 return (0); 419 fail: 420 if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) 421 g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace); 422 g_detach(cp); 423 g_destroy_consumer(cp); 424 return (error); 425 } 426 427 static struct g_geom * 428 g_concat_create(struct g_class *mp, const struct g_concat_metadata *md, 429 u_int type) 430 { 431 struct g_concat_softc *sc; 432 struct g_geom *gp; 433 u_int no; 434 435 G_CONCAT_DEBUG(1, "Creating device %s (id=%u).", md->md_name, 436 md->md_id); 437 438 /* Two disks is minimum. */ 439 if (md->md_all <= 1) 440 return (NULL); 441 442 /* Check for duplicate unit */ 443 LIST_FOREACH(gp, &mp->geom, geom) { 444 sc = gp->softc; 445 if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) { 446 G_CONCAT_DEBUG(0, "Device %s already configured.", 447 gp->name); 448 return (NULL); 449 } 450 } 451 gp = g_new_geomf(mp, "%s", md->md_name); 452 gp->softc = NULL; /* for a moment */ 453 454 sc = malloc(sizeof(*sc), M_CONCAT, M_WAITOK | M_ZERO); 455 gp->start = g_concat_start; 456 gp->spoiled = g_concat_orphan; 457 gp->orphan = g_concat_orphan; 458 gp->access = g_concat_access; 459 gp->dumpconf = g_concat_dumpconf; 460 461 sc->sc_id = md->md_id; 462 sc->sc_ndisks = md->md_all; 463 sc->sc_disks = malloc(sizeof(struct g_concat_disk) * sc->sc_ndisks, 464 M_CONCAT, M_WAITOK | M_ZERO); 465 for (no = 0; no < sc->sc_ndisks; no++) 466 sc->sc_disks[no].d_consumer = NULL; 467 sc->sc_type = type; 468 469 gp->softc = sc; 470 sc->sc_geom = gp; 471 sc->sc_provider = NULL; 472 473 G_CONCAT_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id); 474 475 return (gp); 476 } 477 478 static int 479 g_concat_destroy(struct g_concat_softc *sc, boolean_t force) 480 { 481 struct g_provider *pp; 482 struct g_geom *gp; 483 u_int no; 484 485 g_topology_assert(); 486 487 if (sc == NULL) 488 return (ENXIO); 489 490 pp = sc->sc_provider; 491 if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 492 if (force) { 493 G_CONCAT_DEBUG(0, "Device %s is still open, so it " 494 "can't be definitely removed.", pp->name); 495 } else { 496 G_CONCAT_DEBUG(1, 497 "Device %s is still open (r%dw%de%d).", pp->name, 498 pp->acr, pp->acw, pp->ace); 499 return (EBUSY); 500 } 501 } 502 503 for (no = 0; no < sc->sc_ndisks; no++) { 504 if (sc->sc_disks[no].d_consumer != NULL) 505 g_concat_remove_disk(&sc->sc_disks[no]); 506 } 507 508 gp = sc->sc_geom; 509 gp->softc = NULL; 510 KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)", 511 gp->name)); 512 free(sc->sc_disks, M_CONCAT); 513 free(sc, M_CONCAT); 514 515 pp = LIST_FIRST(&gp->provider); 516 if (pp == NULL || (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)) 517 G_CONCAT_DEBUG(0, "Device %s destroyed.", gp->name); 518 519 g_wither_geom(gp, ENXIO); 520 521 return (0); 522 } 523 524 static int 525 g_concat_destroy_geom(struct gctl_req *req __unused, 526 struct g_class *mp __unused, struct g_geom *gp) 527 { 528 struct g_concat_softc *sc; 529 530 sc = gp->softc; 531 return (g_concat_destroy(sc, 0)); 532 } 533 534 static struct g_geom * 535 g_concat_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 536 { 537 struct g_concat_metadata md; 538 struct g_concat_softc *sc; 539 struct g_consumer *cp; 540 struct g_geom *gp; 541 int error; 542 543 g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); 544 g_topology_assert(); 545 546 G_CONCAT_DEBUG(3, "Tasting %s.", pp->name); 547 548 gp = g_new_geomf(mp, "concat:taste"); 549 gp->start = g_concat_start; 550 gp->access = g_concat_access; 551 gp->orphan = g_concat_orphan; 552 cp = g_new_consumer(gp); 553 g_attach(cp, pp); 554 555 error = g_concat_read_metadata(cp, &md); 556 g_wither_geom(gp, ENXIO); 557 if (error != 0) 558 return (NULL); 559 gp = NULL; 560 561 if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0) 562 return (NULL); 563 if (md.md_version > G_CONCAT_VERSION) { 564 printf("geom_concat.ko module is too old to handle %s.\n", 565 pp->name); 566 return (NULL); 567 } 568 569 /* 570 * Let's check if device already exists. 571 */ 572 sc = NULL; 573 LIST_FOREACH(gp, &mp->geom, geom) { 574 sc = gp->softc; 575 if (sc == NULL) 576 continue; 577 if (sc->sc_type != G_CONCAT_TYPE_AUTOMATIC) 578 continue; 579 if (strcmp(md.md_name, sc->sc_name) != 0) 580 continue; 581 if (md.md_id != sc->sc_id) 582 continue; 583 break; 584 } 585 if (gp != NULL) { 586 G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 587 error = g_concat_add_disk(sc, pp, md.md_no); 588 if (error != 0) { 589 G_CONCAT_DEBUG(0, 590 "Cannot add disk %s to %s (error=%d).", pp->name, 591 gp->name, error); 592 return (NULL); 593 } 594 } else { 595 gp = g_concat_create(mp, &md, G_CONCAT_TYPE_AUTOMATIC); 596 if (gp == NULL) { 597 G_CONCAT_DEBUG(0, "Cannot create device %s.", 598 md.md_name); 599 return (NULL); 600 } 601 sc = gp->softc; 602 G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 603 error = g_concat_add_disk(sc, pp, md.md_no); 604 if (error != 0) { 605 G_CONCAT_DEBUG(0, 606 "Cannot add disk %s to %s (error=%d).", pp->name, 607 gp->name, error); 608 g_concat_destroy(sc, 1); 609 return (NULL); 610 } 611 } 612 613 return (gp); 614 } 615 616 static void 617 g_concat_ctl_create(struct gctl_req *req, struct g_class *mp) 618 { 619 u_int attached, no; 620 struct g_concat_metadata md; 621 struct g_provider *pp; 622 struct g_concat_softc *sc; 623 struct g_geom *gp; 624 struct sbuf *sb; 625 const char *name; 626 char param[16]; 627 int *nargs; 628 629 g_topology_assert(); 630 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 631 if (nargs == NULL) { 632 gctl_error(req, "No '%s' argument.", "nargs"); 633 return; 634 } 635 if (*nargs <= 2) { 636 gctl_error(req, "Too few arguments."); 637 return; 638 } 639 640 strlcpy(md.md_magic, G_CONCAT_MAGIC, sizeof(md.md_magic)); 641 md.md_version = G_CONCAT_VERSION; 642 name = gctl_get_asciiparam(req, "arg0"); 643 if (name == NULL) { 644 gctl_error(req, "No 'arg%u' argument.", 0); 645 return; 646 } 647 strlcpy(md.md_name, name, sizeof(md.md_name)); 648 md.md_id = arc4random(); 649 md.md_no = 0; 650 md.md_all = *nargs - 1; 651 652 /* Check all providers are valid */ 653 for (no = 1; no < *nargs; no++) { 654 snprintf(param, sizeof(param), "arg%u", no); 655 name = gctl_get_asciiparam(req, param); 656 if (name == NULL) { 657 gctl_error(req, "No 'arg%u' argument.", no); 658 return; 659 } 660 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 661 name += strlen("/dev/"); 662 pp = g_provider_by_name(name); 663 if (pp == NULL) { 664 G_CONCAT_DEBUG(1, "Disk %s is invalid.", name); 665 gctl_error(req, "Disk %s is invalid.", name); 666 return; 667 } 668 } 669 670 gp = g_concat_create(mp, &md, G_CONCAT_TYPE_MANUAL); 671 if (gp == NULL) { 672 gctl_error(req, "Can't configure %s.", md.md_name); 673 return; 674 } 675 676 sc = gp->softc; 677 sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 678 sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name); 679 for (attached = 0, no = 1; no < *nargs; no++) { 680 snprintf(param, sizeof(param), "arg%u", no); 681 name = gctl_get_asciiparam(req, param); 682 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 683 name += strlen("/dev/"); 684 pp = g_provider_by_name(name); 685 KASSERT(pp != NULL, ("Provider %s disappear?!", name)); 686 if (g_concat_add_disk(sc, pp, no - 1) != 0) { 687 G_CONCAT_DEBUG(1, "Disk %u (%s) not attached to %s.", 688 no, pp->name, gp->name); 689 sbuf_printf(sb, " %s", pp->name); 690 continue; 691 } 692 attached++; 693 } 694 sbuf_finish(sb); 695 if (md.md_all != attached) { 696 g_concat_destroy(gp->softc, 1); 697 gctl_error(req, "%s", sbuf_data(sb)); 698 } 699 sbuf_delete(sb); 700 } 701 702 static struct g_concat_softc * 703 g_concat_find_device(struct g_class *mp, const char *name) 704 { 705 struct g_concat_softc *sc; 706 struct g_geom *gp; 707 708 LIST_FOREACH(gp, &mp->geom, geom) { 709 sc = gp->softc; 710 if (sc == NULL) 711 continue; 712 if (strcmp(sc->sc_name, name) == 0) 713 return (sc); 714 } 715 return (NULL); 716 } 717 718 static void 719 g_concat_ctl_destroy(struct gctl_req *req, struct g_class *mp) 720 { 721 struct g_concat_softc *sc; 722 int *force, *nargs, error; 723 const char *name; 724 char param[16]; 725 u_int i; 726 727 g_topology_assert(); 728 729 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 730 if (nargs == NULL) { 731 gctl_error(req, "No '%s' argument.", "nargs"); 732 return; 733 } 734 if (*nargs <= 0) { 735 gctl_error(req, "Missing device(s)."); 736 return; 737 } 738 force = gctl_get_paraml(req, "force", sizeof(*force)); 739 if (force == NULL) { 740 gctl_error(req, "No '%s' argument.", "force"); 741 return; 742 } 743 744 for (i = 0; i < (u_int)*nargs; i++) { 745 snprintf(param, sizeof(param), "arg%u", i); 746 name = gctl_get_asciiparam(req, param); 747 if (name == NULL) { 748 gctl_error(req, "No 'arg%u' argument.", i); 749 return; 750 } 751 sc = g_concat_find_device(mp, name); 752 if (sc == NULL) { 753 gctl_error(req, "No such device: %s.", name); 754 return; 755 } 756 error = g_concat_destroy(sc, *force); 757 if (error != 0) { 758 gctl_error(req, "Cannot destroy device %s (error=%d).", 759 sc->sc_name, error); 760 return; 761 } 762 } 763 } 764 765 static void 766 g_concat_config(struct gctl_req *req, struct g_class *mp, const char *verb) 767 { 768 uint32_t *version; 769 770 g_topology_assert(); 771 772 version = gctl_get_paraml(req, "version", sizeof(*version)); 773 if (version == NULL) { 774 gctl_error(req, "No '%s' argument.", "version"); 775 return; 776 } 777 if (*version != G_CONCAT_VERSION) { 778 gctl_error(req, "Userland and kernel parts are out of sync."); 779 return; 780 } 781 782 if (strcmp(verb, "create") == 0) { 783 g_concat_ctl_create(req, mp); 784 return; 785 } else if (strcmp(verb, "destroy") == 0 || 786 strcmp(verb, "stop") == 0) { 787 g_concat_ctl_destroy(req, mp); 788 return; 789 } 790 gctl_error(req, "Unknown verb."); 791 } 792 793 static void 794 g_concat_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 795 struct g_consumer *cp, struct g_provider *pp) 796 { 797 struct g_concat_softc *sc; 798 799 g_topology_assert(); 800 sc = gp->softc; 801 if (sc == NULL) 802 return; 803 if (pp != NULL) { 804 /* Nothing here. */ 805 } else if (cp != NULL) { 806 struct g_concat_disk *disk; 807 808 disk = cp->private; 809 if (disk == NULL) 810 return; 811 sbuf_printf(sb, "%s<End>%jd</End>\n", indent, 812 (intmax_t)disk->d_end); 813 sbuf_printf(sb, "%s<Start>%jd</Start>\n", indent, 814 (intmax_t)disk->d_start); 815 } else { 816 sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id); 817 sbuf_printf(sb, "%s<Type>", indent); 818 switch (sc->sc_type) { 819 case G_CONCAT_TYPE_AUTOMATIC: 820 sbuf_printf(sb, "AUTOMATIC"); 821 break; 822 case G_CONCAT_TYPE_MANUAL: 823 sbuf_printf(sb, "MANUAL"); 824 break; 825 default: 826 sbuf_printf(sb, "UNKNOWN"); 827 break; 828 } 829 sbuf_printf(sb, "</Type>\n"); 830 sbuf_printf(sb, "%s<Status>Total=%u, Online=%u</Status>\n", 831 indent, sc->sc_ndisks, g_concat_nvalid(sc)); 832 sbuf_printf(sb, "%s<State>", indent); 833 if (sc->sc_provider != NULL && sc->sc_provider->error == 0) 834 sbuf_printf(sb, "UP"); 835 else 836 sbuf_printf(sb, "DOWN"); 837 sbuf_printf(sb, "</State>\n"); 838 } 839 } 840 841 DECLARE_GEOM_CLASS(g_concat_class, g_concat); 842