1167050Smjacob/*-
2227464Smav * Copyright (c) 2011 Alexander Motin <mav@FreeBSD.org>
3167050Smjacob * Copyright (c) 2006-2007 Matthew Jacob <mjacob@FreeBSD.org>
4167050Smjacob * All rights reserved.
5167050Smjacob *
6167050Smjacob * Redistribution and use in source and binary forms, with or without
7167050Smjacob * modification, are permitted provided that the following conditions
8167050Smjacob * are met:
9167050Smjacob * 1. Redistributions of source code must retain the above copyright
10167050Smjacob *    notice, this list of conditions and the following disclaimer.
11167050Smjacob * 2. Redistributions in binary form must reproduce the above copyright
12167050Smjacob *    notice, this list of conditions and the following disclaimer in the
13167050Smjacob *    documentation and/or other materials provided with the distribution.
14167050Smjacob *
15167050Smjacob * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16167050Smjacob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17167050Smjacob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18167050Smjacob * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19167050Smjacob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20167050Smjacob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21167050Smjacob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22167050Smjacob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23167050Smjacob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24167050Smjacob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25167050Smjacob * SUCH DAMAGE.
26167050Smjacob */
27167050Smjacob/*
28167050Smjacob * Based upon work by Pawel Jakub Dawidek <pjd@FreeBSD.org> for all of the
29167050Smjacob * fine geom examples, and by Poul Henning Kamp <phk@FreeBSD.org> for GEOM
30167050Smjacob * itself, all of which is most gratefully acknowledged.
31167050Smjacob */
32167050Smjacob
33167050Smjacob#include <sys/cdefs.h>
34167050Smjacob__FBSDID("$FreeBSD$");
35167050Smjacob#include <sys/param.h>
36167050Smjacob#include <sys/systm.h>
37167050Smjacob#include <sys/kernel.h>
38167050Smjacob#include <sys/module.h>
39167050Smjacob#include <sys/lock.h>
40167050Smjacob#include <sys/mutex.h>
41167050Smjacob#include <sys/bio.h>
42223921Sae#include <sys/sbuf.h>
43167050Smjacob#include <sys/sysctl.h>
44167050Smjacob#include <sys/kthread.h>
45167050Smjacob#include <sys/malloc.h>
46167050Smjacob#include <geom/geom.h>
47167050Smjacob#include <geom/multipath/g_multipath.h>
48167050Smjacob
49219029SnetchildFEATURE(geom_multipath, "GEOM multipath support");
50167050Smjacob
51167050SmjacobSYSCTL_DECL(_kern_geom);
52227309Sedstatic SYSCTL_NODE(_kern_geom, OID_AUTO, multipath, CTLFLAG_RW, 0,
53167050Smjacob    "GEOM_MULTIPATH tunables");
54167050Smjacobstatic u_int g_multipath_debug = 0;
55167050SmjacobSYSCTL_UINT(_kern_geom_multipath, OID_AUTO, debug, CTLFLAG_RW,
56167050Smjacob    &g_multipath_debug, 0, "Debug level");
57227464Smavstatic u_int g_multipath_exclusive = 1;
58227464SmavSYSCTL_UINT(_kern_geom_multipath, OID_AUTO, exclusive, CTLFLAG_RW,
59227464Smav    &g_multipath_exclusive, 0, "Exclusively open providers");
60167050Smjacob
61167050Smjacobstatic enum {
62167050Smjacob	GKT_NIL,
63167050Smjacob	GKT_RUN,
64167050Smjacob	GKT_DIE
65167050Smjacob} g_multipath_kt_state;
66167050Smjacobstatic struct bio_queue_head gmtbq;
67167050Smjacobstatic struct mtx gmtbq_mtx;
68167050Smjacob
69167050Smjacobstatic void g_multipath_orphan(struct g_consumer *);
70167050Smjacobstatic void g_multipath_start(struct bio *);
71167050Smjacobstatic void g_multipath_done(struct bio *);
72167050Smjacobstatic void g_multipath_done_error(struct bio *);
73167050Smjacobstatic void g_multipath_kt(void *);
74167050Smjacob
75167050Smjacobstatic int g_multipath_destroy(struct g_geom *);
76167050Smjacobstatic int
77167050Smjacobg_multipath_destroy_geom(struct gctl_req *, struct g_class *, struct g_geom *);
78167050Smjacob
79205847Smjacobstatic struct g_geom *g_multipath_find_geom(struct g_class *, const char *);
80205412Smjacobstatic int g_multipath_rotate(struct g_geom *);
81205412Smjacob
82167050Smjacobstatic g_taste_t g_multipath_taste;
83167050Smjacobstatic g_ctl_req_t g_multipath_config;
84167050Smjacobstatic g_init_t g_multipath_init;
85167050Smjacobstatic g_fini_t g_multipath_fini;
86227464Smavstatic g_dumpconf_t g_multipath_dumpconf;
87167050Smjacob
88167050Smjacobstruct g_class g_multipath_class = {
89167050Smjacob	.name		= G_MULTIPATH_CLASS_NAME,
90167050Smjacob	.version	= G_VERSION,
91167050Smjacob	.ctlreq		= g_multipath_config,
92167050Smjacob	.taste		= g_multipath_taste,
93167050Smjacob	.destroy_geom	= g_multipath_destroy_geom,
94167050Smjacob	.init		= g_multipath_init,
95167050Smjacob	.fini		= g_multipath_fini
96167050Smjacob};
97167050Smjacob
98227464Smav#define	MP_FAIL		0x00000001
99227464Smav#define	MP_LOST		0x00000002
100227464Smav#define	MP_NEW		0x00000004
101227464Smav#define	MP_POSTED	0x00000008
102227464Smav#define	MP_BAD		(MP_FAIL | MP_LOST | MP_NEW)
103227464Smav#define MP_IDLE		0x00000010
104227464Smav#define MP_IDLE_MASK	0xfffffff0
105167050Smjacob
106227464Smavstatic int
107227464Smavg_multipath_good(struct g_geom *gp)
108227464Smav{
109227464Smav	struct g_consumer *cp;
110227464Smav	int n = 0;
111227464Smav
112227464Smav	LIST_FOREACH(cp, &gp->consumer, consumer) {
113227464Smav		if ((cp->index & MP_BAD) == 0)
114227464Smav			n++;
115227464Smav	}
116227464Smav	return (n);
117227464Smav}
118227464Smav
119167050Smjacobstatic void
120227464Smavg_multipath_fault(struct g_consumer *cp, int cause)
121227464Smav{
122227464Smav	struct g_multipath_softc *sc;
123227464Smav	struct g_consumer *lcp;
124227464Smav	struct g_geom *gp;
125227464Smav
126227464Smav	gp = cp->geom;
127227464Smav	sc = gp->softc;
128227464Smav	cp->index |= cause;
129227464Smav	if (g_multipath_good(gp) == 0 && sc->sc_ndisks > 0) {
130227464Smav		LIST_FOREACH(lcp, &gp->consumer, consumer) {
131227464Smav			if (lcp->provider == NULL ||
132227464Smav			    (lcp->index & (MP_LOST | MP_NEW)))
133227464Smav				continue;
134227464Smav			if (sc->sc_ndisks > 1 && lcp == cp)
135227464Smav				continue;
136227464Smav			printf("GEOM_MULTIPATH: "
137227464Smav			    "all paths in %s were marked FAIL, restore %s\n",
138227464Smav			    sc->sc_name, lcp->provider->name);
139227464Smav			lcp->index &= ~MP_FAIL;
140227464Smav		}
141227464Smav	}
142227464Smav	if (cp != sc->sc_active)
143227464Smav		return;
144227464Smav	sc->sc_active = NULL;
145227464Smav	LIST_FOREACH(lcp, &gp->consumer, consumer) {
146227464Smav		if ((lcp->index & MP_BAD) == 0) {
147227464Smav			sc->sc_active = lcp;
148227464Smav			break;
149227464Smav		}
150227464Smav	}
151227464Smav	if (sc->sc_active == NULL) {
152227464Smav		printf("GEOM_MULTIPATH: out of providers for %s\n",
153227464Smav		    sc->sc_name);
154234415Smav	} else if (sc->sc_active_active != 1) {
155227464Smav		printf("GEOM_MULTIPATH: %s is now active path in %s\n",
156227464Smav		    sc->sc_active->provider->name, sc->sc_name);
157227464Smav	}
158227464Smav}
159227464Smav
160227464Smavstatic struct g_consumer *
161234415Smavg_multipath_choose(struct g_geom *gp, struct bio *bp)
162227464Smav{
163227464Smav	struct g_multipath_softc *sc;
164227464Smav	struct g_consumer *best, *cp;
165227464Smav
166227464Smav	sc = gp->softc;
167234415Smav	if (sc->sc_active_active == 0 ||
168234415Smav	    (sc->sc_active_active == 2 && bp->bio_cmd != BIO_READ))
169227464Smav		return (sc->sc_active);
170227464Smav	best = NULL;
171227464Smav	LIST_FOREACH(cp, &gp->consumer, consumer) {
172227464Smav		if (cp->index & MP_BAD)
173227464Smav			continue;
174227464Smav		cp->index += MP_IDLE;
175227464Smav		if (best == NULL || cp->private < best->private ||
176227464Smav		    (cp->private == best->private && cp->index > best->index))
177227464Smav			best = cp;
178227464Smav	}
179227464Smav	if (best != NULL)
180227464Smav		best->index &= ~MP_IDLE_MASK;
181227464Smav	return (best);
182227464Smav}
183227464Smav
184227464Smavstatic void
185167050Smjacobg_mpd(void *arg, int flags __unused)
186167050Smjacob{
187227464Smav	struct g_geom *gp;
188227464Smav	struct g_multipath_softc *sc;
189167050Smjacob	struct g_consumer *cp;
190227464Smav	int w;
191167050Smjacob
192167050Smjacob	g_topology_assert();
193167050Smjacob	cp = arg;
194227464Smav	gp = cp->geom;
195227464Smav	if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) {
196227464Smav		w = cp->acw;
197167050Smjacob		g_access(cp, -cp->acr, -cp->acw, -cp->ace);
198227464Smav		if (w > 0 && cp->provider != NULL &&
199227464Smav		    (cp->provider->geom->flags & G_GEOM_WITHER) == 0) {
200227464Smav			g_post_event(g_mpd, cp, M_WAITOK, NULL);
201227464Smav			return;
202227464Smav		}
203227464Smav	}
204227464Smav	sc = gp->softc;
205227464Smav	mtx_lock(&sc->sc_mtx);
206167050Smjacob	if (cp->provider) {
207167050Smjacob		printf("GEOM_MULTIPATH: %s removed from %s\n",
208227464Smav		    cp->provider->name, gp->name);
209167050Smjacob		g_detach(cp);
210167050Smjacob	}
211167050Smjacob	g_destroy_consumer(cp);
212227464Smav	mtx_unlock(&sc->sc_mtx);
213227464Smav	if (LIST_EMPTY(&gp->consumer))
214227464Smav		g_multipath_destroy(gp);
215167050Smjacob}
216167050Smjacob
217167050Smjacobstatic void
218167050Smjacobg_multipath_orphan(struct g_consumer *cp)
219167050Smjacob{
220227464Smav	struct g_multipath_softc *sc;
221227464Smav	uintptr_t *cnt;
222227464Smav
223227464Smav	g_topology_assert();
224227464Smav	printf("GEOM_MULTIPATH: %s in %s was disconnected\n",
225227464Smav	    cp->provider->name, cp->geom->name);
226227464Smav	sc = cp->geom->softc;
227227464Smav	cnt = (uintptr_t *)&cp->private;
228227464Smav	mtx_lock(&sc->sc_mtx);
229227464Smav	sc->sc_ndisks--;
230227464Smav	g_multipath_fault(cp, MP_LOST);
231227464Smav	if (*cnt == 0 && (cp->index & MP_POSTED) == 0) {
232167050Smjacob		cp->index |= MP_POSTED;
233227464Smav		mtx_unlock(&sc->sc_mtx);
234167050Smjacob		g_mpd(cp, 0);
235227464Smav	} else
236227464Smav		mtx_unlock(&sc->sc_mtx);
237167050Smjacob}
238167050Smjacob
239167050Smjacobstatic void
240167050Smjacobg_multipath_start(struct bio *bp)
241167050Smjacob{
242167050Smjacob	struct g_multipath_softc *sc;
243167050Smjacob	struct g_geom *gp;
244167050Smjacob	struct g_consumer *cp;
245167050Smjacob	struct bio *cbp;
246227464Smav	uintptr_t *cnt;
247167050Smjacob
248167050Smjacob	gp = bp->bio_to->geom;
249167050Smjacob	sc = gp->softc;
250167050Smjacob	KASSERT(sc != NULL, ("NULL sc"));
251167050Smjacob	cbp = g_clone_bio(bp);
252167050Smjacob	if (cbp == NULL) {
253167050Smjacob		g_io_deliver(bp, ENOMEM);
254167050Smjacob		return;
255167050Smjacob	}
256227464Smav	mtx_lock(&sc->sc_mtx);
257234415Smav	cp = g_multipath_choose(gp, bp);
258227464Smav	if (cp == NULL) {
259227464Smav		mtx_unlock(&sc->sc_mtx);
260227464Smav		g_destroy_bio(cbp);
261227464Smav		g_io_deliver(bp, ENXIO);
262227464Smav		return;
263227464Smav	}
264227464Smav	if ((uintptr_t)bp->bio_driver1 < sc->sc_ndisks)
265227464Smav		bp->bio_driver1 = (void *)(uintptr_t)sc->sc_ndisks;
266227464Smav	cnt = (uintptr_t *)&cp->private;
267227464Smav	(*cnt)++;
268227464Smav	mtx_unlock(&sc->sc_mtx);
269167050Smjacob	cbp->bio_done = g_multipath_done;
270167050Smjacob	g_io_request(cbp, cp);
271167050Smjacob}
272167050Smjacob
273167050Smjacobstatic void
274167050Smjacobg_multipath_done(struct bio *bp)
275167050Smjacob{
276227464Smav	struct g_multipath_softc *sc;
277227464Smav	struct g_consumer *cp;
278227464Smav	uintptr_t *cnt;
279227464Smav
280167050Smjacob	if (bp->bio_error == ENXIO || bp->bio_error == EIO) {
281167050Smjacob		mtx_lock(&gmtbq_mtx);
282167050Smjacob		bioq_insert_tail(&gmtbq, bp);
283227464Smav		mtx_unlock(&gmtbq_mtx);
284167050Smjacob		wakeup(&g_multipath_kt_state);
285167050Smjacob	} else {
286227464Smav		cp = bp->bio_from;
287227464Smav		sc = cp->geom->softc;
288227464Smav		cnt = (uintptr_t *)&cp->private;
289227464Smav		mtx_lock(&sc->sc_mtx);
290227464Smav		(*cnt)--;
291227464Smav		if (*cnt == 0 && (cp->index & MP_LOST)) {
292227464Smav			cp->index |= MP_POSTED;
293227464Smav			mtx_unlock(&sc->sc_mtx);
294227464Smav			g_post_event(g_mpd, cp, M_WAITOK, NULL);
295227464Smav		} else
296227464Smav			mtx_unlock(&sc->sc_mtx);
297167050Smjacob		g_std_done(bp);
298167050Smjacob	}
299167050Smjacob}
300167050Smjacob
301167050Smjacobstatic void
302167050Smjacobg_multipath_done_error(struct bio *bp)
303167050Smjacob{
304167050Smjacob	struct bio *pbp;
305167050Smjacob	struct g_geom *gp;
306167050Smjacob	struct g_multipath_softc *sc;
307167050Smjacob	struct g_consumer *cp;
308167050Smjacob	struct g_provider *pp;
309227464Smav	uintptr_t *cnt;
310167050Smjacob
311167050Smjacob	/*
312167050Smjacob	 * If we had a failure, we have to check first to see
313167050Smjacob	 * whether the consumer it failed on was the currently
314167050Smjacob	 * active consumer (i.e., this is the first in perhaps
315167050Smjacob	 * a number of failures). If so, we then switch consumers
316167050Smjacob	 * to the next available consumer.
317167050Smjacob	 */
318167050Smjacob
319167050Smjacob	pbp = bp->bio_parent;
320167050Smjacob	gp = pbp->bio_to->geom;
321167050Smjacob	sc = gp->softc;
322167050Smjacob	cp = bp->bio_from;
323167050Smjacob	pp = cp->provider;
324227464Smav	cnt = (uintptr_t *)&cp->private;
325167050Smjacob
326227464Smav	mtx_lock(&sc->sc_mtx);
327234415Smav	if ((cp->index & MP_FAIL) == 0) {
328234415Smav		printf("GEOM_MULTIPATH: Error %d, %s in %s marked FAIL\n",
329234415Smav		    bp->bio_error, pp->name, sc->sc_name);
330234415Smav		g_multipath_fault(cp, MP_FAIL);
331234415Smav	}
332227464Smav	(*cnt)--;
333227464Smav	if (*cnt == 0 && (cp->index & (MP_LOST | MP_POSTED)) == MP_LOST) {
334167050Smjacob		cp->index |= MP_POSTED;
335227464Smav		mtx_unlock(&sc->sc_mtx);
336227464Smav		g_post_event(g_mpd, cp, M_WAITOK, NULL);
337227464Smav	} else
338227464Smav		mtx_unlock(&sc->sc_mtx);
339167050Smjacob
340167050Smjacob	/*
341167050Smjacob	 * If we can fruitfully restart the I/O, do so.
342167050Smjacob	 */
343227464Smav	if (pbp->bio_children < (uintptr_t)pbp->bio_driver1) {
344227464Smav		pbp->bio_inbed++;
345167050Smjacob		g_destroy_bio(bp);
346167050Smjacob		g_multipath_start(pbp);
347167050Smjacob	} else {
348167050Smjacob		g_std_done(bp);
349167050Smjacob	}
350167050Smjacob}
351167050Smjacob
352167050Smjacobstatic void
353167050Smjacobg_multipath_kt(void *arg)
354167050Smjacob{
355204071Spjd
356167050Smjacob	g_multipath_kt_state = GKT_RUN;
357167050Smjacob	mtx_lock(&gmtbq_mtx);
358167050Smjacob	while (g_multipath_kt_state == GKT_RUN) {
359167050Smjacob		for (;;) {
360167050Smjacob			struct bio *bp;
361204071Spjd
362167050Smjacob			bp = bioq_takefirst(&gmtbq);
363204071Spjd			if (bp == NULL)
364167050Smjacob				break;
365167050Smjacob			mtx_unlock(&gmtbq_mtx);
366167050Smjacob			g_multipath_done_error(bp);
367167050Smjacob			mtx_lock(&gmtbq_mtx);
368167050Smjacob		}
369234415Smav		if (g_multipath_kt_state != GKT_RUN)
370234415Smav			break;
371167050Smjacob		msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO,
372234415Smav		    "gkt:wait", 0);
373167050Smjacob	}
374167050Smjacob	mtx_unlock(&gmtbq_mtx);
375167050Smjacob	wakeup(&g_multipath_kt_state);
376172836Sjulian	kproc_exit(0);
377167050Smjacob}
378167050Smjacob
379167050Smjacob
380167050Smjacobstatic int
381167050Smjacobg_multipath_access(struct g_provider *pp, int dr, int dw, int de)
382167050Smjacob{
383167050Smjacob	struct g_geom *gp;
384167050Smjacob	struct g_consumer *cp, *badcp = NULL;
385227464Smav	struct g_multipath_softc *sc;
386167050Smjacob	int error;
387167050Smjacob
388167050Smjacob	gp = pp->geom;
389167050Smjacob
390167050Smjacob	LIST_FOREACH(cp, &gp->consumer, consumer) {
391167050Smjacob		error = g_access(cp, dr, dw, de);
392167050Smjacob		if (error) {
393167050Smjacob			badcp = cp;
394167050Smjacob			goto fail;
395167050Smjacob		}
396167050Smjacob	}
397227464Smav	sc = gp->softc;
398227464Smav	sc->sc_opened += dr + dw + de;
399227464Smav	if (sc->sc_stopping && sc->sc_opened == 0)
400227464Smav		g_multipath_destroy(gp);
401167050Smjacob	return (0);
402167050Smjacob
403167050Smjacobfail:
404167050Smjacob	LIST_FOREACH(cp, &gp->consumer, consumer) {
405204071Spjd		if (cp == badcp)
406167050Smjacob			break;
407167050Smjacob		(void) g_access(cp, -dr, -dw, -de);
408167050Smjacob	}
409167050Smjacob	return (error);
410167050Smjacob}
411167050Smjacob
412167050Smjacobstatic struct g_geom *
413167050Smjacobg_multipath_create(struct g_class *mp, struct g_multipath_metadata *md)
414167050Smjacob{
415167050Smjacob	struct g_multipath_softc *sc;
416167050Smjacob	struct g_geom *gp;
417167050Smjacob	struct g_provider *pp;
418167050Smjacob
419167050Smjacob	g_topology_assert();
420167050Smjacob
421167050Smjacob	LIST_FOREACH(gp, &mp->geom, geom) {
422227464Smav		sc = gp->softc;
423227464Smav		if (sc == NULL || sc->sc_stopping)
424227464Smav			continue;
425167050Smjacob		if (strcmp(gp->name, md->md_name) == 0) {
426167050Smjacob			printf("GEOM_MULTIPATH: name %s already exists\n",
427167050Smjacob			    md->md_name);
428167050Smjacob			return (NULL);
429167050Smjacob		}
430167050Smjacob	}
431167050Smjacob
432243333Sjh	gp = g_new_geomf(mp, "%s", md->md_name);
433167050Smjacob	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
434227464Smav	mtx_init(&sc->sc_mtx, "multipath", NULL, MTX_DEF);
435227464Smav	memcpy(sc->sc_uuid, md->md_uuid, sizeof (sc->sc_uuid));
436227464Smav	memcpy(sc->sc_name, md->md_name, sizeof (sc->sc_name));
437227464Smav	sc->sc_active_active = md->md_active_active;
438167050Smjacob	gp->softc = sc;
439167050Smjacob	gp->start = g_multipath_start;
440167050Smjacob	gp->orphan = g_multipath_orphan;
441167050Smjacob	gp->access = g_multipath_access;
442227464Smav	gp->dumpconf = g_multipath_dumpconf;
443167050Smjacob
444167050Smjacob	pp = g_new_providerf(gp, "multipath/%s", md->md_name);
445227464Smav	if (md->md_size != 0) {
446227464Smav		pp->mediasize = md->md_size -
447227464Smav		    ((md->md_uuid[0] != 0) ? md->md_sectorsize : 0);
448227464Smav		pp->sectorsize = md->md_sectorsize;
449227464Smav	}
450227464Smav	sc->sc_pp = pp;
451167050Smjacob	g_error_provider(pp, 0);
452227464Smav	printf("GEOM_MULTIPATH: %s created\n", gp->name);
453167050Smjacob	return (gp);
454167050Smjacob}
455167050Smjacob
456167050Smjacobstatic int
457167050Smjacobg_multipath_add_disk(struct g_geom *gp, struct g_provider *pp)
458167050Smjacob{
459167050Smjacob	struct g_multipath_softc *sc;
460167050Smjacob	struct g_consumer *cp, *nxtcp;
461227464Smav	int error, acr, acw, ace;
462167050Smjacob
463167050Smjacob	g_topology_assert();
464167050Smjacob
465167050Smjacob	sc = gp->softc;
466167050Smjacob	KASSERT(sc, ("no softc"));
467167050Smjacob
468167050Smjacob	/*
469167050Smjacob	 * Make sure that the passed provider isn't already attached
470167050Smjacob	 */
471167050Smjacob	LIST_FOREACH(cp, &gp->consumer, consumer) {
472204071Spjd		if (cp->provider == pp)
473167050Smjacob			break;
474167050Smjacob	}
475167050Smjacob	if (cp) {
476167050Smjacob		printf("GEOM_MULTIPATH: provider %s already attached to %s\n",
477167050Smjacob		    pp->name, gp->name);
478167050Smjacob		return (EEXIST);
479167050Smjacob	}
480167050Smjacob	nxtcp = LIST_FIRST(&gp->consumer);
481167050Smjacob	cp = g_new_consumer(gp);
482227464Smav	cp->private = NULL;
483227464Smav	cp->index = MP_NEW;
484167050Smjacob	error = g_attach(cp, pp);
485167050Smjacob	if (error != 0) {
486167050Smjacob		printf("GEOM_MULTIPATH: cannot attach %s to %s",
487167050Smjacob		    pp->name, sc->sc_name);
488167050Smjacob		g_destroy_consumer(cp);
489167050Smjacob		return (error);
490167050Smjacob	}
491167050Smjacob
492167050Smjacob	/*
493167050Smjacob	 * Set access permissions on new consumer to match other consumers
494167050Smjacob	 */
495227464Smav	if (sc->sc_pp) {
496227464Smav		acr = sc->sc_pp->acr;
497227464Smav		acw = sc->sc_pp->acw;
498227464Smav		ace = sc->sc_pp->ace;
499227464Smav	} else
500227464Smav		acr = acw = ace = 0;
501227464Smav	if (g_multipath_exclusive) {
502227464Smav		acr++;
503227464Smav		acw++;
504227464Smav		ace++;
505167050Smjacob	}
506227464Smav	error = g_access(cp, acr, acw, ace);
507227464Smav	if (error) {
508227464Smav		printf("GEOM_MULTIPATH: cannot set access in "
509227464Smav		    "attaching %s to %s (%d)\n",
510227464Smav		    pp->name, sc->sc_name, error);
511227464Smav		g_detach(cp);
512227464Smav		g_destroy_consumer(cp);
513227464Smav		return (error);
514167050Smjacob	}
515227464Smav	if (sc->sc_pp != NULL && sc->sc_pp->mediasize == 0) {
516227464Smav		sc->sc_pp->mediasize = pp->mediasize -
517227464Smav		    ((sc->sc_uuid[0] != 0) ? pp->sectorsize : 0);
518227464Smav		sc->sc_pp->sectorsize = pp->sectorsize;
519227464Smav	}
520227464Smav	if (sc->sc_pp != NULL &&
521227464Smav	    sc->sc_pp->stripesize == 0 && sc->sc_pp->stripeoffset == 0) {
522227464Smav		sc->sc_pp->stripesize = pp->stripesize;
523227464Smav		sc->sc_pp->stripeoffset = pp->stripeoffset;
524227464Smav	}
525248696Smav	if (sc->sc_pp != NULL)
526248696Smav		sc->sc_pp->flags |= pp->flags & G_PF_ACCEPT_UNMAPPED;
527227464Smav	mtx_lock(&sc->sc_mtx);
528227464Smav	cp->index = 0;
529227464Smav	sc->sc_ndisks++;
530227464Smav	mtx_unlock(&sc->sc_mtx);
531227464Smav	printf("GEOM_MULTIPATH: %s added to %s\n",
532227464Smav	    pp->name, sc->sc_name);
533227464Smav	if (sc->sc_active == NULL) {
534227464Smav		sc->sc_active = cp;
535234415Smav		if (sc->sc_active_active != 1)
536227464Smav			printf("GEOM_MULTIPATH: %s is now active path in %s\n",
537227464Smav			    pp->name, sc->sc_name);
538227464Smav	}
539167050Smjacob	return (0);
540167050Smjacob}
541167050Smjacob
542167050Smjacobstatic int
543167050Smjacobg_multipath_destroy(struct g_geom *gp)
544167050Smjacob{
545227464Smav	struct g_multipath_softc *sc;
546227464Smav	struct g_consumer *cp, *cp1;
547167050Smjacob
548167050Smjacob	g_topology_assert();
549204071Spjd	if (gp->softc == NULL)
550167050Smjacob		return (ENXIO);
551227464Smav	sc = gp->softc;
552227464Smav	if (!sc->sc_stopping) {
553227464Smav		printf("GEOM_MULTIPATH: destroying %s\n", gp->name);
554227464Smav		sc->sc_stopping = 1;
555227464Smav	}
556227464Smav	if (sc->sc_opened != 0) {
557227464Smav		if (sc->sc_pp != NULL) {
558227464Smav			g_wither_provider(sc->sc_pp, ENXIO);
559227464Smav			sc->sc_pp = NULL;
560227464Smav		}
561227464Smav		return (EINPROGRESS);
562227464Smav	}
563227464Smav	LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) {
564227464Smav		mtx_lock(&sc->sc_mtx);
565227464Smav		if ((cp->index & MP_POSTED) == 0) {
566227464Smav			cp->index |= MP_POSTED;
567227464Smav			mtx_unlock(&sc->sc_mtx);
568227464Smav			g_mpd(cp, 0);
569227464Smav			if (cp1 == NULL)
570227464Smav				return(0);	/* Recursion happened. */
571227464Smav		} else
572227464Smav			mtx_unlock(&sc->sc_mtx);
573227464Smav	}
574227464Smav	if (!LIST_EMPTY(&gp->consumer))
575227464Smav		return (EINPROGRESS);
576227464Smav	mtx_destroy(&sc->sc_mtx);
577167050Smjacob	g_free(gp->softc);
578167050Smjacob	gp->softc = NULL;
579227464Smav	printf("GEOM_MULTIPATH: %s destroyed\n", gp->name);
580167050Smjacob	g_wither_geom(gp, ENXIO);
581167050Smjacob	return (0);
582167050Smjacob}
583167050Smjacob
584167050Smjacobstatic int
585167050Smjacobg_multipath_destroy_geom(struct gctl_req *req, struct g_class *mp,
586167050Smjacob    struct g_geom *gp)
587167050Smjacob{
588204071Spjd
589167050Smjacob	return (g_multipath_destroy(gp));
590167050Smjacob}
591167050Smjacob
592205412Smjacobstatic int
593205412Smjacobg_multipath_rotate(struct g_geom *gp)
594205412Smjacob{
595239673Sthomas	struct g_consumer *lcp, *first_good_cp = NULL;
596205412Smjacob	struct g_multipath_softc *sc = gp->softc;
597239673Sthomas	int active_cp_seen = 0;
598205412Smjacob
599205412Smjacob	g_topology_assert();
600205412Smjacob	if (sc == NULL)
601205412Smjacob		return (ENXIO);
602205412Smjacob	LIST_FOREACH(lcp, &gp->consumer, consumer) {
603205412Smjacob		if ((lcp->index & MP_BAD) == 0) {
604239673Sthomas			if (first_good_cp == NULL)
605239673Sthomas				first_good_cp = lcp;
606239673Sthomas			if (active_cp_seen)
607205412Smjacob				break;
608205412Smjacob		}
609239673Sthomas		if (sc->sc_active == lcp)
610239673Sthomas			active_cp_seen = 1;
611205412Smjacob	}
612239673Sthomas	if (lcp == NULL)
613239673Sthomas		lcp = first_good_cp;
614239673Sthomas	if (lcp && lcp != sc->sc_active) {
615227464Smav		sc->sc_active = lcp;
616234415Smav		if (sc->sc_active_active != 1)
617227464Smav			printf("GEOM_MULTIPATH: %s is now active path in %s\n",
618227464Smav			    lcp->provider->name, sc->sc_name);
619205412Smjacob	}
620205412Smjacob	return (0);
621205412Smjacob}
622205412Smjacob
623167050Smjacobstatic void
624167050Smjacobg_multipath_init(struct g_class *mp)
625167050Smjacob{
626167050Smjacob	bioq_init(&gmtbq);
627167050Smjacob	mtx_init(&gmtbq_mtx, "gmtbq", NULL, MTX_DEF);
628234415Smav	kproc_create(g_multipath_kt, mp, NULL, 0, 0, "g_mp_kt");
629167050Smjacob}
630167050Smjacob
631167050Smjacobstatic void
632167050Smjacobg_multipath_fini(struct g_class *mp)
633167050Smjacob{
634167050Smjacob	if (g_multipath_kt_state == GKT_RUN) {
635167050Smjacob		mtx_lock(&gmtbq_mtx);
636167050Smjacob		g_multipath_kt_state = GKT_DIE;
637167050Smjacob		wakeup(&g_multipath_kt_state);
638167050Smjacob		msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO,
639167050Smjacob		    "gmp:fini", 0);
640167050Smjacob		mtx_unlock(&gmtbq_mtx);
641167050Smjacob	}
642167050Smjacob}
643167050Smjacob
644167050Smjacobstatic int
645167050Smjacobg_multipath_read_metadata(struct g_consumer *cp,
646167050Smjacob    struct g_multipath_metadata *md)
647167050Smjacob{
648167050Smjacob	struct g_provider *pp;
649167050Smjacob	u_char *buf;
650167050Smjacob	int error;
651167050Smjacob
652167050Smjacob	g_topology_assert();
653167050Smjacob	error = g_access(cp, 1, 0, 0);
654204071Spjd	if (error != 0)
655167050Smjacob		return (error);
656167050Smjacob	pp = cp->provider;
657167050Smjacob	g_topology_unlock();
658167050Smjacob	buf = g_read_data(cp, pp->mediasize - pp->sectorsize,
659167050Smjacob	    pp->sectorsize, &error);
660167050Smjacob	g_topology_lock();
661167050Smjacob	g_access(cp, -1, 0, 0);
662204071Spjd	if (buf == NULL)
663167050Smjacob		return (error);
664167050Smjacob	multipath_metadata_decode(buf, md);
665167050Smjacob	g_free(buf);
666167050Smjacob	return (0);
667167050Smjacob}
668167050Smjacob
669167050Smjacobstatic struct g_geom *
670167050Smjacobg_multipath_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
671167050Smjacob{
672167050Smjacob	struct g_multipath_metadata md;
673167050Smjacob	struct g_multipath_softc *sc;
674167050Smjacob	struct g_consumer *cp;
675167050Smjacob	struct g_geom *gp, *gp1;
676167050Smjacob	int error, isnew;
677167050Smjacob
678167050Smjacob	g_topology_assert();
679167050Smjacob
680167050Smjacob	gp = g_new_geomf(mp, "multipath:taste");
681167050Smjacob	gp->start = g_multipath_start;
682167050Smjacob	gp->access = g_multipath_access;
683167050Smjacob	gp->orphan = g_multipath_orphan;
684167050Smjacob	cp = g_new_consumer(gp);
685167050Smjacob	g_attach(cp, pp);
686167050Smjacob	error = g_multipath_read_metadata(cp, &md);
687167050Smjacob	g_detach(cp);
688167050Smjacob	g_destroy_consumer(cp);
689167050Smjacob	g_destroy_geom(gp);
690204071Spjd	if (error != 0)
691167050Smjacob		return (NULL);
692167050Smjacob	gp = NULL;
693167050Smjacob
694167050Smjacob	if (strcmp(md.md_magic, G_MULTIPATH_MAGIC) != 0) {
695204071Spjd		if (g_multipath_debug)
696167050Smjacob			printf("%s is not MULTIPATH\n", pp->name);
697167050Smjacob		return (NULL);
698167050Smjacob	}
699167050Smjacob	if (md.md_version != G_MULTIPATH_VERSION) {
700167050Smjacob		printf("%s has version %d multipath id- this module is version "
701167050Smjacob		    " %d: rejecting\n", pp->name, md.md_version,
702167050Smjacob		    G_MULTIPATH_VERSION);
703167050Smjacob		return (NULL);
704167050Smjacob	}
705227464Smav	if (md.md_size != 0 && md.md_size != pp->mediasize)
706227464Smav		return (NULL);
707227464Smav	if (md.md_sectorsize != 0 && md.md_sectorsize != pp->sectorsize)
708227464Smav		return (NULL);
709204071Spjd	if (g_multipath_debug)
710167050Smjacob		printf("MULTIPATH: %s/%s\n", md.md_name, md.md_uuid);
711167050Smjacob
712167050Smjacob	/*
713167050Smjacob	 * Let's check if such a device already is present. We check against
714167050Smjacob	 * uuid alone first because that's the true distinguishor. If that
715167050Smjacob	 * passes, then we check for name conflicts. If there are conflicts,
716167050Smjacob	 * modify the name.
717167050Smjacob	 *
718167050Smjacob	 * The whole purpose of this is to solve the problem that people don't
719167050Smjacob	 * pick good unique names, but good unique names (like uuids) are a
720167050Smjacob	 * pain to use. So, we allow people to build GEOMs with friendly names
721167050Smjacob	 * and uuids, and modify the names in case there's a collision.
722167050Smjacob	 */
723167050Smjacob	sc = NULL;
724167050Smjacob	LIST_FOREACH(gp, &mp->geom, geom) {
725167050Smjacob		sc = gp->softc;
726227464Smav		if (sc == NULL || sc->sc_stopping)
727167050Smjacob			continue;
728204071Spjd		if (strncmp(md.md_uuid, sc->sc_uuid, sizeof(md.md_uuid)) == 0)
729167050Smjacob			break;
730167050Smjacob	}
731167050Smjacob
732167050Smjacob	LIST_FOREACH(gp1, &mp->geom, geom) {
733204071Spjd		if (gp1 == gp)
734167050Smjacob			continue;
735167050Smjacob		sc = gp1->softc;
736227464Smav		if (sc == NULL || sc->sc_stopping)
737167050Smjacob			continue;
738204071Spjd		if (strncmp(md.md_name, sc->sc_name, sizeof(md.md_name)) == 0)
739167050Smjacob			break;
740167050Smjacob	}
741167050Smjacob
742167050Smjacob	/*
743167050Smjacob	 * If gp is NULL, we had no extant MULTIPATH geom with this uuid.
744167050Smjacob	 *
745167050Smjacob	 * If gp1 is *not* NULL, that means we have a MULTIPATH geom extant
746167050Smjacob	 * with the same name (but a different UUID).
747167050Smjacob	 *
748167050Smjacob	 * If gp is NULL, then modify the name with a random number and
749167050Smjacob  	 * complain, but allow the creation of the geom to continue.
750167050Smjacob	 *
751167050Smjacob	 * If gp is *not* NULL, just use the geom's name as we're attaching
752167050Smjacob	 * this disk to the (previously generated) name.
753167050Smjacob	 */
754167050Smjacob
755167050Smjacob	if (gp1) {
756167050Smjacob		sc = gp1->softc;
757167050Smjacob		if (gp == NULL) {
758167050Smjacob			char buf[16];
759167050Smjacob			u_long rand = random();
760167050Smjacob
761167050Smjacob			snprintf(buf, sizeof (buf), "%s-%lu", md.md_name, rand);
762167050Smjacob			printf("GEOM_MULTIPATH: geom %s/%s exists already\n",
763167050Smjacob			    sc->sc_name, sc->sc_uuid);
764167050Smjacob			printf("GEOM_MULTIPATH: %s will be (temporarily) %s\n",
765167050Smjacob			    md.md_uuid, buf);
766204071Spjd			strlcpy(md.md_name, buf, sizeof(md.md_name));
767167050Smjacob		} else {
768204071Spjd			strlcpy(md.md_name, sc->sc_name, sizeof(md.md_name));
769167050Smjacob		}
770167050Smjacob	}
771167050Smjacob
772167050Smjacob	if (gp == NULL) {
773167050Smjacob		gp = g_multipath_create(mp, &md);
774167050Smjacob		if (gp == NULL) {
775167050Smjacob			printf("GEOM_MULTIPATH: cannot create geom %s/%s\n",
776167050Smjacob			    md.md_name, md.md_uuid);
777167050Smjacob			return (NULL);
778167050Smjacob		}
779167050Smjacob		isnew = 1;
780167050Smjacob	} else {
781167050Smjacob		isnew = 0;
782167050Smjacob	}
783167050Smjacob
784167050Smjacob	sc = gp->softc;
785167050Smjacob	KASSERT(sc != NULL, ("sc is NULL"));
786167050Smjacob	error = g_multipath_add_disk(gp, pp);
787167050Smjacob	if (error != 0) {
788204071Spjd		if (isnew)
789167050Smjacob			g_multipath_destroy(gp);
790167050Smjacob		return (NULL);
791167050Smjacob	}
792167050Smjacob	return (gp);
793167050Smjacob}
794167050Smjacob
795167050Smjacobstatic void
796227464Smavg_multipath_ctl_add_name(struct gctl_req *req, struct g_class *mp,
797227464Smav    const char *name)
798167050Smjacob{
799227464Smav	struct g_multipath_softc *sc;
800167050Smjacob	struct g_geom *gp;
801205847Smjacob	struct g_consumer *cp;
802227464Smav	struct g_provider *pp;
803227464Smav	const char *mpname;
804167050Smjacob	static const char devpf[6] = "/dev/";
805167050Smjacob
806167050Smjacob	g_topology_assert();
807167050Smjacob
808167050Smjacob	mpname = gctl_get_asciiparam(req, "arg0");
809167050Smjacob        if (mpname == NULL) {
810167050Smjacob                gctl_error(req, "No 'arg0' argument");
811167050Smjacob                return;
812167050Smjacob        }
813205847Smjacob	gp = g_multipath_find_geom(mp, mpname);
814205847Smjacob	if (gp == NULL) {
815205847Smjacob		gctl_error(req, "Device %s is invalid", mpname);
816167050Smjacob		return;
817167050Smjacob	}
818227464Smav	sc = gp->softc;
819167050Smjacob
820204071Spjd	if (strncmp(name, devpf, 5) == 0)
821167050Smjacob		name += 5;
822205847Smjacob	pp = g_provider_by_name(name);
823205847Smjacob	if (pp == NULL) {
824167050Smjacob		gctl_error(req, "Provider %s is invalid", name);
825167050Smjacob		return;
826167050Smjacob	}
827167050Smjacob
828167050Smjacob	/*
829227464Smav	 * Check to make sure parameters match.
830167050Smjacob	 */
831227464Smav	LIST_FOREACH(cp, &gp->consumer, consumer) {
832227464Smav		if (cp->provider == pp) {
833227464Smav			gctl_error(req, "provider %s is already there",
834227464Smav			    pp->name);
835205847Smjacob			return;
836205847Smjacob		}
837167050Smjacob	}
838227464Smav	if (sc->sc_pp != NULL && sc->sc_pp->mediasize != 0 &&
839227464Smav	    sc->sc_pp->mediasize + (sc->sc_uuid[0] != 0 ? pp->sectorsize : 0)
840227464Smav	     != pp->mediasize) {
841227464Smav		gctl_error(req, "Providers size mismatch %jd != %jd",
842227464Smav		    (intmax_t) sc->sc_pp->mediasize +
843227464Smav			(sc->sc_uuid[0] != 0 ? pp->sectorsize : 0),
844227464Smav		    (intmax_t) pp->mediasize);
845227464Smav		return;
846227464Smav	}
847227464Smav	if (sc->sc_pp != NULL && sc->sc_pp->sectorsize != 0 &&
848227464Smav	    sc->sc_pp->sectorsize != pp->sectorsize) {
849227464Smav		gctl_error(req, "Providers sectorsize mismatch %u != %u",
850227464Smav		    sc->sc_pp->sectorsize, pp->sectorsize);
851227464Smav		return;
852227464Smav	}
853167050Smjacob
854167050Smjacob	/*
855205847Smjacob	 * Now add....
856167050Smjacob	 */
857205847Smjacob	(void) g_multipath_add_disk(gp, pp);
858167050Smjacob}
859167050Smjacob
860227464Smavstatic void
861239012Sthomasg_multipath_ctl_prefer(struct gctl_req *req, struct g_class *mp)
862239012Sthomas{
863239012Sthomas	struct g_geom *gp;
864239012Sthomas	struct g_multipath_softc *sc;
865239012Sthomas	struct g_consumer *cp;
866239012Sthomas	const char *name, *mpname;
867239012Sthomas	static const char devpf[6] = "/dev/";
868239012Sthomas	int *nargs;
869239012Sthomas
870239012Sthomas	g_topology_assert();
871239012Sthomas
872239012Sthomas	mpname = gctl_get_asciiparam(req, "arg0");
873239012Sthomas        if (mpname == NULL) {
874239012Sthomas                gctl_error(req, "No 'arg0' argument");
875239012Sthomas                return;
876239012Sthomas        }
877239012Sthomas	gp = g_multipath_find_geom(mp, mpname);
878239012Sthomas	if (gp == NULL) {
879239012Sthomas		gctl_error(req, "Device %s is invalid", mpname);
880239012Sthomas		return;
881239012Sthomas	}
882239012Sthomas	sc = gp->softc;
883239012Sthomas
884239012Sthomas	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
885239012Sthomas	if (nargs == NULL) {
886239012Sthomas		gctl_error(req, "No 'nargs' argument");
887239012Sthomas		return;
888239012Sthomas	}
889239012Sthomas	if (*nargs != 2) {
890239012Sthomas		gctl_error(req, "missing device");
891239012Sthomas		return;
892239012Sthomas	}
893239012Sthomas
894239012Sthomas	name = gctl_get_asciiparam(req, "arg1");
895239012Sthomas	if (name == NULL) {
896239012Sthomas		gctl_error(req, "No 'arg1' argument");
897239012Sthomas		return;
898239012Sthomas	}
899239012Sthomas	if (strncmp(name, devpf, 5) == 0) {
900239012Sthomas		name += 5;
901239012Sthomas	}
902239012Sthomas
903239012Sthomas	LIST_FOREACH(cp, &gp->consumer, consumer) {
904239012Sthomas		if (cp->provider != NULL
905239012Sthomas                      && strcmp(cp->provider->name, name) == 0)
906239012Sthomas		    break;
907239012Sthomas	}
908239012Sthomas
909239012Sthomas	if (cp == NULL) {
910239012Sthomas		gctl_error(req, "Provider %s not found", name);
911239012Sthomas		return;
912239012Sthomas	}
913239012Sthomas
914239012Sthomas	mtx_lock(&sc->sc_mtx);
915239012Sthomas
916239012Sthomas	if (cp->index & MP_BAD) {
917239012Sthomas		gctl_error(req, "Consumer %s is invalid", name);
918239012Sthomas		mtx_unlock(&sc->sc_mtx);
919239012Sthomas		return;
920239012Sthomas	}
921239012Sthomas
922239012Sthomas	/* Here when the consumer is present and in good shape */
923239012Sthomas
924239012Sthomas	sc->sc_active = cp;
925239012Sthomas	if (!sc->sc_active_active)
926239012Sthomas	    printf("GEOM_MULTIPATH: %s now active path in %s\n",
927239012Sthomas		sc->sc_active->provider->name, sc->sc_name);
928239012Sthomas
929239012Sthomas	mtx_unlock(&sc->sc_mtx);
930239012Sthomas}
931239012Sthomas
932239012Sthomasstatic void
933227464Smavg_multipath_ctl_add(struct gctl_req *req, struct g_class *mp)
934227464Smav{
935227464Smav	struct g_multipath_softc *sc;
936227464Smav	struct g_geom *gp;
937227464Smav	const char *mpname, *name;
938227464Smav
939227464Smav	mpname = gctl_get_asciiparam(req, "arg0");
940227464Smav        if (mpname == NULL) {
941227464Smav                gctl_error(req, "No 'arg0' argument");
942227464Smav                return;
943227464Smav        }
944227464Smav	gp = g_multipath_find_geom(mp, mpname);
945227464Smav	if (gp == NULL) {
946227464Smav		gctl_error(req, "Device %s not found", mpname);
947227464Smav		return;
948227464Smav	}
949227464Smav	sc = gp->softc;
950227464Smav
951227464Smav	name = gctl_get_asciiparam(req, "arg1");
952227464Smav	if (name == NULL) {
953227464Smav		gctl_error(req, "No 'arg1' argument");
954227464Smav		return;
955227464Smav	}
956227464Smav	g_multipath_ctl_add_name(req, mp, name);
957227464Smav}
958227464Smav
959227464Smavstatic void
960227464Smavg_multipath_ctl_create(struct gctl_req *req, struct g_class *mp)
961227464Smav{
962227464Smav	struct g_multipath_metadata md;
963227464Smav	struct g_multipath_softc *sc;
964227464Smav	struct g_geom *gp;
965227464Smav	const char *mpname, *name;
966227464Smav	char param[16];
967234415Smav	int *nargs, i, *val;
968227464Smav
969227464Smav	g_topology_assert();
970227464Smav
971227464Smav	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
972227464Smav	if (*nargs < 2) {
973227464Smav		gctl_error(req, "wrong number of arguments.");
974227464Smav		return;
975227464Smav	}
976227464Smav
977227464Smav	mpname = gctl_get_asciiparam(req, "arg0");
978227464Smav        if (mpname == NULL) {
979227464Smav                gctl_error(req, "No 'arg0' argument");
980227464Smav                return;
981227464Smav        }
982227464Smav	gp = g_multipath_find_geom(mp, mpname);
983227464Smav	if (gp != NULL) {
984227464Smav		gctl_error(req, "Device %s already exist", mpname);
985227464Smav		return;
986227464Smav	}
987227464Smav	sc = gp->softc;
988227464Smav
989227464Smav	memset(&md, 0, sizeof(md));
990227464Smav	strlcpy(md.md_magic, G_MULTIPATH_MAGIC, sizeof(md.md_magic));
991227464Smav	md.md_version = G_MULTIPATH_VERSION;
992227464Smav	strlcpy(md.md_name, mpname, sizeof(md.md_name));
993227464Smav	md.md_size = 0;
994227464Smav	md.md_sectorsize = 0;
995227464Smav	md.md_uuid[0] = 0;
996234415Smav	md.md_active_active = 0;
997234415Smav	val = gctl_get_paraml(req, "active_active", sizeof(*val));
998234415Smav	if (val != NULL && *val != 0)
999234415Smav		md.md_active_active = 1;
1000234415Smav	val = gctl_get_paraml(req, "active_read", sizeof(*val));
1001234415Smav	if (val != NULL && *val != 0)
1002234415Smav		md.md_active_active = 2;
1003227464Smav	gp = g_multipath_create(mp, &md);
1004227464Smav	if (gp == NULL) {
1005227464Smav		gctl_error(req, "GEOM_MULTIPATH: cannot create geom %s/%s\n",
1006227464Smav		    md.md_name, md.md_uuid);
1007227464Smav		return;
1008227464Smav	}
1009227464Smav	sc = gp->softc;
1010227464Smav
1011227464Smav	for (i = 1; i < *nargs; i++) {
1012227464Smav		snprintf(param, sizeof(param), "arg%d", i);
1013227464Smav		name = gctl_get_asciiparam(req, param);
1014227464Smav		g_multipath_ctl_add_name(req, mp, name);
1015227464Smav	}
1016227464Smav
1017227464Smav	if (sc->sc_ndisks != (*nargs - 1))
1018227464Smav		g_multipath_destroy(gp);
1019227464Smav}
1020227464Smav
1021227464Smavstatic void
1022234415Smavg_multipath_ctl_configure(struct gctl_req *req, struct g_class *mp)
1023234415Smav{
1024234415Smav	struct g_multipath_softc *sc;
1025234415Smav	struct g_geom *gp;
1026234415Smav	struct g_consumer *cp;
1027234415Smav	struct g_provider *pp;
1028235069Smav	struct g_multipath_metadata md;
1029234415Smav	const char *name;
1030234415Smav	int error, *val;
1031234415Smav	void *buf;
1032234415Smav
1033234415Smav	g_topology_assert();
1034234415Smav
1035234415Smav	name = gctl_get_asciiparam(req, "arg0");
1036234415Smav	if (name == NULL) {
1037234415Smav		gctl_error(req, "No 'arg0' argument");
1038234415Smav		return;
1039234415Smav	}
1040234415Smav	gp = g_multipath_find_geom(mp, name);
1041234415Smav	if (gp == NULL) {
1042234415Smav		gctl_error(req, "Device %s is invalid", name);
1043234415Smav		return;
1044234415Smav	}
1045234415Smav	sc = gp->softc;
1046234415Smav	val = gctl_get_paraml(req, "active_active", sizeof(*val));
1047234415Smav	if (val != NULL && *val != 0)
1048234415Smav		sc->sc_active_active = 1;
1049234415Smav	val = gctl_get_paraml(req, "active_read", sizeof(*val));
1050234415Smav	if (val != NULL && *val != 0)
1051234415Smav		sc->sc_active_active = 2;
1052234415Smav	val = gctl_get_paraml(req, "active_passive", sizeof(*val));
1053234415Smav	if (val != NULL && *val != 0)
1054234415Smav		sc->sc_active_active = 0;
1055234415Smav	if (sc->sc_uuid[0] != 0 && sc->sc_active != NULL) {
1056234415Smav		cp = sc->sc_active;
1057234415Smav		pp = cp->provider;
1058234415Smav		error = g_access(cp, 1, 1, 1);
1059234415Smav		if (error != 0) {
1060234415Smav			gctl_error(req, "Can't open %s (%d)", pp->name, error);
1061234415Smav			return;
1062234415Smav		}
1063234415Smav		g_topology_unlock();
1064235069Smav		buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
1065235069Smav		strlcpy(md.md_magic, G_MULTIPATH_MAGIC, sizeof(md.md_magic));
1066235069Smav		memcpy(md.md_uuid, sc->sc_uuid, sizeof (sc->sc_uuid));
1067235069Smav		strlcpy(md.md_name, name, sizeof(md.md_name));
1068235069Smav		md.md_version = G_MULTIPATH_VERSION;
1069235069Smav		md.md_size = pp->mediasize;
1070235069Smav		md.md_sectorsize = pp->sectorsize;
1071235069Smav		md.md_active_active = sc->sc_active_active;
1072235069Smav		multipath_metadata_encode(&md, buf);
1073234415Smav		error = g_write_data(cp, pp->mediasize - pp->sectorsize,
1074234415Smav		    buf, pp->sectorsize);
1075234415Smav		g_topology_lock();
1076234415Smav		g_access(cp, -1, -1, -1);
1077234415Smav		if (error != 0)
1078234415Smav			gctl_error(req, "Can't update metadata on %s (%d)",
1079234415Smav			    pp->name, error);
1080234415Smav	}
1081234415Smav}
1082234415Smav
1083234415Smavstatic void
1084227464Smavg_multipath_ctl_fail(struct gctl_req *req, struct g_class *mp, int fail)
1085227464Smav{
1086227464Smav	struct g_multipath_softc *sc;
1087227464Smav	struct g_geom *gp;
1088227464Smav	struct g_consumer *cp;
1089227464Smav	const char *mpname, *name;
1090227464Smav	int found;
1091227464Smav
1092227464Smav	mpname = gctl_get_asciiparam(req, "arg0");
1093227464Smav        if (mpname == NULL) {
1094227464Smav                gctl_error(req, "No 'arg0' argument");
1095227464Smav                return;
1096227464Smav        }
1097227464Smav	gp = g_multipath_find_geom(mp, mpname);
1098227464Smav	if (gp == NULL) {
1099227464Smav		gctl_error(req, "Device %s not found", mpname);
1100227464Smav		return;
1101227464Smav	}
1102227464Smav	sc = gp->softc;
1103227464Smav
1104227464Smav	name = gctl_get_asciiparam(req, "arg1");
1105227464Smav	if (name == NULL) {
1106227464Smav		gctl_error(req, "No 'arg1' argument");
1107227464Smav		return;
1108227464Smav	}
1109227464Smav
1110227464Smav	found = 0;
1111227464Smav	mtx_lock(&sc->sc_mtx);
1112227464Smav	LIST_FOREACH(cp, &gp->consumer, consumer) {
1113227464Smav		if (cp->provider != NULL &&
1114227464Smav		    strcmp(cp->provider->name, name) == 0 &&
1115227464Smav		    (cp->index & MP_LOST) == 0) {
1116227464Smav			found = 1;
1117234415Smav			if (!fail == !(cp->index & MP_FAIL))
1118234415Smav				continue;
1119227464Smav			printf("GEOM_MULTIPATH: %s in %s is marked %s.\n",
1120227464Smav				name, sc->sc_name, fail ? "FAIL" : "OK");
1121227464Smav			if (fail) {
1122227464Smav				g_multipath_fault(cp, MP_FAIL);
1123227464Smav			} else {
1124227464Smav				cp->index &= ~MP_FAIL;
1125227464Smav			}
1126227464Smav		}
1127227464Smav	}
1128227464Smav	mtx_unlock(&sc->sc_mtx);
1129227464Smav	if (found == 0)
1130227464Smav		gctl_error(req, "Provider %s not found", name);
1131227464Smav}
1132227464Smav
1133227464Smavstatic void
1134227464Smavg_multipath_ctl_remove(struct gctl_req *req, struct g_class *mp)
1135227464Smav{
1136227464Smav	struct g_multipath_softc *sc;
1137227464Smav	struct g_geom *gp;
1138227464Smav	struct g_consumer *cp, *cp1;
1139227464Smav	const char *mpname, *name;
1140227464Smav	uintptr_t *cnt;
1141227464Smav	int found;
1142227464Smav
1143227464Smav	mpname = gctl_get_asciiparam(req, "arg0");
1144227464Smav        if (mpname == NULL) {
1145227464Smav                gctl_error(req, "No 'arg0' argument");
1146227464Smav                return;
1147227464Smav        }
1148227464Smav	gp = g_multipath_find_geom(mp, mpname);
1149227464Smav	if (gp == NULL) {
1150227464Smav		gctl_error(req, "Device %s not found", mpname);
1151227464Smav		return;
1152227464Smav	}
1153227464Smav	sc = gp->softc;
1154227464Smav
1155227464Smav	name = gctl_get_asciiparam(req, "arg1");
1156227464Smav	if (name == NULL) {
1157227464Smav		gctl_error(req, "No 'arg1' argument");
1158227464Smav		return;
1159227464Smav	}
1160227464Smav
1161227464Smav	found = 0;
1162227464Smav	mtx_lock(&sc->sc_mtx);
1163227464Smav	LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) {
1164227464Smav		if (cp->provider != NULL &&
1165227464Smav		    strcmp(cp->provider->name, name) == 0 &&
1166227464Smav		    (cp->index & MP_LOST) == 0) {
1167227464Smav			found = 1;
1168227464Smav			printf("GEOM_MULTIPATH: removing %s from %s\n",
1169227464Smav			    cp->provider->name, cp->geom->name);
1170227464Smav			sc->sc_ndisks--;
1171227464Smav			g_multipath_fault(cp, MP_LOST);
1172227464Smav			cnt = (uintptr_t *)&cp->private;
1173227464Smav			if (*cnt == 0 && (cp->index & MP_POSTED) == 0) {
1174227464Smav				cp->index |= MP_POSTED;
1175227464Smav				mtx_unlock(&sc->sc_mtx);
1176227464Smav				g_mpd(cp, 0);
1177227464Smav				if (cp1 == NULL)
1178227464Smav					return;	/* Recursion happened. */
1179227464Smav				mtx_lock(&sc->sc_mtx);
1180227464Smav			}
1181227464Smav		}
1182227464Smav	}
1183227464Smav	mtx_unlock(&sc->sc_mtx);
1184227464Smav	if (found == 0)
1185227464Smav		gctl_error(req, "Provider %s not found", name);
1186227464Smav}
1187227464Smav
1188167050Smjacobstatic struct g_geom *
1189167050Smjacobg_multipath_find_geom(struct g_class *mp, const char *name)
1190167050Smjacob{
1191167050Smjacob	struct g_geom *gp;
1192227464Smav	struct g_multipath_softc *sc;
1193167050Smjacob
1194167050Smjacob	LIST_FOREACH(gp, &mp->geom, geom) {
1195227464Smav		sc = gp->softc;
1196227464Smav		if (sc == NULL || sc->sc_stopping)
1197227464Smav			continue;
1198227464Smav		if (strcmp(gp->name, name) == 0)
1199167050Smjacob			return (gp);
1200167050Smjacob	}
1201167050Smjacob	return (NULL);
1202167050Smjacob}
1203167050Smjacob
1204167050Smjacobstatic void
1205227464Smavg_multipath_ctl_stop(struct gctl_req *req, struct g_class *mp)
1206227464Smav{
1207227464Smav	struct g_geom *gp;
1208227464Smav	const char *name;
1209227464Smav	int error;
1210227464Smav
1211227464Smav	g_topology_assert();
1212227464Smav
1213227464Smav	name = gctl_get_asciiparam(req, "arg0");
1214227464Smav        if (name == NULL) {
1215227464Smav                gctl_error(req, "No 'arg0' argument");
1216227464Smav                return;
1217227464Smav        }
1218227464Smav	gp = g_multipath_find_geom(mp, name);
1219227464Smav	if (gp == NULL) {
1220227464Smav		gctl_error(req, "Device %s is invalid", name);
1221227464Smav		return;
1222227464Smav	}
1223227464Smav	error = g_multipath_destroy(gp);
1224227464Smav	if (error != 0 && error != EINPROGRESS)
1225227464Smav		gctl_error(req, "failed to stop %s (err=%d)", name, error);
1226227464Smav}
1227227464Smav
1228227464Smavstatic void
1229167050Smjacobg_multipath_ctl_destroy(struct gctl_req *req, struct g_class *mp)
1230167050Smjacob{
1231167050Smjacob	struct g_geom *gp;
1232227464Smav	struct g_multipath_softc *sc;
1233227464Smav	struct g_consumer *cp;
1234227464Smav	struct g_provider *pp;
1235167050Smjacob	const char *name;
1236227464Smav	uint8_t *buf;
1237167050Smjacob	int error;
1238167050Smjacob
1239167050Smjacob	g_topology_assert();
1240167050Smjacob
1241167050Smjacob	name = gctl_get_asciiparam(req, "arg0");
1242167050Smjacob        if (name == NULL) {
1243167050Smjacob                gctl_error(req, "No 'arg0' argument");
1244167050Smjacob                return;
1245167050Smjacob        }
1246167050Smjacob	gp = g_multipath_find_geom(mp, name);
1247167050Smjacob	if (gp == NULL) {
1248167050Smjacob		gctl_error(req, "Device %s is invalid", name);
1249167050Smjacob		return;
1250167050Smjacob	}
1251227464Smav	sc = gp->softc;
1252227464Smav
1253227464Smav	if (sc->sc_uuid[0] != 0 && sc->sc_active != NULL) {
1254227464Smav		cp = sc->sc_active;
1255227464Smav		pp = cp->provider;
1256227464Smav		error = g_access(cp, 1, 1, 1);
1257227464Smav		if (error != 0) {
1258227464Smav			gctl_error(req, "Can't open %s (%d)", pp->name, error);
1259227464Smav			goto destroy;
1260227464Smav		}
1261227464Smav		g_topology_unlock();
1262227464Smav		buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
1263227464Smav		error = g_write_data(cp, pp->mediasize - pp->sectorsize,
1264227464Smav		    buf, pp->sectorsize);
1265227464Smav		g_topology_lock();
1266227464Smav		g_access(cp, -1, -1, -1);
1267227464Smav		if (error != 0)
1268227464Smav			gctl_error(req, "Can't erase metadata on %s (%d)",
1269227464Smav			    pp->name, error);
1270227464Smav	}
1271227464Smav
1272227464Smavdestroy:
1273167050Smjacob	error = g_multipath_destroy(gp);
1274227464Smav	if (error != 0 && error != EINPROGRESS)
1275167050Smjacob		gctl_error(req, "failed to destroy %s (err=%d)", name, error);
1276167050Smjacob}
1277167050Smjacob
1278167050Smjacobstatic void
1279205412Smjacobg_multipath_ctl_rotate(struct gctl_req *req, struct g_class *mp)
1280205412Smjacob{
1281205412Smjacob	struct g_geom *gp;
1282205412Smjacob	const char *name;
1283205412Smjacob	int error;
1284205412Smjacob
1285205412Smjacob	g_topology_assert();
1286205412Smjacob
1287205412Smjacob	name = gctl_get_asciiparam(req, "arg0");
1288205412Smjacob        if (name == NULL) {
1289205412Smjacob                gctl_error(req, "No 'arg0' argument");
1290205412Smjacob                return;
1291205412Smjacob        }
1292205412Smjacob	gp = g_multipath_find_geom(mp, name);
1293205412Smjacob	if (gp == NULL) {
1294205412Smjacob		gctl_error(req, "Device %s is invalid", name);
1295205412Smjacob		return;
1296205412Smjacob	}
1297205412Smjacob	error = g_multipath_rotate(gp);
1298205412Smjacob	if (error != 0) {
1299205412Smjacob		gctl_error(req, "failed to rotate %s (err=%d)", name, error);
1300205412Smjacob	}
1301205412Smjacob}
1302205412Smjacob
1303205412Smjacobstatic void
1304205412Smjacobg_multipath_ctl_getactive(struct gctl_req *req, struct g_class *mp)
1305205412Smjacob{
1306205412Smjacob	struct sbuf *sb;
1307205412Smjacob	struct g_geom *gp;
1308205412Smjacob	struct g_multipath_softc *sc;
1309227464Smav	struct g_consumer *cp;
1310205412Smjacob	const char *name;
1311227464Smav	int empty;
1312205412Smjacob
1313205412Smjacob	sb = sbuf_new_auto();
1314205412Smjacob
1315205412Smjacob	g_topology_assert();
1316205412Smjacob	name = gctl_get_asciiparam(req, "arg0");
1317205412Smjacob        if (name == NULL) {
1318205412Smjacob                gctl_error(req, "No 'arg0' argument");
1319205412Smjacob                return;
1320205412Smjacob        }
1321205412Smjacob	gp = g_multipath_find_geom(mp, name);
1322205412Smjacob	if (gp == NULL) {
1323205412Smjacob		gctl_error(req, "Device %s is invalid", name);
1324205412Smjacob		return;
1325205412Smjacob	}
1326205412Smjacob	sc = gp->softc;
1327234415Smav	if (sc->sc_active_active == 1) {
1328227464Smav		empty = 1;
1329227464Smav		LIST_FOREACH(cp, &gp->consumer, consumer) {
1330227464Smav			if (cp->index & MP_BAD)
1331227464Smav				continue;
1332227464Smav			if (!empty)
1333227464Smav				sbuf_cat(sb, " ");
1334227464Smav			sbuf_cat(sb, cp->provider->name);
1335227464Smav			empty = 0;
1336227464Smav		}
1337227464Smav		if (empty)
1338227464Smav			sbuf_cat(sb, "none");
1339227464Smav		sbuf_cat(sb, "\n");
1340227464Smav	} else if (sc->sc_active && sc->sc_active->provider) {
1341227464Smav		sbuf_printf(sb, "%s\n", sc->sc_active->provider->name);
1342205412Smjacob	} else {
1343205412Smjacob		sbuf_printf(sb, "none\n");
1344205412Smjacob	}
1345205412Smjacob	sbuf_finish(sb);
1346205412Smjacob	gctl_set_param_err(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1347205412Smjacob	sbuf_delete(sb);
1348205412Smjacob}
1349205412Smjacob
1350205412Smjacobstatic void
1351167050Smjacobg_multipath_config(struct gctl_req *req, struct g_class *mp, const char *verb)
1352167050Smjacob{
1353167050Smjacob	uint32_t *version;
1354167050Smjacob	g_topology_assert();
1355167050Smjacob	version = gctl_get_paraml(req, "version", sizeof(*version));
1356167050Smjacob	if (version == NULL) {
1357167050Smjacob		gctl_error(req, "No 'version' argument");
1358167050Smjacob	} else if (*version != G_MULTIPATH_VERSION) {
1359167050Smjacob		gctl_error(req, "Userland and kernel parts are out of sync");
1360205847Smjacob	} else if (strcmp(verb, "add") == 0) {
1361205847Smjacob		g_multipath_ctl_add(req, mp);
1362239012Sthomas	} else if (strcmp(verb, "prefer") == 0) {
1363239012Sthomas		g_multipath_ctl_prefer(req, mp);
1364227464Smav	} else if (strcmp(verb, "create") == 0) {
1365227464Smav		g_multipath_ctl_create(req, mp);
1366234415Smav	} else if (strcmp(verb, "configure") == 0) {
1367234415Smav		g_multipath_ctl_configure(req, mp);
1368227464Smav	} else if (strcmp(verb, "stop") == 0) {
1369227464Smav		g_multipath_ctl_stop(req, mp);
1370167050Smjacob	} else if (strcmp(verb, "destroy") == 0) {
1371167050Smjacob		g_multipath_ctl_destroy(req, mp);
1372227464Smav	} else if (strcmp(verb, "fail") == 0) {
1373227464Smav		g_multipath_ctl_fail(req, mp, 1);
1374227464Smav	} else if (strcmp(verb, "restore") == 0) {
1375227464Smav		g_multipath_ctl_fail(req, mp, 0);
1376227464Smav	} else if (strcmp(verb, "remove") == 0) {
1377227464Smav		g_multipath_ctl_remove(req, mp);
1378205412Smjacob	} else if (strcmp(verb, "rotate") == 0) {
1379205412Smjacob		g_multipath_ctl_rotate(req, mp);
1380205412Smjacob	} else if (strcmp(verb, "getactive") == 0) {
1381205412Smjacob		g_multipath_ctl_getactive(req, mp);
1382167050Smjacob	} else {
1383167050Smjacob		gctl_error(req, "Unknown verb %s", verb);
1384167050Smjacob	}
1385167050Smjacob}
1386227464Smav
1387227464Smavstatic void
1388227464Smavg_multipath_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
1389227464Smav    struct g_consumer *cp, struct g_provider *pp)
1390227464Smav{
1391227464Smav	struct g_multipath_softc *sc;
1392227464Smav	int good;
1393227464Smav
1394227464Smav	g_topology_assert();
1395227464Smav
1396227464Smav	sc = gp->softc;
1397227464Smav	if (sc == NULL)
1398227464Smav		return;
1399227464Smav	if (cp != NULL) {
1400236619Smav		sbuf_printf(sb, "%s<State>%s</State>\n", indent,
1401227464Smav		    (cp->index & MP_NEW) ? "NEW" :
1402227464Smav		    (cp->index & MP_LOST) ? "LOST" :
1403227464Smav		    (cp->index & MP_FAIL) ? "FAIL" :
1404234415Smav		    (sc->sc_active_active == 1 || sc->sc_active == cp) ?
1405234415Smav		     "ACTIVE" :
1406234415Smav		     sc->sc_active_active == 2 ? "READ" : "PASSIVE");
1407227464Smav	} else {
1408227464Smav		good = g_multipath_good(gp);
1409236619Smav		sbuf_printf(sb, "%s<State>%s</State>\n", indent,
1410227464Smav		    good == 0 ? "BROKEN" :
1411227464Smav		    (good != sc->sc_ndisks || sc->sc_ndisks == 1) ?
1412227464Smav		    "DEGRADED" : "OPTIMAL");
1413227464Smav	}
1414227464Smav	if (cp == NULL && pp == NULL) {
1415236619Smav		sbuf_printf(sb, "%s<UUID>%s</UUID>\n", indent, sc->sc_uuid);
1416236619Smav		sbuf_printf(sb, "%s<Mode>Active/%s</Mode>\n", indent,
1417234415Smav		    sc->sc_active_active == 2 ? "Read" :
1418234415Smav		    sc->sc_active_active == 1 ? "Active" : "Passive");
1419236619Smav		sbuf_printf(sb, "%s<Type>%s</Type>\n", indent,
1420227464Smav		    sc->sc_uuid[0] == 0 ? "MANUAL" : "AUTOMATIC");
1421227464Smav	}
1422227464Smav}
1423227464Smav
1424167050SmjacobDECLARE_GEOM_CLASS(g_multipath_class, g_multipath);
1425