summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>2015-03-16 10:36:48 +0300
committerFelipe Balbi <balbi@ti.com>2015-03-19 19:28:15 +0300
commit00f30d29b497577954b20237b405e9d22b5286c2 (patch)
tree65af4661113d147de8fbbd2bd32479ae10bf83ec
parent990919cad55a8c38fc73b0a6a0c9e5e4e927a5f8 (diff)
downloadlinux-00f30d29b497577954b20237b405e9d22b5286c2.tar.xz
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>
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c22
1 files changed, 19 insertions, 3 deletions
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index e0384af77e56..dc2aa3261202 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -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)