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