1167050Smjacob/*-
2260478Smav * Copyright (c) 2011-2013 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: releng/10.3/sys/geom/multipath/g_multipath.c 294709 2016-01-25 09:29:29Z smh $");
35167050Smjacob#include <sys/param.h>
36167050Smjacob#include <sys/systm.h>
37167050Smjacob#include <sys/kernel.h>
38167050Smjacob#include <sys/module.h>
39260478Smav#include <sys/limits.h>
40167050Smjacob#include <sys/lock.h>
41167050Smjacob#include <sys/mutex.h>
42167050Smjacob#include <sys/bio.h>
43223921Sae#include <sys/sbuf.h>
44167050Smjacob#include <sys/sysctl.h>
45167050Smjacob#include <sys/kthread.h>
46167050Smjacob#include <sys/malloc.h>
47167050Smjacob#include <geom/geom.h>
48167050Smjacob#include <geom/multipath/g_multipath.h>
49167050Smjacob
50219029SnetchildFEATURE(geom_multipath, "GEOM multipath support");
51167050Smjacob
52167050SmjacobSYSCTL_DECL(_kern_geom);
53227309Sedstatic SYSCTL_NODE(_kern_geom, OID_AUTO, multipath, CTLFLAG_RW, 0,
54167050Smjacob    "GEOM_MULTIPATH tunables");
55167050Smjacobstatic u_int g_multipath_debug = 0;
56167050SmjacobSYSCTL_UINT(_kern_geom_multipath, OID_AUTO, debug, CTLFLAG_RW,
57167050Smjacob    &g_multipath_debug, 0, "Debug level");
58227464Smavstatic u_int g_multipath_exclusive = 1;
59227464SmavSYSCTL_UINT(_kern_geom_multipath, OID_AUTO, exclusive, CTLFLAG_RW,
60227464Smav    &g_multipath_exclusive, 0, "Exclusively open providers");
61167050Smjacob
62167050Smjacobstatic enum {
63167050Smjacob	GKT_NIL,
64167050Smjacob	GKT_RUN,
65167050Smjacob	GKT_DIE
66167050Smjacob} g_multipath_kt_state;
67167050Smjacobstatic struct bio_queue_head gmtbq;
68167050Smjacobstatic struct mtx gmtbq_mtx;
69167050Smjacob
70260478Smavstatic int g_multipath_read_metadata(struct g_consumer *cp,
71260478Smav    struct g_multipath_metadata *md);
72260478Smavstatic int g_multipath_write_metadata(struct g_consumer *cp,
73260478Smav    struct g_multipath_metadata *md);
74260478Smav
75167050Smjacobstatic void g_multipath_orphan(struct g_consumer *);
76260478Smavstatic void g_multipath_resize(struct g_consumer *);
77167050Smjacobstatic void g_multipath_start(struct bio *);
78167050Smjacobstatic void g_multipath_done(struct bio *);
79167050Smjacobstatic void g_multipath_done_error(struct bio *);
80167050Smjacobstatic void g_multipath_kt(void *);
81167050Smjacob
82167050Smjacobstatic int g_multipath_destroy(struct g_geom *);
83167050Smjacobstatic int
84167050Smjacobg_multipath_destroy_geom(struct gctl_req *, struct g_class *, struct g_geom *);
85167050Smjacob
86205847Smjacobstatic struct g_geom *g_multipath_find_geom(struct g_class *, const char *);
87205412Smjacobstatic int g_multipath_rotate(struct g_geom *);
88205412Smjacob
89167050Smjacobstatic g_taste_t g_multipath_taste;
90167050Smjacobstatic g_ctl_req_t g_multipath_config;
91167050Smjacobstatic g_init_t g_multipath_init;
92167050Smjacobstatic g_fini_t g_multipath_fini;
93227464Smavstatic g_dumpconf_t g_multipath_dumpconf;
94167050Smjacob
95167050Smjacobstruct g_class g_multipath_class = {
96167050Smjacob	.name		= G_MULTIPATH_CLASS_NAME,
97167050Smjacob	.version	= G_VERSION,
98167050Smjacob	.ctlreq		= g_multipath_config,
99167050Smjacob	.taste		= g_multipath_taste,
100167050Smjacob	.destroy_geom	= g_multipath_destroy_geom,
101167050Smjacob	.init		= g_multipath_init,
102167050Smjacob	.fini		= g_multipath_fini
103167050Smjacob};
104167050Smjacob
105227464Smav#define	MP_FAIL		0x00000001
106227464Smav#define	MP_LOST		0x00000002
107227464Smav#define	MP_NEW		0x00000004
108227464Smav#define	MP_POSTED	0x00000008
109227464Smav#define	MP_BAD		(MP_FAIL | MP_LOST | MP_NEW)
110294709Ssmh#define	MP_WITHER	0x00000010
111294709Ssmh#define	MP_IDLE		0x00000020
112294709Ssmh#define	MP_IDLE_MASK	0xffffffe0
113167050Smjacob
114227464Smavstatic int
115227464Smavg_multipath_good(struct g_geom *gp)
116227464Smav{
117227464Smav	struct g_consumer *cp;
118227464Smav	int n = 0;
119227464Smav
120227464Smav	LIST_FOREACH(cp, &gp->consumer, consumer) {
121227464Smav		if ((cp->index & MP_BAD) == 0)
122227464Smav			n++;
123227464Smav	}
124227464Smav	return (n);
125227464Smav}
126227464Smav
127167050Smjacobstatic void
128227464Smavg_multipath_fault(struct g_consumer *cp, int cause)
129227464Smav{
130227464Smav	struct g_multipath_softc *sc;
131227464Smav	struct g_consumer *lcp;
132227464Smav	struct g_geom *gp;
133227464Smav
134227464Smav	gp = cp->geom;
135227464Smav	sc = gp->softc;
136227464Smav	cp->index |= cause;
137227464Smav	if (g_multipath_good(gp) == 0 && sc->sc_ndisks > 0) {
138227464Smav		LIST_FOREACH(lcp, &gp->consumer, consumer) {
139227464Smav			if (lcp->provider == NULL ||
140227464Smav			    (lcp->index & (MP_LOST | MP_NEW)))
141227464Smav				continue;
142227464Smav			if (sc->sc_ndisks > 1 && lcp == cp)
143227464Smav				continue;
144227464Smav			printf("GEOM_MULTIPATH: "
145227464Smav			    "all paths in %s were marked FAIL, restore %s\n",
146227464Smav			    sc->sc_name, lcp->provider->name);
147227464Smav			lcp->index &= ~MP_FAIL;
148227464Smav		}
149227464Smav	}
150227464Smav	if (cp != sc->sc_active)
151227464Smav		return;
152227464Smav	sc->sc_active = NULL;
153227464Smav	LIST_FOREACH(lcp, &gp->consumer, consumer) {
154227464Smav		if ((lcp->index & MP_BAD) == 0) {
155227464Smav			sc->sc_active = lcp;
156227464Smav			break;
157227464Smav		}
158227464Smav	}
159227464Smav	if (sc->sc_active == NULL) {
160227464Smav		printf("GEOM_MULTIPATH: out of providers for %s\n",
161227464Smav		    sc->sc_name);
162234415Smav	} else if (sc->sc_active_active != 1) {
163227464Smav		printf("GEOM_MULTIPATH: %s is now active path in %s\n",
164227464Smav		    sc->sc_active->provider->name, sc->sc_name);
165227464Smav	}
166227464Smav}
167227464Smav
168227464Smavstatic struct g_consumer *
169234415Smavg_multipath_choose(struct g_geom *gp, struct bio *bp)
170227464Smav{
171227464Smav	struct g_multipath_softc *sc;
172227464Smav	struct g_consumer *best, *cp;
173227464Smav
174227464Smav	sc = gp->softc;
175234415Smav	if (sc->sc_active_active == 0 ||
176234415Smav	    (sc->sc_active_active == 2 && bp->bio_cmd != BIO_READ))
177227464Smav		return (sc->sc_active);
178227464Smav	best = NULL;
179227464Smav	LIST_FOREACH(cp, &gp->consumer, consumer) {
180227464Smav		if (cp->index & MP_BAD)
181227464Smav			continue;
182227464Smav		cp->index += MP_IDLE;
183227464Smav		if (best == NULL || cp->private < best->private ||
184227464Smav		    (cp->private == best->private && cp->index > best->index))
185227464Smav			best = cp;
186227464Smav	}
187227464Smav	if (best != NULL)
188227464Smav		best->index &= ~MP_IDLE_MASK;
189227464Smav	return (best);
190227464Smav}
191227464Smav
192227464Smavstatic void
193167050Smjacobg_mpd(void *arg, int flags __unused)
194167050Smjacob{
195227464Smav	struct g_geom *gp;
196227464Smav	struct g_multipath_softc *sc;
197167050Smjacob	struct g_consumer *cp;
198227464Smav	int w;
199167050Smjacob
200167050Smjacob	g_topology_assert();
201167050Smjacob	cp = arg;
202227464Smav	gp = cp->geom;
203227464Smav	if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) {
204227464Smav		w = cp->acw;
205167050Smjacob		g_access(cp, -cp->acr, -cp->acw, -cp->ace);
206227464Smav		if (w > 0 && cp->provider != NULL &&
207227464Smav		    (cp->provider->geom->flags & G_GEOM_WITHER) == 0) {
208294709Ssmh			cp->index |= MP_WITHER;
209227464Smav			g_post_event(g_mpd, cp, M_WAITOK, NULL);
210227464Smav			return;
211227464Smav		}
212227464Smav	}
213227464Smav	sc = gp->softc;
214227464Smav	mtx_lock(&sc->sc_mtx);
215167050Smjacob	if (cp->provider) {
216167050Smjacob		printf("GEOM_MULTIPATH: %s removed from %s\n",
217227464Smav		    cp->provider->name, gp->name);
218167050Smjacob		g_detach(cp);
219167050Smjacob	}
220167050Smjacob	g_destroy_consumer(cp);
221227464Smav	mtx_unlock(&sc->sc_mtx);
222227464Smav	if (LIST_EMPTY(&gp->consumer))
223227464Smav		g_multipath_destroy(gp);
224167050Smjacob}
225167050Smjacob
226167050Smjacobstatic void
227167050Smjacobg_multipath_orphan(struct g_consumer *cp)
228167050Smjacob{
229227464Smav	struct g_multipath_softc *sc;
230227464Smav	uintptr_t *cnt;
231227464Smav
232227464Smav	g_topology_assert();
233227464Smav	printf("GEOM_MULTIPATH: %s in %s was disconnected\n",
234227464Smav	    cp->provider->name, cp->geom->name);
235227464Smav	sc = cp->geom->softc;
236227464Smav	cnt = (uintptr_t *)&cp->private;
237227464Smav	mtx_lock(&sc->sc_mtx);
238227464Smav	sc->sc_ndisks--;
239227464Smav	g_multipath_fault(cp, MP_LOST);
240227464Smav	if (*cnt == 0 && (cp->index & MP_POSTED) == 0) {
241167050Smjacob		cp->index |= MP_POSTED;
242227464Smav		mtx_unlock(&sc->sc_mtx);
243167050Smjacob		g_mpd(cp, 0);
244227464Smav	} else
245227464Smav		mtx_unlock(&sc->sc_mtx);
246167050Smjacob}
247167050Smjacob
248167050Smjacobstatic void
249260478Smavg_multipath_resize(struct g_consumer *cp)
250260478Smav{
251260478Smav	struct g_multipath_softc *sc;
252260478Smav	struct g_geom *gp;
253260478Smav	struct g_consumer *cp1;
254260478Smav	struct g_provider *pp;
255260478Smav	struct g_multipath_metadata md;
256260478Smav	off_t size, psize, ssize;
257260478Smav	int error;
258260478Smav
259260478Smav	g_topology_assert();
260260478Smav
261260478Smav	gp = cp->geom;
262260478Smav	pp = cp->provider;
263260478Smav	sc = gp->softc;
264260478Smav
265260478Smav	if (sc->sc_stopping)
266260478Smav		return;
267260478Smav
268260478Smav	if (pp->mediasize < sc->sc_size) {
269260478Smav		size = pp->mediasize;
270260478Smav		ssize = pp->sectorsize;
271260478Smav	} else {
272260478Smav		size = ssize = OFF_MAX;
273260478Smav		mtx_lock(&sc->sc_mtx);
274260478Smav		LIST_FOREACH(cp1, &gp->consumer, consumer) {
275260478Smav			pp = cp1->provider;
276260478Smav			if (pp == NULL)
277260478Smav				continue;
278260478Smav			if (pp->mediasize < size) {
279260478Smav				size = pp->mediasize;
280260478Smav				ssize = pp->sectorsize;
281260478Smav			}
282260478Smav		}
283260478Smav		mtx_unlock(&sc->sc_mtx);
284260478Smav		if (size == OFF_MAX || size == sc->sc_size)
285260478Smav			return;
286260478Smav	}
287260478Smav	psize = size - ((sc->sc_uuid[0] != 0) ? ssize : 0);
288260478Smav	printf("GEOM_MULTIPATH: %s size changed from %jd to %jd\n",
289260478Smav	    sc->sc_name, sc->sc_pp->mediasize, psize);
290260478Smav	if (sc->sc_uuid[0] != 0 && size < sc->sc_size) {
291260478Smav		error = g_multipath_read_metadata(cp, &md);
292260478Smav		if (error ||
293260478Smav		    (strcmp(md.md_magic, G_MULTIPATH_MAGIC) != 0) ||
294260478Smav		    (memcmp(md.md_uuid, sc->sc_uuid, sizeof(sc->sc_uuid)) != 0) ||
295260478Smav		    (strcmp(md.md_name, sc->sc_name) != 0) ||
296260478Smav		    (md.md_size != 0 && md.md_size != size) ||
297260478Smav		    (md.md_sectorsize != 0 && md.md_sectorsize != ssize)) {
298260478Smav			g_multipath_destroy(gp);
299260478Smav			return;
300260478Smav		}
301260478Smav	}
302260478Smav	sc->sc_size = size;
303260478Smav	g_resize_provider(sc->sc_pp, psize);
304260478Smav
305260478Smav	if (sc->sc_uuid[0] != 0) {
306260478Smav		pp = cp->provider;
307260478Smav		strlcpy(md.md_magic, G_MULTIPATH_MAGIC, sizeof(md.md_magic));
308260478Smav		memcpy(md.md_uuid, sc->sc_uuid, sizeof (sc->sc_uuid));
309260478Smav		strlcpy(md.md_name, sc->sc_name, sizeof(md.md_name));
310260478Smav		md.md_version = G_MULTIPATH_VERSION;
311260478Smav		md.md_size = size;
312260478Smav		md.md_sectorsize = ssize;
313260478Smav		md.md_active_active = sc->sc_active_active;
314260478Smav		error = g_multipath_write_metadata(cp, &md);
315260478Smav		if (error != 0)
316260478Smav			printf("GEOM_MULTIPATH: Can't update metadata on %s "
317260478Smav			    "(%d)\n", pp->name, error);
318260478Smav	}
319260478Smav}
320260478Smav
321260478Smavstatic void
322167050Smjacobg_multipath_start(struct bio *bp)
323167050Smjacob{
324167050Smjacob	struct g_multipath_softc *sc;
325167050Smjacob	struct g_geom *gp;
326167050Smjacob	struct g_consumer *cp;
327167050Smjacob	struct bio *cbp;
328227464Smav	uintptr_t *cnt;
329167050Smjacob
330167050Smjacob	gp = bp->bio_to->geom;
331167050Smjacob	sc = gp->softc;
332167050Smjacob	KASSERT(sc != NULL, ("NULL sc"));
333167050Smjacob	cbp = g_clone_bio(bp);
334167050Smjacob	if (cbp == NULL) {
335167050Smjacob		g_io_deliver(bp, ENOMEM);
336167050Smjacob		return;
337167050Smjacob	}
338227464Smav	mtx_lock(&sc->sc_mtx);
339234415Smav	cp = g_multipath_choose(gp, bp);
340227464Smav	if (cp == NULL) {
341227464Smav		mtx_unlock(&sc->sc_mtx);
342227464Smav		g_destroy_bio(cbp);
343227464Smav		g_io_deliver(bp, ENXIO);
344227464Smav		return;
345227464Smav	}
346227464Smav	if ((uintptr_t)bp->bio_driver1 < sc->sc_ndisks)
347227464Smav		bp->bio_driver1 = (void *)(uintptr_t)sc->sc_ndisks;
348227464Smav	cnt = (uintptr_t *)&cp->private;
349227464Smav	(*cnt)++;
350227464Smav	mtx_unlock(&sc->sc_mtx);
351167050Smjacob	cbp->bio_done = g_multipath_done;
352167050Smjacob	g_io_request(cbp, cp);
353167050Smjacob}
354167050Smjacob
355167050Smjacobstatic void
356167050Smjacobg_multipath_done(struct bio *bp)
357167050Smjacob{
358227464Smav	struct g_multipath_softc *sc;
359227464Smav	struct g_consumer *cp;
360227464Smav	uintptr_t *cnt;
361227464Smav
362167050Smjacob	if (bp->bio_error == ENXIO || bp->bio_error == EIO) {
363167050Smjacob		mtx_lock(&gmtbq_mtx);
364167050Smjacob		bioq_insert_tail(&gmtbq, bp);
365227464Smav		mtx_unlock(&gmtbq_mtx);
366167050Smjacob		wakeup(&g_multipath_kt_state);
367167050Smjacob	} else {
368227464Smav		cp = bp->bio_from;
369227464Smav		sc = cp->geom->softc;
370227464Smav		cnt = (uintptr_t *)&cp->private;
371227464Smav		mtx_lock(&sc->sc_mtx);
372227464Smav		(*cnt)--;
373227464Smav		if (*cnt == 0 && (cp->index & MP_LOST)) {
374287850Simp			if (g_post_event(g_mpd, cp, M_NOWAIT, NULL) == 0)
375287850Simp				cp->index |= MP_POSTED;
376227464Smav			mtx_unlock(&sc->sc_mtx);
377227464Smav		} else
378227464Smav			mtx_unlock(&sc->sc_mtx);
379167050Smjacob		g_std_done(bp);
380167050Smjacob	}
381167050Smjacob}
382167050Smjacob
383167050Smjacobstatic void
384167050Smjacobg_multipath_done_error(struct bio *bp)
385167050Smjacob{
386167050Smjacob	struct bio *pbp;
387167050Smjacob	struct g_geom *gp;
388167050Smjacob	struct g_multipath_softc *sc;
389167050Smjacob	struct g_consumer *cp;
390167050Smjacob	struct g_provider *pp;
391227464Smav	uintptr_t *cnt;
392167050Smjacob
393167050Smjacob	/*
394167050Smjacob	 * If we had a failure, we have to check first to see
395167050Smjacob	 * whether the consumer it failed on was the currently
396167050Smjacob	 * active consumer (i.e., this is the first in perhaps
397167050Smjacob	 * a number of failures). If so, we then switch consumers
398167050Smjacob	 * to the next available consumer.
399167050Smjacob	 */
400167050Smjacob
401167050Smjacob	pbp = bp->bio_parent;
402167050Smjacob	gp = pbp->bio_to->geom;
403167050Smjacob	sc = gp->softc;
404167050Smjacob	cp = bp->bio_from;
405167050Smjacob	pp = cp->provider;
406227464Smav	cnt = (uintptr_t *)&cp->private;
407167050Smjacob
408227464Smav	mtx_lock(&sc->sc_mtx);
409234415Smav	if ((cp->index & MP_FAIL) == 0) {
410234415Smav		printf("GEOM_MULTIPATH: Error %d, %s in %s marked FAIL\n",
411234415Smav		    bp->bio_error, pp->name, sc->sc_name);
412234415Smav		g_multipath_fault(cp, MP_FAIL);
413234415Smav	}
414227464Smav	(*cnt)--;
415227464Smav	if (*cnt == 0 && (cp->index & (MP_LOST | MP_POSTED)) == MP_LOST) {
416167050Smjacob		cp->index |= MP_POSTED;
417227464Smav		mtx_unlock(&sc->sc_mtx);
418227464Smav		g_post_event(g_mpd, cp, M_WAITOK, NULL);
419227464Smav	} else
420227464Smav		mtx_unlock(&sc->sc_mtx);
421167050Smjacob
422167050Smjacob	/*
423167050Smjacob	 * If we can fruitfully restart the I/O, do so.
424167050Smjacob	 */
425227464Smav	if (pbp->bio_children < (uintptr_t)pbp->bio_driver1) {
426227464Smav		pbp->bio_inbed++;
427167050Smjacob		g_destroy_bio(bp);
428167050Smjacob		g_multipath_start(pbp);
429167050Smjacob	} else {
430167050Smjacob		g_std_done(bp);
431167050Smjacob	}
432167050Smjacob}
433167050Smjacob
434167050Smjacobstatic void
435167050Smjacobg_multipath_kt(void *arg)
436167050Smjacob{
437204071Spjd
438167050Smjacob	g_multipath_kt_state = GKT_RUN;
439167050Smjacob	mtx_lock(&gmtbq_mtx);
440167050Smjacob	while (g_multipath_kt_state == GKT_RUN) {
441167050Smjacob		for (;;) {
442167050Smjacob			struct bio *bp;
443204071Spjd
444167050Smjacob			bp = bioq_takefirst(&gmtbq);
445204071Spjd			if (bp == NULL)
446167050Smjacob				break;
447167050Smjacob			mtx_unlock(&gmtbq_mtx);
448167050Smjacob			g_multipath_done_error(bp);
449167050Smjacob			mtx_lock(&gmtbq_mtx);
450167050Smjacob		}
451234415Smav		if (g_multipath_kt_state != GKT_RUN)
452234415Smav			break;
453167050Smjacob		msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO,
454234415Smav		    "gkt:wait", 0);
455167050Smjacob	}
456167050Smjacob	mtx_unlock(&gmtbq_mtx);
457167050Smjacob	wakeup(&g_multipath_kt_state);
458172836Sjulian	kproc_exit(0);
459167050Smjacob}
460167050Smjacob
461167050Smjacob
462167050Smjacobstatic int
463167050Smjacobg_multipath_access(struct g_provider *pp, int dr, int dw, int de)
464167050Smjacob{
465167050Smjacob	struct g_geom *gp;
466167050Smjacob	struct g_consumer *cp, *badcp = NULL;
467227464Smav	struct g_multipath_softc *sc;
468167050Smjacob	int error;
469167050Smjacob
470167050Smjacob	gp = pp->geom;
471167050Smjacob
472294709Ssmh	/* Error used if we have no valid consumers. */
473294709Ssmh	error = ENXIO;
474294709Ssmh
475167050Smjacob	LIST_FOREACH(cp, &gp->consumer, consumer) {
476294709Ssmh		if (cp->index & MP_WITHER)
477294709Ssmh			continue;
478294709Ssmh
479167050Smjacob		error = g_access(cp, dr, dw, de);
480167050Smjacob		if (error) {
481167050Smjacob			badcp = cp;
482167050Smjacob			goto fail;
483167050Smjacob		}
484167050Smjacob	}
485294709Ssmh
486294709Ssmh	if (error != 0)
487294709Ssmh		return (error);
488294709Ssmh
489227464Smav	sc = gp->softc;
490227464Smav	sc->sc_opened += dr + dw + de;
491227464Smav	if (sc->sc_stopping && sc->sc_opened == 0)
492227464Smav		g_multipath_destroy(gp);
493294709Ssmh
494167050Smjacob	return (0);
495167050Smjacob
496167050Smjacobfail:
497167050Smjacob	LIST_FOREACH(cp, &gp->consumer, consumer) {
498204071Spjd		if (cp == badcp)
499167050Smjacob			break;
500294709Ssmh		if (cp->index & MP_WITHER)
501294709Ssmh			continue;
502294709Ssmh
503167050Smjacob		(void) g_access(cp, -dr, -dw, -de);
504167050Smjacob	}
505167050Smjacob	return (error);
506167050Smjacob}
507167050Smjacob
508167050Smjacobstatic struct g_geom *
509167050Smjacobg_multipath_create(struct g_class *mp, struct g_multipath_metadata *md)
510167050Smjacob{
511167050Smjacob	struct g_multipath_softc *sc;
512167050Smjacob	struct g_geom *gp;
513167050Smjacob	struct g_provider *pp;
514167050Smjacob
515167050Smjacob	g_topology_assert();
516167050Smjacob
517167050Smjacob	LIST_FOREACH(gp, &mp->geom, geom) {
518227464Smav		sc = gp->softc;
519227464Smav		if (sc == NULL || sc->sc_stopping)
520227464Smav			continue;
521167050Smjacob		if (strcmp(gp->name, md->md_name) == 0) {
522167050Smjacob			printf("GEOM_MULTIPATH: name %s already exists\n",
523167050Smjacob			    md->md_name);
524167050Smjacob			return (NULL);
525167050Smjacob		}
526167050Smjacob	}
527167050Smjacob
528243333Sjh	gp = g_new_geomf(mp, "%s", md->md_name);
529167050Smjacob	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
530227464Smav	mtx_init(&sc->sc_mtx, "multipath", NULL, MTX_DEF);
531227464Smav	memcpy(sc->sc_uuid, md->md_uuid, sizeof (sc->sc_uuid));
532227464Smav	memcpy(sc->sc_name, md->md_name, sizeof (sc->sc_name));
533227464Smav	sc->sc_active_active = md->md_active_active;
534260478Smav	sc->sc_size = md->md_size;
535167050Smjacob	gp->softc = sc;
536167050Smjacob	gp->start = g_multipath_start;
537167050Smjacob	gp->orphan = g_multipath_orphan;
538260478Smav	gp->resize = g_multipath_resize;
539167050Smjacob	gp->access = g_multipath_access;
540227464Smav	gp->dumpconf = g_multipath_dumpconf;
541167050Smjacob
542167050Smjacob	pp = g_new_providerf(gp, "multipath/%s", md->md_name);
543260385Sscottl	pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE;
544227464Smav	if (md->md_size != 0) {
545227464Smav		pp->mediasize = md->md_size -
546227464Smav		    ((md->md_uuid[0] != 0) ? md->md_sectorsize : 0);
547227464Smav		pp->sectorsize = md->md_sectorsize;
548227464Smav	}
549227464Smav	sc->sc_pp = pp;
550167050Smjacob	g_error_provider(pp, 0);
551227464Smav	printf("GEOM_MULTIPATH: %s created\n", gp->name);
552167050Smjacob	return (gp);
553167050Smjacob}
554167050Smjacob
555167050Smjacobstatic int
556167050Smjacobg_multipath_add_disk(struct g_geom *gp, struct g_provider *pp)
557167050Smjacob{
558167050Smjacob	struct g_multipath_softc *sc;
559167050Smjacob	struct g_consumer *cp, *nxtcp;
560227464Smav	int error, acr, acw, ace;
561167050Smjacob
562167050Smjacob	g_topology_assert();
563167050Smjacob
564167050Smjacob	sc = gp->softc;
565167050Smjacob	KASSERT(sc, ("no softc"));
566167050Smjacob
567167050Smjacob	/*
568167050Smjacob	 * Make sure that the passed provider isn't already attached
569167050Smjacob	 */
570167050Smjacob	LIST_FOREACH(cp, &gp->consumer, consumer) {
571204071Spjd		if (cp->provider == pp)
572167050Smjacob			break;
573167050Smjacob	}
574167050Smjacob	if (cp) {
575167050Smjacob		printf("GEOM_MULTIPATH: provider %s already attached to %s\n",
576167050Smjacob		    pp->name, gp->name);
577167050Smjacob		return (EEXIST);
578167050Smjacob	}
579167050Smjacob	nxtcp = LIST_FIRST(&gp->consumer);
580167050Smjacob	cp = g_new_consumer(gp);
581260385Sscottl	cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
582227464Smav	cp->private = NULL;
583227464Smav	cp->index = MP_NEW;
584167050Smjacob	error = g_attach(cp, pp);
585167050Smjacob	if (error != 0) {
586167050Smjacob		printf("GEOM_MULTIPATH: cannot attach %s to %s",
587167050Smjacob		    pp->name, sc->sc_name);
588167050Smjacob		g_destroy_consumer(cp);
589167050Smjacob		return (error);
590167050Smjacob	}
591167050Smjacob
592167050Smjacob	/*
593167050Smjacob	 * Set access permissions on new consumer to match other consumers
594167050Smjacob	 */
595227464Smav	if (sc->sc_pp) {
596227464Smav		acr = sc->sc_pp->acr;
597227464Smav		acw = sc->sc_pp->acw;
598227464Smav		ace = sc->sc_pp->ace;
599227464Smav	} else
600227464Smav		acr = acw = ace = 0;
601227464Smav	if (g_multipath_exclusive) {
602227464Smav		acr++;
603227464Smav		acw++;
604227464Smav		ace++;
605167050Smjacob	}
606227464Smav	error = g_access(cp, acr, acw, ace);
607227464Smav	if (error) {
608227464Smav		printf("GEOM_MULTIPATH: cannot set access in "
609227464Smav		    "attaching %s to %s (%d)\n",
610227464Smav		    pp->name, sc->sc_name, error);
611227464Smav		g_detach(cp);
612227464Smav		g_destroy_consumer(cp);
613227464Smav		return (error);
614167050Smjacob	}
615260478Smav	if (sc->sc_size == 0) {
616260478Smav		sc->sc_size = pp->mediasize -
617227464Smav		    ((sc->sc_uuid[0] != 0) ? pp->sectorsize : 0);
618260478Smav		sc->sc_pp->mediasize = sc->sc_size;
619227464Smav		sc->sc_pp->sectorsize = pp->sectorsize;
620227464Smav	}
621260478Smav	if (sc->sc_pp->stripesize == 0 && sc->sc_pp->stripeoffset == 0) {
622227464Smav		sc->sc_pp->stripesize = pp->stripesize;
623227464Smav		sc->sc_pp->stripeoffset = pp->stripeoffset;
624227464Smav	}
625260478Smav	sc->sc_pp->flags |= pp->flags & G_PF_ACCEPT_UNMAPPED;
626227464Smav	mtx_lock(&sc->sc_mtx);
627227464Smav	cp->index = 0;
628227464Smav	sc->sc_ndisks++;
629227464Smav	mtx_unlock(&sc->sc_mtx);
630227464Smav	printf("GEOM_MULTIPATH: %s added to %s\n",
631227464Smav	    pp->name, sc->sc_name);
632227464Smav	if (sc->sc_active == NULL) {
633227464Smav		sc->sc_active = cp;
634234415Smav		if (sc->sc_active_active != 1)
635227464Smav			printf("GEOM_MULTIPATH: %s is now active path in %s\n",
636227464Smav			    pp->name, sc->sc_name);
637227464Smav	}
638167050Smjacob	return (0);
639167050Smjacob}
640167050Smjacob
641167050Smjacobstatic int
642167050Smjacobg_multipath_destroy(struct g_geom *gp)
643167050Smjacob{
644227464Smav	struct g_multipath_softc *sc;
645227464Smav	struct g_consumer *cp, *cp1;
646167050Smjacob
647167050Smjacob	g_topology_assert();
648204071Spjd	if (gp->softc == NULL)
649167050Smjacob		return (ENXIO);
650227464Smav	sc = gp->softc;
651227464Smav	if (!sc->sc_stopping) {
652227464Smav		printf("GEOM_MULTIPATH: destroying %s\n", gp->name);
653227464Smav		sc->sc_stopping = 1;
654227464Smav	}
655227464Smav	if (sc->sc_opened != 0) {
656260478Smav		g_wither_provider(sc->sc_pp, ENXIO);
657260478Smav		sc->sc_pp = NULL;
658227464Smav		return (EINPROGRESS);
659227464Smav	}
660227464Smav	LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) {
661227464Smav		mtx_lock(&sc->sc_mtx);
662227464Smav		if ((cp->index & MP_POSTED) == 0) {
663227464Smav			cp->index |= MP_POSTED;
664227464Smav			mtx_unlock(&sc->sc_mtx);
665227464Smav			g_mpd(cp, 0);
666227464Smav			if (cp1 == NULL)
667227464Smav				return(0);	/* Recursion happened. */
668227464Smav		} else
669227464Smav			mtx_unlock(&sc->sc_mtx);
670227464Smav	}
671227464Smav	if (!LIST_EMPTY(&gp->consumer))
672227464Smav		return (EINPROGRESS);
673227464Smav	mtx_destroy(&sc->sc_mtx);
674167050Smjacob	g_free(gp->softc);
675167050Smjacob	gp->softc = NULL;
676227464Smav	printf("GEOM_MULTIPATH: %s destroyed\n", gp->name);
677167050Smjacob	g_wither_geom(gp, ENXIO);
678167050Smjacob	return (0);
679167050Smjacob}
680167050Smjacob
681167050Smjacobstatic int
682167050Smjacobg_multipath_destroy_geom(struct gctl_req *req, struct g_class *mp,
683167050Smjacob    struct g_geom *gp)
684167050Smjacob{
685204071Spjd
686167050Smjacob	return (g_multipath_destroy(gp));
687167050Smjacob}
688167050Smjacob
689205412Smjacobstatic int
690205412Smjacobg_multipath_rotate(struct g_geom *gp)
691205412Smjacob{
692239673Sthomas	struct g_consumer *lcp, *first_good_cp = NULL;
693205412Smjacob	struct g_multipath_softc *sc = gp->softc;
694239673Sthomas	int active_cp_seen = 0;
695205412Smjacob
696205412Smjacob	g_topology_assert();
697205412Smjacob	if (sc == NULL)
698205412Smjacob		return (ENXIO);
699205412Smjacob	LIST_FOREACH(lcp, &gp->consumer, consumer) {
700205412Smjacob		if ((lcp->index & MP_BAD) == 0) {
701239673Sthomas			if (first_good_cp == NULL)
702239673Sthomas				first_good_cp = lcp;
703239673Sthomas			if (active_cp_seen)
704205412Smjacob				break;
705205412Smjacob		}
706239673Sthomas		if (sc->sc_active == lcp)
707239673Sthomas			active_cp_seen = 1;
708205412Smjacob	}
709239673Sthomas	if (lcp == NULL)
710239673Sthomas		lcp = first_good_cp;
711239673Sthomas	if (lcp && lcp != sc->sc_active) {
712227464Smav		sc->sc_active = lcp;
713234415Smav		if (sc->sc_active_active != 1)
714227464Smav			printf("GEOM_MULTIPATH: %s is now active path in %s\n",
715227464Smav			    lcp->provider->name, sc->sc_name);
716205412Smjacob	}
717205412Smjacob	return (0);
718205412Smjacob}
719205412Smjacob
720167050Smjacobstatic void
721167050Smjacobg_multipath_init(struct g_class *mp)
722167050Smjacob{
723167050Smjacob	bioq_init(&gmtbq);
724167050Smjacob	mtx_init(&gmtbq_mtx, "gmtbq", NULL, MTX_DEF);
725234415Smav	kproc_create(g_multipath_kt, mp, NULL, 0, 0, "g_mp_kt");
726167050Smjacob}
727167050Smjacob
728167050Smjacobstatic void
729167050Smjacobg_multipath_fini(struct g_class *mp)
730167050Smjacob{
731167050Smjacob	if (g_multipath_kt_state == GKT_RUN) {
732167050Smjacob		mtx_lock(&gmtbq_mtx);
733167050Smjacob		g_multipath_kt_state = GKT_DIE;
734167050Smjacob		wakeup(&g_multipath_kt_state);
735167050Smjacob		msleep(&g_multipath_kt_state, &gmtbq_mtx, PRIBIO,
736167050Smjacob		    "gmp:fini", 0);
737167050Smjacob		mtx_unlock(&gmtbq_mtx);
738167050Smjacob	}
739167050Smjacob}
740167050Smjacob
741167050Smjacobstatic int
742167050Smjacobg_multipath_read_metadata(struct g_consumer *cp,
743167050Smjacob    struct g_multipath_metadata *md)
744167050Smjacob{
745167050Smjacob	struct g_provider *pp;
746167050Smjacob	u_char *buf;
747167050Smjacob	int error;
748167050Smjacob
749167050Smjacob	g_topology_assert();
750167050Smjacob	error = g_access(cp, 1, 0, 0);
751204071Spjd	if (error != 0)
752167050Smjacob		return (error);
753167050Smjacob	pp = cp->provider;
754167050Smjacob	g_topology_unlock();
755167050Smjacob	buf = g_read_data(cp, pp->mediasize - pp->sectorsize,
756167050Smjacob	    pp->sectorsize, &error);
757167050Smjacob	g_topology_lock();
758167050Smjacob	g_access(cp, -1, 0, 0);
759204071Spjd	if (buf == NULL)
760167050Smjacob		return (error);
761167050Smjacob	multipath_metadata_decode(buf, md);
762167050Smjacob	g_free(buf);
763167050Smjacob	return (0);
764167050Smjacob}
765167050Smjacob
766260478Smavstatic int
767260478Smavg_multipath_write_metadata(struct g_consumer *cp,
768260478Smav    struct g_multipath_metadata *md)
769260478Smav{
770260478Smav	struct g_provider *pp;
771260478Smav	u_char *buf;
772260478Smav	int error;
773260478Smav
774260478Smav	g_topology_assert();
775260478Smav	error = g_access(cp, 1, 1, 1);
776260478Smav	if (error != 0)
777260478Smav		return (error);
778260478Smav	pp = cp->provider;
779260478Smav	g_topology_unlock();
780260478Smav	buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
781260478Smav	multipath_metadata_encode(md, buf);
782260478Smav	error = g_write_data(cp, pp->mediasize - pp->sectorsize,
783260478Smav	    buf, pp->sectorsize);
784260478Smav	g_topology_lock();
785260478Smav	g_access(cp, -1, -1, -1);
786260478Smav	g_free(buf);
787260478Smav	return (error);
788260478Smav}
789260478Smav
790167050Smjacobstatic struct g_geom *
791167050Smjacobg_multipath_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
792167050Smjacob{
793167050Smjacob	struct g_multipath_metadata md;
794167050Smjacob	struct g_multipath_softc *sc;
795167050Smjacob	struct g_consumer *cp;
796167050Smjacob	struct g_geom *gp, *gp1;
797167050Smjacob	int error, isnew;
798167050Smjacob
799167050Smjacob	g_topology_assert();
800167050Smjacob
801167050Smjacob	gp = g_new_geomf(mp, "multipath:taste");
802167050Smjacob	gp->start = g_multipath_start;
803167050Smjacob	gp->access = g_multipath_access;
804167050Smjacob	gp->orphan = g_multipath_orphan;
805167050Smjacob	cp = g_new_consumer(gp);
806167050Smjacob	g_attach(cp, pp);
807167050Smjacob	error = g_multipath_read_metadata(cp, &md);
808167050Smjacob	g_detach(cp);
809167050Smjacob	g_destroy_consumer(cp);
810167050Smjacob	g_destroy_geom(gp);
811204071Spjd	if (error != 0)
812167050Smjacob		return (NULL);
813167050Smjacob	gp = NULL;
814167050Smjacob
815167050Smjacob	if (strcmp(md.md_magic, G_MULTIPATH_MAGIC) != 0) {
816204071Spjd		if (g_multipath_debug)
817167050Smjacob			printf("%s is not MULTIPATH\n", pp->name);
818167050Smjacob		return (NULL);
819167050Smjacob	}
820167050Smjacob	if (md.md_version != G_MULTIPATH_VERSION) {
821167050Smjacob		printf("%s has version %d multipath id- this module is version "
822167050Smjacob		    " %d: rejecting\n", pp->name, md.md_version,
823167050Smjacob		    G_MULTIPATH_VERSION);
824167050Smjacob		return (NULL);
825167050Smjacob	}
826227464Smav	if (md.md_size != 0 && md.md_size != pp->mediasize)
827227464Smav		return (NULL);
828227464Smav	if (md.md_sectorsize != 0 && md.md_sectorsize != pp->sectorsize)
829227464Smav		return (NULL);
830204071Spjd	if (g_multipath_debug)
831167050Smjacob		printf("MULTIPATH: %s/%s\n", md.md_name, md.md_uuid);
832167050Smjacob
833167050Smjacob	/*
834167050Smjacob	 * Let's check if such a device already is present. We check against
835167050Smjacob	 * uuid alone first because that's the true distinguishor. If that
836167050Smjacob	 * passes, then we check for name conflicts. If there are conflicts,
837167050Smjacob	 * modify the name.
838167050Smjacob	 *
839167050Smjacob	 * The whole purpose of this is to solve the problem that people don't
840167050Smjacob	 * pick good unique names, but good unique names (like uuids) are a
841167050Smjacob	 * pain to use. So, we allow people to build GEOMs with friendly names
842167050Smjacob	 * and uuids, and modify the names in case there's a collision.
843167050Smjacob	 */
844167050Smjacob	sc = NULL;
845167050Smjacob	LIST_FOREACH(gp, &mp->geom, geom) {
846167050Smjacob		sc = gp->softc;
847227464Smav		if (sc == NULL || sc->sc_stopping)
848167050Smjacob			continue;
849204071Spjd		if (strncmp(md.md_uuid, sc->sc_uuid, sizeof(md.md_uuid)) == 0)
850167050Smjacob			break;
851167050Smjacob	}
852167050Smjacob
853167050Smjacob	LIST_FOREACH(gp1, &mp->geom, geom) {
854204071Spjd		if (gp1 == gp)
855167050Smjacob			continue;
856167050Smjacob		sc = gp1->softc;
857227464Smav		if (sc == NULL || sc->sc_stopping)
858167050Smjacob			continue;
859204071Spjd		if (strncmp(md.md_name, sc->sc_name, sizeof(md.md_name)) == 0)
860167050Smjacob			break;
861167050Smjacob	}
862167050Smjacob
863167050Smjacob	/*
864167050Smjacob	 * If gp is NULL, we had no extant MULTIPATH geom with this uuid.
865167050Smjacob	 *
866167050Smjacob	 * If gp1 is *not* NULL, that means we have a MULTIPATH geom extant
867167050Smjacob	 * with the same name (but a different UUID).
868167050Smjacob	 *
869167050Smjacob	 * If gp is NULL, then modify the name with a random number and
870167050Smjacob  	 * complain, but allow the creation of the geom to continue.
871167050Smjacob	 *
872167050Smjacob	 * If gp is *not* NULL, just use the geom's name as we're attaching
873167050Smjacob	 * this disk to the (previously generated) name.
874167050Smjacob	 */
875167050Smjacob
876167050Smjacob	if (gp1) {
877167050Smjacob		sc = gp1->softc;
878167050Smjacob		if (gp == NULL) {
879167050Smjacob			char buf[16];
880167050Smjacob			u_long rand = random();
881167050Smjacob
882167050Smjacob			snprintf(buf, sizeof (buf), "%s-%lu", md.md_name, rand);
883167050Smjacob			printf("GEOM_MULTIPATH: geom %s/%s exists already\n",
884167050Smjacob			    sc->sc_name, sc->sc_uuid);
885167050Smjacob			printf("GEOM_MULTIPATH: %s will be (temporarily) %s\n",
886167050Smjacob			    md.md_uuid, buf);
887204071Spjd			strlcpy(md.md_name, buf, sizeof(md.md_name));
888167050Smjacob		} else {
889204071Spjd			strlcpy(md.md_name, sc->sc_name, sizeof(md.md_name));
890167050Smjacob		}
891167050Smjacob	}
892167050Smjacob
893167050Smjacob	if (gp == NULL) {
894167050Smjacob		gp = g_multipath_create(mp, &md);
895167050Smjacob		if (gp == NULL) {
896167050Smjacob			printf("GEOM_MULTIPATH: cannot create geom %s/%s\n",
897167050Smjacob			    md.md_name, md.md_uuid);
898167050Smjacob			return (NULL);
899167050Smjacob		}
900167050Smjacob		isnew = 1;
901167050Smjacob	} else {
902167050Smjacob		isnew = 0;
903167050Smjacob	}
904167050Smjacob
905167050Smjacob	sc = gp->softc;
906167050Smjacob	KASSERT(sc != NULL, ("sc is NULL"));
907167050Smjacob	error = g_multipath_add_disk(gp, pp);
908167050Smjacob	if (error != 0) {
909204071Spjd		if (isnew)
910167050Smjacob			g_multipath_destroy(gp);
911167050Smjacob		return (NULL);
912167050Smjacob	}
913167050Smjacob	return (gp);
914167050Smjacob}
915167050Smjacob
916167050Smjacobstatic void
917227464Smavg_multipath_ctl_add_name(struct gctl_req *req, struct g_class *mp,
918227464Smav    const char *name)
919167050Smjacob{
920227464Smav	struct g_multipath_softc *sc;
921167050Smjacob	struct g_geom *gp;
922205847Smjacob	struct g_consumer *cp;
923227464Smav	struct g_provider *pp;
924227464Smav	const char *mpname;
925167050Smjacob	static const char devpf[6] = "/dev/";
926167050Smjacob
927167050Smjacob	g_topology_assert();
928167050Smjacob
929167050Smjacob	mpname = gctl_get_asciiparam(req, "arg0");
930167050Smjacob        if (mpname == NULL) {
931167050Smjacob                gctl_error(req, "No 'arg0' argument");
932167050Smjacob                return;
933167050Smjacob        }
934205847Smjacob	gp = g_multipath_find_geom(mp, mpname);
935205847Smjacob	if (gp == NULL) {
936205847Smjacob		gctl_error(req, "Device %s is invalid", mpname);
937167050Smjacob		return;
938167050Smjacob	}
939227464Smav	sc = gp->softc;
940167050Smjacob
941204071Spjd	if (strncmp(name, devpf, 5) == 0)
942167050Smjacob		name += 5;
943205847Smjacob	pp = g_provider_by_name(name);
944205847Smjacob	if (pp == NULL) {
945167050Smjacob		gctl_error(req, "Provider %s is invalid", name);
946167050Smjacob		return;
947167050Smjacob	}
948167050Smjacob
949167050Smjacob	/*
950227464Smav	 * Check to make sure parameters match.
951167050Smjacob	 */
952227464Smav	LIST_FOREACH(cp, &gp->consumer, consumer) {
953227464Smav		if (cp->provider == pp) {
954227464Smav			gctl_error(req, "provider %s is already there",
955227464Smav			    pp->name);
956205847Smjacob			return;
957205847Smjacob		}
958167050Smjacob	}
959260478Smav	if (sc->sc_pp->mediasize != 0 &&
960227464Smav	    sc->sc_pp->mediasize + (sc->sc_uuid[0] != 0 ? pp->sectorsize : 0)
961227464Smav	     != pp->mediasize) {
962227464Smav		gctl_error(req, "Providers size mismatch %jd != %jd",
963227464Smav		    (intmax_t) sc->sc_pp->mediasize +
964227464Smav			(sc->sc_uuid[0] != 0 ? pp->sectorsize : 0),
965227464Smav		    (intmax_t) pp->mediasize);
966227464Smav		return;
967227464Smav	}
968260478Smav	if (sc->sc_pp->sectorsize != 0 &&
969227464Smav	    sc->sc_pp->sectorsize != pp->sectorsize) {
970227464Smav		gctl_error(req, "Providers sectorsize mismatch %u != %u",
971227464Smav		    sc->sc_pp->sectorsize, pp->sectorsize);
972227464Smav		return;
973227464Smav	}
974167050Smjacob
975167050Smjacob	/*
976205847Smjacob	 * Now add....
977167050Smjacob	 */
978205847Smjacob	(void) g_multipath_add_disk(gp, pp);
979167050Smjacob}
980167050Smjacob
981227464Smavstatic void
982239012Sthomasg_multipath_ctl_prefer(struct gctl_req *req, struct g_class *mp)
983239012Sthomas{
984239012Sthomas	struct g_geom *gp;
985239012Sthomas	struct g_multipath_softc *sc;
986239012Sthomas	struct g_consumer *cp;
987239012Sthomas	const char *name, *mpname;
988239012Sthomas	static const char devpf[6] = "/dev/";
989239012Sthomas	int *nargs;
990239012Sthomas
991239012Sthomas	g_topology_assert();
992239012Sthomas
993239012Sthomas	mpname = gctl_get_asciiparam(req, "arg0");
994239012Sthomas        if (mpname == NULL) {
995239012Sthomas                gctl_error(req, "No 'arg0' argument");
996239012Sthomas                return;
997239012Sthomas        }
998239012Sthomas	gp = g_multipath_find_geom(mp, mpname);
999239012Sthomas	if (gp == NULL) {
1000239012Sthomas		gctl_error(req, "Device %s is invalid", mpname);
1001239012Sthomas		return;
1002239012Sthomas	}
1003239012Sthomas	sc = gp->softc;
1004239012Sthomas
1005239012Sthomas	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
1006239012Sthomas	if (nargs == NULL) {
1007239012Sthomas		gctl_error(req, "No 'nargs' argument");
1008239012Sthomas		return;
1009239012Sthomas	}
1010239012Sthomas	if (*nargs != 2) {
1011239012Sthomas		gctl_error(req, "missing device");
1012239012Sthomas		return;
1013239012Sthomas	}
1014239012Sthomas
1015239012Sthomas	name = gctl_get_asciiparam(req, "arg1");
1016239012Sthomas	if (name == NULL) {
1017239012Sthomas		gctl_error(req, "No 'arg1' argument");
1018239012Sthomas		return;
1019239012Sthomas	}
1020239012Sthomas	if (strncmp(name, devpf, 5) == 0) {
1021239012Sthomas		name += 5;
1022239012Sthomas	}
1023239012Sthomas
1024239012Sthomas	LIST_FOREACH(cp, &gp->consumer, consumer) {
1025239012Sthomas		if (cp->provider != NULL
1026239012Sthomas                      && strcmp(cp->provider->name, name) == 0)
1027239012Sthomas		    break;
1028239012Sthomas	}
1029239012Sthomas
1030239012Sthomas	if (cp == NULL) {
1031239012Sthomas		gctl_error(req, "Provider %s not found", name);
1032239012Sthomas		return;
1033239012Sthomas	}
1034239012Sthomas
1035239012Sthomas	mtx_lock(&sc->sc_mtx);
1036239012Sthomas
1037239012Sthomas	if (cp->index & MP_BAD) {
1038239012Sthomas		gctl_error(req, "Consumer %s is invalid", name);
1039239012Sthomas		mtx_unlock(&sc->sc_mtx);
1040239012Sthomas		return;
1041239012Sthomas	}
1042239012Sthomas
1043239012Sthomas	/* Here when the consumer is present and in good shape */
1044239012Sthomas
1045239012Sthomas	sc->sc_active = cp;
1046239012Sthomas	if (!sc->sc_active_active)
1047239012Sthomas	    printf("GEOM_MULTIPATH: %s now active path in %s\n",
1048239012Sthomas		sc->sc_active->provider->name, sc->sc_name);
1049239012Sthomas
1050239012Sthomas	mtx_unlock(&sc->sc_mtx);
1051239012Sthomas}
1052239012Sthomas
1053239012Sthomasstatic void
1054227464Smavg_multipath_ctl_add(struct gctl_req *req, struct g_class *mp)
1055227464Smav{
1056227464Smav	struct g_multipath_softc *sc;
1057227464Smav	struct g_geom *gp;
1058227464Smav	const char *mpname, *name;
1059227464Smav
1060227464Smav	mpname = gctl_get_asciiparam(req, "arg0");
1061227464Smav        if (mpname == NULL) {
1062227464Smav                gctl_error(req, "No 'arg0' argument");
1063227464Smav                return;
1064227464Smav        }
1065227464Smav	gp = g_multipath_find_geom(mp, mpname);
1066227464Smav	if (gp == NULL) {
1067227464Smav		gctl_error(req, "Device %s not found", mpname);
1068227464Smav		return;
1069227464Smav	}
1070227464Smav	sc = gp->softc;
1071227464Smav
1072227464Smav	name = gctl_get_asciiparam(req, "arg1");
1073227464Smav	if (name == NULL) {
1074227464Smav		gctl_error(req, "No 'arg1' argument");
1075227464Smav		return;
1076227464Smav	}
1077227464Smav	g_multipath_ctl_add_name(req, mp, name);
1078227464Smav}
1079227464Smav
1080227464Smavstatic void
1081227464Smavg_multipath_ctl_create(struct gctl_req *req, struct g_class *mp)
1082227464Smav{
1083227464Smav	struct g_multipath_metadata md;
1084227464Smav	struct g_multipath_softc *sc;
1085227464Smav	struct g_geom *gp;
1086227464Smav	const char *mpname, *name;
1087227464Smav	char param[16];
1088234415Smav	int *nargs, i, *val;
1089227464Smav
1090227464Smav	g_topology_assert();
1091227464Smav
1092227464Smav	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
1093227464Smav	if (*nargs < 2) {
1094227464Smav		gctl_error(req, "wrong number of arguments.");
1095227464Smav		return;
1096227464Smav	}
1097227464Smav
1098227464Smav	mpname = gctl_get_asciiparam(req, "arg0");
1099227464Smav        if (mpname == NULL) {
1100227464Smav                gctl_error(req, "No 'arg0' argument");
1101227464Smav                return;
1102227464Smav        }
1103227464Smav	gp = g_multipath_find_geom(mp, mpname);
1104227464Smav	if (gp != NULL) {
1105227464Smav		gctl_error(req, "Device %s already exist", mpname);
1106227464Smav		return;
1107227464Smav	}
1108227464Smav
1109227464Smav	memset(&md, 0, sizeof(md));
1110227464Smav	strlcpy(md.md_magic, G_MULTIPATH_MAGIC, sizeof(md.md_magic));
1111227464Smav	md.md_version = G_MULTIPATH_VERSION;
1112227464Smav	strlcpy(md.md_name, mpname, sizeof(md.md_name));
1113227464Smav	md.md_size = 0;
1114227464Smav	md.md_sectorsize = 0;
1115227464Smav	md.md_uuid[0] = 0;
1116234415Smav	md.md_active_active = 0;
1117234415Smav	val = gctl_get_paraml(req, "active_active", sizeof(*val));
1118234415Smav	if (val != NULL && *val != 0)
1119234415Smav		md.md_active_active = 1;
1120234415Smav	val = gctl_get_paraml(req, "active_read", sizeof(*val));
1121234415Smav	if (val != NULL && *val != 0)
1122234415Smav		md.md_active_active = 2;
1123227464Smav	gp = g_multipath_create(mp, &md);
1124227464Smav	if (gp == NULL) {
1125227464Smav		gctl_error(req, "GEOM_MULTIPATH: cannot create geom %s/%s\n",
1126227464Smav		    md.md_name, md.md_uuid);
1127227464Smav		return;
1128227464Smav	}
1129227464Smav	sc = gp->softc;
1130227464Smav
1131227464Smav	for (i = 1; i < *nargs; i++) {
1132227464Smav		snprintf(param, sizeof(param), "arg%d", i);
1133227464Smav		name = gctl_get_asciiparam(req, param);
1134227464Smav		g_multipath_ctl_add_name(req, mp, name);
1135227464Smav	}
1136227464Smav
1137227464Smav	if (sc->sc_ndisks != (*nargs - 1))
1138227464Smav		g_multipath_destroy(gp);
1139227464Smav}
1140227464Smav
1141227464Smavstatic void
1142234415Smavg_multipath_ctl_configure(struct gctl_req *req, struct g_class *mp)
1143234415Smav{
1144234415Smav	struct g_multipath_softc *sc;
1145234415Smav	struct g_geom *gp;
1146234415Smav	struct g_consumer *cp;
1147234415Smav	struct g_provider *pp;
1148235069Smav	struct g_multipath_metadata md;
1149234415Smav	const char *name;
1150234415Smav	int error, *val;
1151234415Smav
1152234415Smav	g_topology_assert();
1153234415Smav
1154234415Smav	name = gctl_get_asciiparam(req, "arg0");
1155234415Smav	if (name == NULL) {
1156234415Smav		gctl_error(req, "No 'arg0' argument");
1157234415Smav		return;
1158234415Smav	}
1159234415Smav	gp = g_multipath_find_geom(mp, name);
1160234415Smav	if (gp == NULL) {
1161234415Smav		gctl_error(req, "Device %s is invalid", name);
1162234415Smav		return;
1163234415Smav	}
1164234415Smav	sc = gp->softc;
1165234415Smav	val = gctl_get_paraml(req, "active_active", sizeof(*val));
1166234415Smav	if (val != NULL && *val != 0)
1167234415Smav		sc->sc_active_active = 1;
1168234415Smav	val = gctl_get_paraml(req, "active_read", sizeof(*val));
1169234415Smav	if (val != NULL && *val != 0)
1170234415Smav		sc->sc_active_active = 2;
1171234415Smav	val = gctl_get_paraml(req, "active_passive", sizeof(*val));
1172234415Smav	if (val != NULL && *val != 0)
1173234415Smav		sc->sc_active_active = 0;
1174234415Smav	if (sc->sc_uuid[0] != 0 && sc->sc_active != NULL) {
1175234415Smav		cp = sc->sc_active;
1176234415Smav		pp = cp->provider;
1177235069Smav		strlcpy(md.md_magic, G_MULTIPATH_MAGIC, sizeof(md.md_magic));
1178235069Smav		memcpy(md.md_uuid, sc->sc_uuid, sizeof (sc->sc_uuid));
1179235069Smav		strlcpy(md.md_name, name, sizeof(md.md_name));
1180235069Smav		md.md_version = G_MULTIPATH_VERSION;
1181235069Smav		md.md_size = pp->mediasize;
1182235069Smav		md.md_sectorsize = pp->sectorsize;
1183235069Smav		md.md_active_active = sc->sc_active_active;
1184260478Smav		error = g_multipath_write_metadata(cp, &md);
1185234415Smav		if (error != 0)
1186234415Smav			gctl_error(req, "Can't update metadata on %s (%d)",
1187234415Smav			    pp->name, error);
1188234415Smav	}
1189234415Smav}
1190234415Smav
1191234415Smavstatic void
1192227464Smavg_multipath_ctl_fail(struct gctl_req *req, struct g_class *mp, int fail)
1193227464Smav{
1194227464Smav	struct g_multipath_softc *sc;
1195227464Smav	struct g_geom *gp;
1196227464Smav	struct g_consumer *cp;
1197227464Smav	const char *mpname, *name;
1198227464Smav	int found;
1199227464Smav
1200227464Smav	mpname = gctl_get_asciiparam(req, "arg0");
1201227464Smav        if (mpname == NULL) {
1202227464Smav                gctl_error(req, "No 'arg0' argument");
1203227464Smav                return;
1204227464Smav        }
1205227464Smav	gp = g_multipath_find_geom(mp, mpname);
1206227464Smav	if (gp == NULL) {
1207227464Smav		gctl_error(req, "Device %s not found", mpname);
1208227464Smav		return;
1209227464Smav	}
1210227464Smav	sc = gp->softc;
1211227464Smav
1212227464Smav	name = gctl_get_asciiparam(req, "arg1");
1213227464Smav	if (name == NULL) {
1214227464Smav		gctl_error(req, "No 'arg1' argument");
1215227464Smav		return;
1216227464Smav	}
1217227464Smav
1218227464Smav	found = 0;
1219227464Smav	mtx_lock(&sc->sc_mtx);
1220227464Smav	LIST_FOREACH(cp, &gp->consumer, consumer) {
1221227464Smav		if (cp->provider != NULL &&
1222227464Smav		    strcmp(cp->provider->name, name) == 0 &&
1223227464Smav		    (cp->index & MP_LOST) == 0) {
1224227464Smav			found = 1;
1225234415Smav			if (!fail == !(cp->index & MP_FAIL))
1226234415Smav				continue;
1227227464Smav			printf("GEOM_MULTIPATH: %s in %s is marked %s.\n",
1228227464Smav				name, sc->sc_name, fail ? "FAIL" : "OK");
1229227464Smav			if (fail) {
1230227464Smav				g_multipath_fault(cp, MP_FAIL);
1231227464Smav			} else {
1232227464Smav				cp->index &= ~MP_FAIL;
1233227464Smav			}
1234227464Smav		}
1235227464Smav	}
1236227464Smav	mtx_unlock(&sc->sc_mtx);
1237227464Smav	if (found == 0)
1238227464Smav		gctl_error(req, "Provider %s not found", name);
1239227464Smav}
1240227464Smav
1241227464Smavstatic void
1242227464Smavg_multipath_ctl_remove(struct gctl_req *req, struct g_class *mp)
1243227464Smav{
1244227464Smav	struct g_multipath_softc *sc;
1245227464Smav	struct g_geom *gp;
1246227464Smav	struct g_consumer *cp, *cp1;
1247227464Smav	const char *mpname, *name;
1248227464Smav	uintptr_t *cnt;
1249227464Smav	int found;
1250227464Smav
1251227464Smav	mpname = gctl_get_asciiparam(req, "arg0");
1252227464Smav        if (mpname == NULL) {
1253227464Smav                gctl_error(req, "No 'arg0' argument");
1254227464Smav                return;
1255227464Smav        }
1256227464Smav	gp = g_multipath_find_geom(mp, mpname);
1257227464Smav	if (gp == NULL) {
1258227464Smav		gctl_error(req, "Device %s not found", mpname);
1259227464Smav		return;
1260227464Smav	}
1261227464Smav	sc = gp->softc;
1262227464Smav
1263227464Smav	name = gctl_get_asciiparam(req, "arg1");
1264227464Smav	if (name == NULL) {
1265227464Smav		gctl_error(req, "No 'arg1' argument");
1266227464Smav		return;
1267227464Smav	}
1268227464Smav
1269227464Smav	found = 0;
1270227464Smav	mtx_lock(&sc->sc_mtx);
1271227464Smav	LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) {
1272227464Smav		if (cp->provider != NULL &&
1273227464Smav		    strcmp(cp->provider->name, name) == 0 &&
1274227464Smav		    (cp->index & MP_LOST) == 0) {
1275227464Smav			found = 1;
1276227464Smav			printf("GEOM_MULTIPATH: removing %s from %s\n",
1277227464Smav			    cp->provider->name, cp->geom->name);
1278227464Smav			sc->sc_ndisks--;
1279227464Smav			g_multipath_fault(cp, MP_LOST);
1280227464Smav			cnt = (uintptr_t *)&cp->private;
1281227464Smav			if (*cnt == 0 && (cp->index & MP_POSTED) == 0) {
1282227464Smav				cp->index |= MP_POSTED;
1283227464Smav				mtx_unlock(&sc->sc_mtx);
1284227464Smav				g_mpd(cp, 0);
1285227464Smav				if (cp1 == NULL)
1286227464Smav					return;	/* Recursion happened. */
1287227464Smav				mtx_lock(&sc->sc_mtx);
1288227464Smav			}
1289227464Smav		}
1290227464Smav	}
1291227464Smav	mtx_unlock(&sc->sc_mtx);
1292227464Smav	if (found == 0)
1293227464Smav		gctl_error(req, "Provider %s not found", name);
1294227464Smav}
1295227464Smav
1296167050Smjacobstatic struct g_geom *
1297167050Smjacobg_multipath_find_geom(struct g_class *mp, const char *name)
1298167050Smjacob{
1299167050Smjacob	struct g_geom *gp;
1300227464Smav	struct g_multipath_softc *sc;
1301167050Smjacob
1302167050Smjacob	LIST_FOREACH(gp, &mp->geom, geom) {
1303227464Smav		sc = gp->softc;
1304227464Smav		if (sc == NULL || sc->sc_stopping)
1305227464Smav			continue;
1306227464Smav		if (strcmp(gp->name, name) == 0)
1307167050Smjacob			return (gp);
1308167050Smjacob	}
1309167050Smjacob	return (NULL);
1310167050Smjacob}
1311167050Smjacob
1312167050Smjacobstatic void
1313227464Smavg_multipath_ctl_stop(struct gctl_req *req, struct g_class *mp)
1314227464Smav{
1315227464Smav	struct g_geom *gp;
1316227464Smav	const char *name;
1317227464Smav	int error;
1318227464Smav
1319227464Smav	g_topology_assert();
1320227464Smav
1321227464Smav	name = gctl_get_asciiparam(req, "arg0");
1322227464Smav        if (name == NULL) {
1323227464Smav                gctl_error(req, "No 'arg0' argument");
1324227464Smav                return;
1325227464Smav        }
1326227464Smav	gp = g_multipath_find_geom(mp, name);
1327227464Smav	if (gp == NULL) {
1328227464Smav		gctl_error(req, "Device %s is invalid", name);
1329227464Smav		return;
1330227464Smav	}
1331227464Smav	error = g_multipath_destroy(gp);
1332227464Smav	if (error != 0 && error != EINPROGRESS)
1333227464Smav		gctl_error(req, "failed to stop %s (err=%d)", name, error);
1334227464Smav}
1335227464Smav
1336227464Smavstatic void
1337167050Smjacobg_multipath_ctl_destroy(struct gctl_req *req, struct g_class *mp)
1338167050Smjacob{
1339167050Smjacob	struct g_geom *gp;
1340227464Smav	struct g_multipath_softc *sc;
1341227464Smav	struct g_consumer *cp;
1342227464Smav	struct g_provider *pp;
1343167050Smjacob	const char *name;
1344227464Smav	uint8_t *buf;
1345167050Smjacob	int error;
1346167050Smjacob
1347167050Smjacob	g_topology_assert();
1348167050Smjacob
1349167050Smjacob	name = gctl_get_asciiparam(req, "arg0");
1350167050Smjacob        if (name == NULL) {
1351167050Smjacob                gctl_error(req, "No 'arg0' argument");
1352167050Smjacob                return;
1353167050Smjacob        }
1354167050Smjacob	gp = g_multipath_find_geom(mp, name);
1355167050Smjacob	if (gp == NULL) {
1356167050Smjacob		gctl_error(req, "Device %s is invalid", name);
1357167050Smjacob		return;
1358167050Smjacob	}
1359227464Smav	sc = gp->softc;
1360227464Smav
1361227464Smav	if (sc->sc_uuid[0] != 0 && sc->sc_active != NULL) {
1362227464Smav		cp = sc->sc_active;
1363227464Smav		pp = cp->provider;
1364227464Smav		error = g_access(cp, 1, 1, 1);
1365227464Smav		if (error != 0) {
1366227464Smav			gctl_error(req, "Can't open %s (%d)", pp->name, error);
1367227464Smav			goto destroy;
1368227464Smav		}
1369227464Smav		g_topology_unlock();
1370227464Smav		buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
1371227464Smav		error = g_write_data(cp, pp->mediasize - pp->sectorsize,
1372227464Smav		    buf, pp->sectorsize);
1373227464Smav		g_topology_lock();
1374227464Smav		g_access(cp, -1, -1, -1);
1375227464Smav		if (error != 0)
1376227464Smav			gctl_error(req, "Can't erase metadata on %s (%d)",
1377227464Smav			    pp->name, error);
1378227464Smav	}
1379227464Smav
1380227464Smavdestroy:
1381167050Smjacob	error = g_multipath_destroy(gp);
1382227464Smav	if (error != 0 && error != EINPROGRESS)
1383167050Smjacob		gctl_error(req, "failed to destroy %s (err=%d)", name, error);
1384167050Smjacob}
1385167050Smjacob
1386167050Smjacobstatic void
1387205412Smjacobg_multipath_ctl_rotate(struct gctl_req *req, struct g_class *mp)
1388205412Smjacob{
1389205412Smjacob	struct g_geom *gp;
1390205412Smjacob	const char *name;
1391205412Smjacob	int error;
1392205412Smjacob
1393205412Smjacob	g_topology_assert();
1394205412Smjacob
1395205412Smjacob	name = gctl_get_asciiparam(req, "arg0");
1396205412Smjacob        if (name == NULL) {
1397205412Smjacob                gctl_error(req, "No 'arg0' argument");
1398205412Smjacob                return;
1399205412Smjacob        }
1400205412Smjacob	gp = g_multipath_find_geom(mp, name);
1401205412Smjacob	if (gp == NULL) {
1402205412Smjacob		gctl_error(req, "Device %s is invalid", name);
1403205412Smjacob		return;
1404205412Smjacob	}
1405205412Smjacob	error = g_multipath_rotate(gp);
1406205412Smjacob	if (error != 0) {
1407205412Smjacob		gctl_error(req, "failed to rotate %s (err=%d)", name, error);
1408205412Smjacob	}
1409205412Smjacob}
1410205412Smjacob
1411205412Smjacobstatic void
1412205412Smjacobg_multipath_ctl_getactive(struct gctl_req *req, struct g_class *mp)
1413205412Smjacob{
1414205412Smjacob	struct sbuf *sb;
1415205412Smjacob	struct g_geom *gp;
1416205412Smjacob	struct g_multipath_softc *sc;
1417227464Smav	struct g_consumer *cp;
1418205412Smjacob	const char *name;
1419227464Smav	int empty;
1420205412Smjacob
1421205412Smjacob	sb = sbuf_new_auto();
1422205412Smjacob
1423205412Smjacob	g_topology_assert();
1424205412Smjacob	name = gctl_get_asciiparam(req, "arg0");
1425205412Smjacob        if (name == NULL) {
1426205412Smjacob                gctl_error(req, "No 'arg0' argument");
1427205412Smjacob                return;
1428205412Smjacob        }
1429205412Smjacob	gp = g_multipath_find_geom(mp, name);
1430205412Smjacob	if (gp == NULL) {
1431205412Smjacob		gctl_error(req, "Device %s is invalid", name);
1432205412Smjacob		return;
1433205412Smjacob	}
1434205412Smjacob	sc = gp->softc;
1435234415Smav	if (sc->sc_active_active == 1) {
1436227464Smav		empty = 1;
1437227464Smav		LIST_FOREACH(cp, &gp->consumer, consumer) {
1438227464Smav			if (cp->index & MP_BAD)
1439227464Smav				continue;
1440227464Smav			if (!empty)
1441227464Smav				sbuf_cat(sb, " ");
1442227464Smav			sbuf_cat(sb, cp->provider->name);
1443227464Smav			empty = 0;
1444227464Smav		}
1445227464Smav		if (empty)
1446227464Smav			sbuf_cat(sb, "none");
1447227464Smav		sbuf_cat(sb, "\n");
1448227464Smav	} else if (sc->sc_active && sc->sc_active->provider) {
1449227464Smav		sbuf_printf(sb, "%s\n", sc->sc_active->provider->name);
1450205412Smjacob	} else {
1451205412Smjacob		sbuf_printf(sb, "none\n");
1452205412Smjacob	}
1453205412Smjacob	sbuf_finish(sb);
1454205412Smjacob	gctl_set_param_err(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1455205412Smjacob	sbuf_delete(sb);
1456205412Smjacob}
1457205412Smjacob
1458205412Smjacobstatic void
1459167050Smjacobg_multipath_config(struct gctl_req *req, struct g_class *mp, const char *verb)
1460167050Smjacob{
1461167050Smjacob	uint32_t *version;
1462167050Smjacob	g_topology_assert();
1463167050Smjacob	version = gctl_get_paraml(req, "version", sizeof(*version));
1464167050Smjacob	if (version == NULL) {
1465167050Smjacob		gctl_error(req, "No 'version' argument");
1466167050Smjacob	} else if (*version != G_MULTIPATH_VERSION) {
1467167050Smjacob		gctl_error(req, "Userland and kernel parts are out of sync");
1468205847Smjacob	} else if (strcmp(verb, "add") == 0) {
1469205847Smjacob		g_multipath_ctl_add(req, mp);
1470239012Sthomas	} else if (strcmp(verb, "prefer") == 0) {
1471239012Sthomas		g_multipath_ctl_prefer(req, mp);
1472227464Smav	} else if (strcmp(verb, "create") == 0) {
1473227464Smav		g_multipath_ctl_create(req, mp);
1474234415Smav	} else if (strcmp(verb, "configure") == 0) {
1475234415Smav		g_multipath_ctl_configure(req, mp);
1476227464Smav	} else if (strcmp(verb, "stop") == 0) {
1477227464Smav		g_multipath_ctl_stop(req, mp);
1478167050Smjacob	} else if (strcmp(verb, "destroy") == 0) {
1479167050Smjacob		g_multipath_ctl_destroy(req, mp);
1480227464Smav	} else if (strcmp(verb, "fail") == 0) {
1481227464Smav		g_multipath_ctl_fail(req, mp, 1);
1482227464Smav	} else if (strcmp(verb, "restore") == 0) {
1483227464Smav		g_multipath_ctl_fail(req, mp, 0);
1484227464Smav	} else if (strcmp(verb, "remove") == 0) {
1485227464Smav		g_multipath_ctl_remove(req, mp);
1486205412Smjacob	} else if (strcmp(verb, "rotate") == 0) {
1487205412Smjacob		g_multipath_ctl_rotate(req, mp);
1488205412Smjacob	} else if (strcmp(verb, "getactive") == 0) {
1489205412Smjacob		g_multipath_ctl_getactive(req, mp);
1490167050Smjacob	} else {
1491167050Smjacob		gctl_error(req, "Unknown verb %s", verb);
1492167050Smjacob	}
1493167050Smjacob}
1494227464Smav
1495227464Smavstatic void
1496227464Smavg_multipath_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
1497227464Smav    struct g_consumer *cp, struct g_provider *pp)
1498227464Smav{
1499227464Smav	struct g_multipath_softc *sc;
1500227464Smav	int good;
1501227464Smav
1502227464Smav	g_topology_assert();
1503227464Smav
1504227464Smav	sc = gp->softc;
1505227464Smav	if (sc == NULL)
1506227464Smav		return;
1507227464Smav	if (cp != NULL) {
1508236619Smav		sbuf_printf(sb, "%s<State>%s</State>\n", indent,
1509227464Smav		    (cp->index & MP_NEW) ? "NEW" :
1510227464Smav		    (cp->index & MP_LOST) ? "LOST" :
1511227464Smav		    (cp->index & MP_FAIL) ? "FAIL" :
1512234415Smav		    (sc->sc_active_active == 1 || sc->sc_active == cp) ?
1513234415Smav		     "ACTIVE" :
1514234415Smav		     sc->sc_active_active == 2 ? "READ" : "PASSIVE");
1515227464Smav	} else {
1516227464Smav		good = g_multipath_good(gp);
1517236619Smav		sbuf_printf(sb, "%s<State>%s</State>\n", indent,
1518227464Smav		    good == 0 ? "BROKEN" :
1519227464Smav		    (good != sc->sc_ndisks || sc->sc_ndisks == 1) ?
1520227464Smav		    "DEGRADED" : "OPTIMAL");
1521227464Smav	}
1522227464Smav	if (cp == NULL && pp == NULL) {
1523236619Smav		sbuf_printf(sb, "%s<UUID>%s</UUID>\n", indent, sc->sc_uuid);
1524236619Smav		sbuf_printf(sb, "%s<Mode>Active/%s</Mode>\n", indent,
1525234415Smav		    sc->sc_active_active == 2 ? "Read" :
1526234415Smav		    sc->sc_active_active == 1 ? "Active" : "Passive");
1527236619Smav		sbuf_printf(sb, "%s<Type>%s</Type>\n", indent,
1528227464Smav		    sc->sc_uuid[0] == 0 ? "MANUAL" : "AUTOMATIC");
1529227464Smav	}
1530227464Smav}
1531227464Smav
1532167050SmjacobDECLARE_GEOM_CLASS(g_multipath_class, g_multipath);
1533