--- a/sys/bus/u4b/serial/usb_serial.c +++ b/sys/bus/u4b/serial/usb_serial.c @@ -956,6 +956,25 @@ &sc->sc_close_task[0].hdr, &sc->sc_close_task[1].hdr); + /* + * DF-5422: drain pending TX output while t_oproc is still live. + * ucom_close() must not clear UCOM_FLAG_HL_READY (which turns + * ucom_start() into a no-op, usb_serial.c:1734) before the tty + * output queue has drained, else l_close() -> ttylclose() -> + * ttywflush() -> ttywait() can sleep forever on a queue nobody + * wakes ("ttywai"). Bound the drain so an evil/dead device + * cannot wedge close(2) indefinitely. + */ + lwkt_gettoken(&tp->t_token); + if (tp->t_outq.c_cc != 0) { + int save_timo = tp->t_timeout; + + tp->t_timeout = 5; /* seconds */ + ttywait(tp); + tp->t_timeout = save_timo; + } + lwkt_reltoken(&tp->t_token); + sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW); if (sc->sc_callback->ucom_stop_read) { @@ -1929,6 +1948,15 @@ break; } } + + /* + * DF-5422: the USB TX completion is the only place that observes + * the final drain of t_outq -- wake TSA_OCOMPLETE/TSA_OLOWAT + * sleepers (close(2)/drain ioctls) or they sleep forever. + */ + if (tp->t_outq.c_cc == 0) + ttwwakeup(tp); + lwkt_reltoken(&tp->t_token); actlen[0] = offset - offset_orig;