]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/geom/multipath/g_multipath.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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 int g_multipath_rotate(struct g_geom *);
74
75 static g_taste_t g_multipath_taste;
76 static g_ctl_req_t g_multipath_config;
77 static g_init_t g_multipath_init;
78 static g_fini_t g_multipath_fini;
79
80 struct g_class g_multipath_class = {
81         .name           = G_MULTIPATH_CLASS_NAME,
82         .version        = G_VERSION,
83         .ctlreq         = g_multipath_config,
84         .taste          = g_multipath_taste,
85         .destroy_geom   = g_multipath_destroy_geom,
86         .init           = g_multipath_init,
87         .fini           = g_multipath_fini
88 };
89
90 #define MP_BAD          0x1
91 #define MP_POSTED       0x2
92
93 static void
94 g_mpd(void *arg, int flags __unused)
95 {
96         struct g_consumer *cp;
97
98         g_topology_assert();
99         cp = arg;
100         if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0)
101                 g_access(cp, -cp->acr, -cp->acw, -cp->ace);
102         if (cp->provider) {
103                 printf("GEOM_MULTIPATH: %s removed from %s\n",
104                     cp->provider->name, cp->geom->name);
105                 g_detach(cp);
106         }
107         g_destroy_consumer(cp);
108 }
109
110 static void
111 g_multipath_orphan(struct g_consumer *cp)
112 {
113         if ((cp->index & MP_POSTED) == 0) {
114                 cp->index |= MP_POSTED;
115                 printf("GEOM_MULTIPATH: %s orphaned in %s\n",
116                     cp->provider->name, cp->geom->name);
117                 g_mpd(cp, 0);
118         }
119 }
120
121 static void
122 g_multipath_start(struct bio *bp)
123 {
124         struct g_multipath_softc *sc;
125         struct g_geom *gp;
126         struct g_consumer *cp;
127         struct bio *cbp;
128
129         gp = bp->bio_to->geom;
130         sc = gp->softc;
131         KASSERT(sc != NULL, ("NULL sc"));
132         cp = sc->cp_active;
133         if (cp == NULL) {
134                 g_io_deliver(bp, ENXIO);
135                 return;
136         }
137         cbp = g_clone_bio(bp);
138         if (cbp == NULL) {
139                 g_io_deliver(bp, ENOMEM);
140                 return;
141         }
142         cbp->bio_done = g_multipath_done;
143         g_io_request(cbp, cp);
144 }
145
146 static void
147 g_multipath_done(struct bio *bp)
148 {
149         if (bp->bio_error == ENXIO || bp->bio_error == EIO) {
150                 mtx_lock(&gmtbq_mtx);
151                 bioq_insert_tail(&gmtbq, bp);
152                 wakeup(&g_multipath_kt_state);
153                 mtx_unlock(&gmtbq_mtx);
154         } else {
155                 g_std_done(bp);
156         }
157 }
158
159 static void
160 g_multipath_done_error(struct bio *bp)
161 {
162         struct bio *pbp;
163         struct g_geom *gp;
164         struct g_multipath_softc *sc;
165         struct g_consumer *cp;
166         struct g_provider *pp;
167
168         /*
169          * If we had a failure, we have to check first to see
170          * whether the consumer it failed on was the currently
171          * active consumer (i.e., this is the first in perhaps
172          * a number of failures). If so, we then switch consumers
173          * to the next available consumer.
174          */
175
176         g_topology_lock();
177         pbp = bp->bio_parent;
178         gp = pbp->bio_to->geom;
179         sc = gp->softc;
180         cp = bp->bio_from;
181         pp = cp->provider;
182
183         cp->index |= MP_BAD;
184         if (cp->nend == cp->nstart && pp->nend == pp->nstart) {
185                 cp->index |= MP_POSTED;
186                 g_post_event(g_mpd, cp, M_NOWAIT, NULL);
187         }
188         if (cp == sc->cp_active) {
189                 struct g_consumer *lcp;
190                 printf("GEOM_MULTIPATH: %s failed in %s\n",
191                     pp->name, sc->sc_name);
192                 sc->cp_active = NULL;
193                 LIST_FOREACH(lcp, &gp->consumer, consumer) {
194                         if ((lcp->index & MP_BAD) == 0) {
195                                 sc->cp_active = lcp;
196                                 break;
197                         }
198                 }
199                 if (sc->cp_active == NULL) {
200                         printf("GEOM_MULTIPATH: out of providers for %s\n",
201                             sc->sc_name);
202                         g_topology_unlock();
203                         return;
204                 } else {
205                         printf("GEOM_MULTIPATH: %s now active path in %s\n",
206                             sc->cp_active->provider->name, sc->sc_name);
207                 }
208         }
209         g_topology_unlock();
210
211         /*
212          * If we can fruitfully restart the I/O, do so.
213          */
214         if (sc->cp_active) {
215                 g_destroy_bio(bp);
216                 pbp->bio_children--;
217                 g_multipath_start(pbp);
218         } else {
219                 g_std_done(bp);
220         }
221 }
222
223 static void
224 g_multipath_kt(void *arg)
225 {
226
227         g_multipath_kt_state = GKT_RUN;
228         mtx_lock(&gmtbq_mtx);
229         while (g_multipath_kt_state == GKT_RUN) {
230                 for (;;) {
231                         struct bio *bp;
232
233                         bp = bioq_takefirst(&gmtbq);
234                         if (bp == NULL)
235                                 break;
236                         mtx_unlock(&gmtbq_mtx);
237                         g_multipath_done_error(bp);
238                         mtx_lock(&gmtbq_mtx);
239                 }
240                 msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO,
241                     "gkt:wait", hz / 10);
242         }
243         mtx_unlock(&gmtbq_mtx);
244         wakeup(&g_multipath_kt_state);
245         kproc_exit(0);
246 }
247
248
249 static int
250 g_multipath_access(struct g_provider *pp, int dr, int dw, int de)
251 {
252         struct g_geom *gp;
253         struct g_consumer *cp, *badcp = NULL;
254         int error;
255
256         gp = pp->geom;
257
258         LIST_FOREACH(cp, &gp->consumer, consumer) {
259                 error = g_access(cp, dr, dw, de);
260                 if (error) {
261                         badcp = cp;
262                         goto fail;
263                 }
264         }
265         return (0);
266
267 fail:
268         LIST_FOREACH(cp, &gp->consumer, consumer) {
269                 if (cp == badcp)
270                         break;
271                 (void) g_access(cp, -dr, -dw, -de);
272         }
273         return (error);
274 }
275
276 static struct g_geom *
277 g_multipath_create(struct g_class *mp, struct g_multipath_metadata *md)
278 {
279         struct g_multipath_softc *sc;
280         struct g_geom *gp;
281         struct g_provider *pp;
282
283         g_topology_assert();
284
285         LIST_FOREACH(gp, &mp->geom, geom) {
286                 if (strcmp(gp->name, md->md_name) == 0) {
287                         printf("GEOM_MULTIPATH: name %s already exists\n",
288                             md->md_name);
289                         return (NULL);
290                 }
291         }
292
293         gp = g_new_geomf(mp, md->md_name);
294         if (gp == NULL)
295                 goto fail;
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         /* limit the provider to not have it stomp on metadata */
309         pp->mediasize = md->md_size - md->md_sectorsize;
310         pp->sectorsize = md->md_sectorsize;
311         sc->pp = pp;
312         g_error_provider(pp, 0);
313         return (gp);
314 fail:
315         if (gp != NULL) {
316                 if (gp->softc != NULL)
317                         g_free(gp->softc);
318                 g_destroy_geom(gp);
319         }
320         return (NULL);
321 }
322
323 static int
324 g_multipath_add_disk(struct g_geom *gp, struct g_provider *pp)
325 {
326         struct g_multipath_softc *sc;
327         struct g_consumer *cp, *nxtcp;
328         int error;
329
330         g_topology_assert();
331
332         sc = gp->softc;
333         KASSERT(sc, ("no softc"));
334
335         /*
336          * Make sure that the passed provider isn't already attached
337          */
338         LIST_FOREACH(cp, &gp->consumer, consumer) {
339                 if (cp->provider == pp)
340                         break;
341         }
342         if (cp) {
343                 printf("GEOM_MULTIPATH: provider %s already attached to %s\n",
344                     pp->name, gp->name);
345                 return (EEXIST);
346         }
347         nxtcp = LIST_FIRST(&gp->consumer);
348         cp = g_new_consumer(gp);
349         if (cp == NULL)
350                 return (ENOMEM);
351         error = g_attach(cp, pp);
352         if (error != 0) {
353                 printf("GEOM_MULTIPATH: cannot attach %s to %s",
354                     pp->name, sc->sc_name);
355                 g_destroy_consumer(cp);
356                 return (error);
357         }
358         cp->private = sc;
359         cp->index = 0;
360
361         /*
362          * Set access permissions on new consumer to match other consumers
363          */
364         if (nxtcp && (nxtcp->acr + nxtcp->acw +  nxtcp->ace)) {
365                 error = g_access(cp, nxtcp->acr, nxtcp->acw, nxtcp->ace);
366                 if (error) {
367                         printf("GEOM_MULTIPATH: cannot set access in "
368                             "attaching %s to %s/%s (%d)\n",
369                             pp->name, sc->sc_name, sc->sc_uuid, error);
370                         g_detach(cp);
371                         g_destroy_consumer(cp);
372                         return (error);
373                 }
374         }
375         printf("GEOM_MULTIPATH: adding %s to %s/%s\n",
376             pp->name, sc->sc_name, sc->sc_uuid);
377         if (sc->cp_active == NULL) {
378                 sc->cp_active = cp;
379                 printf("GEOM_MULTIPATH: %s now active path in %s\n",
380                     pp->name, sc->sc_name);
381         }
382         return (0);
383 }
384
385 static int
386 g_multipath_destroy(struct g_geom *gp)
387 {
388         struct g_provider *pp;
389
390         g_topology_assert();
391         if (gp->softc == NULL)
392                 return (ENXIO);
393         pp = LIST_FIRST(&gp->provider);
394         if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0))
395                 return (EBUSY);
396         printf("GEOM_MULTIPATH: destroying %s\n", gp->name);
397         g_free(gp->softc);
398         gp->softc = NULL;
399         g_wither_geom(gp, ENXIO);
400         return (0);
401 }
402
403 static int
404 g_multipath_destroy_geom(struct gctl_req *req, struct g_class *mp,
405     struct g_geom *gp)
406 {
407
408         return (g_multipath_destroy(gp));
409 }
410
411 static int
412 g_multipath_rotate(struct g_geom *gp)
413 {
414         struct g_consumer *lcp;
415         struct g_multipath_softc *sc = gp->softc;
416
417         g_topology_assert();
418         if (sc == NULL)
419                 return (ENXIO);
420         LIST_FOREACH(lcp, &gp->consumer, consumer) {
421                 if ((lcp->index & MP_BAD) == 0) {
422                         if (sc->cp_active != lcp) {
423                                 break;
424                         }
425                 }
426         }
427         if (lcp) {
428                 sc->cp_active = lcp;
429                 printf("GEOM_MULTIPATH: %s now active path in %s\n",
430                     lcp->provider->name, sc->sc_name);
431         }
432         return (0);
433 }
434
435 static void
436 g_multipath_init(struct g_class *mp)
437 {
438         bioq_init(&gmtbq);
439         mtx_init(&gmtbq_mtx, "gmtbq", NULL, MTX_DEF);
440         if (kproc_create(g_multipath_kt, mp, NULL, 0, 0, "g_mp_kt") == 0)
441                 g_multipath_kt_state = GKT_RUN;
442 }
443
444 static void
445 g_multipath_fini(struct g_class *mp)
446 {
447         if (g_multipath_kt_state == GKT_RUN) {
448                 mtx_lock(&gmtbq_mtx);
449                 g_multipath_kt_state = GKT_DIE;
450                 wakeup(&g_multipath_kt_state);
451                 msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO,
452                     "gmp:fini", 0);
453                 mtx_unlock(&gmtbq_mtx);
454         }
455 }
456
457 static int
458 g_multipath_read_metadata(struct g_consumer *cp,
459     struct g_multipath_metadata *md)
460 {
461         struct g_provider *pp;
462         u_char *buf;
463         int error;
464
465         g_topology_assert();
466         error = g_access(cp, 1, 0, 0);
467         if (error != 0)
468                 return (error);
469         pp = cp->provider;
470         g_topology_unlock();
471         buf = g_read_data(cp, pp->mediasize - pp->sectorsize,
472             pp->sectorsize, &error);
473         g_topology_lock();
474         g_access(cp, -1, 0, 0);
475         if (buf == NULL)
476                 return (error);
477         multipath_metadata_decode(buf, md);
478         g_free(buf);
479         return (0);
480 }
481
482 static struct g_geom *
483 g_multipath_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
484 {
485         struct g_multipath_metadata md;
486         struct g_multipath_softc *sc;
487         struct g_consumer *cp;
488         struct g_geom *gp, *gp1;
489         int error, isnew;
490
491         g_topology_assert();
492
493         gp = g_new_geomf(mp, "multipath:taste");
494         gp->start = g_multipath_start;
495         gp->access = g_multipath_access;
496         gp->orphan = g_multipath_orphan;
497         cp = g_new_consumer(gp);
498         g_attach(cp, pp);
499         error = g_multipath_read_metadata(cp, &md);
500         g_detach(cp);
501         g_destroy_consumer(cp);
502         g_destroy_geom(gp);
503         if (error != 0)
504                 return (NULL);
505         gp = NULL;
506
507         if (strcmp(md.md_magic, G_MULTIPATH_MAGIC) != 0) {
508                 if (g_multipath_debug)
509                         printf("%s is not MULTIPATH\n", pp->name);
510                 return (NULL);
511         }
512         if (md.md_version != G_MULTIPATH_VERSION) {
513                 printf("%s has version %d multipath id- this module is version "
514                     " %d: rejecting\n", pp->name, md.md_version,
515                     G_MULTIPATH_VERSION);
516                 return (NULL);
517         }
518         if (g_multipath_debug)
519                 printf("MULTIPATH: %s/%s\n", md.md_name, md.md_uuid);
520
521         /*
522          * Let's check if such a device already is present. We check against
523          * uuid alone first because that's the true distinguishor. If that
524          * passes, then we check for name conflicts. If there are conflicts, 
525          * modify the name.
526          *
527          * The whole purpose of this is to solve the problem that people don't
528          * pick good unique names, but good unique names (like uuids) are a
529          * pain to use. So, we allow people to build GEOMs with friendly names
530          * and uuids, and modify the names in case there's a collision.
531          */
532         sc = NULL;
533         LIST_FOREACH(gp, &mp->geom, geom) {
534                 sc = gp->softc;
535                 if (sc == NULL)
536                         continue;
537                 if (strncmp(md.md_uuid, sc->sc_uuid, sizeof(md.md_uuid)) == 0)
538                         break;
539         }
540
541         LIST_FOREACH(gp1, &mp->geom, geom) {
542                 if (gp1 == gp)
543                         continue;
544                 sc = gp1->softc;
545                 if (sc == NULL)
546                         continue;
547                 if (strncmp(md.md_name, sc->sc_name, sizeof(md.md_name)) == 0)
548                         break;
549         }
550
551         /*
552          * If gp is NULL, we had no extant MULTIPATH geom with this uuid.
553          *
554          * If gp1 is *not* NULL, that means we have a MULTIPATH geom extant
555          * with the same name (but a different UUID).
556          *
557          * If gp is NULL, then modify the name with a random number and
558          * complain, but allow the creation of the geom to continue.
559          *
560          * If gp is *not* NULL, just use the geom's name as we're attaching
561          * this disk to the (previously generated) name.
562          */
563
564         if (gp1) {
565                 sc = gp1->softc;
566                 if (gp == NULL) {
567                         char buf[16];
568                         u_long rand = random();
569
570                         snprintf(buf, sizeof (buf), "%s-%lu", md.md_name, rand);
571                         printf("GEOM_MULTIPATH: geom %s/%s exists already\n",
572                             sc->sc_name, sc->sc_uuid);
573                         printf("GEOM_MULTIPATH: %s will be (temporarily) %s\n",
574                             md.md_uuid, buf);
575                         strlcpy(md.md_name, buf, sizeof(md.md_name));
576                 } else {
577                         strlcpy(md.md_name, sc->sc_name, sizeof(md.md_name));
578                 }
579         }
580
581         if (gp == NULL) {
582                 gp = g_multipath_create(mp, &md);
583                 if (gp == NULL) {
584                         printf("GEOM_MULTIPATH: cannot create geom %s/%s\n",
585                             md.md_name, md.md_uuid);
586                         return (NULL);
587                 }
588                 isnew = 1;
589         } else {
590                 isnew = 0;
591         }
592
593         sc = gp->softc;
594         KASSERT(sc != NULL, ("sc is NULL"));
595         error = g_multipath_add_disk(gp, pp);
596         if (error != 0) {
597                 if (isnew)
598                         g_multipath_destroy(gp);
599                 return (NULL);
600         }
601         return (gp);
602 }
603
604 static void
605 g_multipath_ctl_create(struct gctl_req *req, struct g_class *mp)
606 {
607         struct g_geom *gp;
608         struct g_provider *pp0, *pp1;
609         struct g_multipath_metadata md;
610         const char *name, *mpname, *uuid;
611         static const char devpf[6] = "/dev/";
612         int *nargs, error;
613
614         g_topology_assert();
615
616         mpname = gctl_get_asciiparam(req, "arg0");
617         if (mpname == NULL) {
618                 gctl_error(req, "No 'arg0' argument");
619                 return;
620         }
621
622         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
623         if (nargs == NULL) {
624                 gctl_error(req, "No 'nargs' argument");
625                 return;
626         }
627         if (*nargs != 4) {
628                 gctl_error(req, "missing device or uuid arguments");
629                 return;
630         }
631
632         name = gctl_get_asciiparam(req, "arg1");
633         if (name == NULL) {
634                 gctl_error(req, "No 'arg1' argument");
635                 return;
636         }
637         if (strncmp(name, devpf, 5) == 0)
638                 name += 5;
639         pp0 = g_provider_by_name(name);
640         if (pp0 == NULL) {
641                 gctl_error(req, "Provider %s is invalid", name);
642                 return;
643         }
644
645         name = gctl_get_asciiparam(req, "arg2");
646         if (name == NULL) {
647                 gctl_error(req, "No 'arg2' argument");
648                 return;
649         }
650         if (strncmp(name, devpf, 5) == 0)
651                 name += 5;
652         pp1 = g_provider_by_name(name);
653         if (pp1 == NULL) {
654                 gctl_error(req, "Provider %s is invalid", name);
655                 return;
656         }
657
658         uuid = gctl_get_asciiparam(req, "arg3");
659         if (uuid == NULL) {
660                 gctl_error(req, "No uuid argument");
661                 return;
662         }
663         if (strlen(uuid) != 36) {
664                 gctl_error(req, "Malformed uuid argument");
665                 return;
666         }
667
668         /*
669          * Check to make sure parameters from the two providers are the same
670          */
671         if (pp0 == pp1) {
672                 gctl_error(req, "providers %s and %s are the same",
673                     pp0->name, pp1->name);
674                 return;
675         }
676         if (pp0->mediasize != pp1->mediasize) {
677                 gctl_error(req, "Provider %s is %jd; Provider %s is %jd",
678                     pp0->name, (intmax_t) pp0->mediasize,
679                     pp1->name, (intmax_t) pp1->mediasize);
680                 return;
681         }
682         if (pp0->sectorsize != pp1->sectorsize) {
683                 gctl_error(req, "Provider %s has sectorsize %u; Provider %s "
684                     "has sectorsize %u", pp0->name, pp0->sectorsize,
685                     pp1->name, pp1->sectorsize);
686                 return;
687         }
688
689         /*
690          * cons up enough of a metadata structure to use.
691          */
692         memset(&md, 0, sizeof(md));
693         md.md_size = pp0->mediasize;
694         md.md_sectorsize = pp0->sectorsize;
695         strlcpy(md.md_name, mpname, sizeof(md.md_name));
696         strlcpy(md.md_uuid, uuid, sizeof(md.md_uuid));
697
698         gp = g_multipath_create(mp, &md);
699         if (gp == NULL)
700                 return;
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_ctl_rotate(struct gctl_req *req, struct g_class *mp)
753 {
754         struct g_geom *gp;
755         const char *name;
756         int error;
757
758         g_topology_assert();
759
760         name = gctl_get_asciiparam(req, "arg0");
761         if (name == NULL) {
762                 gctl_error(req, "No 'arg0' argument");
763                 return;
764         }
765         gp = g_multipath_find_geom(mp, name);
766         if (gp == NULL) {
767                 gctl_error(req, "Device %s is invalid", name);
768                 return;
769         }
770         error = g_multipath_rotate(gp);
771         if (error != 0) {
772                 gctl_error(req, "failed to rotate %s (err=%d)", name, error);
773         }
774 }
775
776 static void
777 g_multipath_ctl_getactive(struct gctl_req *req, struct g_class *mp)
778 {
779         struct sbuf *sb;
780         struct g_geom *gp;
781         struct g_multipath_softc *sc;
782         const char *name;
783
784         sb = sbuf_new_auto();
785
786         g_topology_assert();
787         name = gctl_get_asciiparam(req, "arg0");
788         if (name == NULL) {
789                 gctl_error(req, "No 'arg0' argument");
790                 return;
791         }
792         gp = g_multipath_find_geom(mp, name);
793         if (gp == NULL) {
794                 gctl_error(req, "Device %s is invalid", name);
795                 return;
796         }
797         sc = gp->softc;
798         if (sc->cp_active) {
799                 sbuf_printf(sb, "%s\n", sc->cp_active->provider->name);
800         } else {
801                 sbuf_printf(sb, "none\n");
802         }
803         sbuf_finish(sb);
804         gctl_set_param_err(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
805         sbuf_delete(sb);
806 }
807
808 static void
809 g_multipath_config(struct gctl_req *req, struct g_class *mp, const char *verb)
810 {
811         uint32_t *version;
812         g_topology_assert();
813         version = gctl_get_paraml(req, "version", sizeof(*version));
814         if (version == NULL) {
815                 gctl_error(req, "No 'version' argument");
816         } else if (*version != G_MULTIPATH_VERSION) {
817                 gctl_error(req, "Userland and kernel parts are out of sync");
818         } else if (strcmp(verb, "create") == 0) {
819                 g_multipath_ctl_create(req, mp);
820         } else if (strcmp(verb, "destroy") == 0) {
821                 g_multipath_ctl_destroy(req, mp);
822         } else if (strcmp(verb, "rotate") == 0) {
823                 g_multipath_ctl_rotate(req, mp);
824         } else if (strcmp(verb, "getactive") == 0) {
825                 g_multipath_ctl_getactive(req, mp);
826         } else {
827                 gctl_error(req, "Unknown verb %s", verb);
828         }
829 }
830 DECLARE_GEOM_CLASS(g_multipath_class, g_multipath);