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