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(1, "Removing disk %s from %s.", cp->provider->name, 121 sc->sc_provider->name); 122 123 disk->d_consumer = NULL; 124 125 g_error_provider(sc->sc_provider, ENXIO); 126 127 G_CONCAT_DEBUG(0, "Disk %s removed from %s.", cp->provider->name, 128 sc->sc_provider->name); 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 (sc->sc_type == G_CONCAT_TYPE_MANUAL) { 155 /* 156 * For manually configured devices, don't remove provider 157 * even is there are no vaild disks at all. 158 */ 159 return; 160 } 161 162 /* If there are no valid disks anymore, remove device. */ 163 if (g_concat_nvalid(sc) == 0) 164 g_concat_destroy(sc, 1); 165 } 166 167 static int 168 g_concat_access(struct g_provider *pp, int dr, int dw, int de) 169 { 170 struct g_consumer *cp1, *cp2; 171 struct g_concat_softc *sc; 172 struct g_geom *gp; 173 int error; 174 175 gp = pp->geom; 176 sc = gp->softc; 177 178 if (sc == NULL) { 179 /* 180 * It looks like geom is being withered. 181 * In that case we allow only negative requests. 182 */ 183 KASSERT(dr <= 0 && dw <= 0 && de <= 0, 184 ("Positive access request (device=%s).", pp->name)); 185 if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && 186 (pp->ace + de) == 0) { 187 G_CONCAT_DEBUG(0, "Device %s definitely destroyed.", 188 gp->name); 189 } 190 return (0); 191 } 192 193 /* On first open, grab an extra "exclusive" bit */ 194 if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) 195 de++; 196 /* ... and let go of it on last close */ 197 if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && (pp->ace + de) == 1) 198 de--; 199 200 error = ENXIO; 201 LIST_FOREACH(cp1, &gp->consumer, consumer) { 202 error = g_access(cp1, dr, dw, de); 203 if (error == 0) 204 continue; 205 /* 206 * If we fail here, backout all previous changes. 207 */ 208 LIST_FOREACH(cp2, &gp->consumer, consumer) { 209 if (cp1 == cp2) 210 return (error); 211 g_access(cp2, -dr, -dw, -de); 212 } 213 /* NOTREACHED */ 214 } 215 216 return (error); 217 } 218 219 static void 220 g_concat_start(struct bio *bp) 221 { 222 struct g_concat_softc *sc; 223 struct g_concat_disk *disk; 224 struct g_provider *pp; 225 off_t offset, end, length, off, len; 226 struct bio *cbp; 227 char *addr; 228 u_int no; 229 230 pp = bp->bio_to; 231 sc = pp->geom->softc; 232 /* 233 * If sc == NULL, provider's error should be set and g_concat_start() 234 * should not be called at all. 235 */ 236 KASSERT(sc != NULL, 237 ("Provider's error should be set (error=%d)(device=%s).", 238 bp->bio_to->error, bp->bio_to->name)); 239 240 G_CONCAT_LOGREQ(bp, "Request received."); 241 242 switch (bp->bio_cmd) { 243 case BIO_READ: 244 case BIO_WRITE: 245 case BIO_DELETE: 246 break; 247 case BIO_GETATTR: 248 /* To which provider it should be delivered? */ 249 default: 250 g_io_deliver(bp, EOPNOTSUPP); 251 return; 252 } 253 254 offset = bp->bio_offset; 255 length = bp->bio_length; 256 addr = bp->bio_data; 257 end = offset + length; 258 259 for (no = 0; no < sc->sc_ndisks; no++) { 260 disk = &sc->sc_disks[no]; 261 if (disk->d_end <= offset) 262 continue; 263 if (disk->d_start >= end) 264 break; 265 266 off = offset - disk->d_start; 267 len = MIN(length, disk->d_end - offset); 268 length -= len; 269 offset += len; 270 271 cbp = g_clone_bio(bp); 272 if (cbp == NULL) { 273 if (bp->bio_error == 0) 274 bp->bio_error = ENOMEM; 275 return; 276 } 277 /* 278 * Fill in the component buf structure. 279 */ 280 cbp->bio_done = g_std_done; 281 cbp->bio_offset = off; 282 cbp->bio_data = addr; 283 addr += len; 284 cbp->bio_length = len; 285 cbp->bio_to = disk->d_consumer->provider; 286 G_CONCAT_LOGREQ(cbp, "Sending request."); 287 g_io_request(cbp, disk->d_consumer); 288 289 if (length == 0) 290 break; 291 } 292 293 KASSERT(length == 0, 294 ("Length is still greater than 0 (class=%s, name=%s).", 295 bp->bio_to->geom->class->name, bp->bio_to->geom->name)); 296 } 297 298 static void 299 g_concat_check_and_run(struct g_concat_softc *sc) 300 { 301 struct g_concat_disk *disk; 302 u_int no, sectorsize = 0; 303 off_t start; 304 305 if (g_concat_nvalid(sc) != sc->sc_ndisks) 306 return; 307 308 start = 0; 309 for (no = 0; no < sc->sc_ndisks; no++) { 310 disk = &sc->sc_disks[no]; 311 disk->d_start = start; 312 disk->d_end = disk->d_start + 313 disk->d_consumer->provider->mediasize; 314 if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) 315 disk->d_end -= disk->d_consumer->provider->sectorsize; 316 start = disk->d_end; 317 if (no == 0) 318 sectorsize = disk->d_consumer->provider->sectorsize; 319 else { 320 sectorsize = lcm(sectorsize, 321 disk->d_consumer->provider->sectorsize); 322 } 323 } 324 sc->sc_provider->sectorsize = sectorsize; 325 /* We have sc->sc_disks[sc->sc_ndisks - 1].d_end in 'start'. */ 326 sc->sc_provider->mediasize = start; 327 g_error_provider(sc->sc_provider, 0); 328 329 G_CONCAT_DEBUG(0, "Device %s activated.", sc->sc_provider->name); 330 } 331 332 static int 333 g_concat_read_metadata(struct g_consumer *cp, struct g_concat_metadata *md) 334 { 335 struct g_provider *pp; 336 u_char *buf; 337 int error; 338 339 g_topology_assert(); 340 341 error = g_access(cp, 1, 0, 0); 342 if (error != 0) 343 return (error); 344 pp = cp->provider; 345 g_topology_unlock(); 346 buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 347 &error); 348 g_topology_lock(); 349 g_access(cp, -1, 0, 0); 350 if (buf == NULL) 351 return (error); 352 353 /* Decode metadata. */ 354 concat_metadata_decode(buf, md); 355 g_free(buf); 356 357 return (0); 358 } 359 360 /* 361 * Add disk to given device. 362 */ 363 static int 364 g_concat_add_disk(struct g_concat_softc *sc, struct g_provider *pp, u_int no) 365 { 366 struct g_concat_disk *disk; 367 struct g_provider *ourpp; 368 struct g_consumer *cp; 369 struct g_geom *gp; 370 int error; 371 372 /* Metadata corrupted? */ 373 if (no >= sc->sc_ndisks) 374 return (EINVAL); 375 376 disk = &sc->sc_disks[no]; 377 /* Check if disk is not already attached. */ 378 if (disk->d_consumer != NULL) 379 return (EEXIST); 380 381 ourpp = sc->sc_provider; 382 gp = ourpp->geom; 383 384 cp = g_new_consumer(gp); 385 error = g_attach(cp, pp); 386 if (error != 0) { 387 g_destroy_consumer(cp); 388 return (error); 389 } 390 391 if (ourpp->acr > 0 || ourpp->acw > 0 || ourpp->ace > 0) { 392 error = g_access(cp, ourpp->acr, ourpp->acw, ourpp->ace); 393 if (error != 0) { 394 g_detach(cp); 395 g_destroy_consumer(cp); 396 return (error); 397 } 398 } 399 if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) { 400 struct g_concat_metadata md; 401 402 /* Re-read metadata. */ 403 error = g_concat_read_metadata(cp, &md); 404 if (error != 0) 405 goto fail; 406 407 if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0 || 408 strcmp(md.md_name, sc->sc_name) != 0 || 409 md.md_id != sc->sc_id) { 410 G_CONCAT_DEBUG(0, "Metadata on %s changed.", pp->name); 411 goto fail; 412 } 413 } 414 415 cp->private = disk; 416 disk->d_consumer = cp; 417 disk->d_softc = sc; 418 disk->d_start = 0; /* not yet */ 419 disk->d_end = 0; /* not yet */ 420 421 G_CONCAT_DEBUG(0, "Disk %s attached to %s.", pp->name, gp->name); 422 423 g_concat_check_and_run(sc); 424 425 return (0); 426 fail: 427 if (ourpp->acr > 0 || ourpp->acw > 0 || ourpp->ace > 0) 428 g_access(cp, -ourpp->acr, -ourpp->acw, -ourpp->ace); 429 g_detach(cp); 430 g_destroy_consumer(cp); 431 return (error); 432 } 433 434 static struct g_geom * 435 g_concat_create(struct g_class *mp, const struct g_concat_metadata *md, 436 u_int type) 437 { 438 struct g_provider *pp; 439 struct g_concat_softc *sc; 440 struct g_geom *gp; 441 u_int no; 442 443 G_CONCAT_DEBUG(1, "Creating device %s.concat (id=%u).", md->md_name, 444 md->md_id); 445 446 /* Two disks is minimum. */ 447 if (md->md_all <= 1) 448 return (NULL); 449 450 /* Check for duplicate unit */ 451 LIST_FOREACH(gp, &mp->geom, geom) { 452 sc = gp->softc; 453 if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) { 454 G_CONCAT_DEBUG(0, "Device %s already cofigured.", 455 gp->name); 456 return (NULL); 457 } 458 } 459 gp = g_new_geomf(mp, "%s.concat", md->md_name); 460 gp->softc = NULL; /* for a moment */ 461 462 sc = malloc(sizeof(*sc), M_CONCAT, M_NOWAIT | M_ZERO); 463 if (sc == NULL) { 464 G_CONCAT_DEBUG(0, "Can't allocate memory for device %s.", 465 gp->name); 466 g_destroy_geom(gp); 467 return (NULL); 468 } 469 470 gp->start = g_concat_start; 471 gp->spoiled = g_concat_orphan; 472 gp->orphan = g_concat_orphan; 473 gp->access = g_concat_access; 474 gp->dumpconf = g_concat_dumpconf; 475 476 strlcpy(sc->sc_name, md->md_name, sizeof(sc->sc_name)); 477 sc->sc_id = md->md_id; 478 sc->sc_ndisks = md->md_all; 479 sc->sc_disks = malloc(sizeof(struct g_concat_disk) * sc->sc_ndisks, 480 M_CONCAT, M_WAITOK | M_ZERO); 481 for (no = 0; no < sc->sc_ndisks; no++) 482 sc->sc_disks[no].d_consumer = NULL; 483 sc->sc_type = type; 484 485 gp->softc = sc; 486 487 pp = g_new_providerf(gp, "%s", gp->name); 488 sc->sc_provider = pp; 489 /* 490 * Don't run provider yet (by setting its error to 0), because we're 491 * not aware of its media and sector size. 492 */ 493 494 G_CONCAT_DEBUG(0, "Device %s created (id=%u).", gp->name, sc->sc_id); 495 496 return (gp); 497 } 498 499 static int 500 g_concat_destroy(struct g_concat_softc *sc, boolean_t force) 501 { 502 struct g_provider *pp; 503 struct g_geom *gp; 504 u_int no; 505 506 g_topology_assert(); 507 508 if (sc == NULL) 509 return (ENXIO); 510 511 pp = sc->sc_provider; 512 if (pp->acr != 0 || pp->acw != 0 || pp->ace != 0) { 513 if (force) { 514 G_CONCAT_DEBUG(0, "Device %s is still open, so it " 515 "can't be definitely removed.", 516 sc->sc_provider->name); 517 } else { 518 G_CONCAT_DEBUG(1, 519 "Device %s is still open (r%dw%de%d).", 520 sc->sc_provider->name, pp->acr, pp->acw, pp->ace); 521 return (EBUSY); 522 } 523 } 524 525 g_error_provider(pp, ENXIO); 526 527 for (no = 0; no < sc->sc_ndisks; no++) { 528 if (sc->sc_disks[no].d_consumer != NULL) 529 g_concat_remove_disk(&sc->sc_disks[no]); 530 } 531 532 if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) 533 G_CONCAT_DEBUG(0, "Device %s removed.", pp->name); 534 535 gp = sc->sc_provider->geom; 536 gp->softc = NULL; 537 free(sc->sc_disks, M_CONCAT); 538 free(sc, M_CONCAT); 539 540 g_wither_geom(gp, ENXIO); 541 542 return (0); 543 } 544 545 static int 546 g_concat_destroy_geom(struct gctl_req *req __unused, 547 struct g_class *mp __unused, struct g_geom *gp) 548 { 549 struct g_concat_softc *sc; 550 551 sc = gp->softc; 552 return (g_concat_destroy(sc, 0)); 553 } 554 555 static struct g_geom * 556 g_concat_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 557 { 558 struct g_concat_metadata md; 559 struct g_concat_softc *sc; 560 struct g_consumer *cp; 561 struct g_geom *gp; 562 int error; 563 564 g_trace(G_T_TOPOLOGY, "g_concat_taste(%s, %s)", mp->name, pp->name); 565 g_topology_assert(); 566 567 G_CONCAT_DEBUG(3, "Tasting %s.", pp->name); 568 569 gp = g_new_geomf(mp, "concat:taste"); 570 gp->start = g_concat_start; 571 gp->access = g_concat_access; 572 gp->orphan = g_concat_orphan; 573 cp = g_new_consumer(gp); 574 g_attach(cp, pp); 575 576 error = g_concat_read_metadata(cp, &md); 577 if (error != 0) { 578 g_wither_geom(gp, ENXIO); 579 return (NULL); 580 } 581 g_wither_geom(gp, ENXIO); 582 gp = NULL; 583 584 if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0) 585 return (NULL); 586 if (md.md_version > G_CONCAT_VERSION) { 587 printf("geom_concat.ko module is too old to handle %s.\n", 588 pp->name); 589 return (NULL); 590 } 591 592 /* 593 * Let's check if device already exists. 594 */ 595 sc = NULL; 596 LIST_FOREACH(gp, &mp->geom, geom) { 597 sc = gp->softc; 598 if (sc == NULL) 599 continue; 600 if (sc->sc_type != G_CONCAT_TYPE_AUTOMATIC) 601 continue; 602 if (strcmp(md.md_name, sc->sc_name) != 0) 603 continue; 604 if (md.md_id != sc->sc_id) 605 continue; 606 break; 607 } 608 if (gp != NULL) { 609 G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 610 error = g_concat_add_disk(sc, pp, md.md_no); 611 if (error != 0) { 612 G_CONCAT_DEBUG(0, "Cannot add disk %s to %s.", pp->name, 613 gp->name); 614 return (NULL); 615 } 616 } else { 617 gp = g_concat_create(mp, &md, G_CONCAT_TYPE_AUTOMATIC); 618 if (gp == NULL) { 619 G_CONCAT_DEBUG(0, "Cannot create device %s.concat.", 620 md.md_name); 621 return (NULL); 622 } 623 sc = gp->softc; 624 G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); 625 error = g_concat_add_disk(sc, pp, md.md_no); 626 if (error != 0) { 627 G_CONCAT_DEBUG(0, "Cannot add disk %s to %s.", pp->name, 628 gp->name); 629 g_concat_destroy(sc, 1); 630 return (NULL); 631 } 632 } 633 634 return (gp); 635 } 636 637 static void 638 g_concat_ctl_create(struct gctl_req *req, struct g_class *mp) 639 { 640 u_int attached, no; 641 struct g_concat_metadata *md; 642 struct g_provider *pp; 643 struct g_concat_softc *sc; 644 struct g_geom *gp; 645 struct sbuf *sb; 646 char buf[20]; 647 648 g_topology_assert(); 649 md = gctl_get_paraml(req, "metadata", sizeof(*md)); 650 if (md == NULL) { 651 gctl_error(req, "No '%s' argument.", "metadata"); 652 return; 653 } 654 if (md->md_all <= 1) { 655 G_CONCAT_DEBUG(1, "Invalid 'md_all' value (= %d).", md->md_all); 656 gctl_error(req, "Invalid 'md_all' value (= %d).", md->md_all); 657 return; 658 } 659 660 /* Check all providers are valid */ 661 for (no = 0; no < md->md_all; no++) { 662 snprintf(buf, sizeof(buf), "disk%u", no); 663 pp = gctl_get_provider(req, buf); 664 if (pp == NULL) { 665 G_CONCAT_DEBUG(1, "Disk %u is invalid.", no + 1); 666 gctl_error(req, "Disk %u is invalid.", no + 1); 667 return; 668 } 669 } 670 671 gp = g_concat_create(mp, md, G_CONCAT_TYPE_MANUAL); 672 if (gp == NULL) { 673 gctl_error(req, "Can't configure %s.concat.", md->md_name); 674 return; 675 } 676 677 sc = gp->softc; 678 sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 679 sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name); 680 for (attached = no = 0; no < md->md_all; no++) { 681 snprintf(buf, sizeof(buf), "disk%u", no); 682 pp = gctl_get_provider(req, buf); 683 if (g_concat_add_disk(sc, pp, no) != 0) { 684 G_CONCAT_DEBUG(1, "Disk %u (%s) not attached to %s.", 685 no + 1, pp->name, gp->name); 686 sbuf_printf(sb, " %s", pp->name); 687 continue; 688 } 689 attached++; 690 } 691 sbuf_finish(sb); 692 if (md->md_all != attached) { 693 g_concat_destroy(gp->softc, 1); 694 gctl_error(req, "%s", sbuf_data(sb)); 695 } 696 sbuf_delete(sb); 697 } 698 699 static void 700 g_concat_ctl_destroy(struct gctl_req *req, struct g_geom *gp) 701 { 702 struct g_concat_softc *sc; 703 int *force, error; 704 705 g_topology_assert(); 706 707 force = gctl_get_paraml(req, "force", sizeof(*force)); 708 if (force == NULL) { 709 gctl_error(req, "No '%s' argument.", "force"); 710 return; 711 } 712 sc = gp->softc; 713 if (sc == NULL) { 714 gctl_error(req, "Cannot destroy device %s (not configured).", 715 gp->name); 716 return; 717 } 718 error = g_concat_destroy(sc, *force); 719 if (error != 0) { 720 gctl_error(req, "Cannot destroy device %s (error=%d).", 721 gp->name, error); 722 return; 723 } 724 } 725 726 static void 727 g_concat_config(struct gctl_req *req, struct g_class *mp, const char *verb) 728 { 729 struct g_geom *gp; 730 731 g_topology_assert(); 732 733 if (strcmp(verb, "create device") == 0) { 734 g_concat_ctl_create(req, mp); 735 return; 736 } 737 738 gp = gctl_get_geom(req, mp, "geom"); 739 if (gp == NULL || gp->softc == NULL) { 740 gctl_error(req, "unknown device"); 741 return; 742 } 743 if (strcmp(verb, "destroy device") == 0) { 744 g_concat_ctl_destroy(req, gp); 745 return; 746 } 747 gctl_error(req, "Unknown verb."); 748 } 749 750 static void 751 g_concat_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 752 struct g_consumer *cp, struct g_provider *pp) 753 { 754 struct g_concat_softc *sc; 755 756 sc = gp->softc; 757 if (sc == NULL) 758 return; 759 if (pp == NULL && cp == NULL) { 760 sbuf_printf(sb, "%s<id>%u</id>\n", indent, (u_int)sc->sc_id); 761 switch (sc->sc_type) { 762 case G_CONCAT_TYPE_AUTOMATIC: 763 sbuf_printf(sb, "%s<type>%s</type>\n", indent, 764 "automatic"); 765 break; 766 case G_CONCAT_TYPE_MANUAL: 767 sbuf_printf(sb, "%s<type>%s</type>\n", indent, 768 "manual"); 769 break; 770 default: 771 sbuf_printf(sb, "%s<type>%s</type>\n", indent, 772 "unknown"); 773 break; 774 } 775 } 776 } 777 778 DECLARE_GEOM_CLASS(g_concat_class, g_concat); 779