[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openbsd-tech
Subject:    avoid uvideo lockup when nothing is transferred
From:       Vadim Zhukov <persgray () gmail ! com>
Date:       2020-01-15 22:07:11
Message-ID: 275bf3b0583b7e8e () persx240 ! my ! domain
[Download RAW message or body]

Hi all.

This fixes an issue I'm seeing with a uvideo(4), that doesn't like
commands we're sending to it. The camera simply sends nothing,
and since we're sleeping forever (xfer timeout is 0, which is
USBD_NO_TIMEOUT), we never get out from 'while (bulk_running)' loop,
visible in the scond chunk of the diff. This not only makes video(1)
and other V4L2 users lockup, but also results in process freeze upon
detach: it calls uvideo_vs_close(), which in turn calls
usbd_ref_wait(), which doesn't return because we still have something
in queue.

Also, in case of error we keep bulk_running set. This seems just
a leftover, as well as the first chunk: if we failed to create kthread,
there won't be anything running under bulk_running==1 for sure.

I must thank kettenis@ for help during diagnosis and mpi@ for a patch
for related issue.

OK?

--
WBR,
  Vadim Zhukov


Index: uvideo.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uvideo.c,v
retrieving revision 1.205
diff -u -p -r1.205 uvideo.c
--- uvideo.c	14 Oct 2019 09:20:48 -0000	1.205
+++ uvideo.c	15 Jan 2020 21:54:43 -0000
@@ -2026,6 +2027,7 @@ uvideo_vs_start_bulk(struct uvideo_softc
 	error = kthread_create(uvideo_vs_start_bulk_thread, sc, NULL,
 	    DEVNAME(sc));
 	if (error) {
+		sc->sc_vs_cur->bulk_running = 0;
 		printf("%s: can't create kernel thread!", DEVNAME(sc));
 		return (error);
 	}
@@ -2044,6 +2046,12 @@ uvideo_vs_start_bulk_thread(void *arg)
 	while (sc->sc_vs_cur->bulk_running) {
 		size = UGETDW(sc->sc_desc_probe.dwMaxPayloadTransferSize);
 
+		/*
+		 * We can't wait infinitely, since otherwise we'll
+		 * block forever if camera stops (or don't even starts)
+		 * sending frames. Use '2*' multiplier to compensate
+		 * possible lags.
+		 */
 		usbd_setup_xfer(
 		    sc->sc_vs_cur->bxfer.xfer,
 		    sc->sc_vs_cur->pipeh,
@@ -2051,10 +2059,11 @@ uvideo_vs_start_bulk_thread(void *arg)
 		    sc->sc_vs_cur->bxfer.buf,
 		    size,
 		    USBD_NO_COPY | USBD_SHORT_XFER_OK | USBD_SYNCHRONOUS,
-		    0,
+		    2 * 1000 / (sc->sc_frame_rate || 1),
 		    NULL);
 		error = usbd_transfer(sc->sc_vs_cur->bxfer.xfer);
 		if (error != USBD_NORMAL_COMPLETION) {
+			sc->sc_vs_cur->bulk_running = 0;
 			DPRINTF(1, "%s: error in bulk xfer: %s!\n",
 			    DEVNAME(sc), usbd_errstr(error));
 			break;

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic