Index: etc/mtree/BSD.include.dist =================================================================== --- etc/mtree/BSD.include.dist +++ etc/mtree/BSD.include.dist @@ -220,6 +220,8 @@ .. shsec .. + slow + .. stripe .. virstor Index: include/Makefile =================================================================== --- include/Makefile +++ include/Makefile @@ -52,7 +52,7 @@ fs/procfs fs/smbfs fs/udf fs/unionfs \ geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \ geom/mirror geom/mountver geom/multipath geom/nop \ - geom/raid geom/raid3 geom/shsec geom/stripe geom/virstor \ + geom/raid geom/raid3 geom/shsec geom/slow geom/stripe geom/virstor \ net/altq \ netgraph/atm netgraph/netflow \ netinet/cc \ Index: sbin/geom/class/Makefile =================================================================== --- sbin/geom/class/Makefile +++ sbin/geom/class/Makefile @@ -18,6 +18,7 @@ SUBDIR+=raid3 SUBDIR+=sched SUBDIR+=shsec +SUBDIR+=slow SUBDIR+=stripe SUBDIR+=virstor Index: sbin/geom/class/slow/Makefile =================================================================== --- /dev/null +++ sbin/geom/class/slow/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +PACKAGE=runtime +.PATH: ${.CURDIR}/../../misc + +GEOM_CLASS= slow + +.include Index: sbin/geom/class/slow/Makefile.depend =================================================================== --- /dev/null +++ sbin/geom/class/slow/Makefile.depend @@ -0,0 +1,20 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + lib/libgeom \ + sbin/geom/core \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif Index: sbin/geom/class/slow/geom_slow.c =================================================================== --- /dev/null +++ sbin/geom/class/slow/geom_slow.c @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2016 Allan Jude + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include "core/geom.h" + + +uint32_t lib_version = G_LIB_VERSION; +uint32_t version = G_SLOW_VERSION; + +struct g_command class_commands[] = { + { "create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, + { + { 'i', "iops", "0", G_TYPE_NUMBER }, + { 'r', "rate", "0", G_TYPE_NUMBER }, + G_OPT_SENTINEL + }, + "[-v] [-i iops] [-r rate] dev ..." + }, + { "configure", G_FLAG_VERBOSE, NULL, + { + { 'i', "iops", "0", G_TYPE_NUMBER }, + { 'r', "rate", "0", G_TYPE_NUMBER }, + G_OPT_SENTINEL + }, + "[-v] [-i iops] [-r rate] prov ..." + }, + { "destroy", G_FLAG_VERBOSE, NULL, + { + { 'f', "force", NULL, G_TYPE_BOOL }, + G_OPT_SENTINEL + }, + "[-fv] prov ..." + }, + { "reset", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, + "[-v] prov ..." + }, + G_CMD_SENTINEL +}; Index: sbin/geom/class/slow/gslow.8 =================================================================== --- /dev/null +++ sbin/geom/class/slow/gslow.8 @@ -0,0 +1,161 @@ +.\" Copyright (c) 2016 Allan Jude +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd February 8, 2017 +.Dt GSLOW 8 +.Os +.Sh NAME +.Nm gslow +.Nd "control utility for SLOW GEOM class" +.Sh SYNOPSIS +.Nm +.Cm create +.Op Fl v +.Op Fl i Ar iops +.Op Fl r Ar rate +.Ar dev ... +.Nm +.Cm configure +.Op Fl v +.Op Fl i Ar iops +.Op Fl r Ar rate +.Ar prov ... +.Nm +.Cm destroy +.Op Fl fv +.Ar prov ... +.Nm +.Cm reset +.Op Fl v +.Ar prov ... +.Nm +.Cm list +.Nm +.Cm status +.Nm +.Cm load +.Nm +.Cm unload +.Sh DESCRIPTION +The +.Nm +utility is used for setting up throttling providers on existing ones. +Its main purpose is testing other GEOM classes, as it allows limiting the +throughout or IOPS rate of a provider. +It also gathers statistics on the number of read, write, delete, +getattr, flush, and other requests, and the number of bytes read and written. +.Pp +The first argument to +.Nm +indicates an action to be performed: +.Bl -tag -width ".Cm configure" +.It Cm create +Set up a throttling provider on the given devices. +If the operation succeeds, the new provider should appear with name +.Pa /dev/ Ns Ao Ar dev Ac Ns Pa .slow . +The kernel module +.Pa geom_slow.ko +will be loaded if it is not loaded already. +.It Cm configure +Configure existing transparent provider. +At the moment it is only used for changing failure probability. +.It Cm destroy +Turn off the given transparent providers. +.It Cm reset +Reset statistics for the given transparent providers. +.It Cm list +See +.Xr geom 8 . +.It Cm status +See +.Xr geom 8 . +.It Cm load +See +.Xr geom 8 . +.It Cm unload +See +.Xr geom 8 . +.El +.Pp +Additional options: +.Bl -tag -width ".Fl r Ar rate" +.It Fl i Ar iops +Specifies the IOPS limit. +.It Fl f +Force the removal of the specified provider. +.It Fl r Ar rate +Specifies the throughput limit. +.It Fl v +Be more verbose. +.El +.Sh SYSCTL VARIABLES +The following +.Xr sysctl 8 +variables can be used to control the behavior of the +.Nm SLOW +GEOM class. +The default value is shown next to each variable. +.Bl -tag -width indent +.It Va kern.geom.slow.debug : No 0 +Debug level of the +.Nm SLOW +GEOM class. +This can be set to a number between 0 and 2 inclusive. +If set to 0, minimal debug information is printed. +If set to 1, basic debug information is logged along with the I/O requests +that were returned as errors. +If set to 2, the maximum amount of debug information is printed including +all I/O requests. +.It Va kern.geom.slow.sleep : No 10000 +Number of microseconds to sleep if one of the rate limits is exceeded. +A smaller value will give a more accurate rate limit, at the cost of +greater CPU usage. +.El +.Sh EXIT STATUS +Exit status is 0 on success, and 1 if the command fails. +.Sh EXAMPLES +The following example shows how to create a transparent provider for disk +.Pa /dev/da0 +with a 50 MB/s read/write throttle, and how to destroy it. +.Bd -literal -offset indent +gslow create -v -r 50m da0 +gslow destroy -v da0.slow +.Ed +.Pp +The traffic statistics for the given transparent providers can be obtained +with the +.Cm list +command. +.Sh SEE ALSO +.Xr geom 4 , +.Xr geom 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Fx 12.0 . +.Sh AUTHORS +.An Allan Jude Aq Mt allanjude@FreeBSD.org Index: sys/geom/slow/g_slow.h =================================================================== --- /dev/null +++ sys/geom/slow/g_slow.h @@ -0,0 +1,81 @@ +/*- + * Copyright (c) 2016 Allan Jude + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _G_SLOW_H_ +#define _G_SLOW_H_ + +#define G_SLOW_CLASS_NAME "SLOW" +#define G_SLOW_VERSION 1 +#define G_SLOW_SUFFIX ".slow" + +#ifdef _KERNEL +#define G_SLOW_DEBUG(lvl, ...) do { \ + if (g_slow_debug >= (lvl)) { \ + printf("GEOM_SLOW"); \ + if (g_slow_debug > 0) \ + printf("[%u]", lvl); \ + printf(": "); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + } \ +} while (0) +#define G_SLOW_LOGREQ(bp, ...) G_SLOW_LOGREQLVL(2, bp, __VA_ARGS__) +#define G_SLOW_LOGREQLVL(lvl, bp, ...) do { \ + if (g_slow_debug >= (lvl)) { \ + printf("GEOM_SLOW[%d]: ", (lvl)); \ + printf(__VA_ARGS__); \ + printf(" "); \ + g_print_bio(bp); \ + printf("\n"); \ + } \ +} while (0) + +struct g_slow_softc { + uintmax_t sc_rate; + uintmax_t sc_rate_last; + struct timeval sc_rate_time; + uintmax_t sc_iops; + uintmax_t sc_iops_last; + struct timeval sc_iops_time; + /* Stats */ + uintmax_t sc_reads; + uintmax_t sc_writes; + uintmax_t sc_deletes; + uintmax_t sc_getattrs; + uintmax_t sc_flushes; + uintmax_t sc_cmd0s; + uintmax_t sc_cmd1s; + uintmax_t sc_cmd2s; + uintmax_t sc_readbytes; + uintmax_t sc_wrotebytes; + uintmax_t sc_sleeps; + struct mtx sc_lock; +}; +#endif /* _KERNEL */ + +#endif /* _G_SLOW_H_ */ Index: sys/geom/slow/g_slow.c =================================================================== --- /dev/null +++ sys/geom/slow/g_slow.c @@ -0,0 +1,639 @@ +/*- + * Copyright (c) 2016 Allan Jude + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +SYSCTL_DECL(_kern_geom); +static SYSCTL_NODE(_kern_geom, OID_AUTO, slow, CTLFLAG_RW, 0, + "GEOM_SLOW configuration"); +static u_int g_slow_debug = 0; +SYSCTL_UINT(_kern_geom_slow, OID_AUTO, debug, CTLFLAG_RW, &g_slow_debug, 0, + "Debug level"); +static u_int g_slow_sleep = 10000; +SYSCTL_UINT(_kern_geom_slow, OID_AUTO, sleep, CTLFLAG_RW, &g_slow_sleep, 0, + "Number of microseconds to sleep if over rate limit"); + +static int g_slow_destroy(struct g_geom *gp, boolean_t force); +static int g_slow_destroy_geom(struct gctl_req *req, struct g_class *mp, + struct g_geom *gp); +static void g_slow_config(struct gctl_req *req, struct g_class *mp, + const char *verb); +static void g_slow_dumpconf(struct sbuf *sb, const char *indent, + struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp); + +struct g_class g_slow_class = { + .name = G_SLOW_CLASS_NAME, + .version = G_VERSION, + .ctlreq = g_slow_config, + .destroy_geom = g_slow_destroy_geom +}; + + +static void +g_slow_orphan(struct g_consumer *cp) +{ + + g_topology_assert(); + g_slow_destroy(cp->geom, 1); +} + +static int +throttle(struct timeval *lasttime, uintmax_t *cur, uintmax_t max, + uintmax_t diff) +{ + struct timeval now, delta; + + /* + * Reset the last time and counter if this is the first call + * or more than a second has passed since the last update of + * lasttime. + */ + getmicrouptime(&now); + delta = now; + timevalsub(&delta, lasttime); + if (lasttime->tv_sec == 0 || delta.tv_sec < 0 || delta.tv_sec > 0) { + lasttime->tv_sec = now.tv_sec; + lasttime->tv_usec = now.tv_usec; + *cur = 1; + return (0); + } else { + *cur += diff; + /* First request this second */ + if (*cur - diff == 1) { + return (0); + } + /* Exceeded the maximum */ + if (*cur > max) { + return (1); + } + /* Exceeded proportional limit */ + if ((((max + diff) * delta.tv_usec) / 1000000) < (*cur - diff)) { + return (1); + } + return (0); + } +} + +static void +g_slow_done(struct bio *bp) +{ + struct g_geom *gp; + struct g_slow_softc *sc; + struct timeval ltime; + uintmax_t lrate, lsize, numsleep; + + gp = bp->bio_from->geom; + sc = gp->softc; + if (sc->sc_iops > 0) { + ltime.tv_sec = sc->sc_rate_time.tv_sec; + ltime.tv_usec = sc->sc_rate_time.tv_usec; + lrate = sc->sc_iops_last; + lsize = 1; + numsleep = 0; + if (throttle(<ime, &lrate, sc->sc_iops, lsize) == 0) { + G_SLOW_LOGREQLVL(1, bp, "IOPS: %zu exceeds limit of: %zu.", sc->sc_iops_last, sc->sc_iops); + pause_sbt("gsiops", g_slow_sleep * SBT_1US, 0, C_HARDCLOCK); + numsleep++; + lsize = 0; + } + mtx_lock(&sc->sc_lock); + sc->sc_iops_time.tv_sec = ltime.tv_sec; + sc->sc_iops_time.tv_usec = ltime.tv_usec; + sc->sc_iops_last = lrate; + sc->sc_sleeps += numsleep; + mtx_unlock(&sc->sc_lock); + } + if (sc->sc_rate > 0) { + ltime.tv_sec = sc->sc_rate_time.tv_sec; + ltime.tv_usec = sc->sc_rate_time.tv_usec; + lrate = sc->sc_rate_last; + lsize = bp->bio_length; + numsleep = 0; + while (throttle(<ime, &lrate, sc->sc_rate, lsize) == 1) { + G_SLOW_LOGREQLVL(1, bp, "Rate: %ju exceeds limit of: %ju.", sc->sc_rate_last, sc->sc_rate); + pause_sbt("gsrate", g_slow_sleep * SBT_1US, 0, C_HARDCLOCK); + numsleep++; + lsize = 0; + } + mtx_lock(&sc->sc_lock); + sc->sc_rate_time.tv_sec = ltime.tv_sec; + sc->sc_rate_time.tv_usec = ltime.tv_usec; + sc->sc_rate_last = lrate; + sc->sc_sleeps += numsleep; + mtx_unlock(&sc->sc_lock); + } + + g_std_done(bp); +} + +static void +g_slow_start(struct bio *bp) +{ + struct g_slow_softc *sc; + struct g_geom *gp; + struct g_provider *pp; + struct bio *cbp; + + gp = bp->bio_to->geom; + sc = gp->softc; + G_SLOW_LOGREQ(bp, "Request received."); + mtx_lock(&sc->sc_lock); + switch (bp->bio_cmd) { + case BIO_READ: + sc->sc_reads++; + sc->sc_readbytes += bp->bio_length; + break; + case BIO_WRITE: + sc->sc_writes++; + sc->sc_wrotebytes += bp->bio_length; + break; + case BIO_DELETE: + sc->sc_deletes++; + break; + case BIO_GETATTR: + sc->sc_getattrs++; + break; + case BIO_FLUSH: + sc->sc_flushes++; + break; + case BIO_CMD0: + sc->sc_cmd0s++; + break; + case BIO_CMD1: + sc->sc_cmd1s++; + break; + case BIO_CMD2: + sc->sc_cmd2s++; + break; + } + mtx_unlock(&sc->sc_lock); + cbp = g_clone_bio(bp); + if (cbp == NULL) { + g_io_deliver(bp, ENOMEM); + return; + } + //cbp->bio_done = g_std_done; + cbp->bio_done = g_slow_done; + pp = LIST_FIRST(&gp->provider); + KASSERT(pp != NULL, ("NULL pp")); + cbp->bio_to = pp; + G_SLOW_LOGREQ(cbp, "Sending request."); + g_io_request(cbp, LIST_FIRST(&gp->consumer)); +} + +static int +g_slow_access(struct g_provider *pp, int dr, int dw, int de) +{ + struct g_geom *gp; + struct g_consumer *cp; + int error; + + gp = pp->geom; + cp = LIST_FIRST(&gp->consumer); + error = g_access(cp, dr, dw, de); + + return (error); +} + +static int +g_slow_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp, + uintmax_t rate, uintmax_t iops) +{ + struct g_slow_softc *sc; + struct g_geom *gp; + struct g_provider *newpp; + struct g_consumer *cp; + char name[64]; + int error; + + g_topology_assert(); + + gp = NULL; + newpp = NULL; + cp = NULL; + + snprintf(name, sizeof(name), "%s%s", pp->name, G_SLOW_SUFFIX); + LIST_FOREACH(gp, &mp->geom, geom) { + if (strcmp(gp->name, name) == 0) { + gctl_error(req, "Provider %s already exists.", name); + return (EEXIST); + } + } + gp = g_new_geomf(mp, "%s", name); + sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO); + sc->sc_rate = rate; + sc->sc_rate_last = 1; + getmicrouptime(&sc->sc_rate_time); + /* No rate limit for the first 5 seconds, due to locks */ + sc->sc_rate_time.tv_sec += 5; + sc->sc_iops = iops; + sc->sc_iops_last = 1; + getmicrouptime(&sc->sc_iops_time); + /* No rate limit for the first 5 seconds, due to locks */ + sc->sc_rate_time.tv_sec += 5; + /* Stats */ + sc->sc_reads = 0; + sc->sc_writes = 0; + sc->sc_deletes = 0; + sc->sc_getattrs = 0; + sc->sc_flushes = 0; + sc->sc_cmd0s = 0; + sc->sc_cmd1s = 0; + sc->sc_cmd2s = 0; + sc->sc_readbytes = 0; + sc->sc_wrotebytes = 0; + sc->sc_sleeps = 0; + mtx_init(&sc->sc_lock, "gslow lock", NULL, MTX_DEF); + gp->softc = sc; + gp->start = g_slow_start; + gp->orphan = g_slow_orphan; + gp->access = g_slow_access; + gp->dumpconf = g_slow_dumpconf; + + newpp = g_new_providerf(gp, "%s", gp->name); + newpp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE; + newpp->mediasize = pp->mediasize; + newpp->sectorsize = pp->sectorsize; + newpp->stripesize = pp->stripesize; + newpp->stripeoffset = pp->stripeoffset; + + cp = g_new_consumer(gp); + cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; + error = g_attach(cp, pp); + if (error != 0) { + gctl_error(req, "Cannot attach to provider %s.", pp->name); + goto fail; + } + + newpp->flags |= pp->flags & G_PF_ACCEPT_UNMAPPED; + g_error_provider(newpp, 0); + G_SLOW_DEBUG(0, "Device %s created.", gp->name); + return (0); +fail: + if (cp->provider != NULL) + g_detach(cp); + g_destroy_consumer(cp); + g_destroy_provider(newpp); + mtx_destroy(&sc->sc_lock); + g_free(gp->softc); + g_destroy_geom(gp); + return (error); +} + +static int +g_slow_destroy(struct g_geom *gp, boolean_t force) +{ + struct g_slow_softc *sc; + struct g_provider *pp; + + g_topology_assert(); + sc = gp->softc; + if (sc == NULL) + return (ENXIO); + pp = LIST_FIRST(&gp->provider); + if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { + if (force) { + G_SLOW_DEBUG(0, "Device %s is still open, so it " + "can't be definitely removed.", pp->name); + } else { + G_SLOW_DEBUG(1, "Device %s is still open (r%dw%de%d).", + pp->name, pp->acr, pp->acw, pp->ace); + return (EBUSY); + } + } else { + G_SLOW_DEBUG(0, "Device %s removed.", gp->name); + } + gp->softc = NULL; + mtx_destroy(&sc->sc_lock); + g_free(sc); + g_wither_geom(gp, ENXIO); + + return (0); +} + +static int +g_slow_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp) +{ + + return (g_slow_destroy(gp, 0)); +} + +static void +g_slow_ctl_create(struct gctl_req *req, struct g_class *mp) +{ + struct g_provider *pp; + intmax_t *rate, *iops; + const char *name; + char param[16]; + int i, *nargs; + + g_topology_assert(); + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument", "nargs"); + return; + } + if (*nargs <= 0) { + gctl_error(req, "Missing device(s)."); + return; + } + rate = gctl_get_paraml(req, "rate", sizeof(*rate)); + if (rate == NULL) { + gctl_error(req, "No '%s' argument", "rate"); + return; + } + iops = gctl_get_paraml(req, "iops", sizeof(*iops)); + if (iops == NULL) { + gctl_error(req, "No '%s' argument", "iops"); + return; + } + + for (i = 0; i < *nargs; i++) { + snprintf(param, sizeof(param), "arg%d", i); + name = gctl_get_asciiparam(req, param); + if (name == NULL) { + gctl_error(req, "No 'arg%d' argument", i); + return; + } + if (strncmp(name, "/dev/", strlen("/dev/")) == 0) + name += strlen("/dev/"); + pp = g_provider_by_name(name); + if (pp == NULL) { + G_SLOW_DEBUG(1, "Provider %s is invalid.", name); + gctl_error(req, "Provider %s is invalid.", name); + return; + } + if (g_slow_create(req, mp, pp, *rate, *iops) != 0) { + return; + } + } +} + +static void +g_slow_ctl_configure(struct gctl_req *req, struct g_class *mp) +{ + struct g_slow_softc *sc; + struct g_provider *pp; + intmax_t *rate, *iops; + const char *name; + char param[16]; + int i, *nargs; + + g_topology_assert(); + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument", "nargs"); + return; + } + if (*nargs <= 0) { + gctl_error(req, "Missing device(s)."); + return; + } + rate = gctl_get_paraml(req, "rate", sizeof(*rate)); + if (rate == NULL) { + gctl_error(req, "No '%s' argument", "rate"); + return; + } + iops = gctl_get_paraml(req, "iops", sizeof(*iops)); + if (iops == NULL) { + gctl_error(req, "No '%s' argument", "iops"); + return; + } + + for (i = 0; i < *nargs; i++) { + snprintf(param, sizeof(param), "arg%d", i); + name = gctl_get_asciiparam(req, param); + if (name == NULL) { + gctl_error(req, "No 'arg%d' argument", i); + return; + } + if (strncmp(name, "/dev/", strlen("/dev/")) == 0) + name += strlen("/dev/"); + pp = g_provider_by_name(name); + if (pp == NULL || pp->geom->class != mp) { + G_SLOW_DEBUG(1, "Provider %s is invalid.", name); + gctl_error(req, "Provider %s is invalid.", name); + return; + } + sc = pp->geom->softc; + if (*rate != -1) + sc->sc_rate = (int)*rate; + if (*iops != -1) + sc->sc_iops = (int)*iops; + } +} + +static struct g_geom * +g_slow_find_geom(struct g_class *mp, const char *name) +{ + struct g_geom *gp; + + LIST_FOREACH(gp, &mp->geom, geom) { + if (strcmp(gp->name, name) == 0) + return (gp); + } + return (NULL); +} + +static void +g_slow_ctl_destroy(struct gctl_req *req, struct g_class *mp) +{ + int *nargs, *force, error, i; + struct g_geom *gp; + const char *name; + char param[16]; + + g_topology_assert(); + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument", "nargs"); + return; + } + if (*nargs <= 0) { + gctl_error(req, "Missing device(s)."); + return; + } + force = gctl_get_paraml(req, "force", sizeof(*force)); + if (force == NULL) { + gctl_error(req, "No 'force' argument"); + return; + } + + for (i = 0; i < *nargs; i++) { + snprintf(param, sizeof(param), "arg%d", i); + name = gctl_get_asciiparam(req, param); + if (name == NULL) { + gctl_error(req, "No 'arg%d' argument", i); + return; + } + if (strncmp(name, "/dev/", strlen("/dev/")) == 0) + name += strlen("/dev/"); + gp = g_slow_find_geom(mp, name); + if (gp == NULL) { + G_SLOW_DEBUG(1, "Device %s is invalid.", name); + gctl_error(req, "Device %s is invalid.", name); + return; + } + error = g_slow_destroy(gp, *force); + if (error != 0) { + gctl_error(req, "Cannot destroy device %s (error=%d).", + gp->name, error); + return; + } + } +} + +static void +g_slow_ctl_reset(struct gctl_req *req, struct g_class *mp) +{ + struct g_slow_softc *sc; + struct g_provider *pp; + const char *name; + char param[16]; + int i, *nargs; + + g_topology_assert(); + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument", "nargs"); + return; + } + if (*nargs <= 0) { + gctl_error(req, "Missing device(s)."); + return; + } + + for (i = 0; i < *nargs; i++) { + snprintf(param, sizeof(param), "arg%d", i); + name = gctl_get_asciiparam(req, param); + if (name == NULL) { + gctl_error(req, "No 'arg%d' argument", i); + return; + } + if (strncmp(name, "/dev/", strlen("/dev/")) == 0) + name += strlen("/dev/"); + pp = g_provider_by_name(name); + if (pp == NULL || pp->geom->class != mp) { + G_SLOW_DEBUG(1, "Provider %s is invalid.", name); + gctl_error(req, "Provider %s is invalid.", name); + return; + } + sc = pp->geom->softc; + sc->sc_reads = 0; + sc->sc_writes = 0; + sc->sc_deletes = 0; + sc->sc_getattrs = 0; + sc->sc_flushes = 0; + sc->sc_cmd0s = 0; + sc->sc_cmd1s = 0; + sc->sc_cmd2s = 0; + sc->sc_readbytes = 0; + sc->sc_wrotebytes = 0; + sc->sc_sleeps = 0; + } +} + +static void +g_slow_config(struct gctl_req *req, struct g_class *mp, const char *verb) +{ + uint32_t *version; + + g_topology_assert(); + + version = gctl_get_paraml(req, "version", sizeof(*version)); + if (version == NULL) { + gctl_error(req, "No '%s' argument.", "version"); + return; + } + if (*version != G_SLOW_VERSION) { + gctl_error(req, "Userland and kernel parts are out of sync."); + return; + } + + if (strcmp(verb, "create") == 0) { + g_slow_ctl_create(req, mp); + return; + } else if (strcmp(verb, "configure") == 0) { + g_slow_ctl_configure(req, mp); + return; + } else if (strcmp(verb, "destroy") == 0) { + g_slow_ctl_destroy(req, mp); + return; + } else if (strcmp(verb, "reset") == 0) { + g_slow_ctl_reset(req, mp); + return; + } + + gctl_error(req, "Unknown verb."); +} + +static void +g_slow_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, + struct g_consumer *cp, struct g_provider *pp) +{ + struct g_slow_softc *sc; + + if (pp != NULL || cp != NULL) + return; + sc = gp->softc; + sbuf_printf(sb, "%s%ju\n", indent, sc->sc_rate); + sbuf_printf(sb, "%s%ju\n", indent, sc->sc_iops); + sbuf_printf(sb, "%s%ju\n", indent, sc->sc_reads); + sbuf_printf(sb, "%s%ju\n", indent, sc->sc_writes); + sbuf_printf(sb, "%s%ju\n", indent, sc->sc_deletes); + sbuf_printf(sb, "%s%ju\n", indent, sc->sc_getattrs); + sbuf_printf(sb, "%s%ju\n", indent, sc->sc_flushes); + sbuf_printf(sb, "%s%ju\n", indent, sc->sc_cmd0s); + sbuf_printf(sb, "%s%ju\n", indent, sc->sc_cmd1s); + sbuf_printf(sb, "%s%ju\n", indent, sc->sc_cmd2s); + sbuf_printf(sb, "%s%ju\n", indent, + sc->sc_readbytes); + sbuf_printf(sb, "%s%ju\n", indent, + sc->sc_wrotebytes); + sbuf_printf(sb, "%s%ju\n", indent, sc->sc_sleeps); +} + +DECLARE_GEOM_CLASS(g_slow_class, g_slow); Index: sys/modules/geom/Makefile =================================================================== --- sys/modules/geom/Makefile +++ sys/modules/geom/Makefile @@ -20,6 +20,7 @@ geom_raid3 \ geom_sched \ geom_shsec \ + geom_slow \ geom_stripe \ geom_uzip \ geom_vinum \ Index: sys/modules/geom/geom_slow/Makefile =================================================================== --- /dev/null +++ sys/modules/geom/geom_slow/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../geom/slow + +KMOD= geom_slow +SRCS= g_slow.c + +.include