From 01b21ef8dc28ba597280f8230cc90446fbb4ca9c Mon Sep 17 00:00:00 2001 From: Danny Segal Date: Thu, 7 Aug 2014 17:26:53 +0300 Subject: [PATCH] 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 --- drivers/usb/gadget/composite.c | 20 +++++++++++++------- 1 file 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,