]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/4.11.2/usb-hub-fix-error-loop-seen-after-hub-communication-errors.patch
4.9-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 4.11.2 / usb-hub-fix-error-loop-seen-after-hub-communication-errors.patch
1 From 245b2eecee2aac6fdc77dcafaa73c33f9644c3c7 Mon Sep 17 00:00:00 2001
2 From: Guenter Roeck <linux@roeck-us.net>
3 Date: Mon, 20 Mar 2017 11:16:11 -0700
4 Subject: usb: hub: Fix error loop seen after hub communication errors
5
6 From: Guenter Roeck <linux@roeck-us.net>
7
8 commit 245b2eecee2aac6fdc77dcafaa73c33f9644c3c7 upstream.
9
10 While stress testing a usb controller using a bind/unbind looop, the
11 following error loop was observed.
12
13 usb 7-1.2: new low-speed USB device number 3 using xhci-hcd
14 usb 7-1.2: hub failed to enable device, error -108
15 usb 7-1-port2: cannot disable (err = -22)
16 usb 7-1-port2: couldn't allocate usb_device
17 usb 7-1-port2: cannot disable (err = -22)
18 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
19 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
20 hub 7-1:1.0: activate --> -22
21 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
22 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
23 hub 7-1:1.0: activate --> -22
24 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
25 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
26 hub 7-1:1.0: activate --> -22
27 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
28 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
29 hub 7-1:1.0: activate --> -22
30 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
31 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
32 hub 7-1:1.0: activate --> -22
33 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
34 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
35 hub 7-1:1.0: activate --> -22
36 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
37 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
38 hub 7-1:1.0: activate --> -22
39 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
40 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
41 hub 7-1:1.0: activate --> -22
42 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
43 hub 7-1:1.0: hub_ext_port_status failed (err = -22)
44 ** 57 printk messages dropped ** hub 7-1:1.0: activate --> -22
45 ** 82 printk messages dropped ** hub 7-1:1.0: hub_ext_port_status failed (err = -22)
46
47 This continues forever. After adding tracebacks into the code,
48 the call sequence leading to this is found to be as follows.
49
50 [<ffffffc0007fc8e0>] hub_activate+0x368/0x7b8
51 [<ffffffc0007fceb4>] hub_resume+0x2c/0x3c
52 [<ffffffc00080b3b8>] usb_resume_interface.isra.6+0x128/0x158
53 [<ffffffc00080b5d0>] usb_suspend_both+0x1e8/0x288
54 [<ffffffc00080c9c4>] usb_runtime_suspend+0x3c/0x98
55 [<ffffffc0007820a0>] __rpm_callback+0x48/0x7c
56 [<ffffffc00078217c>] rpm_callback+0xa8/0xd4
57 [<ffffffc000786234>] rpm_suspend+0x84/0x758
58 [<ffffffc000786ca4>] rpm_idle+0x2c8/0x498
59 [<ffffffc000786ed4>] __pm_runtime_idle+0x60/0xac
60 [<ffffffc00080eba8>] usb_autopm_put_interface+0x6c/0x7c
61 [<ffffffc000803798>] hub_event+0x10ac/0x12ac
62 [<ffffffc000249bb8>] process_one_work+0x390/0x6b8
63 [<ffffffc00024abcc>] worker_thread+0x480/0x610
64 [<ffffffc000251a80>] kthread+0x164/0x178
65 [<ffffffc0002045d0>] ret_from_fork+0x10/0x40
66
67 kick_hub_wq() is called from hub_activate() even after failures to
68 communicate with the hub. This results in an endless sequence of
69 hub event -> hub activate -> wq trigger -> hub event -> ...
70
71 Provide two solutions for the problem.
72
73 - Only trigger the hub event queue if communication with the hub
74 is successful.
75 - After a suspend failure, only resume already suspended interfaces
76 if the communication with the device is still possible.
77
78 Each of the changes fixes the observed problem. Use both to improve
79 robustness.
80
81 Acked-by: Alan Stern <stern@rowland.harvard.edu>
82 Signed-off-by: Guenter Roeck <linux@roeck-us.net>
83 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
84
85 ---
86 drivers/usb/core/driver.c | 18 ++++++++++++++++++
87 drivers/usb/core/hub.c | 5 ++++-
88 2 files changed, 22 insertions(+), 1 deletion(-)
89
90 --- a/drivers/usb/core/driver.c
91 +++ b/drivers/usb/core/driver.c
92 @@ -1331,6 +1331,24 @@ static int usb_suspend_both(struct usb_d
93 */
94 if (udev->parent && !PMSG_IS_AUTO(msg))
95 status = 0;
96 +
97 + /*
98 + * If the device is inaccessible, don't try to resume
99 + * suspended interfaces and just return the error.
100 + */
101 + if (status && status != -EBUSY) {
102 + int err;
103 + u16 devstat;
104 +
105 + err = usb_get_status(udev, USB_RECIP_DEVICE, 0,
106 + &devstat);
107 + if (err) {
108 + dev_err(&udev->dev,
109 + "Failed to suspend device, error %d\n",
110 + status);
111 + goto done;
112 + }
113 + }
114 }
115
116 /* If the suspend failed, resume interfaces that did get suspended */
117 --- a/drivers/usb/core/hub.c
118 +++ b/drivers/usb/core/hub.c
119 @@ -1066,6 +1066,9 @@ static void hub_activate(struct usb_hub
120
121 portstatus = portchange = 0;
122 status = hub_port_status(hub, port1, &portstatus, &portchange);
123 + if (status)
124 + goto abort;
125 +
126 if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
127 dev_dbg(&port_dev->dev, "status %04x change %04x\n",
128 portstatus, portchange);
129 @@ -1198,7 +1201,7 @@ static void hub_activate(struct usb_hub
130
131 /* Scan all ports that need attention */
132 kick_hub_wq(hub);
133 -
134 + abort:
135 if (type == HUB_INIT2 || type == HUB_INIT3) {
136 /* Allow autosuspend if it was suppressed */
137 disconnected: