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