xref: /freebsd/sys/geom/multipath/g_multipath.c (revision 195ebc7e9e4b129de810833791a19dfb4349d6a9)
1 /*-
2  * Copyright (c) 2006-2007 Matthew Jacob <mjacob@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 /*
27  * Based upon work by Pawel Jakub Dawidek <pjd@FreeBSD.org> for all of the
28  * fine geom examples, and by Poul Henning Kamp <phk@FreeBSD.org> for GEOM
29  * itself, all of which is most gratefully acknowledged.
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/lock.h>
39 #include <sys/mutex.h>
40 #include <sys/bio.h>
41 #include <sys/sysctl.h>
42 #include <sys/kthread.h>
43 #include <sys/malloc.h>
44 #include <geom/geom.h>
45 #include <geom/multipath/g_multipath.h>
46 
47 
48 SYSCTL_DECL(_kern_geom);
49 SYSCTL_NODE(_kern_geom, OID_AUTO, multipath, CTLFLAG_RW, 0,
50     "GEOM_MULTIPATH tunables");
51 static u_int g_multipath_debug = 0;
52 SYSCTL_UINT(_kern_geom_multipath, OID_AUTO, debug, CTLFLAG_RW,
53     &g_multipath_debug, 0, "Debug level");
54 
55 static enum {
56 	GKT_NIL,
57 	GKT_RUN,
58 	GKT_DIE
59 } g_multipath_kt_state;
60 static struct bio_queue_head gmtbq;
61 static struct mtx gmtbq_mtx;
62 
63 static void g_multipath_orphan(struct g_consumer *);
64 static void g_multipath_start(struct bio *);
65 static void g_multipath_done(struct bio *);
66 static void g_multipath_done_error(struct bio *);
67 static void g_multipath_kt(void *);
68 
69 static int g_multipath_destroy(struct g_geom *);
70 static int
71 g_multipath_destroy_geom(struct gctl_req *, struct g_class *, struct g_geom *);
72 
73 static g_taste_t g_multipath_taste;
74 static g_ctl_req_t g_multipath_config;
75 static g_init_t g_multipath_init;
76 static g_fini_t g_multipath_fini;
77 
78 struct g_class g_multipath_class = {
79 	.name		= G_MULTIPATH_CLASS_NAME,
80 	.version	= G_VERSION,
81 	.ctlreq		= g_multipath_config,
82 	.taste		= g_multipath_taste,
83 	.destroy_geom	= g_multipath_destroy_geom,
84 	.init		= g_multipath_init,
85 	.fini		= g_multipath_fini
86 };
87 
88 #define	MP_BAD		0x1
89 #define	MP_POSTED	0x2
90 
91 static void
92 g_mpd(void *arg, int flags __unused)
93 {
94 	struct g_consumer *cp;
95 
96 	g_topology_assert();
97 	cp = arg;
98 	if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) {
99 		g_access(cp, -cp->acr, -cp->acw, -cp->ace);
100 	}
101 	if (cp->provider) {
102 		printf("GEOM_MULTIPATH: %s removed from %s\n",
103 		    cp->provider->name, cp->geom->name);
104 		g_detach(cp);
105 	}
106 	g_destroy_consumer(cp);
107 }
108 
109 static void
110 g_multipath_orphan(struct g_consumer *cp)
111 {
112 	if ((cp->index & MP_POSTED) == 0) {
113 		cp->index |= MP_POSTED;
114 		printf("GEOM_MULTIPATH: %s orphaned in %s\n",
115 		    cp->provider->name, cp->geom->name);
116 		g_mpd(cp, 0);
117 	}
118 }
119 
120 static void
121 g_multipath_start(struct bio *bp)
122 {
123 	struct g_multipath_softc *sc;
124 	struct g_geom *gp;
125 	struct g_consumer *cp;
126 	struct bio *cbp;
127 
128 	gp = bp->bio_to->geom;
129 	sc = gp->softc;
130 	KASSERT(sc != NULL, ("NULL sc"));
131 	cp = sc->cp_active;
132 	if (cp == NULL) {
133 		g_io_deliver(bp, ENXIO);
134 		return;
135 	}
136 	cbp = g_clone_bio(bp);
137 	if (cbp == NULL) {
138 		g_io_deliver(bp, ENOMEM);
139 		return;
140 	}
141 	cbp->bio_done = g_multipath_done;
142 	g_io_request(cbp, cp);
143 }
144 
145 static void
146 g_multipath_done(struct bio *bp)
147 {
148 	if (bp->bio_error == ENXIO || bp->bio_error == EIO) {
149 		mtx_lock(&gmtbq_mtx);
150 		bioq_insert_tail(&gmtbq, bp);
151 		wakeup(&g_multipath_kt_state);
152 		mtx_unlock(&gmtbq_mtx);
153 	} else {
154 		g_std_done(bp);
155 	}
156 }
157 
158 static void
159 g_multipath_done_error(struct bio *bp)
160 {
161 	struct bio *pbp;
162 	struct g_geom *gp;
163 	struct g_multipath_softc *sc;
164 	struct g_consumer *cp;
165 	struct g_provider *pp;
166 
167 	/*
168 	 * If we had a failure, we have to check first to see
169 	 * whether the consumer it failed on was the currently
170 	 * active consumer (i.e., this is the first in perhaps
171 	 * a number of failures). If so, we then switch consumers
172 	 * to the next available consumer.
173 	 */
174 
175 	g_topology_lock();
176 	pbp = bp->bio_parent;
177 	gp = pbp->bio_to->geom;
178 	sc = gp->softc;
179 	cp = bp->bio_from;
180 	pp = cp->provider;
181 
182 	cp->index |= MP_BAD;
183 	if (cp->nend == cp->nstart && pp->nend == pp->nstart) {
184 		cp->index |= MP_POSTED;
185 		g_post_event(g_mpd, cp, M_NOWAIT, NULL);
186 	}
187 	if (cp == sc->cp_active) {
188 		struct g_consumer *lcp;
189 		printf("GEOM_MULTIPATH: %s failed in %s\n",
190 		    pp->name, sc->sc_name);
191 		sc->cp_active = NULL;
192 		LIST_FOREACH(lcp, &gp->consumer, consumer) {
193 			if ((lcp->index & MP_BAD) == 0) {
194 				sc->cp_active = lcp;
195 				break;
196 			}
197 		}
198 		if (sc->cp_active == NULL) {
199 			printf("GEOM_MULTIPATH: out of providers for %s\n",
200 			    sc->sc_name);
201 			return;
202 		} else {
203 			printf("GEOM_MULTIPATH: %s now active path in %s\n",
204 			    sc->cp_active->provider->name, sc->sc_name);
205 		}
206 	}
207 	g_topology_unlock();
208 
209 	/*
210 	 * If we can fruitfully restart the I/O, do so.
211 	 */
212 	if (sc->cp_active) {
213 		g_destroy_bio(bp);
214 		pbp->bio_children--;
215 		g_multipath_start(pbp);
216 	} else {
217 		g_std_done(bp);
218 	}
219 }
220 
221 static void
222 g_multipath_kt(void *arg)
223 {
224 	g_multipath_kt_state = GKT_RUN;
225 	mtx_lock(&gmtbq_mtx);
226 	while (g_multipath_kt_state == GKT_RUN) {
227 		for (;;) {
228 			struct bio *bp;
229 			bp = bioq_takefirst(&gmtbq);
230 			if (bp == NULL) {
231 				break;
232 			}
233 			mtx_unlock(&gmtbq_mtx);
234 			g_multipath_done_error(bp);
235 			mtx_lock(&gmtbq_mtx);
236 		}
237 		msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO,
238 		    "gkt:wait", hz / 10);
239 	}
240 	mtx_unlock(&gmtbq_mtx);
241 	wakeup(&g_multipath_kt_state);
242 	kproc_exit(0);
243 }
244 
245 
246 static int
247 g_multipath_access(struct g_provider *pp, int dr, int dw, int de)
248 {
249 	struct g_geom *gp;
250 	struct g_consumer *cp, *badcp = NULL;
251 	int error;
252 
253 	gp = pp->geom;
254 
255 	LIST_FOREACH(cp, &gp->consumer, consumer) {
256 		error = g_access(cp, dr, dw, de);
257 		if (error) {
258 			badcp = cp;
259 			goto fail;
260 		}
261 	}
262 	return (0);
263 
264 fail:
265 	LIST_FOREACH(cp, &gp->consumer, consumer) {
266 		if (cp == badcp) {
267 			break;
268 		}
269 		(void) g_access(cp, -dr, -dw, -de);
270 	}
271 	return (error);
272 }
273 
274 static struct g_geom *
275 g_multipath_create(struct g_class *mp, struct g_multipath_metadata *md)
276 {
277 	struct g_multipath_softc *sc;
278 	struct g_geom *gp;
279 	struct g_provider *pp;
280 
281 	g_topology_assert();
282 
283 	LIST_FOREACH(gp, &mp->geom, geom) {
284 		if (strcmp(gp->name, md->md_name) == 0) {
285 			printf("GEOM_MULTIPATH: name %s already exists\n",
286 			    md->md_name);
287 			return (NULL);
288 		}
289 	}
290 
291 	gp = g_new_geomf(mp, md->md_name);
292 	if (gp == NULL) {
293 		goto fail;
294 	}
295 
296 	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
297 	if (sc == NULL) {
298 		goto fail;
299 	}
300 
301 	gp->softc = sc;
302 	gp->start = g_multipath_start;
303 	gp->orphan = g_multipath_orphan;
304 	gp->access = g_multipath_access;
305 	memcpy(sc->sc_uuid, md->md_uuid, sizeof (sc->sc_uuid));
306 	memcpy(sc->sc_name, md->md_name, sizeof (sc->sc_name));
307 
308 	pp = g_new_providerf(gp, "multipath/%s", md->md_name);
309 	if (pp == NULL) {
310 		goto fail;
311 	}
312 	/* limit the provider to not have it stomp on metadata */
313 	pp->mediasize = md->md_size - md->md_sectorsize;
314 	pp->sectorsize = md->md_sectorsize;
315 	sc->pp = pp;
316 	g_error_provider(pp, 0);
317 	return (gp);
318 fail:
319 	if (gp != NULL) {
320 		if (gp->softc != NULL) {
321 			g_free(gp->softc);
322 		}
323 		g_destroy_geom(gp);
324 	}
325 	return (NULL);
326 }
327 
328 static int
329 g_multipath_add_disk(struct g_geom *gp, struct g_provider *pp)
330 {
331 	struct g_multipath_softc *sc;
332 	struct g_consumer *cp, *nxtcp;
333 	int error;
334 
335 	g_topology_assert();
336 
337 	sc = gp->softc;
338 	KASSERT(sc, ("no softc"));
339 
340 	/*
341 	 * Make sure that the passed provider isn't already attached
342 	 */
343 	LIST_FOREACH(cp, &gp->consumer, consumer) {
344 		if (cp->provider == pp) {
345 			break;
346 		}
347 	}
348 	if (cp) {
349 		printf("GEOM_MULTIPATH: provider %s already attached to %s\n",
350 		    pp->name, gp->name);
351 		return (EEXIST);
352 	}
353 	nxtcp = LIST_FIRST(&gp->consumer);
354 	cp = g_new_consumer(gp);
355 	if (cp == NULL) {
356 		return (ENOMEM);
357 	}
358 	error = g_attach(cp, pp);
359 	if (error != 0) {
360 		printf("GEOM_MULTIPATH: cannot attach %s to %s",
361 		    pp->name, sc->sc_name);
362 		g_destroy_consumer(cp);
363 		return (error);
364 	}
365 	cp->private = sc;
366 	cp->index = 0;
367 
368 	/*
369 	 * Set access permissions on new consumer to match other consumers
370 	 */
371 	if (nxtcp && (nxtcp->acr + nxtcp->acw +  nxtcp->ace)) {
372 		error = g_access(cp, nxtcp->acr, nxtcp->acw, nxtcp->ace);
373 		if (error) {
374 			printf("GEOM_MULTIPATH: cannot set access in "
375 			    "attaching %s to %s/%s (%d)\n",
376 			    pp->name, sc->sc_name, sc->sc_uuid, error);
377 			g_detach(cp);
378 			g_destroy_consumer(cp);
379 			return (error);
380 		}
381 	}
382 	printf("GEOM_MULTIPATH: adding %s to %s/%s\n",
383 	    pp->name, sc->sc_name, sc->sc_uuid);
384 	if (sc->cp_active == NULL) {
385 		sc->cp_active = cp;
386 		printf("GEOM_MULTIPATH: %s now active path in %s\n",
387 		    pp->name, sc->sc_name);
388 	}
389 	return (0);
390 }
391 
392 static int
393 g_multipath_destroy(struct g_geom *gp)
394 {
395 	struct g_provider *pp;
396 
397 	g_topology_assert();
398 	if (gp->softc == NULL) {
399 		return (ENXIO);
400 	}
401 	pp = LIST_FIRST(&gp->provider);
402 	if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
403 		return (EBUSY);
404 	}
405 	printf("GEOM_MULTIPATH: destroying %s\n", gp->name);
406 	g_free(gp->softc);
407 	gp->softc = NULL;
408 	g_wither_geom(gp, ENXIO);
409 	return (0);
410 }
411 
412 static int
413 g_multipath_destroy_geom(struct gctl_req *req, struct g_class *mp,
414     struct g_geom *gp)
415 {
416 	return (g_multipath_destroy(gp));
417 }
418 
419 static void
420 g_multipath_init(struct g_class *mp)
421 {
422 	bioq_init(&gmtbq);
423 	mtx_init(&gmtbq_mtx, "gmtbq", NULL, MTX_DEF);
424 	if (kproc_create(g_multipath_kt, mp, NULL, 0, 0, "g_mp_kt") == 0) {
425 		g_multipath_kt_state = GKT_RUN;
426 	}
427 }
428 
429 static void
430 g_multipath_fini(struct g_class *mp)
431 {
432 	if (g_multipath_kt_state == GKT_RUN) {
433 		mtx_lock(&gmtbq_mtx);
434 		g_multipath_kt_state = GKT_DIE;
435 		wakeup(&g_multipath_kt_state);
436 		msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO,
437 		    "gmp:fini", 0);
438 		mtx_unlock(&gmtbq_mtx);
439 	}
440 }
441 
442 static int
443 g_multipath_read_metadata(struct g_consumer *cp,
444     struct g_multipath_metadata *md)
445 {
446 	struct g_provider *pp;
447 	u_char *buf;
448 	int error;
449 
450 	g_topology_assert();
451 	error = g_access(cp, 1, 0, 0);
452 	if (error != 0) {
453 		return (error);
454 	}
455 	pp = cp->provider;
456 	g_topology_unlock();
457 	buf = g_read_data(cp, pp->mediasize - pp->sectorsize,
458 	    pp->sectorsize, &error);
459 	g_topology_lock();
460 	g_access(cp, -1, 0, 0);
461 	if (buf == NULL) {
462 		return (error);
463 	}
464 	multipath_metadata_decode(buf, md);
465 	g_free(buf);
466 	return (0);
467 }
468 
469 static struct g_geom *
470 g_multipath_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
471 {
472 	struct g_multipath_metadata md;
473 	struct g_multipath_softc *sc;
474 	struct g_consumer *cp;
475 	struct g_geom *gp, *gp1;
476 	int error, isnew;
477 
478 	g_topology_assert();
479 
480 	gp = g_new_geomf(mp, "multipath:taste");
481 	gp->start = g_multipath_start;
482 	gp->access = g_multipath_access;
483 	gp->orphan = g_multipath_orphan;
484 	cp = g_new_consumer(gp);
485 	g_attach(cp, pp);
486 	error = g_multipath_read_metadata(cp, &md);
487 	g_detach(cp);
488 	g_destroy_consumer(cp);
489 	g_destroy_geom(gp);
490 	if (error != 0) {
491 		return (NULL);
492 	}
493 	gp = NULL;
494 
495 	if (strcmp(md.md_magic, G_MULTIPATH_MAGIC) != 0) {
496 		if (g_multipath_debug) {
497 			printf("%s is not MULTIPATH\n", pp->name);
498 		}
499 		return (NULL);
500 	}
501 	if (md.md_version != G_MULTIPATH_VERSION) {
502 		printf("%s has version %d multipath id- this module is version "
503 		    " %d: rejecting\n", pp->name, md.md_version,
504 		    G_MULTIPATH_VERSION);
505 		return (NULL);
506 	}
507 	if (g_multipath_debug) {
508 		printf("MULTIPATH: %s/%s\n", md.md_name, md.md_uuid);
509 	}
510 
511 	/*
512 	 * Let's check if such a device already is present. We check against
513 	 * uuid alone first because that's the true distinguishor. If that
514 	 * passes, then we check for name conflicts. If there are conflicts,
515 	 * modify the name.
516 	 *
517 	 * The whole purpose of this is to solve the problem that people don't
518 	 * pick good unique names, but good unique names (like uuids) are a
519 	 * pain to use. So, we allow people to build GEOMs with friendly names
520 	 * and uuids, and modify the names in case there's a collision.
521 	 */
522 	sc = NULL;
523 	LIST_FOREACH(gp, &mp->geom, geom) {
524 		sc = gp->softc;
525 		if (sc == NULL) {
526 			continue;
527 		}
528 		if (strncmp(md.md_uuid, sc->sc_uuid, sizeof(md.md_uuid)) == 0) {
529 			break;
530 		}
531 	}
532 
533 	LIST_FOREACH(gp1, &mp->geom, geom) {
534 		if (gp1 == gp) {
535 			continue;
536 		}
537 		sc = gp1->softc;
538 		if (sc == NULL) {
539 			continue;
540 		}
541 		if (strncmp(md.md_name, sc->sc_name, sizeof(md.md_name)) == 0) {
542 			break;
543 		}
544 	}
545 
546 	/*
547 	 * If gp is NULL, we had no extant MULTIPATH geom with this uuid.
548 	 *
549 	 * If gp1 is *not* NULL, that means we have a MULTIPATH geom extant
550 	 * with the same name (but a different UUID).
551 	 *
552 	 * If gp is NULL, then modify the name with a random number and
553   	 * complain, but allow the creation of the geom to continue.
554 	 *
555 	 * If gp is *not* NULL, just use the geom's name as we're attaching
556 	 * this disk to the (previously generated) name.
557 	 */
558 
559 	if (gp1) {
560 		sc = gp1->softc;
561 		if (gp == NULL) {
562 			char buf[16];
563 			u_long rand = random();
564 
565 			snprintf(buf, sizeof (buf), "%s-%lu", md.md_name, rand);
566 			printf("GEOM_MULTIPATH: geom %s/%s exists already\n",
567 			    sc->sc_name, sc->sc_uuid);
568 			printf("GEOM_MULTIPATH: %s will be (temporarily) %s\n",
569 			    md.md_uuid, buf);
570 			strlcpy(md.md_name, buf, sizeof (md.md_name));
571 		} else {
572 			strlcpy(md.md_name, sc->sc_name, sizeof (md.md_name));
573 		}
574 	}
575 
576 	if (gp == NULL) {
577 		gp = g_multipath_create(mp, &md);
578 		if (gp == NULL) {
579 			printf("GEOM_MULTIPATH: cannot create geom %s/%s\n",
580 			    md.md_name, md.md_uuid);
581 			return (NULL);
582 		}
583 		isnew = 1;
584 	} else {
585 		isnew = 0;
586 	}
587 
588 	sc = gp->softc;
589 	KASSERT(sc != NULL, ("sc is NULL"));
590 	error = g_multipath_add_disk(gp, pp);
591 	if (error != 0) {
592 		if (isnew) {
593 			g_multipath_destroy(gp);
594 		}
595 		return (NULL);
596 	}
597 	return (gp);
598 }
599 
600 static void
601 g_multipath_ctl_create(struct gctl_req *req, struct g_class *mp)
602 {
603 	struct g_geom *gp;
604 	struct g_provider *pp0, *pp1;
605 	struct g_multipath_metadata md;
606 	const char *name, *mpname, *uuid;
607 	static const char devpf[6] = "/dev/";
608 	int *nargs, error;
609 
610 	g_topology_assert();
611 
612 	mpname = gctl_get_asciiparam(req, "arg0");
613         if (mpname == NULL) {
614                 gctl_error(req, "No 'arg0' argument");
615                 return;
616         }
617 
618 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
619 	if (nargs == NULL) {
620 		gctl_error(req, "No 'nargs' argument");
621 		return;
622 	}
623 	if (*nargs != 4) {
624 		gctl_error(req, "missing device or uuid arguments");
625 		return;
626 	}
627 
628 	name = gctl_get_asciiparam(req, "arg1");
629 	if (name == NULL) {
630 		gctl_error(req, "No 'arg1' argument");
631 		return;
632 	}
633 	if (strncmp(name, devpf, 5) == 0) {
634 		name += 5;
635 	}
636 	pp0 = g_provider_by_name(name);
637 	if (pp0 == NULL) {
638 		gctl_error(req, "Provider %s is invalid", name);
639 		return;
640 	}
641 
642 	name = gctl_get_asciiparam(req, "arg2");
643 	if (name == NULL) {
644 		gctl_error(req, "No 'arg2' argument");
645 		return;
646 	}
647 	if (strncmp(name, devpf, 5) == 0) {
648 		name += 5;
649 	}
650 	pp1 = g_provider_by_name(name);
651 	if (pp1 == NULL) {
652 		gctl_error(req, "Provider %s is invalid", name);
653 		return;
654 	}
655 
656 	uuid = gctl_get_asciiparam(req, "arg3");
657 	if (uuid == NULL) {
658 		gctl_error(req, "No uuid argument");
659 		return;
660 	}
661 	if (strlen(uuid) != 36) {
662 		gctl_error(req, "Malformed uuid argument");
663 		return;
664 	}
665 
666 	/*
667 	 * Check to make sure parameters from the two providers are the same
668 	 */
669 	if (pp0 == pp1) {
670 		gctl_error(req, "providers %s and %s are the same",
671 		    pp0->name, pp1->name);
672 		return;
673 	}
674     	if (pp0->mediasize != pp1->mediasize) {
675 		gctl_error(req, "Provider %s is %jd; Provider %s is %jd",
676 		    pp0->name, (intmax_t) pp0->mediasize,
677 		    pp1->name, (intmax_t) pp1->mediasize);
678 		return;
679 	}
680     	if (pp0->sectorsize != pp1->sectorsize) {
681 		gctl_error(req, "Provider %s has sectorsize %u; Provider %s "
682 		    "has sectorsize %u", pp0->name, pp0->sectorsize,
683 		    pp1->name, pp1->sectorsize);
684 		return;
685 	}
686 
687 	/*
688 	 * cons up enough of a metadata structure to use.
689 	 */
690 	memset(&md, 0, sizeof(md));
691 	md.md_size = pp0->mediasize;
692 	md.md_sectorsize = pp0->sectorsize;
693 	strncpy(md.md_name, mpname, sizeof (md.md_name));
694 	strncpy(md.md_uuid, uuid, sizeof (md.md_uuid));
695 
696 	gp = g_multipath_create(mp, &md);
697 	if (gp == NULL) {
698 		return;
699 	}
700 	error = g_multipath_add_disk(gp, pp0);
701 	if (error) {
702 		g_multipath_destroy(gp);
703 		return;
704 	}
705 	error = g_multipath_add_disk(gp, pp1);
706 	if (error) {
707 		g_multipath_destroy(gp);
708 		return;
709 	}
710 }
711 
712 static struct g_geom *
713 g_multipath_find_geom(struct g_class *mp, const char *name)
714 {
715 	struct g_geom *gp;
716 
717 	LIST_FOREACH(gp, &mp->geom, geom) {
718 		if (strcmp(gp->name, name) == 0) {
719 			return (gp);
720 		}
721 	}
722 	return (NULL);
723 }
724 
725 static void
726 g_multipath_ctl_destroy(struct gctl_req *req, struct g_class *mp)
727 {
728 	struct g_geom *gp;
729 	const char *name;
730 	int error;
731 
732 	g_topology_assert();
733 
734 	name = gctl_get_asciiparam(req, "arg0");
735         if (name == NULL) {
736                 gctl_error(req, "No 'arg0' argument");
737                 return;
738         }
739 	gp = g_multipath_find_geom(mp, name);
740 	if (gp == NULL) {
741 		gctl_error(req, "Device %s is invalid", name);
742 		return;
743 	}
744 	error = g_multipath_destroy(gp);
745 	if (error != 0) {
746 		gctl_error(req, "failed to destroy %s (err=%d)", name, error);
747 	}
748 }
749 
750 static void
751 g_multipath_config(struct gctl_req *req, struct g_class *mp, const char *verb)
752 {
753 	uint32_t *version;
754 	g_topology_assert();
755 	version = gctl_get_paraml(req, "version", sizeof(*version));
756 	if (version == NULL) {
757 		gctl_error(req, "No 'version' argument");
758 	} else if (*version != G_MULTIPATH_VERSION) {
759 		gctl_error(req, "Userland and kernel parts are out of sync");
760 	} else if (strcmp(verb, "create") == 0) {
761 		g_multipath_ctl_create(req, mp);
762 	} else if (strcmp(verb, "destroy") == 0) {
763 		g_multipath_ctl_destroy(req, mp);
764 	} else {
765 		gctl_error(req, "Unknown verb %s", verb);
766 	}
767 }
768 DECLARE_GEOM_CLASS(g_multipath_class, g_multipath);
769