DF-5367 / fix.diff
--- a/sys/bus/u4b/serial/ufoma.c +++ b/sys/bus/u4b/serial/ufoma.c @@ -184,6 +184,7 @@ uint8_t sc_msr; uint8_t sc_modetoactivate; uint8_t sc_currentmode; + uint8_t sc_dying; uint8_t sc_name[16]; }; @@ -477,6 +478,16 @@ { struct ufoma_softc *sc = device_get_softc(dev); + /* + * Wake any cfg callback blocked waiting for a mode-switch ACK so + * that the usb_proc_drain() inside ucom_detach() below is not + * stalled for the full ACK timeout by a dead/unplugged device. + */ + lockmgr(&sc->sc_lock, LK_EXCLUSIVE); + sc->sc_dying = 1; + cv_broadcast(&sc->sc_cv); + lockmgr(&sc->sc_lock, LK_RELEASE); + ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX); usbd_transfer_unsetup(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX); @@ -509,11 +520,31 @@ return (desc); } +/* + * Wait for the device ACK, at most "timo" seconds in total, but in + * one-second slices so that a detach (sc_dying) aborts the wait + * immediately. Without this, ucom_detach() -> usb_proc_drain() stalls + * the USB hub event thread for the full UFOMA_MAX_TIMEOUT whenever the + * device is removed while a mode-switch ACK is pending (which, with an + * unresponsive or malicious device, is every open/close). + */ +static void +ufoma_cfg_wait_ack(struct ufoma_softc *sc, int timo) +{ + int error = EWOULDBLOCK; + + while ((timo-- > 0) && !sc->sc_dying) { + error = cv_timedwait(&sc->sc_cv, &sc->sc_lock, hz); + if (error == 0) + return; /* ACK received */ + } + DPRINTF("no or aborted response\n"); +} + static void ufoma_cfg_link_state(struct ufoma_softc *sc) { struct usb_device_request req; - int32_t error; req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; req.bRequest = UMCPC_SET_LINK; @@ -524,18 +555,13 @@ ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, sc->sc_modetable, 0, 1000); - error = cv_timedwait(&sc->sc_cv, &sc->sc_lock, hz); - - if (error) { - DPRINTF("NO response\n"); - } + ufoma_cfg_wait_ack(sc, 1); } static void ufoma_cfg_activate_state(struct ufoma_softc *sc, uint16_t state) { struct usb_device_request req; - int32_t error; req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; req.bRequest = UMCPC_ACTIVATE_MODE; @@ -546,11 +572,7 @@ ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); - error = cv_timedwait(&sc->sc_cv, &sc->sc_lock, - (UFOMA_MAX_TIMEOUT * hz)); - if (error) { - DPRINTF("No response\n"); - } + ufoma_cfg_wait_ack(sc, UFOMA_MAX_TIMEOUT); } static void |