usb: renesas_usbhs: fix spinlock suspected in a gadget complete function
authorYoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Mon, 16 Mar 2015 07:36:48 +0000 (16:36 +0900)
committerFelipe Balbi <balbi@ti.com>
Thu, 19 Mar 2015 16:28:15 +0000 (11:28 -0500)
According to the gadget.h, a "complete" function will always be called
with interrupts disabled. However, sometimes usbhsg_queue_pop() function
is called with interrupts enabled. So, this function should be held by
usbhs_lock() to disable interruption. Also, this driver has to call
spin_unlock() to avoid spinlock recursion by this driver before calling
usb_gadget_giveback_request().
Otherwise, there is possible to cause a spinlock suspected in a gadget
complete function.

Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/renesas_usbhs/mod_gadget.c

index e0384af77e567c8a278f80b1368d8fe2ce9b59e3..dc2aa326120252bcb1ed3eb45c3f31459d4f6497 100644 (file)
@@ -119,18 +119,34 @@ struct usbhsg_recip_handle {
 /*
  *             queue push/pop
  */
-static void usbhsg_queue_pop(struct usbhsg_uep *uep,
-                            struct usbhsg_request *ureq,
-                            int status)
+static void __usbhsg_queue_pop(struct usbhsg_uep *uep,
+                              struct usbhsg_request *ureq,
+                              int status)
 {
        struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
        struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
        struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+       struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
 
        dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
 
        ureq->req.status = status;
+       spin_unlock(usbhs_priv_to_lock(priv));
        usb_gadget_giveback_request(&uep->ep, &ureq->req);
+       spin_lock(usbhs_priv_to_lock(priv));
+}
+
+static void usbhsg_queue_pop(struct usbhsg_uep *uep,
+                            struct usbhsg_request *ureq,
+                            int status)
+{
+       struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+       struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+       unsigned long flags;
+
+       usbhs_lock(priv, flags);
+       __usbhsg_queue_pop(uep, ureq, status);
+       usbhs_unlock(priv, flags);
 }
 
 static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)