usb: renesas_usbhs: fix spinlock suspected in a gadget complete function
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>
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index e0384af..dc2aa32 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -119,18 +119,34 @@
/*
* queue push/pop
*/
+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_pipe *pipe = usbhsg_uep_to_pipe(uep);
- struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+ unsigned long flags;
- dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
-
- ureq->req.status = status;
- usb_gadget_giveback_request(&uep->ep, &ureq->req);
+ 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)