From 519461f843727ce5480786efb95c76c5883c1c9b Mon Sep 17 00:00:00 2001 From: Josef Karthauser <joe@FreeBSD.org> Date: Sun, 20 Jan 2002 23:38:33 +0000 Subject: [PATCH] Merge from NetBSD: uhci.c: -r1.124 uhcireg.h: -r1.13 date: 2000/08/13 18:20:14; author: augustss; state: Exp; Fix race condition when unlinking xfers. Thanks to IWAMOTO Toshihiro <iwamoto@sat.t.u-tokyo.ac.jp> for analyzing the problem and suggesting a fix. Fixes PR 10662. --- sys/dev/usb/uhci.c | 35 ++++++++++++++++++++++++++++++++++- sys/dev/usb/uhcireg.h | 8 +++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/sys/dev/usb/uhci.c b/sys/dev/usb/uhci.c index aa4bacbca851..036773e86205 100644 --- a/sys/dev/usb/uhci.c +++ b/sys/dev/usb/uhci.c @@ -954,9 +954,23 @@ uhci_remove_hs_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) DPRINTFN(10, ("uhci_remove_hs_ctrl: sqh=%p\n", sqh)); uhci_rem_loop(sc); + /* + * The T bit should be set in the elink of the QH so that the HC + * doesn't follow the pointer. This condition may fail if the + * the transferred packet was short so that the QH still points + * at the last used TD. + * In this case we set the T bit and wait a little for the HC + * to stop looking at the TD. + */ + if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) { + sqh->qh.qh_elink = htole32(UHCI_PTR_T); + delay(UHCI_QH_REMOVE_DELAY); + } + pqh = uhci_find_prev_qh(sc->sc_hctl_start, sqh); pqh->hlink = sqh->hlink; pqh->qh.qh_hlink = sqh->qh.qh_hlink; + delay(UHCI_QH_REMOVE_DELAY); if (sc->sc_hctl_end == sqh) sc->sc_hctl_end = pqh; } @@ -987,9 +1001,15 @@ uhci_remove_ls_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) SPLUSBCHECK; DPRINTFN(10, ("uhci_remove_ls_ctrl: sqh=%p\n", sqh)); + /* See comment in uhci_remove_hs_ctrl() */ + if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) { + sqh->qh.qh_elink = htole32(UHCI_PTR_T); + delay(UHCI_QH_REMOVE_DELAY); + } pqh = uhci_find_prev_qh(sc->sc_lctl_start, sqh); pqh->hlink = sqh->hlink; pqh->qh.qh_hlink = sqh->qh.qh_hlink; + delay(UHCI_QH_REMOVE_DELAY); if (sc->sc_lctl_end == sqh) sc->sc_lctl_end = pqh; } @@ -1022,9 +1042,15 @@ uhci_remove_bulk(uhci_softc_t *sc, uhci_soft_qh_t *sqh) DPRINTFN(10, ("uhci_remove_bulk: sqh=%p\n", sqh)); uhci_rem_loop(sc); + /* See comment in uhci_remove_hs_ctrl() */ + if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) { + sqh->qh.qh_elink = htole32(UHCI_PTR_T); + delay(UHCI_QH_REMOVE_DELAY); + } pqh = uhci_find_prev_qh(sc->sc_bulk_start, sqh); pqh->hlink = sqh->hlink; pqh->qh.qh_hlink = sqh->qh.qh_hlink; + delay(UHCI_QH_REMOVE_DELAY); if (sc->sc_bulk_end == sqh) sc->sc_bulk_end = pqh; } @@ -2538,6 +2564,12 @@ uhci_remove_intr(uhci_softc_t *sc, int n, uhci_soft_qh_t *sqh) DPRINTFN(4, ("uhci_remove_intr: n=%d sqh=%p\n", n, sqh)); + /* See comment in uhci_remove_ctrl() */ + if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) { + sqh->qh.qh_elink = htole32(UHCI_PTR_T); + delay(UHCI_QH_REMOVE_DELAY); + } + for (pqh = vf->hqh; pqh->hlink != sqh; pqh = pqh->hlink) #if defined(DIAGNOSTIC) || defined(UHCI_DEBUG) if (le32toh(pqh->qh.qh_hlink) & UHCI_PTR_T) { @@ -2549,6 +2581,7 @@ uhci_remove_intr(uhci_softc_t *sc, int n, uhci_soft_qh_t *sqh) #endif pqh->hlink = sqh->hlink; pqh->qh.qh_hlink = sqh->qh.qh_hlink; + delay(UHCI_QH_REMOVE_DELAY); if (vf->eqh == sqh) vf->eqh = pqh; vf->bandwidth--; @@ -3086,7 +3119,7 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) delay(100); x = UREAD2(sc, port); UWRITE2(sc, port, x | UHCI_PORTSC_PE); - delay(100); + usb_delay_ms(&sc->sc_bus, 10); /* XXX */ DPRINTFN(3,("uhci port %d reset, status = 0x%04x\n", index, UREAD2(sc, port))); sc->sc_isreset = 1; diff --git a/sys/dev/usb/uhcireg.h b/sys/dev/usb/uhcireg.h index ab3c9e75afbd..c8c547987ae1 100644 --- a/sys/dev/usb/uhcireg.h +++ b/sys/dev/usb/uhcireg.h @@ -1,4 +1,4 @@ -/* $NetBSD: usb/uhcireg.h,v 1.12 2000/07/23 19:43:38 augustss Exp $ */ +/* $NetBSD: usb/uhcireg.h,v 1.13 2000/08/13 18:20:15 augustss Exp $ */ /* $FreeBSD$ */ /* @@ -121,6 +121,12 @@ typedef u_int32_t uhci_physaddr_t; #define UHCI_PTR_QH 0x00000002 #define UHCI_PTR_VF 0x00000004 +/* + * Wait this long after a QH has been removed. This gives that HC a + * chance to stop looking at it before it's recycled. + */ +#define UHCI_QH_REMOVE_DELAY 5 + /* * The Queue Heads and Transfer Descriptors are accessed * by both the CPU and the USB controller which run