xref: /freebsd/sys/geom/gate/g_gate.c (revision 7029da5c36f2d3cf6bb6c81bf551229f416399e8)
1fe27e772SPawel Jakub Dawidek /*-
23728855aSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
33728855aSPedro F. Giffuni  *
4fc024f7aSPawel Jakub Dawidek  * Copyright (c) 2004-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
532115b10SPawel Jakub Dawidek  * Copyright (c) 2009-2010 The FreeBSD Foundation
6fe27e772SPawel Jakub Dawidek  * All rights reserved.
7fe27e772SPawel Jakub Dawidek  *
832115b10SPawel Jakub Dawidek  * Portions of this software were developed by Pawel Jakub Dawidek
932115b10SPawel Jakub Dawidek  * under sponsorship from the FreeBSD Foundation.
1032115b10SPawel Jakub Dawidek  *
11fe27e772SPawel Jakub Dawidek  * Redistribution and use in source and binary forms, with or without
12fe27e772SPawel Jakub Dawidek  * modification, are permitted provided that the following conditions
13fe27e772SPawel Jakub Dawidek  * are met:
14fe27e772SPawel Jakub Dawidek  * 1. Redistributions of source code must retain the above copyright
15fe27e772SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer.
16fe27e772SPawel Jakub Dawidek  * 2. Redistributions in binary form must reproduce the above copyright
17fe27e772SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer in the
18fe27e772SPawel Jakub Dawidek  *    documentation and/or other materials provided with the distribution.
19fe27e772SPawel Jakub Dawidek  *
20fe27e772SPawel Jakub Dawidek  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21fe27e772SPawel Jakub Dawidek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22fe27e772SPawel Jakub Dawidek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23fe27e772SPawel Jakub Dawidek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24fe27e772SPawel Jakub Dawidek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25fe27e772SPawel Jakub Dawidek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26fe27e772SPawel Jakub Dawidek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27fe27e772SPawel Jakub Dawidek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28fe27e772SPawel Jakub Dawidek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29fe27e772SPawel Jakub Dawidek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30fe27e772SPawel Jakub Dawidek  * SUCH DAMAGE.
31fe27e772SPawel Jakub Dawidek  */
32fe27e772SPawel Jakub Dawidek 
33c0767902SPawel Jakub Dawidek #include <sys/cdefs.h>
34c0767902SPawel Jakub Dawidek __FBSDID("$FreeBSD$");
35c0767902SPawel Jakub Dawidek 
36fe27e772SPawel Jakub Dawidek #include <sys/param.h>
37fe27e772SPawel Jakub Dawidek #include <sys/systm.h>
38fe27e772SPawel Jakub Dawidek #include <sys/bio.h>
39fe27e772SPawel Jakub Dawidek #include <sys/conf.h>
40fe27e772SPawel Jakub Dawidek #include <sys/kernel.h>
41fe27e772SPawel Jakub Dawidek #include <sys/kthread.h>
42fe27e772SPawel Jakub Dawidek #include <sys/fcntl.h>
43fe27e772SPawel Jakub Dawidek #include <sys/linker.h>
44fe27e772SPawel Jakub Dawidek #include <sys/lock.h>
45fe27e772SPawel Jakub Dawidek #include <sys/malloc.h>
46fe27e772SPawel Jakub Dawidek #include <sys/mutex.h>
47fe27e772SPawel Jakub Dawidek #include <sys/proc.h>
48fe27e772SPawel Jakub Dawidek #include <sys/limits.h>
49fe27e772SPawel Jakub Dawidek #include <sys/queue.h>
505d807a0eSAndrey V. Elsukov #include <sys/sbuf.h>
51fe27e772SPawel Jakub Dawidek #include <sys/sysctl.h>
52fe27e772SPawel Jakub Dawidek #include <sys/signalvar.h>
53fe27e772SPawel Jakub Dawidek #include <sys/time.h>
54fe27e772SPawel Jakub Dawidek #include <machine/atomic.h>
55fe27e772SPawel Jakub Dawidek 
56fe27e772SPawel Jakub Dawidek #include <geom/geom.h>
57ac03832eSConrad Meyer #include <geom/geom_dbg.h>
58fe27e772SPawel Jakub Dawidek #include <geom/gate/g_gate.h>
59fe27e772SPawel Jakub Dawidek 
60cb08c2ccSAlexander Leidinger FEATURE(geom_gate, "GEOM Gate module");
61cb08c2ccSAlexander Leidinger 
625bb84bc8SRobert Watson static MALLOC_DEFINE(M_GATE, "gg_data", "GEOM Gate Data");
63fe27e772SPawel Jakub Dawidek 
64fe27e772SPawel Jakub Dawidek SYSCTL_DECL(_kern_geom);
65*7029da5cSPawel Biernacki static SYSCTL_NODE(_kern_geom, OID_AUTO, gate, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
66e08ec037SPawel Jakub Dawidek     "GEOM_GATE configuration");
6732115b10SPawel Jakub Dawidek static int g_gate_debug = 0;
68af3b2549SHans Petter Selasky SYSCTL_INT(_kern_geom_gate, OID_AUTO, debug, CTLFLAG_RWTUN, &g_gate_debug, 0,
69fe27e772SPawel Jakub Dawidek     "Debug level");
7032115b10SPawel Jakub Dawidek static u_int g_gate_maxunits = 256;
7132115b10SPawel Jakub Dawidek SYSCTL_UINT(_kern_geom_gate, OID_AUTO, maxunits, CTLFLAG_RDTUN,
7232115b10SPawel Jakub Dawidek     &g_gate_maxunits, 0, "Maximum number of ggate devices");
73fe27e772SPawel Jakub Dawidek 
74fe27e772SPawel Jakub Dawidek struct g_class g_gate_class = {
75fe27e772SPawel Jakub Dawidek 	.name = G_GATE_CLASS_NAME,
765721c9c7SPoul-Henning Kamp 	.version = G_VERSION,
77fe27e772SPawel Jakub Dawidek };
78fe27e772SPawel Jakub Dawidek 
7989c9c53dSPoul-Henning Kamp static struct cdev *status_dev;
80fe27e772SPawel Jakub Dawidek static d_ioctl_t g_gate_ioctl;
81fe27e772SPawel Jakub Dawidek static struct cdevsw g_gate_cdevsw = {
82fe27e772SPawel Jakub Dawidek 	.d_version =	D_VERSION,
83fe27e772SPawel Jakub Dawidek 	.d_ioctl =	g_gate_ioctl,
84fe27e772SPawel Jakub Dawidek 	.d_name =	G_GATE_CTL_NAME
85fe27e772SPawel Jakub Dawidek };
86fe27e772SPawel Jakub Dawidek 
87fe27e772SPawel Jakub Dawidek 
8832115b10SPawel Jakub Dawidek static struct g_gate_softc **g_gate_units;
8932115b10SPawel Jakub Dawidek static u_int g_gate_nunits;
9032115b10SPawel Jakub Dawidek static struct mtx g_gate_units_lock;
91fe27e772SPawel Jakub Dawidek 
92351d0fa6SAlexander Motin static void
93351d0fa6SAlexander Motin g_gate_detach(void *arg, int flags __unused)
94351d0fa6SAlexander Motin {
95351d0fa6SAlexander Motin 	struct g_consumer *cp = arg;
96351d0fa6SAlexander Motin 
97351d0fa6SAlexander Motin 	g_topology_assert();
98351d0fa6SAlexander Motin 	G_GATE_DEBUG(1, "Destroying read consumer on provider %s orphan.",
99351d0fa6SAlexander Motin 	    cp->provider->name);
100351d0fa6SAlexander Motin 	(void)g_access(cp, -1, 0, 0);
101351d0fa6SAlexander Motin 	g_detach(cp);
102351d0fa6SAlexander Motin 	g_destroy_consumer(cp);
103351d0fa6SAlexander Motin }
104351d0fa6SAlexander Motin 
105fe27e772SPawel Jakub Dawidek static int
106fe27e772SPawel Jakub Dawidek g_gate_destroy(struct g_gate_softc *sc, boolean_t force)
107fe27e772SPawel Jakub Dawidek {
10840ea77a0SAlexander Motin 	struct bio_queue_head queue;
109fe27e772SPawel Jakub Dawidek 	struct g_provider *pp;
110e08ec037SPawel Jakub Dawidek 	struct g_consumer *cp;
1117ffb6e0fSPawel Jakub Dawidek 	struct g_geom *gp;
112fe27e772SPawel Jakub Dawidek 	struct bio *bp;
113fe27e772SPawel Jakub Dawidek 
114fe27e772SPawel Jakub Dawidek 	g_topology_assert();
11532115b10SPawel Jakub Dawidek 	mtx_assert(&g_gate_units_lock, MA_OWNED);
116fe27e772SPawel Jakub Dawidek 	pp = sc->sc_provider;
117fe27e772SPawel Jakub Dawidek 	if (!force && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
11832115b10SPawel Jakub Dawidek 		mtx_unlock(&g_gate_units_lock);
119fe27e772SPawel Jakub Dawidek 		return (EBUSY);
120fe27e772SPawel Jakub Dawidek 	}
12132115b10SPawel Jakub Dawidek 	mtx_unlock(&g_gate_units_lock);
122e35d3a78SPawel Jakub Dawidek 	mtx_lock(&sc->sc_queue_mtx);
1237ffb6e0fSPawel Jakub Dawidek 	if ((sc->sc_flags & G_GATE_FLAG_DESTROY) == 0)
1247ffb6e0fSPawel Jakub Dawidek 		sc->sc_flags |= G_GATE_FLAG_DESTROY;
125fe27e772SPawel Jakub Dawidek 	wakeup(sc);
126e35d3a78SPawel Jakub Dawidek 	mtx_unlock(&sc->sc_queue_mtx);
1277ffb6e0fSPawel Jakub Dawidek 	gp = pp->geom;
1288b64f3caSAlexander Motin 	g_wither_provider(pp, ENXIO);
129fe27e772SPawel Jakub Dawidek 	callout_drain(&sc->sc_callout);
13040ea77a0SAlexander Motin 	bioq_init(&queue);
131e35d3a78SPawel Jakub Dawidek 	mtx_lock(&sc->sc_queue_mtx);
13240ea77a0SAlexander Motin 	while ((bp = bioq_takefirst(&sc->sc_inqueue)) != NULL) {
133e35d3a78SPawel Jakub Dawidek 		sc->sc_queue_count--;
13440ea77a0SAlexander Motin 		bioq_insert_tail(&queue, bp);
135fe27e772SPawel Jakub Dawidek 	}
13640ea77a0SAlexander Motin 	while ((bp = bioq_takefirst(&sc->sc_outqueue)) != NULL) {
137e35d3a78SPawel Jakub Dawidek 		sc->sc_queue_count--;
13840ea77a0SAlexander Motin 		bioq_insert_tail(&queue, bp);
139fe27e772SPawel Jakub Dawidek 	}
1407ffb6e0fSPawel Jakub Dawidek 	mtx_unlock(&sc->sc_queue_mtx);
1417ffb6e0fSPawel Jakub Dawidek 	g_topology_unlock();
14240ea77a0SAlexander Motin 	while ((bp = bioq_takefirst(&queue)) != NULL) {
14340ea77a0SAlexander Motin 		G_GATE_LOGREQ(1, bp, "Request canceled.");
14440ea77a0SAlexander Motin 		g_io_deliver(bp, ENXIO);
14540ea77a0SAlexander Motin 	}
14632115b10SPawel Jakub Dawidek 	mtx_lock(&g_gate_units_lock);
1477ffb6e0fSPawel Jakub Dawidek 	/* One reference is ours. */
1487ffb6e0fSPawel Jakub Dawidek 	sc->sc_ref--;
14932115b10SPawel Jakub Dawidek 	while (sc->sc_ref > 0)
15032115b10SPawel Jakub Dawidek 		msleep(&sc->sc_ref, &g_gate_units_lock, 0, "gg:destroy", 0);
15132115b10SPawel Jakub Dawidek 	g_gate_units[sc->sc_unit] = NULL;
15232115b10SPawel Jakub Dawidek 	KASSERT(g_gate_nunits > 0, ("negative g_gate_nunits?"));
15332115b10SPawel Jakub Dawidek 	g_gate_nunits--;
15432115b10SPawel Jakub Dawidek 	mtx_unlock(&g_gate_units_lock);
155e35d3a78SPawel Jakub Dawidek 	mtx_destroy(&sc->sc_queue_mtx);
156351d0fa6SAlexander Motin 	mtx_destroy(&sc->sc_read_mtx);
1577ffb6e0fSPawel Jakub Dawidek 	g_topology_lock();
158e08ec037SPawel Jakub Dawidek 	if ((cp = sc->sc_readcons) != NULL) {
159e08ec037SPawel Jakub Dawidek 		sc->sc_readcons = NULL;
160e08ec037SPawel Jakub Dawidek 		(void)g_access(cp, -1, 0, 0);
161e08ec037SPawel Jakub Dawidek 		g_detach(cp);
162e08ec037SPawel Jakub Dawidek 		g_destroy_consumer(cp);
163e08ec037SPawel Jakub Dawidek 	}
164bd119384SMikolaj Golub 	G_GATE_DEBUG(1, "Device %s destroyed.", gp->name);
1657ffb6e0fSPawel Jakub Dawidek 	gp->softc = NULL;
1667ffb6e0fSPawel Jakub Dawidek 	g_wither_geom(gp, ENXIO);
167fe27e772SPawel Jakub Dawidek 	sc->sc_provider = NULL;
168fe27e772SPawel Jakub Dawidek 	free(sc, M_GATE);
169fe27e772SPawel Jakub Dawidek 	return (0);
170fe27e772SPawel Jakub Dawidek }
171fe27e772SPawel Jakub Dawidek 
172fe27e772SPawel Jakub Dawidek static int
173fe27e772SPawel Jakub Dawidek g_gate_access(struct g_provider *pp, int dr, int dw, int de)
174fe27e772SPawel Jakub Dawidek {
175fe27e772SPawel Jakub Dawidek 	struct g_gate_softc *sc;
176fe27e772SPawel Jakub Dawidek 
177fe27e772SPawel Jakub Dawidek 	if (dr <= 0 && dw <= 0 && de <= 0)
178fe27e772SPawel Jakub Dawidek 		return (0);
179fe27e772SPawel Jakub Dawidek 	sc = pp->geom->softc;
180fe27e772SPawel Jakub Dawidek 	if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0)
181fe27e772SPawel Jakub Dawidek 		return (ENXIO);
18255336b83SPawel Jakub Dawidek 	/* XXX: Hack to allow read-only mounts. */
18355336b83SPawel Jakub Dawidek #if 0
184fe27e772SPawel Jakub Dawidek 	if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0 && dw > 0)
185fe27e772SPawel Jakub Dawidek 		return (EPERM);
18655336b83SPawel Jakub Dawidek #endif
187fe27e772SPawel Jakub Dawidek 	if ((sc->sc_flags & G_GATE_FLAG_WRITEONLY) != 0 && dr > 0)
188fe27e772SPawel Jakub Dawidek 		return (EPERM);
189fe27e772SPawel Jakub Dawidek 	return (0);
190fe27e772SPawel Jakub Dawidek }
191fe27e772SPawel Jakub Dawidek 
192fe27e772SPawel Jakub Dawidek static void
193e08ec037SPawel Jakub Dawidek g_gate_queue_io(struct bio *bp)
194fe27e772SPawel Jakub Dawidek {
195fe27e772SPawel Jakub Dawidek 	struct g_gate_softc *sc;
196fe27e772SPawel Jakub Dawidek 
197fe27e772SPawel Jakub Dawidek 	sc = bp->bio_to->geom->softc;
198fe27e772SPawel Jakub Dawidek 	if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) {
199fe27e772SPawel Jakub Dawidek 		g_io_deliver(bp, ENXIO);
200fe27e772SPawel Jakub Dawidek 		return;
201fe27e772SPawel Jakub Dawidek 	}
202fe27e772SPawel Jakub Dawidek 
203e35d3a78SPawel Jakub Dawidek 	mtx_lock(&sc->sc_queue_mtx);
204e08ec037SPawel Jakub Dawidek 
20563a6c5c1SPawel Jakub Dawidek 	if (sc->sc_queue_size > 0 && sc->sc_queue_count > sc->sc_queue_size) {
20635f855d9SPawel Jakub Dawidek 		mtx_unlock(&sc->sc_queue_mtx);
207fe27e772SPawel Jakub Dawidek 		G_GATE_LOGREQ(1, bp, "Queue full, request canceled.");
20832115b10SPawel Jakub Dawidek 		g_io_deliver(bp, ENOMEM);
209fe27e772SPawel Jakub Dawidek 		return;
210fe27e772SPawel Jakub Dawidek 	}
211e35d3a78SPawel Jakub Dawidek 
212fe27e772SPawel Jakub Dawidek 	bp->bio_driver1 = (void *)sc->sc_seq;
213fe27e772SPawel Jakub Dawidek 	sc->sc_seq++;
214e35d3a78SPawel Jakub Dawidek 	sc->sc_queue_count++;
215fe27e772SPawel Jakub Dawidek 
216662a4e58SPawel Jakub Dawidek 	bioq_insert_tail(&sc->sc_inqueue, bp);
217fe27e772SPawel Jakub Dawidek 	wakeup(sc);
218e35d3a78SPawel Jakub Dawidek 
219e35d3a78SPawel Jakub Dawidek 	mtx_unlock(&sc->sc_queue_mtx);
220fe27e772SPawel Jakub Dawidek }
221fe27e772SPawel Jakub Dawidek 
222e08ec037SPawel Jakub Dawidek static void
223e08ec037SPawel Jakub Dawidek g_gate_done(struct bio *cbp)
224e08ec037SPawel Jakub Dawidek {
225351d0fa6SAlexander Motin 	struct g_gate_softc *sc;
226e08ec037SPawel Jakub Dawidek 	struct bio *pbp;
227351d0fa6SAlexander Motin 	struct g_consumer *cp;
228e08ec037SPawel Jakub Dawidek 
229351d0fa6SAlexander Motin 	cp = cbp->bio_from;
230e08ec037SPawel Jakub Dawidek 	pbp = cbp->bio_parent;
231e08ec037SPawel Jakub Dawidek 	if (cbp->bio_error == 0) {
232e08ec037SPawel Jakub Dawidek 		pbp->bio_completed = cbp->bio_completed;
233e08ec037SPawel Jakub Dawidek 		g_destroy_bio(cbp);
234e08ec037SPawel Jakub Dawidek 		pbp->bio_inbed++;
235e08ec037SPawel Jakub Dawidek 		g_io_deliver(pbp, 0);
236e08ec037SPawel Jakub Dawidek 	} else {
237e08ec037SPawel Jakub Dawidek 		/* If direct read failed, pass it through userland daemon. */
238e08ec037SPawel Jakub Dawidek 		g_destroy_bio(cbp);
239e08ec037SPawel Jakub Dawidek 		pbp->bio_children--;
240e08ec037SPawel Jakub Dawidek 		g_gate_queue_io(pbp);
241e08ec037SPawel Jakub Dawidek 	}
242351d0fa6SAlexander Motin 
243351d0fa6SAlexander Motin 	sc = cp->geom->softc;
244351d0fa6SAlexander Motin 	mtx_lock(&sc->sc_read_mtx);
245351d0fa6SAlexander Motin 	if (--cp->index == 0 && sc->sc_readcons != cp)
246351d0fa6SAlexander Motin 		g_post_event(g_gate_detach, cp, M_NOWAIT, NULL);
247351d0fa6SAlexander Motin 	mtx_unlock(&sc->sc_read_mtx);
248e08ec037SPawel Jakub Dawidek }
249e08ec037SPawel Jakub Dawidek 
250e08ec037SPawel Jakub Dawidek static void
251e08ec037SPawel Jakub Dawidek g_gate_start(struct bio *pbp)
252e08ec037SPawel Jakub Dawidek {
253e08ec037SPawel Jakub Dawidek 	struct g_gate_softc *sc;
254351d0fa6SAlexander Motin 	struct g_consumer *cp;
255351d0fa6SAlexander Motin 	struct bio *cbp;
256e08ec037SPawel Jakub Dawidek 
257e08ec037SPawel Jakub Dawidek 	sc = pbp->bio_to->geom->softc;
258e08ec037SPawel Jakub Dawidek 	if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) {
259e08ec037SPawel Jakub Dawidek 		g_io_deliver(pbp, ENXIO);
260e08ec037SPawel Jakub Dawidek 		return;
261e08ec037SPawel Jakub Dawidek 	}
262e08ec037SPawel Jakub Dawidek 	G_GATE_LOGREQ(2, pbp, "Request received.");
263e08ec037SPawel Jakub Dawidek 	switch (pbp->bio_cmd) {
264e08ec037SPawel Jakub Dawidek 	case BIO_READ:
265351d0fa6SAlexander Motin 		if (sc->sc_readcons == NULL)
266351d0fa6SAlexander Motin 			break;
267e08ec037SPawel Jakub Dawidek 		cbp = g_clone_bio(pbp);
268e08ec037SPawel Jakub Dawidek 		if (cbp == NULL) {
269e08ec037SPawel Jakub Dawidek 			g_io_deliver(pbp, ENOMEM);
270e08ec037SPawel Jakub Dawidek 			return;
271e08ec037SPawel Jakub Dawidek 		}
272351d0fa6SAlexander Motin 		mtx_lock(&sc->sc_read_mtx);
273351d0fa6SAlexander Motin 		if ((cp = sc->sc_readcons) == NULL) {
274351d0fa6SAlexander Motin 			mtx_unlock(&sc->sc_read_mtx);
275351d0fa6SAlexander Motin 			g_destroy_bio(cbp);
276351d0fa6SAlexander Motin 			pbp->bio_children--;
277e08ec037SPawel Jakub Dawidek 			break;
278351d0fa6SAlexander Motin 		}
279351d0fa6SAlexander Motin 		cp->index++;
280351d0fa6SAlexander Motin 		cbp->bio_offset = pbp->bio_offset + sc->sc_readoffset;
281351d0fa6SAlexander Motin 		mtx_unlock(&sc->sc_read_mtx);
282351d0fa6SAlexander Motin 		cbp->bio_done = g_gate_done;
283351d0fa6SAlexander Motin 		g_io_request(cbp, cp);
284351d0fa6SAlexander Motin 		return;
285e08ec037SPawel Jakub Dawidek 	case BIO_DELETE:
286e08ec037SPawel Jakub Dawidek 	case BIO_WRITE:
287e08ec037SPawel Jakub Dawidek 	case BIO_FLUSH:
2888b522bdaSWarner Losh 	case BIO_SPEEDUP:
289e08ec037SPawel Jakub Dawidek 		/* XXX: Hack to allow read-only mounts. */
290e08ec037SPawel Jakub Dawidek 		if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0) {
291e08ec037SPawel Jakub Dawidek 			g_io_deliver(pbp, EPERM);
292e08ec037SPawel Jakub Dawidek 			return;
293e08ec037SPawel Jakub Dawidek 		}
294e08ec037SPawel Jakub Dawidek 		break;
295e08ec037SPawel Jakub Dawidek 	case BIO_GETATTR:
296e08ec037SPawel Jakub Dawidek 	default:
297e08ec037SPawel Jakub Dawidek 		G_GATE_LOGREQ(2, pbp, "Ignoring request.");
298e08ec037SPawel Jakub Dawidek 		g_io_deliver(pbp, EOPNOTSUPP);
299e08ec037SPawel Jakub Dawidek 		return;
300e08ec037SPawel Jakub Dawidek 	}
301e08ec037SPawel Jakub Dawidek 
302e08ec037SPawel Jakub Dawidek 	g_gate_queue_io(pbp);
303e08ec037SPawel Jakub Dawidek }
304e08ec037SPawel Jakub Dawidek 
305fe27e772SPawel Jakub Dawidek static struct g_gate_softc *
3062aa15ffdSPawel Jakub Dawidek g_gate_hold(int unit, const char *name)
307fe27e772SPawel Jakub Dawidek {
30832115b10SPawel Jakub Dawidek 	struct g_gate_softc *sc = NULL;
309fe27e772SPawel Jakub Dawidek 
31032115b10SPawel Jakub Dawidek 	mtx_lock(&g_gate_units_lock);
31132115b10SPawel Jakub Dawidek 	if (unit >= 0 && unit < g_gate_maxunits)
31232115b10SPawel Jakub Dawidek 		sc = g_gate_units[unit];
31332115b10SPawel Jakub Dawidek 	else if (unit == G_GATE_NAME_GIVEN) {
31432115b10SPawel Jakub Dawidek 		KASSERT(name != NULL, ("name is NULL"));
31532115b10SPawel Jakub Dawidek 		for (unit = 0; unit < g_gate_maxunits; unit++) {
31632115b10SPawel Jakub Dawidek 			if (g_gate_units[unit] == NULL)
31732115b10SPawel Jakub Dawidek 				continue;
31832115b10SPawel Jakub Dawidek 			if (strcmp(name,
31932115b10SPawel Jakub Dawidek 			    g_gate_units[unit]->sc_provider->name) != 0) {
32032115b10SPawel Jakub Dawidek 				continue;
32132115b10SPawel Jakub Dawidek 			}
32232115b10SPawel Jakub Dawidek 			sc = g_gate_units[unit];
3237ffb6e0fSPawel Jakub Dawidek 			break;
324fe27e772SPawel Jakub Dawidek 		}
32532115b10SPawel Jakub Dawidek 	}
3267ffb6e0fSPawel Jakub Dawidek 	if (sc != NULL)
3277ffb6e0fSPawel Jakub Dawidek 		sc->sc_ref++;
32832115b10SPawel Jakub Dawidek 	mtx_unlock(&g_gate_units_lock);
329fe27e772SPawel Jakub Dawidek 	return (sc);
330fe27e772SPawel Jakub Dawidek }
331fe27e772SPawel Jakub Dawidek 
332fe27e772SPawel Jakub Dawidek static void
333fe27e772SPawel Jakub Dawidek g_gate_release(struct g_gate_softc *sc)
334fe27e772SPawel Jakub Dawidek {
335fe27e772SPawel Jakub Dawidek 
336fe27e772SPawel Jakub Dawidek 	g_topology_assert_not();
33732115b10SPawel Jakub Dawidek 	mtx_lock(&g_gate_units_lock);
338fe27e772SPawel Jakub Dawidek 	sc->sc_ref--;
339fe27e772SPawel Jakub Dawidek 	KASSERT(sc->sc_ref >= 0, ("Negative sc_ref for %s.", sc->sc_name));
34032115b10SPawel Jakub Dawidek 	if (sc->sc_ref == 0 && (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0)
3417ffb6e0fSPawel Jakub Dawidek 		wakeup(&sc->sc_ref);
34232115b10SPawel Jakub Dawidek 	mtx_unlock(&g_gate_units_lock);
343fe27e772SPawel Jakub Dawidek }
344fe27e772SPawel Jakub Dawidek 
345fe27e772SPawel Jakub Dawidek static int
34632115b10SPawel Jakub Dawidek g_gate_getunit(int unit, int *errorp)
347fe27e772SPawel Jakub Dawidek {
348fe27e772SPawel Jakub Dawidek 
34932115b10SPawel Jakub Dawidek 	mtx_assert(&g_gate_units_lock, MA_OWNED);
350fe27e772SPawel Jakub Dawidek 	if (unit >= 0) {
35132115b10SPawel Jakub Dawidek 		if (unit >= g_gate_maxunits)
35232115b10SPawel Jakub Dawidek 			*errorp = EINVAL;
35332115b10SPawel Jakub Dawidek 		else if (g_gate_units[unit] == NULL)
354fe27e772SPawel Jakub Dawidek 			return (unit);
35532115b10SPawel Jakub Dawidek 		else
35632115b10SPawel Jakub Dawidek 			*errorp = EEXIST;
35732115b10SPawel Jakub Dawidek 	} else {
35832115b10SPawel Jakub Dawidek 		for (unit = 0; unit < g_gate_maxunits; unit++) {
35932115b10SPawel Jakub Dawidek 			if (g_gate_units[unit] == NULL)
36032115b10SPawel Jakub Dawidek 				return (unit);
36132115b10SPawel Jakub Dawidek 		}
36232115b10SPawel Jakub Dawidek 		*errorp = ENFILE;
36332115b10SPawel Jakub Dawidek 	}
36432115b10SPawel Jakub Dawidek 	return (-1);
365fe27e772SPawel Jakub Dawidek }
366fe27e772SPawel Jakub Dawidek 
367fe27e772SPawel Jakub Dawidek static void
368fe27e772SPawel Jakub Dawidek g_gate_guard(void *arg)
369fe27e772SPawel Jakub Dawidek {
37040ea77a0SAlexander Motin 	struct bio_queue_head queue;
371fe27e772SPawel Jakub Dawidek 	struct g_gate_softc *sc;
372fe27e772SPawel Jakub Dawidek 	struct bintime curtime;
373fe27e772SPawel Jakub Dawidek 	struct bio *bp, *bp2;
374fe27e772SPawel Jakub Dawidek 
375fe27e772SPawel Jakub Dawidek 	sc = arg;
376fe27e772SPawel Jakub Dawidek 	binuptime(&curtime);
37732115b10SPawel Jakub Dawidek 	g_gate_hold(sc->sc_unit, NULL);
37840ea77a0SAlexander Motin 	bioq_init(&queue);
379e35d3a78SPawel Jakub Dawidek 	mtx_lock(&sc->sc_queue_mtx);
380fe27e772SPawel Jakub Dawidek 	TAILQ_FOREACH_SAFE(bp, &sc->sc_inqueue.queue, bio_queue, bp2) {
381fe27e772SPawel Jakub Dawidek 		if (curtime.sec - bp->bio_t0.sec < 5)
382fe27e772SPawel Jakub Dawidek 			continue;
383fe27e772SPawel Jakub Dawidek 		bioq_remove(&sc->sc_inqueue, bp);
384e35d3a78SPawel Jakub Dawidek 		sc->sc_queue_count--;
38540ea77a0SAlexander Motin 		bioq_insert_tail(&queue, bp);
386fe27e772SPawel Jakub Dawidek 	}
387fe27e772SPawel Jakub Dawidek 	TAILQ_FOREACH_SAFE(bp, &sc->sc_outqueue.queue, bio_queue, bp2) {
388fe27e772SPawel Jakub Dawidek 		if (curtime.sec - bp->bio_t0.sec < 5)
389fe27e772SPawel Jakub Dawidek 			continue;
390fe27e772SPawel Jakub Dawidek 		bioq_remove(&sc->sc_outqueue, bp);
391e35d3a78SPawel Jakub Dawidek 		sc->sc_queue_count--;
39240ea77a0SAlexander Motin 		bioq_insert_tail(&queue, bp);
39340ea77a0SAlexander Motin 	}
39440ea77a0SAlexander Motin 	mtx_unlock(&sc->sc_queue_mtx);
39540ea77a0SAlexander Motin 	while ((bp = bioq_takefirst(&queue)) != NULL) {
396fe27e772SPawel Jakub Dawidek 		G_GATE_LOGREQ(1, bp, "Request timeout.");
397fe27e772SPawel Jakub Dawidek 		g_io_deliver(bp, EIO);
398fe27e772SPawel Jakub Dawidek 	}
399fe27e772SPawel Jakub Dawidek 	if ((sc->sc_flags & G_GATE_FLAG_DESTROY) == 0) {
400fe27e772SPawel Jakub Dawidek 		callout_reset(&sc->sc_callout, sc->sc_timeout * hz,
401fe27e772SPawel Jakub Dawidek 		    g_gate_guard, sc);
402fe27e772SPawel Jakub Dawidek 	}
403fe27e772SPawel Jakub Dawidek 	g_gate_release(sc);
404fe27e772SPawel Jakub Dawidek }
405fe27e772SPawel Jakub Dawidek 
406fe27e772SPawel Jakub Dawidek static void
407e08ec037SPawel Jakub Dawidek g_gate_orphan(struct g_consumer *cp)
408e08ec037SPawel Jakub Dawidek {
409e08ec037SPawel Jakub Dawidek 	struct g_gate_softc *sc;
410e08ec037SPawel Jakub Dawidek 	struct g_geom *gp;
411351d0fa6SAlexander Motin 	int done;
412e08ec037SPawel Jakub Dawidek 
413e08ec037SPawel Jakub Dawidek 	g_topology_assert();
414e08ec037SPawel Jakub Dawidek 	gp = cp->geom;
415e08ec037SPawel Jakub Dawidek 	sc = gp->softc;
416351d0fa6SAlexander Motin 	mtx_lock(&sc->sc_read_mtx);
417351d0fa6SAlexander Motin 	if (sc->sc_readcons == cp)
418e08ec037SPawel Jakub Dawidek 		sc->sc_readcons = NULL;
419351d0fa6SAlexander Motin 	done = (cp->index == 0);
420351d0fa6SAlexander Motin 	mtx_unlock(&sc->sc_read_mtx);
421351d0fa6SAlexander Motin 	if (done)
422351d0fa6SAlexander Motin 		g_gate_detach(cp, 0);
423e08ec037SPawel Jakub Dawidek }
424e08ec037SPawel Jakub Dawidek 
425e08ec037SPawel Jakub Dawidek static void
426fe27e772SPawel Jakub Dawidek g_gate_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
427fe27e772SPawel Jakub Dawidek     struct g_consumer *cp, struct g_provider *pp)
428fe27e772SPawel Jakub Dawidek {
429fe27e772SPawel Jakub Dawidek 	struct g_gate_softc *sc;
430fe27e772SPawel Jakub Dawidek 
431fe27e772SPawel Jakub Dawidek 	sc = gp->softc;
432fe27e772SPawel Jakub Dawidek 	if (sc == NULL || pp != NULL || cp != NULL)
433fe27e772SPawel Jakub Dawidek 		return;
4341d9db37cSMikolaj Golub 	sc = g_gate_hold(sc->sc_unit, NULL);
4351d9db37cSMikolaj Golub 	if (sc == NULL)
4361d9db37cSMikolaj Golub 		return;
437fe27e772SPawel Jakub Dawidek 	if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0) {
438fe27e772SPawel Jakub Dawidek 		sbuf_printf(sb, "%s<access>%s</access>\n", indent, "read-only");
439fe27e772SPawel Jakub Dawidek 	} else if ((sc->sc_flags & G_GATE_FLAG_WRITEONLY) != 0) {
440fe27e772SPawel Jakub Dawidek 		sbuf_printf(sb, "%s<access>%s</access>\n", indent,
441fe27e772SPawel Jakub Dawidek 		    "write-only");
442fe27e772SPawel Jakub Dawidek 	} else {
443fe27e772SPawel Jakub Dawidek 		sbuf_printf(sb, "%s<access>%s</access>\n", indent,
444fe27e772SPawel Jakub Dawidek 		    "read-write");
445fe27e772SPawel Jakub Dawidek 	}
446e08ec037SPawel Jakub Dawidek 	if (sc->sc_readcons != NULL) {
447e08ec037SPawel Jakub Dawidek 		sbuf_printf(sb, "%s<read_offset>%jd</read_offset>\n",
448e08ec037SPawel Jakub Dawidek 		    indent, (intmax_t)sc->sc_readoffset);
449e08ec037SPawel Jakub Dawidek 		sbuf_printf(sb, "%s<read_provider>%s</read_provider>\n",
450e08ec037SPawel Jakub Dawidek 		    indent, sc->sc_readcons->provider->name);
451e08ec037SPawel Jakub Dawidek 	}
452fe27e772SPawel Jakub Dawidek 	sbuf_printf(sb, "%s<timeout>%u</timeout>\n", indent, sc->sc_timeout);
453fe27e772SPawel Jakub Dawidek 	sbuf_printf(sb, "%s<info>%s</info>\n", indent, sc->sc_info);
454fe27e772SPawel Jakub Dawidek 	sbuf_printf(sb, "%s<queue_count>%u</queue_count>\n", indent,
455fe27e772SPawel Jakub Dawidek 	    sc->sc_queue_count);
456fe27e772SPawel Jakub Dawidek 	sbuf_printf(sb, "%s<queue_size>%u</queue_size>\n", indent,
457fe27e772SPawel Jakub Dawidek 	    sc->sc_queue_size);
458fe27e772SPawel Jakub Dawidek 	sbuf_printf(sb, "%s<ref>%u</ref>\n", indent, sc->sc_ref);
45932115b10SPawel Jakub Dawidek 	sbuf_printf(sb, "%s<unit>%d</unit>\n", indent, sc->sc_unit);
46047f44cb7SPawel Jakub Dawidek 	g_topology_unlock();
461fe27e772SPawel Jakub Dawidek 	g_gate_release(sc);
46247f44cb7SPawel Jakub Dawidek 	g_topology_lock();
463fe27e772SPawel Jakub Dawidek }
464fe27e772SPawel Jakub Dawidek 
465fe27e772SPawel Jakub Dawidek static int
466fe27e772SPawel Jakub Dawidek g_gate_create(struct g_gate_ctl_create *ggio)
467fe27e772SPawel Jakub Dawidek {
468fe27e772SPawel Jakub Dawidek 	struct g_gate_softc *sc;
469fe27e772SPawel Jakub Dawidek 	struct g_geom *gp;
470e08ec037SPawel Jakub Dawidek 	struct g_provider *pp, *ropp;
471e08ec037SPawel Jakub Dawidek 	struct g_consumer *cp;
47232115b10SPawel Jakub Dawidek 	char name[NAME_MAX];
47332115b10SPawel Jakub Dawidek 	int error = 0, unit;
474fe27e772SPawel Jakub Dawidek 
475e08ec037SPawel Jakub Dawidek 	if (ggio->gctl_mediasize <= 0) {
476fe27e772SPawel Jakub Dawidek 		G_GATE_DEBUG(1, "Invalid media size.");
477fe27e772SPawel Jakub Dawidek 		return (EINVAL);
478fe27e772SPawel Jakub Dawidek 	}
479e08ec037SPawel Jakub Dawidek 	if (ggio->gctl_sectorsize <= 0) {
480e08ec037SPawel Jakub Dawidek 		G_GATE_DEBUG(1, "Invalid sector size.");
481e08ec037SPawel Jakub Dawidek 		return (EINVAL);
482e08ec037SPawel Jakub Dawidek 	}
483e08ec037SPawel Jakub Dawidek 	if (!powerof2(ggio->gctl_sectorsize)) {
484fe27e772SPawel Jakub Dawidek 		G_GATE_DEBUG(1, "Invalid sector size.");
485fe27e772SPawel Jakub Dawidek 		return (EINVAL);
486fe27e772SPawel Jakub Dawidek 	}
487662a4e58SPawel Jakub Dawidek 	if ((ggio->gctl_mediasize % ggio->gctl_sectorsize) != 0) {
488662a4e58SPawel Jakub Dawidek 		G_GATE_DEBUG(1, "Invalid media size.");
489662a4e58SPawel Jakub Dawidek 		return (EINVAL);
490662a4e58SPawel Jakub Dawidek 	}
491fe27e772SPawel Jakub Dawidek 	if ((ggio->gctl_flags & G_GATE_FLAG_READONLY) != 0 &&
492fe27e772SPawel Jakub Dawidek 	    (ggio->gctl_flags & G_GATE_FLAG_WRITEONLY) != 0) {
493fe27e772SPawel Jakub Dawidek 		G_GATE_DEBUG(1, "Invalid flags.");
494fe27e772SPawel Jakub Dawidek 		return (EINVAL);
495fe27e772SPawel Jakub Dawidek 	}
49632115b10SPawel Jakub Dawidek 	if (ggio->gctl_unit != G_GATE_UNIT_AUTO &&
49732115b10SPawel Jakub Dawidek 	    ggio->gctl_unit != G_GATE_NAME_GIVEN &&
49832115b10SPawel Jakub Dawidek 	    ggio->gctl_unit < 0) {
499fe27e772SPawel Jakub Dawidek 		G_GATE_DEBUG(1, "Invalid unit number.");
500fe27e772SPawel Jakub Dawidek 		return (EINVAL);
501fe27e772SPawel Jakub Dawidek 	}
50232115b10SPawel Jakub Dawidek 	if (ggio->gctl_unit == G_GATE_NAME_GIVEN &&
50332115b10SPawel Jakub Dawidek 	    ggio->gctl_name[0] == '\0') {
50432115b10SPawel Jakub Dawidek 		G_GATE_DEBUG(1, "No device name.");
50532115b10SPawel Jakub Dawidek 		return (EINVAL);
50632115b10SPawel Jakub Dawidek 	}
507fe27e772SPawel Jakub Dawidek 
508fe27e772SPawel Jakub Dawidek 	sc = malloc(sizeof(*sc), M_GATE, M_WAITOK | M_ZERO);
509fe27e772SPawel Jakub Dawidek 	sc->sc_flags = (ggio->gctl_flags & G_GATE_USERFLAGS);
510fe27e772SPawel Jakub Dawidek 	strlcpy(sc->sc_info, ggio->gctl_info, sizeof(sc->sc_info));
51132115b10SPawel Jakub Dawidek 	sc->sc_seq = 1;
512fe27e772SPawel Jakub Dawidek 	bioq_init(&sc->sc_inqueue);
513fe27e772SPawel Jakub Dawidek 	bioq_init(&sc->sc_outqueue);
514e35d3a78SPawel Jakub Dawidek 	mtx_init(&sc->sc_queue_mtx, "gg:queue", NULL, MTX_DEF);
515351d0fa6SAlexander Motin 	mtx_init(&sc->sc_read_mtx, "gg:read", NULL, MTX_DEF);
516fe27e772SPawel Jakub Dawidek 	sc->sc_queue_count = 0;
517fe27e772SPawel Jakub Dawidek 	sc->sc_queue_size = ggio->gctl_maxcount;
518fe27e772SPawel Jakub Dawidek 	if (sc->sc_queue_size > G_GATE_MAX_QUEUE_SIZE)
519fe27e772SPawel Jakub Dawidek 		sc->sc_queue_size = G_GATE_MAX_QUEUE_SIZE;
520fe27e772SPawel Jakub Dawidek 	sc->sc_timeout = ggio->gctl_timeout;
521fd90e2edSJung-uk Kim 	callout_init(&sc->sc_callout, 1);
522e08ec037SPawel Jakub Dawidek 
52332115b10SPawel Jakub Dawidek 	mtx_lock(&g_gate_units_lock);
52432115b10SPawel Jakub Dawidek 	sc->sc_unit = g_gate_getunit(ggio->gctl_unit, &error);
525a277f47bSMikolaj Golub 	if (sc->sc_unit < 0)
526a277f47bSMikolaj Golub 		goto fail1;
52732115b10SPawel Jakub Dawidek 	if (ggio->gctl_unit == G_GATE_NAME_GIVEN)
52832115b10SPawel Jakub Dawidek 		snprintf(name, sizeof(name), "%s", ggio->gctl_name);
52932115b10SPawel Jakub Dawidek 	else {
53032115b10SPawel Jakub Dawidek 		snprintf(name, sizeof(name), "%s%d", G_GATE_PROVIDER_NAME,
53132115b10SPawel Jakub Dawidek 		    sc->sc_unit);
53232115b10SPawel Jakub Dawidek 	}
53332115b10SPawel Jakub Dawidek 	/* Check for name collision. */
53432115b10SPawel Jakub Dawidek 	for (unit = 0; unit < g_gate_maxunits; unit++) {
53532115b10SPawel Jakub Dawidek 		if (g_gate_units[unit] == NULL)
53632115b10SPawel Jakub Dawidek 			continue;
537baf63f65SMikolaj Golub 		if (strcmp(name, g_gate_units[unit]->sc_name) != 0)
53832115b10SPawel Jakub Dawidek 			continue;
539a277f47bSMikolaj Golub 		error = EEXIST;
540a277f47bSMikolaj Golub 		goto fail1;
54132115b10SPawel Jakub Dawidek 	}
542baf63f65SMikolaj Golub 	sc->sc_name = name;
54332115b10SPawel Jakub Dawidek 	g_gate_units[sc->sc_unit] = sc;
54432115b10SPawel Jakub Dawidek 	g_gate_nunits++;
54532115b10SPawel Jakub Dawidek 	mtx_unlock(&g_gate_units_lock);
54632115b10SPawel Jakub Dawidek 
547a277f47bSMikolaj Golub 	g_topology_lock();
548a277f47bSMikolaj Golub 
549a277f47bSMikolaj Golub 	if (ggio->gctl_readprov[0] == '\0') {
550a277f47bSMikolaj Golub 		ropp = NULL;
551a277f47bSMikolaj Golub 	} else {
552a277f47bSMikolaj Golub 		ropp = g_provider_by_name(ggio->gctl_readprov);
553a277f47bSMikolaj Golub 		if (ropp == NULL) {
554a277f47bSMikolaj Golub 			G_GATE_DEBUG(1, "Provider %s doesn't exist.",
555a277f47bSMikolaj Golub 			    ggio->gctl_readprov);
556a277f47bSMikolaj Golub 			error = EINVAL;
557a277f47bSMikolaj Golub 			goto fail2;
558a277f47bSMikolaj Golub 		}
559a277f47bSMikolaj Golub 		if ((ggio->gctl_readoffset % ggio->gctl_sectorsize) != 0) {
560a277f47bSMikolaj Golub 			G_GATE_DEBUG(1, "Invalid read offset.");
561a277f47bSMikolaj Golub 			error = EINVAL;
562a277f47bSMikolaj Golub 			goto fail2;
563a277f47bSMikolaj Golub 		}
564a277f47bSMikolaj Golub 		if (ggio->gctl_mediasize + ggio->gctl_readoffset >
565a277f47bSMikolaj Golub 		    ropp->mediasize) {
566a277f47bSMikolaj Golub 			G_GATE_DEBUG(1, "Invalid read offset or media size.");
567a277f47bSMikolaj Golub 			error = EINVAL;
568a277f47bSMikolaj Golub 			goto fail2;
569a277f47bSMikolaj Golub 		}
570a277f47bSMikolaj Golub 	}
571a277f47bSMikolaj Golub 
572a277f47bSMikolaj Golub 	gp = g_new_geomf(&g_gate_class, "%s", name);
573a277f47bSMikolaj Golub 	gp->start = g_gate_start;
574a277f47bSMikolaj Golub 	gp->access = g_gate_access;
575a277f47bSMikolaj Golub 	gp->orphan = g_gate_orphan;
576a277f47bSMikolaj Golub 	gp->dumpconf = g_gate_dumpconf;
577a277f47bSMikolaj Golub 	gp->softc = sc;
578a277f47bSMikolaj Golub 
579a277f47bSMikolaj Golub 	if (ropp != NULL) {
580a277f47bSMikolaj Golub 		cp = g_new_consumer(gp);
58140ea77a0SAlexander Motin 		cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
582a277f47bSMikolaj Golub 		error = g_attach(cp, ropp);
583a277f47bSMikolaj Golub 		if (error != 0) {
584a277f47bSMikolaj Golub 			G_GATE_DEBUG(1, "Unable to attach to %s.", ropp->name);
585a277f47bSMikolaj Golub 			goto fail3;
586a277f47bSMikolaj Golub 		}
587a277f47bSMikolaj Golub 		error = g_access(cp, 1, 0, 0);
588a277f47bSMikolaj Golub 		if (error != 0) {
589a277f47bSMikolaj Golub 			G_GATE_DEBUG(1, "Unable to access %s.", ropp->name);
590a277f47bSMikolaj Golub 			g_detach(cp);
591a277f47bSMikolaj Golub 			goto fail3;
592a277f47bSMikolaj Golub 		}
593a277f47bSMikolaj Golub 		sc->sc_readcons = cp;
594a277f47bSMikolaj Golub 		sc->sc_readoffset = ggio->gctl_readoffset;
595a277f47bSMikolaj Golub 	}
596a277f47bSMikolaj Golub 
59732115b10SPawel Jakub Dawidek 	ggio->gctl_unit = sc->sc_unit;
598fe27e772SPawel Jakub Dawidek 
59932115b10SPawel Jakub Dawidek 	pp = g_new_providerf(gp, "%s", name);
60040ea77a0SAlexander Motin 	pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE;
601fe27e772SPawel Jakub Dawidek 	pp->mediasize = ggio->gctl_mediasize;
602fe27e772SPawel Jakub Dawidek 	pp->sectorsize = ggio->gctl_sectorsize;
603fe27e772SPawel Jakub Dawidek 	sc->sc_provider = pp;
604fe27e772SPawel Jakub Dawidek 	g_error_provider(pp, 0);
605e08ec037SPawel Jakub Dawidek 
606fe27e772SPawel Jakub Dawidek 	g_topology_unlock();
607baf63f65SMikolaj Golub 	mtx_lock(&g_gate_units_lock);
608baf63f65SMikolaj Golub 	sc->sc_name = sc->sc_provider->name;
609baf63f65SMikolaj Golub 	mtx_unlock(&g_gate_units_lock);
610bd119384SMikolaj Golub 	G_GATE_DEBUG(1, "Device %s created.", gp->name);
611fe27e772SPawel Jakub Dawidek 
612fe27e772SPawel Jakub Dawidek 	if (sc->sc_timeout > 0) {
613fe27e772SPawel Jakub Dawidek 		callout_reset(&sc->sc_callout, sc->sc_timeout * hz,
614fe27e772SPawel Jakub Dawidek 		    g_gate_guard, sc);
615fe27e772SPawel Jakub Dawidek 	}
616fe27e772SPawel Jakub Dawidek 	return (0);
617a277f47bSMikolaj Golub fail3:
618a277f47bSMikolaj Golub 	g_destroy_consumer(cp);
619a277f47bSMikolaj Golub 	g_destroy_geom(gp);
620a277f47bSMikolaj Golub fail2:
621a277f47bSMikolaj Golub 	g_topology_unlock();
622a277f47bSMikolaj Golub 	mtx_lock(&g_gate_units_lock);
623a277f47bSMikolaj Golub 	g_gate_units[sc->sc_unit] = NULL;
624a277f47bSMikolaj Golub 	KASSERT(g_gate_nunits > 0, ("negative g_gate_nunits?"));
625a277f47bSMikolaj Golub 	g_gate_nunits--;
626a277f47bSMikolaj Golub fail1:
627a277f47bSMikolaj Golub 	mtx_unlock(&g_gate_units_lock);
628a277f47bSMikolaj Golub 	mtx_destroy(&sc->sc_queue_mtx);
629351d0fa6SAlexander Motin 	mtx_destroy(&sc->sc_read_mtx);
630a277f47bSMikolaj Golub 	free(sc, M_GATE);
631a277f47bSMikolaj Golub 	return (error);
632fe27e772SPawel Jakub Dawidek }
633fe27e772SPawel Jakub Dawidek 
634e08ec037SPawel Jakub Dawidek static int
635e08ec037SPawel Jakub Dawidek g_gate_modify(struct g_gate_softc *sc, struct g_gate_ctl_modify *ggio)
636e08ec037SPawel Jakub Dawidek {
637e08ec037SPawel Jakub Dawidek 	struct g_provider *pp;
638e08ec037SPawel Jakub Dawidek 	struct g_consumer *cp;
639351d0fa6SAlexander Motin 	int done, error;
640e08ec037SPawel Jakub Dawidek 
641e08ec037SPawel Jakub Dawidek 	if ((ggio->gctl_modify & GG_MODIFY_MEDIASIZE) != 0) {
642e08ec037SPawel Jakub Dawidek 		if (ggio->gctl_mediasize <= 0) {
643e08ec037SPawel Jakub Dawidek 			G_GATE_DEBUG(1, "Invalid media size.");
644e08ec037SPawel Jakub Dawidek 			return (EINVAL);
645e08ec037SPawel Jakub Dawidek 		}
646e08ec037SPawel Jakub Dawidek 		pp = sc->sc_provider;
647e08ec037SPawel Jakub Dawidek 		if ((ggio->gctl_mediasize % pp->sectorsize) != 0) {
648e08ec037SPawel Jakub Dawidek 			G_GATE_DEBUG(1, "Invalid media size.");
649e08ec037SPawel Jakub Dawidek 			return (EINVAL);
650e08ec037SPawel Jakub Dawidek 		}
651874774c5SMikolaj Golub 		g_resize_provider(pp, ggio->gctl_mediasize);
652874774c5SMikolaj Golub 		return (0);
653e08ec037SPawel Jakub Dawidek 	}
654e08ec037SPawel Jakub Dawidek 
655e08ec037SPawel Jakub Dawidek 	if ((ggio->gctl_modify & GG_MODIFY_INFO) != 0)
656e08ec037SPawel Jakub Dawidek 		(void)strlcpy(sc->sc_info, ggio->gctl_info, sizeof(sc->sc_info));
657e08ec037SPawel Jakub Dawidek 
658e08ec037SPawel Jakub Dawidek 	cp = NULL;
659e08ec037SPawel Jakub Dawidek 
660e08ec037SPawel Jakub Dawidek 	if ((ggio->gctl_modify & GG_MODIFY_READPROV) != 0) {
661e08ec037SPawel Jakub Dawidek 		g_topology_lock();
662351d0fa6SAlexander Motin 		mtx_lock(&sc->sc_read_mtx);
663351d0fa6SAlexander Motin 		if ((cp = sc->sc_readcons) != NULL) {
664e08ec037SPawel Jakub Dawidek 			sc->sc_readcons = NULL;
665351d0fa6SAlexander Motin 			done = (cp->index == 0);
666351d0fa6SAlexander Motin 			mtx_unlock(&sc->sc_read_mtx);
667351d0fa6SAlexander Motin 			if (done)
668351d0fa6SAlexander Motin 				g_gate_detach(cp, 0);
669351d0fa6SAlexander Motin 		} else
670351d0fa6SAlexander Motin 			mtx_unlock(&sc->sc_read_mtx);
671e08ec037SPawel Jakub Dawidek 		if (ggio->gctl_readprov[0] != '\0') {
672e08ec037SPawel Jakub Dawidek 			pp = g_provider_by_name(ggio->gctl_readprov);
673e08ec037SPawel Jakub Dawidek 			if (pp == NULL) {
674e08ec037SPawel Jakub Dawidek 				g_topology_unlock();
675e08ec037SPawel Jakub Dawidek 				G_GATE_DEBUG(1, "Provider %s doesn't exist.",
676e08ec037SPawel Jakub Dawidek 				    ggio->gctl_readprov);
677e08ec037SPawel Jakub Dawidek 				return (EINVAL);
678e08ec037SPawel Jakub Dawidek 			}
679e08ec037SPawel Jakub Dawidek 			cp = g_new_consumer(sc->sc_provider->geom);
68040ea77a0SAlexander Motin 			cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
681e08ec037SPawel Jakub Dawidek 			error = g_attach(cp, pp);
682e08ec037SPawel Jakub Dawidek 			if (error != 0) {
683e08ec037SPawel Jakub Dawidek 				G_GATE_DEBUG(1, "Unable to attach to %s.",
684e08ec037SPawel Jakub Dawidek 				    pp->name);
685e08ec037SPawel Jakub Dawidek 			} else {
686e08ec037SPawel Jakub Dawidek 				error = g_access(cp, 1, 0, 0);
687e08ec037SPawel Jakub Dawidek 				if (error != 0) {
688e08ec037SPawel Jakub Dawidek 					G_GATE_DEBUG(1, "Unable to access %s.",
689e08ec037SPawel Jakub Dawidek 					    pp->name);
690e08ec037SPawel Jakub Dawidek 					g_detach(cp);
691e08ec037SPawel Jakub Dawidek 				}
692e08ec037SPawel Jakub Dawidek 			}
693e08ec037SPawel Jakub Dawidek 			if (error != 0) {
694e08ec037SPawel Jakub Dawidek 				g_destroy_consumer(cp);
695e08ec037SPawel Jakub Dawidek 				g_topology_unlock();
696e08ec037SPawel Jakub Dawidek 				return (error);
697e08ec037SPawel Jakub Dawidek 			}
698e08ec037SPawel Jakub Dawidek 		}
699e08ec037SPawel Jakub Dawidek 	} else {
700e08ec037SPawel Jakub Dawidek 		cp = sc->sc_readcons;
701e08ec037SPawel Jakub Dawidek 	}
702e08ec037SPawel Jakub Dawidek 
703e08ec037SPawel Jakub Dawidek 	if ((ggio->gctl_modify & GG_MODIFY_READOFFSET) != 0) {
704e08ec037SPawel Jakub Dawidek 		if (cp == NULL) {
705e08ec037SPawel Jakub Dawidek 			G_GATE_DEBUG(1, "No read provider.");
706e08ec037SPawel Jakub Dawidek 			return (EINVAL);
707e08ec037SPawel Jakub Dawidek 		}
708e08ec037SPawel Jakub Dawidek 		pp = sc->sc_provider;
709e08ec037SPawel Jakub Dawidek 		if ((ggio->gctl_readoffset % pp->sectorsize) != 0) {
710e08ec037SPawel Jakub Dawidek 			G_GATE_DEBUG(1, "Invalid read offset.");
711e08ec037SPawel Jakub Dawidek 			return (EINVAL);
712e08ec037SPawel Jakub Dawidek 		}
713e08ec037SPawel Jakub Dawidek 		if (pp->mediasize + ggio->gctl_readoffset >
714e08ec037SPawel Jakub Dawidek 		    cp->provider->mediasize) {
715e08ec037SPawel Jakub Dawidek 			G_GATE_DEBUG(1, "Invalid read offset or media size.");
716e08ec037SPawel Jakub Dawidek 			return (EINVAL);
717e08ec037SPawel Jakub Dawidek 		}
718e08ec037SPawel Jakub Dawidek 		sc->sc_readoffset = ggio->gctl_readoffset;
719e08ec037SPawel Jakub Dawidek 	}
720e08ec037SPawel Jakub Dawidek 
721e08ec037SPawel Jakub Dawidek 	if ((ggio->gctl_modify & GG_MODIFY_READPROV) != 0) {
722e08ec037SPawel Jakub Dawidek 		sc->sc_readcons = cp;
723e08ec037SPawel Jakub Dawidek 		g_topology_unlock();
724e08ec037SPawel Jakub Dawidek 	}
725e08ec037SPawel Jakub Dawidek 
726e08ec037SPawel Jakub Dawidek 	return (0);
727e08ec037SPawel Jakub Dawidek }
728e08ec037SPawel Jakub Dawidek 
729fe27e772SPawel Jakub Dawidek #define	G_GATE_CHECK_VERSION(ggio)	do {				\
73084436f14SPawel Jakub Dawidek 	if ((ggio)->gctl_version != G_GATE_VERSION) {			\
73184436f14SPawel Jakub Dawidek 		printf("Version mismatch %d != %d.\n",			\
73284436f14SPawel Jakub Dawidek 		    ggio->gctl_version, G_GATE_VERSION);		\
733fe27e772SPawel Jakub Dawidek 		return (EINVAL);					\
73484436f14SPawel Jakub Dawidek 	}								\
735fe27e772SPawel Jakub Dawidek } while (0)
736fe27e772SPawel Jakub Dawidek static int
73789c9c53dSPoul-Henning Kamp g_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td)
738fe27e772SPawel Jakub Dawidek {
739fe27e772SPawel Jakub Dawidek 	struct g_gate_softc *sc;
740fe27e772SPawel Jakub Dawidek 	struct bio *bp;
741fe27e772SPawel Jakub Dawidek 	int error = 0;
742fe27e772SPawel Jakub Dawidek 
743fe27e772SPawel Jakub Dawidek 	G_GATE_DEBUG(4, "ioctl(%s, %lx, %p, %x, %p)", devtoname(dev), cmd, addr,
744fe27e772SPawel Jakub Dawidek 	    flags, td);
745fe27e772SPawel Jakub Dawidek 
746fe27e772SPawel Jakub Dawidek 	switch (cmd) {
747fe27e772SPawel Jakub Dawidek 	case G_GATE_CMD_CREATE:
748fe27e772SPawel Jakub Dawidek 	    {
749fe27e772SPawel Jakub Dawidek 		struct g_gate_ctl_create *ggio = (void *)addr;
750fe27e772SPawel Jakub Dawidek 
751fe27e772SPawel Jakub Dawidek 		G_GATE_CHECK_VERSION(ggio);
752a17dd95fSPawel Jakub Dawidek 		error = g_gate_create(ggio);
753f9065812SPawel Jakub Dawidek 		/*
754f9065812SPawel Jakub Dawidek 		 * Reset TDP_GEOM flag.
755f9065812SPawel Jakub Dawidek 		 * There are pending events for sure, because we just created
756f9065812SPawel Jakub Dawidek 		 * new provider and other classes want to taste it, but we
757f9065812SPawel Jakub Dawidek 		 * cannot answer on I/O requests until we're here.
758f9065812SPawel Jakub Dawidek 		 */
759f9065812SPawel Jakub Dawidek 		td->td_pflags &= ~TDP_GEOM;
760a17dd95fSPawel Jakub Dawidek 		return (error);
761fe27e772SPawel Jakub Dawidek 	    }
762e08ec037SPawel Jakub Dawidek 	case G_GATE_CMD_MODIFY:
763e08ec037SPawel Jakub Dawidek 	    {
764e08ec037SPawel Jakub Dawidek 		struct g_gate_ctl_modify *ggio = (void *)addr;
765e08ec037SPawel Jakub Dawidek 
766e08ec037SPawel Jakub Dawidek 		G_GATE_CHECK_VERSION(ggio);
767e08ec037SPawel Jakub Dawidek 		sc = g_gate_hold(ggio->gctl_unit, NULL);
768e08ec037SPawel Jakub Dawidek 		if (sc == NULL)
769e08ec037SPawel Jakub Dawidek 			return (ENXIO);
770e08ec037SPawel Jakub Dawidek 		error = g_gate_modify(sc, ggio);
771e08ec037SPawel Jakub Dawidek 		g_gate_release(sc);
772e08ec037SPawel Jakub Dawidek 		return (error);
773e08ec037SPawel Jakub Dawidek 	    }
774fe27e772SPawel Jakub Dawidek 	case G_GATE_CMD_DESTROY:
775fe27e772SPawel Jakub Dawidek 	    {
776fe27e772SPawel Jakub Dawidek 		struct g_gate_ctl_destroy *ggio = (void *)addr;
777fe27e772SPawel Jakub Dawidek 
778fe27e772SPawel Jakub Dawidek 		G_GATE_CHECK_VERSION(ggio);
77932115b10SPawel Jakub Dawidek 		sc = g_gate_hold(ggio->gctl_unit, ggio->gctl_name);
780fe27e772SPawel Jakub Dawidek 		if (sc == NULL)
781fe27e772SPawel Jakub Dawidek 			return (ENXIO);
782fe27e772SPawel Jakub Dawidek 		g_topology_lock();
78332115b10SPawel Jakub Dawidek 		mtx_lock(&g_gate_units_lock);
784fe27e772SPawel Jakub Dawidek 		error = g_gate_destroy(sc, ggio->gctl_force);
785fe27e772SPawel Jakub Dawidek 		g_topology_unlock();
7867ffb6e0fSPawel Jakub Dawidek 		if (error != 0)
787fe27e772SPawel Jakub Dawidek 			g_gate_release(sc);
788fe27e772SPawel Jakub Dawidek 		return (error);
789fe27e772SPawel Jakub Dawidek 	    }
79084436f14SPawel Jakub Dawidek 	case G_GATE_CMD_CANCEL:
79184436f14SPawel Jakub Dawidek 	    {
79284436f14SPawel Jakub Dawidek 		struct g_gate_ctl_cancel *ggio = (void *)addr;
79384436f14SPawel Jakub Dawidek 		struct bio *tbp, *lbp;
79484436f14SPawel Jakub Dawidek 
79584436f14SPawel Jakub Dawidek 		G_GATE_CHECK_VERSION(ggio);
79632115b10SPawel Jakub Dawidek 		sc = g_gate_hold(ggio->gctl_unit, ggio->gctl_name);
79784436f14SPawel Jakub Dawidek 		if (sc == NULL)
79884436f14SPawel Jakub Dawidek 			return (ENXIO);
79984436f14SPawel Jakub Dawidek 		lbp = NULL;
80084436f14SPawel Jakub Dawidek 		mtx_lock(&sc->sc_queue_mtx);
80184436f14SPawel Jakub Dawidek 		TAILQ_FOREACH_SAFE(bp, &sc->sc_outqueue.queue, bio_queue, tbp) {
80284436f14SPawel Jakub Dawidek 			if (ggio->gctl_seq == 0 ||
80384436f14SPawel Jakub Dawidek 			    ggio->gctl_seq == (uintptr_t)bp->bio_driver1) {
80484436f14SPawel Jakub Dawidek 				G_GATE_LOGREQ(1, bp, "Request canceled.");
80584436f14SPawel Jakub Dawidek 				bioq_remove(&sc->sc_outqueue, bp);
80684436f14SPawel Jakub Dawidek 				/*
80784436f14SPawel Jakub Dawidek 				 * Be sure to put requests back onto incoming
80884436f14SPawel Jakub Dawidek 				 * queue in the proper order.
80984436f14SPawel Jakub Dawidek 				 */
81084436f14SPawel Jakub Dawidek 				if (lbp == NULL)
81184436f14SPawel Jakub Dawidek 					bioq_insert_head(&sc->sc_inqueue, bp);
81284436f14SPawel Jakub Dawidek 				else {
81384436f14SPawel Jakub Dawidek 					TAILQ_INSERT_AFTER(&sc->sc_inqueue.queue,
81484436f14SPawel Jakub Dawidek 					    lbp, bp, bio_queue);
81584436f14SPawel Jakub Dawidek 				}
81684436f14SPawel Jakub Dawidek 				lbp = bp;
81784436f14SPawel Jakub Dawidek 				/*
81884436f14SPawel Jakub Dawidek 				 * If only one request was canceled, leave now.
81984436f14SPawel Jakub Dawidek 				 */
82084436f14SPawel Jakub Dawidek 				if (ggio->gctl_seq != 0)
82184436f14SPawel Jakub Dawidek 					break;
82284436f14SPawel Jakub Dawidek 			}
82384436f14SPawel Jakub Dawidek 		}
82432115b10SPawel Jakub Dawidek 		if (ggio->gctl_unit == G_GATE_NAME_GIVEN)
82532115b10SPawel Jakub Dawidek 			ggio->gctl_unit = sc->sc_unit;
82684436f14SPawel Jakub Dawidek 		mtx_unlock(&sc->sc_queue_mtx);
82784436f14SPawel Jakub Dawidek 		g_gate_release(sc);
82884436f14SPawel Jakub Dawidek 		return (error);
82984436f14SPawel Jakub Dawidek 	    }
830fe27e772SPawel Jakub Dawidek 	case G_GATE_CMD_START:
831fe27e772SPawel Jakub Dawidek 	    {
832fe27e772SPawel Jakub Dawidek 		struct g_gate_ctl_io *ggio = (void *)addr;
833fe27e772SPawel Jakub Dawidek 
834fe27e772SPawel Jakub Dawidek 		G_GATE_CHECK_VERSION(ggio);
83532115b10SPawel Jakub Dawidek 		sc = g_gate_hold(ggio->gctl_unit, NULL);
836fe27e772SPawel Jakub Dawidek 		if (sc == NULL)
837fe27e772SPawel Jakub Dawidek 			return (ENXIO);
8387ffb6e0fSPawel Jakub Dawidek 		error = 0;
839fe27e772SPawel Jakub Dawidek 		for (;;) {
840e35d3a78SPawel Jakub Dawidek 			mtx_lock(&sc->sc_queue_mtx);
841fe27e772SPawel Jakub Dawidek 			bp = bioq_first(&sc->sc_inqueue);
842fe27e772SPawel Jakub Dawidek 			if (bp != NULL)
843fe27e772SPawel Jakub Dawidek 				break;
8447ffb6e0fSPawel Jakub Dawidek 			if ((sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) {
8457ffb6e0fSPawel Jakub Dawidek 				ggio->gctl_error = ECANCELED;
8467ffb6e0fSPawel Jakub Dawidek 				mtx_unlock(&sc->sc_queue_mtx);
8477ffb6e0fSPawel Jakub Dawidek 				goto start_end;
8487ffb6e0fSPawel Jakub Dawidek 			}
849e35d3a78SPawel Jakub Dawidek 			if (msleep(sc, &sc->sc_queue_mtx,
850fe27e772SPawel Jakub Dawidek 			    PPAUSE | PDROP | PCATCH, "ggwait", 0) != 0) {
851fe27e772SPawel Jakub Dawidek 				ggio->gctl_error = ECANCELED;
8527ffb6e0fSPawel Jakub Dawidek 				goto start_end;
853fe27e772SPawel Jakub Dawidek 			}
854fe27e772SPawel Jakub Dawidek 		}
8554d1e1bf3SPawel Jakub Dawidek 		ggio->gctl_cmd = bp->bio_cmd;
856c4d2d401SPawel Jakub Dawidek 		if (bp->bio_cmd == BIO_WRITE &&
857fe27e772SPawel Jakub Dawidek 		    bp->bio_length > ggio->gctl_length) {
858e35d3a78SPawel Jakub Dawidek 			mtx_unlock(&sc->sc_queue_mtx);
859fe27e772SPawel Jakub Dawidek 			ggio->gctl_length = bp->bio_length;
860fe27e772SPawel Jakub Dawidek 			ggio->gctl_error = ENOMEM;
8617ffb6e0fSPawel Jakub Dawidek 			goto start_end;
862fe27e772SPawel Jakub Dawidek 		}
863fe27e772SPawel Jakub Dawidek 		bioq_remove(&sc->sc_inqueue, bp);
864e35d3a78SPawel Jakub Dawidek 		bioq_insert_tail(&sc->sc_outqueue, bp);
865e35d3a78SPawel Jakub Dawidek 		mtx_unlock(&sc->sc_queue_mtx);
866e35d3a78SPawel Jakub Dawidek 
8670d785336SPawel Jakub Dawidek 		ggio->gctl_seq = (uintptr_t)bp->bio_driver1;
868fe27e772SPawel Jakub Dawidek 		ggio->gctl_offset = bp->bio_offset;
869fe27e772SPawel Jakub Dawidek 		ggio->gctl_length = bp->bio_length;
87084436f14SPawel Jakub Dawidek 
871fe27e772SPawel Jakub Dawidek 		switch (bp->bio_cmd) {
872fe27e772SPawel Jakub Dawidek 		case BIO_READ:
873fe27e772SPawel Jakub Dawidek 		case BIO_DELETE:
874204a4e19SPawel Jakub Dawidek 		case BIO_FLUSH:
8758b522bdaSWarner Losh 		case BIO_SPEEDUP:
87615725379SPawel Jakub Dawidek 			break;
877fe27e772SPawel Jakub Dawidek 		case BIO_WRITE:
878fe27e772SPawel Jakub Dawidek 			error = copyout(bp->bio_data, ggio->gctl_data,
879fe27e772SPawel Jakub Dawidek 			    bp->bio_length);
880fe27e772SPawel Jakub Dawidek 			if (error != 0) {
881e35d3a78SPawel Jakub Dawidek 				mtx_lock(&sc->sc_queue_mtx);
882e35d3a78SPawel Jakub Dawidek 				bioq_remove(&sc->sc_outqueue, bp);
883662a4e58SPawel Jakub Dawidek 				bioq_insert_head(&sc->sc_inqueue, bp);
884e35d3a78SPawel Jakub Dawidek 				mtx_unlock(&sc->sc_queue_mtx);
8857ffb6e0fSPawel Jakub Dawidek 				goto start_end;
886fe27e772SPawel Jakub Dawidek 			}
887fe27e772SPawel Jakub Dawidek 			break;
888fe27e772SPawel Jakub Dawidek 		}
8897ffb6e0fSPawel Jakub Dawidek start_end:
8907ffb6e0fSPawel Jakub Dawidek 		g_gate_release(sc);
8917ffb6e0fSPawel Jakub Dawidek 		return (error);
892fe27e772SPawel Jakub Dawidek 	    }
893fe27e772SPawel Jakub Dawidek 	case G_GATE_CMD_DONE:
894fe27e772SPawel Jakub Dawidek 	    {
895fe27e772SPawel Jakub Dawidek 		struct g_gate_ctl_io *ggio = (void *)addr;
896fe27e772SPawel Jakub Dawidek 
897fe27e772SPawel Jakub Dawidek 		G_GATE_CHECK_VERSION(ggio);
89832115b10SPawel Jakub Dawidek 		sc = g_gate_hold(ggio->gctl_unit, NULL);
899fe27e772SPawel Jakub Dawidek 		if (sc == NULL)
900fe27e772SPawel Jakub Dawidek 			return (ENOENT);
9017ffb6e0fSPawel Jakub Dawidek 		error = 0;
902e35d3a78SPawel Jakub Dawidek 		mtx_lock(&sc->sc_queue_mtx);
903fe27e772SPawel Jakub Dawidek 		TAILQ_FOREACH(bp, &sc->sc_outqueue.queue, bio_queue) {
9040d785336SPawel Jakub Dawidek 			if (ggio->gctl_seq == (uintptr_t)bp->bio_driver1)
905fe27e772SPawel Jakub Dawidek 				break;
906fe27e772SPawel Jakub Dawidek 		}
907fe27e772SPawel Jakub Dawidek 		if (bp != NULL) {
908fe27e772SPawel Jakub Dawidek 			bioq_remove(&sc->sc_outqueue, bp);
909e35d3a78SPawel Jakub Dawidek 			sc->sc_queue_count--;
910fe27e772SPawel Jakub Dawidek 		}
911e35d3a78SPawel Jakub Dawidek 		mtx_unlock(&sc->sc_queue_mtx);
912fe27e772SPawel Jakub Dawidek 		if (bp == NULL) {
913fe27e772SPawel Jakub Dawidek 			/*
914fe27e772SPawel Jakub Dawidek 			 * Request was probably canceled.
915fe27e772SPawel Jakub Dawidek 			 */
9167ffb6e0fSPawel Jakub Dawidek 			goto done_end;
917fe27e772SPawel Jakub Dawidek 		}
918fe27e772SPawel Jakub Dawidek 		if (ggio->gctl_error == EAGAIN) {
919fe27e772SPawel Jakub Dawidek 			bp->bio_error = 0;
920fe27e772SPawel Jakub Dawidek 			G_GATE_LOGREQ(1, bp, "Request desisted.");
921e35d3a78SPawel Jakub Dawidek 			mtx_lock(&sc->sc_queue_mtx);
922e35d3a78SPawel Jakub Dawidek 			sc->sc_queue_count++;
923662a4e58SPawel Jakub Dawidek 			bioq_insert_head(&sc->sc_inqueue, bp);
924fe27e772SPawel Jakub Dawidek 			wakeup(sc);
925e35d3a78SPawel Jakub Dawidek 			mtx_unlock(&sc->sc_queue_mtx);
926fe27e772SPawel Jakub Dawidek 		} else {
927fe27e772SPawel Jakub Dawidek 			bp->bio_error = ggio->gctl_error;
928fe27e772SPawel Jakub Dawidek 			if (bp->bio_error == 0) {
929fe27e772SPawel Jakub Dawidek 				bp->bio_completed = bp->bio_length;
930fe27e772SPawel Jakub Dawidek 				switch (bp->bio_cmd) {
931fe27e772SPawel Jakub Dawidek 				case BIO_READ:
932fe27e772SPawel Jakub Dawidek 					error = copyin(ggio->gctl_data,
933fe27e772SPawel Jakub Dawidek 					    bp->bio_data, bp->bio_length);
934fe27e772SPawel Jakub Dawidek 					if (error != 0)
935fe27e772SPawel Jakub Dawidek 						bp->bio_error = error;
936fe27e772SPawel Jakub Dawidek 					break;
937fe27e772SPawel Jakub Dawidek 				case BIO_DELETE:
938fe27e772SPawel Jakub Dawidek 				case BIO_WRITE:
939204a4e19SPawel Jakub Dawidek 				case BIO_FLUSH:
9408b522bdaSWarner Losh 				case BIO_SPEEDUP:
941fe27e772SPawel Jakub Dawidek 					break;
942fe27e772SPawel Jakub Dawidek 				}
943fe27e772SPawel Jakub Dawidek 			}
944fe27e772SPawel Jakub Dawidek 			G_GATE_LOGREQ(2, bp, "Request done.");
945fe27e772SPawel Jakub Dawidek 			g_io_deliver(bp, bp->bio_error);
946fe27e772SPawel Jakub Dawidek 		}
9477ffb6e0fSPawel Jakub Dawidek done_end:
9487ffb6e0fSPawel Jakub Dawidek 		g_gate_release(sc);
949fe27e772SPawel Jakub Dawidek 		return (error);
950fe27e772SPawel Jakub Dawidek 	    }
951fe27e772SPawel Jakub Dawidek 	}
952fe27e772SPawel Jakub Dawidek 	return (ENOIOCTL);
953fe27e772SPawel Jakub Dawidek }
954fe27e772SPawel Jakub Dawidek 
955fe27e772SPawel Jakub Dawidek static void
9569ecdd506SPawel Jakub Dawidek g_gate_device(void)
957fe27e772SPawel Jakub Dawidek {
958fe27e772SPawel Jakub Dawidek 
959fe27e772SPawel Jakub Dawidek 	status_dev = make_dev(&g_gate_cdevsw, 0x0, UID_ROOT, GID_WHEEL, 0600,
960fe27e772SPawel Jakub Dawidek 	    G_GATE_CTL_NAME);
961fe27e772SPawel Jakub Dawidek }
962fe27e772SPawel Jakub Dawidek 
963fe27e772SPawel Jakub Dawidek static int
964fe27e772SPawel Jakub Dawidek g_gate_modevent(module_t mod, int type, void *data)
965fe27e772SPawel Jakub Dawidek {
966fe27e772SPawel Jakub Dawidek 	int error = 0;
967fe27e772SPawel Jakub Dawidek 
968fe27e772SPawel Jakub Dawidek 	switch (type) {
969fe27e772SPawel Jakub Dawidek 	case MOD_LOAD:
97032115b10SPawel Jakub Dawidek 		mtx_init(&g_gate_units_lock, "gg_units_lock", NULL, MTX_DEF);
97132115b10SPawel Jakub Dawidek 		g_gate_units = malloc(g_gate_maxunits * sizeof(g_gate_units[0]),
97232115b10SPawel Jakub Dawidek 		    M_GATE, M_WAITOK | M_ZERO);
97332115b10SPawel Jakub Dawidek 		g_gate_nunits = 0;
9749ecdd506SPawel Jakub Dawidek 		g_gate_device();
975fe27e772SPawel Jakub Dawidek 		break;
976fe27e772SPawel Jakub Dawidek 	case MOD_UNLOAD:
97732115b10SPawel Jakub Dawidek 		mtx_lock(&g_gate_units_lock);
97832115b10SPawel Jakub Dawidek 		if (g_gate_nunits > 0) {
97932115b10SPawel Jakub Dawidek 			mtx_unlock(&g_gate_units_lock);
980fe27e772SPawel Jakub Dawidek 			error = EBUSY;
981fe27e772SPawel Jakub Dawidek 			break;
982fe27e772SPawel Jakub Dawidek 		}
98332115b10SPawel Jakub Dawidek 		mtx_unlock(&g_gate_units_lock);
98432115b10SPawel Jakub Dawidek 		mtx_destroy(&g_gate_units_lock);
98501b5c6f7SPedro F. Giffuni 		if (status_dev != NULL)
986fe27e772SPawel Jakub Dawidek 			destroy_dev(status_dev);
98732115b10SPawel Jakub Dawidek 		free(g_gate_units, M_GATE);
988fe27e772SPawel Jakub Dawidek 		break;
989fe27e772SPawel Jakub Dawidek 	default:
9903e019deaSPoul-Henning Kamp 		return (EOPNOTSUPP);
991fe27e772SPawel Jakub Dawidek 		break;
992fe27e772SPawel Jakub Dawidek 	}
993fe27e772SPawel Jakub Dawidek 
994fe27e772SPawel Jakub Dawidek 	return (error);
995fe27e772SPawel Jakub Dawidek }
996fe27e772SPawel Jakub Dawidek static moduledata_t g_gate_module = {
997fe27e772SPawel Jakub Dawidek 	G_GATE_MOD_NAME,
998fe27e772SPawel Jakub Dawidek 	g_gate_modevent,
999fe27e772SPawel Jakub Dawidek 	NULL
1000fe27e772SPawel Jakub Dawidek };
1001fe27e772SPawel Jakub Dawidek DECLARE_MODULE(geom_gate, g_gate_module, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
1002fe27e772SPawel Jakub Dawidek DECLARE_GEOM_CLASS(g_gate_class, g_gate);
100374d6c131SKyle Evans MODULE_VERSION(geom_gate, 0);
1004