Module Name:    src
Committed By:   riastradh
Date:           Sun May 30 11:24:02 UTC 2021

Modified Files:
        src/sys/dev: ld.c ldvar.h

Log Message:
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.111 -r1.112 src/sys/dev/ld.c
cvs rdiff -u -r1.34 -r1.35 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.111 src/sys/dev/ld.c:1.112
--- src/sys/dev/ld.c:1.111	Sun Aug  2 01:17:56 2020
+++ src/sys/dev/ld.c	Sun May 30 11:24:02 2021
@@ -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 @@ __KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.111
 
 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 @@ 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");
 
@@ -276,7 +278,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 */
@@ -438,17 +488,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.34 src/sys/dev/ldvar.h:1.35
--- src/sys/dev/ldvar.h:1.34	Sun Aug  2 01:17:56 2020
+++ src/sys/dev/ldvar.h	Sun May 30 11:24:02 2021
@@ -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 @@ struct ld_softc {
 #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 *);

Reply via email to