diff options
Diffstat (limited to 'sys/geom/gate/g_gate.c')
-rw-r--r-- | sys/geom/gate/g_gate.c | 190 |
1 files changed, 119 insertions, 71 deletions
diff --git a/sys/geom/gate/g_gate.c b/sys/geom/gate/g_gate.c index 26df0f4..952e856 100644 --- a/sys/geom/gate/g_gate.c +++ b/sys/geom/gate/g_gate.c @@ -1,7 +1,11 @@ /*- * Copyright (c) 2004-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org> + * Copyright (c) 2009-2010 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Pawel Jakub Dawidek + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -53,9 +57,14 @@ static MALLOC_DEFINE(M_GATE, "gg_data", "GEOM Gate Data"); SYSCTL_DECL(_kern_geom); SYSCTL_NODE(_kern_geom, OID_AUTO, gate, CTLFLAG_RW, 0, "GEOM_GATE stuff"); -static u_int g_gate_debug = 0; -SYSCTL_UINT(_kern_geom_gate, OID_AUTO, debug, CTLFLAG_RW, &g_gate_debug, 0, +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, "Debug level"); +static u_int g_gate_maxunits = 256; +TUNABLE_INT("kern.geom.gate.maxunits", &g_gate_maxunits); +SYSCTL_UINT(_kern_geom_gate, OID_AUTO, maxunits, CTLFLAG_RDTUN, + &g_gate_maxunits, 0, "Maximum number of ggate devices"); struct g_class g_gate_class = { .name = G_GATE_CLASS_NAME, @@ -71,10 +80,9 @@ static struct cdevsw g_gate_cdevsw = { }; -static LIST_HEAD(, g_gate_softc) g_gate_list = - LIST_HEAD_INITIALIZER(g_gate_list); -static struct mtx g_gate_list_mtx; - +static struct g_gate_softc **g_gate_units; +static u_int g_gate_nunits; +static struct mtx g_gate_units_lock; static int g_gate_destroy(struct g_gate_softc *sc, boolean_t force) @@ -84,13 +92,13 @@ g_gate_destroy(struct g_gate_softc *sc, boolean_t force) struct bio *bp; g_topology_assert(); - mtx_assert(&g_gate_list_mtx, MA_OWNED); + mtx_assert(&g_gate_units_lock, MA_OWNED); pp = sc->sc_provider; if (!force && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { - mtx_unlock(&g_gate_list_mtx); + mtx_unlock(&g_gate_units_lock); return (EBUSY); } - mtx_unlock(&g_gate_list_mtx); + mtx_unlock(&g_gate_units_lock); mtx_lock(&sc->sc_queue_mtx); if ((sc->sc_flags & G_GATE_FLAG_DESTROY) == 0) sc->sc_flags |= G_GATE_FLAG_DESTROY; @@ -125,14 +133,15 @@ g_gate_destroy(struct g_gate_softc *sc, boolean_t force) } mtx_unlock(&sc->sc_queue_mtx); g_topology_unlock(); - mtx_lock(&g_gate_list_mtx); + mtx_lock(&g_gate_units_lock); /* One reference is ours. */ sc->sc_ref--; - while (sc->sc_ref > 0) { - msleep(&sc->sc_ref, &g_gate_list_mtx, 0, "gg:destroy", 0); - } - LIST_REMOVE(sc, sc_next); - mtx_unlock(&g_gate_list_mtx); + while (sc->sc_ref > 0) + msleep(&sc->sc_ref, &g_gate_units_lock, 0, "gg:destroy", 0); + g_gate_units[sc->sc_unit] = NULL; + KASSERT(g_gate_nunits > 0, ("negative g_gate_nunits?")); + g_gate_nunits--; + mtx_unlock(&g_gate_units_lock); mtx_destroy(&sc->sc_queue_mtx); g_topology_lock(); G_GATE_DEBUG(0, "Device %s destroyed.", gp->name); @@ -196,7 +205,7 @@ g_gate_start(struct bio *bp) if (sc->sc_queue_count > sc->sc_queue_size) { mtx_unlock(&sc->sc_queue_mtx); G_GATE_LOGREQ(1, bp, "Queue full, request canceled."); - g_io_deliver(bp, EIO); + g_io_deliver(bp, ENOMEM); return; } @@ -211,18 +220,29 @@ g_gate_start(struct bio *bp) } static struct g_gate_softc * -g_gate_hold(u_int unit) +g_gate_hold(u_int unit, const char *name) { - struct g_gate_softc *sc; - - mtx_lock(&g_gate_list_mtx); - LIST_FOREACH(sc, &g_gate_list, sc_next) { - if (sc->sc_unit == unit) + struct g_gate_softc *sc = NULL; + + mtx_lock(&g_gate_units_lock); + if (unit >= 0 && unit < g_gate_maxunits) + sc = g_gate_units[unit]; + else if (unit == G_GATE_NAME_GIVEN) { + KASSERT(name != NULL, ("name is NULL")); + for (unit = 0; unit < g_gate_maxunits; unit++) { + if (g_gate_units[unit] == NULL) + continue; + if (strcmp(name, + g_gate_units[unit]->sc_provider->name) != 0) { + continue; + } + sc = g_gate_units[unit]; break; + } } if (sc != NULL) sc->sc_ref++; - mtx_unlock(&g_gate_list_mtx); + mtx_unlock(&g_gate_units_lock); return (sc); } @@ -231,40 +251,34 @@ g_gate_release(struct g_gate_softc *sc) { g_topology_assert_not(); - mtx_lock(&g_gate_list_mtx); + mtx_lock(&g_gate_units_lock); sc->sc_ref--; KASSERT(sc->sc_ref >= 0, ("Negative sc_ref for %s.", sc->sc_name)); - if (sc->sc_ref == 0 && (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) { + if (sc->sc_ref == 0 && (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) wakeup(&sc->sc_ref); - mtx_unlock(&g_gate_list_mtx); - } else { - mtx_unlock(&g_gate_list_mtx); - } + mtx_unlock(&g_gate_units_lock); } static int -g_gate_getunit(int unit) +g_gate_getunit(int unit, int *errorp) { - struct g_gate_softc *sc; - mtx_assert(&g_gate_list_mtx, MA_OWNED); + mtx_assert(&g_gate_units_lock, MA_OWNED); if (unit >= 0) { - LIST_FOREACH(sc, &g_gate_list, sc_next) { - if (sc->sc_unit == unit) - return (-1); - } + if (unit >= g_gate_maxunits) + *errorp = EINVAL; + else if (g_gate_units[unit] == NULL) + return (unit); + else + *errorp = EEXIST; } else { - unit = 0; -once_again: - LIST_FOREACH(sc, &g_gate_list, sc_next) { - if (sc->sc_unit == unit) { - if (++unit > 666) - return (-1); - goto once_again; - } + for (unit = 0; unit < g_gate_maxunits; unit++) { + if (g_gate_units[unit] == NULL) + return (unit); } + *errorp = ENFILE; } - return (unit); + return (-1); } static void @@ -276,7 +290,7 @@ g_gate_guard(void *arg) sc = arg; binuptime(&curtime); - g_gate_hold(sc->sc_unit); + g_gate_hold(sc->sc_unit, NULL); mtx_lock(&sc->sc_queue_mtx); TAILQ_FOREACH_SAFE(bp, &sc->sc_inqueue.queue, bio_queue, bp2) { if (curtime.sec - bp->bio_t0.sec < 5) @@ -311,7 +325,7 @@ g_gate_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, sc = gp->softc; if (sc == NULL || pp != NULL || cp != NULL) return; - g_gate_hold(sc->sc_unit); + g_gate_hold(sc->sc_unit, NULL); if ((sc->sc_flags & G_GATE_FLAG_READONLY) != 0) { sbuf_printf(sb, "%s<access>%s</access>\n", indent, "read-only"); } else if ((sc->sc_flags & G_GATE_FLAG_WRITEONLY) != 0) { @@ -328,6 +342,7 @@ g_gate_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, sbuf_printf(sb, "%s<queue_size>%u</queue_size>\n", indent, sc->sc_queue_size); sbuf_printf(sb, "%s<ref>%u</ref>\n", indent, sc->sc_ref); + sbuf_printf(sb, "%s<unit>%d</unit>\n", indent, sc->sc_unit); g_topology_unlock(); g_gate_release(sc); g_topology_lock(); @@ -339,6 +354,8 @@ g_gate_create(struct g_gate_ctl_create *ggio) struct g_gate_softc *sc; struct g_geom *gp; struct g_provider *pp; + char name[NAME_MAX]; + int error = 0, unit; if (ggio->gctl_mediasize == 0) { G_GATE_DEBUG(1, "Invalid media size."); @@ -357,15 +374,22 @@ g_gate_create(struct g_gate_ctl_create *ggio) G_GATE_DEBUG(1, "Invalid flags."); return (EINVAL); } - if (ggio->gctl_unit < -1) { + if (ggio->gctl_unit != G_GATE_UNIT_AUTO && + ggio->gctl_unit != G_GATE_NAME_GIVEN && + ggio->gctl_unit < 0) { G_GATE_DEBUG(1, "Invalid unit number."); return (EINVAL); } + if (ggio->gctl_unit == G_GATE_NAME_GIVEN && + ggio->gctl_name[0] == '\0') { + G_GATE_DEBUG(1, "No device name."); + 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)); - sc->sc_seq = 0; + sc->sc_seq = 1; bioq_init(&sc->sc_inqueue); bioq_init(&sc->sc_outqueue); mtx_init(&sc->sc_queue_mtx, "gg:queue", NULL, MTX_DEF); @@ -375,26 +399,44 @@ 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); - mtx_lock(&g_gate_list_mtx); - ggio->gctl_unit = g_gate_getunit(ggio->gctl_unit); - if (ggio->gctl_unit == -1) { - mtx_unlock(&g_gate_list_mtx); + 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); mtx_destroy(&sc->sc_queue_mtx); free(sc, M_GATE); - return (EBUSY); + return (error); + } + if (ggio->gctl_unit == G_GATE_NAME_GIVEN) + snprintf(name, sizeof(name), "%s", ggio->gctl_name); + else { + snprintf(name, sizeof(name), "%s%d", G_GATE_PROVIDER_NAME, + sc->sc_unit); } - sc->sc_unit = ggio->gctl_unit; - LIST_INSERT_HEAD(&g_gate_list, sc, sc_next); - mtx_unlock(&g_gate_list_mtx); + /* Check for name collision. */ + for (unit = 0; unit < g_gate_maxunits; unit++) { + if (g_gate_units[unit] == NULL) + continue; + if (strcmp(name, g_gate_units[unit]->sc_provider->name) != 0) + continue; + mtx_unlock(&g_gate_units_lock); + mtx_destroy(&sc->sc_queue_mtx); + free(sc, M_GATE); + return (EEXIST); + } + g_gate_units[sc->sc_unit] = sc; + g_gate_nunits++; + mtx_unlock(&g_gate_units_lock); + + ggio->gctl_unit = sc->sc_unit; g_topology_lock(); - gp = g_new_geomf(&g_gate_class, "%s%d", G_GATE_PROVIDER_NAME, - sc->sc_unit); + 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%d", G_GATE_PROVIDER_NAME, sc->sc_unit); + pp = g_new_providerf(gp, "%s", name); pp->mediasize = ggio->gctl_mediasize; pp->sectorsize = ggio->gctl_sectorsize; sc->sc_provider = pp; @@ -446,11 +488,11 @@ g_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct threa struct g_gate_ctl_destroy *ggio = (void *)addr; G_GATE_CHECK_VERSION(ggio); - sc = g_gate_hold(ggio->gctl_unit); + sc = g_gate_hold(ggio->gctl_unit, ggio->gctl_name); if (sc == NULL) return (ENXIO); g_topology_lock(); - mtx_lock(&g_gate_list_mtx); + mtx_lock(&g_gate_units_lock); error = g_gate_destroy(sc, ggio->gctl_force); g_topology_unlock(); if (error != 0) @@ -463,7 +505,7 @@ g_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct threa struct bio *tbp, *lbp; G_GATE_CHECK_VERSION(ggio); - sc = g_gate_hold(ggio->gctl_unit); + sc = g_gate_hold(ggio->gctl_unit, ggio->gctl_name); if (sc == NULL) return (ENXIO); lbp = NULL; @@ -491,6 +533,8 @@ g_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct threa break; } } + if (ggio->gctl_unit == G_GATE_NAME_GIVEN) + ggio->gctl_unit = sc->sc_unit; mtx_unlock(&sc->sc_queue_mtx); g_gate_release(sc); return (error); @@ -500,7 +544,7 @@ g_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct threa struct g_gate_ctl_io *ggio = (void *)addr; G_GATE_CHECK_VERSION(ggio); - sc = g_gate_hold(ggio->gctl_unit); + sc = g_gate_hold(ggio->gctl_unit, NULL); if (sc == NULL) return (ENXIO); error = 0; @@ -561,7 +605,7 @@ start_end: struct g_gate_ctl_io *ggio = (void *)addr; G_GATE_CHECK_VERSION(ggio); - sc = g_gate_hold(ggio->gctl_unit); + sc = g_gate_hold(ggio->gctl_unit, NULL); if (sc == NULL) return (ENOENT); error = 0; @@ -631,20 +675,24 @@ g_gate_modevent(module_t mod, int type, void *data) switch (type) { case MOD_LOAD: - mtx_init(&g_gate_list_mtx, "gg_list_lock", NULL, MTX_DEF); + mtx_init(&g_gate_units_lock, "gg_units_lock", NULL, MTX_DEF); + g_gate_units = malloc(g_gate_maxunits * sizeof(g_gate_units[0]), + M_GATE, M_WAITOK | M_ZERO); + g_gate_nunits = 0; g_gate_device(); break; case MOD_UNLOAD: - mtx_lock(&g_gate_list_mtx); - if (!LIST_EMPTY(&g_gate_list)) { - mtx_unlock(&g_gate_list_mtx); + mtx_lock(&g_gate_units_lock); + if (g_gate_nunits > 0) { + mtx_unlock(&g_gate_units_lock); error = EBUSY; break; } - mtx_unlock(&g_gate_list_mtx); - mtx_destroy(&g_gate_list_mtx); + mtx_unlock(&g_gate_units_lock); + mtx_destroy(&g_gate_units_lock); if (status_dev != 0) destroy_dev(status_dev); + free(g_gate_units, M_GATE); break; default: return (EOPNOTSUPP); |