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 <st...@rowland.harvard.edu>
Cc: <sta...@vger.kernel.org>
Signed-off-by: Huang Rui <ray.hu...@amd.com>
---
 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to