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>
This commit is contained in:
Danny Segal 2014-08-07 17:26:53 +03:00 committed by David Keitel
parent 190c05e664
commit 01b21ef8dc

View file

@ -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,