]>
Commit | Line | Data |
---|---|---|
e56a7dfa GKH |
1 | From 6aec044cc2f5670cf3b143c151c8be846499bd15 Mon Sep 17 00:00:00 2001 |
2 | From: Alan Stern <stern@rowland.harvard.edu> | |
3 | Date: Wed, 12 Mar 2014 11:30:38 -0400 | |
4 | Subject: USB: unbind all interfaces before rebinding any | |
5 | ||
6 | From: Alan Stern <stern@rowland.harvard.edu> | |
7 | ||
8 | commit 6aec044cc2f5670cf3b143c151c8be846499bd15 upstream. | |
9 | ||
10 | When a driver doesn't have pre_reset, post_reset, or reset_resume | |
11 | methods, the USB core unbinds that driver when its device undergoes a | |
12 | reset or a reset-resume, and then rebinds it afterward. | |
13 | ||
14 | The existing straightforward implementation can lead to problems, | |
15 | because each interface gets unbound and rebound before the next | |
16 | interface is handled. If a driver claims additional interfaces, the | |
17 | claim may fail because the old binding instance may still own the | |
18 | additional interface when the new instance tries to claim it. | |
19 | ||
20 | This patch fixes the problem by first unbinding all the interfaces | |
21 | that are marked (i.e., their needs_binding flag is set) and then | |
22 | rebinding all of them. | |
23 | ||
24 | The patch also makes the helper functions in driver.c a little more | |
25 | uniform and adjusts some out-of-date comments. | |
26 | ||
27 | Signed-off-by: Alan Stern <stern@rowland.harvard.edu> | |
28 | Reported-and-tested-by: "Poulain, Loic" <loic.poulain@intel.com> | |
29 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
30 | ||
31 | --- | |
32 | drivers/usb/core/driver.c | 94 +++++++++++++++++++++++++++------------------- | |
33 | drivers/usb/core/hub.c | 5 +- | |
34 | drivers/usb/core/usb.h | 2 | |
35 | 3 files changed, 60 insertions(+), 41 deletions(-) | |
36 | ||
37 | --- a/drivers/usb/core/driver.c | |
38 | +++ b/drivers/usb/core/driver.c | |
39 | @@ -953,8 +953,7 @@ EXPORT_SYMBOL_GPL(usb_deregister); | |
40 | * it doesn't support pre_reset/post_reset/reset_resume or | |
41 | * because it doesn't support suspend/resume. | |
42 | * | |
43 | - * The caller must hold @intf's device's lock, but not its pm_mutex | |
44 | - * and not @intf->dev.sem. | |
45 | + * The caller must hold @intf's device's lock, but not @intf's lock. | |
46 | */ | |
47 | void usb_forced_unbind_intf(struct usb_interface *intf) | |
48 | { | |
49 | @@ -967,16 +966,37 @@ void usb_forced_unbind_intf(struct usb_i | |
50 | intf->needs_binding = 1; | |
51 | } | |
52 | ||
53 | +/* | |
54 | + * Unbind drivers for @udev's marked interfaces. These interfaces have | |
55 | + * the needs_binding flag set, for example by usb_resume_interface(). | |
56 | + * | |
57 | + * The caller must hold @udev's device lock. | |
58 | + */ | |
59 | +static void unbind_marked_interfaces(struct usb_device *udev) | |
60 | +{ | |
61 | + struct usb_host_config *config; | |
62 | + int i; | |
63 | + struct usb_interface *intf; | |
64 | + | |
65 | + config = udev->actconfig; | |
66 | + if (config) { | |
67 | + for (i = 0; i < config->desc.bNumInterfaces; ++i) { | |
68 | + intf = config->interface[i]; | |
69 | + if (intf->dev.driver && intf->needs_binding) | |
70 | + usb_forced_unbind_intf(intf); | |
71 | + } | |
72 | + } | |
73 | +} | |
74 | + | |
75 | /* Delayed forced unbinding of a USB interface driver and scan | |
76 | * for rebinding. | |
77 | * | |
78 | - * The caller must hold @intf's device's lock, but not its pm_mutex | |
79 | - * and not @intf->dev.sem. | |
80 | + * The caller must hold @intf's device's lock, but not @intf's lock. | |
81 | * | |
82 | * Note: Rebinds will be skipped if a system sleep transition is in | |
83 | * progress and the PM "complete" callback hasn't occurred yet. | |
84 | */ | |
85 | -void usb_rebind_intf(struct usb_interface *intf) | |
86 | +static void usb_rebind_intf(struct usb_interface *intf) | |
87 | { | |
88 | int rc; | |
89 | ||
90 | @@ -993,68 +1013,66 @@ void usb_rebind_intf(struct usb_interfac | |
91 | } | |
92 | } | |
93 | ||
94 | -#ifdef CONFIG_PM | |
95 | - | |
96 | -/* Unbind drivers for @udev's interfaces that don't support suspend/resume | |
97 | - * There is no check for reset_resume here because it can be determined | |
98 | - * only during resume whether reset_resume is needed. | |
99 | +/* | |
100 | + * Rebind drivers to @udev's marked interfaces. These interfaces have | |
101 | + * the needs_binding flag set. | |
102 | * | |
103 | * The caller must hold @udev's device lock. | |
104 | */ | |
105 | -static void unbind_no_pm_drivers_interfaces(struct usb_device *udev) | |
106 | +static void rebind_marked_interfaces(struct usb_device *udev) | |
107 | { | |
108 | struct usb_host_config *config; | |
109 | int i; | |
110 | struct usb_interface *intf; | |
111 | - struct usb_driver *drv; | |
112 | ||
113 | config = udev->actconfig; | |
114 | if (config) { | |
115 | for (i = 0; i < config->desc.bNumInterfaces; ++i) { | |
116 | intf = config->interface[i]; | |
117 | - | |
118 | - if (intf->dev.driver) { | |
119 | - drv = to_usb_driver(intf->dev.driver); | |
120 | - if (!drv->suspend || !drv->resume) | |
121 | - usb_forced_unbind_intf(intf); | |
122 | - } | |
123 | + if (intf->needs_binding) | |
124 | + usb_rebind_intf(intf); | |
125 | } | |
126 | } | |
127 | } | |
128 | ||
129 | -/* Unbind drivers for @udev's interfaces that failed to support reset-resume. | |
130 | - * These interfaces have the needs_binding flag set by usb_resume_interface(). | |
131 | +/* | |
132 | + * Unbind all of @udev's marked interfaces and then rebind all of them. | |
133 | + * This ordering is necessary because some drivers claim several interfaces | |
134 | + * when they are first probed. | |
135 | * | |
136 | * The caller must hold @udev's device lock. | |
137 | */ | |
138 | -static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev) | |
139 | +void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev) | |
140 | { | |
141 | - struct usb_host_config *config; | |
142 | - int i; | |
143 | - struct usb_interface *intf; | |
144 | - | |
145 | - config = udev->actconfig; | |
146 | - if (config) { | |
147 | - for (i = 0; i < config->desc.bNumInterfaces; ++i) { | |
148 | - intf = config->interface[i]; | |
149 | - if (intf->dev.driver && intf->needs_binding) | |
150 | - usb_forced_unbind_intf(intf); | |
151 | - } | |
152 | - } | |
153 | + unbind_marked_interfaces(udev); | |
154 | + rebind_marked_interfaces(udev); | |
155 | } | |
156 | ||
157 | -static void do_rebind_interfaces(struct usb_device *udev) | |
158 | +#ifdef CONFIG_PM | |
159 | + | |
160 | +/* Unbind drivers for @udev's interfaces that don't support suspend/resume | |
161 | + * There is no check for reset_resume here because it can be determined | |
162 | + * only during resume whether reset_resume is needed. | |
163 | + * | |
164 | + * The caller must hold @udev's device lock. | |
165 | + */ | |
166 | +static void unbind_no_pm_drivers_interfaces(struct usb_device *udev) | |
167 | { | |
168 | struct usb_host_config *config; | |
169 | int i; | |
170 | struct usb_interface *intf; | |
171 | + struct usb_driver *drv; | |
172 | ||
173 | config = udev->actconfig; | |
174 | if (config) { | |
175 | for (i = 0; i < config->desc.bNumInterfaces; ++i) { | |
176 | intf = config->interface[i]; | |
177 | - if (intf->needs_binding) | |
178 | - usb_rebind_intf(intf); | |
179 | + | |
180 | + if (intf->dev.driver) { | |
181 | + drv = to_usb_driver(intf->dev.driver); | |
182 | + if (!drv->suspend || !drv->resume) | |
183 | + usb_forced_unbind_intf(intf); | |
184 | + } | |
185 | } | |
186 | } | |
187 | } | |
188 | @@ -1379,7 +1397,7 @@ int usb_resume_complete(struct device *d | |
189 | * whose needs_binding flag is set | |
190 | */ | |
191 | if (udev->state != USB_STATE_NOTATTACHED) | |
192 | - do_rebind_interfaces(udev); | |
193 | + rebind_marked_interfaces(udev); | |
194 | return 0; | |
195 | } | |
196 | ||
197 | @@ -1401,7 +1419,7 @@ int usb_resume(struct device *dev, pm_me | |
198 | pm_runtime_disable(dev); | |
199 | pm_runtime_set_active(dev); | |
200 | pm_runtime_enable(dev); | |
201 | - unbind_no_reset_resume_drivers_interfaces(udev); | |
202 | + unbind_marked_interfaces(udev); | |
203 | } | |
204 | ||
205 | /* Avoid PM error messages for devices disconnected while suspended | |
206 | --- a/drivers/usb/core/hub.c | |
207 | +++ b/drivers/usb/core/hub.c | |
208 | @@ -5252,10 +5252,11 @@ int usb_reset_device(struct usb_device * | |
209 | else if (cintf->condition == | |
210 | USB_INTERFACE_BOUND) | |
211 | rebind = 1; | |
212 | + if (rebind) | |
213 | + cintf->needs_binding = 1; | |
214 | } | |
215 | - if (ret == 0 && rebind) | |
216 | - usb_rebind_intf(cintf); | |
217 | } | |
218 | + usb_unbind_and_rebind_marked_interfaces(udev); | |
219 | } | |
220 | ||
221 | usb_autosuspend_device(udev); | |
222 | --- a/drivers/usb/core/usb.h | |
223 | +++ b/drivers/usb/core/usb.h | |
224 | @@ -55,7 +55,7 @@ extern int usb_match_one_id_intf(struct | |
225 | extern int usb_match_device(struct usb_device *dev, | |
226 | const struct usb_device_id *id); | |
227 | extern void usb_forced_unbind_intf(struct usb_interface *intf); | |
228 | -extern void usb_rebind_intf(struct usb_interface *intf); | |
229 | +extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev); | |
230 | ||
231 | extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port, | |
232 | struct dev_state *owner); |