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

(cherry picked from commit 729eb176a465cedc55c5980f116d87be592421f1)
This commit is contained in:
Kyle Evans 2024-12-10 19:23:10 -06:00
parent 2882d0c61a
commit 2ba5217057
4 changed files with 69 additions and 17 deletions

View file

@ -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

View file

@ -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));
}
/*------------------------------------------------------------------------*

View file

@ -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);

View file

@ -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