]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/geom/multipath/g_multipath.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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                         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         kthread_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 (kthread_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);