The branch main has been updated by kevans:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=729eb176a465cedc55c5980f116d87be592421f1

commit 729eb176a465cedc55c5980f116d87be592421f1
Author:     Kyle Evans <kev...@freebsd.org>
AuthorDate: 2024-12-11 01:23:10 +0000
Commit:     Kyle Evans <kev...@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
 

Reply via email to