summaryrefslogtreecommitdiffstats
path: root/drivers/usb/core/port.c
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2014-05-20 18:09:26 -0700
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-05-27 16:51:50 -0700
commit5c79a1e303363d46082408fd306cdea6d33013fc (patch)
tree5a5cf733987737405c656db033a37a080fc89233 /drivers/usb/core/port.c
parent097a155f05e88dc71184ceb93ad1aab1a13d1e41 (diff)
downloadblackbird-op-linux-5c79a1e303363d46082408fd306cdea6d33013fc.tar.gz
blackbird-op-linux-5c79a1e303363d46082408fd306cdea6d33013fc.zip
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>
Diffstat (limited to 'drivers/usb/core/port.c')
-rw-r--r--drivers/usb/core/port.c6
1 files changed, 1 insertions, 5 deletions
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index fb83c2c13920..8b1655700104 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -95,8 +95,6 @@ static int usb_port_runtime_resume(struct device *dev)
pm_runtime_get_sync(&peer->dev);
usb_autopm_get_interface(intf);
- set_bit(port1, hub->busy_bits);
-
retval = usb_hub_set_port_power(hdev, hub, port1, true);
msleep(hub_power_on_good_delay(hub));
if (port_dev->child && !retval) {
@@ -113,7 +111,6 @@ static int usb_port_runtime_resume(struct device *dev)
retval = 0;
}
- clear_bit(port1, hub->busy_bits);
usb_autopm_put_interface(intf);
return retval;
@@ -139,12 +136,10 @@ static int usb_port_runtime_suspend(struct device *dev)
return -EAGAIN;
usb_autopm_get_interface(intf);
- set_bit(port1, hub->busy_bits);
retval = usb_hub_set_port_power(hdev, hub, port1, false);
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
if (!port_dev->is_superspeed)
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
- clear_bit(port1, hub->busy_bits);
usb_autopm_put_interface(intf);
/*
@@ -400,6 +395,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
port_dev->is_superspeed = 1;
dev_set_name(&port_dev->dev, "%s-port%d", dev_name(&hub->hdev->dev),
port1);
+ mutex_init(&port_dev->status_lock);
retval = device_register(&port_dev->dev);
if (retval)
goto error_register;
OpenPOWER on IntegriCloud