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:
parent
190c05e664
commit
01b21ef8dc
1 changed files with 13 additions and 7 deletions
|
@ -388,7 +388,8 @@ int usb_get_func_interface_id(struct usb_function *func)
|
||||||
return -ENODEV;
|
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 ret;
|
||||||
int interface_id;
|
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_gadget *gadget;
|
||||||
struct usb_composite_dev *cdev;
|
struct usb_composite_dev *cdev;
|
||||||
|
|
||||||
pr_debug("%s function wakeup\n",
|
pr_debug("%s - %s function wakeup, use pending: %u\n",
|
||||||
func->name ? func->name : "");
|
__func__, func->name ? func->name : "", use_pending_flag);
|
||||||
|
|
||||||
if (!func || !func->config || !func->config->cdev ||
|
if (!func || !func->config || !func->config->cdev ||
|
||||||
!func->config->cdev->gadget)
|
!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,
|
ERROR(func->config->cdev,
|
||||||
"Function %s - Unknown interface id. Canceling USB request. ret=%d\n",
|
"Function %s - Unknown interface id. Canceling USB request. ret=%d\n",
|
||||||
func->name ? func->name : "", ret);
|
func->name ? func->name : "", ret);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface_id = ret;
|
interface_id = ret;
|
||||||
ret = usb_gadget_func_wakeup(gadget, interface_id);
|
ret = usb_gadget_func_wakeup(gadget, interface_id);
|
||||||
|
|
||||||
if (use_pending_flag)
|
if (use_pending_flag) {
|
||||||
func->func_wakeup_pending = false;
|
func->func_wakeup_pending = false;
|
||||||
|
} else {
|
||||||
|
if (ret == -EAGAIN)
|
||||||
|
func->func_wakeup_pending = true;
|
||||||
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&cdev->lock, flags);
|
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||||
|
|
||||||
|
@ -448,12 +455,11 @@ int usb_func_wakeup(struct usb_function *func)
|
||||||
pr_debug("%s function wakeup\n",
|
pr_debug("%s function wakeup\n",
|
||||||
func->name ? func->name : "");
|
func->name ? func->name : "");
|
||||||
|
|
||||||
ret = _usb_func_wakeup(func, false);
|
ret = usb_func_wakeup_int(func, false);
|
||||||
if (ret == -EAGAIN) {
|
if (ret == -EAGAIN) {
|
||||||
DBG(func->config->cdev,
|
DBG(func->config->cdev,
|
||||||
"Function wakeup for %s could not complete due to suspend state. Delayed until after bus resume.\n",
|
"Function wakeup for %s could not complete due to suspend state. Delayed until after bus resume.\n",
|
||||||
func->name ? func->name : "");
|
func->name ? func->name : "");
|
||||||
func->func_wakeup_pending = true;
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
} else if (ret < 0) {
|
} else if (ret < 0) {
|
||||||
ERROR(func->config->cdev,
|
ERROR(func->config->cdev,
|
||||||
|
@ -2304,7 +2310,7 @@ void composite_resume(struct usb_gadget *gadget)
|
||||||
|
|
||||||
if (cdev->config) {
|
if (cdev->config) {
|
||||||
list_for_each_entry(f, &cdev->config->functions, list) {
|
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) {
|
||||||
if (ret == -EAGAIN) {
|
if (ret == -EAGAIN) {
|
||||||
ERROR(f->config->cdev,
|
ERROR(f->config->cdev,
|
||||||
|
|
Loading…
Add table
Reference in a new issue