aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuenter Roeck <linux@roeck-us.net>2017-03-20 14:16:11 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-03-23 03:13:22 -0400
commit245b2eecee2aac6fdc77dcafaa73c33f9644c3c7 (patch)
treed86a86789f729ef97034a64f88d67a384a9e9ce8
parent21a60f6e65181cad64fd66ccc8080d413721ba27 (diff)
usb: hub: Fix error loop seen after hub communication errors
While stress testing a usb controller using a bind/unbind looop, the following error loop was observed. usb 7-1.2: new low-speed USB device number 3 using xhci-hcd usb 7-1.2: hub failed to enable device, error -108 usb 7-1-port2: cannot disable (err = -22) usb 7-1-port2: couldn't allocate usb_device usb 7-1-port2: cannot disable (err = -22) hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: activate --> -22 hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: activate --> -22 hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: activate --> -22 hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: activate --> -22 hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: activate --> -22 hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: activate --> -22 hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: activate --> -22 hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: activate --> -22 hub 7-1:1.0: hub_ext_port_status failed (err = -22) hub 7-1:1.0: hub_ext_port_status failed (err = -22) ** 57 printk messages dropped ** hub 7-1:1.0: activate --> -22 ** 82 printk messages dropped ** hub 7-1:1.0: hub_ext_port_status failed (err = -22) This continues forever. After adding tracebacks into the code, the call sequence leading to this is found to be as follows. [<ffffffc0007fc8e0>] hub_activate+0x368/0x7b8 [<ffffffc0007fceb4>] hub_resume+0x2c/0x3c [<ffffffc00080b3b8>] usb_resume_interface.isra.6+0x128/0x158 [<ffffffc00080b5d0>] usb_suspend_both+0x1e8/0x288 [<ffffffc00080c9c4>] usb_runtime_suspend+0x3c/0x98 [<ffffffc0007820a0>] __rpm_callback+0x48/0x7c [<ffffffc00078217c>] rpm_callback+0xa8/0xd4 [<ffffffc000786234>] rpm_suspend+0x84/0x758 [<ffffffc000786ca4>] rpm_idle+0x2c8/0x498 [<ffffffc000786ed4>] __pm_runtime_idle+0x60/0xac [<ffffffc00080eba8>] usb_autopm_put_interface+0x6c/0x7c [<ffffffc000803798>] hub_event+0x10ac/0x12ac [<ffffffc000249bb8>] process_one_work+0x390/0x6b8 [<ffffffc00024abcc>] worker_thread+0x480/0x610 [<ffffffc000251a80>] kthread+0x164/0x178 [<ffffffc0002045d0>] ret_from_fork+0x10/0x40 kick_hub_wq() is called from hub_activate() even after failures to communicate with the hub. This results in an endless sequence of hub event -> hub activate -> wq trigger -> hub event -> ... Provide two solutions for the problem. - Only trigger the hub event queue if communication with the hub is successful. - After a suspend failure, only resume already suspended interfaces if the communication with the device is still possible. Each of the changes fixes the observed problem. Use both to improve robustness. Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/core/driver.c18
-rw-r--r--drivers/usb/core/hub.c5
2 files changed, 22 insertions, 1 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index cdee5130638b..7ebdf2a4e8fe 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1331,6 +1331,24 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
1331 */ 1331 */
1332 if (udev->parent && !PMSG_IS_AUTO(msg)) 1332 if (udev->parent && !PMSG_IS_AUTO(msg))
1333 status = 0; 1333 status = 0;
1334
1335 /*
1336 * If the device is inaccessible, don't try to resume
1337 * suspended interfaces and just return the error.
1338 */
1339 if (status && status != -EBUSY) {
1340 int err;
1341 u16 devstat;
1342
1343 err = usb_get_status(udev, USB_RECIP_DEVICE, 0,
1344 &devstat);
1345 if (err) {
1346 dev_err(&udev->dev,
1347 "Failed to suspend device, error %d\n",
1348 status);
1349 goto done;
1350 }
1351 }
1334 } 1352 }
1335 1353
1336 /* If the suspend failed, resume interfaces that did get suspended */ 1354 /* If the suspend failed, resume interfaces that did get suspended */
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index f0dd08198d74..735e8ab2cc1f 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1066,6 +1066,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
1066 1066
1067 portstatus = portchange = 0; 1067 portstatus = portchange = 0;
1068 status = hub_port_status(hub, port1, &portstatus, &portchange); 1068 status = hub_port_status(hub, port1, &portstatus, &portchange);
1069 if (status)
1070 goto abort;
1071
1069 if (udev || (portstatus & USB_PORT_STAT_CONNECTION)) 1072 if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
1070 dev_dbg(&port_dev->dev, "status %04x change %04x\n", 1073 dev_dbg(&port_dev->dev, "status %04x change %04x\n",
1071 portstatus, portchange); 1074 portstatus, portchange);
@@ -1198,7 +1201,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
1198 1201
1199 /* Scan all ports that need attention */ 1202 /* Scan all ports that need attention */
1200 kick_hub_wq(hub); 1203 kick_hub_wq(hub);
1201 1204 abort:
1202 if (type == HUB_INIT2 || type == HUB_INIT3) { 1205 if (type == HUB_INIT2 || type == HUB_INIT3) {
1203 /* Allow autosuspend if it was suppressed */ 1206 /* Allow autosuspend if it was suppressed */
1204 disconnected: 1207 disconnected: