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: releng/10.2/sys/geom/gate/g_gate.c 260385 2014-01-07 01:32:23Z scottl $"); 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> 48223921Sae#include <sys/sbuf.h> 49128760Spjd#include <sys/sysctl.h> 50128760Spjd#include <sys/signalvar.h> 51128760Spjd#include <sys/time.h> 52128760Spjd#include <machine/atomic.h> 53128760Spjd 54128760Spjd#include <geom/geom.h> 55128760Spjd#include <geom/gate/g_gate.h> 56128760Spjd 57219029SnetchildFEATURE(geom_gate, "GEOM Gate module"); 58219029Snetchild 59151897Srwatsonstatic MALLOC_DEFINE(M_GATE, "gg_data", "GEOM Gate Data"); 60128760Spjd 61128760SpjdSYSCTL_DECL(_kern_geom); 62227309Sedstatic SYSCTL_NODE(_kern_geom, OID_AUTO, gate, CTLFLAG_RW, 0, 63238119Spjd "GEOM_GATE configuration"); 64204076Spjdstatic int g_gate_debug = 0; 65204076SpjdTUNABLE_INT("kern.geom.gate.debug", &g_gate_debug); 66204076SpjdSYSCTL_INT(_kern_geom_gate, OID_AUTO, debug, CTLFLAG_RW, &g_gate_debug, 0, 67128760Spjd "Debug level"); 68204076Spjdstatic u_int g_gate_maxunits = 256; 69204076SpjdTUNABLE_INT("kern.geom.gate.maxunits", &g_gate_maxunits); 70204076SpjdSYSCTL_UINT(_kern_geom_gate, OID_AUTO, maxunits, CTLFLAG_RDTUN, 71204076Spjd &g_gate_maxunits, 0, "Maximum number of ggate devices"); 72128760Spjd 73128760Spjdstruct g_class g_gate_class = { 74128760Spjd .name = G_GATE_CLASS_NAME, 75133318Sphk .version = G_VERSION, 76128760Spjd}; 77128760Spjd 78130585Sphkstatic struct cdev *status_dev; 79128760Spjdstatic d_ioctl_t g_gate_ioctl; 80128760Spjdstatic struct cdevsw g_gate_cdevsw = { 81128760Spjd .d_version = D_VERSION, 82128760Spjd .d_ioctl = g_gate_ioctl, 83128760Spjd .d_name = G_GATE_CTL_NAME 84128760Spjd}; 85128760Spjd 86128760Spjd 87204076Spjdstatic struct g_gate_softc **g_gate_units; 88204076Spjdstatic u_int g_gate_nunits; 89204076Spjdstatic struct mtx g_gate_units_lock; 90128760Spjd 91128760Spjdstatic int 92128760Spjdg_gate_destroy(struct g_gate_softc *sc, boolean_t force) 93128760Spjd{ 94260385Sscottl struct bio_queue_head queue; 95128760Spjd struct g_provider *pp; 96238119Spjd struct g_consumer *cp; 97162056Spjd struct g_geom *gp; 98128760Spjd struct bio *bp; 99128760Spjd 100128760Spjd g_topology_assert(); 101204076Spjd mtx_assert(&g_gate_units_lock, MA_OWNED); 102128760Spjd pp = sc->sc_provider; 103128760Spjd if (!force && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 104204076Spjd mtx_unlock(&g_gate_units_lock); 105128760Spjd return (EBUSY); 106128760Spjd } 107204076Spjd mtx_unlock(&g_gate_units_lock); 108141561Spjd mtx_lock(&sc->sc_queue_mtx); 109162056Spjd if ((sc->sc_flags & G_GATE_FLAG_DESTROY) == 0) 110162056Spjd sc->sc_flags |= G_GATE_FLAG_DESTROY; 111128760Spjd wakeup(sc); 112141561Spjd mtx_unlock(&sc->sc_queue_mtx); 113162056Spjd gp = pp->geom; 114162056Spjd pp->flags |= G_PF_WITHER; 115162056Spjd g_orphan_provider(pp, ENXIO); 116128760Spjd callout_drain(&sc->sc_callout); 117260385Sscottl bioq_init(&queue); 118141561Spjd mtx_lock(&sc->sc_queue_mtx); 119260385Sscottl while ((bp = bioq_takefirst(&sc->sc_inqueue)) != NULL) { 120205279Spjd sc->sc_queue_count--; 121260385Sscottl bioq_insert_tail(&queue, bp); 122128760Spjd } 123260385Sscottl while ((bp = bioq_takefirst(&sc->sc_outqueue)) != NULL) { 124205279Spjd sc->sc_queue_count--; 125260385Sscottl bioq_insert_tail(&queue, bp); 126260385Sscottl } 127260385Sscottl mtx_unlock(&sc->sc_queue_mtx); 128260385Sscottl g_topology_unlock(); 129260385Sscottl while ((bp = bioq_takefirst(&queue)) != NULL) { 130205279Spjd G_GATE_LOGREQ(1, bp, "Request canceled."); 131205279Spjd g_io_deliver(bp, ENXIO); 132128760Spjd } 133204076Spjd mtx_lock(&g_gate_units_lock); 134162056Spjd /* One reference is ours. */ 135162056Spjd sc->sc_ref--; 136204076Spjd while (sc->sc_ref > 0) 137204076Spjd msleep(&sc->sc_ref, &g_gate_units_lock, 0, "gg:destroy", 0); 138204076Spjd g_gate_units[sc->sc_unit] = NULL; 139204076Spjd KASSERT(g_gate_nunits > 0, ("negative g_gate_nunits?")); 140204076Spjd g_gate_nunits--; 141204076Spjd mtx_unlock(&g_gate_units_lock); 142141561Spjd mtx_destroy(&sc->sc_queue_mtx); 143162056Spjd g_topology_lock(); 144238119Spjd if ((cp = sc->sc_readcons) != NULL) { 145238119Spjd sc->sc_readcons = NULL; 146238119Spjd (void)g_access(cp, -1, 0, 0); 147238119Spjd g_detach(cp); 148238119Spjd g_destroy_consumer(cp); 149238119Spjd } 150220173Strociny G_GATE_DEBUG(1, "Device %s destroyed.", gp->name); 151162056Spjd gp->softc = NULL; 152162056Spjd g_wither_geom(gp, ENXIO); 153128760Spjd sc->sc_provider = NULL; 154128760Spjd free(sc, M_GATE); 155128760Spjd return (0); 156128760Spjd} 157128760Spjd 158128760Spjdstatic int 159128760Spjdg_gate_access(struct g_provider *pp, int dr, int dw, int de) 160128760Spjd{ 161128760Spjd struct g_gate_softc *sc; 162128760Spjd 163128760Spjd if (dr <= 0 && dw <= 0 && de <= 0) 164128760Spjd return (0); 165128760Spjd sc = pp->geom->softc; 166128760Spjd if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) 167128760Spjd return (ENXIO); 168131188Spjd /* XXX: Hack to allow read-only mounts. */ 169131188Spjd#if 0 170128760Spjd if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0 && dw > 0) 171128760Spjd return (EPERM); 172131188Spjd#endif 173128760Spjd if ((sc->sc_flags & G_GATE_FLAG_WRITEONLY) != 0 && dr > 0) 174128760Spjd return (EPERM); 175128760Spjd return (0); 176128760Spjd} 177128760Spjd 178128760Spjdstatic void 179238119Spjdg_gate_queue_io(struct bio *bp) 180128760Spjd{ 181128760Spjd struct g_gate_softc *sc; 182128760Spjd 183128760Spjd sc = bp->bio_to->geom->softc; 184128760Spjd if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) { 185128760Spjd g_io_deliver(bp, ENXIO); 186128760Spjd return; 187128760Spjd } 188128760Spjd 189141561Spjd mtx_lock(&sc->sc_queue_mtx); 190238119Spjd 191220264Spjd if (sc->sc_queue_size > 0 && sc->sc_queue_count > sc->sc_queue_size) { 192141742Spjd mtx_unlock(&sc->sc_queue_mtx); 193128760Spjd G_GATE_LOGREQ(1, bp, "Queue full, request canceled."); 194204076Spjd g_io_deliver(bp, ENOMEM); 195128760Spjd return; 196128760Spjd } 197141561Spjd 198128760Spjd bp->bio_driver1 = (void *)sc->sc_seq; 199128760Spjd sc->sc_seq++; 200141561Spjd sc->sc_queue_count++; 201128760Spjd 202141312Spjd bioq_insert_tail(&sc->sc_inqueue, bp); 203128957Spjd wakeup(sc); 204141561Spjd 205141561Spjd mtx_unlock(&sc->sc_queue_mtx); 206128760Spjd} 207128760Spjd 208238119Spjdstatic void 209238119Spjdg_gate_done(struct bio *cbp) 210238119Spjd{ 211238119Spjd struct bio *pbp; 212238119Spjd 213238119Spjd pbp = cbp->bio_parent; 214238119Spjd if (cbp->bio_error == 0) { 215238119Spjd pbp->bio_completed = cbp->bio_completed; 216238119Spjd g_destroy_bio(cbp); 217238119Spjd pbp->bio_inbed++; 218238119Spjd g_io_deliver(pbp, 0); 219238119Spjd } else { 220238119Spjd /* If direct read failed, pass it through userland daemon. */ 221238119Spjd g_destroy_bio(cbp); 222238119Spjd pbp->bio_children--; 223238119Spjd g_gate_queue_io(pbp); 224238119Spjd } 225238119Spjd} 226238119Spjd 227238119Spjdstatic void 228238119Spjdg_gate_start(struct bio *pbp) 229238119Spjd{ 230238119Spjd struct g_gate_softc *sc; 231238119Spjd 232238119Spjd sc = pbp->bio_to->geom->softc; 233238119Spjd if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) { 234238119Spjd g_io_deliver(pbp, ENXIO); 235238119Spjd return; 236238119Spjd } 237238119Spjd G_GATE_LOGREQ(2, pbp, "Request received."); 238238119Spjd switch (pbp->bio_cmd) { 239238119Spjd case BIO_READ: 240238119Spjd if (sc->sc_readcons != NULL) { 241238119Spjd struct bio *cbp; 242238119Spjd 243238119Spjd cbp = g_clone_bio(pbp); 244238119Spjd if (cbp == NULL) { 245238119Spjd g_io_deliver(pbp, ENOMEM); 246238119Spjd return; 247238119Spjd } 248238119Spjd cbp->bio_done = g_gate_done; 249238119Spjd cbp->bio_offset = pbp->bio_offset + sc->sc_readoffset; 250238119Spjd cbp->bio_to = sc->sc_readcons->provider; 251238119Spjd g_io_request(cbp, sc->sc_readcons); 252238119Spjd return; 253238119Spjd } 254238119Spjd break; 255238119Spjd case BIO_DELETE: 256238119Spjd case BIO_WRITE: 257238119Spjd case BIO_FLUSH: 258238119Spjd /* XXX: Hack to allow read-only mounts. */ 259238119Spjd if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0) { 260238119Spjd g_io_deliver(pbp, EPERM); 261238119Spjd return; 262238119Spjd } 263238119Spjd break; 264238119Spjd case BIO_GETATTR: 265238119Spjd default: 266238119Spjd G_GATE_LOGREQ(2, pbp, "Ignoring request."); 267238119Spjd g_io_deliver(pbp, EOPNOTSUPP); 268238119Spjd return; 269238119Spjd } 270238119Spjd 271238119Spjd g_gate_queue_io(pbp); 272238119Spjd} 273238119Spjd 274128760Spjdstatic struct g_gate_softc * 275209187Spjdg_gate_hold(int unit, const char *name) 276128760Spjd{ 277204076Spjd struct g_gate_softc *sc = NULL; 278128760Spjd 279204076Spjd mtx_lock(&g_gate_units_lock); 280204076Spjd if (unit >= 0 && unit < g_gate_maxunits) 281204076Spjd sc = g_gate_units[unit]; 282204076Spjd else if (unit == G_GATE_NAME_GIVEN) { 283204076Spjd KASSERT(name != NULL, ("name is NULL")); 284204076Spjd for (unit = 0; unit < g_gate_maxunits; unit++) { 285204076Spjd if (g_gate_units[unit] == NULL) 286204076Spjd continue; 287204076Spjd if (strcmp(name, 288204076Spjd g_gate_units[unit]->sc_provider->name) != 0) { 289204076Spjd continue; 290204076Spjd } 291204076Spjd sc = g_gate_units[unit]; 292128760Spjd break; 293204076Spjd } 294128760Spjd } 295162056Spjd if (sc != NULL) 296162056Spjd sc->sc_ref++; 297204076Spjd mtx_unlock(&g_gate_units_lock); 298128760Spjd return (sc); 299128760Spjd} 300128760Spjd 301128760Spjdstatic void 302128760Spjdg_gate_release(struct g_gate_softc *sc) 303128760Spjd{ 304128760Spjd 305128760Spjd g_topology_assert_not(); 306204076Spjd mtx_lock(&g_gate_units_lock); 307128760Spjd sc->sc_ref--; 308128760Spjd KASSERT(sc->sc_ref >= 0, ("Negative sc_ref for %s.", sc->sc_name)); 309204076Spjd if (sc->sc_ref == 0 && (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) 310162056Spjd wakeup(&sc->sc_ref); 311204076Spjd mtx_unlock(&g_gate_units_lock); 312128760Spjd} 313128760Spjd 314128760Spjdstatic int 315204076Spjdg_gate_getunit(int unit, int *errorp) 316128760Spjd{ 317128760Spjd 318204076Spjd mtx_assert(&g_gate_units_lock, MA_OWNED); 319128760Spjd if (unit >= 0) { 320204076Spjd if (unit >= g_gate_maxunits) 321204076Spjd *errorp = EINVAL; 322204076Spjd else if (g_gate_units[unit] == NULL) 323204076Spjd return (unit); 324204076Spjd else 325204076Spjd *errorp = EEXIST; 326128760Spjd } else { 327204076Spjd for (unit = 0; unit < g_gate_maxunits; unit++) { 328204076Spjd if (g_gate_units[unit] == NULL) 329204076Spjd return (unit); 330128760Spjd } 331204076Spjd *errorp = ENFILE; 332128760Spjd } 333204076Spjd return (-1); 334128760Spjd} 335128760Spjd 336128760Spjdstatic void 337128760Spjdg_gate_guard(void *arg) 338128760Spjd{ 339260385Sscottl struct bio_queue_head queue; 340128760Spjd struct g_gate_softc *sc; 341128760Spjd struct bintime curtime; 342128760Spjd struct bio *bp, *bp2; 343128760Spjd 344128760Spjd sc = arg; 345128760Spjd binuptime(&curtime); 346204076Spjd g_gate_hold(sc->sc_unit, NULL); 347260385Sscottl bioq_init(&queue); 348141561Spjd mtx_lock(&sc->sc_queue_mtx); 349128760Spjd TAILQ_FOREACH_SAFE(bp, &sc->sc_inqueue.queue, bio_queue, bp2) { 350128760Spjd if (curtime.sec - bp->bio_t0.sec < 5) 351128760Spjd continue; 352128760Spjd bioq_remove(&sc->sc_inqueue, bp); 353141561Spjd sc->sc_queue_count--; 354260385Sscottl bioq_insert_tail(&queue, bp); 355128760Spjd } 356128760Spjd TAILQ_FOREACH_SAFE(bp, &sc->sc_outqueue.queue, bio_queue, bp2) { 357128760Spjd if (curtime.sec - bp->bio_t0.sec < 5) 358128760Spjd continue; 359128760Spjd bioq_remove(&sc->sc_outqueue, bp); 360141561Spjd sc->sc_queue_count--; 361260385Sscottl bioq_insert_tail(&queue, bp); 362260385Sscottl } 363260385Sscottl mtx_unlock(&sc->sc_queue_mtx); 364260385Sscottl while ((bp = bioq_takefirst(&queue)) != NULL) { 365128760Spjd G_GATE_LOGREQ(1, bp, "Request timeout."); 366128760Spjd g_io_deliver(bp, EIO); 367128760Spjd } 368128760Spjd if ((sc->sc_flags & G_GATE_FLAG_DESTROY) == 0) { 369128760Spjd callout_reset(&sc->sc_callout, sc->sc_timeout * hz, 370128760Spjd g_gate_guard, sc); 371128760Spjd } 372128760Spjd g_gate_release(sc); 373128760Spjd} 374128760Spjd 375128760Spjdstatic void 376238119Spjdg_gate_orphan(struct g_consumer *cp) 377238119Spjd{ 378238119Spjd struct g_gate_softc *sc; 379238119Spjd struct g_geom *gp; 380238119Spjd 381238119Spjd g_topology_assert(); 382238119Spjd gp = cp->geom; 383238119Spjd sc = gp->softc; 384238119Spjd if (sc == NULL) 385238119Spjd return; 386238119Spjd KASSERT(cp == sc->sc_readcons, ("cp=%p sc_readcons=%p", cp, 387238119Spjd sc->sc_readcons)); 388238119Spjd sc->sc_readcons = NULL; 389238119Spjd G_GATE_DEBUG(1, "Destroying read consumer on provider %s orphan.", 390238119Spjd cp->provider->name); 391238119Spjd (void)g_access(cp, -1, 0, 0); 392238119Spjd g_detach(cp); 393238119Spjd g_destroy_consumer(cp); 394238119Spjd} 395238119Spjd 396238119Spjdstatic void 397128760Spjdg_gate_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 398128760Spjd struct g_consumer *cp, struct g_provider *pp) 399128760Spjd{ 400128760Spjd struct g_gate_softc *sc; 401128760Spjd 402128760Spjd sc = gp->softc; 403128760Spjd if (sc == NULL || pp != NULL || cp != NULL) 404128760Spjd return; 405239131Strociny sc = g_gate_hold(sc->sc_unit, NULL); 406239131Strociny if (sc == NULL) 407239131Strociny return; 408128760Spjd if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0) { 409128760Spjd sbuf_printf(sb, "%s<access>%s</access>\n", indent, "read-only"); 410128760Spjd } else if ((sc->sc_flags & G_GATE_FLAG_WRITEONLY) != 0) { 411128760Spjd sbuf_printf(sb, "%s<access>%s</access>\n", indent, 412128760Spjd "write-only"); 413128760Spjd } else { 414128760Spjd sbuf_printf(sb, "%s<access>%s</access>\n", indent, 415128760Spjd "read-write"); 416128760Spjd } 417238119Spjd if (sc->sc_readcons != NULL) { 418238119Spjd sbuf_printf(sb, "%s<read_offset>%jd</read_offset>\n", 419238119Spjd indent, (intmax_t)sc->sc_readoffset); 420238119Spjd sbuf_printf(sb, "%s<read_provider>%s</read_provider>\n", 421238119Spjd indent, sc->sc_readcons->provider->name); 422238119Spjd } 423128760Spjd sbuf_printf(sb, "%s<timeout>%u</timeout>\n", indent, sc->sc_timeout); 424128760Spjd sbuf_printf(sb, "%s<info>%s</info>\n", indent, sc->sc_info); 425128760Spjd sbuf_printf(sb, "%s<queue_count>%u</queue_count>\n", indent, 426128760Spjd sc->sc_queue_count); 427128760Spjd sbuf_printf(sb, "%s<queue_size>%u</queue_size>\n", indent, 428128760Spjd sc->sc_queue_size); 429128760Spjd sbuf_printf(sb, "%s<ref>%u</ref>\n", indent, sc->sc_ref); 430204076Spjd sbuf_printf(sb, "%s<unit>%d</unit>\n", indent, sc->sc_unit); 431130836Spjd g_topology_unlock(); 432128760Spjd g_gate_release(sc); 433130836Spjd g_topology_lock(); 434128760Spjd} 435128760Spjd 436128760Spjdstatic int 437128760Spjdg_gate_create(struct g_gate_ctl_create *ggio) 438128760Spjd{ 439128760Spjd struct g_gate_softc *sc; 440128760Spjd struct g_geom *gp; 441238119Spjd struct g_provider *pp, *ropp; 442238119Spjd struct g_consumer *cp; 443204076Spjd char name[NAME_MAX]; 444204076Spjd int error = 0, unit; 445128760Spjd 446238119Spjd if (ggio->gctl_mediasize <= 0) { 447128760Spjd G_GATE_DEBUG(1, "Invalid media size."); 448128760Spjd return (EINVAL); 449128760Spjd } 450238119Spjd if (ggio->gctl_sectorsize <= 0) { 451128760Spjd G_GATE_DEBUG(1, "Invalid sector size."); 452128760Spjd return (EINVAL); 453128760Spjd } 454238119Spjd if (!powerof2(ggio->gctl_sectorsize)) { 455238119Spjd G_GATE_DEBUG(1, "Invalid sector size."); 456238119Spjd return (EINVAL); 457238119Spjd } 458141312Spjd if ((ggio->gctl_mediasize % ggio->gctl_sectorsize) != 0) { 459141312Spjd G_GATE_DEBUG(1, "Invalid media size."); 460141312Spjd return (EINVAL); 461141312Spjd } 462128760Spjd if ((ggio->gctl_flags & G_GATE_FLAG_READONLY) != 0 && 463128760Spjd (ggio->gctl_flags & G_GATE_FLAG_WRITEONLY) != 0) { 464128760Spjd G_GATE_DEBUG(1, "Invalid flags."); 465128760Spjd return (EINVAL); 466128760Spjd } 467204076Spjd if (ggio->gctl_unit != G_GATE_UNIT_AUTO && 468204076Spjd ggio->gctl_unit != G_GATE_NAME_GIVEN && 469204076Spjd ggio->gctl_unit < 0) { 470128760Spjd G_GATE_DEBUG(1, "Invalid unit number."); 471128760Spjd return (EINVAL); 472128760Spjd } 473204076Spjd if (ggio->gctl_unit == G_GATE_NAME_GIVEN && 474204076Spjd ggio->gctl_name[0] == '\0') { 475204076Spjd G_GATE_DEBUG(1, "No device name."); 476204076Spjd return (EINVAL); 477204076Spjd } 478128760Spjd 479238868Strociny sc = malloc(sizeof(*sc), M_GATE, M_WAITOK | M_ZERO); 480238868Strociny sc->sc_flags = (ggio->gctl_flags & G_GATE_USERFLAGS); 481238868Strociny strlcpy(sc->sc_info, ggio->gctl_info, sizeof(sc->sc_info)); 482238868Strociny sc->sc_seq = 1; 483238868Strociny bioq_init(&sc->sc_inqueue); 484238868Strociny bioq_init(&sc->sc_outqueue); 485238868Strociny mtx_init(&sc->sc_queue_mtx, "gg:queue", NULL, MTX_DEF); 486238868Strociny sc->sc_queue_count = 0; 487238868Strociny sc->sc_queue_size = ggio->gctl_maxcount; 488238868Strociny if (sc->sc_queue_size > G_GATE_MAX_QUEUE_SIZE) 489238868Strociny sc->sc_queue_size = G_GATE_MAX_QUEUE_SIZE; 490238868Strociny sc->sc_timeout = ggio->gctl_timeout; 491238868Strociny callout_init(&sc->sc_callout, CALLOUT_MPSAFE); 492238868Strociny 493238868Strociny mtx_lock(&g_gate_units_lock); 494238868Strociny sc->sc_unit = g_gate_getunit(ggio->gctl_unit, &error); 495238868Strociny if (sc->sc_unit < 0) 496238868Strociny goto fail1; 497238868Strociny if (ggio->gctl_unit == G_GATE_NAME_GIVEN) 498238868Strociny snprintf(name, sizeof(name), "%s", ggio->gctl_name); 499238868Strociny else { 500238868Strociny snprintf(name, sizeof(name), "%s%d", G_GATE_PROVIDER_NAME, 501238868Strociny sc->sc_unit); 502238868Strociny } 503238868Strociny /* Check for name collision. */ 504238868Strociny for (unit = 0; unit < g_gate_maxunits; unit++) { 505238868Strociny if (g_gate_units[unit] == NULL) 506238868Strociny continue; 507238868Strociny if (strcmp(name, g_gate_units[unit]->sc_name) != 0) 508238868Strociny continue; 509238868Strociny error = EEXIST; 510238868Strociny goto fail1; 511238868Strociny } 512238868Strociny sc->sc_name = name; 513238868Strociny g_gate_units[sc->sc_unit] = sc; 514238868Strociny g_gate_nunits++; 515238868Strociny mtx_unlock(&g_gate_units_lock); 516238868Strociny 517238119Spjd g_topology_lock(); 518238119Spjd 519238119Spjd if (ggio->gctl_readprov[0] == '\0') { 520238119Spjd ropp = NULL; 521238119Spjd } else { 522238119Spjd ropp = g_provider_by_name(ggio->gctl_readprov); 523238119Spjd if (ropp == NULL) { 524238119Spjd G_GATE_DEBUG(1, "Provider %s doesn't exist.", 525238119Spjd ggio->gctl_readprov); 526238868Strociny error = EINVAL; 527238868Strociny goto fail2; 528238119Spjd } 529238119Spjd if ((ggio->gctl_readoffset % ggio->gctl_sectorsize) != 0) { 530238119Spjd G_GATE_DEBUG(1, "Invalid read offset."); 531238868Strociny error = EINVAL; 532238868Strociny goto fail2; 533238119Spjd } 534238119Spjd if (ggio->gctl_mediasize + ggio->gctl_readoffset > 535238119Spjd ropp->mediasize) { 536238119Spjd G_GATE_DEBUG(1, "Invalid read offset or media size."); 537238868Strociny error = EINVAL; 538238868Strociny goto fail2; 539238119Spjd } 540238119Spjd } 541238119Spjd 542238119Spjd gp = g_new_geomf(&g_gate_class, "%s", name); 543238119Spjd gp->start = g_gate_start; 544238119Spjd gp->access = g_gate_access; 545238119Spjd gp->orphan = g_gate_orphan; 546238119Spjd gp->dumpconf = g_gate_dumpconf; 547238119Spjd gp->softc = sc; 548238119Spjd 549238119Spjd if (ropp != NULL) { 550238119Spjd cp = g_new_consumer(gp); 551260385Sscottl cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 552238119Spjd error = g_attach(cp, ropp); 553238119Spjd if (error != 0) { 554238119Spjd G_GATE_DEBUG(1, "Unable to attach to %s.", ropp->name); 555238868Strociny goto fail3; 556238119Spjd } 557238868Strociny error = g_access(cp, 1, 0, 0); 558238119Spjd if (error != 0) { 559238868Strociny G_GATE_DEBUG(1, "Unable to access %s.", ropp->name); 560238868Strociny g_detach(cp); 561238868Strociny goto fail3; 562238119Spjd } 563238119Spjd sc->sc_readcons = cp; 564238119Spjd sc->sc_readoffset = ggio->gctl_readoffset; 565238119Spjd } 566238119Spjd 567204076Spjd ggio->gctl_unit = sc->sc_unit; 568204076Spjd 569204076Spjd pp = g_new_providerf(gp, "%s", name); 570260385Sscottl pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE; 571128760Spjd pp->mediasize = ggio->gctl_mediasize; 572128760Spjd pp->sectorsize = ggio->gctl_sectorsize; 573128760Spjd sc->sc_provider = pp; 574128760Spjd g_error_provider(pp, 0); 575238119Spjd 576128760Spjd g_topology_unlock(); 577220062Strociny mtx_lock(&g_gate_units_lock); 578220062Strociny sc->sc_name = sc->sc_provider->name; 579220062Strociny mtx_unlock(&g_gate_units_lock); 580220173Strociny G_GATE_DEBUG(1, "Device %s created.", gp->name); 581128760Spjd 582128760Spjd if (sc->sc_timeout > 0) { 583128760Spjd callout_reset(&sc->sc_callout, sc->sc_timeout * hz, 584128760Spjd g_gate_guard, sc); 585128760Spjd } 586128760Spjd return (0); 587238868Strocinyfail3: 588238868Strociny g_destroy_consumer(cp); 589238868Strociny g_destroy_geom(gp); 590238868Strocinyfail2: 591238868Strociny g_topology_unlock(); 592238868Strociny mtx_lock(&g_gate_units_lock); 593238868Strociny g_gate_units[sc->sc_unit] = NULL; 594238868Strociny KASSERT(g_gate_nunits > 0, ("negative g_gate_nunits?")); 595238868Strociny g_gate_nunits--; 596238868Strocinyfail1: 597238868Strociny mtx_unlock(&g_gate_units_lock); 598238868Strociny mtx_destroy(&sc->sc_queue_mtx); 599238868Strociny free(sc, M_GATE); 600238868Strociny return (error); 601128760Spjd} 602128760Spjd 603238119Spjdstatic int 604238119Spjdg_gate_modify(struct g_gate_softc *sc, struct g_gate_ctl_modify *ggio) 605238119Spjd{ 606238119Spjd struct g_provider *pp; 607238119Spjd struct g_consumer *cp; 608238119Spjd int error; 609238119Spjd 610238119Spjd if ((ggio->gctl_modify & GG_MODIFY_MEDIASIZE) != 0) { 611238119Spjd if (ggio->gctl_mediasize <= 0) { 612238119Spjd G_GATE_DEBUG(1, "Invalid media size."); 613238119Spjd return (EINVAL); 614238119Spjd } 615238119Spjd pp = sc->sc_provider; 616238119Spjd if ((ggio->gctl_mediasize % pp->sectorsize) != 0) { 617238119Spjd G_GATE_DEBUG(1, "Invalid media size."); 618238119Spjd return (EINVAL); 619238119Spjd } 620238119Spjd /* TODO */ 621238119Spjd return (EOPNOTSUPP); 622238119Spjd } 623238119Spjd 624238119Spjd if ((ggio->gctl_modify & GG_MODIFY_INFO) != 0) 625238119Spjd (void)strlcpy(sc->sc_info, ggio->gctl_info, sizeof(sc->sc_info)); 626238119Spjd 627238119Spjd cp = NULL; 628238119Spjd 629238119Spjd if ((ggio->gctl_modify & GG_MODIFY_READPROV) != 0) { 630238119Spjd g_topology_lock(); 631238119Spjd if (sc->sc_readcons != NULL) { 632238119Spjd cp = sc->sc_readcons; 633238119Spjd sc->sc_readcons = NULL; 634238119Spjd (void)g_access(cp, -1, 0, 0); 635238119Spjd g_detach(cp); 636238119Spjd g_destroy_consumer(cp); 637238119Spjd } 638238119Spjd if (ggio->gctl_readprov[0] != '\0') { 639238119Spjd pp = g_provider_by_name(ggio->gctl_readprov); 640238119Spjd if (pp == NULL) { 641238119Spjd g_topology_unlock(); 642238119Spjd G_GATE_DEBUG(1, "Provider %s doesn't exist.", 643238119Spjd ggio->gctl_readprov); 644238119Spjd return (EINVAL); 645238119Spjd } 646238119Spjd cp = g_new_consumer(sc->sc_provider->geom); 647260385Sscottl cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 648238119Spjd error = g_attach(cp, pp); 649238119Spjd if (error != 0) { 650238119Spjd G_GATE_DEBUG(1, "Unable to attach to %s.", 651238119Spjd pp->name); 652238119Spjd } else { 653238119Spjd error = g_access(cp, 1, 0, 0); 654238119Spjd if (error != 0) { 655238119Spjd G_GATE_DEBUG(1, "Unable to access %s.", 656238119Spjd pp->name); 657238119Spjd g_detach(cp); 658238119Spjd } 659238119Spjd } 660238119Spjd if (error != 0) { 661238119Spjd g_destroy_consumer(cp); 662238119Spjd g_topology_unlock(); 663238119Spjd return (error); 664238119Spjd } 665238119Spjd } 666238119Spjd } else { 667238119Spjd cp = sc->sc_readcons; 668238119Spjd } 669238119Spjd 670238119Spjd if ((ggio->gctl_modify & GG_MODIFY_READOFFSET) != 0) { 671238119Spjd if (cp == NULL) { 672238119Spjd G_GATE_DEBUG(1, "No read provider."); 673238119Spjd return (EINVAL); 674238119Spjd } 675238119Spjd pp = sc->sc_provider; 676238119Spjd if ((ggio->gctl_readoffset % pp->sectorsize) != 0) { 677238119Spjd G_GATE_DEBUG(1, "Invalid read offset."); 678238119Spjd return (EINVAL); 679238119Spjd } 680238119Spjd if (pp->mediasize + ggio->gctl_readoffset > 681238119Spjd cp->provider->mediasize) { 682238119Spjd G_GATE_DEBUG(1, "Invalid read offset or media size."); 683238119Spjd return (EINVAL); 684238119Spjd } 685238119Spjd sc->sc_readoffset = ggio->gctl_readoffset; 686238119Spjd } 687238119Spjd 688238119Spjd if ((ggio->gctl_modify & GG_MODIFY_READPROV) != 0) { 689238119Spjd sc->sc_readcons = cp; 690238119Spjd g_topology_unlock(); 691238119Spjd } 692238119Spjd 693238119Spjd return (0); 694238119Spjd} 695238119Spjd 696128760Spjd#define G_GATE_CHECK_VERSION(ggio) do { \ 697147843Spjd if ((ggio)->gctl_version != G_GATE_VERSION) { \ 698147843Spjd printf("Version mismatch %d != %d.\n", \ 699147843Spjd ggio->gctl_version, G_GATE_VERSION); \ 700128760Spjd return (EINVAL); \ 701147843Spjd } \ 702128760Spjd} while (0) 703128760Spjdstatic int 704130585Sphkg_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) 705128760Spjd{ 706128760Spjd struct g_gate_softc *sc; 707128760Spjd struct bio *bp; 708128760Spjd int error = 0; 709128760Spjd 710128760Spjd G_GATE_DEBUG(4, "ioctl(%s, %lx, %p, %x, %p)", devtoname(dev), cmd, addr, 711128760Spjd flags, td); 712128760Spjd 713128760Spjd switch (cmd) { 714128760Spjd case G_GATE_CMD_CREATE: 715128760Spjd { 716128760Spjd struct g_gate_ctl_create *ggio = (void *)addr; 717128760Spjd 718128760Spjd G_GATE_CHECK_VERSION(ggio); 719138014Spjd error = g_gate_create(ggio); 720141972Spjd /* 721141972Spjd * Reset TDP_GEOM flag. 722141972Spjd * There are pending events for sure, because we just created 723141972Spjd * new provider and other classes want to taste it, but we 724141972Spjd * cannot answer on I/O requests until we're here. 725141972Spjd */ 726141972Spjd td->td_pflags &= ~TDP_GEOM; 727138014Spjd return (error); 728128760Spjd } 729238119Spjd case G_GATE_CMD_MODIFY: 730238119Spjd { 731238119Spjd struct g_gate_ctl_modify *ggio = (void *)addr; 732238119Spjd 733238119Spjd G_GATE_CHECK_VERSION(ggio); 734238119Spjd sc = g_gate_hold(ggio->gctl_unit, NULL); 735238119Spjd if (sc == NULL) 736238119Spjd return (ENXIO); 737238119Spjd error = g_gate_modify(sc, ggio); 738238119Spjd g_gate_release(sc); 739238119Spjd return (error); 740238119Spjd } 741128760Spjd case G_GATE_CMD_DESTROY: 742128760Spjd { 743128760Spjd struct g_gate_ctl_destroy *ggio = (void *)addr; 744128760Spjd 745128760Spjd G_GATE_CHECK_VERSION(ggio); 746204076Spjd sc = g_gate_hold(ggio->gctl_unit, ggio->gctl_name); 747128760Spjd if (sc == NULL) 748128760Spjd return (ENXIO); 749128760Spjd g_topology_lock(); 750204076Spjd mtx_lock(&g_gate_units_lock); 751128760Spjd error = g_gate_destroy(sc, ggio->gctl_force); 752128760Spjd g_topology_unlock(); 753162056Spjd if (error != 0) 754162056Spjd g_gate_release(sc); 755128760Spjd return (error); 756128760Spjd } 757147843Spjd case G_GATE_CMD_CANCEL: 758147843Spjd { 759147843Spjd struct g_gate_ctl_cancel *ggio = (void *)addr; 760147843Spjd struct bio *tbp, *lbp; 761147843Spjd 762147843Spjd G_GATE_CHECK_VERSION(ggio); 763204076Spjd sc = g_gate_hold(ggio->gctl_unit, ggio->gctl_name); 764147843Spjd if (sc == NULL) 765147843Spjd return (ENXIO); 766147843Spjd lbp = NULL; 767147843Spjd mtx_lock(&sc->sc_queue_mtx); 768147843Spjd TAILQ_FOREACH_SAFE(bp, &sc->sc_outqueue.queue, bio_queue, tbp) { 769147843Spjd if (ggio->gctl_seq == 0 || 770147843Spjd ggio->gctl_seq == (uintptr_t)bp->bio_driver1) { 771147843Spjd G_GATE_LOGREQ(1, bp, "Request canceled."); 772147843Spjd bioq_remove(&sc->sc_outqueue, bp); 773147843Spjd /* 774147843Spjd * Be sure to put requests back onto incoming 775147843Spjd * queue in the proper order. 776147843Spjd */ 777147843Spjd if (lbp == NULL) 778147843Spjd bioq_insert_head(&sc->sc_inqueue, bp); 779147843Spjd else { 780147843Spjd TAILQ_INSERT_AFTER(&sc->sc_inqueue.queue, 781147843Spjd lbp, bp, bio_queue); 782147843Spjd } 783147843Spjd lbp = bp; 784147843Spjd /* 785147843Spjd * If only one request was canceled, leave now. 786147843Spjd */ 787147843Spjd if (ggio->gctl_seq != 0) 788147843Spjd break; 789147843Spjd } 790147843Spjd } 791204076Spjd if (ggio->gctl_unit == G_GATE_NAME_GIVEN) 792204076Spjd ggio->gctl_unit = sc->sc_unit; 793147843Spjd mtx_unlock(&sc->sc_queue_mtx); 794147843Spjd g_gate_release(sc); 795147843Spjd return (error); 796147843Spjd } 797128760Spjd case G_GATE_CMD_START: 798128760Spjd { 799128760Spjd struct g_gate_ctl_io *ggio = (void *)addr; 800128760Spjd 801128760Spjd G_GATE_CHECK_VERSION(ggio); 802204076Spjd sc = g_gate_hold(ggio->gctl_unit, NULL); 803128760Spjd if (sc == NULL) 804128760Spjd return (ENXIO); 805162056Spjd error = 0; 806128760Spjd for (;;) { 807141561Spjd mtx_lock(&sc->sc_queue_mtx); 808128760Spjd bp = bioq_first(&sc->sc_inqueue); 809128760Spjd if (bp != NULL) 810128760Spjd break; 811162056Spjd if ((sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) { 812162056Spjd ggio->gctl_error = ECANCELED; 813162056Spjd mtx_unlock(&sc->sc_queue_mtx); 814162056Spjd goto start_end; 815162056Spjd } 816141561Spjd if (msleep(sc, &sc->sc_queue_mtx, 817128760Spjd PPAUSE | PDROP | PCATCH, "ggwait", 0) != 0) { 818128760Spjd ggio->gctl_error = ECANCELED; 819162056Spjd goto start_end; 820128760Spjd } 821128760Spjd } 822128881Spjd ggio->gctl_cmd = bp->bio_cmd; 823248295Spjd if (bp->bio_cmd == BIO_WRITE && 824128760Spjd bp->bio_length > ggio->gctl_length) { 825141561Spjd mtx_unlock(&sc->sc_queue_mtx); 826128760Spjd ggio->gctl_length = bp->bio_length; 827128760Spjd ggio->gctl_error = ENOMEM; 828162056Spjd goto start_end; 829128760Spjd } 830128760Spjd bioq_remove(&sc->sc_inqueue, bp); 831141561Spjd bioq_insert_tail(&sc->sc_outqueue, bp); 832141561Spjd mtx_unlock(&sc->sc_queue_mtx); 833141561Spjd 834128835Spjd ggio->gctl_seq = (uintptr_t)bp->bio_driver1; 835128760Spjd ggio->gctl_offset = bp->bio_offset; 836128760Spjd ggio->gctl_length = bp->bio_length; 837147843Spjd 838128760Spjd switch (bp->bio_cmd) { 839128760Spjd case BIO_READ: 840209186Spjd case BIO_DELETE: 841222225Spjd case BIO_FLUSH: 842128760Spjd break; 843128760Spjd case BIO_WRITE: 844128760Spjd error = copyout(bp->bio_data, ggio->gctl_data, 845128760Spjd bp->bio_length); 846128760Spjd if (error != 0) { 847141561Spjd mtx_lock(&sc->sc_queue_mtx); 848141561Spjd bioq_remove(&sc->sc_outqueue, bp); 849141312Spjd bioq_insert_head(&sc->sc_inqueue, bp); 850141561Spjd mtx_unlock(&sc->sc_queue_mtx); 851162056Spjd goto start_end; 852128760Spjd } 853128760Spjd break; 854128760Spjd } 855162056Spjdstart_end: 856162056Spjd g_gate_release(sc); 857162056Spjd return (error); 858128760Spjd } 859128760Spjd case G_GATE_CMD_DONE: 860128760Spjd { 861128760Spjd struct g_gate_ctl_io *ggio = (void *)addr; 862128760Spjd 863128760Spjd G_GATE_CHECK_VERSION(ggio); 864204076Spjd sc = g_gate_hold(ggio->gctl_unit, NULL); 865128760Spjd if (sc == NULL) 866128760Spjd return (ENOENT); 867162056Spjd error = 0; 868141561Spjd mtx_lock(&sc->sc_queue_mtx); 869128760Spjd TAILQ_FOREACH(bp, &sc->sc_outqueue.queue, bio_queue) { 870128835Spjd if (ggio->gctl_seq == (uintptr_t)bp->bio_driver1) 871128760Spjd break; 872128760Spjd } 873128760Spjd if (bp != NULL) { 874128760Spjd bioq_remove(&sc->sc_outqueue, bp); 875141561Spjd sc->sc_queue_count--; 876128760Spjd } 877141561Spjd mtx_unlock(&sc->sc_queue_mtx); 878128760Spjd if (bp == NULL) { 879128760Spjd /* 880128760Spjd * Request was probably canceled. 881128760Spjd */ 882162056Spjd goto done_end; 883128760Spjd } 884128760Spjd if (ggio->gctl_error == EAGAIN) { 885128760Spjd bp->bio_error = 0; 886128760Spjd G_GATE_LOGREQ(1, bp, "Request desisted."); 887141561Spjd mtx_lock(&sc->sc_queue_mtx); 888141561Spjd sc->sc_queue_count++; 889141312Spjd bioq_insert_head(&sc->sc_inqueue, bp); 890128957Spjd wakeup(sc); 891141561Spjd mtx_unlock(&sc->sc_queue_mtx); 892128760Spjd } else { 893128760Spjd bp->bio_error = ggio->gctl_error; 894128760Spjd if (bp->bio_error == 0) { 895128760Spjd bp->bio_completed = bp->bio_length; 896128760Spjd switch (bp->bio_cmd) { 897128760Spjd case BIO_READ: 898128760Spjd error = copyin(ggio->gctl_data, 899128760Spjd bp->bio_data, bp->bio_length); 900128760Spjd if (error != 0) 901128760Spjd bp->bio_error = error; 902128760Spjd break; 903128760Spjd case BIO_DELETE: 904128760Spjd case BIO_WRITE: 905222225Spjd case BIO_FLUSH: 906128760Spjd break; 907128760Spjd } 908128760Spjd } 909128760Spjd G_GATE_LOGREQ(2, bp, "Request done."); 910128760Spjd g_io_deliver(bp, bp->bio_error); 911128760Spjd } 912162056Spjddone_end: 913162056Spjd g_gate_release(sc); 914128760Spjd return (error); 915128760Spjd } 916128760Spjd } 917128760Spjd return (ENOIOCTL); 918128760Spjd} 919128760Spjd 920128760Spjdstatic void 921131411Spjdg_gate_device(void) 922128760Spjd{ 923128760Spjd 924128760Spjd status_dev = make_dev(&g_gate_cdevsw, 0x0, UID_ROOT, GID_WHEEL, 0600, 925128760Spjd G_GATE_CTL_NAME); 926128760Spjd} 927128760Spjd 928128760Spjdstatic int 929128760Spjdg_gate_modevent(module_t mod, int type, void *data) 930128760Spjd{ 931128760Spjd int error = 0; 932128760Spjd 933128760Spjd switch (type) { 934128760Spjd case MOD_LOAD: 935204076Spjd mtx_init(&g_gate_units_lock, "gg_units_lock", NULL, MTX_DEF); 936204076Spjd g_gate_units = malloc(g_gate_maxunits * sizeof(g_gate_units[0]), 937204076Spjd M_GATE, M_WAITOK | M_ZERO); 938204076Spjd g_gate_nunits = 0; 939131411Spjd g_gate_device(); 940128760Spjd break; 941128760Spjd case MOD_UNLOAD: 942204076Spjd mtx_lock(&g_gate_units_lock); 943204076Spjd if (g_gate_nunits > 0) { 944204076Spjd mtx_unlock(&g_gate_units_lock); 945128760Spjd error = EBUSY; 946128760Spjd break; 947128760Spjd } 948204076Spjd mtx_unlock(&g_gate_units_lock); 949204076Spjd mtx_destroy(&g_gate_units_lock); 950128760Spjd if (status_dev != 0) 951128760Spjd destroy_dev(status_dev); 952204076Spjd free(g_gate_units, M_GATE); 953128760Spjd break; 954128760Spjd default: 955132199Sphk return (EOPNOTSUPP); 956128760Spjd break; 957128760Spjd } 958128760Spjd 959128760Spjd return (error); 960128760Spjd} 961128760Spjdstatic moduledata_t g_gate_module = { 962128760Spjd G_GATE_MOD_NAME, 963128760Spjd g_gate_modevent, 964128760Spjd NULL 965128760Spjd}; 966128760SpjdDECLARE_MODULE(geom_gate, g_gate_module, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 967128760SpjdDECLARE_GEOM_CLASS(g_gate_class, g_gate); 968