Module Name: src
Committed By: riastradh
Date: Sun Mar 13 11:29:10 UTC 2022
Modified Files:
src/sys/dev/usb: ehci.c ehcivar.h
Log Message:
ehci(4): Fix doorbell synchronization.
ehci_sync_hc was previously subject to spurious wakeup, in which case
the CPU might proceed from aborting and recycle a DMA buffer before
the hardware was done writing to it. Now the code is not subject to
spurious wakeup -- it waits (up to the 1sec timeout) for the relevant
interrupt to be delivered, not for anything else.
To generate a diff of this commit:
cvs rdiff -u -r1.307 -r1.308 src/sys/dev/usb/ehci.c
cvs rdiff -u -r1.49 -r1.50 src/sys/dev/usb/ehcivar.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/usb/ehci.c
diff -u src/sys/dev/usb/ehci.c:1.307 src/sys/dev/usb/ehci.c:1.308
--- src/sys/dev/usb/ehci.c:1.307 Wed Mar 9 22:18:13 2022
+++ src/sys/dev/usb/ehci.c Sun Mar 13 11:29:10 2022
@@ -1,4 +1,4 @@
-/* $NetBSD: ehci.c,v 1.307 2022/03/09 22:18:13 riastradh Exp $ */
+/* $NetBSD: ehci.c,v 1.308 2022/03/13 11:29:10 riastradh Exp $ */
/*
* Copyright (c) 2004-2012,2016,2020 The NetBSD Foundation, Inc.
@@ -54,7 +54,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.307 2022/03/09 22:18:13 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.308 2022/03/13 11:29:10 riastradh Exp $");
#include "ohci.h"
#include "uhci.h"
@@ -816,7 +816,10 @@ ehci_doorbell(void *addr)
EHCIHIST_FUNC(); EHCIHIST_CALLED();
mutex_enter(&sc->sc_lock);
- cv_broadcast(&sc->sc_doorbell);
+ if (sc->sc_doorbelllwp == NULL)
+ DPRINTF("spurious doorbell interrupt", 0, 0, 0, 0);
+ sc->sc_doorbelllwp = NULL;
+ cv_signal(&sc->sc_doorbell);
mutex_exit(&sc->sc_lock);
}
@@ -2220,11 +2223,16 @@ ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehc
* by asking for a Async Advance Doorbell interrupt and then we wait for
* the interrupt.
* To make this easier we first obtain exclusive use of the doorbell.
+ *
+ * Releases the bus lock to sleep while waiting for interrupt.
*/
Static void
ehci_sync_hc(ehci_softc_t *sc)
{
- int error __diagused;
+ unsigned delta = hz;
+ unsigned starttime = getticks();
+ unsigned endtime = starttime + delta;
+ unsigned now;
KASSERT(mutex_owned(&sc->sc_lock));
@@ -2235,22 +2243,39 @@ ehci_sync_hc(ehci_softc_t *sc)
return;
}
+ /*
+ * Wait until any concurrent ehci_sync_hc has completed so we
+ * have exclusive access to the doorbell.
+ */
+ while (sc->sc_doorbelllwp)
+ cv_wait(&sc->sc_doorbell, &sc->sc_lock);
+ sc->sc_doorbelllwp = curlwp;
+
/* ask for doorbell */
EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD);
DPRINTF("cmd = 0x%08jx sts = 0x%08jx",
EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), 0, 0);
- error = cv_timedwait(&sc->sc_doorbell, &sc->sc_lock, hz); /* bell wait */
+ /*
+ * Wait for the ehci to ring our doorbell.
+ */
+ while (sc->sc_doorbelllwp == curlwp) {
+ now = getticks();
+ if (endtime - now > delta) {
+ sc->sc_doorbelllwp = NULL;
+ cv_signal(&sc->sc_doorbell);
+ DPRINTF("doorbell timeout", 0, 0, 0, 0);
+#ifdef DIAGNOSTIC /* XXX DIAGNOSTIC abuse, do this differently */
+ printf("ehci_sync_hc: timed out\n");
+#endif
+ break;
+ }
+ (void)cv_timedwait(&sc->sc_doorbell, &sc->sc_lock,
+ endtime - now);
+ }
DPRINTF("cmd = 0x%08jx sts = 0x%08jx ... done",
EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), 0, 0);
-#ifdef DIAGNOSTIC
- if (error == EWOULDBLOCK) {
- printf("ehci_sync_hc: timed out\n");
- } else if (error) {
- printf("ehci_sync_hc: cv_timedwait: error %d\n", error);
- }
-#endif
}
Static void
Index: src/sys/dev/usb/ehcivar.h
diff -u src/sys/dev/usb/ehcivar.h:1.49 src/sys/dev/usb/ehcivar.h:1.50
--- src/sys/dev/usb/ehcivar.h:1.49 Wed Dec 22 21:45:02 2021
+++ src/sys/dev/usb/ehcivar.h Sun Mar 13 11:29:10 2022
@@ -1,4 +1,4 @@
-/* $NetBSD: ehcivar.h,v 1.49 2021/12/22 21:45:02 skrll Exp $ */
+/* $NetBSD: ehcivar.h,v 1.50 2022/03/13 11:29:10 riastradh Exp $ */
/*
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -166,6 +166,7 @@ typedef struct ehci_softc {
kmutex_t sc_intr_lock;
kcondvar_t sc_doorbell;
void *sc_doorbell_si;
+ struct lwp *sc_doorbelllwp;
void *sc_pcd_si;
struct usbd_bus sc_bus;
bus_space_tag_t iot;