diff options
author | pjd <pjd@FreeBSD.org> | 2012-07-04 20:16:28 +0000 |
---|---|---|
committer | pjd <pjd@FreeBSD.org> | 2012-07-04 20:16:28 +0000 |
commit | 5ef9eb30da6a8c09fc9ae0ae312ccd23e820e006 (patch) | |
tree | abea9b92119e3f02fec250110374237f2f6b3c2a /sys | |
parent | 8cbf06904c76a2c9ed6b6e796cc669bb7b92e090 (diff) | |
download | FreeBSD-src-5ef9eb30da6a8c09fc9ae0ae312ccd23e820e006.zip FreeBSD-src-5ef9eb30da6a8c09fc9ae0ae312ccd23e820e006.tar.gz |
Extend GEOM Gate class to handle read I/O requests directly within the kernel.
This will allow HAST to read directly from the local component without
even communicating userland daemon.
Sponsored by: Panzura, http://www.panzura.com
MFC after: 1 month
Diffstat (limited to 'sys')
-rw-r--r-- | sys/geom/gate/g_gate.c | 320 | ||||
-rw-r--r-- | sys/geom/gate/g_gate.h | 29 |
2 files changed, 314 insertions, 35 deletions
diff --git a/sys/geom/gate/g_gate.c b/sys/geom/gate/g_gate.c index 52df914..b3c09df 100644 --- a/sys/geom/gate/g_gate.c +++ b/sys/geom/gate/g_gate.c @@ -60,7 +60,7 @@ static MALLOC_DEFINE(M_GATE, "gg_data", "GEOM Gate Data"); SYSCTL_DECL(_kern_geom); static SYSCTL_NODE(_kern_geom, OID_AUTO, gate, CTLFLAG_RW, 0, - "GEOM_GATE stuff"); + "GEOM_GATE configuration"); static int g_gate_debug = 0; TUNABLE_INT("kern.geom.gate.debug", &g_gate_debug); SYSCTL_INT(_kern_geom_gate, OID_AUTO, debug, CTLFLAG_RW, &g_gate_debug, 0, @@ -92,6 +92,7 @@ static int g_gate_destroy(struct g_gate_softc *sc, boolean_t force) { struct g_provider *pp; + struct g_consumer *cp; struct g_geom *gp; struct bio *bp; @@ -138,6 +139,12 @@ g_gate_destroy(struct g_gate_softc *sc, boolean_t force) mtx_unlock(&g_gate_units_lock); mtx_destroy(&sc->sc_queue_mtx); g_topology_lock(); + if ((cp = sc->sc_readcons) != NULL) { + sc->sc_readcons = NULL; + (void)g_access(cp, -1, 0, 0); + g_detach(cp); + g_destroy_consumer(cp); + } G_GATE_DEBUG(1, "Device %s destroyed.", gp->name); gp->softc = NULL; g_wither_geom(gp, ENXIO); @@ -167,7 +174,7 @@ g_gate_access(struct g_provider *pp, int dr, int dw, int de) } static void -g_gate_start(struct bio *bp) +g_gate_queue_io(struct bio *bp) { struct g_gate_softc *sc; @@ -176,27 +183,9 @@ g_gate_start(struct bio *bp) g_io_deliver(bp, ENXIO); return; } - G_GATE_LOGREQ(2, bp, "Request received."); - switch (bp->bio_cmd) { - case BIO_READ: - break; - case BIO_DELETE: - case BIO_WRITE: - case BIO_FLUSH: - /* XXX: Hack to allow read-only mounts. */ - if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0) { - g_io_deliver(bp, EPERM); - return; - } - break; - case BIO_GETATTR: - default: - G_GATE_LOGREQ(2, bp, "Ignoring request."); - g_io_deliver(bp, EOPNOTSUPP); - return; - } mtx_lock(&sc->sc_queue_mtx); + if (sc->sc_queue_size > 0 && sc->sc_queue_count > sc->sc_queue_size) { mtx_unlock(&sc->sc_queue_mtx); G_GATE_LOGREQ(1, bp, "Queue full, request canceled."); @@ -214,6 +203,74 @@ g_gate_start(struct bio *bp) mtx_unlock(&sc->sc_queue_mtx); } +static void +g_gate_done(struct bio *cbp) +{ + struct bio *pbp; + + pbp = cbp->bio_parent; + if (cbp->bio_error == 0) { + pbp->bio_completed = cbp->bio_completed; + g_destroy_bio(cbp); + pbp->bio_inbed++; + g_io_deliver(pbp, 0); + } else { + /* If direct read failed, pass it through userland daemon. */ + g_destroy_bio(cbp); + pbp->bio_children--; + g_gate_queue_io(pbp); + } +} + +static void +g_gate_start(struct bio *pbp) +{ + struct g_gate_softc *sc; + + sc = pbp->bio_to->geom->softc; + if (sc == NULL || (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) { + g_io_deliver(pbp, ENXIO); + return; + } + G_GATE_LOGREQ(2, pbp, "Request received."); + switch (pbp->bio_cmd) { + case BIO_READ: + if (sc->sc_readcons != NULL) { + struct bio *cbp; + + cbp = g_clone_bio(pbp); + if (cbp == NULL) { + g_io_deliver(pbp, ENOMEM); + return; + } + cbp->bio_done = g_gate_done; + cbp->bio_offset = pbp->bio_offset + sc->sc_readoffset; + cbp->bio_data = pbp->bio_data; + cbp->bio_length = pbp->bio_length; + cbp->bio_to = sc->sc_readcons->provider; + g_io_request(cbp, sc->sc_readcons); + return; + } + break; + case BIO_DELETE: + case BIO_WRITE: + case BIO_FLUSH: + /* XXX: Hack to allow read-only mounts. */ + if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0) { + g_io_deliver(pbp, EPERM); + return; + } + break; + case BIO_GETATTR: + default: + G_GATE_LOGREQ(2, pbp, "Ignoring request."); + g_io_deliver(pbp, EOPNOTSUPP); + return; + } + + g_gate_queue_io(pbp); +} + static struct g_gate_softc * g_gate_hold(int unit, const char *name) { @@ -312,6 +369,27 @@ g_gate_guard(void *arg) } static void +g_gate_orphan(struct g_consumer *cp) +{ + struct g_gate_softc *sc; + struct g_geom *gp; + + g_topology_assert(); + gp = cp->geom; + sc = gp->softc; + if (sc == NULL) + return; + KASSERT(cp == sc->sc_readcons, ("cp=%p sc_readcons=%p", cp, + sc->sc_readcons)); + sc->sc_readcons = NULL; + G_GATE_DEBUG(1, "Destroying read consumer on provider %s orphan.", + cp->provider->name); + (void)g_access(cp, -1, 0, 0); + g_detach(cp); + g_destroy_consumer(cp); +} + +static void g_gate_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp) { @@ -330,6 +408,12 @@ g_gate_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, sbuf_printf(sb, "%s<access>%s</access>\n", indent, "read-write"); } + if (sc->sc_readcons != NULL) { + sbuf_printf(sb, "%s<read_offset>%jd</read_offset>\n", + indent, (intmax_t)sc->sc_readoffset); + sbuf_printf(sb, "%s<read_provider>%s</read_provider>\n", + indent, sc->sc_readcons->provider->name); + } sbuf_printf(sb, "%s<timeout>%u</timeout>\n", indent, sc->sc_timeout); sbuf_printf(sb, "%s<info>%s</info>\n", indent, sc->sc_info); sbuf_printf(sb, "%s<queue_count>%u</queue_count>\n", indent, @@ -348,15 +432,20 @@ g_gate_create(struct g_gate_ctl_create *ggio) { struct g_gate_softc *sc; struct g_geom *gp; - struct g_provider *pp; + struct g_provider *pp, *ropp; + struct g_consumer *cp; char name[NAME_MAX]; int error = 0, unit; - if (ggio->gctl_mediasize == 0) { + if (ggio->gctl_mediasize <= 0) { G_GATE_DEBUG(1, "Invalid media size."); return (EINVAL); } - if (ggio->gctl_sectorsize > 0 && !powerof2(ggio->gctl_sectorsize)) { + if (ggio->gctl_sectorsize <= 0) { + G_GATE_DEBUG(1, "Invalid sector size."); + return (EINVAL); + } + if (!powerof2(ggio->gctl_sectorsize)) { G_GATE_DEBUG(1, "Invalid sector size."); return (EINVAL); } @@ -381,6 +470,31 @@ g_gate_create(struct g_gate_ctl_create *ggio) return (EINVAL); } + g_topology_lock(); + + if (ggio->gctl_readprov[0] == '\0') { + ropp = NULL; + } else { + ropp = g_provider_by_name(ggio->gctl_readprov); + if (ropp == NULL) { + g_topology_unlock(); + G_GATE_DEBUG(1, "Provider %s doesn't exist.", + ggio->gctl_readprov); + return (EINVAL); + } + if ((ggio->gctl_readoffset % ggio->gctl_sectorsize) != 0) { + g_topology_unlock(); + G_GATE_DEBUG(1, "Invalid read offset."); + return (EINVAL); + } + if (ggio->gctl_mediasize + ggio->gctl_readoffset > + ropp->mediasize) { + g_topology_unlock(); + G_GATE_DEBUG(1, "Invalid read offset or media size."); + return (EINVAL); + } + } + sc = malloc(sizeof(*sc), M_GATE, M_WAITOK | M_ZERO); sc->sc_flags = (ggio->gctl_flags & G_GATE_USERFLAGS); strlcpy(sc->sc_info, ggio->gctl_info, sizeof(sc->sc_info)); @@ -394,10 +508,50 @@ g_gate_create(struct g_gate_ctl_create *ggio) sc->sc_queue_size = G_GATE_MAX_QUEUE_SIZE; sc->sc_timeout = ggio->gctl_timeout; callout_init(&sc->sc_callout, CALLOUT_MPSAFE); + + gp = g_new_geomf(&g_gate_class, "%s", name); + gp->start = g_gate_start; + gp->access = g_gate_access; + gp->orphan = g_gate_orphan; + gp->dumpconf = g_gate_dumpconf; + gp->softc = sc; + + if (ropp != NULL) { + cp = g_new_consumer(gp); + error = g_attach(cp, ropp); + if (error != 0) { + G_GATE_DEBUG(1, "Unable to attach to %s.", ropp->name); + } else { + error = g_access(cp, 1, 0, 0); + if (error != 0) { + G_GATE_DEBUG(1, "Unable to access %s.", + ropp->name); + g_detach(cp); + } + } + if (error != 0) { + g_destroy_consumer(cp); + g_destroy_geom(gp); + g_topology_unlock(); + mtx_destroy(&sc->sc_queue_mtx); + free(sc, M_GATE); + return (error); + } + sc->sc_readcons = cp; + sc->sc_readoffset = ggio->gctl_readoffset; + } + mtx_lock(&g_gate_units_lock); sc->sc_unit = g_gate_getunit(ggio->gctl_unit, &error); if (sc->sc_unit < 0) { mtx_unlock(&g_gate_units_lock); + if (sc->sc_readcons != NULL) { + (void)g_access(sc->sc_readcons, -1, 0, 0); + g_detach(sc->sc_readcons); + g_destroy_consumer(sc->sc_readcons); + } + g_destroy_geom(gp); + g_topology_unlock(); mtx_destroy(&sc->sc_queue_mtx); free(sc, M_GATE); return (error); @@ -415,6 +569,13 @@ g_gate_create(struct g_gate_ctl_create *ggio) if (strcmp(name, g_gate_units[unit]->sc_name) != 0) continue; mtx_unlock(&g_gate_units_lock); + if (sc->sc_readcons != NULL) { + (void)g_access(sc->sc_readcons, -1, 0, 0); + g_detach(sc->sc_readcons); + g_destroy_consumer(sc->sc_readcons); + } + g_destroy_geom(gp); + g_topology_unlock(); mtx_destroy(&sc->sc_queue_mtx); free(sc, M_GATE); return (EEXIST); @@ -426,17 +587,12 @@ g_gate_create(struct g_gate_ctl_create *ggio) ggio->gctl_unit = sc->sc_unit; - g_topology_lock(); - gp = g_new_geomf(&g_gate_class, "%s", name); - gp->start = g_gate_start; - gp->access = g_gate_access; - gp->dumpconf = g_gate_dumpconf; - gp->softc = sc; pp = g_new_providerf(gp, "%s", name); pp->mediasize = ggio->gctl_mediasize; pp->sectorsize = ggio->gctl_sectorsize; sc->sc_provider = pp; g_error_provider(pp, 0); + g_topology_unlock(); mtx_lock(&g_gate_units_lock); sc->sc_name = sc->sc_provider->name; @@ -450,6 +606,98 @@ g_gate_create(struct g_gate_ctl_create *ggio) return (0); } +static int +g_gate_modify(struct g_gate_softc *sc, struct g_gate_ctl_modify *ggio) +{ + struct g_provider *pp; + struct g_consumer *cp; + int error; + + if ((ggio->gctl_modify & GG_MODIFY_MEDIASIZE) != 0) { + if (ggio->gctl_mediasize <= 0) { + G_GATE_DEBUG(1, "Invalid media size."); + return (EINVAL); + } + pp = sc->sc_provider; + if ((ggio->gctl_mediasize % pp->sectorsize) != 0) { + G_GATE_DEBUG(1, "Invalid media size."); + return (EINVAL); + } + /* TODO */ + return (EOPNOTSUPP); + } + + if ((ggio->gctl_modify & GG_MODIFY_INFO) != 0) + (void)strlcpy(sc->sc_info, ggio->gctl_info, sizeof(sc->sc_info)); + + cp = NULL; + + if ((ggio->gctl_modify & GG_MODIFY_READPROV) != 0) { + g_topology_lock(); + if (sc->sc_readcons != NULL) { + cp = sc->sc_readcons; + sc->sc_readcons = NULL; + (void)g_access(cp, -1, 0, 0); + g_detach(cp); + g_destroy_consumer(cp); + } + if (ggio->gctl_readprov[0] != '\0') { + pp = g_provider_by_name(ggio->gctl_readprov); + if (pp == NULL) { + g_topology_unlock(); + G_GATE_DEBUG(1, "Provider %s doesn't exist.", + ggio->gctl_readprov); + return (EINVAL); + } + cp = g_new_consumer(sc->sc_provider->geom); + error = g_attach(cp, pp); + if (error != 0) { + G_GATE_DEBUG(1, "Unable to attach to %s.", + pp->name); + } else { + error = g_access(cp, 1, 0, 0); + if (error != 0) { + G_GATE_DEBUG(1, "Unable to access %s.", + pp->name); + g_detach(cp); + } + } + if (error != 0) { + g_destroy_consumer(cp); + g_topology_unlock(); + return (error); + } + } + } else { + cp = sc->sc_readcons; + } + + if ((ggio->gctl_modify & GG_MODIFY_READOFFSET) != 0) { + if (cp == NULL) { + G_GATE_DEBUG(1, "No read provider."); + return (EINVAL); + } + pp = sc->sc_provider; + if ((ggio->gctl_readoffset % pp->sectorsize) != 0) { + G_GATE_DEBUG(1, "Invalid read offset."); + return (EINVAL); + } + if (pp->mediasize + ggio->gctl_readoffset > + cp->provider->mediasize) { + G_GATE_DEBUG(1, "Invalid read offset or media size."); + return (EINVAL); + } + sc->sc_readoffset = ggio->gctl_readoffset; + } + + if ((ggio->gctl_modify & GG_MODIFY_READPROV) != 0) { + sc->sc_readcons = cp; + g_topology_unlock(); + } + + return (0); +} + #define G_GATE_CHECK_VERSION(ggio) do { \ if ((ggio)->gctl_version != G_GATE_VERSION) { \ printf("Version mismatch %d != %d.\n", \ @@ -483,6 +731,18 @@ g_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct threa td->td_pflags &= ~TDP_GEOM; return (error); } + case G_GATE_CMD_MODIFY: + { + struct g_gate_ctl_modify *ggio = (void *)addr; + + G_GATE_CHECK_VERSION(ggio); + sc = g_gate_hold(ggio->gctl_unit, NULL); + if (sc == NULL) + return (ENXIO); + error = g_gate_modify(sc, ggio); + g_gate_release(sc); + return (error); + } case G_GATE_CMD_DESTROY: { struct g_gate_ctl_destroy *ggio = (void *)addr; diff --git a/sys/geom/gate/g_gate.h b/sys/geom/gate/g_gate.h index 0a57ce3..b0c563b 100644 --- a/sys/geom/gate/g_gate.h +++ b/sys/geom/gate/g_gate.h @@ -41,7 +41,7 @@ #define G_GATE_MOD_NAME "ggate" #define G_GATE_CTL_NAME "ggctl" -#define G_GATE_VERSION 2 +#define G_GATE_VERSION 3 /* * Maximum number of request that can be stored in @@ -64,10 +64,11 @@ #define G_GATE_NAME_GIVEN (-2) #define G_GATE_CMD_CREATE _IOWR('m', 0, struct g_gate_ctl_create) -#define G_GATE_CMD_DESTROY _IOWR('m', 1, struct g_gate_ctl_destroy) -#define G_GATE_CMD_CANCEL _IOWR('m', 2, struct g_gate_ctl_cancel) -#define G_GATE_CMD_START _IOWR('m', 3, struct g_gate_ctl_io) -#define G_GATE_CMD_DONE _IOWR('m', 4, struct g_gate_ctl_io) +#define G_GATE_CMD_MODIFY _IOWR('m', 1, struct g_gate_ctl_modify) +#define G_GATE_CMD_DESTROY _IOWR('m', 2, struct g_gate_ctl_destroy) +#define G_GATE_CMD_CANCEL _IOWR('m', 3, struct g_gate_ctl_cancel) +#define G_GATE_CMD_START _IOWR('m', 4, struct g_gate_ctl_io) +#define G_GATE_CMD_DONE _IOWR('m', 5, struct g_gate_ctl_io) #define G_GATE_INFOSIZE 2048 @@ -88,6 +89,8 @@ struct g_gate_softc { uint32_t sc_queue_count; /* P: sc_queue_mtx */ uint32_t sc_queue_size; /* P: (read-only) */ u_int sc_timeout; /* P: (read-only) */ + struct g_consumer *sc_readcons; /* P: XXX */ + off_t sc_readoffset; /* P: XXX */ struct callout sc_callout; /* P: (modified only from callout thread) */ @@ -131,9 +134,25 @@ struct g_gate_ctl_create { u_int gctl_timeout; char gctl_name[NAME_MAX]; char gctl_info[G_GATE_INFOSIZE]; + char gctl_readprov[NAME_MAX]; + off_t gctl_readoffset; int gctl_unit; /* in/out */ }; +#define GG_MODIFY_MEDIASIZE 0x01 +#define GG_MODIFY_INFO 0x02 +#define GG_MODIFY_READPROV 0x04 +#define GG_MODIFY_READOFFSET 0x08 +struct g_gate_ctl_modify { + u_int gctl_version; + int gctl_unit; + uint32_t gctl_modify; + off_t gctl_mediasize; + char gctl_info[G_GATE_INFOSIZE]; + char gctl_readprov[NAME_MAX]; + off_t gctl_readoffset; +}; + struct g_gate_ctl_destroy { u_int gctl_version; int gctl_unit; |