Takashi Iwai 475cb5
From 18abf874367456540846319574864e6ff32752e2 Mon Sep 17 00:00:00 2001
Takashi Iwai 475cb5
From: Oliver Neukum <oneukum@suse.com>
Takashi Iwai 475cb5
Date: Mon, 26 Apr 2021 11:26:22 +0200
Takashi Iwai 475cb5
Subject: [PATCH] cdc-wdm: untangle a circular dependency between callback and softint
Takashi Iwai 475cb5
Git-commit: 18abf874367456540846319574864e6ff32752e2
Takashi Iwai 475cb5
Patch-mainline: v5.13-rc2
Takashi Iwai 475cb5
References: git-fixes
Takashi Iwai 475cb5
Takashi Iwai 475cb5
We have a cycle of callbacks scheduling works which submit
Takashi Iwai 475cb5
URBs with those callbacks. This needs to be blocked, stopped
Takashi Iwai 475cb5
and unblocked to untangle the circle.
Takashi Iwai 475cb5
Takashi Iwai 475cb5
Signed-off-by: Oliver Neukum <oneukum@suse.com>
Takashi Iwai 475cb5
Link: https://lore.kernel.org/r/20210426092622.20433-1-oneukum@suse.com
Takashi Iwai 475cb5
Cc: stable <stable@vger.kernel.org>
Takashi Iwai 475cb5
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Takashi Iwai 475cb5
Acked-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai 475cb5
Takashi Iwai 475cb5
---
Takashi Iwai 475cb5
 drivers/usb/class/cdc-wdm.c | 30 ++++++++++++++++++++++--------
Takashi Iwai 475cb5
 1 file changed, 22 insertions(+), 8 deletions(-)
Takashi Iwai 475cb5
Takashi Iwai 475cb5
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
Takashi Iwai 475cb5
index 508b1c3f8b73..d1e4a7379beb 100644
Takashi Iwai 475cb5
--- a/drivers/usb/class/cdc-wdm.c
Takashi Iwai 475cb5
+++ b/drivers/usb/class/cdc-wdm.c
Takashi Iwai 475cb5
@@ -321,12 +321,23 @@ static void wdm_int_callback(struct urb *urb)
Takashi Iwai 475cb5
 
Takashi Iwai 475cb5
 }
Takashi Iwai 475cb5
 
Takashi Iwai 475cb5
-static void kill_urbs(struct wdm_device *desc)
Takashi Iwai 475cb5
+static void poison_urbs(struct wdm_device *desc)
Takashi Iwai 475cb5
 {
Takashi Iwai 475cb5
 	/* the order here is essential */
Takashi Iwai 475cb5
-	usb_kill_urb(desc->command);
Takashi Iwai 475cb5
-	usb_kill_urb(desc->validity);
Takashi Iwai 475cb5
-	usb_kill_urb(desc->response);
Takashi Iwai 475cb5
+	usb_poison_urb(desc->command);
Takashi Iwai 475cb5
+	usb_poison_urb(desc->validity);
Takashi Iwai 475cb5
+	usb_poison_urb(desc->response);
Takashi Iwai 475cb5
+}
Takashi Iwai 475cb5
+
Takashi Iwai 475cb5
+static void unpoison_urbs(struct wdm_device *desc)
Takashi Iwai 475cb5
+{
Takashi Iwai 475cb5
+	/*
Takashi Iwai 475cb5
+	 *  the order here is not essential
Takashi Iwai 475cb5
+	 *  it is symmetrical just to be nice
Takashi Iwai 475cb5
+	 */
Takashi Iwai 475cb5
+	usb_unpoison_urb(desc->response);
Takashi Iwai 475cb5
+	usb_unpoison_urb(desc->validity);
Takashi Iwai 475cb5
+	usb_unpoison_urb(desc->command);
Takashi Iwai 475cb5
 }
Takashi Iwai 475cb5
 
Takashi Iwai 475cb5
 static void free_urbs(struct wdm_device *desc)
Takashi Iwai 475cb5
@@ -741,11 +752,12 @@ static int wdm_release(struct inode *inode, struct file *file)
Takashi Iwai 475cb5
 	if (!desc->count) {
Takashi Iwai 475cb5
 		if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
Takashi Iwai 475cb5
 			dev_dbg(&desc->intf->dev, "wdm_release: cleanup\n");
Takashi Iwai 475cb5
-			kill_urbs(desc);
Takashi Iwai 475cb5
+			poison_urbs(desc);
Takashi Iwai 475cb5
 			spin_lock_irq(&desc->iuspin);
Takashi Iwai 475cb5
 			desc->resp_count = 0;
Takashi Iwai 475cb5
 			spin_unlock_irq(&desc->iuspin);
Takashi Iwai 475cb5
 			desc->manage_power(desc->intf, 0);
Takashi Iwai 475cb5
+			unpoison_urbs(desc);
Takashi Iwai 475cb5
 		} else {
Takashi Iwai 475cb5
 			/* must avoid dev_printk here as desc->intf is invalid */
Takashi Iwai 475cb5
 			pr_debug(KBUILD_MODNAME " %s: device gone - cleaning up\n", __func__);
Takashi Iwai 475cb5
@@ -1037,9 +1049,9 @@ static void wdm_disconnect(struct usb_interface *intf)
Takashi Iwai 475cb5
 	wake_up_all(&desc->wait);
Takashi Iwai 475cb5
 	mutex_lock(&desc->rlock);
Takashi Iwai 475cb5
 	mutex_lock(&desc->wlock);
Takashi Iwai 475cb5
+	poison_urbs(desc);
Takashi Iwai 475cb5
 	cancel_work_sync(&desc->rxwork);
Takashi Iwai 475cb5
 	cancel_work_sync(&desc->service_outs_intr);
Takashi Iwai 475cb5
-	kill_urbs(desc);
Takashi Iwai 475cb5
 	mutex_unlock(&desc->wlock);
Takashi Iwai 475cb5
 	mutex_unlock(&desc->rlock);
Takashi Iwai 475cb5
 
Takashi Iwai 475cb5
@@ -1080,9 +1092,10 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
Takashi Iwai 475cb5
 		set_bit(WDM_SUSPENDING, &desc->flags);
Takashi Iwai 475cb5
 		spin_unlock_irq(&desc->iuspin);
Takashi Iwai 475cb5
 		/* callback submits work - order is essential */
Takashi Iwai 475cb5
-		kill_urbs(desc);
Takashi Iwai 475cb5
+		poison_urbs(desc);
Takashi Iwai 475cb5
 		cancel_work_sync(&desc->rxwork);
Takashi Iwai 475cb5
 		cancel_work_sync(&desc->service_outs_intr);
Takashi Iwai 475cb5
+		unpoison_urbs(desc);
Takashi Iwai 475cb5
 	}
Takashi Iwai 475cb5
 	if (!PMSG_IS_AUTO(message)) {
Takashi Iwai 475cb5
 		mutex_unlock(&desc->wlock);
Takashi Iwai 475cb5
@@ -1140,7 +1153,7 @@ static int wdm_pre_reset(struct usb_interface *intf)
Takashi Iwai 475cb5
 	wake_up_all(&desc->wait);
Takashi Iwai 475cb5
 	mutex_lock(&desc->rlock);
Takashi Iwai 475cb5
 	mutex_lock(&desc->wlock);
Takashi Iwai 475cb5
-	kill_urbs(desc);
Takashi Iwai 475cb5
+	poison_urbs(desc);
Takashi Iwai 475cb5
 	cancel_work_sync(&desc->rxwork);
Takashi Iwai 475cb5
 	cancel_work_sync(&desc->service_outs_intr);
Takashi Iwai 475cb5
 	return 0;
Takashi Iwai 475cb5
@@ -1151,6 +1164,7 @@ static int wdm_post_reset(struct usb_interface *intf)
Takashi Iwai 475cb5
 	struct wdm_device *desc = wdm_find_device(intf);
Takashi Iwai 475cb5
 	int rv;
Takashi Iwai 475cb5
 
Takashi Iwai 475cb5
+	unpoison_urbs(desc);
Takashi Iwai 475cb5
 	clear_bit(WDM_OVERFLOW, &desc->flags);
Takashi Iwai 475cb5
 	clear_bit(WDM_RESETTING, &desc->flags);
Takashi Iwai 475cb5
 	rv = recover_from_urb_loss(desc);
Takashi Iwai 475cb5
-- 
Takashi Iwai 475cb5
2.26.2
Takashi Iwai 475cb5