@@ -1,4 +1,4 @@
-/* $NetBSD: ld.c,v 1.111 2020/08/02 01:17:56 riastradh Exp $ */
+/* $NetBSD: ld.c,v 1.112 2021/05/30 11:24:02 riastradh 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.111 2020/08/02 01:17:56 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.112 2021/05/30 11:24:02 riastradh Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -63,6 +63,7 @@
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 *);
@@ -166,7 +167,8 @@
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");
@@ -276,9 +278,57 @@
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 */
static bool
ld_shutdown(device_t dev, int flags)
@@ -438,17 +488,24 @@
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++;
@@ -1,4 +1,4 @@
-/* $NetBSD: ldvar.h,v 1.34 2020/08/02 01:17:56 riastradh Exp $ */
+/* $NetBSD: ldvar.h,v 1.35 2021/05/30 11:24:02 riastradh Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@@ -70,6 +70,7 @@
#define LDF_UNUSED0 0x020 /* was LDF_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 *);