aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2014-05-20 21:09:26 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-05-27 19:51:50 -0400
commit5c79a1e303363d46082408fd306cdea6d33013fc (patch)
tree5a5cf733987737405c656db033a37a080fc89233
parent097a155f05e88dc71184ceb93ad1aab1a13d1e41 (diff)
usb: introduce port status lock
In general we do not want khubd to act on port status changes that are the result of in progress resets or USB runtime PM operations. Specifically port power control testing has been able to trigger an unintended disconnect in hub_port_connect_change(), paraphrasing: if ((portstatus & USB_PORT_STAT_CONNECTION) && udev && udev->state != USB_STATE_NOTATTACHED) { if (portstatus & USB_PORT_STAT_ENABLE) { /* Nothing to do */ } else if (udev->state == USB_STATE_SUSPENDED && udev->persist_enabled) { ... } else { /* Don't resuscitate */; } } ...by falling to the "Don't resuscitate" path or missing USB_PORT_STAT_CONNECTION because usb_port_resume() was in the middle of modifying the port status. So, we want a new lock to hold off khubd for a given port while the child device is being suspended, resumed, or reset. The lock ordering rules are now usb_lock_device() => usb_lock_port(). This is mandated by the device core which may hold the device_lock on the usb_device before invoking usb_port_{suspend|resume} which in turn take the status_lock on the usb_port. We attempt to hold the status_lock for the duration of a port_event() run, and drop/re-acquire it when needing to take the device_lock. The lock is also dropped/re-acquired during hub_port_reconnect(). This patch also deletes hub->busy_bits as all use cases are now covered by port PM runtime synchronization or the port->status_lock and it pushes down usb_device_lock() into usb_remote_wakeup(). Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/core/hcd.c2
-rw-r--r--drivers/usb/core/hub.c97
-rw-r--r--drivers/usb/core/hub.h4
-rw-r--r--drivers/usb/core/port.c6
4 files changed, 72 insertions, 37 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index b81407518fdf..bec31e2efb88 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2267,9 +2267,7 @@ static void hcd_resume_work(struct work_struct *work)
2267 struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work); 2267 struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work);
2268 struct usb_device *udev = hcd->self.root_hub; 2268 struct usb_device *udev = hcd->self.root_hub;
2269 2269
2270 usb_lock_device(udev);
2271 usb_remote_wakeup(udev); 2270 usb_remote_wakeup(udev);
2272 usb_unlock_device(udev);
2273} 2271}
2274 2272
2275/** 2273/**
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 988f227e796f..d43054e8e257 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2781,6 +2781,20 @@ static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
2781 return ret; 2781 return ret;
2782} 2782}
2783 2783
2784static void usb_lock_port(struct usb_port *port_dev)
2785 __acquires(&port_dev->status_lock)
2786{
2787 mutex_lock(&port_dev->status_lock);
2788 __acquire(&port_dev->status_lock);
2789}
2790
2791static void usb_unlock_port(struct usb_port *port_dev)
2792 __releases(&port_dev->status_lock)
2793{
2794 mutex_unlock(&port_dev->status_lock);
2795 __release(&port_dev->status_lock);
2796}
2797
2784#ifdef CONFIG_PM 2798#ifdef CONFIG_PM
2785 2799
2786/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */ 2800/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */
@@ -3003,6 +3017,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
3003 int status; 3017 int status;
3004 bool really_suspend = true; 3018 bool really_suspend = true;
3005 3019
3020 usb_lock_port(port_dev);
3021
3006 /* enable remote wakeup when appropriate; this lets the device 3022 /* enable remote wakeup when appropriate; this lets the device
3007 * wake up the upstream hub (including maybe the root hub). 3023 * wake up the upstream hub (including maybe the root hub).
3008 * 3024 *
@@ -3096,6 +3112,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
3096 pm_runtime_put_sync(&port_dev->dev); 3112 pm_runtime_put_sync(&port_dev->dev);
3097 3113
3098 usb_mark_last_busy(hub->hdev); 3114 usb_mark_last_busy(hub->hdev);
3115
3116 usb_unlock_port(port_dev);
3099 return status; 3117 return status;
3100} 3118}
3101 3119
@@ -3244,13 +3262,13 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
3244 } 3262 }
3245 } 3263 }
3246 3264
3265 usb_lock_port(port_dev);
3266
3247 /* Skip the initial Clear-Suspend step for a remote wakeup */ 3267 /* Skip the initial Clear-Suspend step for a remote wakeup */
3248 status = hub_port_status(hub, port1, &portstatus, &portchange); 3268 status = hub_port_status(hub, port1, &portstatus, &portchange);
3249 if (status == 0 && !port_is_suspended(hub, portstatus)) 3269 if (status == 0 && !port_is_suspended(hub, portstatus))
3250 goto SuspendCleared; 3270 goto SuspendCleared;
3251 3271
3252 set_bit(port1, hub->busy_bits);
3253
3254 /* see 7.1.7.7; affects power usage, but not budgeting */ 3272 /* see 7.1.7.7; affects power usage, but not budgeting */
3255 if (hub_is_superspeed(hub->hdev)) 3273 if (hub_is_superspeed(hub->hdev))
3256 status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U0); 3274 status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U0);
@@ -3289,8 +3307,6 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
3289 } 3307 }
3290 } 3308 }
3291 3309
3292 clear_bit(port1, hub->busy_bits);
3293
3294 status = check_port_resume_type(udev, 3310 status = check_port_resume_type(udev,
3295 hub, port1, status, portchange, portstatus); 3311 hub, port1, status, portchange, portstatus);
3296 if (status == 0) 3312 if (status == 0)
@@ -3308,16 +3324,18 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
3308 usb_unlocked_enable_lpm(udev); 3324 usb_unlocked_enable_lpm(udev);
3309 } 3325 }
3310 3326
3327 usb_unlock_port(port_dev);
3328
3311 return status; 3329 return status;
3312} 3330}
3313 3331
3314#ifdef CONFIG_PM_RUNTIME 3332#ifdef CONFIG_PM_RUNTIME
3315 3333
3316/* caller has locked udev */
3317int usb_remote_wakeup(struct usb_device *udev) 3334int usb_remote_wakeup(struct usb_device *udev)
3318{ 3335{
3319 int status = 0; 3336 int status = 0;
3320 3337
3338 usb_lock_device(udev);
3321 if (udev->state == USB_STATE_SUSPENDED) { 3339 if (udev->state == USB_STATE_SUSPENDED) {
3322 dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); 3340 dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
3323 status = usb_autoresume_device(udev); 3341 status = usb_autoresume_device(udev);
@@ -3326,6 +3344,7 @@ int usb_remote_wakeup(struct usb_device *udev)
3326 usb_autosuspend_device(udev); 3344 usb_autosuspend_device(udev);
3327 } 3345 }
3328 } 3346 }
3347 usb_unlock_device(udev);
3329 return status; 3348 return status;
3330} 3349}
3331 3350
@@ -4030,9 +4049,10 @@ static int hub_enable_device(struct usb_device *udev)
4030 * Returns device in USB_STATE_ADDRESS, except on error. 4049 * Returns device in USB_STATE_ADDRESS, except on error.
4031 * 4050 *
4032 * If this is called for an already-existing device (as part of 4051 * If this is called for an already-existing device (as part of
4033 * usb_reset_and_verify_device), the caller must own the device lock. For a 4052 * usb_reset_and_verify_device), the caller must own the device lock and
4034 * newly detected device that is not accessible through any global 4053 * the port lock. For a newly detected device that is not accessible
4035 * pointers, it's not necessary to lock the device. 4054 * through any global pointers, it's not necessary to lock the device,
4055 * but it is still necessary to lock the port.
4036 */ 4056 */
4037static int 4057static int
4038hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, 4058hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
@@ -4502,7 +4522,9 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
4502 } 4522 }
4503 4523
4504 /* reset (non-USB 3.0 devices) and get descriptor */ 4524 /* reset (non-USB 3.0 devices) and get descriptor */
4525 usb_lock_port(port_dev);
4505 status = hub_port_init(hub, udev, port1, i); 4526 status = hub_port_init(hub, udev, port1, i);
4527 usb_unlock_port(port_dev);
4506 if (status < 0) 4528 if (status < 0)
4507 goto loop; 4529 goto loop;
4508 4530
@@ -4624,6 +4646,7 @@ done:
4624 */ 4646 */
4625static void hub_port_connect_change(struct usb_hub *hub, int port1, 4647static void hub_port_connect_change(struct usb_hub *hub, int port1,
4626 u16 portstatus, u16 portchange) 4648 u16 portstatus, u16 portchange)
4649 __must_hold(&port_dev->status_lock)
4627{ 4650{
4628 struct usb_port *port_dev = hub->ports[port1 - 1]; 4651 struct usb_port *port_dev = hub->ports[port1 - 1];
4629 struct usb_device *udev = port_dev->child; 4652 struct usb_device *udev = port_dev->child;
@@ -4655,26 +4678,29 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
4655 /* For a suspended device, treat this as a 4678 /* For a suspended device, treat this as a
4656 * remote wakeup event. 4679 * remote wakeup event.
4657 */ 4680 */
4658 usb_lock_device(udev); 4681 usb_unlock_port(port_dev);
4659 status = usb_remote_wakeup(udev); 4682 status = usb_remote_wakeup(udev);
4660 usb_unlock_device(udev); 4683 usb_lock_port(port_dev);
4661#endif 4684#endif
4662 } else { 4685