git: 729eb176a465 - main - usb: serial: allow the open/close sleep to be interruptible

From: Kyle Evans <kevans_at_FreeBSD.org>
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