Index: lib/geom/nop/geom_nop.c =================================================================== --- lib/geom/nop/geom_nop.c +++ lib/geom/nop/geom_nop.c @@ -43,29 +43,36 @@ struct g_command class_commands[] = { { "create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, { + { 'd', "delaymsec", "-1", G_TYPE_NUMBER }, { 'e', "error", "-1", G_TYPE_NUMBER }, { 'o', "offset", "0", G_TYPE_NUMBER }, { 'p', "stripesize", "0", G_TYPE_NUMBER }, { 'P', "stripeoffset", "0", G_TYPE_NUMBER }, + { 'q', "rdelayprob", "-1", G_TYPE_NUMBER }, { 'r', "rfailprob", "-1", G_TYPE_NUMBER }, { 's', "size", "0", G_TYPE_NUMBER }, { 'S', "secsize", "0", G_TYPE_NUMBER }, { 'w', "wfailprob", "-1", G_TYPE_NUMBER }, + { 'x', "wdelayprob", "1", G_TYPE_NUMBER }, { 'z', "physpath", G_NOP_PHYSPATH_PASSTHROUGH, G_TYPE_STRING }, G_OPT_SENTINEL }, - "[-v] [-e error] [-o offset] [-p stripesize] [-P stripeoffset] " - "[-r rfailprob] [-s size] [-S secsize] [-w wfailprob] " - "[-z physpath] dev ..." + "[-v] [-d delaymsec] [-e error] [-o offset] [-p stripesize] " + "[-P stripeoffset] [-q rdelayprob] [-r rfailprob] [-s size] " + "[-S secsize] [-w wfailprob] [-x wdelayprob] [-z physpath] dev ..." }, { "configure", G_FLAG_VERBOSE, NULL, { + { 'd', "delaymsec", "-1", G_TYPE_NUMBER }, { 'e', "error", "-1", G_TYPE_NUMBER }, + { 'q', "rdelayprob", "-1", G_TYPE_NUMBER }, { 'r', "rfailprob", "-1", G_TYPE_NUMBER }, { 'w', "wfailprob", "-1", G_TYPE_NUMBER }, + { 'x', "wdelayprob", "1", G_TYPE_NUMBER }, G_OPT_SENTINEL }, - "[-v] [-e error] [-r rfailprob] [-w wfailprob] prov ..." + "[-v] [-d delaymsec] [-e error] [-q rdelayprob] [-r rfailprob] " + "[-w wfailprob] [-x wdelayprob] prov ..." }, { "destroy", G_FLAG_VERBOSE, NULL, { Index: lib/geom/nop/gnop.8 =================================================================== --- lib/geom/nop/gnop.8 +++ lib/geom/nop/gnop.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 17, 2018 +.Dd July 24, 2019 .Dt GNOP 8 .Os .Sh NAME @@ -34,22 +34,28 @@ .Nm .Cm create .Op Fl v +.Op Fl d Ar delaymsec .Op Fl e Ar error .Op Fl o Ar offset .Op Fl p Ar stripesize .Op Fl P Ar stripeoffset +.Op Fl q Ar rdelayprob .Op Fl r Ar rfailprob .Op Fl s Ar size .Op Fl S Ar secsize .Op Fl w Ar wfailprob +.Op Fl x Ar wdelayprob .Op Fl z Ar physpath .Ar dev ... .Nm .Cm configure .Op Fl v +.Op Fl d Ar delaymsec .Op Fl e Ar error +.Op Fl q Ar rdelayprob .Op Fl r Ar rfailprob .Op Fl w Ar wfailprob +.Op Fl x Ar wdelayprob .Ar prov ... .Nm .Cm destroy @@ -113,6 +119,9 @@ .Pp Additional options: .Bl -tag -width ".Fl r Ar rfailprob" +.It Fl d Ar delaymsec +Specifies the delay of the requests in milliseconds. +Note that requests will be delayed before they are sent to the backing device. .It Fl e Ar error Specifies the error number to return on failure. .It Fl f @@ -123,6 +132,8 @@ Value of the stripesize property of the transparent provider. .It Fl P Ar stripeoffset Value of the stripeoffset property of the transparent provider. +.It Fl q Ar rdelayprob +Specifies read delay probability in percent. .It Fl r Ar rfailprob Specifies read failure probability in percent. .It Fl s Ar size @@ -133,6 +144,8 @@ Specifies write failure probability in percent. .It Fl v Be more verbose. +.It Fl x Ar wdelayprob +Specifies write delay probability in percent. .It Fl z Ar physpath Physical path of the transparent provider. .El Index: sys/geom/nop/g_nop.h =================================================================== --- sys/geom/nop/g_nop.h +++ sys/geom/nop/g_nop.h @@ -62,26 +62,34 @@ } \ } while (0) +struct g_nop_delay; + +TAILQ_HEAD(g_nop_delay_head, g_nop_delay); + struct g_nop_softc { - int sc_error; - off_t sc_offset; - off_t sc_explicitsize; - off_t sc_stripesize; - off_t sc_stripeoffset; - u_int sc_rfailprob; - u_int sc_wfailprob; - 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; - char* sc_physpath; - struct mtx sc_lock; + int sc_error; + off_t sc_offset; + off_t sc_explicitsize; + off_t sc_stripesize; + off_t sc_stripeoffset; + u_int sc_rfailprob; + u_int sc_wfailprob; + u_int sc_delaymsec; + u_int sc_rdelayprob; + u_int sc_wdelayprob; + 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; + char *sc_physpath; + struct mtx sc_lock; + struct g_nop_delay_head sc_head_delay; }; #endif /* _KERNEL */ Index: sys/geom/nop/g_nop.c =================================================================== --- sys/geom/nop/g_nop.c +++ sys/geom/nop/g_nop.c @@ -74,6 +74,12 @@ .start = g_nop_start, }; +struct g_nop_delay { + struct callout dl_cal; + struct bio *dl_bio; + TAILQ_ENTRY(g_nop_delay) dl_next; +}; + static void g_nop_orphan(struct g_consumer *cp) { @@ -142,6 +148,35 @@ g_io_deliver(bp, 0); } +static void +g_nop_pass(struct bio *cbp, struct g_geom *gp) +{ + + G_NOP_LOGREQ(cbp, "Sending request."); + g_io_request(cbp, LIST_FIRST(&gp->consumer)); +} + +static void +g_nop_pass_timeout(void *data) +{ + struct g_nop_softc *sc; + struct g_geom *gp; + struct g_nop_delay *gndelay; + + gndelay = (struct g_nop_delay *)data; + + gp = gndelay->dl_bio->bio_to->geom; + sc = gp->softc; + + mtx_lock(&sc->sc_lock); + TAILQ_REMOVE(&sc->sc_head_delay, gndelay, dl_next); + mtx_unlock(&sc->sc_lock); + + g_nop_pass(gndelay->dl_bio, gp); + + g_free(data); +} + static void g_nop_start(struct bio *bp) { @@ -149,10 +184,13 @@ struct g_geom *gp; struct g_provider *pp; struct bio *cbp; - u_int failprob = 0; + u_int failprob, delayprob, delaytime; + + failprob = delayprob = 0; gp = bp->bio_to->geom; sc = gp->softc; + G_NOP_LOGREQ(bp, "Request received."); mtx_lock(&sc->sc_lock); switch (bp->bio_cmd) { @@ -160,11 +198,15 @@ sc->sc_reads++; sc->sc_readbytes += bp->bio_length; failprob = sc->sc_rfailprob; + delayprob = sc->sc_rdelayprob; + delaytime = sc->sc_delaymsec; break; case BIO_WRITE: sc->sc_writes++; sc->sc_wrotebytes += bp->bio_length; failprob = sc->sc_wfailprob; + delayprob = sc->sc_wdelayprob; + delaytime = sc->sc_delaymsec; break; case BIO_DELETE: sc->sc_deletes++; @@ -208,6 +250,7 @@ return; } } + cbp = g_clone_bio(bp); if (cbp == NULL) { g_io_deliver(bp, ENOMEM); @@ -218,8 +261,33 @@ pp = LIST_FIRST(&gp->provider); KASSERT(pp != NULL, ("NULL pp")); cbp->bio_to = pp; - G_NOP_LOGREQ(cbp, "Sending request."); - g_io_request(cbp, LIST_FIRST(&gp->consumer)); + + if (delayprob > 0) { + struct g_nop_delay *gndelay; + u_int rval; + + rval = arc4random() % 100; + if (rval < delayprob) { + gndelay = g_malloc(sizeof(*gndelay), M_NOWAIT | M_ZERO); + if (gndelay != NULL) { + callout_init(&gndelay->dl_cal, 1); + + gndelay->dl_bio = cbp; + + mtx_lock(&sc->sc_lock); + TAILQ_INSERT_TAIL(&sc->sc_head_delay, gndelay, + dl_next); + mtx_unlock(&sc->sc_lock); + + callout_reset(&gndelay->dl_cal, + MSEC_2_TICKS(delaytime), g_nop_pass_timeout, + gndelay); + return; + } + } + } + + g_nop_pass(cbp, gp); } static int @@ -238,8 +306,9 @@ static int g_nop_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp, - int ioerror, u_int rfailprob, u_int wfailprob, off_t offset, off_t size, - u_int secsize, off_t stripesize, off_t stripeoffset, const char *physpath) + int ioerror, u_int rfailprob, u_int wfailprob, u_int delaymsec, u_int rdelayprob, + u_int wdelayprob, off_t offset, off_t size, u_int secsize, off_t stripesize, + off_t stripeoffset, const char *physpath) { struct g_nop_softc *sc; struct g_geom *gp; @@ -317,6 +386,9 @@ sc->sc_error = ioerror; sc->sc_rfailprob = rfailprob; sc->sc_wfailprob = wfailprob; + sc->sc_delaymsec = delaymsec; + sc->sc_rdelayprob = rdelayprob; + sc->sc_wdelayprob = wdelayprob; sc->sc_reads = 0; sc->sc_writes = 0; sc->sc_deletes = 0; @@ -327,6 +399,7 @@ sc->sc_cmd2s = 0; sc->sc_readbytes = 0; sc->sc_wrotebytes = 0; + TAILQ_INIT(&sc->sc_head_delay); mtx_init(&sc->sc_lock, "gnop lock", NULL, MTX_DEF); gp->softc = sc; @@ -367,6 +440,9 @@ struct g_geom *gp = pp->geom; struct g_nop_softc *sc = gp->softc; + KASSERT(TAILQ_EMPTY(&sc->sc_head_delay), + ("delayed request list is not empty")); + gp->softc = NULL; free(sc->sc_physpath, M_GEOM); mtx_destroy(&sc->sc_lock); @@ -396,6 +472,7 @@ } else { G_NOP_DEBUG(0, "Device %s removed.", gp->name); } + g_wither_geom(gp, ENXIO); return (0); @@ -413,7 +490,7 @@ { struct g_provider *pp; intmax_t *error, *rfailprob, *wfailprob, *offset, *secsize, *size, - *stripesize, *stripeoffset; + *stripesize, *stripeoffset, *delaymsec, *rdelayprob, *wdelayprob; const char *name, *physpath; char param[16]; int i, *nargs; @@ -452,6 +529,33 @@ gctl_error(req, "Invalid '%s' argument", "wfailprob"); return; } + delaymsec = gctl_get_paraml(req, "delaymsec", sizeof(*delaymsec)); + if (delaymsec == NULL) { + gctl_error(req, "No '%s' argument", "delaymsec"); + return; + } + if (*delaymsec < 1 && *delaymsec != -1) { + gctl_error(req, "Invalid '%s' argument", "delaymsec"); + return; + } + rdelayprob = gctl_get_paraml(req, "rdelayprob", sizeof(*rdelayprob)); + if (rdelayprob == NULL) { + gctl_error(req, "No '%s' argument", "rdelayprob"); + return; + } + if (*rdelayprob < -1 || *rdelayprob > 100) { + gctl_error(req, "Invalid '%s' argument", "rdelayprob"); + return; + } + wdelayprob = gctl_get_paraml(req, "wdelayprob", sizeof(*wdelayprob)); + if (wdelayprob == NULL) { + gctl_error(req, "No '%s' argument", "wdelayprob"); + return; + } + if (*wdelayprob < -1 || *wdelayprob > 100) { + gctl_error(req, "Invalid '%s' argument", "wdelayprob"); + return; + } offset = gctl_get_paraml(req, "offset", sizeof(*offset)); if (offset == NULL) { gctl_error(req, "No '%s' argument", "offset"); @@ -518,6 +622,9 @@ *error == -1 ? EIO : (int)*error, *rfailprob == -1 ? 0 : (u_int)*rfailprob, *wfailprob == -1 ? 0 : (u_int)*wfailprob, + *delaymsec == -1 ? 1 : (u_int)*delaymsec, + *rdelayprob == -1 ? 0 : (u_int)*rdelayprob, + *wdelayprob == -1 ? 0 : (u_int)*wdelayprob, (off_t)*offset, (off_t)*size, (u_int)*secsize, (off_t)*stripesize, (off_t)*stripeoffset, physpath) != 0) { @@ -531,7 +638,7 @@ { struct g_nop_softc *sc; struct g_provider *pp; - intmax_t *error, *rfailprob, *wfailprob; + intmax_t *delaymsec, *error, *rdelayprob, *rfailprob, *wdelayprob, *wfailprob; const char *name; char param[16]; int i, *nargs; @@ -571,6 +678,34 @@ return; } + delaymsec = gctl_get_paraml(req, "delaymsec", sizeof(*delaymsec)); + if (delaymsec == NULL) { + gctl_error(req, "No '%s' argument", "delaymsec"); + return; + } + if (*delaymsec < 1 && *delaymsec != -1) { + gctl_error(req, "Invalid '%s' argument", "delaymsec"); + return; + } + rdelayprob = gctl_get_paraml(req, "rdelayprob", sizeof(*rdelayprob)); + if (rdelayprob == NULL) { + gctl_error(req, "No '%s' argument", "rdelayprob"); + return; + } + if (*rdelayprob < -1 || *rdelayprob > 100) { + gctl_error(req, "Invalid '%s' argument", "rdelayprob"); + return; + } + wdelayprob = gctl_get_paraml(req, "wdelayprob", sizeof(*wdelayprob)); + if (wdelayprob == NULL) { + gctl_error(req, "No '%s' argument", "wdelayprob"); + return; + } + if (*wdelayprob < -1 || *wdelayprob > 100) { + gctl_error(req, "Invalid '%s' argument", "wdelayprob"); + return; + } + for (i = 0; i < *nargs; i++) { snprintf(param, sizeof(param), "arg%d", i); name = gctl_get_asciiparam(req, param); @@ -593,6 +728,12 @@ sc->sc_rfailprob = (u_int)*rfailprob; if (*wfailprob != -1) sc->sc_wfailprob = (u_int)*wfailprob; + if (*rdelayprob != -1) + sc->sc_rdelayprob = (u_int)*rdelayprob; + if (*wdelayprob != -1) + sc->sc_wdelayprob = (u_int)*wdelayprob; + if (*delaymsec != -1) + sc->sc_delaymsec = (u_int)*delaymsec; } } @@ -756,6 +897,11 @@ sc->sc_rfailprob); sbuf_printf(sb, "%s%u\n", indent, sc->sc_wfailprob); + sbuf_printf(sb, "%s%u\n", indent, + sc->sc_rdelayprob); + sbuf_printf(sb, "%s%u\n", indent, + sc->sc_wdelayprob); + sbuf_printf(sb, "%s%d\n", indent, sc->sc_delaymsec); sbuf_printf(sb, "%s%d\n", indent, sc->sc_error); sbuf_printf(sb, "%s%ju\n", indent, sc->sc_reads); sbuf_printf(sb, "%s%ju\n", indent, sc->sc_writes);