Module Name: src Committed By: riastradh Date: Sat Jun 12 12:11:01 UTC 2021
Modified Files: src/sys/dev/usb: uhub.c Log Message: uhub(4): Allow only one explore/rescan at a time. Otherwise we might simultaneously attach two autoconf instances of the same device, which leads to no good. To generate a diff of this commit: cvs rdiff -u -r1.147 -r1.148 src/sys/dev/usb/uhub.c 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/uhub.c diff -u src/sys/dev/usb/uhub.c:1.147 src/sys/dev/usb/uhub.c:1.148 --- src/sys/dev/usb/uhub.c:1.147 Fri Jun 5 17:20:56 2020 +++ src/sys/dev/usb/uhub.c Sat Jun 12 12:11:01 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: uhub.c,v 1.147 2020/06/05 17:20:56 maxv Exp $ */ +/* $NetBSD: uhub.c,v 1.148 2021/06/12 12:11:01 riastradh Exp $ */ /* $FreeBSD: src/sys/dev/usb/uhub.c,v 1.18 1999/11/17 22:33:43 n_hibma Exp $ */ /* $OpenBSD: uhub.c,v 1.86 2015/06/29 18:27:40 mpi Exp $ */ @@ -37,7 +37,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: uhub.c,v 1.147 2020/06/05 17:20:56 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uhub.c,v 1.148 2021/06/12 12:11:01 riastradh Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" @@ -110,6 +110,7 @@ struct uhub_softc { struct usbd_pipe *sc_ipipe; /* interrupt pipe */ kmutex_t sc_lock; + kcondvar_t sc_cv; uint8_t *sc_statusbuf; uint8_t *sc_statuspend; @@ -118,6 +119,8 @@ struct uhub_softc { bool sc_explorepending; bool sc_first_explore; bool sc_running; + + struct lwp *sc_exploring; }; #define UHUB_IS_HIGH_SPEED(sc) \ @@ -127,6 +130,8 @@ struct uhub_softc { #define PORTSTAT_ISSET(sc, port) \ ((sc)->sc_status[(port) / 8] & (1 << ((port) % 8))) +Static usbd_status uhub_explore_enter(struct uhub_softc *); +Static void uhub_explore_exit(struct uhub_softc *); Static usbd_status uhub_explore(struct usbd_device *); Static void uhub_intr(struct usbd_xfer *, void *, usbd_status); @@ -365,6 +370,7 @@ uhub_attach(device_t parent, device_t se sc->sc_statuspend = kmem_zalloc(sc->sc_statuslen, KM_SLEEP); sc->sc_status = kmem_alloc(sc->sc_statuslen, KM_SLEEP); mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); + cv_init(&sc->sc_cv, "uhubex"); /* force initial scan */ memset(sc->sc_status, 0xff, sc->sc_statuslen); @@ -476,6 +482,41 @@ uhub_attach(device_t parent, device_t se config_pending_decr(self); } +Static usbd_status +uhub_explore_enter(struct uhub_softc *sc) +{ + usbd_status err; + + mutex_enter(&sc->sc_lock); + for (;;) { + if (sc->sc_exploring == NULL) { + sc->sc_exploring = curlwp; + err = 0; + break; + } + KASSERT(sc->sc_exploring != curlwp); + if (cv_wait_sig(&sc->sc_cv, &sc->sc_lock)) { + err = USBD_INTERRUPTED; + break; + } + } + mutex_exit(&sc->sc_lock); + + return err; +} + +Static void +uhub_explore_exit(struct uhub_softc *sc) +{ + + mutex_enter(&sc->sc_lock); + KASSERTMSG(sc->sc_exploring == curlwp, "lwp %p exploring %s", + sc->sc_exploring, device_xname(sc->sc_dev)); + sc->sc_exploring = NULL; + cv_broadcast(&sc->sc_cv); + mutex_exit(&sc->sc_lock); +} + usbd_status uhub_explore(struct usbd_device *dev) { @@ -499,6 +540,11 @@ uhub_explore(struct usbd_device *dev) if (dev->ud_depth > USB_HUB_MAX_DEPTH) return USBD_TOO_DEEP; + /* Only one explore at a time, please. */ + err = uhub_explore_enter(sc); + if (err) + return err; + if (PORTSTAT_ISSET(sc, 0)) { /* hub status change */ usb_hub_status_t hs; @@ -803,6 +849,7 @@ uhub_explore(struct usbd_device *dev) } } mutex_exit(&sc->sc_lock); + uhub_explore_exit(sc); if (sc->sc_first_explore) { config_pending_decr(sc->sc_dev); sc->sc_first_explore = false; @@ -866,6 +913,7 @@ uhub_detach(device_t self, int flags) if (sc->sc_statusbuf) kmem_free(sc->sc_statusbuf, sc->sc_statuslen); + cv_destroy(&sc->sc_cv); mutex_destroy(&sc->sc_lock); /* XXXSMP usb */ @@ -882,12 +930,15 @@ uhub_rescan(device_t self, const char *i struct usbd_device *dev; int port; + if (uhub_explore_enter(sc) != 0) + return EBUSY; for (port = 1; port <= hub->uh_hubdesc.bNbrPorts; port++) { dev = hub->uh_ports[port - 1].up_dev; if (dev == NULL) continue; usbd_reattach_device(sc->sc_dev, dev, port, locators); } + uhub_explore_exit(sc); return 0; }