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$"); 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); 62240265Strocinystatic SYSCTL_NODE(_kern_geom, OID_AUTO, gate, CTLFLAG_RW, 0, 63240265Strociny "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{ 94128760Spjd struct g_provider *pp; 95240265Strociny struct g_consumer *cp; 96162056Spjd struct g_geom *gp; 97128760Spjd struct bio *bp; 98128760Spjd 99128760Spjd g_topology_assert(); 100204076Spjd mtx_assert(&g_gate_units_lock, MA_OWNED); 101128760Spjd pp = sc->sc_provider; 102128760Spjd if (!force && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 103204076Spjd mtx_unlock(&g_gate_units_lock); 104128760Spjd return (EBUSY); 105128760Spjd } 106204076Spjd mtx_unlock(&g_gate_units_lock); 107141561Spjd mtx_lock(&sc->sc_queue_mtx); 108162056Spjd if ((sc->sc_flags & G_GATE_FLAG_DESTROY) == 0) 109162056Spjd sc->sc_flags |= G_GATE_FLAG_DESTROY; 110128760Spjd wakeup(sc); 111141561Spjd mtx_unlock(&sc->sc_queue_mtx); 112162056Spjd gp = pp->geom; 113162056Spjd pp->flags |= G_PF_WITHER; 114162056Spjd g_orphan_provider(pp, ENXIO); 115128760Spjd callout_drain(&sc->sc_callout); 116141561Spjd mtx_lock(&sc->sc_queue_mtx); 117205279Spjd while ((bp = bioq_first(&sc->sc_inqueue)) != NULL) { 118205279Spjd bioq_remove(&sc->sc_inqueue, bp); 119205279Spjd sc->sc_queue_count--; 120205279Spjd G_GATE_LOGREQ(1, bp, "Request canceled."); 121205279Spjd g_io_deliver(bp, ENXIO); 122128760Spjd } 123205279Spjd while ((bp = bioq_first(&sc->sc_outqueue)) != NULL) { 124205279Spjd bioq_remove(&sc->sc_outqueue, bp); 125205279Spjd sc->sc_queue_count--; 126205279Spjd G_GATE_LOGREQ(1, bp, "Request canceled."); 127205279Spjd g_io_deliver(bp, ENXIO); 128128760Spjd } 129162056Spjd mtx_unlock(&sc->sc_queue_mtx); 130162056Spjd g_topology_unlock(); 131204076Spjd mtx_lock(&g_gate_units_lock); 132162056Spjd /* One reference is ours. */ 133162056Spjd sc->sc_ref--; 134204076Spjd while (sc->sc_ref > 0) 135204076Spjd msleep(&sc->sc_ref, &g_gate_units_lock, 0, "gg:destroy", 0); 136204076Spjd g_gate_units[sc->sc_unit] = NULL; 137204076Spjd KASSERT(g_gate_nunits > 0, ("negative g_gate_nunits?")); 138204076Spjd g_gate_nunits--; 139204076Spjd mtx_unlock(&g_gate_units_lock); 140141561Spjd mtx_destroy(&sc->sc_queue_mtx); 141162056Spjd g_topology_lock(); 142240265Strociny if ((cp = sc->sc_readcons) != NULL) { 143240265Strociny sc->sc_readcons = NULL; 144240265Strociny (void)g_access(cp, -1, 0, 0); 145240265Strociny g_detach(cp); 146240265Strociny g_destroy_consumer(cp); 147240265Strociny } 148220173Strociny G_GATE_DEBUG(1, "Device %s destroyed.", gp->name); 149162056Spjd gp->softc = NULL; 150162056Spjd g_wither_geom(gp, ENXIO); 151128760Spjd sc->sc_provider = NULL; 152128760Spjd free(sc, M_GATE); 153128760Spjd return (0); 154128760Spjd} 155128760Spjd 156128760Spjdstatic int 157128760Spjdg_gate_access(struct g_provider *pp, int dr, int dw, int de) 158128760Spjd{ 159128760Spjd struct g_gate_softc *sc; 160128760Spjd 161128760Spjd if (dr <= 0 && dw <= 0 && de <= 0) 162128760Spjd return (0); 163128760Spjd sc = pp->geom->softc; 164128760Spjd if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) 165128760Spjd return (ENXIO); 166131188Spjd /* XXX: Hack to allow read-only mounts. */ 167131188Spjd#if 0 168128760Spjd if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0 && dw > 0) 169128760Spjd return (EPERM); 170131188Spjd#endif 171128760Spjd if ((sc->sc_flags & G_GATE_FLAG_WRITEONLY) != 0 && dr > 0) 172128760Spjd return (EPERM); 173128760Spjd return (0); 174128760Spjd} 175128760Spjd 176128760Spjdstatic void 177240265Strocinyg_gate_queue_io(struct bio *bp) 178128760Spjd{ 179128760Spjd struct g_gate_softc *sc; 180128760Spjd 181128760Spjd sc = bp->bio_to->geom->softc; 182128760Spjd if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) { 183128760Spjd g_io_deliver(bp, ENXIO); 184128760Spjd return; 185128760Spjd } 186128760Spjd 187141561Spjd mtx_lock(&sc->sc_queue_mtx); 188240265Strociny 189220264Spjd if (sc->sc_queue_size > 0 && sc->sc_queue_count > sc->sc_queue_size) { 190141742Spjd mtx_unlock(&sc->sc_queue_mtx); 191128760Spjd G_GATE_LOGREQ(1, bp, "Queue full, request canceled."); 192204076Spjd g_io_deliver(bp, ENOMEM); 193128760Spjd return; 194128760Spjd } 195141561Spjd 196128760Spjd bp->bio_driver1 = (void *)sc->sc_seq; 197128760Spjd sc->sc_seq++; 198141561Spjd sc->sc_queue_count++; 199128760Spjd 200141312Spjd bioq_insert_tail(&sc->sc_inqueue, bp); 201128957Spjd wakeup(sc); 202141561Spjd 203141561Spjd mtx_unlock(&sc->sc_queue_mtx); 204128760Spjd} 205128760Spjd 206240265Strocinystatic void 207240265Strocinyg_gate_done(struct bio *cbp) 208240265Strociny{ 209240265Strociny struct bio *pbp; 210240265Strociny 211240265Strociny pbp = cbp->bio_parent; 212240265Strociny if (cbp->bio_error == 0) { 213240265Strociny pbp->bio_completed = cbp->bio_completed; 214240265Strociny g_destroy_bio(cbp); 215240265Strociny pbp->bio_inbed++; 216240265Strociny g_io_deliver(pbp, 0); 217240265Strociny } else { 218240265Strociny /* If direct read failed, pass it through userland daemon. */ 219240265Strociny g_destroy_bio(cbp); 220240265Strociny pbp->bio_children--; 221240265Strociny g_gate_queue_io(pbp); 222240265Strociny } 223240265Strociny} 224240265Strociny 225240265Strocinystatic void 226240265Strocinyg_gate_start(struct bio *pbp) 227240265Strociny{ 228240265Strociny struct g_gate_softc *sc; 229240265Strociny 230240265Strociny sc = pbp->bio_to->geom->softc; 231240265Strociny if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) { 232240265Strociny g_io_deliver(pbp, ENXIO); 233240265Strociny return; 234240265Strociny } 235240265Strociny G_GATE_LOGREQ(2, pbp, "Request received."); 236240265Strociny switch (pbp->bio_cmd) { 237240265Strociny case BIO_READ: 238240265Strociny if (sc->sc_readcons != NULL) { 239240265Strociny struct bio *cbp; 240240265Strociny 241240265Strociny cbp = g_clone_bio(pbp); 242240265Strociny if (cbp == NULL) { 243240265Strociny g_io_deliver(pbp, ENOMEM); 244240265Strociny return; 245240265Strociny } 246240265Strociny cbp->bio_done = g_gate_done; 247240265Strociny cbp->bio_offset = pbp->bio_offset + sc->sc_readoffset; 248240265Strociny cbp->bio_to = sc->sc_readcons->provider; 249240265Strociny g_io_request(cbp, sc->sc_readcons); 250240265Strociny return; 251240265Strociny } 252240265Strociny break; 253240265Strociny case BIO_DELETE: 254240265Strociny case BIO_WRITE: 255240265Strociny case BIO_FLUSH: 256240265Strociny /* XXX: Hack to allow read-only mounts. */ 257240265Strociny if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0) { 258240265Strociny g_io_deliver(pbp, EPERM); 259240265Strociny return; 260240265Strociny } 261240265Strociny break; 262240265Strociny case BIO_GETATTR: 263240265Strociny default: 264240265Strociny G_GATE_LOGREQ(2, pbp, "Ignoring request."); 265240265Strociny g_io_deliver(pbp, EOPNOTSUPP); 266240265Strociny return; 267240265Strociny } 268240265Strociny 269240265Strociny g_gate_queue_io(pbp); 270240265Strociny} 271240265Strociny 272128760Spjdstatic struct g_gate_softc * 273209187Spjdg_gate_hold(int unit, const char *name) 274128760Spjd{ 275204076Spjd struct g_gate_softc *sc = NULL; 276128760Spjd 277204076Spjd mtx_lock(&g_gate_units_lock); 278204076Spjd if (unit >= 0 && unit < g_gate_maxunits) 279204076Spjd sc = g_gate_units[unit]; 280204076Spjd else if (unit == G_GATE_NAME_GIVEN) { 281204076Spjd KASSERT(name != NULL, ("name is NULL")); 282204076Spjd for (unit = 0; unit < g_gate_maxunits; unit++) { 283204076Spjd if (g_gate_units[unit] == NULL) 284204076Spjd continue; 285204076Spjd if (strcmp(name, 286204076Spjd g_gate_units[unit]->sc_provider->name) != 0) { 287204076Spjd continue; 288204076Spjd } 289204076Spjd sc = g_gate_units[unit]; 290128760Spjd break; 291204076Spjd } 292128760Spjd } 293162056Spjd if (sc != NULL) 294162056Spjd sc->sc_ref++; 295204076Spjd mtx_unlock(&g_gate_units_lock); 296128760Spjd return (sc); 297128760Spjd} 298128760Spjd 299128760Spjdstatic void 300128760Spjdg_gate_release(struct g_gate_softc *sc) 301128760Spjd{ 302128760Spjd 303128760Spjd g_topology_assert_not(); 304204076Spjd mtx_lock(&g_gate_units_lock); 305128760Spjd sc->sc_ref--; 306128760Spjd KASSERT(sc->sc_ref >= 0, ("Negative sc_ref for %s.", sc->sc_name)); 307204076Spjd if (sc->sc_ref == 0 && (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) 308162056Spjd wakeup(&sc->sc_ref); 309204076Spjd mtx_unlock(&g_gate_units_lock); 310128760Spjd} 311128760Spjd 312128760Spjdstatic int 313204076Spjdg_gate_getunit(int unit, int *errorp) 314128760Spjd{ 315128760Spjd 316204076Spjd mtx_assert(&g_gate_units_lock, MA_OWNED); 317128760Spjd if (unit >= 0) { 318204076Spjd if (unit >= g_gate_maxunits) 319204076Spjd *errorp = EINVAL; 320204076Spjd else if (g_gate_units[unit] == NULL) 321204076Spjd return (unit); 322204076Spjd else 323204076Spjd *errorp = EEXIST; 324128760Spjd } else { 325204076Spjd for (unit = 0; unit < g_gate_maxunits; unit++) { 326204076Spjd if (g_gate_units[unit] == NULL) 327204076Spjd return (unit); 328128760Spjd } 329204076Spjd *errorp = ENFILE; 330128760Spjd } 331204076Spjd return (-1); 332128760Spjd} 333128760Spjd 334128760Spjdstatic void 335128760Spjdg_gate_guard(void *arg) 336128760Spjd{ 337128760Spjd struct g_gate_softc *sc; 338128760Spjd struct bintime curtime; 339128760Spjd struct bio *bp, *bp2; 340128760Spjd 341128760Spjd sc = arg; 342128760Spjd binuptime(&curtime); 343204076Spjd g_gate_hold(sc->sc_unit, NULL); 344141561Spjd mtx_lock(&sc->sc_queue_mtx); 345128760Spjd TAILQ_FOREACH_SAFE(bp, &sc->sc_inqueue.queue, bio_queue, bp2) { 346128760Spjd if (curtime.sec - bp->bio_t0.sec < 5) 347128760Spjd continue; 348128760Spjd bioq_remove(&sc->sc_inqueue, bp); 349141561Spjd sc->sc_queue_count--; 350128760Spjd G_GATE_LOGREQ(1, bp, "Request timeout."); 351128760Spjd g_io_deliver(bp, EIO); 352128760Spjd } 353128760Spjd TAILQ_FOREACH_SAFE(bp, &sc->sc_outqueue.queue, bio_queue, bp2) { 354128760Spjd if (curtime.sec - bp->bio_t0.sec < 5) 355128760Spjd continue; 356128760Spjd bioq_remove(&sc->sc_outqueue, bp); 357141561Spjd sc->sc_queue_count--; 358128760Spjd G_GATE_LOGREQ(1, bp, "Request timeout."); 359128760Spjd g_io_deliver(bp, EIO); 360128760Spjd } 361141561Spjd mtx_unlock(&sc->sc_queue_mtx); 362128760Spjd if ((sc->sc_flags & G_GATE_FLAG_DESTROY) == 0) { 363128760Spjd callout_reset(&sc->sc_callout, sc->sc_timeout * hz, 364128760Spjd g_gate_guard, sc); 365128760Spjd } 366128760Spjd g_gate_release(sc); 367128760Spjd} 368128760Spjd 369128760Spjdstatic void 370240265Strocinyg_gate_orphan(struct g_consumer *cp) 371240265Strociny{ 372240265Strociny struct g_gate_softc *sc; 373240265Strociny struct g_geom *gp; 374240265Strociny 375240265Strociny g_topology_assert(); 376240265Strociny gp = cp->geom; 377240265Strociny sc = gp->softc; 378240265Strociny if (sc == NULL) 379240265Strociny return; 380240265Strociny KASSERT(cp == sc->sc_readcons, ("cp=%p sc_readcons=%p", cp, 381240265Strociny sc->sc_readcons)); 382240265Strociny sc->sc_readcons = NULL; 383240265Strociny G_GATE_DEBUG(1, "Destroying read consumer on provider %s orphan.", 384240265Strociny cp->provider->name); 385240265Strociny (void)g_access(cp, -1, 0, 0); 386240265Strociny g_detach(cp); 387240265Strociny g_destroy_consumer(cp); 388240265Strociny} 389240265Strociny 390240265Strocinystatic void 391128760Spjdg_gate_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 392128760Spjd struct g_consumer *cp, struct g_provider *pp) 393128760Spjd{ 394128760Spjd struct g_gate_softc *sc; 395128760Spjd 396128760Spjd sc = gp->softc; 397128760Spjd if (sc == NULL || pp != NULL || cp != NULL) 398128760Spjd return; 399240265Strociny sc = g_gate_hold(sc->sc_unit, NULL); 400240265Strociny if (sc == NULL) 401240265Strociny return; 402128760Spjd if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0) { 403128760Spjd sbuf_printf(sb, "%s<access>%s</access>\n", indent, "read-only"); 404128760Spjd } else if ((sc->sc_flags & G_GATE_FLAG_WRITEONLY) != 0) { 405128760Spjd sbuf_printf(sb, "%s<access>%s</access>\n", indent, 406128760Spjd "write-only"); 407128760Spjd } else { 408128760Spjd sbuf_printf(sb, "%s<access>%s</access>\n", indent, 409128760Spjd "read-write"); 410128760Spjd } 411240265Strociny if (sc->sc_readcons != NULL) { 412240265Strociny sbuf_printf(sb, "%s<read_offset>%jd</read_offset>\n", 413240265Strociny indent, (intmax_t)sc->sc_readoffset); 414240265Strociny sbuf_printf(sb, "%s<read_provider>%s</read_provider>\n", 415240265Strociny indent, sc->sc_readcons->provider->name); 416240265Strociny } 417128760Spjd sbuf_printf(sb, "%s<timeout>%u</timeout>\n", indent, sc->sc_timeout); 418128760Spjd sbuf_printf(sb, "%s<info>%s</info>\n", indent, sc->sc_info); 419128760Spjd sbuf_printf(sb, "%s<queue_count>%u</queue_count>\n", indent, 420128760Spjd sc->sc_queue_count); 421128760Spjd sbuf_printf(sb, "%s<queue_size>%u</queue_size>\n", indent, 422128760Spjd sc->sc_queue_size); 423128760Spjd sbuf_printf(sb, "%s<ref>%u</ref>\n", indent, sc->sc_ref); 424204076Spjd sbuf_printf(sb, "%s<unit>%d</unit>\n", indent, sc->sc_unit); 425130836Spjd g_topology_unlock(); 426128760Spjd g_gate_release(sc); 427130836Spjd g_topology_lock(); 428128760Spjd} 429128760Spjd 430128760Spjdstatic int 431128760Spjdg_gate_create(struct g_gate_ctl_create *ggio) 432128760Spjd{ 433128760Spjd struct g_gate_softc *sc; 434128760Spjd struct g_geom *gp; 435240265Strociny struct g_provider *pp, *ropp; 436240265Strociny struct g_consumer *cp; 437204076Spjd char name[NAME_MAX]; 438204076Spjd int error = 0, unit; 439128760Spjd 440240265Strociny if (ggio->gctl_mediasize <= 0) { 441128760Spjd G_GATE_DEBUG(1, "Invalid media size."); 442128760Spjd return (EINVAL); 443128760Spjd } 444240265Strociny if (ggio->gctl_sectorsize <= 0) { 445128760Spjd G_GATE_DEBUG(1, "Invalid sector size."); 446128760Spjd return (EINVAL); 447128760Spjd } 448240265Strociny if (!powerof2(ggio->gctl_sectorsize)) { 449240265Strociny G_GATE_DEBUG(1, "Invalid sector size."); 450240265Strociny return (EINVAL); 451240265Strociny } 452141312Spjd if ((ggio->gctl_mediasize % ggio->gctl_sectorsize) != 0) { 453141312Spjd G_GATE_DEBUG(1, "Invalid media size."); 454141312Spjd return (EINVAL); 455141312Spjd } 456128760Spjd if ((ggio->gctl_flags & G_GATE_FLAG_READONLY) != 0 && 457128760Spjd (ggio->gctl_flags & G_GATE_FLAG_WRITEONLY) != 0) { 458128760Spjd G_GATE_DEBUG(1, "Invalid flags."); 459128760Spjd return (EINVAL); 460128760Spjd } 461204076Spjd if (ggio->gctl_unit != G_GATE_UNIT_AUTO && 462204076Spjd ggio->gctl_unit != G_GATE_NAME_GIVEN && 463204076Spjd ggio->gctl_unit < 0) { 464128760Spjd G_GATE_DEBUG(1, "Invalid unit number."); 465128760Spjd return (EINVAL); 466128760Spjd } 467204076Spjd if (ggio->gctl_unit == G_GATE_NAME_GIVEN && 468204076Spjd ggio->gctl_name[0] == '\0') { 469204076Spjd G_GATE_DEBUG(1, "No device name."); 470204076Spjd return (EINVAL); 471204076Spjd } 472128760Spjd 473128760Spjd sc = malloc(sizeof(*sc), M_GATE, M_WAITOK | M_ZERO); 474128760Spjd sc->sc_flags = (ggio->gctl_flags & G_GATE_USERFLAGS); 475128760Spjd strlcpy(sc->sc_info, ggio->gctl_info, sizeof(sc->sc_info)); 476204076Spjd sc->sc_seq = 1; 477128760Spjd bioq_init(&sc->sc_inqueue); 478128760Spjd bioq_init(&sc->sc_outqueue); 479141561Spjd mtx_init(&sc->sc_queue_mtx, "gg:queue", NULL, MTX_DEF); 480128760Spjd sc->sc_queue_count = 0; 481128760Spjd sc->sc_queue_size = ggio->gctl_maxcount; 482128760Spjd if (sc->sc_queue_size > G_GATE_MAX_QUEUE_SIZE) 483128760Spjd sc->sc_queue_size = G_GATE_MAX_QUEUE_SIZE; 484128760Spjd sc->sc_timeout = ggio->gctl_timeout; 485128881Spjd callout_init(&sc->sc_callout, CALLOUT_MPSAFE); 486240265Strociny 487204076Spjd mtx_lock(&g_gate_units_lock); 488204076Spjd sc->sc_unit = g_gate_getunit(ggio->gctl_unit, &error); 489240265Strociny if (sc->sc_unit < 0) 490240265Strociny goto fail1; 491204076Spjd if (ggio->gctl_unit == G_GATE_NAME_GIVEN) 492204076Spjd snprintf(name, sizeof(name), "%s", ggio->gctl_name); 493204076Spjd else { 494204076Spjd snprintf(name, sizeof(name), "%s%d", G_GATE_PROVIDER_NAME, 495204076Spjd sc->sc_unit); 496204076Spjd } 497204076Spjd /* Check for name collision. */ 498204076Spjd for (unit = 0; unit < g_gate_maxunits; unit++) { 499204076Spjd if (g_gate_units[unit] == NULL) 500204076Spjd continue; 501220062Strociny if (strcmp(name, g_gate_units[unit]->sc_name) != 0) 502204076Spjd continue; 503240265Strociny error = EEXIST; 504240265Strociny goto fail1; 505204076Spjd } 506220062Strociny sc->sc_name = name; 507204076Spjd g_gate_units[sc->sc_unit] = sc; 508204076Spjd g_gate_nunits++; 509204076Spjd mtx_unlock(&g_gate_units_lock); 510128760Spjd 511240265Strociny g_topology_lock(); 512204076Spjd 513240265Strociny if (ggio->gctl_readprov[0] == '\0') { 514240265Strociny ropp = NULL; 515240265Strociny } else { 516240265Strociny ropp = g_provider_by_name(ggio->gctl_readprov); 517240265Strociny if (ropp == NULL) { 518240265Strociny G_GATE_DEBUG(1, "Provider %s doesn't exist.", 519240265Strociny ggio->gctl_readprov); 520240265Strociny error = EINVAL; 521240265Strociny goto fail2; 522240265Strociny } 523240265Strociny if ((ggio->gctl_readoffset % ggio->gctl_sectorsize) != 0) { 524240265Strociny G_GATE_DEBUG(1, "Invalid read offset."); 525240265Strociny error = EINVAL; 526240265Strociny goto fail2; 527240265Strociny } 528240265Strociny if (ggio->gctl_mediasize + ggio->gctl_readoffset > 529240265Strociny ropp->mediasize) { 530240265Strociny G_GATE_DEBUG(1, "Invalid read offset or media size."); 531240265Strociny error = EINVAL; 532240265Strociny goto fail2; 533240265Strociny } 534240265Strociny } 535240265Strociny 536204076Spjd gp = g_new_geomf(&g_gate_class, "%s", name); 537128760Spjd gp->start = g_gate_start; 538128760Spjd gp->access = g_gate_access; 539240265Strociny gp->orphan = g_gate_orphan; 540128760Spjd gp->dumpconf = g_gate_dumpconf; 541128760Spjd gp->softc = sc; 542240265Strociny 543240265Strociny if (ropp != NULL) { 544240265Strociny cp = g_new_consumer(gp); 545240265Strociny error = g_attach(cp, ropp); 546240265Strociny if (error != 0) { 547240265Strociny G_GATE_DEBUG(1, "Unable to attach to %s.", ropp->name); 548240265Strociny goto fail3; 549240265Strociny } 550240265Strociny error = g_access(cp, 1, 0, 0); 551240265Strociny if (error != 0) { 552240265Strociny G_GATE_DEBUG(1, "Unable to access %s.", ropp->name); 553240265Strociny g_detach(cp); 554240265Strociny goto fail3; 555240265Strociny } 556240265Strociny sc->sc_readcons = cp; 557240265Strociny sc->sc_readoffset = ggio->gctl_readoffset; 558240265Strociny } 559240265Strociny 560240265Strociny ggio->gctl_unit = sc->sc_unit; 561240265Strociny 562204076Spjd pp = g_new_providerf(gp, "%s", name); 563128760Spjd pp->mediasize = ggio->gctl_mediasize; 564128760Spjd pp->sectorsize = ggio->gctl_sectorsize; 565128760Spjd sc->sc_provider = pp; 566128760Spjd g_error_provider(pp, 0); 567240265Strociny 568128760Spjd g_topology_unlock(); 569220062Strociny mtx_lock(&g_gate_units_lock); 570220062Strociny sc->sc_name = sc->sc_provider->name; 571220062Strociny mtx_unlock(&g_gate_units_lock); 572220173Strociny G_GATE_DEBUG(1, "Device %s created.", gp->name); 573128760Spjd 574128760Spjd if (sc->sc_timeout > 0) { 575128760Spjd callout_reset(&sc->sc_callout, sc->sc_timeout * hz, 576128760Spjd g_gate_guard, sc); 577128760Spjd } 578128760Spjd return (0); 579240265Strocinyfail3: 580240265Strociny g_destroy_consumer(cp); 581240265Strociny g_destroy_geom(gp); 582240265Strocinyfail2: 583240265Strociny g_topology_unlock(); 584240265Strociny mtx_lock(&g_gate_units_lock); 585240265Strociny g_gate_units[sc->sc_unit] = NULL; 586240265Strociny KASSERT(g_gate_nunits > 0, ("negative g_gate_nunits?")); 587240265Strociny g_gate_nunits--; 588240265Strocinyfail1: 589240265Strociny mtx_unlock(&g_gate_units_lock); 590240265Strociny mtx_destroy(&sc->sc_queue_mtx); 591240265Strociny free(sc, M_GATE); 592240265Strociny return (error); 593128760Spjd} 594128760Spjd 595240265Strocinystatic int 596240265Strocinyg_gate_modify(struct g_gate_softc *sc, struct g_gate_ctl_modify *ggio) 597240265Strociny{ 598240265Strociny struct g_provider *pp; 599240265Strociny struct g_consumer *cp; 600240265Strociny int error; 601240265Strociny 602240265Strociny if ((ggio->gctl_modify & GG_MODIFY_MEDIASIZE) != 0) { 603240265Strociny if (ggio->gctl_mediasize <= 0) { 604240265Strociny G_GATE_DEBUG(1, "Invalid media size."); 605240265Strociny return (EINVAL); 606240265Strociny } 607240265Strociny pp = sc->sc_provider; 608240265Strociny if ((ggio->gctl_mediasize % pp->sectorsize) != 0) { 609240265Strociny G_GATE_DEBUG(1, "Invalid media size."); 610240265Strociny return (EINVAL); 611240265Strociny } 612240265Strociny /* TODO */ 613240265Strociny return (EOPNOTSUPP); 614240265Strociny } 615240265Strociny 616240265Strociny if ((ggio->gctl_modify & GG_MODIFY_INFO) != 0) 617240265Strociny (void)strlcpy(sc->sc_info, ggio->gctl_info, sizeof(sc->sc_info)); 618240265Strociny 619240265Strociny cp = NULL; 620240265Strociny 621240265Strociny if ((ggio->gctl_modify & GG_MODIFY_READPROV) != 0) { 622240265Strociny g_topology_lock(); 623240265Strociny if (sc->sc_readcons != NULL) { 624240265Strociny cp = sc->sc_readcons; 625240265Strociny sc->sc_readcons = NULL; 626240265Strociny (void)g_access(cp, -1, 0, 0); 627240265Strociny g_detach(cp); 628240265Strociny g_destroy_consumer(cp); 629240265Strociny } 630240265Strociny if (ggio->gctl_readprov[0] != '\0') { 631240265Strociny pp = g_provider_by_name(ggio->gctl_readprov); 632240265Strociny if (pp == NULL) { 633240265Strociny g_topology_unlock(); 634240265Strociny G_GATE_DEBUG(1, "Provider %s doesn't exist.", 635240265Strociny ggio->gctl_readprov); 636240265Strociny return (EINVAL); 637240265Strociny } 638240265Strociny cp = g_new_consumer(sc->sc_provider->geom); 639240265Strociny error = g_attach(cp, pp); 640240265Strociny if (error != 0) { 641240265Strociny G_GATE_DEBUG(1, "Unable to attach to %s.", 642240265Strociny pp->name); 643240265Strociny } else { 644240265Strociny error = g_access(cp, 1, 0, 0); 645240265Strociny if (error != 0) { 646240265Strociny G_GATE_DEBUG(1, "Unable to access %s.", 647240265Strociny pp->name); 648240265Strociny g_detach(cp); 649240265Strociny } 650240265Strociny } 651240265Strociny if (error != 0) { 652240265Strociny g_destroy_consumer(cp); 653240265Strociny g_topology_unlock(); 654240265Strociny return (error); 655240265Strociny } 656240265Strociny } 657240265Strociny } else { 658240265Strociny cp = sc->sc_readcons; 659240265Strociny } 660240265Strociny 661240265Strociny if ((ggio->gctl_modify & GG_MODIFY_READOFFSET) != 0) { 662240265Strociny if (cp == NULL) { 663240265Strociny G_GATE_DEBUG(1, "No read provider."); 664240265Strociny return (EINVAL); 665240265Strociny } 666240265Strociny pp = sc->sc_provider; 667240265Strociny if ((ggio->gctl_readoffset % pp->sectorsize) != 0) { 668240265Strociny G_GATE_DEBUG(1, "Invalid read offset."); 669240265Strociny return (EINVAL); 670240265Strociny } 671240265Strociny if (pp->mediasize + ggio->gctl_readoffset > 672240265Strociny cp->provider->mediasize) { 673240265Strociny G_GATE_DEBUG(1, "Invalid read offset or media size."); 674240265Strociny return (EINVAL); 675240265Strociny } 676240265Strociny sc->sc_readoffset = ggio->gctl_readoffset; 677240265Strociny } 678240265Strociny 679240265Strociny if ((ggio->gctl_modify & GG_MODIFY_READPROV) != 0) { 680240265Strociny sc->sc_readcons = cp; 681240265Strociny g_topology_unlock(); 682240265Strociny } 683240265Strociny 684240265Strociny return (0); 685240265Strociny} 686240265Strociny 687128760Spjd#define G_GATE_CHECK_VERSION(ggio) do { \ 688147843Spjd if ((ggio)->gctl_version != G_GATE_VERSION) { \ 689147843Spjd printf("Version mismatch %d != %d.\n", \ 690147843Spjd ggio->gctl_version, G_GATE_VERSION); \ 691128760Spjd return (EINVAL); \ 692147843Spjd } \ 693128760Spjd} while (0) 694128760Spjdstatic int 695130585Sphkg_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) 696128760Spjd{ 697128760Spjd struct g_gate_softc *sc; 698128760Spjd struct bio *bp; 699128760Spjd int error = 0; 700128760Spjd 701128760Spjd G_GATE_DEBUG(4, "ioctl(%s, %lx, %p, %x, %p)", devtoname(dev), cmd, addr, 702128760Spjd flags, td); 703128760Spjd 704128760Spjd switch (cmd) { 705128760Spjd case G_GATE_CMD_CREATE: 706128760Spjd { 707128760Spjd struct g_gate_ctl_create *ggio = (void *)addr; 708128760Spjd 709128760Spjd G_GATE_CHECK_VERSION(ggio); 710138014Spjd error = g_gate_create(ggio); 711141972Spjd /* 712141972Spjd * Reset TDP_GEOM flag. 713141972Spjd * There are pending events for sure, because we just created 714141972Spjd * new provider and other classes want to taste it, but we 715141972Spjd * cannot answer on I/O requests until we're here. 716141972Spjd */ 717141972Spjd td->td_pflags &= ~TDP_GEOM; 718138014Spjd return (error); 719128760Spjd } 720240265Strociny case G_GATE_CMD_MODIFY: 721240265Strociny { 722240265Strociny struct g_gate_ctl_modify *ggio = (void *)addr; 723240265Strociny 724240265Strociny G_GATE_CHECK_VERSION(ggio); 725240265Strociny sc = g_gate_hold(ggio->gctl_unit, NULL); 726240265Strociny if (sc == NULL) 727240265Strociny return (ENXIO); 728240265Strociny error = g_gate_modify(sc, ggio); 729240265Strociny g_gate_release(sc); 730240265Strociny return (error); 731240265Strociny } 732128760Spjd case G_GATE_CMD_DESTROY: 733128760Spjd { 734128760Spjd struct g_gate_ctl_destroy *ggio = (void *)addr; 735128760Spjd 736128760Spjd G_GATE_CHECK_VERSION(ggio); 737204076Spjd sc = g_gate_hold(ggio->gctl_unit, ggio->gctl_name); 738128760Spjd if (sc == NULL) 739128760Spjd return (ENXIO); 740128760Spjd g_topology_lock(); 741204076Spjd mtx_lock(&g_gate_units_lock); 742128760Spjd error = g_gate_destroy(sc, ggio->gctl_force); 743128760Spjd g_topology_unlock(); 744162056Spjd if (error != 0) 745162056Spjd g_gate_release(sc); 746128760Spjd return (error); 747128760Spjd } 748147843Spjd case G_GATE_CMD_CANCEL: 749147843Spjd { 750147843Spjd struct g_gate_ctl_cancel *ggio = (void *)addr; 751147843Spjd struct bio *tbp, *lbp; 752147843Spjd 753147843Spjd G_GATE_CHECK_VERSION(ggio); 754204076Spjd sc = g_gate_hold(ggio->gctl_unit, ggio->gctl_name); 755147843Spjd if (sc == NULL) 756147843Spjd return (ENXIO); 757147843Spjd lbp = NULL; 758147843Spjd mtx_lock(&sc->sc_queue_mtx); 759147843Spjd TAILQ_FOREACH_SAFE(bp, &sc->sc_outqueue.queue, bio_queue, tbp) { 760147843Spjd if (ggio->gctl_seq == 0 || 761147843Spjd ggio->gctl_seq == (uintptr_t)bp->bio_driver1) { 762147843Spjd G_GATE_LOGREQ(1, bp, "Request canceled."); 763147843Spjd bioq_remove(&sc->sc_outqueue, bp); 764147843Spjd /* 765147843Spjd * Be sure to put requests back onto incoming 766147843Spjd * queue in the proper order. 767147843Spjd */ 768147843Spjd if (lbp == NULL) 769147843Spjd bioq_insert_head(&sc->sc_inqueue, bp); 770147843Spjd else { 771147843Spjd TAILQ_INSERT_AFTER(&sc->sc_inqueue.queue, 772147843Spjd lbp, bp, bio_queue); 773147843Spjd } 774147843Spjd lbp = bp; 775147843Spjd /* 776147843Spjd * If only one request was canceled, leave now. 777147843Spjd */ 778147843Spjd if (ggio->gctl_seq != 0) 779147843Spjd break; 780147843Spjd } 781147843Spjd } 782204076Spjd if (ggio->gctl_unit == G_GATE_NAME_GIVEN) 783204076Spjd ggio->gctl_unit = sc->sc_unit; 784147843Spjd mtx_unlock(&sc->sc_queue_mtx); 785147843Spjd g_gate_release(sc); 786147843Spjd return (error); 787147843Spjd } 788128760Spjd case G_GATE_CMD_START: 789128760Spjd { 790128760Spjd struct g_gate_ctl_io *ggio = (void *)addr; 791128760Spjd 792128760Spjd G_GATE_CHECK_VERSION(ggio); 793204076Spjd sc = g_gate_hold(ggio->gctl_unit, NULL); 794128760Spjd if (sc == NULL) 795128760Spjd return (ENXIO); 796162056Spjd error = 0; 797128760Spjd for (;;) { 798141561Spjd mtx_lock(&sc->sc_queue_mtx); 799128760Spjd bp = bioq_first(&sc->sc_inqueue); 800128760Spjd if (bp != NULL) 801128760Spjd break; 802162056Spjd if ((sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) { 803162056Spjd ggio->gctl_error = ECANCELED; 804162056Spjd mtx_unlock(&sc->sc_queue_mtx); 805162056Spjd goto start_end; 806162056Spjd } 807141561Spjd if (msleep(sc, &sc->sc_queue_mtx, 808128760Spjd PPAUSE | PDROP | PCATCH, "ggwait", 0) != 0) { 809128760Spjd ggio->gctl_error = ECANCELED; 810162056Spjd goto start_end; 811128760Spjd } 812128760Spjd } 813128881Spjd ggio->gctl_cmd = bp->bio_cmd; 814251918Smav if (bp->bio_cmd == BIO_WRITE && 815128760Spjd bp->bio_length > ggio->gctl_length) { 816141561Spjd mtx_unlock(&sc->sc_queue_mtx); 817128760Spjd ggio->gctl_length = bp->bio_length; 818128760Spjd ggio->gctl_error = ENOMEM; 819162056Spjd goto start_end; 820128760Spjd } 821128760Spjd bioq_remove(&sc->sc_inqueue, bp); 822141561Spjd bioq_insert_tail(&sc->sc_outqueue, bp); 823141561Spjd mtx_unlock(&sc->sc_queue_mtx); 824141561Spjd 825128835Spjd ggio->gctl_seq = (uintptr_t)bp->bio_driver1; 826128760Spjd ggio->gctl_offset = bp->bio_offset; 827128760Spjd ggio->gctl_length = bp->bio_length; 828147843Spjd 829128760Spjd switch (bp->bio_cmd) { 830128760Spjd case BIO_READ: 831209186Spjd case BIO_DELETE: 832222225Spjd case BIO_FLUSH: 833128760Spjd break; 834128760Spjd case BIO_WRITE: 835128760Spjd error = copyout(bp->bio_data, ggio->gctl_data, 836128760Spjd bp->bio_length); 837128760Spjd if (error != 0) { 838141561Spjd mtx_lock(&sc->sc_queue_mtx); 839141561Spjd bioq_remove(&sc->sc_outqueue, bp); 840141312Spjd bioq_insert_head(&sc->sc_inqueue, bp); 841141561Spjd mtx_unlock(&sc->sc_queue_mtx); 842162056Spjd goto start_end; 843128760Spjd } 844128760Spjd break; 845128760Spjd } 846162056Spjdstart_end: 847162056Spjd g_gate_release(sc); 848162056Spjd return (error); 849128760Spjd } 850128760Spjd case G_GATE_CMD_DONE: 851128760Spjd { 852128760Spjd struct g_gate_ctl_io *ggio = (void *)addr; 853128760Spjd 854128760Spjd G_GATE_CHECK_VERSION(ggio); 855204076Spjd sc = g_gate_hold(ggio->gctl_unit, NULL); 856128760Spjd if (sc == NULL) 857128760Spjd return (ENOENT); 858162056Spjd error = 0; 859141561Spjd mtx_lock(&sc->sc_queue_mtx); 860128760Spjd TAILQ_FOREACH(bp, &sc->sc_outqueue.queue, bio_queue) { 861128835Spjd if (ggio->gctl_seq == (uintptr_t)bp->bio_driver1) 862128760Spjd break; 863128760Spjd } 864128760Spjd if (bp != NULL) { 865128760Spjd bioq_remove(&sc->sc_outqueue, bp); 866141561Spjd sc->sc_queue_count--; 867128760Spjd } 868141561Spjd mtx_unlock(&sc->sc_queue_mtx); 869128760Spjd if (bp == NULL) { 870128760Spjd /* 871128760Spjd * Request was probably canceled. 872128760Spjd */ 873162056Spjd goto done_end; 874128760Spjd } 875128760Spjd if (ggio->gctl_error == EAGAIN) { 876128760Spjd bp->bio_error = 0; 877128760Spjd G_GATE_LOGREQ(1, bp, "Request desisted."); 878141561Spjd mtx_lock(&sc->sc_queue_mtx); 879141561Spjd sc->sc_queue_count++; 880141312Spjd bioq_insert_head(&sc->sc_inqueue, bp); 881128957Spjd wakeup(sc); 882141561Spjd mtx_unlock(&sc->sc_queue_mtx); 883128760Spjd } else { 884128760Spjd bp->bio_error = ggio->gctl_error; 885128760Spjd if (bp->bio_error == 0) { 886128760Spjd bp->bio_completed = bp->bio_length; 887128760Spjd switch (bp->bio_cmd) { 888128760Spjd case BIO_READ: 889128760Spjd error = copyin(ggio->gctl_data, 890128760Spjd bp->bio_data, bp->bio_length); 891128760Spjd if (error != 0) 892128760Spjd bp->bio_error = error; 893128760Spjd break; 894128760Spjd case BIO_DELETE: 895128760Spjd case BIO_WRITE: 896222225Spjd case BIO_FLUSH: 897128760Spjd break; 898128760Spjd } 899128760Spjd } 900128760Spjd G_GATE_LOGREQ(2, bp, "Request done."); 901128760Spjd g_io_deliver(bp, bp->bio_error); 902128760Spjd } 903162056Spjddone_end: 904162056Spjd g_gate_release(sc); 905128760Spjd return (error); 906128760Spjd } 907128760Spjd } 908128760Spjd return (ENOIOCTL); 909128760Spjd} 910128760Spjd 911128760Spjdstatic void 912131411Spjdg_gate_device(void) 913128760Spjd{ 914128760Spjd 915128760Spjd status_dev = make_dev(&g_gate_cdevsw, 0x0, UID_ROOT, GID_WHEEL, 0600, 916128760Spjd G_GATE_CTL_NAME); 917128760Spjd} 918128760Spjd 919128760Spjdstatic int 920128760Spjdg_gate_modevent(module_t mod, int type, void *data) 921128760Spjd{ 922128760Spjd int error = 0; 923128760Spjd 924128760Spjd switch (type) { 925128760Spjd case MOD_LOAD: 926204076Spjd mtx_init(&g_gate_units_lock, "gg_units_lock", NULL, MTX_DEF); 927204076Spjd g_gate_units = malloc(g_gate_maxunits * sizeof(g_gate_units[0]), 928204076Spjd M_GATE, M_WAITOK | M_ZERO); 929204076Spjd g_gate_nunits = 0; 930131411Spjd g_gate_device(); 931128760Spjd break; 932128760Spjd case MOD_UNLOAD: 933204076Spjd mtx_lock(&g_gate_units_lock); 934204076Spjd if (g_gate_nunits > 0) { 935204076Spjd mtx_unlock(&g_gate_units_lock); 936128760Spjd error = EBUSY; 937128760Spjd break; 938128760Spjd } 939204076Spjd mtx_unlock(&g_gate_units_lock); 940204076Spjd mtx_destroy(&g_gate_units_lock); 941128760Spjd if (status_dev != 0) 942128760Spjd destroy_dev(status_dev); 943204076Spjd free(g_gate_units, M_GATE); 944128760Spjd break; 945128760Spjd default: 946132199Sphk return (EOPNOTSUPP); 947128760Spjd break; 948128760Spjd } 949128760Spjd 950128760Spjd return (error); 951128760Spjd} 952128760Spjdstatic moduledata_t g_gate_module = { 953128760Spjd G_GATE_MOD_NAME, 954128760Spjd g_gate_modevent, 955128760Spjd NULL 956128760Spjd}; 957128760SpjdDECLARE_MODULE(geom_gate, g_gate_module, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 958128760SpjdDECLARE_GEOM_CLASS(g_gate_class, g_gate); 959