I tracked down this issue.  Using usbmon, the control and interrupt
transfers would go on for days without problem, but once and a while,
d1c301e0 2316273046 S Ci:1:002:0 s a1 01 0314 0000 0003 8 <
de7cfde0 2334333394 C Ii:1:002:1 0:64 6 = 0c640000 3c00
de7cfde0 2334333468 S Ii:1:002:1 -115:64 6 <
de7cfde0 2337308683 C Ii:1:002:1 0:64 4 = 06000008

there would be a control transfer submitted followed by only interrupt
transfers.  The control transfer would not complete and the usbhid
queue would built up until it was full and
"generic-usb 0003:051D:0002.0001: control queue full" would be printed
from then on.  The following patch has been merged into the git kernel
tree after 2.6.33, and will be in 2.6.34.  I verified it fixes this
bug on my system.

Note the 10 second delay and error -104 in one request.
c2fede40 161671434 S Ci:1:002:0 s a1 01 0326 0000 0004 8 <
c2fede40 161747009 C Ci:1:002:0 0 4 = 26343130
c2fede40 161847590 S Ci:1:002:0 s a1 01 0314 0000 0003 8 <
c2fede40 161848982 C Ci:1:002:0 0 3 = 140000
c2fede40 161908315 S Ci:1:002:0 s a1 01 0314 0000 0003 8 <
c2fede40 171928585 C Ci:1:002:0 -104 3 = 140000
c2fede40 171928605 S Ci:1:002:0 s a1 01 0306 0000 0004 8 <
c2fede40 171930579 C Ci:1:002:0 0 4 = 06000008
c2fede40 171968852 S Ci:1:002:0 s a1 01 030c 0000 0006 8 <
c2fede40 171970572 C Ci:1:002:0 0 6 = 0c640000 3f00
c2fede40 171989721 S Ci:1:002:0 s a1 01 030c 0000 0006 8 <
c2fede40 171991562 C Ci:1:002:0 0 6 = 0c640000 3f00

-- 
David Fries <[email protected]>
http://fries.net/~david/ (PGP encryption key available)


commit 858155fbcc0cd713f6382c527bb1c3abc0ed6d00
Author: Oliver Neukum <[email protected]>
Date:   Fri Feb 12 13:02:28 2010 +0100

    HID: usbhid: introduce timeout for stuck ctrl/out URBs
    
    Some devices do not react to a control request (seen on APC UPS's) 
resulting in
    a slow stream of messages, "generic-usb ... control queue full".  Therefore
    request needs a timeout.
    
    Cc: [email protected]
    Signed-off-by: Oliver Neukum <[email protected]>
    Signed-off-by: David Fries <[email protected]>
    Signed-off-by: Jiri Kosina <[email protected]>

diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 5406074..74bd3ca 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -316,6 +316,7 @@ static int hid_submit_out(struct hid_device *hid)
                        err_hid("usb_submit_urb(out) failed");
                        return -1;
                }
+               usbhid->last_out = jiffies;
        } else {
                /*
                 * queue work to wake up the device.
@@ -377,6 +378,7 @@ static int hid_submit_ctrl(struct hid_device *hid)
                        err_hid("usb_submit_urb(ctrl) failed");
                        return -1;
                }
+               usbhid->last_ctrl = jiffies;
        } else {
                /*
                 * queue work to wake up the device.
@@ -512,9 +514,20 @@ static void __usbhid_submit_report(struct hid_device *hid, 
struct hid_report *re
                usbhid->out[usbhid->outhead].report = report;
                usbhid->outhead = head;
 
-               if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
+               if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) {
                        if (hid_submit_out(hid))
                                clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
+               } else {
+                       /*
+                        * the queue is known to run
+                        * but an earlier request may be stuck
+                        * we may need to time out
+                        * no race because this is called under
+                        * spinlock
+                        */
+                       if (time_after(jiffies, usbhid->last_out + HZ * 5))
+                               usb_unlink_urb(usbhid->urbout);
+               }
                return;
        }
 
@@ -535,9 +548,20 @@ static void __usbhid_submit_report(struct hid_device *hid, 
struct hid_report *re
        usbhid->ctrl[usbhid->ctrlhead].dir = dir;
        usbhid->ctrlhead = head;
 
-       if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl))
+       if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) {
                if (hid_submit_ctrl(hid))
                        clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
+       } else {
+               /*
+                * the queue is known to run
+                * but an earlier request may be stuck
+                * we may need to time out
+                * no race because this is called under
+                * spinlock
+                */
+               if (time_after(jiffies, usbhid->last_ctrl + HZ * 5))
+                       usb_unlink_urb(usbhid->urbctrl);
+       }
 }
 
 void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, 
unsigned char dir)
diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h
index 08f505c..ec20400 100644
--- a/drivers/hid/usbhid/usbhid.h
+++ b/drivers/hid/usbhid/usbhid.h
@@ -80,12 +80,14 @@ struct usbhid_device {
        unsigned char ctrlhead, ctrltail;                               /* 
Control fifo head & tail */
        char *ctrlbuf;                                                  /* 
Control buffer */
        dma_addr_t ctrlbuf_dma;                                         /* 
Control buffer dma */
+       unsigned long last_ctrl;                                                
/* record of last output for timeouts */
 
        struct urb *urbout;                                             /* 
Output URB */
        struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE];              /* 
Output pipe fifo */
        unsigned char outhead, outtail;                                 /* 
Output pipe fifo head & tail */
        char *outbuf;                                                   /* 
Output buffer */
        dma_addr_t outbuf_dma;                                          /* 
Output buffer dma */
+       unsigned long last_out;                                                 
/* record of last output for timeouts */
 
        spinlock_t lock;                                                /* fifo 
spinlock */
        unsigned long iofl;                                             /* I/O 
flags (CTRL_RUNNING, OUT_RUNNING) */



-- 
To UNSUBSCRIBE, email to [email protected]
with a subject of "unsubscribe". Trouble? Contact [email protected]

Reply via email to