usb: gadget: Fix race condition between function wakeup and bus resume
When a SS-USB function wishes to wake up the USB bus, it needs to send a SS-USB function wakeup notification to the USB host after the bus is resumed. For this purpose a function wake up pending flag is used to notify the resume callback that a function wake up notification needs to be sent. However, sometimes there is a race condition in which the resume interrupt is fired before the function wakeup function is complete, and this leads to an incorrect state of the function wakeup pending flag. This patch resolves this issue by adding locks in the critical sections. CRs-fixed: 695399 Change-Id: I8f15ac0c433301d6364a49cb31577e30259aa0b9 Signed-off-by: Danny Segal <dsegal@codeaurora.org>
This commit is contained in:
parent
4571803aa1
commit
190c05e664
1 changed files with 55 additions and 24 deletions
|
@ -388,11 +388,13 @@ int usb_get_func_interface_id(struct usb_function *func)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
int usb_func_wakeup(struct usb_function *func)
|
||||
static int _usb_func_wakeup(struct usb_function *func, bool use_pending_flag)
|
||||
{
|
||||
int ret;
|
||||
unsigned interface_id;
|
||||
int interface_id;
|
||||
unsigned long flags;
|
||||
struct usb_gadget *gadget;
|
||||
struct usb_composite_dev *cdev;
|
||||
|
||||
pr_debug("%s function wakeup\n",
|
||||
func->name ? func->name : "");
|
||||
|
@ -411,6 +413,15 @@ int usb_func_wakeup(struct usb_function *func)
|
|||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
cdev = get_gadget_data(gadget);
|
||||
spin_lock_irqsave(&cdev->lock, flags);
|
||||
|
||||
if (use_pending_flag && !func->func_wakeup_pending) {
|
||||
pr_debug("Pending flag is cleared - Function wakeup is cancelled.\n");
|
||||
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = usb_get_func_interface_id(func);
|
||||
if (ret < 0) {
|
||||
ERROR(func->config->cdev,
|
||||
|
@ -421,26 +432,37 @@ int usb_func_wakeup(struct usb_function *func)
|
|||
|
||||
interface_id = ret;
|
||||
ret = usb_gadget_func_wakeup(gadget, interface_id);
|
||||
if (ret) {
|
||||
|
||||
if (use_pending_flag)
|
||||
func->func_wakeup_pending = false;
|
||||
|
||||
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usb_func_wakeup(struct usb_function *func)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_debug("%s function wakeup\n",
|
||||
func->name ? func->name : "");
|
||||
|
||||
ret = _usb_func_wakeup(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 {
|
||||
} else if (ret < 0) {
|
||||
ERROR(func->config->cdev,
|
||||
"Failed to wake function %s from suspend state. interface id: %d, ret=%d. Canceling USB request.\n",
|
||||
func->name ? func->name : "",
|
||||
interface_id, ret);
|
||||
"Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n",
|
||||
func->name ? func->name : "", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
func->func_wakeup_pending = false;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_func_wakeup);
|
||||
|
||||
int usb_func_ep_queue(struct usb_function *func, struct usb_ep *ep,
|
||||
|
@ -744,6 +766,7 @@ static void reset_config(struct usb_composite_dev *cdev)
|
|||
/* USB 3.0 addition */
|
||||
f->func_is_suspended = false;
|
||||
f->func_wakeup_allowed = false;
|
||||
f->func_wakeup_pending = false;
|
||||
|
||||
bitmap_zero(f->endpoints, 32);
|
||||
}
|
||||
|
@ -2278,14 +2301,22 @@ void composite_resume(struct usb_gadget *gadget)
|
|||
DBG(cdev, "resume\n");
|
||||
if (cdev->driver->resume)
|
||||
cdev->driver->resume(cdev);
|
||||
|
||||
if (cdev->config) {
|
||||
list_for_each_entry(f, &cdev->config->functions, list) {
|
||||
if (f->func_wakeup_pending) {
|
||||
ret = usb_func_wakeup(f);
|
||||
if (ret)
|
||||
ERROR(cdev,
|
||||
"Failed to send function wakeup notification for the %s function. Error code: %d\n",
|
||||
f->name ? f->name : "", ret);
|
||||
ret = _usb_func_wakeup(f, true);
|
||||
if (ret) {
|
||||
if (ret == -EAGAIN) {
|
||||
ERROR(f->config->cdev,
|
||||
"Function wakeup for %s could not complete due to suspend state.\n",
|
||||
f->name ? f->name : "");
|
||||
break;
|
||||
} else {
|
||||
ERROR(f->config->cdev,
|
||||
"Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n",
|
||||
f->name ? f->name : "",
|
||||
ret);
|
||||
}
|
||||
}
|
||||
|
||||
if (f->resume)
|
||||
|
|
Loading…
Add table
Reference in a new issue