Currently, in usb_sg_wait routine there isn't a timeout on waiting for completion signal. When using TEST 5 -> 8 at usbtest module to verify a new device controller with gadget zero, it might be hang on this phase and this routine waits forever. And it's better to report a error even though it fails. So it should use a test_sg_wait instead of usb_sg_wait to add a timeout about 10s to export a TIMEOUT error to return in usbtest driver. Reference: http://marc.info/?l=linux-usb&m=140137751813423&w=2 Cc: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> Cc: <stable@xxxxxxxxxxxxxxx> Signed-off-by: Huang Rui <ray.huang@xxxxxxx> --- drivers/usb/misc/usbtest.c | 93 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 4 deletions(-) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 51a6da2..b075c64 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -10,7 +10,7 @@ #include <linux/usb.h> -#define SIMPLE_IO_TIMEOUT 10000 /* in milliseconds */ +#define IO_TIMEOUT 10000 /* in milliseconds */ /*-------------------------------------------------------------------------*/ @@ -380,7 +380,7 @@ static int simple_io( if (retval != 0) break; - expire = msecs_to_jiffies(SIMPLE_IO_TIMEOUT); + expire = msecs_to_jiffies(IO_TIMEOUT); if (!wait_for_completion_timeout(&completion, expire)) { usb_kill_urb(urb); retval = (urb->status == -ENOENT ? @@ -435,6 +435,90 @@ static void free_sglist(struct scatterlist *sg, int nents) kfree(sg); } +/* + * Some codes are borrowed from usb_sg_wait and sg_clean routines + */ +static void test_sg_clean(struct usb_sg_request *io) +{ + if (io->urbs) { + while (io->entries--) + usb_free_urb(io->urbs[io->entries]); + kfree(io->urbs); + io->urbs = NULL; + } + io->dev = NULL; +} + +static int test_sg_wait(struct usb_sg_request *io, unsigned long timeout) +{ + int i; + int entries = io->entries; + long timeleft; + + /* queue the urbs. */ + spin_lock_irq(&io->lock); + i = 0; + while (i < entries && !io->status) { + int retval; + + io->urbs[i]->dev = io->dev; + retval = usb_submit_urb(io->urbs[i], GFP_ATOMIC); + + /* after we submit, let completions or cancelations fire; + * we handshake using io->status. + */ + spin_unlock_irq(&io->lock); + switch (retval) { + /* maybe we retrying will recover */ + case -ENXIO: /* hc didn't queue this one */ + case -EAGAIN: + case -ENOMEM: + retval = 0; + yield(); + break; + + /* no error? continue immediately. + * + * NOTE: to work better with UHCI (4K I/O buffer may + * need 3K of TDs) it may be good to limit how many + * URBs are queued at once; N milliseconds? + */ + case 0: + ++i; + cpu_relax(); + break; + + /* fail any uncompleted urbs */ + default: + io->urbs[i]->status = retval; + dev_dbg(&io->dev->dev, "%s, submit --> %d\n", + __func__, retval); + usb_sg_cancel(io); + } + spin_lock_irq(&io->lock); + if (retval && (io->status == 0 || io->status == -ECONNRESET)) + io->status = retval; + } + io->count -= entries - i; + if (io->count == 0) + complete(&io->complete); + spin_unlock_irq(&io->lock); + + /* OK, yes, this could be packaged as non-blocking. + * So could the submit loop above ... but it's easier to + * solve neither problem than to solve both! + */ + timeleft = wait_for_completion_timeout(&io->complete, timeout); + if (timeleft <= 0) { + usb_sg_cancel(io); + if (timeleft == 0) + io->status = -ETIMEDOUT; + } + + test_sg_clean(io); + return io->status; +} + static struct scatterlist * alloc_sglist(int nents, int max, int vary) { @@ -505,8 +589,9 @@ static int perform_sglist( if (retval) break; - usb_sg_wait(req); - retval = req->status; + + retval = test_sg_wait(req, + msecs_to_jiffies(IO_TIMEOUT)); /* FIXME check resulting data pattern */ -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html