On Thu, 2 Aug 2012, Dan Rittersdorf wrote:
> > It looks like the lesson is not to have one thread accessing an
> > endpoint at the same time as another thread issues a CLEAR_HALT.
>
> Thank you -- that spawned a couple thoughts on a potential solution.
>
> And I appreciate you refraining from simply saying "doh!" :-)
> I should have seen the problem as an application level issue, but was
> blind to it because I presumed that "usb.c" was known to "work".
If you have any bug fixes for usb.c, please send them in.
> > There are some known problems related to locking in gadgetfs (it
> > submits requests while holding a lock that the completion routine will
> > take). So far nobody has cared about it enough to do a careful audit
> > and fix.
>
> Yes, I ran into that issue quickly on this project. I addressed one
> case in ep0_read() by unlocking around the usb_ep_queue() call.
> I have not yet discovered any other instances. ep0_write() doesn't make
> the same mistake.
I have had this patch hanging around for a long time. I don't know if
it's entirely right (I don't use gadgetfs much), but see what you
think.
Alan Stern
Index: usb-3.3/drivers/usb/gadget/inode.c
===================================================================
--- usb-3.3.orig/drivers/usb/gadget/inode.c
+++ usb-3.3/drivers/usb/gadget/inode.c
@@ -998,8 +998,11 @@ ep0_read (struct file *fd, char __user *
struct usb_ep *ep = dev->gadget->ep0;
struct usb_request *req = dev->req;
- if ((retval = setup_req (ep, req, 0)) == 0)
+ if ((retval = setup_req (ep, req, 0)) == 0) {
+ spin_unlock(&dev->lock);
retval = usb_ep_queue (ep, req, GFP_ATOMIC);
+ spin_lock(&dev->lock);
+ }
dev->state = STATE_DEV_CONNECTED;
/* assume that was SET_CONFIGURATION */
@@ -1533,8 +1536,10 @@ delegate:
w_length);
if (value < 0)
break;
+ spin_unlock(&dev->lock);
value = usb_ep_queue (gadget->ep0, dev->req,
GFP_ATOMIC);
+ spin_lock(&dev->lock);
if (value < 0) {
clean_req (gadget->ep0, dev->req);
break;
@@ -1557,7 +1562,9 @@ delegate:
if (value >= 0 && dev->state != STATE_DEV_SETUP) {
req->length = value;
req->zero = value < w_length;
+ spin_unlock(&dev->lock);
value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
+ spin_lock(&dev->lock);
if (value < 0) {
DBG (dev, "ep_queue --> %d\n", value);
req->status = 0;
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html