g_gate.c revision 147843
1171568Sscottl/*- 2211095Sdes * Copyright (c) 2004-2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3171568Sscottl * All rights reserved. 4171568Sscottl * 5171568Sscottl * Redistribution and use in source and binary forms, with or without 6171568Sscottl * modification, are permitted provided that the following conditions 7171568Sscottl * are met: 8171568Sscottl * 1. Redistributions of source code must retain the above copyright 9171568Sscottl * notice, this list of conditions and the following disclaimer. 10171568Sscottl * 2. Redistributions in binary form must reproduce the above copyright 11171568Sscottl * notice, this list of conditions and the following disclaimer in the 12171568Sscottl * documentation and/or other materials provided with the distribution. 13171568Sscottl * 14171568Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15171568Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16171568Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17171568Sscottl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18171568Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19171568Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20171568Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21171568Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22171568Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23171568Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24171568Sscottl * SUCH DAMAGE. 25171568Sscottl * 26171568Sscottl * $FreeBSD: head/sys/geom/gate/g_gate.c 147843 2005-07-08 21:08:53Z pjd $ 27171568Sscottl */ 28211095Sdes 29171568Sscottl#include <sys/param.h> 30171568Sscottl#include <sys/systm.h> 31171568Sscottl#include <sys/bio.h> 32171568Sscottl#include <sys/conf.h> 33171568Sscottl#include <sys/kernel.h> 34171568Sscottl#include <sys/kthread.h> 35171568Sscottl#include <sys/fcntl.h> 36171568Sscottl#include <sys/linker.h> 37171568Sscottl#include <sys/lock.h> 38171568Sscottl#include <sys/malloc.h> 39171568Sscottl#include <sys/mutex.h> 40171568Sscottl#include <sys/proc.h> 41171568Sscottl#include <sys/limits.h> 42171568Sscottl#include <sys/queue.h> 43171568Sscottl#include <sys/sysctl.h> 44171568Sscottl#include <sys/signalvar.h> 45171568Sscottl#include <sys/time.h> 46211095Sdes#include <machine/atomic.h> 47171568Sscottl 48171568Sscottl#include <geom/geom.h> 49171568Sscottl#include <geom/gate/g_gate.h> 50171568Sscottl 51171568Sscottlstatic MALLOC_DEFINE(M_GATE, "gg data", "GEOM Gate Data"); 52171568Sscottl 53171568SscottlSYSCTL_DECL(_kern_geom); 54171568SscottlSYSCTL_NODE(_kern_geom, OID_AUTO, gate, CTLFLAG_RW, 0, "GEOM_GATE stuff"); 55171568Sscottlstatic u_int g_gate_debug = 0; 56254657StraszSYSCTL_UINT(_kern_geom_gate, OID_AUTO, debug, CTLFLAG_RW, &g_gate_debug, 0, 57254657Strasz "Debug level"); 58171568Sscottl 59171568Sscottlstatic int g_gate_destroy_geom(struct gctl_req *, struct g_class *, 60171568Sscottl struct g_geom *); 61171568Sscottlstruct g_class g_gate_class = { 62171568Sscottl .name = G_GATE_CLASS_NAME, 63171568Sscottl .version = G_VERSION, 64171568Sscottl .destroy_geom = g_gate_destroy_geom 65171568Sscottl}; 66171568Sscottl 67171568Sscottlstatic struct cdev *status_dev; 68171568Sscottlstatic d_ioctl_t g_gate_ioctl; 69171568Sscottlstatic struct cdevsw g_gate_cdevsw = { 70171568Sscottl .d_version = D_VERSION, 71171568Sscottl .d_ioctl = g_gate_ioctl, 72171568Sscottl .d_name = G_GATE_CTL_NAME 73171568Sscottl}; 74171568Sscottl 75171568Sscottl 76171568Sscottlstatic LIST_HEAD(, g_gate_softc) g_gate_list = 77171568Sscottl LIST_HEAD_INITIALIZER(&g_gate_list); 78171568Sscottlstatic struct mtx g_gate_list_mtx; 79171568Sscottl 80171568Sscottl 81171568Sscottlstatic void 82171568Sscottlg_gate_wither(struct g_gate_softc *sc) 83171568Sscottl{ 84171568Sscottl 85171568Sscottl atomic_set_32(&sc->sc_flags, G_GATE_FLAG_DESTROY); 86171568Sscottl} 87226208Skib 88171568Sscottlstatic int 89171568Sscottlg_gate_destroy(struct g_gate_softc *sc, boolean_t force) 90171568Sscottl{ 91171568Sscottl struct g_provider *pp; 92171568Sscottl struct bio *bp; 93171568Sscottl 94171568Sscottl g_topology_assert(); 95171568Sscottl mtx_assert(&g_gate_list_mtx, MA_OWNED); 96171568Sscottl pp = sc->sc_provider; 97171568Sscottl if (!force && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 98185289Sscottl mtx_unlock(&g_gate_list_mtx); 99171568Sscottl return (EBUSY); 100185289Sscottl } 101185289Sscottl if ((sc->sc_flags & G_GATE_FLAG_DESTROY) == 0) { 102185289Sscottl g_gate_wither(sc); 103185289Sscottl LIST_REMOVE(sc, sc_next); 104185289Sscottl } 105185289Sscottl mtx_unlock(&g_gate_list_mtx); 106185289Sscottl mtx_lock(&sc->sc_queue_mtx); 107185289Sscottl wakeup(sc); 108185289Sscottl mtx_unlock(&sc->sc_queue_mtx); 109185289Sscottl if (sc->sc_ref > 0) { 110185289Sscottl G_GATE_DEBUG(1, "Cannot destroy %s yet.", sc->sc_name); 111185289Sscottl return (0); 112171568Sscottl } 113171568Sscottl callout_drain(&sc->sc_callout); 114171568Sscottl mtx_lock(&sc->sc_queue_mtx); 115171568Sscottl for (;;) { 116171568Sscottl bp = bioq_first(&sc->sc_inqueue); 117171568Sscottl if (bp != NULL) { 118171568Sscottl bioq_remove(&sc->sc_inqueue, bp); 119171568Sscottl sc->sc_queue_count--; 120171568Sscottl G_GATE_LOGREQ(1, bp, "Request canceled."); 121171568Sscottl g_io_deliver(bp, ENXIO); 122171568Sscottl } else { 123171568Sscottl break; 124171568Sscottl } 125171568Sscottl } 126171568Sscottl for (;;) { 127211095Sdes bp = bioq_first(&sc->sc_outqueue); 128171568Sscottl if (bp != NULL) { 129171568Sscottl bioq_remove(&sc->sc_outqueue, bp); 130171568Sscottl sc->sc_queue_count--; 131171568Sscottl G_GATE_LOGREQ(1, bp, "Request canceled."); 132171568Sscottl g_io_deliver(bp, ENXIO); 133171568Sscottl } else { 134171568Sscottl break; 135171568Sscottl } 136171568Sscottl } 137171568Sscottl mtx_destroy(&sc->sc_queue_mtx); 138171568Sscottl G_GATE_DEBUG(0, "Device %s destroyed.", sc->sc_name); 139171568Sscottl pp->geom->softc = NULL; 140171568Sscottl g_wither_geom(pp->geom, ENXIO); 141171568Sscottl sc->sc_provider = NULL; 142171568Sscottl free(sc, M_GATE); 143171568Sscottl return (0); 144171568Sscottl} 145171568Sscottl 146171568Sscottlstatic void 147171568Sscottlg_gate_destroy_it(void *arg, int flag __unused) 148171568Sscottl{ 149171568Sscottl struct g_gate_softc *sc; 150171568Sscottl 151171568Sscottl g_topology_assert(); 152171568Sscottl sc = arg; 153171568Sscottl mtx_lock(&g_gate_list_mtx); 154171568Sscottl g_gate_destroy(sc, 1); 155171568Sscottl} 156171568Sscottl 157234233Sjpaetzelstatic int 158171568Sscottlg_gate_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp) 159171568Sscottl{ 160171568Sscottl 161171568Sscottl g_topology_assert(); 162171568Sscottl mtx_lock(&g_gate_list_mtx); 163171568Sscottl return (g_gate_destroy(gp->softc, 0)); 164171568Sscottl} 165171568Sscottl 166171568Sscottlstatic int 167171568Sscottlg_gate_access(struct g_provider *pp, int dr, int dw, int de) 168171568Sscottl{ 169171568Sscottl struct g_gate_softc *sc; 170171568Sscottl 171171568Sscottl if (dr <= 0 && dw <= 0 && de <= 0) 172171568Sscottl return (0); 173171568Sscottl sc = pp->geom->softc; 174171568Sscottl if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) 175171568Sscottl return (ENXIO); 176171568Sscottl /* XXX: Hack to allow read-only mounts. */ 177171568Sscottl#if 0 178171568Sscottl if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0 && dw > 0) 179225950Sken return (EPERM); 180234233Sjpaetzel#endif 181225950Sken if ((sc->sc_flags & G_GATE_FLAG_WRITEONLY) != 0 && dr > 0) 182171568Sscottl return (EPERM); 183171568Sscottl return (0); 184171568Sscottl} 185225950Sken 186171568Sscottlstatic void 187171568Sscottlg_gate_start(struct bio *bp) 188171568Sscottl{ 189171568Sscottl struct g_gate_softc *sc; 190171568Sscottl 191171568Sscottl sc = bp->bio_to->geom->softc; 192171568Sscottl if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) { 193171568Sscottl g_io_deliver(bp, ENXIO); 194171568Sscottl return; 195171568Sscottl } 196171568Sscottl G_GATE_LOGREQ(2, bp, "Request received."); 197211095Sdes switch (bp->bio_cmd) { 198171568Sscottl case BIO_READ: 199171568Sscottl break; 200171568Sscottl case BIO_DELETE: 201171568Sscottl case BIO_WRITE: 202171568Sscottl /* XXX: Hack to allow read-only mounts. */ 203171568Sscottl if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0) { 204211095Sdes g_io_deliver(bp, EPERM); 205171568Sscottl return; 206211095Sdes } 207171568Sscottl break; 208171568Sscottl case BIO_GETATTR: 209171568Sscottl default: 210171568Sscottl G_GATE_LOGREQ(2, bp, "Ignoring request."); 211171568Sscottl g_io_deliver(bp, EOPNOTSUPP); 212171568Sscottl return; 213171568Sscottl } 214171568Sscottl 215171568Sscottl mtx_lock(&sc->sc_queue_mtx); 216171568Sscottl if (sc->sc_queue_count > sc->sc_queue_size) { 217171568Sscottl mtx_unlock(&sc->sc_queue_mtx); 218171568Sscottl G_GATE_LOGREQ(1, bp, "Queue full, request canceled."); 219171568Sscottl g_io_deliver(bp, EIO); 220171568Sscottl return; 221171568Sscottl } 222171568Sscottl 223171568Sscottl bp->bio_driver1 = (void *)sc->sc_seq; 224171568Sscottl sc->sc_seq++; 225171568Sscottl sc->sc_queue_count++; 226171568Sscottl 227171568Sscottl bioq_insert_tail(&sc->sc_inqueue, bp); 228171568Sscottl wakeup(sc); 229171568Sscottl 230171568Sscottl mtx_unlock(&sc->sc_queue_mtx); 231171568Sscottl} 232171568Sscottl 233171568Sscottlstatic struct g_gate_softc * 234171568Sscottlg_gate_find(u_int unit) 235171568Sscottl{ 236171568Sscottl struct g_gate_softc *sc; 237171568Sscottl 238185289Sscottl LIST_FOREACH(sc, &g_gate_list, sc_next) { 239171568Sscottl if (sc->sc_unit == unit) 240171568Sscottl break; 241171568Sscottl } 242171568Sscottl return (sc); 243171568Sscottl} 244171568Sscottl 245171568Sscottlstatic struct g_gate_softc * 246171568Sscottlg_gate_hold(u_int unit) 247171568Sscottl{ 248171568Sscottl struct g_gate_softc *sc; 249171568Sscottl 250211095Sdes mtx_lock(&g_gate_list_mtx); 251171568Sscottl sc = g_gate_find(unit); 252211095Sdes if (sc != NULL) { 253171568Sscottl if ((sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) 254171568Sscottl sc = NULL; 255171568Sscottl else 256171568Sscottl sc->sc_ref++; 257171568Sscottl } 258171568Sscottl mtx_unlock(&g_gate_list_mtx); 259171568Sscottl return (sc); 260171568Sscottl} 261171568Sscottl 262171568Sscottlstatic void 263171568Sscottlg_gate_release(struct g_gate_softc *sc) 264171568Sscottl{ 265211095Sdes 266185289Sscottl g_topology_assert_not(); 267171568Sscottl mtx_lock(&g_gate_list_mtx); 268171568Sscottl sc->sc_ref--; 269211095Sdes KASSERT(sc->sc_ref >= 0, ("Negative sc_ref for %s.", sc->sc_name)); 270211095Sdes if (sc->sc_ref == 0 && (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) { 271211095Sdes mtx_unlock(&g_gate_list_mtx); 272211095Sdes g_waitfor_event(g_gate_destroy_it, sc, M_WAITOK, NULL); 273211095Sdes } else { 274211095Sdes mtx_unlock(&g_gate_list_mtx); 275211095Sdes } 276171568Sscottl} 277171568Sscottl 278185289Sscottlstatic int 279171568Sscottlg_gate_getunit(int unit) 280171568Sscottl{ 281171568Sscottl struct g_gate_softc *sc; 282171568Sscottl 283171568Sscottl mtx_assert(&g_gate_list_mtx, MA_OWNED); 284171568Sscottl if (unit >= 0) { 285171568Sscottl LIST_FOREACH(sc, &g_gate_list, sc_next) { 286171568Sscottl if (sc->sc_unit == unit) 287171568Sscottl return (-1); 288171568Sscottl } 289171568Sscottl } else { 290171568Sscottl unit = 0; 291171568Sscottlonce_again: 292171568Sscottl LIST_FOREACH(sc, &g_gate_list, sc_next) { 293171568Sscottl if (sc->sc_unit == unit) { 294171568Sscottl if (++unit > 666) 295171568Sscottl return (-1); 296171568Sscottl goto once_again; 297171568Sscottl } 298171568Sscottl } 299171568Sscottl } 300171568Sscottl return (unit); 301171568Sscottl} 302171568Sscottl 303171568Sscottlstatic void 304171568Sscottlg_gate_guard(void *arg) 305185289Sscottl{ 306185289Sscottl struct g_gate_softc *sc; 307185289Sscottl struct bintime curtime; 308185289Sscottl struct bio *bp, *bp2; 309171568Sscottl 310171568Sscottl sc = arg; 311171568Sscottl binuptime(&curtime); 312171568Sscottl g_gate_hold(sc->sc_unit); 313171568Sscottl mtx_lock(&sc->sc_queue_mtx); 314171568Sscottl TAILQ_FOREACH_SAFE(bp, &sc->sc_inqueue.queue, bio_queue, bp2) { 315171568Sscottl if (curtime.sec - bp->bio_t0.sec < 5) 316171568Sscottl continue; 317171568Sscottl bioq_remove(&sc->sc_inqueue, bp); 318171568Sscottl sc->sc_queue_count--; 319171568Sscottl G_GATE_LOGREQ(1, bp, "Request timeout."); 320171568Sscottl g_io_deliver(bp, EIO); 321171568Sscottl } 322171568Sscottl TAILQ_FOREACH_SAFE(bp, &sc->sc_outqueue.queue, bio_queue, bp2) { 323171568Sscottl if (curtime.sec - bp->bio_t0.sec < 5) 324171568Sscottl continue; 325171568Sscottl bioq_remove(&sc->sc_outqueue, bp); 326211095Sdes sc->sc_queue_count--; 327171568Sscottl G_GATE_LOGREQ(1, bp, "Request timeout."); 328211095Sdes g_io_deliver(bp, EIO); 329211095Sdes } 330211095Sdes mtx_unlock(&sc->sc_queue_mtx); 331211095Sdes if ((sc->sc_flags & G_GATE_FLAG_DESTROY) == 0) { 332171568Sscottl callout_reset(&sc->sc_callout, sc->sc_timeout * hz, 333171568Sscottl g_gate_guard, sc); 334171568Sscottl } 335171568Sscottl g_gate_release(sc); 336171568Sscottl} 337211095Sdes 338211095Sdesstatic void 339211095Sdesg_gate_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 340211095Sdes struct g_consumer *cp, struct g_provider *pp) 341211095Sdes{ 342171568Sscottl struct g_gate_softc *sc; 343171568Sscottl 344171568Sscottl sc = gp->softc; 345171568Sscottl if (sc == NULL || pp != NULL || cp != NULL) 346171568Sscottl return; 347171568Sscottl g_gate_hold(sc->sc_unit); 348171568Sscottl if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0) { 349171568Sscottl sbuf_printf(sb, "%s<access>%s</access>\n", indent, "read-only"); 350171568Sscottl } else if ((sc->sc_flags & G_GATE_FLAG_WRITEONLY) != 0) { 351171568Sscottl sbuf_printf(sb, "%s<access>%s</access>\n", indent, 352171568Sscottl "write-only"); 353171568Sscottl } else { 354171568Sscottl sbuf_printf(sb, "%s<access>%s</access>\n", indent, 355171568Sscottl "read-write"); 356211095Sdes } 357171568Sscottl sbuf_printf(sb, "%s<timeout>%u</timeout>\n", indent, sc->sc_timeout); 358171568Sscottl sbuf_printf(sb, "%s<info>%s</info>\n", indent, sc->sc_info); 359171568Sscottl sbuf_printf(sb, "%s<queue_count>%u</queue_count>\n", indent, 360171568Sscottl sc->sc_queue_count); 361171568Sscottl sbuf_printf(sb, "%s<queue_size>%u</queue_size>\n", indent, 362185289Sscottl sc->sc_queue_size); 363185289Sscottl sbuf_printf(sb, "%s<ref>%u</ref>\n", indent, sc->sc_ref); 364185289Sscottl g_topology_unlock(); 365185289Sscottl g_gate_release(sc); 366185289Sscottl g_topology_lock(); 367185289Sscottl} 368185289Sscottl 369185289Sscottlstatic int 370171568Sscottlg_gate_create(struct g_gate_ctl_create *ggio) 371171568Sscottl{ 372171568Sscottl struct g_gate_softc *sc; 373171568Sscottl struct g_geom *gp; 374171568Sscottl struct g_provider *pp; 375171568Sscottl 376171568Sscottl if (ggio->gctl_mediasize == 0) { 377171568Sscottl G_GATE_DEBUG(1, "Invalid media size."); 378171568Sscottl return (EINVAL); 379171568Sscottl } 380171568Sscottl if (ggio->gctl_sectorsize > 0 && !powerof2(ggio->gctl_sectorsize)) { 381171568Sscottl G_GATE_DEBUG(1, "Invalid sector size."); 382185289Sscottl return (EINVAL); 383171568Sscottl } 384185289Sscottl if ((ggio->gctl_mediasize % ggio->gctl_sectorsize) != 0) { 385171568Sscottl G_GATE_DEBUG(1, "Invalid media size."); 386185289Sscottl return (EINVAL); 387171568Sscottl } 388185289Sscottl if ((ggio->gctl_flags & G_GATE_FLAG_READONLY) != 0 && 389171568Sscottl (ggio->gctl_flags & G_GATE_FLAG_WRITEONLY) != 0) { 390171568Sscottl G_GATE_DEBUG(1, "Invalid flags."); 391171568Sscottl return (EINVAL); 392185289Sscottl } 393171568Sscottl if (ggio->gctl_unit < -1) { 394171568Sscottl G_GATE_DEBUG(1, "Invalid unit number."); 395171568Sscottl return (EINVAL); 396171568Sscottl } 397185289Sscottl 398171568Sscottl sc = malloc(sizeof(*sc), M_GATE, M_WAITOK | M_ZERO); 399171568Sscottl sc->sc_flags = (ggio->gctl_flags & G_GATE_USERFLAGS); 400171568Sscottl strlcpy(sc->sc_info, ggio->gctl_info, sizeof(sc->sc_info)); 401171568Sscottl sc->sc_seq = 0; 402171568Sscottl bioq_init(&sc->sc_inqueue); 403171568Sscottl bioq_init(&sc->sc_outqueue); 404171568Sscottl mtx_init(&sc->sc_queue_mtx, "gg:queue", NULL, MTX_DEF); 405171568Sscottl sc->sc_queue_count = 0; 406171568Sscottl sc->sc_queue_size = ggio->gctl_maxcount; 407171568Sscottl if (sc->sc_queue_size > G_GATE_MAX_QUEUE_SIZE) 408171568Sscottl sc->sc_queue_size = G_GATE_MAX_QUEUE_SIZE; 409171568Sscottl sc->sc_timeout = ggio->gctl_timeout; 410171568Sscottl callout_init(&sc->sc_callout, CALLOUT_MPSAFE); 411171568Sscottl mtx_lock(&g_gate_list_mtx); 412211095Sdes ggio->gctl_unit = g_gate_getunit(ggio->gctl_unit); 413171568Sscottl if (ggio->gctl_unit == -1) { 414171568Sscottl mtx_unlock(&g_gate_list_mtx); 415171568Sscottl mtx_destroy(&sc->sc_queue_mtx); 416171568Sscottl free(sc, M_GATE); 417171568Sscottl return (EBUSY); 418171568Sscottl } 419171568Sscottl sc->sc_unit = ggio->gctl_unit; 420171568Sscottl LIST_INSERT_HEAD(&g_gate_list, sc, sc_next); 421171568Sscottl mtx_unlock(&g_gate_list_mtx); 422171568Sscottl 423171568Sscottl g_topology_lock(); 424211095Sdes gp = g_new_geomf(&g_gate_class, "%s%d", G_GATE_PROVIDER_NAME, 425171568Sscottl sc->sc_unit); 426171568Sscottl gp->start = g_gate_start; 427171568Sscottl gp->access = g_gate_access; 428171568Sscottl gp->dumpconf = g_gate_dumpconf; 429171568Sscottl gp->softc = sc; 430171568Sscottl pp = g_new_providerf(gp, "%s%d", G_GATE_PROVIDER_NAME, sc->sc_unit); 431171568Sscottl pp->mediasize = ggio->gctl_mediasize; 432171568Sscottl pp->sectorsize = ggio->gctl_sectorsize; 433171568Sscottl sc->sc_provider = pp; 434171568Sscottl g_error_provider(pp, 0); 435171568Sscottl g_topology_unlock(); 436171568Sscottl 437171568Sscottl if (sc->sc_timeout > 0) { 438171568Sscottl callout_reset(&sc->sc_callout, sc->sc_timeout * hz, 439171568Sscottl g_gate_guard, sc); 440171568Sscottl } 441171568Sscottl return (0); 442171568Sscottl} 443171568Sscottl 444171568Sscottl#define G_GATE_CHECK_VERSION(ggio) do { \ 445171568Sscottl if ((ggio)->gctl_version != G_GATE_VERSION) { \ 446171568Sscottl printf("Version mismatch %d != %d.\n", \ 447171568Sscottl ggio->gctl_version, G_GATE_VERSION); \ 448171568Sscottl return (EINVAL); \ 449171568Sscottl } \ 450171568Sscottl} while (0) 451171568Sscottlstatic int 452171568Sscottlg_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) 453171568Sscottl{ 454211095Sdes struct g_gate_softc *sc; 455171568Sscottl struct bio *bp; 456171568Sscottl int error = 0; 457171568Sscottl 458171568Sscottl G_GATE_DEBUG(4, "ioctl(%s, %lx, %p, %x, %p)", devtoname(dev), cmd, addr, 459171568Sscottl flags, td); 460171568Sscottl 461171568Sscottl switch (cmd) { 462171568Sscottl case G_GATE_CMD_CREATE: 463171568Sscottl { 464171568Sscottl struct g_gate_ctl_create *ggio = (void *)addr; 465211095Sdes 466185289Sscottl G_GATE_CHECK_VERSION(ggio); 467185289Sscottl error = g_gate_create(ggio); 468185289Sscottl /* 469185289Sscottl * Reset TDP_GEOM flag. 470211095Sdes * There are pending events for sure, because we just created 471185289Sscottl * new provider and other classes want to taste it, but we 472185289Sscottl * cannot answer on I/O requests until we're here. 473185289Sscottl */ 474185289Sscottl td->td_pflags &= ~TDP_GEOM; 475185289Sscottl return (error); 476185289Sscottl } 477171568Sscottl case G_GATE_CMD_DESTROY: 478171568Sscottl { 479171568Sscottl struct g_gate_ctl_destroy *ggio = (void *)addr; 480171568Sscottl 481234233Sjpaetzel G_GATE_CHECK_VERSION(ggio); 482234233Sjpaetzel sc = g_gate_hold(ggio->gctl_unit); 483171568Sscottl if (sc == NULL) 484171568Sscottl return (ENXIO); 485171568Sscottl g_topology_lock(); 486171568Sscottl mtx_lock(&g_gate_list_mtx); 487171568Sscottl error = g_gate_destroy(sc, ggio->gctl_force); 488211095Sdes if (error == 0) 489211095Sdes g_gate_wither(sc); 490171568Sscottl g_topology_unlock(); 491171568Sscottl g_gate_release(sc); 492234233Sjpaetzel return (error); 493234233Sjpaetzel } 494234233Sjpaetzel case G_GATE_CMD_CANCEL: 495171568Sscottl { 496171568Sscottl struct g_gate_ctl_cancel *ggio = (void *)addr; 497171568Sscottl struct bio *tbp, *lbp; 498171568Sscottl 499171568Sscottl G_GATE_CHECK_VERSION(ggio); 500171568Sscottl sc = g_gate_hold(ggio->gctl_unit); 501171568Sscottl if (sc == NULL) 502171568Sscottl return (ENXIO); 503171568Sscottl lbp = NULL; 504171568Sscottl mtx_lock(&sc->sc_queue_mtx); 505171568Sscottl TAILQ_FOREACH_SAFE(bp, &sc->sc_outqueue.queue, bio_queue, tbp) { 506171568Sscottl if (ggio->gctl_seq == 0 || 507171568Sscottl ggio->gctl_seq == (uintptr_t)bp->bio_driver1) { 508171568Sscottl G_GATE_LOGREQ(1, bp, "Request canceled."); 509171568Sscottl bioq_remove(&sc->sc_outqueue, bp); 510171568Sscottl /* 511171568Sscottl * Be sure to put requests back onto incoming 512171568Sscottl * queue in the proper order. 513171568Sscottl */ 514171568Sscottl if (lbp == NULL) 515171568Sscottl bioq_insert_head(&sc->sc_inqueue, bp); 516171568Sscottl else { 517171568Sscottl TAILQ_INSERT_AFTER(&sc->sc_inqueue.queue, 518171568Sscottl lbp, bp, bio_queue); 519171568Sscottl } 520171568Sscottl lbp = bp; 521171568Sscottl /* 522171568Sscottl * If only one request was canceled, leave now. 523171568Sscottl */ 524171568Sscottl if (ggio->gctl_seq != 0) 525171568Sscottl break; 526171568Sscottl } 527171568Sscottl } 528171568Sscottl mtx_unlock(&sc->sc_queue_mtx); 529171568Sscottl g_gate_release(sc); 530211095Sdes return (error); 531211095Sdes } 532171568Sscottl case G_GATE_CMD_START: 533171568Sscottl { 534171568Sscottl struct g_gate_ctl_io *ggio = (void *)addr; 535171568Sscottl 536171568Sscottl G_GATE_CHECK_VERSION(ggio); 537171568Sscottl sc = g_gate_find(ggio->gctl_unit); 538171568Sscottl if (sc == NULL) 539171568Sscottl return (ENXIO); 540171568Sscottl for (;;) { 541171568Sscottl mtx_lock(&sc->sc_queue_mtx); 542171568Sscottl bp = bioq_first(&sc->sc_inqueue); 543171568Sscottl if (bp != NULL) 544171568Sscottl break; 545171568Sscottl if (msleep(sc, &sc->sc_queue_mtx, 546171568Sscottl PPAUSE | PDROP | PCATCH, "ggwait", 0) != 0) { 547171568Sscottl ggio->gctl_error = ECANCELED; 548171568Sscottl return (0); 549171568Sscottl } 550171568Sscottl if ((sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) { 551171568Sscottl ggio->gctl_error = ECANCELED; 552171568Sscottl return (0); 553171568Sscottl } 554171568Sscottl } 555171568Sscottl ggio->gctl_cmd = bp->bio_cmd; 556171568Sscottl if ((bp->bio_cmd == BIO_DELETE || bp->bio_cmd == BIO_WRITE) && 557171568Sscottl bp->bio_length > ggio->gctl_length) { 558171568Sscottl mtx_unlock(&sc->sc_queue_mtx); 559171568Sscottl ggio->gctl_length = bp->bio_length; 560171568Sscottl ggio->gctl_error = ENOMEM; 561171568Sscottl return (0); 562171568Sscottl } 563171568Sscottl bioq_remove(&sc->sc_inqueue, bp); 564185289Sscottl bioq_insert_tail(&sc->sc_outqueue, bp); 565171568Sscottl mtx_unlock(&sc->sc_queue_mtx); 566171568Sscottl 567171568Sscottl ggio->gctl_seq = (uintptr_t)bp->bio_driver1; 568185289Sscottl ggio->gctl_offset = bp->bio_offset; 569185289Sscottl ggio->gctl_length = bp->bio_length; 570185289Sscottl 571171568Sscottl switch (bp->bio_cmd) { 572211095Sdes case BIO_READ: 573185289Sscottl break; 574185289Sscottl case BIO_DELETE: 575211095Sdes case BIO_WRITE: 576171568Sscottl error = copyout(bp->bio_data, ggio->gctl_data, 577211095Sdes bp->bio_length); 578211095Sdes if (error != 0) { 579211095Sdes mtx_lock(&sc->sc_queue_mtx); 580211095Sdes bioq_remove(&sc->sc_outqueue, bp); 581171568Sscottl bioq_insert_head(&sc->sc_inqueue, bp); 582171568Sscottl mtx_unlock(&sc->sc_queue_mtx); 583171568Sscottl return (error); 584171568Sscottl } 585171568Sscottl break; 586171568Sscottl } 587171568Sscottl return (0); 588171568Sscottl } 589171568Sscottl case G_GATE_CMD_DONE: 590171568Sscottl { 591211095Sdes struct g_gate_ctl_io *ggio = (void *)addr; 592171568Sscottl 593171568Sscottl G_GATE_CHECK_VERSION(ggio); 594171568Sscottl sc = g_gate_find(ggio->gctl_unit); 595171568Sscottl if (sc == NULL) 596171568Sscottl return (ENOENT); 597171568Sscottl mtx_lock(&sc->sc_queue_mtx); 598171568Sscottl TAILQ_FOREACH(bp, &sc->sc_outqueue.queue, bio_queue) { 599171568Sscottl if (ggio->gctl_seq == (uintptr_t)bp->bio_driver1) 600171568Sscottl break; 601171568Sscottl } 602171568Sscottl if (bp != NULL) { 603171568Sscottl bioq_remove(&sc->sc_outqueue, bp); 604171568Sscottl sc->sc_queue_count--; 605171568Sscottl } 606171568Sscottl mtx_unlock(&sc->sc_queue_mtx); 607171568Sscottl if (bp == NULL) { 608171568Sscottl /* 609 * Request was probably canceled. 610 */ 611 return (0); 612 } 613 if (ggio->gctl_error == EAGAIN) { 614 bp->bio_error = 0; 615 G_GATE_LOGREQ(1, bp, "Request desisted."); 616 mtx_lock(&sc->sc_queue_mtx); 617 sc->sc_queue_count++; 618 bioq_insert_head(&sc->sc_inqueue, bp); 619 wakeup(sc); 620 mtx_unlock(&sc->sc_queue_mtx); 621 } else { 622 bp->bio_error = ggio->gctl_error; 623 if (bp->bio_error == 0) { 624 bp->bio_completed = bp->bio_length; 625 switch (bp->bio_cmd) { 626 case BIO_READ: 627 error = copyin(ggio->gctl_data, 628 bp->bio_data, bp->bio_length); 629 if (error != 0) 630 bp->bio_error = error; 631 break; 632 case BIO_DELETE: 633 case BIO_WRITE: 634 break; 635 } 636 } 637 G_GATE_LOGREQ(2, bp, "Request done."); 638 g_io_deliver(bp, bp->bio_error); 639 } 640 return (error); 641 } 642 } 643 return (ENOIOCTL); 644} 645 646static void 647g_gate_device(void) 648{ 649 650 status_dev = make_dev(&g_gate_cdevsw, 0x0, UID_ROOT, GID_WHEEL, 0600, 651 G_GATE_CTL_NAME); 652} 653 654static int 655g_gate_modevent(module_t mod, int type, void *data) 656{ 657 int error = 0; 658 659 switch (type) { 660 case MOD_LOAD: 661 mtx_init(&g_gate_list_mtx, "gg_list_lock", NULL, MTX_DEF); 662 g_gate_device(); 663 break; 664 case MOD_UNLOAD: 665 mtx_lock(&g_gate_list_mtx); 666 if (!LIST_EMPTY(&g_gate_list)) { 667 mtx_unlock(&g_gate_list_mtx); 668 error = EBUSY; 669 break; 670 } 671 mtx_unlock(&g_gate_list_mtx); 672 mtx_destroy(&g_gate_list_mtx); 673 if (status_dev != 0) 674 destroy_dev(status_dev); 675 break; 676 default: 677 return (EOPNOTSUPP); 678 break; 679 } 680 681 return (error); 682} 683static moduledata_t g_gate_module = { 684 G_GATE_MOD_NAME, 685 g_gate_modevent, 686 NULL 687}; 688DECLARE_MODULE(geom_gate, g_gate_module, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 689DECLARE_GEOM_CLASS(g_gate_class, g_gate); 690