Module Name: src Committed By: martin Date: Mon Jun 21 17:23:13 UTC 2021
Modified Files: src/sys/dev [netbsd-9]: ld.c ldvar.h Log Message: Pull up following revision(s) (requested by riastradh in ticket #1304): sys/dev/ldvar.h: revision 1.35 sys/dev/ld.c: revision 1.112 ld(4): Block requests while suspended until resumed. Otherwise nothing stops us from continuing to feed I/O to the disk controller when it expects that the queues are quiesced as it pokes registers to change its power states. Fixes resume during disk activity on my T480 with nvme. To generate a diff of this commit: cvs rdiff -u -r1.106.4.2 -r1.106.4.3 src/sys/dev/ld.c cvs rdiff -u -r1.33 -r1.33.4.1 src/sys/dev/ldvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/ld.c diff -u src/sys/dev/ld.c:1.106.4.2 src/sys/dev/ld.c:1.106.4.3 --- src/sys/dev/ld.c:1.106.4.2 Sat Mar 21 15:52:09 2020 +++ src/sys/dev/ld.c Mon Jun 21 17:23:13 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: ld.c,v 1.106.4.2 2020/03/21 15:52:09 martin Exp $ */ +/* $NetBSD: ld.c,v 1.106.4.3 2021/06/21 17:23:13 martin Exp $ */ /*- * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.106.4.2 2020/03/21 15:52:09 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.106.4.3 2021/06/21 17:23:13 martin Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -63,6 +63,7 @@ __KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.106 static void ldminphys(struct buf *bp); static bool ld_suspend(device_t, const pmf_qual_t *); +static bool ld_resume(device_t, const pmf_qual_t *); static bool ld_shutdown(device_t, int); static int ld_diskstart(device_t, struct buf *bp); static void ld_iosize(device_t, int *); @@ -160,7 +161,8 @@ ldattach(struct ld_softc *sc, const char bufq_alloc(&dksc->sc_bufq, default_strategy, BUFQ_SORT_RAWBLOCK); /* Register with PMF */ - if (!pmf_device_register1(dksc->sc_dev, ld_suspend, NULL, ld_shutdown)) + if (!pmf_device_register1(dksc->sc_dev, ld_suspend, ld_resume, + ld_shutdown)) aprint_error_dev(dksc->sc_dev, "couldn't establish power handler\n"); @@ -266,7 +268,55 @@ ldenddetach(struct ld_softc *sc) static bool ld_suspend(device_t dev, const pmf_qual_t *qual) { - return ld_shutdown(dev, 0); + struct ld_softc *sc = device_private(dev); + int queuecnt; + bool ok = false; + + /* Block new requests and wait for outstanding requests to drain. */ + mutex_enter(&sc->sc_mutex); + KASSERT((sc->sc_flags & LDF_SUSPEND) == 0); + sc->sc_flags |= LDF_SUSPEND; + while ((queuecnt = sc->sc_queuecnt) > 0) { + if (cv_timedwait(&sc->sc_drain, &sc->sc_mutex, 30 * hz)) + break; + } + mutex_exit(&sc->sc_mutex); + + /* Block suspend if we couldn't drain everything in 30sec. */ + if (queuecnt > 0) { + device_printf(dev, "timeout draining buffers\n"); + goto out; + } + + /* Flush cache before we lose power. If we can't, block suspend. */ + if (ld_flush(dev, /*poll*/false) != 0) { + device_printf(dev, "failed to flush cache\n"); + goto out; + } + + /* Success! */ + ok = true; + +out: if (!ok) + (void)ld_resume(dev, qual); + return ok; +} + +static bool +ld_resume(device_t dev, const pmf_qual_t *qual) +{ + struct ld_softc *sc = device_private(dev); + + /* Allow new requests to come in. */ + mutex_enter(&sc->sc_mutex); + KASSERT(sc->sc_flags & LDF_SUSPEND); + sc->sc_flags &= ~LDF_SUSPEND; + mutex_exit(&sc->sc_mutex); + + /* Restart any pending queued requests. */ + dk_start(&sc->sc_dksc, NULL); + + return true; } /* ARGSUSED */ @@ -428,17 +478,24 @@ ld_diskstart(device_t dev, struct buf *b struct ld_softc *sc = device_private(dev); int error; - if (sc->sc_queuecnt >= sc->sc_maxqueuecnt) + if (sc->sc_queuecnt >= sc->sc_maxqueuecnt || + sc->sc_flags & LDF_SUSPEND) { + if (sc->sc_flags & LDF_SUSPEND) + aprint_debug_dev(dev, "i/o blocked while suspended\n"); return EAGAIN; + } if ((sc->sc_flags & LDF_MPSAFE) == 0) KERNEL_LOCK(1, curlwp); mutex_enter(&sc->sc_mutex); - if (sc->sc_queuecnt >= sc->sc_maxqueuecnt) + if (sc->sc_queuecnt >= sc->sc_maxqueuecnt || + sc->sc_flags & LDF_SUSPEND) { + if (sc->sc_flags & LDF_SUSPEND) + aprint_debug_dev(dev, "i/o blocked while suspended\n"); error = EAGAIN; - else { + } else { error = (*sc->sc_start)(sc, bp); if (error == 0) sc->sc_queuecnt++; Index: src/sys/dev/ldvar.h diff -u src/sys/dev/ldvar.h:1.33 src/sys/dev/ldvar.h:1.33.4.1 --- src/sys/dev/ldvar.h:1.33 Tue Mar 19 07:01:14 2019 +++ src/sys/dev/ldvar.h Mon Jun 21 17:23:13 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: ldvar.h,v 1.33 2019/03/19 07:01:14 mlelstv Exp $ */ +/* $NetBSD: ldvar.h,v 1.33.4.1 2021/06/21 17:23:13 martin Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. @@ -70,6 +70,7 @@ struct ld_softc { #define LDF_DRAIN 0x020 /* maxqueuecnt has changed; drain */ #define LDF_NO_RND 0x040 /* do not attach rnd source */ #define LDF_MPSAFE 0x080 /* backend is MPSAFE */ +#define LDF_SUSPEND 0x100 /* disk is suspended until resume */ int ldadjqparam(struct ld_softc *, int); void ldattach(struct ld_softc *, const char *);