xref: /freebsd/sys/geom/multipath/g_multipath.c (revision 6472ac3d8a86336899b6cfb789a4cd9897e3fab5)
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 static 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);
781