g_gate.c revision 204076
1128760Spjd/*-
2162149Spjd * Copyright (c) 2004-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation
4128760Spjd * All rights reserved.
5128760Spjd *
6204076Spjd * Portions of this software were developed by Pawel Jakub Dawidek
7204076Spjd * under sponsorship from the FreeBSD Foundation.
8204076Spjd *
9128760Spjd * Redistribution and use in source and binary forms, with or without
10128760Spjd * modification, are permitted provided that the following conditions
11128760Spjd * are met:
12128760Spjd * 1. Redistributions of source code must retain the above copyright
13128760Spjd *    notice, this list of conditions and the following disclaimer.
14128760Spjd * 2. Redistributions in binary form must reproduce the above copyright
15128760Spjd *    notice, this list of conditions and the following disclaimer in the
16128760Spjd *    documentation and/or other materials provided with the distribution.
17155174Spjd *
18128760Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19128760Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20128760Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21128760Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22128760Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23128760Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24128760Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25128760Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26128760Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27128760Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28128760Spjd * SUCH DAMAGE.
29128760Spjd */
30128760Spjd
31162148Spjd#include <sys/cdefs.h>
32162148Spjd__FBSDID("$FreeBSD: head/sys/geom/gate/g_gate.c 204076 2010-02-18 23:16:19Z pjd $");
33162148Spjd
34128760Spjd#include <sys/param.h>
35128760Spjd#include <sys/systm.h>
36128760Spjd#include <sys/bio.h>
37128760Spjd#include <sys/conf.h>
38128760Spjd#include <sys/kernel.h>
39128760Spjd#include <sys/kthread.h>
40128760Spjd#include <sys/fcntl.h>
41128760Spjd#include <sys/linker.h>
42128760Spjd#include <sys/lock.h>
43128760Spjd#include <sys/malloc.h>
44128760Spjd#include <sys/mutex.h>
45128760Spjd#include <sys/proc.h>
46128760Spjd#include <sys/limits.h>
47128760Spjd#include <sys/queue.h>
48128760Spjd#include <sys/sysctl.h>
49128760Spjd#include <sys/signalvar.h>
50128760Spjd#include <sys/time.h>
51128760Spjd#include <machine/atomic.h>
52128760Spjd
53128760Spjd#include <geom/geom.h>
54128760Spjd#include <geom/gate/g_gate.h>
55128760Spjd
56151897Srwatsonstatic MALLOC_DEFINE(M_GATE, "gg_data", "GEOM Gate Data");
57128760Spjd
58128760SpjdSYSCTL_DECL(_kern_geom);
59128760SpjdSYSCTL_NODE(_kern_geom, OID_AUTO, gate, CTLFLAG_RW, 0, "GEOM_GATE stuff");
60204076Spjdstatic int g_gate_debug = 0;
61204076SpjdTUNABLE_INT("kern.geom.gate.debug", &g_gate_debug);
62204076SpjdSYSCTL_INT(_kern_geom_gate, OID_AUTO, debug, CTLFLAG_RW, &g_gate_debug, 0,
63128760Spjd    "Debug level");
64204076Spjdstatic u_int g_gate_maxunits = 256;
65204076SpjdTUNABLE_INT("kern.geom.gate.maxunits", &g_gate_maxunits);
66204076SpjdSYSCTL_UINT(_kern_geom_gate, OID_AUTO, maxunits, CTLFLAG_RDTUN,
67204076Spjd    &g_gate_maxunits, 0, "Maximum number of ggate devices");
68128760Spjd
69128760Spjdstruct g_class g_gate_class = {
70128760Spjd	.name = G_GATE_CLASS_NAME,
71133318Sphk	.version = G_VERSION,
72128760Spjd};
73128760Spjd
74130585Sphkstatic struct cdev *status_dev;
75128760Spjdstatic d_ioctl_t g_gate_ioctl;
76128760Spjdstatic struct cdevsw g_gate_cdevsw = {
77128760Spjd	.d_version =	D_VERSION,
78128760Spjd	.d_ioctl =	g_gate_ioctl,
79128760Spjd	.d_name =	G_GATE_CTL_NAME
80128760Spjd};
81128760Spjd
82128760Spjd
83204076Spjdstatic struct g_gate_softc **g_gate_units;
84204076Spjdstatic u_int g_gate_nunits;
85204076Spjdstatic struct mtx g_gate_units_lock;
86128760Spjd
87128760Spjdstatic int
88128760Spjdg_gate_destroy(struct g_gate_softc *sc, boolean_t force)
89128760Spjd{
90128760Spjd	struct g_provider *pp;
91162056Spjd	struct g_geom *gp;
92128760Spjd	struct bio *bp;
93128760Spjd
94128760Spjd	g_topology_assert();
95204076Spjd	mtx_assert(&g_gate_units_lock, MA_OWNED);
96128760Spjd	pp = sc->sc_provider;
97128760Spjd	if (!force && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
98204076Spjd		mtx_unlock(&g_gate_units_lock);
99128760Spjd		return (EBUSY);
100128760Spjd	}
101204076Spjd	mtx_unlock(&g_gate_units_lock);
102141561Spjd	mtx_lock(&sc->sc_queue_mtx);
103162056Spjd	if ((sc->sc_flags & G_GATE_FLAG_DESTROY) == 0)
104162056Spjd		sc->sc_flags |= G_GATE_FLAG_DESTROY;
105128760Spjd	wakeup(sc);
106141561Spjd	mtx_unlock(&sc->sc_queue_mtx);
107162056Spjd	gp = pp->geom;
108162056Spjd	pp->flags |= G_PF_WITHER;
109162056Spjd	g_orphan_provider(pp, ENXIO);
110128760Spjd	callout_drain(&sc->sc_callout);
111141561Spjd	mtx_lock(&sc->sc_queue_mtx);
112128760Spjd	for (;;) {
113128760Spjd		bp = bioq_first(&sc->sc_inqueue);
114128760Spjd		if (bp != NULL) {
115128760Spjd			bioq_remove(&sc->sc_inqueue, bp);
116141561Spjd			sc->sc_queue_count--;
117128760Spjd			G_GATE_LOGREQ(1, bp, "Request canceled.");
118128760Spjd			g_io_deliver(bp, ENXIO);
119128760Spjd		} else {
120128760Spjd			break;
121128760Spjd		}
122128760Spjd	}
123128760Spjd	for (;;) {
124128760Spjd		bp = bioq_first(&sc->sc_outqueue);
125128760Spjd		if (bp != NULL) {
126128760Spjd			bioq_remove(&sc->sc_outqueue, bp);
127141561Spjd			sc->sc_queue_count--;
128128760Spjd			G_GATE_LOGREQ(1, bp, "Request canceled.");
129128760Spjd			g_io_deliver(bp, ENXIO);
130128760Spjd		} else {
131128760Spjd			break;
132128760Spjd		}
133128760Spjd	}
134162056Spjd	mtx_unlock(&sc->sc_queue_mtx);
135162056Spjd	g_topology_unlock();
136204076Spjd	mtx_lock(&g_gate_units_lock);
137162056Spjd	/* One reference is ours. */
138162056Spjd	sc->sc_ref--;
139204076Spjd	while (sc->sc_ref > 0)
140204076Spjd		msleep(&sc->sc_ref, &g_gate_units_lock, 0, "gg:destroy", 0);
141204076Spjd	g_gate_units[sc->sc_unit] = NULL;
142204076Spjd	KASSERT(g_gate_nunits > 0, ("negative g_gate_nunits?"));
143204076Spjd	g_gate_nunits--;
144204076Spjd	mtx_unlock(&g_gate_units_lock);
145141561Spjd	mtx_destroy(&sc->sc_queue_mtx);
146162056Spjd	g_topology_lock();
147162056Spjd	G_GATE_DEBUG(0, "Device %s destroyed.", gp->name);
148162056Spjd	gp->softc = NULL;
149162056Spjd	g_wither_geom(gp, ENXIO);
150128760Spjd	sc->sc_provider = NULL;
151128760Spjd	free(sc, M_GATE);
152128760Spjd	return (0);
153128760Spjd}
154128760Spjd
155128760Spjdstatic int
156128760Spjdg_gate_access(struct g_provider *pp, int dr, int dw, int de)
157128760Spjd{
158128760Spjd	struct g_gate_softc *sc;
159128760Spjd
160128760Spjd	if (dr <= 0 && dw <= 0 && de <= 0)
161128760Spjd		return (0);
162128760Spjd	sc = pp->geom->softc;
163128760Spjd	if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0)
164128760Spjd		return (ENXIO);
165131188Spjd	/* XXX: Hack to allow read-only mounts. */
166131188Spjd#if 0
167128760Spjd	if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0 && dw > 0)
168128760Spjd		return (EPERM);
169131188Spjd#endif
170128760Spjd	if ((sc->sc_flags & G_GATE_FLAG_WRITEONLY) != 0 && dr > 0)
171128760Spjd		return (EPERM);
172128760Spjd	return (0);
173128760Spjd}
174128760Spjd
175128760Spjdstatic void
176128760Spjdg_gate_start(struct bio *bp)
177128760Spjd{
178128760Spjd	struct g_gate_softc *sc;
179128760Spjd
180128760Spjd	sc = bp->bio_to->geom->softc;
181128760Spjd	if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) {
182128760Spjd		g_io_deliver(bp, ENXIO);
183128760Spjd		return;
184128760Spjd	}
185128760Spjd	G_GATE_LOGREQ(2, bp, "Request received.");
186128760Spjd	switch (bp->bio_cmd) {
187128760Spjd	case BIO_READ:
188131188Spjd		break;
189128760Spjd	case BIO_DELETE:
190128760Spjd	case BIO_WRITE:
191131188Spjd		/* XXX: Hack to allow read-only mounts. */
192131188Spjd		if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0) {
193131188Spjd			g_io_deliver(bp, EPERM);
194131188Spjd			return;
195131188Spjd		}
196128760Spjd		break;
197128760Spjd	case BIO_GETATTR:
198128760Spjd	default:
199128760Spjd		G_GATE_LOGREQ(2, bp, "Ignoring request.");
200128760Spjd		g_io_deliver(bp, EOPNOTSUPP);
201128760Spjd		return;
202128760Spjd	}
203128760Spjd
204141561Spjd	mtx_lock(&sc->sc_queue_mtx);
205141561Spjd	if (sc->sc_queue_count > sc->sc_queue_size) {
206141742Spjd		mtx_unlock(&sc->sc_queue_mtx);
207128760Spjd		G_GATE_LOGREQ(1, bp, "Queue full, request canceled.");
208204076Spjd		g_io_deliver(bp, ENOMEM);
209128760Spjd		return;
210128760Spjd	}
211141561Spjd
212128760Spjd	bp->bio_driver1 = (void *)sc->sc_seq;
213128760Spjd	sc->sc_seq++;
214141561Spjd	sc->sc_queue_count++;
215128760Spjd
216141312Spjd	bioq_insert_tail(&sc->sc_inqueue, bp);
217128957Spjd	wakeup(sc);
218141561Spjd
219141561Spjd	mtx_unlock(&sc->sc_queue_mtx);
220128760Spjd}
221128760Spjd
222128760Spjdstatic struct g_gate_softc *
223204076Spjdg_gate_hold(u_int unit, const char *name)
224128760Spjd{
225204076Spjd	struct g_gate_softc *sc = NULL;
226128760Spjd
227204076Spjd	mtx_lock(&g_gate_units_lock);
228204076Spjd	if (unit >= 0 && unit < g_gate_maxunits)
229204076Spjd		sc = g_gate_units[unit];
230204076Spjd	else if (unit == G_GATE_NAME_GIVEN) {
231204076Spjd		KASSERT(name != NULL, ("name is NULL"));
232204076Spjd		for (unit = 0; unit < g_gate_maxunits; unit++) {
233204076Spjd			if (g_gate_units[unit] == NULL)
234204076Spjd				continue;
235204076Spjd			if (strcmp(name,
236204076Spjd			    g_gate_units[unit]->sc_provider->name) != 0) {
237204076Spjd				continue;
238204076Spjd			}
239204076Spjd			sc = g_gate_units[unit];
240128760Spjd			break;
241204076Spjd		}
242128760Spjd	}
243162056Spjd	if (sc != NULL)
244162056Spjd		sc->sc_ref++;
245204076Spjd	mtx_unlock(&g_gate_units_lock);
246128760Spjd	return (sc);
247128760Spjd}
248128760Spjd
249128760Spjdstatic void
250128760Spjdg_gate_release(struct g_gate_softc *sc)
251128760Spjd{
252128760Spjd
253128760Spjd	g_topology_assert_not();
254204076Spjd	mtx_lock(&g_gate_units_lock);
255128760Spjd	sc->sc_ref--;
256128760Spjd	KASSERT(sc->sc_ref >= 0, ("Negative sc_ref for %s.", sc->sc_name));
257204076Spjd	if (sc->sc_ref == 0 && (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0)
258162056Spjd		wakeup(&sc->sc_ref);
259204076Spjd	mtx_unlock(&g_gate_units_lock);
260128760Spjd}
261128760Spjd
262128760Spjdstatic int
263204076Spjdg_gate_getunit(int unit, int *errorp)
264128760Spjd{
265128760Spjd
266204076Spjd	mtx_assert(&g_gate_units_lock, MA_OWNED);
267128760Spjd	if (unit >= 0) {
268204076Spjd		if (unit >= g_gate_maxunits)
269204076Spjd			*errorp = EINVAL;
270204076Spjd		else if (g_gate_units[unit] == NULL)
271204076Spjd			return (unit);
272204076Spjd		else
273204076Spjd			*errorp = EEXIST;
274128760Spjd	} else {
275204076Spjd		for (unit = 0; unit < g_gate_maxunits; unit++) {
276204076Spjd			if (g_gate_units[unit] == NULL)
277204076Spjd				return (unit);
278128760Spjd		}
279204076Spjd		*errorp = ENFILE;
280128760Spjd	}
281204076Spjd	return (-1);
282128760Spjd}
283128760Spjd
284128760Spjdstatic void
285128760Spjdg_gate_guard(void *arg)
286128760Spjd{
287128760Spjd	struct g_gate_softc *sc;
288128760Spjd	struct bintime curtime;
289128760Spjd	struct bio *bp, *bp2;
290128760Spjd
291128760Spjd	sc = arg;
292128760Spjd	binuptime(&curtime);
293204076Spjd	g_gate_hold(sc->sc_unit, NULL);
294141561Spjd	mtx_lock(&sc->sc_queue_mtx);
295128760Spjd	TAILQ_FOREACH_SAFE(bp, &sc->sc_inqueue.queue, bio_queue, bp2) {
296128760Spjd		if (curtime.sec - bp->bio_t0.sec < 5)
297128760Spjd			continue;
298128760Spjd		bioq_remove(&sc->sc_inqueue, bp);
299141561Spjd		sc->sc_queue_count--;
300128760Spjd		G_GATE_LOGREQ(1, bp, "Request timeout.");
301128760Spjd		g_io_deliver(bp, EIO);
302128760Spjd	}
303128760Spjd	TAILQ_FOREACH_SAFE(bp, &sc->sc_outqueue.queue, bio_queue, bp2) {
304128760Spjd		if (curtime.sec - bp->bio_t0.sec < 5)
305128760Spjd			continue;
306128760Spjd		bioq_remove(&sc->sc_outqueue, bp);
307141561Spjd		sc->sc_queue_count--;
308128760Spjd		G_GATE_LOGREQ(1, bp, "Request timeout.");
309128760Spjd		g_io_deliver(bp, EIO);
310128760Spjd	}
311141561Spjd	mtx_unlock(&sc->sc_queue_mtx);
312128760Spjd	if ((sc->sc_flags & G_GATE_FLAG_DESTROY) == 0) {
313128760Spjd		callout_reset(&sc->sc_callout, sc->sc_timeout * hz,
314128760Spjd		    g_gate_guard, sc);
315128760Spjd	}
316128760Spjd	g_gate_release(sc);
317128760Spjd}
318128760Spjd
319128760Spjdstatic void
320128760Spjdg_gate_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
321128760Spjd    struct g_consumer *cp, struct g_provider *pp)
322128760Spjd{
323128760Spjd	struct g_gate_softc *sc;
324128760Spjd
325128760Spjd	sc = gp->softc;
326128760Spjd	if (sc == NULL || pp != NULL || cp != NULL)
327128760Spjd		return;
328204076Spjd	g_gate_hold(sc->sc_unit, NULL);
329128760Spjd	if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0) {
330128760Spjd		sbuf_printf(sb, "%s<access>%s</access>\n", indent, "read-only");
331128760Spjd	} else if ((sc->sc_flags & G_GATE_FLAG_WRITEONLY) != 0) {
332128760Spjd		sbuf_printf(sb, "%s<access>%s</access>\n", indent,
333128760Spjd		    "write-only");
334128760Spjd	} else {
335128760Spjd		sbuf_printf(sb, "%s<access>%s</access>\n", indent,
336128760Spjd		    "read-write");
337128760Spjd	}
338128760Spjd	sbuf_printf(sb, "%s<timeout>%u</timeout>\n", indent, sc->sc_timeout);
339128760Spjd	sbuf_printf(sb, "%s<info>%s</info>\n", indent, sc->sc_info);
340128760Spjd	sbuf_printf(sb, "%s<queue_count>%u</queue_count>\n", indent,
341128760Spjd	    sc->sc_queue_count);
342128760Spjd	sbuf_printf(sb, "%s<queue_size>%u</queue_size>\n", indent,
343128760Spjd	    sc->sc_queue_size);
344128760Spjd	sbuf_printf(sb, "%s<ref>%u</ref>\n", indent, sc->sc_ref);
345204076Spjd	sbuf_printf(sb, "%s<unit>%d</unit>\n", indent, sc->sc_unit);
346130836Spjd	g_topology_unlock();
347128760Spjd	g_gate_release(sc);
348130836Spjd	g_topology_lock();
349128760Spjd}
350128760Spjd
351128760Spjdstatic int
352128760Spjdg_gate_create(struct g_gate_ctl_create *ggio)
353128760Spjd{
354128760Spjd	struct g_gate_softc *sc;
355128760Spjd	struct g_geom *gp;
356128760Spjd	struct g_provider *pp;
357204076Spjd	char name[NAME_MAX];
358204076Spjd	int error = 0, unit;
359128760Spjd
360128760Spjd	if (ggio->gctl_mediasize == 0) {
361128760Spjd		G_GATE_DEBUG(1, "Invalid media size.");
362128760Spjd		return (EINVAL);
363128760Spjd	}
364128760Spjd	if (ggio->gctl_sectorsize > 0 && !powerof2(ggio->gctl_sectorsize)) {
365128760Spjd		G_GATE_DEBUG(1, "Invalid sector size.");
366128760Spjd		return (EINVAL);
367128760Spjd	}
368141312Spjd	if ((ggio->gctl_mediasize % ggio->gctl_sectorsize) != 0) {
369141312Spjd		G_GATE_DEBUG(1, "Invalid media size.");
370141312Spjd		return (EINVAL);
371141312Spjd	}
372128760Spjd	if ((ggio->gctl_flags & G_GATE_FLAG_READONLY) != 0 &&
373128760Spjd	    (ggio->gctl_flags & G_GATE_FLAG_WRITEONLY) != 0) {
374128760Spjd		G_GATE_DEBUG(1, "Invalid flags.");
375128760Spjd		return (EINVAL);
376128760Spjd	}
377204076Spjd	if (ggio->gctl_unit != G_GATE_UNIT_AUTO &&
378204076Spjd	    ggio->gctl_unit != G_GATE_NAME_GIVEN &&
379204076Spjd	    ggio->gctl_unit < 0) {
380128760Spjd		G_GATE_DEBUG(1, "Invalid unit number.");
381128760Spjd		return (EINVAL);
382128760Spjd	}
383204076Spjd	if (ggio->gctl_unit == G_GATE_NAME_GIVEN &&
384204076Spjd	    ggio->gctl_name[0] == '\0') {
385204076Spjd		G_GATE_DEBUG(1, "No device name.");
386204076Spjd		return (EINVAL);
387204076Spjd	}
388128760Spjd
389128760Spjd	sc = malloc(sizeof(*sc), M_GATE, M_WAITOK | M_ZERO);
390128760Spjd	sc->sc_flags = (ggio->gctl_flags & G_GATE_USERFLAGS);
391128760Spjd	strlcpy(sc->sc_info, ggio->gctl_info, sizeof(sc->sc_info));
392204076Spjd	sc->sc_seq = 1;
393128760Spjd	bioq_init(&sc->sc_inqueue);
394128760Spjd	bioq_init(&sc->sc_outqueue);
395141561Spjd	mtx_init(&sc->sc_queue_mtx, "gg:queue", NULL, MTX_DEF);
396128760Spjd	sc->sc_queue_count = 0;
397128760Spjd	sc->sc_queue_size = ggio->gctl_maxcount;
398128760Spjd	if (sc->sc_queue_size > G_GATE_MAX_QUEUE_SIZE)
399128760Spjd		sc->sc_queue_size = G_GATE_MAX_QUEUE_SIZE;
400128760Spjd	sc->sc_timeout = ggio->gctl_timeout;
401128881Spjd	callout_init(&sc->sc_callout, CALLOUT_MPSAFE);
402204076Spjd	mtx_lock(&g_gate_units_lock);
403204076Spjd	sc->sc_unit = g_gate_getunit(ggio->gctl_unit, &error);
404204076Spjd	if (sc->sc_unit < 0) {
405204076Spjd		mtx_unlock(&g_gate_units_lock);
406141561Spjd		mtx_destroy(&sc->sc_queue_mtx);
407128881Spjd		free(sc, M_GATE);
408204076Spjd		return (error);
409128881Spjd	}
410204076Spjd	if (ggio->gctl_unit == G_GATE_NAME_GIVEN)
411204076Spjd		snprintf(name, sizeof(name), "%s", ggio->gctl_name);
412204076Spjd	else {
413204076Spjd		snprintf(name, sizeof(name), "%s%d", G_GATE_PROVIDER_NAME,
414204076Spjd		    sc->sc_unit);
415204076Spjd	}
416204076Spjd	/* Check for name collision. */
417204076Spjd	for (unit = 0; unit < g_gate_maxunits; unit++) {
418204076Spjd		if (g_gate_units[unit] == NULL)
419204076Spjd			continue;
420204076Spjd		if (strcmp(name, g_gate_units[unit]->sc_provider->name) != 0)
421204076Spjd			continue;
422204076Spjd		mtx_unlock(&g_gate_units_lock);
423204076Spjd		mtx_destroy(&sc->sc_queue_mtx);
424204076Spjd		free(sc, M_GATE);
425204076Spjd		return (EEXIST);
426204076Spjd	}
427204076Spjd	g_gate_units[sc->sc_unit] = sc;
428204076Spjd	g_gate_nunits++;
429204076Spjd	mtx_unlock(&g_gate_units_lock);
430128760Spjd
431204076Spjd	ggio->gctl_unit = sc->sc_unit;
432204076Spjd
433128760Spjd	g_topology_lock();
434204076Spjd	gp = g_new_geomf(&g_gate_class, "%s", name);
435128760Spjd	gp->start = g_gate_start;
436128760Spjd	gp->access = g_gate_access;
437128760Spjd	gp->dumpconf = g_gate_dumpconf;
438128760Spjd	gp->softc = sc;
439204076Spjd	pp = g_new_providerf(gp, "%s", name);
440128760Spjd	pp->mediasize = ggio->gctl_mediasize;
441128760Spjd	pp->sectorsize = ggio->gctl_sectorsize;
442128760Spjd	sc->sc_provider = pp;
443128760Spjd	g_error_provider(pp, 0);
444128760Spjd	g_topology_unlock();
445128760Spjd
446128760Spjd	if (sc->sc_timeout > 0) {
447128760Spjd		callout_reset(&sc->sc_callout, sc->sc_timeout * hz,
448128760Spjd		    g_gate_guard, sc);
449128760Spjd	}
450128760Spjd	return (0);
451128760Spjd}
452128760Spjd
453128760Spjd#define	G_GATE_CHECK_VERSION(ggio)	do {				\
454147843Spjd	if ((ggio)->gctl_version != G_GATE_VERSION) {			\
455147843Spjd		printf("Version mismatch %d != %d.\n",			\
456147843Spjd		    ggio->gctl_version, G_GATE_VERSION);		\
457128760Spjd		return (EINVAL);					\
458147843Spjd	}								\
459128760Spjd} while (0)
460128760Spjdstatic int
461130585Sphkg_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td)
462128760Spjd{
463128760Spjd	struct g_gate_softc *sc;
464128760Spjd	struct bio *bp;
465128760Spjd	int error = 0;
466128760Spjd
467128760Spjd	G_GATE_DEBUG(4, "ioctl(%s, %lx, %p, %x, %p)", devtoname(dev), cmd, addr,
468128760Spjd	    flags, td);
469128760Spjd
470128760Spjd	switch (cmd) {
471128760Spjd	case G_GATE_CMD_CREATE:
472128760Spjd	    {
473128760Spjd		struct g_gate_ctl_create *ggio = (void *)addr;
474128760Spjd
475128760Spjd		G_GATE_CHECK_VERSION(ggio);
476138014Spjd		error = g_gate_create(ggio);
477141972Spjd		/*
478141972Spjd		 * Reset TDP_GEOM flag.
479141972Spjd		 * There are pending events for sure, because we just created
480141972Spjd		 * new provider and other classes want to taste it, but we
481141972Spjd		 * cannot answer on I/O requests until we're here.
482141972Spjd		 */
483141972Spjd		td->td_pflags &= ~TDP_GEOM;
484138014Spjd		return (error);
485128760Spjd	    }
486128760Spjd	case G_GATE_CMD_DESTROY:
487128760Spjd	    {
488128760Spjd		struct g_gate_ctl_destroy *ggio = (void *)addr;
489128760Spjd
490128760Spjd		G_GATE_CHECK_VERSION(ggio);
491204076Spjd		sc = g_gate_hold(ggio->gctl_unit, ggio->gctl_name);
492128760Spjd		if (sc == NULL)
493128760Spjd			return (ENXIO);
494128760Spjd		g_topology_lock();
495204076Spjd		mtx_lock(&g_gate_units_lock);
496128760Spjd		error = g_gate_destroy(sc, ggio->gctl_force);
497128760Spjd		g_topology_unlock();
498162056Spjd		if (error != 0)
499162056Spjd			g_gate_release(sc);
500128760Spjd		return (error);
501128760Spjd	    }
502147843Spjd	case G_GATE_CMD_CANCEL:
503147843Spjd	    {
504147843Spjd		struct g_gate_ctl_cancel *ggio = (void *)addr;
505147843Spjd		struct bio *tbp, *lbp;
506147843Spjd
507147843Spjd		G_GATE_CHECK_VERSION(ggio);
508204076Spjd		sc = g_gate_hold(ggio->gctl_unit, ggio->gctl_name);
509147843Spjd		if (sc == NULL)
510147843Spjd			return (ENXIO);
511147843Spjd		lbp = NULL;
512147843Spjd		mtx_lock(&sc->sc_queue_mtx);
513147843Spjd		TAILQ_FOREACH_SAFE(bp, &sc->sc_outqueue.queue, bio_queue, tbp) {
514147843Spjd			if (ggio->gctl_seq == 0 ||
515147843Spjd			    ggio->gctl_seq == (uintptr_t)bp->bio_driver1) {
516147843Spjd				G_GATE_LOGREQ(1, bp, "Request canceled.");
517147843Spjd				bioq_remove(&sc->sc_outqueue, bp);
518147843Spjd				/*
519147843Spjd				 * Be sure to put requests back onto incoming
520147843Spjd				 * queue in the proper order.
521147843Spjd				 */
522147843Spjd				if (lbp == NULL)
523147843Spjd					bioq_insert_head(&sc->sc_inqueue, bp);
524147843Spjd				else {
525147843Spjd					TAILQ_INSERT_AFTER(&sc->sc_inqueue.queue,
526147843Spjd					    lbp, bp, bio_queue);
527147843Spjd				}
528147843Spjd				lbp = bp;
529147843Spjd				/*
530147843Spjd				 * If only one request was canceled, leave now.
531147843Spjd				 */
532147843Spjd				if (ggio->gctl_seq != 0)
533147843Spjd					break;
534147843Spjd			}
535147843Spjd		}
536204076Spjd		if (ggio->gctl_unit == G_GATE_NAME_GIVEN)
537204076Spjd			ggio->gctl_unit = sc->sc_unit;
538147843Spjd		mtx_unlock(&sc->sc_queue_mtx);
539147843Spjd		g_gate_release(sc);
540147843Spjd		return (error);
541147843Spjd	    }
542128760Spjd	case G_GATE_CMD_START:
543128760Spjd	    {
544128760Spjd		struct g_gate_ctl_io *ggio = (void *)addr;
545128760Spjd
546128760Spjd		G_GATE_CHECK_VERSION(ggio);
547204076Spjd		sc = g_gate_hold(ggio->gctl_unit, NULL);
548128760Spjd		if (sc == NULL)
549128760Spjd			return (ENXIO);
550162056Spjd		error = 0;
551128760Spjd		for (;;) {
552141561Spjd			mtx_lock(&sc->sc_queue_mtx);
553128760Spjd			bp = bioq_first(&sc->sc_inqueue);
554128760Spjd			if (bp != NULL)
555128760Spjd				break;
556162056Spjd			if ((sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) {
557162056Spjd				ggio->gctl_error = ECANCELED;
558162056Spjd				mtx_unlock(&sc->sc_queue_mtx);
559162056Spjd				goto start_end;
560162056Spjd			}
561141561Spjd			if (msleep(sc, &sc->sc_queue_mtx,
562128760Spjd			    PPAUSE | PDROP | PCATCH, "ggwait", 0) != 0) {
563128760Spjd				ggio->gctl_error = ECANCELED;
564162056Spjd				goto start_end;
565128760Spjd			}
566128760Spjd		}
567128881Spjd		ggio->gctl_cmd = bp->bio_cmd;
568128760Spjd		if ((bp->bio_cmd == BIO_DELETE || bp->bio_cmd == BIO_WRITE) &&
569128760Spjd		    bp->bio_length > ggio->gctl_length) {
570141561Spjd			mtx_unlock(&sc->sc_queue_mtx);
571128760Spjd			ggio->gctl_length = bp->bio_length;
572128760Spjd			ggio->gctl_error = ENOMEM;
573162056Spjd			goto start_end;
574128760Spjd		}
575128760Spjd		bioq_remove(&sc->sc_inqueue, bp);
576141561Spjd		bioq_insert_tail(&sc->sc_outqueue, bp);
577141561Spjd		mtx_unlock(&sc->sc_queue_mtx);
578141561Spjd
579128835Spjd		ggio->gctl_seq = (uintptr_t)bp->bio_driver1;
580128760Spjd		ggio->gctl_offset = bp->bio_offset;
581128760Spjd		ggio->gctl_length = bp->bio_length;
582147843Spjd
583128760Spjd		switch (bp->bio_cmd) {
584128760Spjd		case BIO_READ:
585128760Spjd			break;
586128760Spjd		case BIO_DELETE:
587128760Spjd		case BIO_WRITE:
588128760Spjd			error = copyout(bp->bio_data, ggio->gctl_data,
589128760Spjd			    bp->bio_length);
590128760Spjd			if (error != 0) {
591141561Spjd				mtx_lock(&sc->sc_queue_mtx);
592141561Spjd				bioq_remove(&sc->sc_outqueue, bp);
593141312Spjd				bioq_insert_head(&sc->sc_inqueue, bp);
594141561Spjd				mtx_unlock(&sc->sc_queue_mtx);
595162056Spjd				goto start_end;
596128760Spjd			}
597128760Spjd			break;
598128760Spjd		}
599162056Spjdstart_end:
600162056Spjd		g_gate_release(sc);
601162056Spjd		return (error);
602128760Spjd	    }
603128760Spjd	case G_GATE_CMD_DONE:
604128760Spjd	    {
605128760Spjd		struct g_gate_ctl_io *ggio = (void *)addr;
606128760Spjd
607128760Spjd		G_GATE_CHECK_VERSION(ggio);
608204076Spjd		sc = g_gate_hold(ggio->gctl_unit, NULL);
609128760Spjd		if (sc == NULL)
610128760Spjd			return (ENOENT);
611162056Spjd		error = 0;
612141561Spjd		mtx_lock(&sc->sc_queue_mtx);
613128760Spjd		TAILQ_FOREACH(bp, &sc->sc_outqueue.queue, bio_queue) {
614128835Spjd			if (ggio->gctl_seq == (uintptr_t)bp->bio_driver1)
615128760Spjd				break;
616128760Spjd		}
617128760Spjd		if (bp != NULL) {
618128760Spjd			bioq_remove(&sc->sc_outqueue, bp);
619141561Spjd			sc->sc_queue_count--;
620128760Spjd		}
621141561Spjd		mtx_unlock(&sc->sc_queue_mtx);
622128760Spjd		if (bp == NULL) {
623128760Spjd			/*
624128760Spjd			 * Request was probably canceled.
625128760Spjd			 */
626162056Spjd			goto done_end;
627128760Spjd		}
628128760Spjd		if (ggio->gctl_error == EAGAIN) {
629128760Spjd			bp->bio_error = 0;
630128760Spjd			G_GATE_LOGREQ(1, bp, "Request desisted.");
631141561Spjd			mtx_lock(&sc->sc_queue_mtx);
632141561Spjd			sc->sc_queue_count++;
633141312Spjd			bioq_insert_head(&sc->sc_inqueue, bp);
634128957Spjd			wakeup(sc);
635141561Spjd			mtx_unlock(&sc->sc_queue_mtx);
636128760Spjd		} else {
637128760Spjd			bp->bio_error = ggio->gctl_error;
638128760Spjd			if (bp->bio_error == 0) {
639128760Spjd				bp->bio_completed = bp->bio_length;
640128760Spjd				switch (bp->bio_cmd) {
641128760Spjd				case BIO_READ:
642128760Spjd					error = copyin(ggio->gctl_data,
643128760Spjd					    bp->bio_data, bp->bio_length);
644128760Spjd					if (error != 0)
645128760Spjd						bp->bio_error = error;
646128760Spjd					break;
647128760Spjd				case BIO_DELETE:
648128760Spjd				case BIO_WRITE:
649128760Spjd					break;
650128760Spjd				}
651128760Spjd			}
652128760Spjd			G_GATE_LOGREQ(2, bp, "Request done.");
653128760Spjd			g_io_deliver(bp, bp->bio_error);
654128760Spjd		}
655162056Spjddone_end:
656162056Spjd		g_gate_release(sc);
657128760Spjd		return (error);
658128760Spjd	    }
659128760Spjd	}
660128760Spjd	return (ENOIOCTL);
661128760Spjd}
662128760Spjd
663128760Spjdstatic void
664131411Spjdg_gate_device(void)
665128760Spjd{
666128760Spjd
667128760Spjd	status_dev = make_dev(&g_gate_cdevsw, 0x0, UID_ROOT, GID_WHEEL, 0600,
668128760Spjd	    G_GATE_CTL_NAME);
669128760Spjd}
670128760Spjd
671128760Spjdstatic int
672128760Spjdg_gate_modevent(module_t mod, int type, void *data)
673128760Spjd{
674128760Spjd	int error = 0;
675128760Spjd
676128760Spjd	switch (type) {
677128760Spjd	case MOD_LOAD:
678204076Spjd		mtx_init(&g_gate_units_lock, "gg_units_lock", NULL, MTX_DEF);
679204076Spjd		g_gate_units = malloc(g_gate_maxunits * sizeof(g_gate_units[0]),
680204076Spjd		    M_GATE, M_WAITOK | M_ZERO);
681204076Spjd		g_gate_nunits = 0;
682131411Spjd		g_gate_device();
683128760Spjd		break;
684128760Spjd	case MOD_UNLOAD:
685204076Spjd		mtx_lock(&g_gate_units_lock);
686204076Spjd		if (g_gate_nunits > 0) {
687204076Spjd			mtx_unlock(&g_gate_units_lock);
688128760Spjd			error = EBUSY;
689128760Spjd			break;
690128760Spjd		}
691204076Spjd		mtx_unlock(&g_gate_units_lock);
692204076Spjd		mtx_destroy(&g_gate_units_lock);
693128760Spjd		if (status_dev != 0)
694128760Spjd			destroy_dev(status_dev);
695204076Spjd		free(g_gate_units, M_GATE);
696128760Spjd		break;
697128760Spjd	default:
698132199Sphk		return (EOPNOTSUPP);
699128760Spjd		break;
700128760Spjd	}
701128760Spjd
702128760Spjd	return (error);
703128760Spjd}
704128760Spjdstatic moduledata_t g_gate_module = {
705128760Spjd	G_GATE_MOD_NAME,
706128760Spjd	g_gate_modevent,
707128760Spjd	NULL
708128760Spjd};
709128760SpjdDECLARE_MODULE(geom_gate, g_gate_module, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
710128760SpjdDECLARE_GEOM_CLASS(g_gate_class, g_gate);
711