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