xref: /freebsd/sys/geom/multipath/g_multipath.c (revision 0c883cef45bbfe73888046245dfcdb7aed5a40ea)
1e770bc6bSMatt Jacob /*-
2*0c883cefSAlexander Motin  * Copyright (c) 2011 Alexander Motin <mav@FreeBSD.org>
3e770bc6bSMatt Jacob  * Copyright (c) 2006-2007 Matthew Jacob <mjacob@FreeBSD.org>
4e770bc6bSMatt Jacob  * All rights reserved.
5e770bc6bSMatt Jacob  *
6e770bc6bSMatt Jacob  * Redistribution and use in source and binary forms, with or without
7e770bc6bSMatt Jacob  * modification, are permitted provided that the following conditions
8e770bc6bSMatt Jacob  * are met:
9e770bc6bSMatt Jacob  * 1. Redistributions of source code must retain the above copyright
10e770bc6bSMatt Jacob  *    notice, this list of conditions and the following disclaimer.
11e770bc6bSMatt Jacob  * 2. Redistributions in binary form must reproduce the above copyright
12e770bc6bSMatt Jacob  *    notice, this list of conditions and the following disclaimer in the
13e770bc6bSMatt Jacob  *    documentation and/or other materials provided with the distribution.
14e770bc6bSMatt Jacob  *
15e770bc6bSMatt Jacob  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16e770bc6bSMatt Jacob  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17e770bc6bSMatt Jacob  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18e770bc6bSMatt Jacob  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19e770bc6bSMatt Jacob  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20e770bc6bSMatt Jacob  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21e770bc6bSMatt Jacob  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22e770bc6bSMatt Jacob  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23e770bc6bSMatt Jacob  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24e770bc6bSMatt Jacob  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25e770bc6bSMatt Jacob  * SUCH DAMAGE.
26e770bc6bSMatt Jacob  */
27e770bc6bSMatt Jacob /*
28e770bc6bSMatt Jacob  * Based upon work by Pawel Jakub Dawidek <pjd@FreeBSD.org> for all of the
29e770bc6bSMatt Jacob  * fine geom examples, and by Poul Henning Kamp <phk@FreeBSD.org> for GEOM
30e770bc6bSMatt Jacob  * itself, all of which is most gratefully acknowledged.
31e770bc6bSMatt Jacob  */
32e770bc6bSMatt Jacob 
33e770bc6bSMatt Jacob #include <sys/cdefs.h>
34e770bc6bSMatt Jacob __FBSDID("$FreeBSD$");
35e770bc6bSMatt Jacob #include <sys/param.h>
36e770bc6bSMatt Jacob #include <sys/systm.h>
37e770bc6bSMatt Jacob #include <sys/kernel.h>
38e770bc6bSMatt Jacob #include <sys/module.h>
39e770bc6bSMatt Jacob #include <sys/lock.h>
40e770bc6bSMatt Jacob #include <sys/mutex.h>
41e770bc6bSMatt Jacob #include <sys/bio.h>
425d807a0eSAndrey V. Elsukov #include <sys/sbuf.h>
43e770bc6bSMatt Jacob #include <sys/sysctl.h>
44e770bc6bSMatt Jacob #include <sys/kthread.h>
45e770bc6bSMatt Jacob #include <sys/malloc.h>
46e770bc6bSMatt Jacob #include <geom/geom.h>
47e770bc6bSMatt Jacob #include <geom/multipath/g_multipath.h>
48e770bc6bSMatt Jacob 
49cb08c2ccSAlexander Leidinger FEATURE(geom_multipath, "GEOM multipath support");
50e770bc6bSMatt Jacob 
51e770bc6bSMatt Jacob SYSCTL_DECL(_kern_geom);
526472ac3dSEd Schouten static SYSCTL_NODE(_kern_geom, OID_AUTO, multipath, CTLFLAG_RW, 0,
53e770bc6bSMatt Jacob     "GEOM_MULTIPATH tunables");
54e770bc6bSMatt Jacob static u_int g_multipath_debug = 0;
55e770bc6bSMatt Jacob SYSCTL_UINT(_kern_geom_multipath, OID_AUTO, debug, CTLFLAG_RW,
56e770bc6bSMatt Jacob     &g_multipath_debug, 0, "Debug level");
57*0c883cefSAlexander Motin static u_int g_multipath_exclusive = 1;
58*0c883cefSAlexander Motin SYSCTL_UINT(_kern_geom_multipath, OID_AUTO, exclusive, CTLFLAG_RW,
59*0c883cefSAlexander Motin     &g_multipath_exclusive, 0, "Exclusively open providers");
60e770bc6bSMatt Jacob 
61e770bc6bSMatt Jacob static enum {
62e770bc6bSMatt Jacob 	GKT_NIL,
63e770bc6bSMatt Jacob 	GKT_RUN,
64e770bc6bSMatt Jacob 	GKT_DIE
65e770bc6bSMatt Jacob } g_multipath_kt_state;
66e770bc6bSMatt Jacob static struct bio_queue_head gmtbq;
67e770bc6bSMatt Jacob static struct mtx gmtbq_mtx;
68e770bc6bSMatt Jacob 
69e770bc6bSMatt Jacob static void g_multipath_orphan(struct g_consumer *);
70e770bc6bSMatt Jacob static void g_multipath_start(struct bio *);
71e770bc6bSMatt Jacob static void g_multipath_done(struct bio *);
72e770bc6bSMatt Jacob static void g_multipath_done_error(struct bio *);
73e770bc6bSMatt Jacob static void g_multipath_kt(void *);
74e770bc6bSMatt Jacob 
75e770bc6bSMatt Jacob static int g_multipath_destroy(struct g_geom *);
76e770bc6bSMatt Jacob static int
77e770bc6bSMatt Jacob g_multipath_destroy_geom(struct gctl_req *, struct g_class *, struct g_geom *);
78e770bc6bSMatt Jacob 
792b4969ffSMatt Jacob static struct g_geom *g_multipath_find_geom(struct g_class *, const char *);
80b5dce617SMatt Jacob static int g_multipath_rotate(struct g_geom *);
81b5dce617SMatt Jacob 
82e770bc6bSMatt Jacob static g_taste_t g_multipath_taste;
83e770bc6bSMatt Jacob static g_ctl_req_t g_multipath_config;
84e770bc6bSMatt Jacob static g_init_t g_multipath_init;
85e770bc6bSMatt Jacob static g_fini_t g_multipath_fini;
86*0c883cefSAlexander Motin static g_dumpconf_t g_multipath_dumpconf;
87e770bc6bSMatt Jacob 
88e770bc6bSMatt Jacob struct g_class g_multipath_class = {
89e770bc6bSMatt Jacob 	.name		= G_MULTIPATH_CLASS_NAME,
90e770bc6bSMatt Jacob 	.version	= G_VERSION,
91e770bc6bSMatt Jacob 	.ctlreq		= g_multipath_config,
92e770bc6bSMatt Jacob 	.taste		= g_multipath_taste,
93e770bc6bSMatt Jacob 	.destroy_geom	= g_multipath_destroy_geom,
94e770bc6bSMatt Jacob 	.init		= g_multipath_init,
95e770bc6bSMatt Jacob 	.fini		= g_multipath_fini
96e770bc6bSMatt Jacob };
97e770bc6bSMatt Jacob 
98*0c883cefSAlexander Motin #define	MP_FAIL		0x00000001
99*0c883cefSAlexander Motin #define	MP_LOST		0x00000002
100*0c883cefSAlexander Motin #define	MP_NEW		0x00000004
101*0c883cefSAlexander Motin #define	MP_POSTED	0x00000008
102*0c883cefSAlexander Motin #define	MP_BAD		(MP_FAIL | MP_LOST | MP_NEW)
103*0c883cefSAlexander Motin #define MP_IDLE		0x00000010
104*0c883cefSAlexander Motin #define MP_IDLE_MASK	0xfffffff0
105*0c883cefSAlexander Motin 
106*0c883cefSAlexander Motin static int
107*0c883cefSAlexander Motin g_multipath_good(struct g_geom *gp)
108*0c883cefSAlexander Motin {
109*0c883cefSAlexander Motin 	struct g_consumer *cp;
110*0c883cefSAlexander Motin 	int n = 0;
111*0c883cefSAlexander Motin 
112*0c883cefSAlexander Motin 	LIST_FOREACH(cp, &gp->consumer, consumer) {
113*0c883cefSAlexander Motin 		if ((cp->index & MP_BAD) == 0)
114*0c883cefSAlexander Motin 			n++;
115*0c883cefSAlexander Motin 	}
116*0c883cefSAlexander Motin 	return (n);
117*0c883cefSAlexander Motin }
118*0c883cefSAlexander Motin 
119*0c883cefSAlexander Motin static void
120*0c883cefSAlexander Motin g_multipath_fault(struct g_consumer *cp, int cause)
121*0c883cefSAlexander Motin {
122*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
123*0c883cefSAlexander Motin 	struct g_consumer *lcp;
124*0c883cefSAlexander Motin 	struct g_geom *gp;
125*0c883cefSAlexander Motin 
126*0c883cefSAlexander Motin 	gp = cp->geom;
127*0c883cefSAlexander Motin 	sc = gp->softc;
128*0c883cefSAlexander Motin 	cp->index |= cause;
129*0c883cefSAlexander Motin 	if (g_multipath_good(gp) == 0 && sc->sc_ndisks > 0) {
130*0c883cefSAlexander Motin 		LIST_FOREACH(lcp, &gp->consumer, consumer) {
131*0c883cefSAlexander Motin 			if (lcp->provider == NULL ||
132*0c883cefSAlexander Motin 			    (lcp->index & (MP_LOST | MP_NEW)))
133*0c883cefSAlexander Motin 				continue;
134*0c883cefSAlexander Motin 			if (sc->sc_ndisks > 1 && lcp == cp)
135*0c883cefSAlexander Motin 				continue;
136*0c883cefSAlexander Motin 			printf("GEOM_MULTIPATH: "
137*0c883cefSAlexander Motin 			    "all paths in %s were marked FAIL, restore %s\n",
138*0c883cefSAlexander Motin 			    sc->sc_name, lcp->provider->name);
139*0c883cefSAlexander Motin 			lcp->index &= ~MP_FAIL;
140*0c883cefSAlexander Motin 		}
141*0c883cefSAlexander Motin 	}
142*0c883cefSAlexander Motin 	if (cp != sc->sc_active)
143*0c883cefSAlexander Motin 		return;
144*0c883cefSAlexander Motin 	sc->sc_active = NULL;
145*0c883cefSAlexander Motin 	LIST_FOREACH(lcp, &gp->consumer, consumer) {
146*0c883cefSAlexander Motin 		if ((lcp->index & MP_BAD) == 0) {
147*0c883cefSAlexander Motin 			sc->sc_active = lcp;
148*0c883cefSAlexander Motin 			break;
149*0c883cefSAlexander Motin 		}
150*0c883cefSAlexander Motin 	}
151*0c883cefSAlexander Motin 	if (sc->sc_active == NULL) {
152*0c883cefSAlexander Motin 		printf("GEOM_MULTIPATH: out of providers for %s\n",
153*0c883cefSAlexander Motin 		    sc->sc_name);
154*0c883cefSAlexander Motin 	} else if (!sc->sc_active_active) {
155*0c883cefSAlexander Motin 		printf("GEOM_MULTIPATH: %s is now active path in %s\n",
156*0c883cefSAlexander Motin 		    sc->sc_active->provider->name, sc->sc_name);
157*0c883cefSAlexander Motin 	}
158*0c883cefSAlexander Motin }
159*0c883cefSAlexander Motin 
160*0c883cefSAlexander Motin static struct g_consumer *
161*0c883cefSAlexander Motin g_multipath_choose(struct g_geom *gp)
162*0c883cefSAlexander Motin {
163*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
164*0c883cefSAlexander Motin 	struct g_consumer *best, *cp;
165*0c883cefSAlexander Motin 
166*0c883cefSAlexander Motin 	sc = gp->softc;
167*0c883cefSAlexander Motin 	if (!sc->sc_active_active)
168*0c883cefSAlexander Motin 		return (sc->sc_active);
169*0c883cefSAlexander Motin 	best = NULL;
170*0c883cefSAlexander Motin 	LIST_FOREACH(cp, &gp->consumer, consumer) {
171*0c883cefSAlexander Motin 		if (cp->index & MP_BAD)
172*0c883cefSAlexander Motin 			continue;
173*0c883cefSAlexander Motin 		cp->index += MP_IDLE;
174*0c883cefSAlexander Motin 		if (best == NULL || cp->private < best->private ||
175*0c883cefSAlexander Motin 		    (cp->private == best->private && cp->index > best->index))
176*0c883cefSAlexander Motin 			best = cp;
177*0c883cefSAlexander Motin 	}
178*0c883cefSAlexander Motin 	if (best != NULL)
179*0c883cefSAlexander Motin 		best->index &= ~MP_IDLE_MASK;
180*0c883cefSAlexander Motin 	return (best);
181*0c883cefSAlexander Motin }
182e770bc6bSMatt Jacob 
183e770bc6bSMatt Jacob static void
184e770bc6bSMatt Jacob g_mpd(void *arg, int flags __unused)
185e770bc6bSMatt Jacob {
186*0c883cefSAlexander Motin 	struct g_geom *gp;
187*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
188e770bc6bSMatt Jacob 	struct g_consumer *cp;
189*0c883cefSAlexander Motin 	int w;
190e770bc6bSMatt Jacob 
191e770bc6bSMatt Jacob 	g_topology_assert();
192e770bc6bSMatt Jacob 	cp = arg;
193*0c883cefSAlexander Motin 	gp = cp->geom;
194*0c883cefSAlexander Motin 	if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) {
195*0c883cefSAlexander Motin 		w = cp->acw;
196e770bc6bSMatt Jacob 		g_access(cp, -cp->acr, -cp->acw, -cp->ace);
197*0c883cefSAlexander Motin 		if (w > 0 && cp->provider != NULL &&
198*0c883cefSAlexander Motin 		    (cp->provider->geom->flags & G_GEOM_WITHER) == 0) {
199*0c883cefSAlexander Motin 			g_post_event(g_mpd, cp, M_WAITOK, NULL);
200*0c883cefSAlexander Motin 			return;
201*0c883cefSAlexander Motin 		}
202*0c883cefSAlexander Motin 	}
203*0c883cefSAlexander Motin 	sc = gp->softc;
204*0c883cefSAlexander Motin 	mtx_lock(&sc->sc_mtx);
205e770bc6bSMatt Jacob 	if (cp->provider) {
206e770bc6bSMatt Jacob 		printf("GEOM_MULTIPATH: %s removed from %s\n",
207*0c883cefSAlexander Motin 		    cp->provider->name, gp->name);
208e770bc6bSMatt Jacob 		g_detach(cp);
209e770bc6bSMatt Jacob 	}
210e770bc6bSMatt Jacob 	g_destroy_consumer(cp);
211*0c883cefSAlexander Motin 	mtx_unlock(&sc->sc_mtx);
212*0c883cefSAlexander Motin 	if (LIST_EMPTY(&gp->consumer))
213*0c883cefSAlexander Motin 		g_multipath_destroy(gp);
214e770bc6bSMatt Jacob }
215e770bc6bSMatt Jacob 
216e770bc6bSMatt Jacob static void
217e770bc6bSMatt Jacob g_multipath_orphan(struct g_consumer *cp)
218e770bc6bSMatt Jacob {
219*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
220*0c883cefSAlexander Motin 	uintptr_t *cnt;
221*0c883cefSAlexander Motin 
222*0c883cefSAlexander Motin 	g_topology_assert();
223*0c883cefSAlexander Motin 	printf("GEOM_MULTIPATH: %s in %s was disconnected\n",
224e770bc6bSMatt Jacob 	    cp->provider->name, cp->geom->name);
225*0c883cefSAlexander Motin 	sc = cp->geom->softc;
226*0c883cefSAlexander Motin 	cnt = (uintptr_t *)&cp->private;
227*0c883cefSAlexander Motin 	mtx_lock(&sc->sc_mtx);
228*0c883cefSAlexander Motin 	sc->sc_ndisks--;
229*0c883cefSAlexander Motin 	g_multipath_fault(cp, MP_LOST);
230*0c883cefSAlexander Motin 	if (*cnt == 0 && (cp->index & MP_POSTED) == 0) {
231*0c883cefSAlexander Motin 		cp->index |= MP_POSTED;
232*0c883cefSAlexander Motin 		mtx_unlock(&sc->sc_mtx);
233e770bc6bSMatt Jacob 		g_mpd(cp, 0);
234*0c883cefSAlexander Motin 	} else
235*0c883cefSAlexander Motin 		mtx_unlock(&sc->sc_mtx);
236e770bc6bSMatt Jacob }
237e770bc6bSMatt Jacob 
238e770bc6bSMatt Jacob static void
239e770bc6bSMatt Jacob g_multipath_start(struct bio *bp)
240e770bc6bSMatt Jacob {
241e770bc6bSMatt Jacob 	struct g_multipath_softc *sc;
242e770bc6bSMatt Jacob 	struct g_geom *gp;
243e770bc6bSMatt Jacob 	struct g_consumer *cp;
244e770bc6bSMatt Jacob 	struct bio *cbp;
245*0c883cefSAlexander Motin 	uintptr_t *cnt;
246e770bc6bSMatt Jacob 
247e770bc6bSMatt Jacob 	gp = bp->bio_to->geom;
248e770bc6bSMatt Jacob 	sc = gp->softc;
249e770bc6bSMatt Jacob 	KASSERT(sc != NULL, ("NULL sc"));
250e770bc6bSMatt Jacob 	cbp = g_clone_bio(bp);
251e770bc6bSMatt Jacob 	if (cbp == NULL) {
252e770bc6bSMatt Jacob 		g_io_deliver(bp, ENOMEM);
253e770bc6bSMatt Jacob 		return;
254e770bc6bSMatt Jacob 	}
255*0c883cefSAlexander Motin 	mtx_lock(&sc->sc_mtx);
256*0c883cefSAlexander Motin 	cp = g_multipath_choose(gp);
257*0c883cefSAlexander Motin 	if (cp == NULL) {
258*0c883cefSAlexander Motin 		mtx_unlock(&sc->sc_mtx);
259*0c883cefSAlexander Motin 		g_destroy_bio(cbp);
260*0c883cefSAlexander Motin 		g_io_deliver(bp, ENXIO);
261*0c883cefSAlexander Motin 		return;
262*0c883cefSAlexander Motin 	}
263*0c883cefSAlexander Motin 	if ((uintptr_t)bp->bio_driver1 < sc->sc_ndisks)
264*0c883cefSAlexander Motin 		bp->bio_driver1 = (void *)(uintptr_t)sc->sc_ndisks;
265*0c883cefSAlexander Motin 	cnt = (uintptr_t *)&cp->private;
266*0c883cefSAlexander Motin 	(*cnt)++;
267*0c883cefSAlexander Motin 	mtx_unlock(&sc->sc_mtx);
268e770bc6bSMatt Jacob 	cbp->bio_done = g_multipath_done;
269e770bc6bSMatt Jacob 	g_io_request(cbp, cp);
270e770bc6bSMatt Jacob }
271e770bc6bSMatt Jacob 
272e770bc6bSMatt Jacob static void
273e770bc6bSMatt Jacob g_multipath_done(struct bio *bp)
274e770bc6bSMatt Jacob {
275*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
276*0c883cefSAlexander Motin 	struct g_consumer *cp;
277*0c883cefSAlexander Motin 	uintptr_t *cnt;
278*0c883cefSAlexander Motin 
279e770bc6bSMatt Jacob 	if (bp->bio_error == ENXIO || bp->bio_error == EIO) {
280e770bc6bSMatt Jacob 		mtx_lock(&gmtbq_mtx);
281e770bc6bSMatt Jacob 		bioq_insert_tail(&gmtbq, bp);
282e770bc6bSMatt Jacob 		mtx_unlock(&gmtbq_mtx);
283*0c883cefSAlexander Motin 		wakeup(&g_multipath_kt_state);
284e770bc6bSMatt Jacob 	} else {
285*0c883cefSAlexander Motin 		cp = bp->bio_from;
286*0c883cefSAlexander Motin 		sc = cp->geom->softc;
287*0c883cefSAlexander Motin 		cnt = (uintptr_t *)&cp->private;
288*0c883cefSAlexander Motin 		mtx_lock(&sc->sc_mtx);
289*0c883cefSAlexander Motin 		(*cnt)--;
290*0c883cefSAlexander Motin 		if (*cnt == 0 && (cp->index & MP_LOST)) {
291*0c883cefSAlexander Motin 			cp->index |= MP_POSTED;
292*0c883cefSAlexander Motin 			mtx_unlock(&sc->sc_mtx);
293*0c883cefSAlexander Motin 			g_post_event(g_mpd, cp, M_WAITOK, NULL);
294*0c883cefSAlexander Motin 		} else
295*0c883cefSAlexander Motin 			mtx_unlock(&sc->sc_mtx);
296e770bc6bSMatt Jacob 		g_std_done(bp);
297e770bc6bSMatt Jacob 	}
298e770bc6bSMatt Jacob }
299e770bc6bSMatt Jacob 
300e770bc6bSMatt Jacob static void
301e770bc6bSMatt Jacob g_multipath_done_error(struct bio *bp)
302e770bc6bSMatt Jacob {
303e770bc6bSMatt Jacob 	struct bio *pbp;
304e770bc6bSMatt Jacob 	struct g_geom *gp;
305e770bc6bSMatt Jacob 	struct g_multipath_softc *sc;
306e770bc6bSMatt Jacob 	struct g_consumer *cp;
307e770bc6bSMatt Jacob 	struct g_provider *pp;
308*0c883cefSAlexander Motin 	uintptr_t *cnt;
309e770bc6bSMatt Jacob 
310e770bc6bSMatt Jacob 	/*
311e770bc6bSMatt Jacob 	 * If we had a failure, we have to check first to see
312e770bc6bSMatt Jacob 	 * whether the consumer it failed on was the currently
313e770bc6bSMatt Jacob 	 * active consumer (i.e., this is the first in perhaps
314e770bc6bSMatt Jacob 	 * a number of failures). If so, we then switch consumers
315e770bc6bSMatt Jacob 	 * to the next available consumer.
316e770bc6bSMatt Jacob 	 */
317e770bc6bSMatt Jacob 
318e770bc6bSMatt Jacob 	pbp = bp->bio_parent;
319e770bc6bSMatt Jacob 	gp = pbp->bio_to->geom;
320e770bc6bSMatt Jacob 	sc = gp->softc;
321e770bc6bSMatt Jacob 	cp = bp->bio_from;
322e770bc6bSMatt Jacob 	pp = cp->provider;
323*0c883cefSAlexander Motin 	cnt = (uintptr_t *)&cp->private;
324e770bc6bSMatt Jacob 
325*0c883cefSAlexander Motin 	mtx_lock(&sc->sc_mtx);
326*0c883cefSAlexander Motin 	printf("GEOM_MULTIPATH: Error %d, %s in %s marked FAIL\n",
327*0c883cefSAlexander Motin 	    bp->bio_error, pp->name, sc->sc_name);
328*0c883cefSAlexander Motin 	g_multipath_fault(cp, MP_FAIL);
329*0c883cefSAlexander Motin 	(*cnt)--;
330*0c883cefSAlexander Motin 	if (*cnt == 0 && (cp->index & (MP_LOST | MP_POSTED)) == MP_LOST) {
331e770bc6bSMatt Jacob 		cp->index |= MP_POSTED;
332*0c883cefSAlexander Motin 		mtx_unlock(&sc->sc_mtx);
333*0c883cefSAlexander Motin 		g_post_event(g_mpd, cp, M_WAITOK, NULL);
334*0c883cefSAlexander Motin 	} else
335*0c883cefSAlexander Motin 		mtx_unlock(&sc->sc_mtx);
336e770bc6bSMatt Jacob 
337e770bc6bSMatt Jacob 	/*
338e770bc6bSMatt Jacob 	 * If we can fruitfully restart the I/O, do so.
339e770bc6bSMatt Jacob 	 */
340*0c883cefSAlexander Motin 	if (pbp->bio_children < (uintptr_t)pbp->bio_driver1) {
341*0c883cefSAlexander Motin 		pbp->bio_inbed++;
342e770bc6bSMatt Jacob 		g_destroy_bio(bp);
343e770bc6bSMatt Jacob 		g_multipath_start(pbp);
344e770bc6bSMatt Jacob 	} else {
345e770bc6bSMatt Jacob 		g_std_done(bp);
346e770bc6bSMatt Jacob 	}
347e770bc6bSMatt Jacob }
348e770bc6bSMatt Jacob 
349e770bc6bSMatt Jacob static void
350e770bc6bSMatt Jacob g_multipath_kt(void *arg)
351e770bc6bSMatt Jacob {
35212f35a61SPawel Jakub Dawidek 
353e770bc6bSMatt Jacob 	g_multipath_kt_state = GKT_RUN;
354e770bc6bSMatt Jacob 	mtx_lock(&gmtbq_mtx);
355e770bc6bSMatt Jacob 	while (g_multipath_kt_state == GKT_RUN) {
356e770bc6bSMatt Jacob 		for (;;) {
357e770bc6bSMatt Jacob 			struct bio *bp;
35812f35a61SPawel Jakub Dawidek 
359e770bc6bSMatt Jacob 			bp = bioq_takefirst(&gmtbq);
36012f35a61SPawel Jakub Dawidek 			if (bp == NULL)
361e770bc6bSMatt Jacob 				break;
362e770bc6bSMatt Jacob 			mtx_unlock(&gmtbq_mtx);
363e770bc6bSMatt Jacob 			g_multipath_done_error(bp);
364e770bc6bSMatt Jacob 			mtx_lock(&gmtbq_mtx);
365e770bc6bSMatt Jacob 		}
366e770bc6bSMatt Jacob 		msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO,
367e770bc6bSMatt Jacob 		    "gkt:wait", hz / 10);
368e770bc6bSMatt Jacob 	}
369e770bc6bSMatt Jacob 	mtx_unlock(&gmtbq_mtx);
370e770bc6bSMatt Jacob 	wakeup(&g_multipath_kt_state);
3713745c395SJulian Elischer 	kproc_exit(0);
372e770bc6bSMatt Jacob }
373e770bc6bSMatt Jacob 
374e770bc6bSMatt Jacob 
375e770bc6bSMatt Jacob static int
376e770bc6bSMatt Jacob g_multipath_access(struct g_provider *pp, int dr, int dw, int de)
377e770bc6bSMatt Jacob {
378e770bc6bSMatt Jacob 	struct g_geom *gp;
379e770bc6bSMatt Jacob 	struct g_consumer *cp, *badcp = NULL;
380*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
381e770bc6bSMatt Jacob 	int error;
382e770bc6bSMatt Jacob 
383e770bc6bSMatt Jacob 	gp = pp->geom;
384e770bc6bSMatt Jacob 
385e770bc6bSMatt Jacob 	LIST_FOREACH(cp, &gp->consumer, consumer) {
386e770bc6bSMatt Jacob 		error = g_access(cp, dr, dw, de);
387e770bc6bSMatt Jacob 		if (error) {
388e770bc6bSMatt Jacob 			badcp = cp;
389e770bc6bSMatt Jacob 			goto fail;
390e770bc6bSMatt Jacob 		}
391e770bc6bSMatt Jacob 	}
392*0c883cefSAlexander Motin 	sc = gp->softc;
393*0c883cefSAlexander Motin 	sc->sc_opened += dr + dw + de;
394*0c883cefSAlexander Motin 	if (sc->sc_stopping && sc->sc_opened == 0)
395*0c883cefSAlexander Motin 		g_multipath_destroy(gp);
396e770bc6bSMatt Jacob 	return (0);
397e770bc6bSMatt Jacob 
398e770bc6bSMatt Jacob fail:
399e770bc6bSMatt Jacob 	LIST_FOREACH(cp, &gp->consumer, consumer) {
40012f35a61SPawel Jakub Dawidek 		if (cp == badcp)
401e770bc6bSMatt Jacob 			break;
402e770bc6bSMatt Jacob 		(void) g_access(cp, -dr, -dw, -de);
403e770bc6bSMatt Jacob 	}
404e770bc6bSMatt Jacob 	return (error);
405e770bc6bSMatt Jacob }
406e770bc6bSMatt Jacob 
407e770bc6bSMatt Jacob static struct g_geom *
408e770bc6bSMatt Jacob g_multipath_create(struct g_class *mp, struct g_multipath_metadata *md)
409e770bc6bSMatt Jacob {
410e770bc6bSMatt Jacob 	struct g_multipath_softc *sc;
411e770bc6bSMatt Jacob 	struct g_geom *gp;
412e770bc6bSMatt Jacob 	struct g_provider *pp;
413e770bc6bSMatt Jacob 
414e770bc6bSMatt Jacob 	g_topology_assert();
415e770bc6bSMatt Jacob 
416e770bc6bSMatt Jacob 	LIST_FOREACH(gp, &mp->geom, geom) {
417*0c883cefSAlexander Motin 		sc = gp->softc;
418*0c883cefSAlexander Motin 		if (sc == NULL || sc->sc_stopping)
419*0c883cefSAlexander Motin 			continue;
420e770bc6bSMatt Jacob 		if (strcmp(gp->name, md->md_name) == 0) {
421e770bc6bSMatt Jacob 			printf("GEOM_MULTIPATH: name %s already exists\n",
422e770bc6bSMatt Jacob 			    md->md_name);
423e770bc6bSMatt Jacob 			return (NULL);
424e770bc6bSMatt Jacob 		}
425e770bc6bSMatt Jacob 	}
426e770bc6bSMatt Jacob 
427e770bc6bSMatt Jacob 	gp = g_new_geomf(mp, md->md_name);
428e770bc6bSMatt Jacob 	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
429*0c883cefSAlexander Motin 	mtx_init(&sc->sc_mtx, "multipath", NULL, MTX_DEF);
430*0c883cefSAlexander Motin 	memcpy(sc->sc_uuid, md->md_uuid, sizeof (sc->sc_uuid));
431*0c883cefSAlexander Motin 	memcpy(sc->sc_name, md->md_name, sizeof (sc->sc_name));
432*0c883cefSAlexander Motin 	sc->sc_active_active = md->md_active_active;
433e770bc6bSMatt Jacob 	gp->softc = sc;
434e770bc6bSMatt Jacob 	gp->start = g_multipath_start;
435e770bc6bSMatt Jacob 	gp->orphan = g_multipath_orphan;
436e770bc6bSMatt Jacob 	gp->access = g_multipath_access;
437*0c883cefSAlexander Motin 	gp->dumpconf = g_multipath_dumpconf;
438e770bc6bSMatt Jacob 
439e770bc6bSMatt Jacob 	pp = g_new_providerf(gp, "multipath/%s", md->md_name);
440*0c883cefSAlexander Motin 	if (md->md_size != 0) {
441*0c883cefSAlexander Motin 		pp->mediasize = md->md_size -
442*0c883cefSAlexander Motin 		    ((md->md_uuid[0] != 0) ? md->md_sectorsize : 0);
443e770bc6bSMatt Jacob 		pp->sectorsize = md->md_sectorsize;
444*0c883cefSAlexander Motin 	}
445*0c883cefSAlexander Motin 	sc->sc_pp = pp;
446e770bc6bSMatt Jacob 	g_error_provider(pp, 0);
447*0c883cefSAlexander Motin 	printf("GEOM_MULTIPATH: %s created\n", gp->name);
448e770bc6bSMatt Jacob 	return (gp);
449e770bc6bSMatt Jacob }
450e770bc6bSMatt Jacob 
451e770bc6bSMatt Jacob static int
452e770bc6bSMatt Jacob g_multipath_add_disk(struct g_geom *gp, struct g_provider *pp)
453e770bc6bSMatt Jacob {
454e770bc6bSMatt Jacob 	struct g_multipath_softc *sc;
455e770bc6bSMatt Jacob 	struct g_consumer *cp, *nxtcp;
456*0c883cefSAlexander Motin 	int error, acr, acw, ace;
457e770bc6bSMatt Jacob 
458e770bc6bSMatt Jacob 	g_topology_assert();
459e770bc6bSMatt Jacob 
460e770bc6bSMatt Jacob 	sc = gp->softc;
461e770bc6bSMatt Jacob 	KASSERT(sc, ("no softc"));
462e770bc6bSMatt Jacob 
463e770bc6bSMatt Jacob 	/*
464e770bc6bSMatt Jacob 	 * Make sure that the passed provider isn't already attached
465e770bc6bSMatt Jacob 	 */
466e770bc6bSMatt Jacob 	LIST_FOREACH(cp, &gp->consumer, consumer) {
46712f35a61SPawel Jakub Dawidek 		if (cp->provider == pp)
468e770bc6bSMatt Jacob 			break;
469e770bc6bSMatt Jacob 	}
470e770bc6bSMatt Jacob 	if (cp) {
471e770bc6bSMatt Jacob 		printf("GEOM_MULTIPATH: provider %s already attached to %s\n",
472e770bc6bSMatt Jacob 		    pp->name, gp->name);
473e770bc6bSMatt Jacob 		return (EEXIST);
474e770bc6bSMatt Jacob 	}
475e770bc6bSMatt Jacob 	nxtcp = LIST_FIRST(&gp->consumer);
476e770bc6bSMatt Jacob 	cp = g_new_consumer(gp);
477*0c883cefSAlexander Motin 	cp->private = NULL;
478*0c883cefSAlexander Motin 	cp->index = MP_NEW;
479e770bc6bSMatt Jacob 	error = g_attach(cp, pp);
480e770bc6bSMatt Jacob 	if (error != 0) {
481e770bc6bSMatt Jacob 		printf("GEOM_MULTIPATH: cannot attach %s to %s",
482e770bc6bSMatt Jacob 		    pp->name, sc->sc_name);
483e770bc6bSMatt Jacob 		g_destroy_consumer(cp);
484e770bc6bSMatt Jacob 		return (error);
485e770bc6bSMatt Jacob 	}
486e770bc6bSMatt Jacob 
487e770bc6bSMatt Jacob 	/*
488e770bc6bSMatt Jacob 	 * Set access permissions on new consumer to match other consumers
489e770bc6bSMatt Jacob 	 */
490*0c883cefSAlexander Motin 	if (sc->sc_pp) {
491*0c883cefSAlexander Motin 		acr = sc->sc_pp->acr;
492*0c883cefSAlexander Motin 		acw = sc->sc_pp->acw;
493*0c883cefSAlexander Motin 		ace = sc->sc_pp->ace;
494*0c883cefSAlexander Motin 	} else
495*0c883cefSAlexander Motin 		acr = acw = ace = 0;
496*0c883cefSAlexander Motin 	if (g_multipath_exclusive) {
497*0c883cefSAlexander Motin 		acr++;
498*0c883cefSAlexander Motin 		acw++;
499*0c883cefSAlexander Motin 		ace++;
500*0c883cefSAlexander Motin 	}
501*0c883cefSAlexander Motin 	error = g_access(cp, acr, acw, ace);
502e770bc6bSMatt Jacob 	if (error) {
503e770bc6bSMatt Jacob 		printf("GEOM_MULTIPATH: cannot set access in "
504*0c883cefSAlexander Motin 		    "attaching %s to %s (%d)\n",
505*0c883cefSAlexander Motin 		    pp->name, sc->sc_name, error);
506e770bc6bSMatt Jacob 		g_detach(cp);
507e770bc6bSMatt Jacob 		g_destroy_consumer(cp);
508e770bc6bSMatt Jacob 		return (error);
509e770bc6bSMatt Jacob 	}
510*0c883cefSAlexander Motin 	if (sc->sc_pp != NULL && sc->sc_pp->mediasize == 0) {
511*0c883cefSAlexander Motin 		sc->sc_pp->mediasize = pp->mediasize -
512*0c883cefSAlexander Motin 		    ((sc->sc_uuid[0] != 0) ? pp->sectorsize : 0);
513*0c883cefSAlexander Motin 		sc->sc_pp->sectorsize = pp->sectorsize;
514e770bc6bSMatt Jacob 	}
515*0c883cefSAlexander Motin 	if (sc->sc_pp != NULL &&
516*0c883cefSAlexander Motin 	    sc->sc_pp->stripesize == 0 && sc->sc_pp->stripeoffset == 0) {
517*0c883cefSAlexander Motin 		sc->sc_pp->stripesize = pp->stripesize;
518*0c883cefSAlexander Motin 		sc->sc_pp->stripeoffset = pp->stripeoffset;
519*0c883cefSAlexander Motin 	}
520*0c883cefSAlexander Motin 	mtx_lock(&sc->sc_mtx);
521*0c883cefSAlexander Motin 	cp->index = 0;
522*0c883cefSAlexander Motin 	sc->sc_ndisks++;
523*0c883cefSAlexander Motin 	mtx_unlock(&sc->sc_mtx);
524*0c883cefSAlexander Motin 	printf("GEOM_MULTIPATH: %s added to %s\n",
525*0c883cefSAlexander Motin 	    pp->name, sc->sc_name);
526*0c883cefSAlexander Motin 	if (sc->sc_active == NULL) {
527*0c883cefSAlexander Motin 		sc->sc_active = cp;
528*0c883cefSAlexander Motin 		if (!sc->sc_active_active)
529*0c883cefSAlexander Motin 			printf("GEOM_MULTIPATH: %s is now active path in %s\n",
530e770bc6bSMatt Jacob 			    pp->name, sc->sc_name);
531e770bc6bSMatt Jacob 	}
532e770bc6bSMatt Jacob 	return (0);
533e770bc6bSMatt Jacob }
534e770bc6bSMatt Jacob 
535e770bc6bSMatt Jacob static int
536e770bc6bSMatt Jacob g_multipath_destroy(struct g_geom *gp)
537e770bc6bSMatt Jacob {
538*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
539*0c883cefSAlexander Motin 	struct g_consumer *cp, *cp1;
540e770bc6bSMatt Jacob 
541e770bc6bSMatt Jacob 	g_topology_assert();
54212f35a61SPawel Jakub Dawidek 	if (gp->softc == NULL)
543e770bc6bSMatt Jacob 		return (ENXIO);
544*0c883cefSAlexander Motin 	sc = gp->softc;
545*0c883cefSAlexander Motin 	if (!sc->sc_stopping) {
546e770bc6bSMatt Jacob 		printf("GEOM_MULTIPATH: destroying %s\n", gp->name);
547*0c883cefSAlexander Motin 		sc->sc_stopping = 1;
548*0c883cefSAlexander Motin 	}
549*0c883cefSAlexander Motin 	if (sc->sc_opened != 0) {
550*0c883cefSAlexander Motin 		if (sc->sc_pp != NULL) {
551*0c883cefSAlexander Motin 			g_wither_provider(sc->sc_pp, ENXIO);
552*0c883cefSAlexander Motin 			sc->sc_pp = NULL;
553*0c883cefSAlexander Motin 		}
554*0c883cefSAlexander Motin 		return (EINPROGRESS);
555*0c883cefSAlexander Motin 	}
556*0c883cefSAlexander Motin 	LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) {
557*0c883cefSAlexander Motin 		mtx_lock(&sc->sc_mtx);
558*0c883cefSAlexander Motin 		if ((cp->index & MP_POSTED) == 0) {
559*0c883cefSAlexander Motin 			cp->index |= MP_POSTED;
560*0c883cefSAlexander Motin 			mtx_unlock(&sc->sc_mtx);
561*0c883cefSAlexander Motin 			g_mpd(cp, 0);
562*0c883cefSAlexander Motin 			if (cp1 == NULL)
563*0c883cefSAlexander Motin 				return(0);	/* Recursion happened. */
564*0c883cefSAlexander Motin 		} else
565*0c883cefSAlexander Motin 			mtx_unlock(&sc->sc_mtx);
566*0c883cefSAlexander Motin 	}
567*0c883cefSAlexander Motin 	if (!LIST_EMPTY(&gp->consumer))
568*0c883cefSAlexander Motin 		return (EINPROGRESS);
569*0c883cefSAlexander Motin 	mtx_destroy(&sc->sc_mtx);
570e770bc6bSMatt Jacob 	g_free(gp->softc);
571e770bc6bSMatt Jacob 	gp->softc = NULL;
572*0c883cefSAlexander Motin 	printf("GEOM_MULTIPATH: %s destroyed\n", gp->name);
573e770bc6bSMatt Jacob 	g_wither_geom(gp, ENXIO);
574e770bc6bSMatt Jacob 	return (0);
575e770bc6bSMatt Jacob }
576e770bc6bSMatt Jacob 
577e770bc6bSMatt Jacob static int
578e770bc6bSMatt Jacob g_multipath_destroy_geom(struct gctl_req *req, struct g_class *mp,
579e770bc6bSMatt Jacob     struct g_geom *gp)
580e770bc6bSMatt Jacob {
58112f35a61SPawel Jakub Dawidek 
582e770bc6bSMatt Jacob 	return (g_multipath_destroy(gp));
583e770bc6bSMatt Jacob }
584e770bc6bSMatt Jacob 
585b5dce617SMatt Jacob static int
586b5dce617SMatt Jacob g_multipath_rotate(struct g_geom *gp)
587b5dce617SMatt Jacob {
588b5dce617SMatt Jacob 	struct g_consumer *lcp;
589b5dce617SMatt Jacob 	struct g_multipath_softc *sc = gp->softc;
590b5dce617SMatt Jacob 
591b5dce617SMatt Jacob 	g_topology_assert();
592b5dce617SMatt Jacob 	if (sc == NULL)
593b5dce617SMatt Jacob 		return (ENXIO);
594b5dce617SMatt Jacob 	LIST_FOREACH(lcp, &gp->consumer, consumer) {
595b5dce617SMatt Jacob 		if ((lcp->index & MP_BAD) == 0) {
596*0c883cefSAlexander Motin 			if (sc->sc_active != lcp)
597b5dce617SMatt Jacob 				break;
598b5dce617SMatt Jacob 		}
599b5dce617SMatt Jacob 	}
600b5dce617SMatt Jacob 	if (lcp) {
601*0c883cefSAlexander Motin 		sc->sc_active = lcp;
602*0c883cefSAlexander Motin 		if (!sc->sc_active_active)
603*0c883cefSAlexander Motin 			printf("GEOM_MULTIPATH: %s is now active path in %s\n",
604b5dce617SMatt Jacob 			    lcp->provider->name, sc->sc_name);
605b5dce617SMatt Jacob 	}
606b5dce617SMatt Jacob 	return (0);
607b5dce617SMatt Jacob }
608b5dce617SMatt Jacob 
609e770bc6bSMatt Jacob static void
610e770bc6bSMatt Jacob g_multipath_init(struct g_class *mp)
611e770bc6bSMatt Jacob {
612e770bc6bSMatt Jacob 	bioq_init(&gmtbq);
613e770bc6bSMatt Jacob 	mtx_init(&gmtbq_mtx, "gmtbq", NULL, MTX_DEF);
61412f35a61SPawel Jakub Dawidek 	if (kproc_create(g_multipath_kt, mp, NULL, 0, 0, "g_mp_kt") == 0)
615e770bc6bSMatt Jacob 		g_multipath_kt_state = GKT_RUN;
616e770bc6bSMatt Jacob }
617e770bc6bSMatt Jacob 
618e770bc6bSMatt Jacob static void
619e770bc6bSMatt Jacob g_multipath_fini(struct g_class *mp)
620e770bc6bSMatt Jacob {
621e770bc6bSMatt Jacob 	if (g_multipath_kt_state == GKT_RUN) {
622e770bc6bSMatt Jacob 		mtx_lock(&gmtbq_mtx);
623e770bc6bSMatt Jacob 		g_multipath_kt_state = GKT_DIE;
624e770bc6bSMatt Jacob 		wakeup(&g_multipath_kt_state);
625e770bc6bSMatt Jacob 		msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO,
626e770bc6bSMatt Jacob 		    "gmp:fini", 0);
627e770bc6bSMatt Jacob 		mtx_unlock(&gmtbq_mtx);
628e770bc6bSMatt Jacob 	}
629e770bc6bSMatt Jacob }
630e770bc6bSMatt Jacob 
631e770bc6bSMatt Jacob static int
632e770bc6bSMatt Jacob g_multipath_read_metadata(struct g_consumer *cp,
633e770bc6bSMatt Jacob     struct g_multipath_metadata *md)
634e770bc6bSMatt Jacob {
635e770bc6bSMatt Jacob 	struct g_provider *pp;
636e770bc6bSMatt Jacob 	u_char *buf;
637e770bc6bSMatt Jacob 	int error;
638e770bc6bSMatt Jacob 
639e770bc6bSMatt Jacob 	g_topology_assert();
640e770bc6bSMatt Jacob 	error = g_access(cp, 1, 0, 0);
64112f35a61SPawel Jakub Dawidek 	if (error != 0)
642e770bc6bSMatt Jacob 		return (error);
643e770bc6bSMatt Jacob 	pp = cp->provider;
644e770bc6bSMatt Jacob 	g_topology_unlock();
645e770bc6bSMatt Jacob 	buf = g_read_data(cp, pp->mediasize - pp->sectorsize,
646e770bc6bSMatt Jacob 	    pp->sectorsize, &error);
647e770bc6bSMatt Jacob 	g_topology_lock();
648e770bc6bSMatt Jacob 	g_access(cp, -1, 0, 0);
64912f35a61SPawel Jakub Dawidek 	if (buf == NULL)
650e770bc6bSMatt Jacob 		return (error);
651e770bc6bSMatt Jacob 	multipath_metadata_decode(buf, md);
652e770bc6bSMatt Jacob 	g_free(buf);
653e770bc6bSMatt Jacob 	return (0);
654e770bc6bSMatt Jacob }
655e770bc6bSMatt Jacob 
656e770bc6bSMatt Jacob static struct g_geom *
657e770bc6bSMatt Jacob g_multipath_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
658e770bc6bSMatt Jacob {
659e770bc6bSMatt Jacob 	struct g_multipath_metadata md;
660e770bc6bSMatt Jacob 	struct g_multipath_softc *sc;
661e770bc6bSMatt Jacob 	struct g_consumer *cp;
662e770bc6bSMatt Jacob 	struct g_geom *gp, *gp1;
663e770bc6bSMatt Jacob 	int error, isnew;
664e770bc6bSMatt Jacob 
665e770bc6bSMatt Jacob 	g_topology_assert();
666e770bc6bSMatt Jacob 
667e770bc6bSMatt Jacob 	gp = g_new_geomf(mp, "multipath:taste");
668e770bc6bSMatt Jacob 	gp->start = g_multipath_start;
669e770bc6bSMatt Jacob 	gp->access = g_multipath_access;
670e770bc6bSMatt Jacob 	gp->orphan = g_multipath_orphan;
671e770bc6bSMatt Jacob 	cp = g_new_consumer(gp);
672e770bc6bSMatt Jacob 	g_attach(cp, pp);
673e770bc6bSMatt Jacob 	error = g_multipath_read_metadata(cp, &md);
674e770bc6bSMatt Jacob 	g_detach(cp);
675e770bc6bSMatt Jacob 	g_destroy_consumer(cp);
676e770bc6bSMatt Jacob 	g_destroy_geom(gp);
67712f35a61SPawel Jakub Dawidek 	if (error != 0)
678e770bc6bSMatt Jacob 		return (NULL);
679e770bc6bSMatt Jacob 	gp = NULL;
680e770bc6bSMatt Jacob 
681e770bc6bSMatt Jacob 	if (strcmp(md.md_magic, G_MULTIPATH_MAGIC) != 0) {
68212f35a61SPawel Jakub Dawidek 		if (g_multipath_debug)
683e770bc6bSMatt Jacob 			printf("%s is not MULTIPATH\n", pp->name);
684e770bc6bSMatt Jacob 		return (NULL);
685e770bc6bSMatt Jacob 	}
686e770bc6bSMatt Jacob 	if (md.md_version != G_MULTIPATH_VERSION) {
687e770bc6bSMatt Jacob 		printf("%s has version %d multipath id- this module is version "
688e770bc6bSMatt Jacob 		    " %d: rejecting\n", pp->name, md.md_version,
689e770bc6bSMatt Jacob 		    G_MULTIPATH_VERSION);
690e770bc6bSMatt Jacob 		return (NULL);
691e770bc6bSMatt Jacob 	}
692*0c883cefSAlexander Motin 	if (md.md_size != 0 && md.md_size != pp->mediasize)
693*0c883cefSAlexander Motin 		return (NULL);
694*0c883cefSAlexander Motin 	if (md.md_sectorsize != 0 && md.md_sectorsize != pp->sectorsize)
695*0c883cefSAlexander Motin 		return (NULL);
69612f35a61SPawel Jakub Dawidek 	if (g_multipath_debug)
697e770bc6bSMatt Jacob 		printf("MULTIPATH: %s/%s\n", md.md_name, md.md_uuid);
698e770bc6bSMatt Jacob 
699e770bc6bSMatt Jacob 	/*
700e770bc6bSMatt Jacob 	 * Let's check if such a device already is present. We check against
701e770bc6bSMatt Jacob 	 * uuid alone first because that's the true distinguishor. If that
702e770bc6bSMatt Jacob 	 * passes, then we check for name conflicts. If there are conflicts,
703e770bc6bSMatt Jacob 	 * modify the name.
704e770bc6bSMatt Jacob 	 *
705e770bc6bSMatt Jacob 	 * The whole purpose of this is to solve the problem that people don't
706e770bc6bSMatt Jacob 	 * pick good unique names, but good unique names (like uuids) are a
707e770bc6bSMatt Jacob 	 * pain to use. So, we allow people to build GEOMs with friendly names
708e770bc6bSMatt Jacob 	 * and uuids, and modify the names in case there's a collision.
709e770bc6bSMatt Jacob 	 */
710e770bc6bSMatt Jacob 	sc = NULL;
711e770bc6bSMatt Jacob 	LIST_FOREACH(gp, &mp->geom, geom) {
712e770bc6bSMatt Jacob 		sc = gp->softc;
713*0c883cefSAlexander Motin 		if (sc == NULL || sc->sc_stopping)
714e770bc6bSMatt Jacob 			continue;
71512f35a61SPawel Jakub Dawidek 		if (strncmp(md.md_uuid, sc->sc_uuid, sizeof(md.md_uuid)) == 0)
716e770bc6bSMatt Jacob 			break;
717e770bc6bSMatt Jacob 	}
718e770bc6bSMatt Jacob 
719e770bc6bSMatt Jacob 	LIST_FOREACH(gp1, &mp->geom, geom) {
72012f35a61SPawel Jakub Dawidek 		if (gp1 == gp)
721e770bc6bSMatt Jacob 			continue;
722e770bc6bSMatt Jacob 		sc = gp1->softc;
723*0c883cefSAlexander Motin 		if (sc == NULL || sc->sc_stopping)
724e770bc6bSMatt Jacob 			continue;
72512f35a61SPawel Jakub Dawidek 		if (strncmp(md.md_name, sc->sc_name, sizeof(md.md_name)) == 0)
726e770bc6bSMatt Jacob 			break;
727e770bc6bSMatt Jacob 	}
728e770bc6bSMatt Jacob 
729e770bc6bSMatt Jacob 	/*
730e770bc6bSMatt Jacob 	 * If gp is NULL, we had no extant MULTIPATH geom with this uuid.
731e770bc6bSMatt Jacob 	 *
732e770bc6bSMatt Jacob 	 * If gp1 is *not* NULL, that means we have a MULTIPATH geom extant
733e770bc6bSMatt Jacob 	 * with the same name (but a different UUID).
734e770bc6bSMatt Jacob 	 *
735e770bc6bSMatt Jacob 	 * If gp is NULL, then modify the name with a random number and
736e770bc6bSMatt Jacob   	 * complain, but allow the creation of the geom to continue.
737e770bc6bSMatt Jacob 	 *
738e770bc6bSMatt Jacob 	 * If gp is *not* NULL, just use the geom's name as we're attaching
739e770bc6bSMatt Jacob 	 * this disk to the (previously generated) name.
740e770bc6bSMatt Jacob 	 */
741e770bc6bSMatt Jacob 
742e770bc6bSMatt Jacob 	if (gp1) {
743e770bc6bSMatt Jacob 		sc = gp1->softc;
744e770bc6bSMatt Jacob 		if (gp == NULL) {
745e770bc6bSMatt Jacob 			char buf[16];
746e770bc6bSMatt Jacob 			u_long rand = random();
747e770bc6bSMatt Jacob 
748e770bc6bSMatt Jacob 			snprintf(buf, sizeof (buf), "%s-%lu", md.md_name, rand);
749e770bc6bSMatt Jacob 			printf("GEOM_MULTIPATH: geom %s/%s exists already\n",
750e770bc6bSMatt Jacob 			    sc->sc_name, sc->sc_uuid);
751e770bc6bSMatt Jacob 			printf("GEOM_MULTIPATH: %s will be (temporarily) %s\n",
752e770bc6bSMatt Jacob 			    md.md_uuid, buf);
753e770bc6bSMatt Jacob 			strlcpy(md.md_name, buf, sizeof(md.md_name));
754e770bc6bSMatt Jacob 		} else {
755e770bc6bSMatt Jacob 			strlcpy(md.md_name, sc->sc_name, sizeof(md.md_name));
756e770bc6bSMatt Jacob 		}
757e770bc6bSMatt Jacob 	}
758e770bc6bSMatt Jacob 
759e770bc6bSMatt Jacob 	if (gp == NULL) {
760e770bc6bSMatt Jacob 		gp = g_multipath_create(mp, &md);
761e770bc6bSMatt Jacob 		if (gp == NULL) {
762e770bc6bSMatt Jacob 			printf("GEOM_MULTIPATH: cannot create geom %s/%s\n",
763e770bc6bSMatt Jacob 			    md.md_name, md.md_uuid);
764e770bc6bSMatt Jacob 			return (NULL);
765e770bc6bSMatt Jacob 		}
766e770bc6bSMatt Jacob 		isnew = 1;
767e770bc6bSMatt Jacob 	} else {
768e770bc6bSMatt Jacob 		isnew = 0;
769e770bc6bSMatt Jacob 	}
770e770bc6bSMatt Jacob 
771e770bc6bSMatt Jacob 	sc = gp->softc;
772e770bc6bSMatt Jacob 	KASSERT(sc != NULL, ("sc is NULL"));
773e770bc6bSMatt Jacob 	error = g_multipath_add_disk(gp, pp);
774e770bc6bSMatt Jacob 	if (error != 0) {
77512f35a61SPawel Jakub Dawidek 		if (isnew)
776e770bc6bSMatt Jacob 			g_multipath_destroy(gp);
777e770bc6bSMatt Jacob 		return (NULL);
778e770bc6bSMatt Jacob 	}
779e770bc6bSMatt Jacob 	return (gp);
780e770bc6bSMatt Jacob }
781e770bc6bSMatt Jacob 
782e770bc6bSMatt Jacob static void
783*0c883cefSAlexander Motin g_multipath_ctl_add_name(struct gctl_req *req, struct g_class *mp,
784*0c883cefSAlexander Motin     const char *name)
785e770bc6bSMatt Jacob {
786*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
787e770bc6bSMatt Jacob 	struct g_geom *gp;
7882b4969ffSMatt Jacob 	struct g_consumer *cp;
789*0c883cefSAlexander Motin 	struct g_provider *pp;
790*0c883cefSAlexander Motin 	const char *mpname;
791e770bc6bSMatt Jacob 	static const char devpf[6] = "/dev/";
792e770bc6bSMatt Jacob 
793e770bc6bSMatt Jacob 	g_topology_assert();
794e770bc6bSMatt Jacob 
795e770bc6bSMatt Jacob 	mpname = gctl_get_asciiparam(req, "arg0");
796e770bc6bSMatt Jacob         if (mpname == NULL) {
797e770bc6bSMatt Jacob                 gctl_error(req, "No 'arg0' argument");
798e770bc6bSMatt Jacob                 return;
799e770bc6bSMatt Jacob         }
8002b4969ffSMatt Jacob 	gp = g_multipath_find_geom(mp, mpname);
8012b4969ffSMatt Jacob 	if (gp == NULL) {
8022b4969ffSMatt Jacob 		gctl_error(req, "Device %s is invalid", mpname);
803e770bc6bSMatt Jacob 		return;
804e770bc6bSMatt Jacob 	}
805*0c883cefSAlexander Motin 	sc = gp->softc;
806e770bc6bSMatt Jacob 
80712f35a61SPawel Jakub Dawidek 	if (strncmp(name, devpf, 5) == 0)
808e770bc6bSMatt Jacob 		name += 5;
8092b4969ffSMatt Jacob 	pp = g_provider_by_name(name);
8102b4969ffSMatt Jacob 	if (pp == NULL) {
811e770bc6bSMatt Jacob 		gctl_error(req, "Provider %s is invalid", name);
812e770bc6bSMatt Jacob 		return;
813e770bc6bSMatt Jacob 	}
814e770bc6bSMatt Jacob 
815e770bc6bSMatt Jacob 	/*
816*0c883cefSAlexander Motin 	 * Check to make sure parameters match.
817e770bc6bSMatt Jacob 	 */
818*0c883cefSAlexander Motin 	LIST_FOREACH(cp, &gp->consumer, consumer) {
819*0c883cefSAlexander Motin 		if (cp->provider == pp) {
820*0c883cefSAlexander Motin 			gctl_error(req, "provider %s is already there",
821*0c883cefSAlexander Motin 			    pp->name);
822e770bc6bSMatt Jacob 			return;
823e770bc6bSMatt Jacob 		}
824*0c883cefSAlexander Motin 	}
825*0c883cefSAlexander Motin 	if (sc->sc_pp != NULL && sc->sc_pp->mediasize != 0 &&
826*0c883cefSAlexander Motin 	    sc->sc_pp->mediasize + (sc->sc_uuid[0] != 0 ? pp->sectorsize : 0)
827*0c883cefSAlexander Motin 	     != pp->mediasize) {
828*0c883cefSAlexander Motin 		gctl_error(req, "Providers size mismatch %jd != %jd",
829*0c883cefSAlexander Motin 		    (intmax_t) sc->sc_pp->mediasize +
830*0c883cefSAlexander Motin 			(sc->sc_uuid[0] != 0 ? pp->sectorsize : 0),
831*0c883cefSAlexander Motin 		    (intmax_t) pp->mediasize);
832e770bc6bSMatt Jacob 		return;
833e770bc6bSMatt Jacob 	}
834*0c883cefSAlexander Motin 	if (sc->sc_pp != NULL && sc->sc_pp->sectorsize != 0 &&
835*0c883cefSAlexander Motin 	    sc->sc_pp->sectorsize != pp->sectorsize) {
836*0c883cefSAlexander Motin 		gctl_error(req, "Providers sectorsize mismatch %u != %u",
837*0c883cefSAlexander Motin 		    sc->sc_pp->sectorsize, pp->sectorsize);
838e770bc6bSMatt Jacob 		return;
839e770bc6bSMatt Jacob 	}
840e770bc6bSMatt Jacob 
841e770bc6bSMatt Jacob 	/*
8422b4969ffSMatt Jacob 	 * Now add....
843e770bc6bSMatt Jacob 	 */
8442b4969ffSMatt Jacob 	(void) g_multipath_add_disk(gp, pp);
845e770bc6bSMatt Jacob }
846e770bc6bSMatt Jacob 
847*0c883cefSAlexander Motin static void
848*0c883cefSAlexander Motin g_multipath_ctl_add(struct gctl_req *req, struct g_class *mp)
849*0c883cefSAlexander Motin {
850*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
851*0c883cefSAlexander Motin 	struct g_geom *gp;
852*0c883cefSAlexander Motin 	const char *mpname, *name;
853*0c883cefSAlexander Motin 
854*0c883cefSAlexander Motin 	mpname = gctl_get_asciiparam(req, "arg0");
855*0c883cefSAlexander Motin         if (mpname == NULL) {
856*0c883cefSAlexander Motin                 gctl_error(req, "No 'arg0' argument");
857*0c883cefSAlexander Motin                 return;
858*0c883cefSAlexander Motin         }
859*0c883cefSAlexander Motin 	gp = g_multipath_find_geom(mp, mpname);
860*0c883cefSAlexander Motin 	if (gp == NULL) {
861*0c883cefSAlexander Motin 		gctl_error(req, "Device %s not found", mpname);
862*0c883cefSAlexander Motin 		return;
863*0c883cefSAlexander Motin 	}
864*0c883cefSAlexander Motin 	sc = gp->softc;
865*0c883cefSAlexander Motin 
866*0c883cefSAlexander Motin 	name = gctl_get_asciiparam(req, "arg1");
867*0c883cefSAlexander Motin 	if (name == NULL) {
868*0c883cefSAlexander Motin 		gctl_error(req, "No 'arg1' argument");
869*0c883cefSAlexander Motin 		return;
870*0c883cefSAlexander Motin 	}
871*0c883cefSAlexander Motin 	g_multipath_ctl_add_name(req, mp, name);
872*0c883cefSAlexander Motin }
873*0c883cefSAlexander Motin 
874*0c883cefSAlexander Motin static void
875*0c883cefSAlexander Motin g_multipath_ctl_create(struct gctl_req *req, struct g_class *mp)
876*0c883cefSAlexander Motin {
877*0c883cefSAlexander Motin 	struct g_multipath_metadata md;
878*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
879*0c883cefSAlexander Motin 	struct g_geom *gp;
880*0c883cefSAlexander Motin 	const char *mpname, *name;
881*0c883cefSAlexander Motin 	char param[16];
882*0c883cefSAlexander Motin 	int *nargs, i, *active_active;
883*0c883cefSAlexander Motin 
884*0c883cefSAlexander Motin 	g_topology_assert();
885*0c883cefSAlexander Motin 
886*0c883cefSAlexander Motin 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
887*0c883cefSAlexander Motin 	if (*nargs < 2) {
888*0c883cefSAlexander Motin 		gctl_error(req, "wrong number of arguments.");
889*0c883cefSAlexander Motin 		return;
890*0c883cefSAlexander Motin 	}
891*0c883cefSAlexander Motin 
892*0c883cefSAlexander Motin 	mpname = gctl_get_asciiparam(req, "arg0");
893*0c883cefSAlexander Motin         if (mpname == NULL) {
894*0c883cefSAlexander Motin                 gctl_error(req, "No 'arg0' argument");
895*0c883cefSAlexander Motin                 return;
896*0c883cefSAlexander Motin         }
897*0c883cefSAlexander Motin 	gp = g_multipath_find_geom(mp, mpname);
898*0c883cefSAlexander Motin 	if (gp != NULL) {
899*0c883cefSAlexander Motin 		gctl_error(req, "Device %s already exist", mpname);
900*0c883cefSAlexander Motin 		return;
901*0c883cefSAlexander Motin 	}
902*0c883cefSAlexander Motin 	sc = gp->softc;
903*0c883cefSAlexander Motin 
904*0c883cefSAlexander Motin 	memset(&md, 0, sizeof(md));
905*0c883cefSAlexander Motin 	strlcpy(md.md_magic, G_MULTIPATH_MAGIC, sizeof(md.md_magic));
906*0c883cefSAlexander Motin 	md.md_version = G_MULTIPATH_VERSION;
907*0c883cefSAlexander Motin 	strlcpy(md.md_name, mpname, sizeof(md.md_name));
908*0c883cefSAlexander Motin 	md.md_size = 0;
909*0c883cefSAlexander Motin 	md.md_sectorsize = 0;
910*0c883cefSAlexander Motin 	md.md_uuid[0] = 0;
911*0c883cefSAlexander Motin 	active_active = gctl_get_paraml(req, "active_active",
912*0c883cefSAlexander Motin 	    sizeof(*active_active));
913*0c883cefSAlexander Motin 	md.md_active_active =
914*0c883cefSAlexander Motin 	    (active_active == NULL || *active_active == 0) ? 0 : 1;
915*0c883cefSAlexander Motin 	gp = g_multipath_create(mp, &md);
916*0c883cefSAlexander Motin 	if (gp == NULL) {
917*0c883cefSAlexander Motin 		gctl_error(req, "GEOM_MULTIPATH: cannot create geom %s/%s\n",
918*0c883cefSAlexander Motin 		    md.md_name, md.md_uuid);
919*0c883cefSAlexander Motin 		return;
920*0c883cefSAlexander Motin 	}
921*0c883cefSAlexander Motin 	sc = gp->softc;
922*0c883cefSAlexander Motin 
923*0c883cefSAlexander Motin 	for (i = 1; i < *nargs; i++) {
924*0c883cefSAlexander Motin 		snprintf(param, sizeof(param), "arg%d", i);
925*0c883cefSAlexander Motin 		name = gctl_get_asciiparam(req, param);
926*0c883cefSAlexander Motin 		g_multipath_ctl_add_name(req, mp, name);
927*0c883cefSAlexander Motin 	}
928*0c883cefSAlexander Motin 
929*0c883cefSAlexander Motin 	if (sc->sc_ndisks != (*nargs - 1))
930*0c883cefSAlexander Motin 		g_multipath_destroy(gp);
931*0c883cefSAlexander Motin }
932*0c883cefSAlexander Motin 
933*0c883cefSAlexander Motin static void
934*0c883cefSAlexander Motin g_multipath_ctl_fail(struct gctl_req *req, struct g_class *mp, int fail)
935*0c883cefSAlexander Motin {
936*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
937*0c883cefSAlexander Motin 	struct g_geom *gp;
938*0c883cefSAlexander Motin 	struct g_consumer *cp;
939*0c883cefSAlexander Motin 	const char *mpname, *name;
940*0c883cefSAlexander Motin 	int found;
941*0c883cefSAlexander Motin 
942*0c883cefSAlexander Motin 	mpname = gctl_get_asciiparam(req, "arg0");
943*0c883cefSAlexander Motin         if (mpname == NULL) {
944*0c883cefSAlexander Motin                 gctl_error(req, "No 'arg0' argument");
945*0c883cefSAlexander Motin                 return;
946*0c883cefSAlexander Motin         }
947*0c883cefSAlexander Motin 	gp = g_multipath_find_geom(mp, mpname);
948*0c883cefSAlexander Motin 	if (gp == NULL) {
949*0c883cefSAlexander Motin 		gctl_error(req, "Device %s not found", mpname);
950*0c883cefSAlexander Motin 		return;
951*0c883cefSAlexander Motin 	}
952*0c883cefSAlexander Motin 	sc = gp->softc;
953*0c883cefSAlexander Motin 
954*0c883cefSAlexander Motin 	name = gctl_get_asciiparam(req, "arg1");
955*0c883cefSAlexander Motin 	if (name == NULL) {
956*0c883cefSAlexander Motin 		gctl_error(req, "No 'arg1' argument");
957*0c883cefSAlexander Motin 		return;
958*0c883cefSAlexander Motin 	}
959*0c883cefSAlexander Motin 
960*0c883cefSAlexander Motin 	found = 0;
961*0c883cefSAlexander Motin 	mtx_lock(&sc->sc_mtx);
962*0c883cefSAlexander Motin 	LIST_FOREACH(cp, &gp->consumer, consumer) {
963*0c883cefSAlexander Motin 		if (cp->provider != NULL &&
964*0c883cefSAlexander Motin 		    strcmp(cp->provider->name, name) == 0 &&
965*0c883cefSAlexander Motin 		    (cp->index & MP_LOST) == 0) {
966*0c883cefSAlexander Motin 			found = 1;
967*0c883cefSAlexander Motin 			printf("GEOM_MULTIPATH: %s in %s is marked %s.\n",
968*0c883cefSAlexander Motin 				name, sc->sc_name, fail ? "FAIL" : "OK");
969*0c883cefSAlexander Motin 			if (fail) {
970*0c883cefSAlexander Motin 				g_multipath_fault(cp, MP_FAIL);
971*0c883cefSAlexander Motin 			} else {
972*0c883cefSAlexander Motin 				cp->index &= ~MP_FAIL;
973*0c883cefSAlexander Motin 			}
974*0c883cefSAlexander Motin 		}
975*0c883cefSAlexander Motin 	}
976*0c883cefSAlexander Motin 	mtx_unlock(&sc->sc_mtx);
977*0c883cefSAlexander Motin 	if (found == 0)
978*0c883cefSAlexander Motin 		gctl_error(req, "Provider %s not found", name);
979*0c883cefSAlexander Motin }
980*0c883cefSAlexander Motin 
981*0c883cefSAlexander Motin static void
982*0c883cefSAlexander Motin g_multipath_ctl_remove(struct gctl_req *req, struct g_class *mp)
983*0c883cefSAlexander Motin {
984*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
985*0c883cefSAlexander Motin 	struct g_geom *gp;
986*0c883cefSAlexander Motin 	struct g_consumer *cp, *cp1;
987*0c883cefSAlexander Motin 	const char *mpname, *name;
988*0c883cefSAlexander Motin 	uintptr_t *cnt;
989*0c883cefSAlexander Motin 	int found;
990*0c883cefSAlexander Motin 
991*0c883cefSAlexander Motin 	mpname = gctl_get_asciiparam(req, "arg0");
992*0c883cefSAlexander Motin         if (mpname == NULL) {
993*0c883cefSAlexander Motin                 gctl_error(req, "No 'arg0' argument");
994*0c883cefSAlexander Motin                 return;
995*0c883cefSAlexander Motin         }
996*0c883cefSAlexander Motin 	gp = g_multipath_find_geom(mp, mpname);
997*0c883cefSAlexander Motin 	if (gp == NULL) {
998*0c883cefSAlexander Motin 		gctl_error(req, "Device %s not found", mpname);
999*0c883cefSAlexander Motin 		return;
1000*0c883cefSAlexander Motin 	}
1001*0c883cefSAlexander Motin 	sc = gp->softc;
1002*0c883cefSAlexander Motin 
1003*0c883cefSAlexander Motin 	name = gctl_get_asciiparam(req, "arg1");
1004*0c883cefSAlexander Motin 	if (name == NULL) {
1005*0c883cefSAlexander Motin 		gctl_error(req, "No 'arg1' argument");
1006*0c883cefSAlexander Motin 		return;
1007*0c883cefSAlexander Motin 	}
1008*0c883cefSAlexander Motin 
1009*0c883cefSAlexander Motin 	found = 0;
1010*0c883cefSAlexander Motin 	mtx_lock(&sc->sc_mtx);
1011*0c883cefSAlexander Motin 	LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) {
1012*0c883cefSAlexander Motin 		if (cp->provider != NULL &&
1013*0c883cefSAlexander Motin 		    strcmp(cp->provider->name, name) == 0 &&
1014*0c883cefSAlexander Motin 		    (cp->index & MP_LOST) == 0) {
1015*0c883cefSAlexander Motin 			found = 1;
1016*0c883cefSAlexander Motin 			printf("GEOM_MULTIPATH: removing %s from %s\n",
1017*0c883cefSAlexander Motin 			    cp->provider->name, cp->geom->name);
1018*0c883cefSAlexander Motin 			sc->sc_ndisks--;
1019*0c883cefSAlexander Motin 			g_multipath_fault(cp, MP_LOST);
1020*0c883cefSAlexander Motin 			cnt = (uintptr_t *)&cp->private;
1021*0c883cefSAlexander Motin 			if (*cnt == 0 && (cp->index & MP_POSTED) == 0) {
1022*0c883cefSAlexander Motin 				cp->index |= MP_POSTED;
1023*0c883cefSAlexander Motin 				mtx_unlock(&sc->sc_mtx);
1024*0c883cefSAlexander Motin 				g_mpd(cp, 0);
1025*0c883cefSAlexander Motin 				if (cp1 == NULL)
1026*0c883cefSAlexander Motin 					return;	/* Recursion happened. */
1027*0c883cefSAlexander Motin 				mtx_lock(&sc->sc_mtx);
1028*0c883cefSAlexander Motin 			}
1029*0c883cefSAlexander Motin 		}
1030*0c883cefSAlexander Motin 	}
1031*0c883cefSAlexander Motin 	mtx_unlock(&sc->sc_mtx);
1032*0c883cefSAlexander Motin 	if (found == 0)
1033*0c883cefSAlexander Motin 		gctl_error(req, "Provider %s not found", name);
1034*0c883cefSAlexander Motin }
1035*0c883cefSAlexander Motin 
1036e770bc6bSMatt Jacob static struct g_geom *
1037e770bc6bSMatt Jacob g_multipath_find_geom(struct g_class *mp, const char *name)
1038e770bc6bSMatt Jacob {
1039e770bc6bSMatt Jacob 	struct g_geom *gp;
1040*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
1041e770bc6bSMatt Jacob 
1042e770bc6bSMatt Jacob 	LIST_FOREACH(gp, &mp->geom, geom) {
1043*0c883cefSAlexander Motin 		sc = gp->softc;
1044*0c883cefSAlexander Motin 		if (sc == NULL || sc->sc_stopping)
1045*0c883cefSAlexander Motin 			continue;
1046*0c883cefSAlexander Motin 		if (strcmp(gp->name, name) == 0)
1047e770bc6bSMatt Jacob 			return (gp);
1048e770bc6bSMatt Jacob 	}
1049e770bc6bSMatt Jacob 	return (NULL);
1050e770bc6bSMatt Jacob }
1051e770bc6bSMatt Jacob 
1052e770bc6bSMatt Jacob static void
1053*0c883cefSAlexander Motin g_multipath_ctl_stop(struct gctl_req *req, struct g_class *mp)
1054e770bc6bSMatt Jacob {
1055e770bc6bSMatt Jacob 	struct g_geom *gp;
1056e770bc6bSMatt Jacob 	const char *name;
1057e770bc6bSMatt Jacob 	int error;
1058e770bc6bSMatt Jacob 
1059e770bc6bSMatt Jacob 	g_topology_assert();
1060e770bc6bSMatt Jacob 
1061e770bc6bSMatt Jacob 	name = gctl_get_asciiparam(req, "arg0");
1062e770bc6bSMatt Jacob         if (name == NULL) {
1063e770bc6bSMatt Jacob                 gctl_error(req, "No 'arg0' argument");
1064e770bc6bSMatt Jacob                 return;
1065e770bc6bSMatt Jacob         }
1066e770bc6bSMatt Jacob 	gp = g_multipath_find_geom(mp, name);
1067e770bc6bSMatt Jacob 	if (gp == NULL) {
1068e770bc6bSMatt Jacob 		gctl_error(req, "Device %s is invalid", name);
1069e770bc6bSMatt Jacob 		return;
1070e770bc6bSMatt Jacob 	}
1071e770bc6bSMatt Jacob 	error = g_multipath_destroy(gp);
1072*0c883cefSAlexander Motin 	if (error != 0 && error != EINPROGRESS)
1073*0c883cefSAlexander Motin 		gctl_error(req, "failed to stop %s (err=%d)", name, error);
1074e770bc6bSMatt Jacob }
1075*0c883cefSAlexander Motin 
1076*0c883cefSAlexander Motin static void
1077*0c883cefSAlexander Motin g_multipath_ctl_destroy(struct gctl_req *req, struct g_class *mp)
1078*0c883cefSAlexander Motin {
1079*0c883cefSAlexander Motin 	struct g_geom *gp;
1080*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
1081*0c883cefSAlexander Motin 	struct g_consumer *cp;
1082*0c883cefSAlexander Motin 	struct g_provider *pp;
1083*0c883cefSAlexander Motin 	const char *name;
1084*0c883cefSAlexander Motin 	uint8_t *buf;
1085*0c883cefSAlexander Motin 	int error;
1086*0c883cefSAlexander Motin 
1087*0c883cefSAlexander Motin 	g_topology_assert();
1088*0c883cefSAlexander Motin 
1089*0c883cefSAlexander Motin 	name = gctl_get_asciiparam(req, "arg0");
1090*0c883cefSAlexander Motin         if (name == NULL) {
1091*0c883cefSAlexander Motin                 gctl_error(req, "No 'arg0' argument");
1092*0c883cefSAlexander Motin                 return;
1093*0c883cefSAlexander Motin         }
1094*0c883cefSAlexander Motin 	gp = g_multipath_find_geom(mp, name);
1095*0c883cefSAlexander Motin 	if (gp == NULL) {
1096*0c883cefSAlexander Motin 		gctl_error(req, "Device %s is invalid", name);
1097*0c883cefSAlexander Motin 		return;
1098*0c883cefSAlexander Motin 	}
1099*0c883cefSAlexander Motin 	sc = gp->softc;
1100*0c883cefSAlexander Motin 
1101*0c883cefSAlexander Motin 	if (sc->sc_uuid[0] != 0 && sc->sc_active != NULL) {
1102*0c883cefSAlexander Motin 		cp = sc->sc_active;
1103*0c883cefSAlexander Motin 		pp = cp->provider;
1104*0c883cefSAlexander Motin 		error = g_access(cp, 1, 1, 1);
1105*0c883cefSAlexander Motin 		if (error != 0) {
1106*0c883cefSAlexander Motin 			gctl_error(req, "Can't open %s (%d)", pp->name, error);
1107*0c883cefSAlexander Motin 			goto destroy;
1108*0c883cefSAlexander Motin 		}
1109*0c883cefSAlexander Motin 		g_topology_unlock();
1110*0c883cefSAlexander Motin 		buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
1111*0c883cefSAlexander Motin 		error = g_write_data(cp, pp->mediasize - pp->sectorsize,
1112*0c883cefSAlexander Motin 		    buf, pp->sectorsize);
1113*0c883cefSAlexander Motin 		g_topology_lock();
1114*0c883cefSAlexander Motin 		g_access(cp, -1, -1, -1);
1115*0c883cefSAlexander Motin 		if (error != 0)
1116*0c883cefSAlexander Motin 			gctl_error(req, "Can't erase metadata on %s (%d)",
1117*0c883cefSAlexander Motin 			    pp->name, error);
1118*0c883cefSAlexander Motin 	}
1119*0c883cefSAlexander Motin 
1120*0c883cefSAlexander Motin destroy:
1121*0c883cefSAlexander Motin 	error = g_multipath_destroy(gp);
1122*0c883cefSAlexander Motin 	if (error != 0 && error != EINPROGRESS)
1123*0c883cefSAlexander Motin 		gctl_error(req, "failed to destroy %s (err=%d)", name, error);
1124e770bc6bSMatt Jacob }
1125e770bc6bSMatt Jacob 
1126e770bc6bSMatt Jacob static void
1127b5dce617SMatt Jacob g_multipath_ctl_rotate(struct gctl_req *req, struct g_class *mp)
1128b5dce617SMatt Jacob {
1129b5dce617SMatt Jacob 	struct g_geom *gp;
1130b5dce617SMatt Jacob 	const char *name;
1131b5dce617SMatt Jacob 	int error;
1132b5dce617SMatt Jacob 
1133b5dce617SMatt Jacob 	g_topology_assert();
1134b5dce617SMatt Jacob 
1135b5dce617SMatt Jacob 	name = gctl_get_asciiparam(req, "arg0");
1136b5dce617SMatt Jacob         if (name == NULL) {
1137b5dce617SMatt Jacob                 gctl_error(req, "No 'arg0' argument");
1138b5dce617SMatt Jacob                 return;
1139b5dce617SMatt Jacob         }
1140b5dce617SMatt Jacob 	gp = g_multipath_find_geom(mp, name);
1141b5dce617SMatt Jacob 	if (gp == NULL) {
1142b5dce617SMatt Jacob 		gctl_error(req, "Device %s is invalid", name);
1143b5dce617SMatt Jacob 		return;
1144b5dce617SMatt Jacob 	}
1145b5dce617SMatt Jacob 	error = g_multipath_rotate(gp);
1146b5dce617SMatt Jacob 	if (error != 0) {
1147b5dce617SMatt Jacob 		gctl_error(req, "failed to rotate %s (err=%d)", name, error);
1148b5dce617SMatt Jacob 	}
1149b5dce617SMatt Jacob }
1150b5dce617SMatt Jacob 
1151b5dce617SMatt Jacob static void
1152b5dce617SMatt Jacob g_multipath_ctl_getactive(struct gctl_req *req, struct g_class *mp)
1153b5dce617SMatt Jacob {
1154b5dce617SMatt Jacob 	struct sbuf *sb;
1155b5dce617SMatt Jacob 	struct g_geom *gp;
1156b5dce617SMatt Jacob 	struct g_multipath_softc *sc;
1157*0c883cefSAlexander Motin 	struct g_consumer *cp;
1158b5dce617SMatt Jacob 	const char *name;
1159*0c883cefSAlexander Motin 	int empty;
1160b5dce617SMatt Jacob 
1161b5dce617SMatt Jacob 	sb = sbuf_new_auto();
1162b5dce617SMatt Jacob 
1163b5dce617SMatt Jacob 	g_topology_assert();
1164b5dce617SMatt Jacob 	name = gctl_get_asciiparam(req, "arg0");
1165b5dce617SMatt Jacob         if (name == NULL) {
1166b5dce617SMatt Jacob                 gctl_error(req, "No 'arg0' argument");
1167b5dce617SMatt Jacob                 return;
1168b5dce617SMatt Jacob         }
1169b5dce617SMatt Jacob 	gp = g_multipath_find_geom(mp, name);
1170b5dce617SMatt Jacob 	if (gp == NULL) {
1171b5dce617SMatt Jacob 		gctl_error(req, "Device %s is invalid", name);
1172b5dce617SMatt Jacob 		return;
1173b5dce617SMatt Jacob 	}
1174b5dce617SMatt Jacob 	sc = gp->softc;
1175*0c883cefSAlexander Motin 	if (sc->sc_active_active) {
1176*0c883cefSAlexander Motin 		empty = 1;
1177*0c883cefSAlexander Motin 		LIST_FOREACH(cp, &gp->consumer, consumer) {
1178*0c883cefSAlexander Motin 			if (cp->index & MP_BAD)
1179*0c883cefSAlexander Motin 				continue;
1180*0c883cefSAlexander Motin 			if (!empty)
1181*0c883cefSAlexander Motin 				sbuf_cat(sb, " ");
1182*0c883cefSAlexander Motin 			sbuf_cat(sb, cp->provider->name);
1183*0c883cefSAlexander Motin 			empty = 0;
1184*0c883cefSAlexander Motin 		}
1185*0c883cefSAlexander Motin 		if (empty)
1186*0c883cefSAlexander Motin 			sbuf_cat(sb, "none");
1187*0c883cefSAlexander Motin 		sbuf_cat(sb, "\n");
1188*0c883cefSAlexander Motin 	} else if (sc->sc_active && sc->sc_active->provider) {
1189*0c883cefSAlexander Motin 		sbuf_printf(sb, "%s\n", sc->sc_active->provider->name);
1190b5dce617SMatt Jacob 	} else {
1191b5dce617SMatt Jacob 		sbuf_printf(sb, "none\n");
1192b5dce617SMatt Jacob 	}
1193b5dce617SMatt Jacob 	sbuf_finish(sb);
1194b5dce617SMatt Jacob 	gctl_set_param_err(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1195b5dce617SMatt Jacob 	sbuf_delete(sb);
1196b5dce617SMatt Jacob }
1197b5dce617SMatt Jacob 
1198b5dce617SMatt Jacob static void
1199e770bc6bSMatt Jacob g_multipath_config(struct gctl_req *req, struct g_class *mp, const char *verb)
1200e770bc6bSMatt Jacob {
1201e770bc6bSMatt Jacob 	uint32_t *version;
1202e770bc6bSMatt Jacob 	g_topology_assert();
1203e770bc6bSMatt Jacob 	version = gctl_get_paraml(req, "version", sizeof(*version));
1204e770bc6bSMatt Jacob 	if (version == NULL) {
1205e770bc6bSMatt Jacob 		gctl_error(req, "No 'version' argument");
1206e770bc6bSMatt Jacob 	} else if (*version != G_MULTIPATH_VERSION) {
1207e770bc6bSMatt Jacob 		gctl_error(req, "Userland and kernel parts are out of sync");
12082b4969ffSMatt Jacob 	} else if (strcmp(verb, "add") == 0) {
12092b4969ffSMatt Jacob 		g_multipath_ctl_add(req, mp);
1210*0c883cefSAlexander Motin 	} else if (strcmp(verb, "create") == 0) {
1211*0c883cefSAlexander Motin 		g_multipath_ctl_create(req, mp);
1212*0c883cefSAlexander Motin 	} else if (strcmp(verb, "stop") == 0) {
1213*0c883cefSAlexander Motin 		g_multipath_ctl_stop(req, mp);
1214e770bc6bSMatt Jacob 	} else if (strcmp(verb, "destroy") == 0) {
1215e770bc6bSMatt Jacob 		g_multipath_ctl_destroy(req, mp);
1216*0c883cefSAlexander Motin 	} else if (strcmp(verb, "fail") == 0) {
1217*0c883cefSAlexander Motin 		g_multipath_ctl_fail(req, mp, 1);
1218*0c883cefSAlexander Motin 	} else if (strcmp(verb, "restore") == 0) {
1219*0c883cefSAlexander Motin 		g_multipath_ctl_fail(req, mp, 0);
1220*0c883cefSAlexander Motin 	} else if (strcmp(verb, "remove") == 0) {
1221*0c883cefSAlexander Motin 		g_multipath_ctl_remove(req, mp);
1222b5dce617SMatt Jacob 	} else if (strcmp(verb, "rotate") == 0) {
1223b5dce617SMatt Jacob 		g_multipath_ctl_rotate(req, mp);
1224b5dce617SMatt Jacob 	} else if (strcmp(verb, "getactive") == 0) {
1225b5dce617SMatt Jacob 		g_multipath_ctl_getactive(req, mp);
1226e770bc6bSMatt Jacob 	} else {
1227e770bc6bSMatt Jacob 		gctl_error(req, "Unknown verb %s", verb);
1228e770bc6bSMatt Jacob 	}
1229e770bc6bSMatt Jacob }
1230*0c883cefSAlexander Motin 
1231*0c883cefSAlexander Motin static void
1232*0c883cefSAlexander Motin g_multipath_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
1233*0c883cefSAlexander Motin     struct g_consumer *cp, struct g_provider *pp)
1234*0c883cefSAlexander Motin {
1235*0c883cefSAlexander Motin 	struct g_multipath_softc *sc;
1236*0c883cefSAlexander Motin 	int good;
1237*0c883cefSAlexander Motin 
1238*0c883cefSAlexander Motin 	g_topology_assert();
1239*0c883cefSAlexander Motin 
1240*0c883cefSAlexander Motin 	sc = gp->softc;
1241*0c883cefSAlexander Motin 	if (sc == NULL)
1242*0c883cefSAlexander Motin 		return;
1243*0c883cefSAlexander Motin 	if (cp != NULL) {
1244*0c883cefSAlexander Motin 		sbuf_printf(sb, "%s<State>%s</State>", indent,
1245*0c883cefSAlexander Motin 		    (cp->index & MP_NEW) ? "NEW" :
1246*0c883cefSAlexander Motin 		    (cp->index & MP_LOST) ? "LOST" :
1247*0c883cefSAlexander Motin 		    (cp->index & MP_FAIL) ? "FAIL" :
1248*0c883cefSAlexander Motin 		    (sc->sc_active_active || sc->sc_active == cp) ?
1249*0c883cefSAlexander Motin 		     "ACTIVE" : "PASSIVE");
1250*0c883cefSAlexander Motin 	} else {
1251*0c883cefSAlexander Motin 		good = g_multipath_good(gp);
1252*0c883cefSAlexander Motin 		sbuf_printf(sb, "%s<State>%s</State>", indent,
1253*0c883cefSAlexander Motin 		    good == 0 ? "BROKEN" :
1254*0c883cefSAlexander Motin 		    (good != sc->sc_ndisks || sc->sc_ndisks == 1) ?
1255*0c883cefSAlexander Motin 		    "DEGRADED" : "OPTIMAL");
1256*0c883cefSAlexander Motin 	}
1257*0c883cefSAlexander Motin 	if (cp == NULL && pp == NULL) {
1258*0c883cefSAlexander Motin 		sbuf_printf(sb, "%s<UUID>%s</UUID>", indent, sc->sc_uuid);
1259*0c883cefSAlexander Motin 		sbuf_printf(sb, "%s<Mode>Active/%s</Mode>", indent,
1260*0c883cefSAlexander Motin 		    sc->sc_active_active ? "Active" : "Passive");
1261*0c883cefSAlexander Motin 		sbuf_printf(sb, "%s<Type>%s</Type>", indent,
1262*0c883cefSAlexander Motin 		    sc->sc_uuid[0] == 0 ? "MANUAL" : "AUTOMATIC");
1263*0c883cefSAlexander Motin 	}
1264*0c883cefSAlexander Motin }
1265*0c883cefSAlexander Motin 
1266e770bc6bSMatt Jacob DECLARE_GEOM_CLASS(g_multipath_class, g_multipath);
1267