1 /*- 2 * Copyright (c) 2006-2007 Matthew Jacob <mjacob@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 * Based upon work by Pawel Jakub Dawidek <pjd@FreeBSD.org> for all of the 28 * fine geom examples, and by Poul Henning Kamp <phk@FreeBSD.org> for GEOM 29 * itself, all of which is most gratefully acknowledged. 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/kernel.h> 37 #include <sys/module.h> 38 #include <sys/lock.h> 39 #include <sys/mutex.h> 40 #include <sys/bio.h> 41 #include <sys/sysctl.h> 42 #include <sys/kthread.h> 43 #include <sys/malloc.h> 44 #include <geom/geom.h> 45 #include <geom/multipath/g_multipath.h> 46 47 48 SYSCTL_DECL(_kern_geom); 49 SYSCTL_NODE(_kern_geom, OID_AUTO, multipath, CTLFLAG_RW, 0, 50 "GEOM_MULTIPATH tunables"); 51 static u_int g_multipath_debug = 0; 52 SYSCTL_UINT(_kern_geom_multipath, OID_AUTO, debug, CTLFLAG_RW, 53 &g_multipath_debug, 0, "Debug level"); 54 55 static enum { 56 GKT_NIL, 57 GKT_RUN, 58 GKT_DIE 59 } g_multipath_kt_state; 60 static struct bio_queue_head gmtbq; 61 static struct mtx gmtbq_mtx; 62 63 static void g_multipath_orphan(struct g_consumer *); 64 static void g_multipath_start(struct bio *); 65 static void g_multipath_done(struct bio *); 66 static void g_multipath_done_error(struct bio *); 67 static void g_multipath_kt(void *); 68 69 static int g_multipath_destroy(struct g_geom *); 70 static int 71 g_multipath_destroy_geom(struct gctl_req *, struct g_class *, struct g_geom *); 72 73 static g_taste_t g_multipath_taste; 74 static g_ctl_req_t g_multipath_config; 75 static g_init_t g_multipath_init; 76 static g_fini_t g_multipath_fini; 77 78 struct g_class g_multipath_class = { 79 .name = G_MULTIPATH_CLASS_NAME, 80 .version = G_VERSION, 81 .ctlreq = g_multipath_config, 82 .taste = g_multipath_taste, 83 .destroy_geom = g_multipath_destroy_geom, 84 .init = g_multipath_init, 85 .fini = g_multipath_fini 86 }; 87 88 #define MP_BAD 0x1 89 #define MP_POSTED 0x2 90 91 static void 92 g_mpd(void *arg, int flags __unused) 93 { 94 struct g_consumer *cp; 95 96 g_topology_assert(); 97 cp = arg; 98 if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) { 99 g_access(cp, -cp->acr, -cp->acw, -cp->ace); 100 } 101 if (cp->provider) { 102 printf("GEOM_MULTIPATH: %s removed from %s\n", 103 cp->provider->name, cp->geom->name); 104 g_detach(cp); 105 } 106 g_destroy_consumer(cp); 107 } 108 109 static void 110 g_multipath_orphan(struct g_consumer *cp) 111 { 112 if ((cp->index & MP_POSTED) == 0) { 113 cp->index |= MP_POSTED; 114 printf("GEOM_MULTIPATH: %s orphaned in %s\n", 115 cp->provider->name, cp->geom->name); 116 g_mpd(cp, 0); 117 } 118 } 119 120 static void 121 g_multipath_start(struct bio *bp) 122 { 123 struct g_multipath_softc *sc; 124 struct g_geom *gp; 125 struct g_consumer *cp; 126 struct bio *cbp; 127 128 gp = bp->bio_to->geom; 129 sc = gp->softc; 130 KASSERT(sc != NULL, ("NULL sc")); 131 cp = sc->cp_active; 132 if (cp == NULL) { 133 g_io_deliver(bp, ENXIO); 134 return; 135 } 136 cbp = g_clone_bio(bp); 137 if (cbp == NULL) { 138 g_io_deliver(bp, ENOMEM); 139 return; 140 } 141 cbp->bio_done = g_multipath_done; 142 g_io_request(cbp, cp); 143 } 144 145 static void 146 g_multipath_done(struct bio *bp) 147 { 148 if (bp->bio_error == ENXIO || bp->bio_error == EIO) { 149 mtx_lock(&gmtbq_mtx); 150 bioq_insert_tail(&gmtbq, bp); 151 wakeup(&g_multipath_kt_state); 152 mtx_unlock(&gmtbq_mtx); 153 } else { 154 g_std_done(bp); 155 } 156 } 157 158 static void 159 g_multipath_done_error(struct bio *bp) 160 { 161 struct bio *pbp; 162 struct g_geom *gp; 163 struct g_multipath_softc *sc; 164 struct g_consumer *cp; 165 struct g_provider *pp; 166 167 /* 168 * If we had a failure, we have to check first to see 169 * whether the consumer it failed on was the currently 170 * active consumer (i.e., this is the first in perhaps 171 * a number of failures). If so, we then switch consumers 172 * to the next available consumer. 173 */ 174 175 g_topology_lock(); 176 pbp = bp->bio_parent; 177 gp = pbp->bio_to->geom; 178 sc = gp->softc; 179 cp = bp->bio_from; 180 pp = cp->provider; 181 182 cp->index |= MP_BAD; 183 if (cp->nend == cp->nstart && pp->nend == pp->nstart) { 184 cp->index |= MP_POSTED; 185 g_post_event(g_mpd, cp, M_NOWAIT, NULL); 186 } 187 if (cp == sc->cp_active) { 188 struct g_consumer *lcp; 189 printf("GEOM_MULTIPATH: %s failed in %s\n", 190 pp->name, sc->sc_name); 191 sc->cp_active = NULL; 192 LIST_FOREACH(lcp, &gp->consumer, consumer) { 193 if ((lcp->index & MP_BAD) == 0) { 194 sc->cp_active = lcp; 195 break; 196 } 197 } 198 if (sc->cp_active == NULL) { 199 printf("GEOM_MULTIPATH: out of providers for %s\n", 200 sc->sc_name); 201 g_topology_unlock(); 202 return; 203 } else { 204 printf("GEOM_MULTIPATH: %s now active path in %s\n", 205 sc->cp_active->provider->name, sc->sc_name); 206 } 207 } 208 g_topology_unlock(); 209 210 /* 211 * If we can fruitfully restart the I/O, do so. 212 */ 213 if (sc->cp_active) { 214 g_destroy_bio(bp); 215 pbp->bio_children--; 216 g_multipath_start(pbp); 217 } else { 218 g_std_done(bp); 219 } 220 } 221 222 static void 223 g_multipath_kt(void *arg) 224 { 225 g_multipath_kt_state = GKT_RUN; 226 mtx_lock(&gmtbq_mtx); 227 while (g_multipath_kt_state == GKT_RUN) { 228 for (;;) { 229 struct bio *bp; 230 bp = bioq_takefirst(&gmtbq); 231 if (bp == NULL) { 232 break; 233 } 234 mtx_unlock(&gmtbq_mtx); 235 g_multipath_done_error(bp); 236 mtx_lock(&gmtbq_mtx); 237 } 238 msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO, 239 "gkt:wait", hz / 10); 240 } 241 mtx_unlock(&gmtbq_mtx); 242 wakeup(&g_multipath_kt_state); 243 kproc_exit(0); 244 } 245 246 247 static int 248 g_multipath_access(struct g_provider *pp, int dr, int dw, int de) 249 { 250 struct g_geom *gp; 251 struct g_consumer *cp, *badcp = NULL; 252 int error; 253 254 gp = pp->geom; 255 256 LIST_FOREACH(cp, &gp->consumer, consumer) { 257 error = g_access(cp, dr, dw, de); 258 if (error) { 259 badcp = cp; 260 goto fail; 261 } 262 } 263 return (0); 264 265 fail: 266 LIST_FOREACH(cp, &gp->consumer, consumer) { 267 if (cp == badcp) { 268 break; 269 } 270 (void) g_access(cp, -dr, -dw, -de); 271 } 272 return (error); 273 } 274 275 static struct g_geom * 276 g_multipath_create(struct g_class *mp, struct g_multipath_metadata *md) 277 { 278 struct g_multipath_softc *sc; 279 struct g_geom *gp; 280 struct g_provider *pp; 281 282 g_topology_assert(); 283 284 LIST_FOREACH(gp, &mp->geom, geom) { 285 if (strcmp(gp->name, md->md_name) == 0) { 286 printf("GEOM_MULTIPATH: name %s already exists\n", 287 md->md_name); 288 return (NULL); 289 } 290 } 291 292 gp = g_new_geomf(mp, md->md_name); 293 if (gp == NULL) { 294 goto fail; 295 } 296 297 sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO); 298 gp->softc = sc; 299 gp->start = g_multipath_start; 300 gp->orphan = g_multipath_orphan; 301 gp->access = g_multipath_access; 302 memcpy(sc->sc_uuid, md->md_uuid, sizeof (sc->sc_uuid)); 303 memcpy(sc->sc_name, md->md_name, sizeof (sc->sc_name)); 304 305 pp = g_new_providerf(gp, "multipath/%s", md->md_name); 306 if (pp == NULL) { 307 goto fail; 308 } 309 /* limit the provider to not have it stomp on metadata */ 310 pp->mediasize = md->md_size - md->md_sectorsize; 311 pp->sectorsize = md->md_sectorsize; 312 sc->pp = pp; 313 g_error_provider(pp, 0); 314 return (gp); 315 fail: 316 if (gp != NULL) { 317 if (gp->softc != NULL) { 318 g_free(gp->softc); 319 } 320 g_destroy_geom(gp); 321 } 322 return (NULL); 323 } 324 325 static int 326 g_multipath_add_disk(struct g_geom *gp, struct g_provider *pp) 327 { 328 struct g_multipath_softc *sc; 329 struct g_consumer *cp, *nxtcp; 330 int error; 331 332 g_topology_assert(); 333 334 sc = gp->softc; 335 KASSERT(sc, ("no softc")); 336 337 /* 338 * Make sure that the passed provider isn't already attached 339 */ 340 LIST_FOREACH(cp, &gp->consumer, consumer) { 341 if (cp->provider == pp) { 342 break; 343 } 344 } 345 if (cp) { 346 printf("GEOM_MULTIPATH: provider %s already attached to %s\n", 347 pp->name, gp->name); 348 return (EEXIST); 349 } 350 nxtcp = LIST_FIRST(&gp->consumer); 351 cp = g_new_consumer(gp); 352 if (cp == NULL) { 353 return (ENOMEM); 354 } 355 error = g_attach(cp, pp); 356 if (error != 0) { 357 printf("GEOM_MULTIPATH: cannot attach %s to %s", 358 pp->name, sc->sc_name); 359 g_destroy_consumer(cp); 360 return (error); 361 } 362 cp->private = sc; 363 cp->index = 0; 364 365 /* 366 * Set access permissions on new consumer to match other consumers 367 */ 368 if (nxtcp && (nxtcp->acr + nxtcp->acw + nxtcp->ace)) { 369 error = g_access(cp, nxtcp->acr, nxtcp->acw, nxtcp->ace); 370 if (error) { 371 printf("GEOM_MULTIPATH: cannot set access in " 372 "attaching %s to %s/%s (%d)\n", 373 pp->name, sc->sc_name, sc->sc_uuid, error); 374 g_detach(cp); 375 g_destroy_consumer(cp); 376 return (error); 377 } 378 } 379 printf("GEOM_MULTIPATH: adding %s to %s/%s\n", 380 pp->name, sc->sc_name, sc->sc_uuid); 381 if (sc->cp_active == NULL) { 382 sc->cp_active = cp; 383 printf("GEOM_MULTIPATH: %s now active path in %s\n", 384 pp->name, sc->sc_name); 385 } 386 return (0); 387 } 388 389 static int 390 g_multipath_destroy(struct g_geom *gp) 391 { 392 struct g_provider *pp; 393 394 g_topology_assert(); 395 if (gp->softc == NULL) { 396 return (ENXIO); 397 } 398 pp = LIST_FIRST(&gp->provider); 399 if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 400 return (EBUSY); 401 } 402 printf("GEOM_MULTIPATH: destroying %s\n", gp->name); 403 g_free(gp->softc); 404 gp->softc = NULL; 405 g_wither_geom(gp, ENXIO); 406 return (0); 407 } 408 409 static int 410 g_multipath_destroy_geom(struct gctl_req *req, struct g_class *mp, 411 struct g_geom *gp) 412 { 413 return (g_multipath_destroy(gp)); 414 } 415 416 static void 417 g_multipath_init(struct g_class *mp) 418 { 419 bioq_init(&gmtbq); 420 mtx_init(&gmtbq_mtx, "gmtbq", NULL, MTX_DEF); 421 if (kproc_create(g_multipath_kt, mp, NULL, 0, 0, "g_mp_kt") == 0) { 422 g_multipath_kt_state = GKT_RUN; 423 } 424 } 425 426 static void 427 g_multipath_fini(struct g_class *mp) 428 { 429 if (g_multipath_kt_state == GKT_RUN) { 430 mtx_lock(&gmtbq_mtx); 431 g_multipath_kt_state = GKT_DIE; 432 wakeup(&g_multipath_kt_state); 433 msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO, 434 "gmp:fini", 0); 435 mtx_unlock(&gmtbq_mtx); 436 } 437 } 438 439 static int 440 g_multipath_read_metadata(struct g_consumer *cp, 441 struct g_multipath_metadata *md) 442 { 443 struct g_provider *pp; 444 u_char *buf; 445 int error; 446 447 g_topology_assert(); 448 error = g_access(cp, 1, 0, 0); 449 if (error != 0) { 450 return (error); 451 } 452 pp = cp->provider; 453 g_topology_unlock(); 454 buf = g_read_data(cp, pp->mediasize - pp->sectorsize, 455 pp->sectorsize, &error); 456 g_topology_lock(); 457 g_access(cp, -1, 0, 0); 458 if (buf == NULL) { 459 return (error); 460 } 461 multipath_metadata_decode(buf, md); 462 g_free(buf); 463 return (0); 464 } 465 466 static struct g_geom * 467 g_multipath_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 468 { 469 struct g_multipath_metadata md; 470 struct g_multipath_softc *sc; 471 struct g_consumer *cp; 472 struct g_geom *gp, *gp1; 473 int error, isnew; 474 475 g_topology_assert(); 476 477 gp = g_new_geomf(mp, "multipath:taste"); 478 gp->start = g_multipath_start; 479 gp->access = g_multipath_access; 480 gp->orphan = g_multipath_orphan; 481 cp = g_new_consumer(gp); 482 g_attach(cp, pp); 483 error = g_multipath_read_metadata(cp, &md); 484 g_detach(cp); 485 g_destroy_consumer(cp); 486 g_destroy_geom(gp); 487 if (error != 0) { 488 return (NULL); 489 } 490 gp = NULL; 491 492 if (strcmp(md.md_magic, G_MULTIPATH_MAGIC) != 0) { 493 if (g_multipath_debug) { 494 printf("%s is not MULTIPATH\n", pp->name); 495 } 496 return (NULL); 497 } 498 if (md.md_version != G_MULTIPATH_VERSION) { 499 printf("%s has version %d multipath id- this module is version " 500 " %d: rejecting\n", pp->name, md.md_version, 501 G_MULTIPATH_VERSION); 502 return (NULL); 503 } 504 if (g_multipath_debug) { 505 printf("MULTIPATH: %s/%s\n", md.md_name, md.md_uuid); 506 } 507 508 /* 509 * Let's check if such a device already is present. We check against 510 * uuid alone first because that's the true distinguishor. If that 511 * passes, then we check for name conflicts. If there are conflicts, 512 * modify the name. 513 * 514 * The whole purpose of this is to solve the problem that people don't 515 * pick good unique names, but good unique names (like uuids) are a 516 * pain to use. So, we allow people to build GEOMs with friendly names 517 * and uuids, and modify the names in case there's a collision. 518 */ 519 sc = NULL; 520 LIST_FOREACH(gp, &mp->geom, geom) { 521 sc = gp->softc; 522 if (sc == NULL) { 523 continue; 524 } 525 if (strncmp(md.md_uuid, sc->sc_uuid, sizeof(md.md_uuid)) == 0) { 526 break; 527 } 528 } 529 530 LIST_FOREACH(gp1, &mp->geom, geom) { 531 if (gp1 == gp) { 532 continue; 533 } 534 sc = gp1->softc; 535 if (sc == NULL) { 536 continue; 537 } 538 if (strncmp(md.md_name, sc->sc_name, sizeof(md.md_name)) == 0) { 539 break; 540 } 541 } 542 543 /* 544 * If gp is NULL, we had no extant MULTIPATH geom with this uuid. 545 * 546 * If gp1 is *not* NULL, that means we have a MULTIPATH geom extant 547 * with the same name (but a different UUID). 548 * 549 * If gp is NULL, then modify the name with a random number and 550 * complain, but allow the creation of the geom to continue. 551 * 552 * If gp is *not* NULL, just use the geom's name as we're attaching 553 * this disk to the (previously generated) name. 554 */ 555 556 if (gp1) { 557 sc = gp1->softc; 558 if (gp == NULL) { 559 char buf[16]; 560 u_long rand = random(); 561 562 snprintf(buf, sizeof (buf), "%s-%lu", md.md_name, rand); 563 printf("GEOM_MULTIPATH: geom %s/%s exists already\n", 564 sc->sc_name, sc->sc_uuid); 565 printf("GEOM_MULTIPATH: %s will be (temporarily) %s\n", 566 md.md_uuid, buf); 567 strlcpy(md.md_name, buf, sizeof (md.md_name)); 568 } else { 569 strlcpy(md.md_name, sc->sc_name, sizeof (md.md_name)); 570 } 571 } 572 573 if (gp == NULL) { 574 gp = g_multipath_create(mp, &md); 575 if (gp == NULL) { 576 printf("GEOM_MULTIPATH: cannot create geom %s/%s\n", 577 md.md_name, md.md_uuid); 578 return (NULL); 579 } 580 isnew = 1; 581 } else { 582 isnew = 0; 583 } 584 585 sc = gp->softc; 586 KASSERT(sc != NULL, ("sc is NULL")); 587 error = g_multipath_add_disk(gp, pp); 588 if (error != 0) { 589 if (isnew) { 590 g_multipath_destroy(gp); 591 } 592 return (NULL); 593 } 594 return (gp); 595 } 596 597 static void 598 g_multipath_ctl_create(struct gctl_req *req, struct g_class *mp) 599 { 600 struct g_geom *gp; 601 struct g_provider *pp0, *pp1; 602 struct g_multipath_metadata md; 603 const char *name, *mpname, *uuid; 604 static const char devpf[6] = "/dev/"; 605 int *nargs, error; 606 607 g_topology_assert(); 608 609 mpname = gctl_get_asciiparam(req, "arg0"); 610 if (mpname == NULL) { 611 gctl_error(req, "No 'arg0' argument"); 612 return; 613 } 614 615 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 616 if (nargs == NULL) { 617 gctl_error(req, "No 'nargs' argument"); 618 return; 619 } 620 if (*nargs != 4) { 621 gctl_error(req, "missing device or uuid arguments"); 622 return; 623 } 624 625 name = gctl_get_asciiparam(req, "arg1"); 626 if (name == NULL) { 627 gctl_error(req, "No 'arg1' argument"); 628 return; 629 } 630 if (strncmp(name, devpf, 5) == 0) { 631 name += 5; 632 } 633 pp0 = g_provider_by_name(name); 634 if (pp0 == NULL) { 635 gctl_error(req, "Provider %s is invalid", name); 636 return; 637 } 638 639 name = gctl_get_asciiparam(req, "arg2"); 640 if (name == NULL) { 641 gctl_error(req, "No 'arg2' argument"); 642 return; 643 } 644 if (strncmp(name, devpf, 5) == 0) { 645 name += 5; 646 } 647 pp1 = g_provider_by_name(name); 648 if (pp1 == NULL) { 649 gctl_error(req, "Provider %s is invalid", name); 650 return; 651 } 652 653 uuid = gctl_get_asciiparam(req, "arg3"); 654 if (uuid == NULL) { 655 gctl_error(req, "No uuid argument"); 656 return; 657 } 658 if (strlen(uuid) != 36) { 659 gctl_error(req, "Malformed uuid argument"); 660 return; 661 } 662 663 /* 664 * Check to make sure parameters from the two providers are the same 665 */ 666 if (pp0 == pp1) { 667 gctl_error(req, "providers %s and %s are the same", 668 pp0->name, pp1->name); 669 return; 670 } 671 if (pp0->mediasize != pp1->mediasize) { 672 gctl_error(req, "Provider %s is %jd; Provider %s is %jd", 673 pp0->name, (intmax_t) pp0->mediasize, 674 pp1->name, (intmax_t) pp1->mediasize); 675 return; 676 } 677 if (pp0->sectorsize != pp1->sectorsize) { 678 gctl_error(req, "Provider %s has sectorsize %u; Provider %s " 679 "has sectorsize %u", pp0->name, pp0->sectorsize, 680 pp1->name, pp1->sectorsize); 681 return; 682 } 683 684 /* 685 * cons up enough of a metadata structure to use. 686 */ 687 memset(&md, 0, sizeof(md)); 688 md.md_size = pp0->mediasize; 689 md.md_sectorsize = pp0->sectorsize; 690 strncpy(md.md_name, mpname, sizeof (md.md_name)); 691 strncpy(md.md_uuid, uuid, sizeof (md.md_uuid)); 692 693 gp = g_multipath_create(mp, &md); 694 if (gp == NULL) { 695 return; 696 } 697 error = g_multipath_add_disk(gp, pp0); 698 if (error) { 699 g_multipath_destroy(gp); 700 return; 701 } 702 error = g_multipath_add_disk(gp, pp1); 703 if (error) { 704 g_multipath_destroy(gp); 705 return; 706 } 707 } 708 709 static struct g_geom * 710 g_multipath_find_geom(struct g_class *mp, const char *name) 711 { 712 struct g_geom *gp; 713 714 LIST_FOREACH(gp, &mp->geom, geom) { 715 if (strcmp(gp->name, name) == 0) { 716 return (gp); 717 } 718 } 719 return (NULL); 720 } 721 722 static void 723 g_multipath_ctl_destroy(struct gctl_req *req, struct g_class *mp) 724 { 725 struct g_geom *gp; 726 const char *name; 727 int error; 728 729 g_topology_assert(); 730 731 name = gctl_get_asciiparam(req, "arg0"); 732 if (name == NULL) { 733 gctl_error(req, "No 'arg0' argument"); 734 return; 735 } 736 gp = g_multipath_find_geom(mp, name); 737 if (gp == NULL) { 738 gctl_error(req, "Device %s is invalid", name); 739 return; 740 } 741 error = g_multipath_destroy(gp); 742 if (error != 0) { 743 gctl_error(req, "failed to destroy %s (err=%d)", name, error); 744 } 745 } 746 747 static void 748 g_multipath_config(struct gctl_req *req, struct g_class *mp, const char *verb) 749 { 750 uint32_t *version; 751 g_topology_assert(); 752 version = gctl_get_paraml(req, "version", sizeof(*version)); 753 if (version == NULL) { 754 gctl_error(req, "No 'version' argument"); 755 } else if (*version != G_MULTIPATH_VERSION) { 756 gctl_error(req, "Userland and kernel parts are out of sync"); 757 } else if (strcmp(verb, "create") == 0) { 758 g_multipath_ctl_create(req, mp); 759 } else if (strcmp(verb, "destroy") == 0) { 760 g_multipath_ctl_destroy(req, mp); 761 } else { 762 gctl_error(req, "Unknown verb %s", verb); 763 } 764 } 765 DECLARE_GEOM_CLASS(g_multipath_class, g_multipath); 766