cdc-wdm: untangle a circular dependency between callback and softint
commit 18abf874367456540846319574864e6ff32752e2 upstream. We have a cycle of callbacks scheduling works which submit URBs with those callbacks. This needs to be blocked, stopped and unblocked to untangle the circle. Signed-off-by: Oliver Neukum <oneukum@suse.com> Link: https://lore.kernel.org/r/20210426092622.20433-1-oneukum@suse.com Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
12d16c24f3
commit
652c9689f5
@ -321,12 +321,23 @@ exit:
|
||||
|
||||
}
|
||||
|
||||
static void kill_urbs(struct wdm_device *desc)
|
||||
static void poison_urbs(struct wdm_device *desc)
|
||||
{
|
||||
/* the order here is essential */
|
||||
usb_kill_urb(desc->command);
|
||||
usb_kill_urb(desc->validity);
|
||||
usb_kill_urb(desc->response);
|
||||
usb_poison_urb(desc->command);
|
||||
usb_poison_urb(desc->validity);
|
||||
usb_poison_urb(desc->response);
|
||||
}
|
||||
|
||||
static void unpoison_urbs(struct wdm_device *desc)
|
||||
{
|
||||
/*
|
||||
* the order here is not essential
|
||||
* it is symmetrical just to be nice
|
||||
*/
|
||||
usb_unpoison_urb(desc->response);
|
||||
usb_unpoison_urb(desc->validity);
|
||||
usb_unpoison_urb(desc->command);
|
||||
}
|
||||
|
||||
static void free_urbs(struct wdm_device *desc)
|
||||
@ -741,11 +752,12 @@ static int wdm_release(struct inode *inode, struct file *file)
|
||||
if (!desc->count) {
|
||||
if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
|
||||
dev_dbg(&desc->intf->dev, "wdm_release: cleanup\n");
|
||||
kill_urbs(desc);
|
||||
poison_urbs(desc);
|
||||
spin_lock_irq(&desc->iuspin);
|
||||
desc->resp_count = 0;
|
||||
spin_unlock_irq(&desc->iuspin);
|
||||
desc->manage_power(desc->intf, 0);
|
||||
unpoison_urbs(desc);
|
||||
} else {
|
||||
/* must avoid dev_printk here as desc->intf is invalid */
|
||||
pr_debug(KBUILD_MODNAME " %s: device gone - cleaning up\n", __func__);
|
||||
@ -1037,9 +1049,9 @@ static void wdm_disconnect(struct usb_interface *intf)
|
||||
wake_up_all(&desc->wait);
|
||||
mutex_lock(&desc->rlock);
|
||||
mutex_lock(&desc->wlock);
|
||||
poison_urbs(desc);
|
||||
cancel_work_sync(&desc->rxwork);
|
||||
cancel_work_sync(&desc->service_outs_intr);
|
||||
kill_urbs(desc);
|
||||
mutex_unlock(&desc->wlock);
|
||||
mutex_unlock(&desc->rlock);
|
||||
|
||||
@ -1080,9 +1092,10 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
set_bit(WDM_SUSPENDING, &desc->flags);
|
||||
spin_unlock_irq(&desc->iuspin);
|
||||
/* callback submits work - order is essential */
|
||||
kill_urbs(desc);
|
||||
poison_urbs(desc);
|
||||
cancel_work_sync(&desc->rxwork);
|
||||
cancel_work_sync(&desc->service_outs_intr);
|
||||
unpoison_urbs(desc);
|
||||
}
|
||||
if (!PMSG_IS_AUTO(message)) {
|
||||
mutex_unlock(&desc->wlock);
|
||||
@ -1140,7 +1153,7 @@ static int wdm_pre_reset(struct usb_interface *intf)
|
||||
wake_up_all(&desc->wait);
|
||||
mutex_lock(&desc->rlock);
|
||||
mutex_lock(&desc->wlock);
|
||||
kill_urbs(desc);
|
||||
poison_urbs(desc);
|
||||
cancel_work_sync(&desc->rxwork);
|
||||
cancel_work_sync(&desc->service_outs_intr);
|
||||
return 0;
|
||||
@ -1151,6 +1164,7 @@ static int wdm_post_reset(struct usb_interface *intf)
|
||||
struct wdm_device *desc = wdm_find_device(intf);
|
||||
int rv;
|
||||
|
||||
unpoison_urbs(desc);
|
||||
clear_bit(WDM_OVERFLOW, &desc->flags);
|
||||
clear_bit(WDM_RESETTING, &desc->flags);
|
||||
rv = recover_from_urb_loss(desc);
|
||||
|
Loading…
x
Reference in New Issue
Block a user