From 3417a5e86aff3114842719f02d6d5c746012b55e Mon Sep 17 00:00:00 2001 From: Taylor R Campbell Date: Wed, 26 Jan 2022 10:31:41 +0000 Subject: [PATCH] ucycom(4): Defer uhidev_write_async to taskq. Can't submit USB transfers while holding tty_lock, a spin lock. --- sys/dev/usb/ucycom.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/sys/dev/usb/ucycom.c b/sys/dev/usb/ucycom.c index 242ac817fda5..93cac2f9c729 100644 --- a/sys/dev/usb/ucycom.c +++ b/sys/dev/usb/ucycom.c @@ -118,7 +118,9 @@ int ucycomdebug = 20; struct ucycom_softc { struct uhidev sc_hdev; + struct usbd_device *sc_udev; + struct usb_task sc_task; struct tty *sc_tty; enum { @@ -172,6 +174,7 @@ const struct cdevsw ucycom_cdevsw = { Static int ucycomparam(struct tty *, struct termios *); Static void ucycomstart(struct tty *); +Static void ucycomstarttask(void *); Static void ucycomwritecb(struct usbd_xfer *, void *, usbd_status); Static void ucycom_intr(struct uhidev *, void *, u_int); Static int ucycom_configure(struct ucycom_softc *, uint32_t, uint8_t); @@ -223,6 +226,7 @@ ucycom_attach(device_t parent, device_t self, void *aux) sc->sc_hdev.sc_intr = ucycom_intr; sc->sc_hdev.sc_parent = uha->parent; sc->sc_hdev.sc_report_id = uha->reportid; + sc->sc_udev = uha->uiaa->uiaa_device; sc->sc_init_state = UCYCOM_INIT_NONE; uhidev_get_report_desc(uha->parent, &desc, &size); @@ -237,6 +241,9 @@ ucycom_attach(device_t parent, device_t self, void *aux) sc->sc_msr = sc->sc_mcr = 0; + /* not MP-safe */ + usb_init_task(&sc->sc_task, ucycomstarttask, sc, 0); + /* set up tty */ sc->sc_tty = tty_alloc(); sc->sc_tty->t_sc = sc; @@ -272,6 +279,7 @@ ucycom_detach(device_t self, int flags) ttyflush(tp, FREAD|FWRITE); mutex_spin_exit(&tty_lock); } + usb_rem_task_wait(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER, NULL); /* Wait for processes to go away. */ usb_detach_waitold(sc->sc_hdev.sc_dev); splx(s); @@ -482,6 +490,8 @@ ucycomstart(struct tty *tp) u_char *data; int cnt, len, s; + KASSERT(mutex_owned(&tty_lock)); + if (sc->sc_dying) return; @@ -592,6 +602,19 @@ ucycomstart(struct tty *tp) } #endif DPRINTFN(4,("ucycomstart: %d chars\n", len)); + usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER); + return; + +out: + splx(s); +} + +Static void +ucycomstarttask(void *cookie) +{ + struct ucycom_softc *sc = cookie; + usbd_status err; + /* What can we do on error? */ err = uhidev_write_async(&sc->sc_hdev, sc->sc_obuf, sc->sc_olen, 0, USBD_NO_TIMEOUT, ucycomwritecb, sc); @@ -599,11 +622,9 @@ ucycomstart(struct tty *tp) #ifdef UCYCOM_DEBUG if (err != USBD_IN_PROGRESS) DPRINTF(("ucycomstart: err=%s\n", usbd_errstr(err))); +#else + __USE(err); #endif - return; - -out: - splx(s); } Static void