summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorDanny Segal <dsegal@codeaurora.org>2014-08-07 17:26:53 +0300
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 11:02:45 -0700
commit01b21ef8dc28ba597280f8230cc90446fbb4ca9c (patch)
tree554816ea3ab7bef505785ad945830348450f7b09 /drivers/usb
parent190c05e664e74320bbf416dfcee4e227440eef1e (diff)
usb: gadget: Resolve recursive spinlock during remote wakeup
When a USB function wishes to send new data during USB suspend state, it needs to issue USB remote wakeup and send a function wakeup notification after then. This scenario leads to recursive spin locking inside the _usb_func_wakeup() function, because this function gets called recursively. This function issues remote wakeup, which internally calls the resume interrupt callback, which calls the _usb_func_wakeup() function again. This issue is resolved by performing the remote wakeup in a deferred work context, and this splits the recursion loop. CRs-fixed: 700667 Change-Id: I59c8efde098781587d29f08cd60e4aa3521949d8 Signed-off-by: Danny Segal <dsegal@codeaurora.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/gadget/composite.c20
1 files changed, 13 insertions, 7 deletions
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index aa538fc59cab..ae82f856037b 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -388,7 +388,8 @@ int usb_get_func_interface_id(struct usb_function *func)
return -ENODEV;
}
-static int _usb_func_wakeup(struct usb_function *func, bool use_pending_flag)
+static int usb_func_wakeup_int(struct usb_function *func,
+ bool use_pending_flag)
{
int ret;
int interface_id;
@@ -396,8 +397,8 @@ static int _usb_func_wakeup(struct usb_function *func, bool use_pending_flag)
struct usb_gadget *gadget;
struct usb_composite_dev *cdev;
- pr_debug("%s function wakeup\n",
- func->name ? func->name : "");
+ pr_debug("%s - %s function wakeup, use pending: %u\n",
+ __func__, func->name ? func->name : "", use_pending_flag);
if (!func || !func->config || !func->config->cdev ||
!func->config->cdev->gadget)
@@ -427,14 +428,20 @@ static int _usb_func_wakeup(struct usb_function *func, bool use_pending_flag)
ERROR(func->config->cdev,
"Function %s - Unknown interface id. Canceling USB request. ret=%d\n",
func->name ? func->name : "", ret);
+
+ spin_unlock_irqrestore(&cdev->lock, flags);
return ret;
}
interface_id = ret;
ret = usb_gadget_func_wakeup(gadget, interface_id);
- if (use_pending_flag)
+ if (use_pending_flag) {
func->func_wakeup_pending = false;
+ } else {
+ if (ret == -EAGAIN)
+ func->func_wakeup_pending = true;
+ }
spin_unlock_irqrestore(&cdev->lock, flags);
@@ -448,12 +455,11 @@ int usb_func_wakeup(struct usb_function *func)
pr_debug("%s function wakeup\n",
func->name ? func->name : "");
- ret = _usb_func_wakeup(func, false);
+ ret = usb_func_wakeup_int(func, false);
if (ret == -EAGAIN) {
DBG(func->config->cdev,
"Function wakeup for %s could not complete due to suspend state. Delayed until after bus resume.\n",
func->name ? func->name : "");
- func->func_wakeup_pending = true;
ret = 0;
} else if (ret < 0) {
ERROR(func->config->cdev,
@@ -2304,7 +2310,7 @@ void composite_resume(struct usb_gadget *gadget)
if (cdev->config) {
list_for_each_entry(f, &cdev->config->functions, list) {
- ret = _usb_func_wakeup(f, true);
+ ret = usb_func_wakeup_int(f, true);
if (ret) {
if (ret == -EAGAIN) {
ERROR(f->config->cdev,