mirror of
https://github.com/opnsense/src.git
synced 2026-02-18 18:20:26 -05:00
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:
parent
2882d0c61a
commit
2ba5217057
4 changed files with 69 additions and 17 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue