Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/usb ehci(4): Fix doorbell synchronization.



details:   https://anonhg.NetBSD.org/src/rev/2865cf28909f
branches:  trunk
changeset: 363465:2865cf28909f
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Sun Mar 13 11:29:10 2022 +0000

description:
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.

diffstat:

 sys/dev/usb/ehci.c    |  49 +++++++++++++++++++++++++++++++++++++------------
 sys/dev/usb/ehcivar.h |   3 ++-
 2 files changed, 39 insertions(+), 13 deletions(-)

diffs (113 lines):

diff -r 4efd7b257055 -r 2865cf28909f sys/dev/usb/ehci.c
--- a/sys/dev/usb/ehci.c        Sun Mar 13 11:29:01 2022 +0000
+++ b/sys/dev/usb/ehci.c        Sun Mar 13 11:29:10 2022 +0000
@@ -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 @@
        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 @@
  * 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 @@
                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
diff -r 4efd7b257055 -r 2865cf28909f sys/dev/usb/ehcivar.h
--- a/sys/dev/usb/ehcivar.h     Sun Mar 13 11:29:01 2022 +0000
+++ b/sys/dev/usb/ehcivar.h     Sun Mar 13 11:29:10 2022 +0000
@@ -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 @@
        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;



Home | Main Index | Thread Index | Old Index