summaryrefslogtreecommitdiff
path: root/drivers/usb/core/hub.h
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/hub.h
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>
Diffstat (limited to 'drivers/usb/core/hub.h')
-rw-r--r--drivers/usb/core/hub.h4
1 files changed, 2 insertions, 2 deletions
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 906c355e0631..0a7cdc0ef0a9 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -45,8 +45,6 @@ struct usb_hub {
unsigned long event_bits[1]; /* status change bitmask */
unsigned long change_bits[1]; /* ports with logical connect
status change */
- unsigned long busy_bits[1]; /* ports being reset or
- resumed */
unsigned long removed_bits[1]; /* ports with a "removed"
device present */
unsigned long wakeup_bits[1]; /* ports that have signaled
@@ -88,6 +86,7 @@ struct usb_hub {
* @peer: related usb2 and usb3 ports (share the same connector)
* @connect_type: port's connect type
* @location: opaque representation of platform connector location
+ * @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
* @portnum: port index num based one
* @is_superspeed cache super-speed status
*/
@@ -98,6 +97,7 @@ struct usb_port {
struct usb_port *peer;
enum usb_port_connect_type connect_type;
usb_port_location_t location;
+ struct mutex status_lock;
u8 portnum;
unsigned int is_superspeed:1;
};