git: 729eb176a465 - main - usb: serial: allow the open/close sleep to be interruptible
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 11 Dec 2024 01:24:48 UTC
The branch main has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=729eb176a465cedc55c5980f116d87be592421f1 commit 729eb176a465cedc55c5980f116d87be592421f1 Author: Kyle Evans <kevans@FreeBSD.org> AuthorDate: 2024-12-11 01:23:10 +0000 Commit: Kyle Evans <kevans@FreeBSD.org> CommitDate: 2024-12-11 01:23:10 +0000 usb: serial: allow the open/close sleep to be interruptible ucom_queue_command will issue commands for open/close, then wait on them to be finished. In the spirit of playing it safe, allow ucom_queue_command's wait to be interrupted in case the usb process gets jammed up waiting on the hardware -- we can at least recover the user thread that initiated it, even if we can't recover the usb process. Reviewed by: imp, kib Differential Revision: https://reviews.freebsd.org/D47951 --- sys/dev/usb/serial/usb_serial.c | 14 +++++++-- sys/dev/usb/usb_process.c | 69 ++++++++++++++++++++++++++++++++--------- sys/dev/usb/usb_process.h | 1 + sys/dev/usb/usbdi.h | 2 ++ 4 files changed, 69 insertions(+), 17 deletions(-) diff --git a/sys/dev/usb/serial/usb_serial.c b/sys/dev/usb/serial/usb_serial.c index 11a2e1078a67..30a0c6203086 100644 --- a/sys/dev/usb/serial/usb_serial.c +++ b/sys/dev/usb/serial/usb_serial.c @@ -599,6 +599,7 @@ ucom_queue_command(struct ucom_softc *sc, { struct ucom_super_softc *ssc = sc->sc_super; struct ucom_param_task *task; + int error; UCOM_MTX_ASSERT(sc, MA_OWNED); @@ -628,8 +629,15 @@ ucom_queue_command(struct ucom_softc *sc, /* * Closing or opening the device should be synchronous. */ - if (fn == ucom_cfg_close || fn == ucom_cfg_open) - usb_proc_mwait(&ssc->sc_tq, t0, t1); + if (fn == ucom_cfg_close || fn == ucom_cfg_open) { + error = usb_proc_mwait_sig(&ssc->sc_tq, t0, t1); + + /* usb_proc_mwait_sig may have dropped the tty lock. */ + if (error == 0 && sc->sc_tty != NULL && tty_gone(sc->sc_tty)) + error = ENXIO; + } else { + error = 0; + } /* * In case of multiple configure requests, @@ -638,7 +646,7 @@ ucom_queue_command(struct ucom_softc *sc, if (fn == ucom_cfg_start_transfers) sc->sc_last_start_xfer = &task->hdr; - return (0); + return (error); } static void diff --git a/sys/dev/usb/usb_process.c b/sys/dev/usb/usb_process.c index d88de92336f2..4507c999f50a 100644 --- a/sys/dev/usb/usb_process.c +++ b/sys/dev/usb/usb_process.c @@ -361,25 +361,21 @@ usb_proc_is_gone(struct usb_process *up) return (0); } -/*------------------------------------------------------------------------* - * usb_proc_mwait - * - * This function will return when the USB process message pointed to - * by "pm" is no longer on a queue. This function must be called - * having "up->up_mtx" locked. - *------------------------------------------------------------------------*/ -void -usb_proc_mwait(struct usb_process *up, void *_pm0, void *_pm1) +static int +usb_proc_mwait_impl(struct usb_process *up, void *_pm0, void *_pm1, + bool interruptible) { struct usb_proc_msg *pm0 = _pm0; struct usb_proc_msg *pm1 = _pm1; + int error; /* check if gone */ if (up->up_gone) - return; + return (ENXIO); USB_MTX_ASSERT(up->up_mtx, MA_OWNED); + error = 0; if (up->up_curtd == curthread) { /* Just remove the messages from the queue. */ if (pm0->pm_qentry.tqe_prev) { @@ -391,14 +387,59 @@ usb_proc_mwait(struct usb_process *up, void *_pm0, void *_pm1) pm1->pm_qentry.tqe_prev = NULL; } } else - while (pm0->pm_qentry.tqe_prev || - pm1->pm_qentry.tqe_prev) { + while (error == 0 && (pm0->pm_qentry.tqe_prev || + pm1->pm_qentry.tqe_prev)) { /* check if config thread is gone */ if (up->up_gone) - break; + return (ENXIO); up->up_dsleep = 1; - cv_wait(&up->up_drain, up->up_mtx); + if (interruptible) { + error = cv_wait_sig(&up->up_drain, up->up_mtx); + + /* + * The fact that we were interrupted doesn't + * matter if our goal was accomplished anyways. + */ + if (error != 0 && !USB_PROC_MSG_ENQUEUED(pm0) && + !USB_PROC_MSG_ENQUEUED(pm1)) + error = 0; + } else { + cv_wait(&up->up_drain, up->up_mtx); + } } + + if (error == ERESTART) + error = EINTR; + return (error); +} + +/*------------------------------------------------------------------------* + * usb_proc_mwait + * + * This function will return when the USB process message pointed to + * by "pm" is no longer on a queue. This function must be called + * having "up->up_mtx" locked. + *------------------------------------------------------------------------*/ +void +usb_proc_mwait(struct usb_process *up, void *_pm0, void *_pm1) +{ + + (void)usb_proc_mwait_impl(up, _pm0, _pm1, false); +} + +/*------------------------------------------------------------------------* + * usb_proc_mwait_sig + * + * This function will return when the USB process message pointed to + * by "pm" is no longer on a queue. This function must be called + * having "up->up_mtx" locked. This version of usb_proc_mwait is + * interruptible. + *------------------------------------------------------------------------*/ +int +usb_proc_mwait_sig(struct usb_process *up, void *_pm0, void *_pm1) +{ + + return (usb_proc_mwait_impl(up, _pm0, _pm1, true)); } /*------------------------------------------------------------------------* diff --git a/sys/dev/usb/usb_process.h b/sys/dev/usb/usb_process.h index 6a8ac0acda33..745d214d2106 100644 --- a/sys/dev/usb/usb_process.h +++ b/sys/dev/usb/usb_process.h @@ -76,6 +76,7 @@ int usb_proc_create(struct usb_process *up, struct mtx *p_mtx, const char *pmesg, uint8_t prio); void usb_proc_drain(struct usb_process *up); void usb_proc_mwait(struct usb_process *up, void *pm0, void *pm1); +int usb_proc_mwait_sig(struct usb_process *up, void *pm0, void *pm1); void usb_proc_free(struct usb_process *up); void *usb_proc_msignal(struct usb_process *up, void *pm0, void *pm1); void usb_proc_rewakeup(struct usb_process *up); diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h index 5192591281f4..08d130aa2868 100644 --- a/sys/dev/usb/usbdi.h +++ b/sys/dev/usb/usbdi.h @@ -525,6 +525,8 @@ struct usb_proc_msg { usb_size_t pm_num; }; +#define USB_PROC_MSG_ENQUEUED(msg) ((msg)->pm_qentry.tqe_prev != NULL) + #define USB_FIFO_TX 0 #define USB_FIFO_RX 1