aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2010-06-09 17:34:17 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-08-10 17:35:33 -0400
commit6d88e6792574497bfac9a81403cc47712040636f (patch)
treeecd65e7b2c3c0f90f7a5e39313a3fb2850b2fdb8
parent96e077ae347912dfce0e93f5958efc3ed6f311f4 (diff)
USB: don't stop root-hub status polls too soon
This patch (as1390) fixes a problem that crops up when a UHCI host controller is unbound from uhci-hcd while there are still some active URBs. The URBs have to be unlinked when the root hub is unregistered, and uhci-hcd relies upon root-hub status polls as part of its unlinking procedure. But usb_hcd_poll_rh_status() won't make those status calls if hcd->rh_registered is clear, and the flag is cleared _before_ the unregistration takes place. Since hcd->rh_registered is used for other things and needs to be cleared early, the solution is to add a new flag (rh_pollable) and use it instead. It gets cleared _after_ the root hub is unregistered. Now that the status polls don't end too soon, we have to make sure they also don't occur too late -- after the root hub's usb_device structure or the HCD's private structures are deallocated. Therefore the patch adds usb_get_device() and usb_put_device() calls to protect the root hub structure, and it adds an extra del_timer_sync() to prevent the root-hub timer from causing an unexpected status poll. This additional complexity would not be needed if the HCD framework had provided separate stop() and release() callbacks instead of just stop(). This lack could be fixed at some future time (although it would require changes to every host controller driver); when that happens this patch won't be needed any more. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/core/hcd.c32
-rw-r--r--include/linux/usb/hcd.h1
2 files changed, 26 insertions, 7 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index caae4625a1f1..53f14c82ff2e 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -667,7 +667,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
667 unsigned long flags; 667 unsigned long flags;
668 char buffer[6]; /* Any root hubs with > 31 ports? */ 668 char buffer[6]; /* Any root hubs with > 31 ports? */
669 669
670 if (unlikely(!hcd->rh_registered)) 670 if (unlikely(!hcd->rh_pollable))
671 return; 671 return;
672 if (!hcd->uses_new_polling && !hcd->status_urb) 672 if (!hcd->uses_new_polling && !hcd->status_urb)
673 return; 673 return;
@@ -2217,6 +2217,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
2217 retval = -ENOMEM; 2217 retval = -ENOMEM;
2218 goto err_allocate_root_hub; 2218 goto err_allocate_root_hub;
2219 } 2219 }
2220 hcd->self.root_hub = rhdev;
2220 2221
2221 switch (hcd->driver->flags & HCD_MASK) { 2222 switch (hcd->driver->flags & HCD_MASK) {
2222 case HCD_USB11: 2223 case HCD_USB11:
@@ -2231,7 +2232,6 @@ int usb_add_hcd(struct usb_hcd *hcd,
2231 default: 2232 default:
2232 goto err_set_rh_speed; 2233 goto err_set_rh_speed;
2233 } 2234 }
2234 hcd->self.root_hub = rhdev;
2235 2235
2236 /* wakeup flag init defaults to "everything works" for root hubs, 2236 /* wakeup flag init defaults to "everything works" for root hubs,
2237 * but drivers can override it in reset() if needed, along with 2237 * but drivers can override it in reset() if needed, along with
@@ -2246,6 +2246,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
2246 dev_err(hcd->self.controller, "can't setup\n"); 2246 dev_err(hcd->self.controller, "can't setup\n");
2247 goto err_hcd_driver_setup; 2247 goto err_hcd_driver_setup;
2248 } 2248 }
2249 hcd->rh_pollable = 1;
2249 2250
2250 /* NOTE: root hub and controller capabilities may not be the same */ 2251 /* NOTE: root hub and controller capabilities may not be the same */
2251 if (device_can_wakeup(hcd->self.controller) 2252 if (device_can_wakeup(hcd->self.controller)
@@ -2315,9 +2316,12 @@ error_create_attr_group:
2315 cancel_work_sync(&hcd->wakeup_work); 2316 cancel_work_sync(&hcd->wakeup_work);
2316#endif 2317#endif
2317 mutex_lock(&usb_bus_list_lock); 2318 mutex_lock(&usb_bus_list_lock);
2318 usb_disconnect(&hcd->self.root_hub); 2319 usb_disconnect(&rhdev); /* Sets rhdev to NULL */
2319 mutex_unlock(&usb_bus_list_lock); 2320 mutex_unlock(&usb_bus_list_lock);
2320err_register_root_hub: 2321err_register_root_hub:
2322 hcd->rh_pollable = 0;
2323 hcd->poll_rh = 0;
2324 del_timer_sync(&hcd->rh_timer);
2321 hcd->driver->stop(hcd); 2325 hcd->driver->stop(hcd);
2322 hcd->state = HC_STATE_HALT; 2326 hcd->state = HC_STATE_HALT;
2323 hcd->poll_rh = 0; 2327 hcd->poll_rh = 0;
@@ -2328,8 +2332,7 @@ err_hcd_driver_start:
2328err_request_irq: 2332err_request_irq:
2329err_hcd_driver_setup: 2333err_hcd_driver_setup:
2330err_set_rh_speed: 2334err_set_rh_speed:
2331 hcd->self.root_hub = NULL; 2335 usb_put_dev(hcd->self.root_hub);
2332 usb_put_dev(rhdev);
2333err_allocate_root_hub: 2336err_allocate_root_hub:
2334 usb_deregister_bus(&hcd->self); 2337 usb_deregister_bus(&hcd->self);
2335err_register_bus: 2338err_register_bus:
@@ -2348,9 +2351,12 @@ EXPORT_SYMBOL_GPL(usb_add_hcd);
2348 */ 2351 */
2349void usb_remove_hcd(struct usb_hcd *hcd) 2352void usb_remove_hcd(struct usb_hcd *hcd)
2350{ 2353{
2354 struct usb_device *rhdev = hcd->self.root_hub;
2355
2351 dev_info(hcd->self.controller, "remove, state %x\n", hcd->state); 2356 dev_info(hcd->self.controller, "remove, state %x\n", hcd->state);
2352 2357
2353 sysfs_remove_group(&hcd->self.root_hub->dev.kobj, &usb_bus_attr_group); 2358 usb_get_dev(rhdev);
2359 sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group);
2354 2360
2355 if (HC_IS_RUNNING (hcd->state)) 2361 if (HC_IS_RUNNING (hcd->state))
2356 hcd->state = HC_STATE_QUIESCING; 2362 hcd->state = HC_STATE_QUIESCING;
@@ -2365,17 +2371,29 @@ void usb_remove_hcd(struct usb_hcd *hcd)
2365#endif 2371#endif
2366 2372
2367 mutex_lock(&usb_bus_list_lock); 2373 mutex_lock(&usb_bus_list_lock);
2368 usb_disconnect(&hcd->self.root_hub); 2374 usb_disconnect(&rhdev); /* Sets rhdev to NULL */
2369 mutex_unlock(&usb_bus_list_lock); 2375 mutex_unlock(&usb_bus_list_lock);
2370 2376
2377 /* Prevent any more root-hub status calls from the timer.
2378 * The HCD might still restart the timer (if a port status change
2379 * interrupt occurs), but usb_hcd_poll_rh_status() won't invoke
2380 * the hub_status_data() callback.
2381 */
2382 hcd->rh_pollable = 0;
2383 hcd->poll_rh = 0;
2384 del_timer_sync(&hcd->rh_timer);
2385
2371 hcd->driver->stop(hcd); 2386 hcd->driver->stop(hcd);
2372 hcd->state = HC_STATE_HALT; 2387 hcd->state = HC_STATE_HALT;
2373 2388
2389 /* In case the HCD restarted the timer, stop it again. */
2374 hcd->poll_rh = 0; 2390 hcd->poll_rh = 0;
2375 del_timer_sync(&hcd->rh_timer); 2391 del_timer_sync(&hcd->rh_timer);
2376 2392
2377 if (hcd->irq >= 0) 2393 if (hcd->irq >= 0)
2378 free_irq(hcd->irq, hcd); 2394 free_irq(hcd->irq, hcd);
2395
2396 usb_put_dev(hcd->self.root_hub);
2379 usb_deregister_bus(&hcd->self); 2397 usb_deregister_bus(&hcd->self);
2380 hcd_buffer_destroy(hcd); 2398 hcd_buffer_destroy(hcd);
2381} 2399}
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 2e3a4ea1a3da..11b638195901 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -95,6 +95,7 @@ struct usb_hcd {
95#define HCD_FLAG_SAW_IRQ 0x00000002 95#define HCD_FLAG_SAW_IRQ 0x00000002
96 96
97 unsigned rh_registered:1;/* is root hub registered? */ 97 unsigned rh_registered:1;/* is root hub registered? */
98 unsigned rh_pollable:1; /* may we poll the root hub? */
98 99
99 /* The next flag is a stopgap, to be removed when all the HCDs 100 /* The next flag is a stopgap, to be removed when all the HCDs
100 * support the new root-hub polling mechanism. */ 101 * support the new root-hub polling mechanism. */