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 return; 202 } else { 203 printf("GEOM_MULTIPATH: %s now active path in %s\n", 204 sc->cp_active->provider->name, sc->sc_name); 205 } 206 } 207 g_topology_unlock(); 208 209 /* 210 * If we can fruitfully restart the I/O, do so. 211 */ 212 if (sc->cp_active) { 213 g_destroy_bio(bp); 214 pbp->bio_children--; 215 g_multipath_start(pbp); 216 } else { 217 g_std_done(bp); 218 } 219 } 220 221 static void 222 g_multipath_kt(void *arg) 223 { 224 g_multipath_kt_state = GKT_RUN; 225 mtx_lock(&gmtbq_mtx); 226 while (g_multipath_kt_state == GKT_RUN) { 227 for (;;) { 228 struct bio *bp; 229 bp = bioq_takefirst(&gmtbq); 230 if (bp == NULL) { 231 break; 232 } 233 mtx_unlock(&gmtbq_mtx); 234 g_multipath_done_error(bp); 235 mtx_lock(&gmtbq_mtx); 236 } 237 msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO, 238 "gkt:wait", hz / 10); 239 } 240 mtx_unlock(&gmtbq_mtx); 241 wakeup(&g_multipath_kt_state); 242 kproc_exit(0); 243 } 244 245 246 static int 247 g_multipath_access(struct g_provider *pp, int dr, int dw, int de) 248 { 249 struct g_geom *gp; 250 struct g_consumer *cp, *badcp = NULL; 251 int error; 252 253 gp = pp->geom; 254 255 LIST_FOREACH(cp, &gp->consumer, consumer) { 256 error = g_access(cp, dr, dw, de); 257 if (error) { 258 badcp = cp; 259 goto fail; 260 } 261 } 262 return (0); 263 264 fail: 265 LIST_FOREACH(cp, &gp->consumer, consumer) { 266 if (cp == badcp) { 267 break; 268 } 269 (void) g_access(cp, -dr, -dw, -de); 270 } 271 return (error); 272 } 273 274 static struct g_geom * 275 g_multipath_create(struct g_class *mp, struct g_multipath_metadata *md) 276 { 277 struct g_multipath_softc *sc; 278 struct g_geom *gp; 279 struct g_provider *pp; 280 281 g_topology_assert(); 282 283 LIST_FOREACH(gp, &mp->geom, geom) { 284 if (strcmp(gp->name, md->md_name) == 0) { 285 printf("GEOM_MULTIPATH: name %s already exists\n", 286 md->md_name); 287 return (NULL); 288 } 289 } 290 291 gp = g_new_geomf(mp, md->md_name); 292 if (gp == NULL) { 293 goto fail; 294 } 295 296 sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO); 297 if (sc == NULL) { 298 goto fail; 299 } 300 301 gp->softc = sc; 302 gp->start = g_multipath_start; 303 gp->orphan = g_multipath_orphan; 304 gp->access = g_multipath_access; 305 memcpy(sc->sc_uuid, md->md_uuid, sizeof (sc->sc_uuid)); 306 memcpy(sc->sc_name, md->md_name, sizeof (sc->sc_name)); 307 308 pp = g_new_providerf(gp, "multipath/%s", md->md_name); 309 if (pp == NULL) { 310 goto fail; 311 } 312 /* limit the provider to not have it stomp on metadata */ 313 pp->mediasize = md->md_size - md->md_sectorsize; 314 pp->sectorsize = md->md_sectorsize; 315 sc->pp = pp; 316 g_error_provider(pp, 0); 317 return (gp); 318 fail: 319 if (gp != NULL) { 320 if (gp->softc != NULL) { 321 g_free(gp->softc); 322 } 323 g_destroy_geom(gp); 324 } 325 return (NULL); 326 } 327 328 static int 329 g_multipath_add_disk(struct g_geom *gp, struct g_provider *pp) 330 { 331 struct g_multipath_softc *sc; 332 struct g_consumer *cp, *nxtcp; 333 int error; 334 335 g_topology_assert(); 336 337 sc = gp->softc; 338 KASSERT(sc, ("no softc")); 339 340 /* 341 * Make sure that the passed provider isn't already attached 342 */ 343 LIST_FOREACH(cp, &gp->consumer, consumer) { 344 if (cp->provider == pp) { 345 break; 346 } 347 } 348 if (cp) { 349 printf("GEOM_MULTIPATH: provider %s already attached to %s\n", 350 pp->name, gp->name); 351 return (EEXIST); 352 } 353 nxtcp = LIST_FIRST(&gp->consumer); 354 cp = g_new_consumer(gp); 355 if (cp == NULL) { 356 return (ENOMEM); 357 } 358 error = g_attach(cp, pp); 359 if (error != 0) { 360 printf("GEOM_MULTIPATH: cannot attach %s to %s", 361 pp->name, sc->sc_name); 362 g_destroy_consumer(cp); 363 return (error); 364 } 365 cp->private = sc; 366 cp->index = 0; 367 368 /* 369 * Set access permissions on new consumer to match other consumers 370 */ 371 if (nxtcp && (nxtcp->acr + nxtcp->acw + nxtcp->ace)) { 372 error = g_access(cp, nxtcp->acr, nxtcp->acw, nxtcp->ace); 373 if (error) { 374 printf("GEOM_MULTIPATH: cannot set access in " 375 "attaching %s to %s/%s (%d)\n", 376 pp->name, sc->sc_name, sc->sc_uuid, error); 377 g_detach(cp); 378 g_destroy_consumer(cp); 379 return (error); 380 } 381 } 382 printf("GEOM_MULTIPATH: adding %s to %s/%s\n", 383 pp->name, sc->sc_name, sc->sc_uuid); 384 if (sc->cp_active == NULL) { 385 sc->cp_active = cp; 386 printf("GEOM_MULTIPATH: %s now active path in %s\n", 387 pp->name, sc->sc_name); 388 } 389 return (0); 390 } 391 392 static int 393 g_multipath_destroy(struct g_geom *gp) 394 { 395 struct g_provider *pp; 396 397 g_topology_assert(); 398 if (gp->softc == NULL) { 399 return (ENXIO); 400 } 401 pp = LIST_FIRST(&gp->provider); 402 if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 403 return (EBUSY); 404 } 405 printf("GEOM_MULTIPATH: destroying %s\n", gp->name); 406 g_free(gp->softc); 407 gp->softc = NULL; 408 g_wither_geom(gp, ENXIO); 409 return (0); 410 } 411 412 static int 413 g_multipath_destroy_geom(struct gctl_req *req, struct g_class *mp, 414 struct g_geom *gp) 415 { 416 return (g_multipath_destroy(gp)); 417 } 418 419 static void 420 g_multipath_init(struct g_class *mp) 421 { 422 bioq_init(&gmtbq); 423 mtx_init(&gmtbq_mtx, "gmtbq", NULL, MTX_DEF); 424 if (kproc_create(g_multipath_kt, mp, NULL, 0, 0, "g_mp_kt") == 0) { 425 g_multipath_kt_state = GKT_RUN; 426 } 427 } 428 429 static void 430 g_multipath_fini(struct g_class *mp) 431 { 432 if (g_multipath_kt_state == GKT_RUN) { 433 mtx_lock(&gmtbq_mtx); 434 g_multipath_kt_state = GKT_DIE; 435 wakeup(&g_multipath_kt_state); 436 msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO, 437 "gmp:fini", 0); 438 mtx_unlock(&gmtbq_mtx); 439 } 440 } 441 442 static int 443 g_multipath_read_metadata(struct g_consumer *cp, 444 struct g_multipath_metadata *md) 445 { 446 struct g_provider *pp; 447 u_char *buf; 448 int error; 449 450 g_topology_assert(); 451 error = g_access(cp, 1, 0, 0); 452 if (error != 0) { 453 return (error); 454 } 455 pp = cp->provider; 456 g_topology_unlock(); 457 buf = g_read_data(cp, pp->mediasize - pp->sectorsize, 458 pp->sectorsize, &error); 459 g_topology_lock(); 460 g_access(cp, -1, 0, 0); 461 if (buf == NULL) { 462 return (error); 463 } 464 multipath_metadata_decode(buf, md); 465 g_free(buf); 466 return (0); 467 } 468 469 static struct g_geom * 470 g_multipath_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 471 { 472 struct g_multipath_metadata md; 473 struct g_multipath_softc *sc; 474 struct g_consumer *cp; 475 struct g_geom *gp, *gp1; 476 int error, isnew; 477 478 g_topology_assert(); 479 480 gp = g_new_geomf(mp, "multipath:taste"); 481 gp->start = g_multipath_start; 482 gp->access = g_multipath_access; 483 gp->orphan = g_multipath_orphan; 484 cp = g_new_consumer(gp); 485 g_attach(cp, pp); 486 error = g_multipath_read_metadata(cp, &md); 487 g_detach(cp); 488 g_destroy_consumer(cp); 489 g_destroy_geom(gp); 490 if (error != 0) { 491 return (NULL); 492 } 493 gp = NULL; 494 495 if (strcmp(md.md_magic, G_MULTIPATH_MAGIC) != 0) { 496 if (g_multipath_debug) { 497 printf("%s is not MULTIPATH\n", pp->name); 498 } 499 return (NULL); 500 } 501 if (md.md_version != G_MULTIPATH_VERSION) { 502 printf("%s has version %d multipath id- this module is version " 503 " %d: rejecting\n", pp->name, md.md_version, 504 G_MULTIPATH_VERSION); 505 return (NULL); 506 } 507 if (g_multipath_debug) { 508 printf("MULTIPATH: %s/%s\n", md.md_name, md.md_uuid); 509 } 510 511 /* 512 * Let's check if such a device already is present. We check against 513 * uuid alone first because that's the true distinguishor. If that 514 * passes, then we check for name conflicts. If there are conflicts, 515 * modify the name. 516 * 517 * The whole purpose of this is to solve the problem that people don't 518 * pick good unique names, but good unique names (like uuids) are a 519 * pain to use. So, we allow people to build GEOMs with friendly names 520 * and uuids, and modify the names in case there's a collision. 521 */ 522 sc = NULL; 523 LIST_FOREACH(gp, &mp->geom, geom) { 524 sc = gp->softc; 525 if (sc == NULL) { 526 continue; 527 } 528 if (strncmp(md.md_uuid, sc->sc_uuid, sizeof(md.md_uuid)) == 0) { 529 break; 530 } 531 } 532 533 LIST_FOREACH(gp1, &mp->geom, geom) { 534 if (gp1 == gp) { 535 continue; 536 } 537 sc = gp1->softc; 538 if (sc == NULL) { 539 continue; 540 } 541 if (strncmp(md.md_name, sc->sc_name, sizeof(md.md_name)) == 0) { 542 break; 543 } 544 } 545 546 /* 547 * If gp is NULL, we had no extant MULTIPATH geom with this uuid. 548 * 549 * If gp1 is *not* NULL, that means we have a MULTIPATH geom extant 550 * with the same name (but a different UUID). 551 * 552 * If gp is NULL, then modify the name with a random number and 553 * complain, but allow the creation of the geom to continue. 554 * 555 * If gp is *not* NULL, just use the geom's name as we're attaching 556 * this disk to the (previously generated) name. 557 */ 558 559 if (gp1) { 560 sc = gp1->softc; 561 if (gp == NULL) { 562 char buf[16]; 563 u_long rand = random(); 564 565 snprintf(buf, sizeof (buf), "%s-%lu", md.md_name, rand); 566 printf("GEOM_MULTIPATH: geom %s/%s exists already\n", 567 sc->sc_name, sc->sc_uuid); 568 printf("GEOM_MULTIPATH: %s will be (temporarily) %s\n", 569 md.md_uuid, buf); 570 strlcpy(md.md_name, buf, sizeof (md.md_name)); 571 } else { 572 strlcpy(md.md_name, sc->sc_name, sizeof (md.md_name)); 573 } 574 } 575 576 if (gp == NULL) { 577 gp = g_multipath_create(mp, &md); 578 if (gp == NULL) { 579 printf("GEOM_MULTIPATH: cannot create geom %s/%s\n", 580 md.md_name, md.md_uuid); 581 return (NULL); 582 } 583 isnew = 1; 584 } else { 585 isnew = 0; 586 } 587 588 sc = gp->softc; 589 KASSERT(sc != NULL, ("sc is NULL")); 590 error = g_multipath_add_disk(gp, pp); 591 if (error != 0) { 592 if (isnew) { 593 g_multipath_destroy(gp); 594 } 595 return (NULL); 596 } 597 return (gp); 598 } 599 600 static void 601 g_multipath_ctl_create(struct gctl_req *req, struct g_class *mp) 602 { 603 struct g_geom *gp; 604 struct g_provider *pp0, *pp1; 605 struct g_multipath_metadata md; 606 const char *name, *mpname, *uuid; 607 static const char devpf[6] = "/dev/"; 608 int *nargs, error; 609 610 g_topology_assert(); 611 612 mpname = gctl_get_asciiparam(req, "arg0"); 613 if (mpname == NULL) { 614 gctl_error(req, "No 'arg0' argument"); 615 return; 616 } 617 618 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 619 if (nargs == NULL) { 620 gctl_error(req, "No 'nargs' argument"); 621 return; 622 } 623 if (*nargs != 4) { 624 gctl_error(req, "missing device or uuid arguments"); 625 return; 626 } 627 628 name = gctl_get_asciiparam(req, "arg1"); 629 if (name == NULL) { 630 gctl_error(req, "No 'arg1' argument"); 631 return; 632 } 633 if (strncmp(name, devpf, 5) == 0) { 634 name += 5; 635 } 636 pp0 = g_provider_by_name(name); 637 if (pp0 == NULL) { 638 gctl_error(req, "Provider %s is invalid", name); 639 return; 640 } 641 642 name = gctl_get_asciiparam(req, "arg2"); 643 if (name == NULL) { 644 gctl_error(req, "No 'arg2' argument"); 645 return; 646 } 647 if (strncmp(name, devpf, 5) == 0) { 648 name += 5; 649 } 650 pp1 = g_provider_by_name(name); 651 if (pp1 == NULL) { 652 gctl_error(req, "Provider %s is invalid", name); 653 return; 654 } 655 656 uuid = gctl_get_asciiparam(req, "arg3"); 657 if (uuid == NULL) { 658 gctl_error(req, "No uuid argument"); 659 return; 660 } 661 if (strlen(uuid) != 36) { 662 gctl_error(req, "Malformed uuid argument"); 663 return; 664 } 665 666 /* 667 * Check to make sure parameters from the two providers are the same 668 */ 669 if (pp0 == pp1) { 670 gctl_error(req, "providers %s and %s are the same", 671 pp0->name, pp1->name); 672 return; 673 } 674 if (pp0->mediasize != pp1->mediasize) { 675 gctl_error(req, "Provider %s is %jd; Provider %s is %jd", 676 pp0->name, (intmax_t) pp0->mediasize, 677 pp1->name, (intmax_t) pp1->mediasize); 678 return; 679 } 680 if (pp0->sectorsize != pp1->sectorsize) { 681 gctl_error(req, "Provider %s has sectorsize %u; Provider %s " 682 "has sectorsize %u", pp0->name, pp0->sectorsize, 683 pp1->name, pp1->sectorsize); 684 return; 685 } 686 687 /* 688 * cons up enough of a metadata structure to use. 689 */ 690 memset(&md, 0, sizeof(md)); 691 md.md_size = pp0->mediasize; 692 md.md_sectorsize = pp0->sectorsize; 693 strncpy(md.md_name, mpname, sizeof (md.md_name)); 694 strncpy(md.md_uuid, uuid, sizeof (md.md_uuid)); 695 696 gp = g_multipath_create(mp, &md); 697 if (gp == NULL) { 698 return; 699 } 700 error = g_multipath_add_disk(gp, pp0); 701 if (error) { 702 g_multipath_destroy(gp); 703 return; 704 } 705 error = g_multipath_add_disk(gp, pp1); 706 if (error) { 707 g_multipath_destroy(gp); 708 return; 709 } 710 } 711 712 static struct g_geom * 713 g_multipath_find_geom(struct g_class *mp, const char *name) 714 { 715 struct g_geom *gp; 716 717 LIST_FOREACH(gp, &mp->geom, geom) { 718 if (strcmp(gp->name, name) == 0) { 719 return (gp); 720 } 721 } 722 return (NULL); 723 } 724 725 static void 726 g_multipath_ctl_destroy(struct gctl_req *req, struct g_class *mp) 727 { 728 struct g_geom *gp; 729 const char *name; 730 int error; 731 732 g_topology_assert(); 733 734 name = gctl_get_asciiparam(req, "arg0"); 735 if (name == NULL) { 736 gctl_error(req, "No 'arg0' argument"); 737 return; 738 } 739 gp = g_multipath_find_geom(mp, name); 740 if (gp == NULL) { 741 gctl_error(req, "Device %s is invalid", name); 742 return; 743 } 744 error = g_multipath_destroy(gp); 745 if (error != 0) { 746 gctl_error(req, "failed to destroy %s (err=%d)", name, error); 747 } 748 } 749 750 static void 751 g_multipath_config(struct gctl_req *req, struct g_class *mp, const char *verb) 752 { 753 uint32_t *version; 754 g_topology_assert(); 755 version = gctl_get_paraml(req, "version", sizeof(*version)); 756 if (version == NULL) { 757 gctl_error(req, "No 'version' argument"); 758 } else if (*version != G_MULTIPATH_VERSION) { 759 gctl_error(req, "Userland and kernel parts are out of sync"); 760 } else if (strcmp(verb, "create") == 0) { 761 g_multipath_ctl_create(req, mp); 762 } else if (strcmp(verb, "destroy") == 0) { 763 g_multipath_ctl_destroy(req, mp); 764 } else { 765 gctl_error(req, "Unknown verb %s", verb); 766 } 767 } 768 DECLARE_GEOM_CLASS(g_multipath_class, g_multipath); 769