usb: make usb_port flags atomic, rename did_runtime_put to child_usage
authorDan Williams <dan.j.williams@intel.com>
Wed, 21 May 2014 01:08:52 +0000 (18:08 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 27 May 2014 23:38:53 +0000 (16:38 -0700)
We want to manipulate ->did_runtime_put in usb_port_runtime_resume(),
but we don't want that to collide with other updates.  Move usb_port
flags to new port-bitmap fields in usb_hub. "did_runtime_put" is renamed
"child_usage_bits" to reflect that it is strictly standing in for the
fact that usb_devices are not the device_model children of their parent
port.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/core/hub.c
drivers/usb/core/hub.h
drivers/usb/core/port.c

index 5a909ba6fb676f8784cc65a94f9bf400d1057d25..31a492a4a881195f1e5dfa4cc010edb5c44056b5 100644 (file)
@@ -751,16 +751,20 @@ int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
                           int port1, bool set)
 {
        int ret;
-       struct usb_port *port_dev = hub->ports[port1 - 1];
 
        if (set)
                ret = set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
        else
                ret = usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
-       if (!ret)
-               port_dev->power_is_on = set;
-       return ret;
+       if (ret)
+               return ret;
+
+       if (set)
+               set_bit(port1, hub->power_bits);
+       else
+               clear_bit(port1, hub->power_bits);
+       return 0;
 }
 
 /**
@@ -839,7 +843,7 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
                dev_dbg(hub->intfdev, "trying to enable port power on "
                                "non-switchable hub\n");
        for (port1 = 1; port1 <= hub->hdev->maxchild; port1++)
-               if (hub->ports[port1 - 1]->power_is_on)
+               if (test_bit(port1, hub->power_bits))
                        set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
                else
                        usb_clear_port_feature(hub->hdev, port1,
@@ -1180,15 +1184,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                                set_bit(port1, hub->change_bits);
 
                } else if (udev->persist_enabled) {
-                       struct usb_port *port_dev = hub->ports[port1 - 1];
-
 #ifdef CONFIG_PM
                        udev->reset_resume = 1;
 #endif
                        /* Don't set the change_bits when the device
                         * was powered off.
                         */
-                       if (port_dev->power_is_on)
+                       if (test_bit(port1, hub->power_bits))
                                set_bit(port1, hub->change_bits);
 
                } else {
@@ -2096,16 +2098,15 @@ void usb_disconnect(struct usb_device **pdev)
        usb_hcd_synchronize_unlinks(udev);
 
        if (udev->parent) {
+               int port1 = udev->portnum;
                struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
-               struct usb_port *port_dev = hub->ports[udev->portnum - 1];
+               struct usb_port *port_dev = hub->ports[port1 - 1];
 
                sysfs_remove_link(&udev->dev.kobj, "port");
                sysfs_remove_link(&port_dev->dev.kobj, "device");
 
-               if (!port_dev->did_runtime_put)
+               if (test_and_clear_bit(port1, hub->child_usage_bits))
                        pm_runtime_put(&port_dev->dev);
-               else
-                       port_dev->did_runtime_put = false;
        }
 
        usb_remove_ep_devs(&udev->ep0);
@@ -2416,7 +2417,8 @@ int usb_new_device(struct usb_device *udev)
        /* Create link files between child device and usb port device. */
        if (udev->parent) {
                struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
-               struct usb_port *port_dev = hub->ports[udev->portnum - 1];
+               int port1 = udev->portnum;
+               struct usb_port *port_dev = hub->ports[port1 - 1];
 
                err = sysfs_create_link(&udev->dev.kobj,
                                &port_dev->dev.kobj, "port");
@@ -2430,7 +2432,8 @@ int usb_new_device(struct usb_device *udev)
                        goto fail;
                }
 
-               pm_runtime_get_sync(&port_dev->dev);
+               if (!test_and_set_bit(port1, hub->child_usage_bits))
+                       pm_runtime_get_sync(&port_dev->dev);
        }
 
        (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
@@ -3100,10 +3103,9 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                usb_set_device_state(udev, USB_STATE_SUSPENDED);
        }
 
-       if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled) {
+       if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled
+                       && test_and_clear_bit(port1, hub->child_usage_bits))
                pm_runtime_put_sync(&port_dev->dev);
-               port_dev->did_runtime_put = true;
-       }
 
        usb_mark_last_busy(hub->hdev);
        return status;
@@ -3245,9 +3247,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
        int             status;
        u16             portchange, portstatus;
 
-       if (port_dev->did_runtime_put) {
+       if (!test_and_set_bit(port1, hub->child_usage_bits)) {
                status = pm_runtime_get_sync(&port_dev->dev);
-               port_dev->did_runtime_put = false;
                if (status < 0) {
                        dev_dbg(&udev->dev, "can't resume usb port, status %d\n",
                                        status);
index 048c797f394cb42930a8b6d12d58c38da6c01a6c..3ef1c2e435ccd91526035dc97881866250d12e42 100644 (file)
@@ -51,6 +51,9 @@ struct usb_hub {
                                                        device present */
        unsigned long           wakeup_bits[1]; /* ports that have signaled
                                                        remote wakeup */
+       unsigned long           power_bits[1]; /* ports that are powered */
+       unsigned long           child_usage_bits[1]; /* ports powered on for
+                                                       children */
 #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
 #error event_bits[] is too short!
 #endif
@@ -86,8 +89,6 @@ struct usb_hub {
  * @connect_type: port's connect type
  * @location: opaque representation of platform connector location
  * @portnum: port index num based one
- * @power_is_on: port's power state
- * @did_runtime_put: port has done pm_runtime_put().
  */
 struct usb_port {
        struct usb_device *child;
@@ -97,8 +98,6 @@ struct usb_port {
        enum usb_port_connect_type connect_type;
        usb_port_location_t location;
        u8 portnum;
-       unsigned power_is_on:1;
-       unsigned did_runtime_put:1;
 };
 
 #define to_usb_port(_dev) \
index 40c3ac173e9ed284628781676001716344dcb2b7..795778c71e314a32897ed7f853e478a67b63d912 100644 (file)
@@ -82,7 +82,7 @@ static int usb_port_runtime_resume(struct device *dev)
        if (!hub)
                return -EINVAL;
        if (hub->in_reset) {
-               port_dev->power_is_on = 1;
+               set_bit(port1, hub->power_bits);
                return 0;
        }
 
@@ -320,7 +320,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
 
        hub->ports[port1 - 1] = port_dev;
        port_dev->portnum = port1;
-       port_dev->power_is_on = true;
+       set_bit(port1, hub->power_bits);
        port_dev->dev.parent = hub->intfdev;
        port_dev->dev.groups = port_dev_group;
        port_dev->dev.type = &usb_port_device_type;