Preparation patch for synchronizing port warm reset recovery with khubd
and child device resume recovery.  End goal is to ensure khubd does not
run while port recovery in-progress (as the port is known to appear
disconnected), and also arrange for any optional warm resets to
complete before usb_port_resume() runs.

Signed-off-by: Dan Williams <dan.j.willi...@intel.com>
---
 drivers/usb/core/generic.c |   23 ++++++++++++++++++++---
 drivers/usb/core/hub.c     |   11 +++++------
 drivers/usb/core/usb.c     |    1 +
 drivers/usb/core/usb.h     |    6 ++++++
 include/linux/usb.h        |    3 +++
 5 files changed, 35 insertions(+), 9 deletions(-)

diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index acbfeb0a0119..2925db4e3859 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -216,6 +216,16 @@ static int generic_suspend(struct usb_device *udev, 
pm_message_t msg)
        return rc;
 }
 
+/* port resume runs in khubd context to coalesce port recovery
+ * actions and synchronize with hub_events
+ */
+void usb_port_resume_work(struct work_struct *w)
+{
+       struct usb_device *udev = container_of(w, typeof(*udev), resume_work);
+
+       udev->pm_result = usb_port_resume(udev, udev->pm_msg);
+}
+
 static int generic_resume(struct usb_device *udev, pm_message_t msg)
 {
        int rc;
@@ -225,10 +235,17 @@ static int generic_resume(struct usb_device *udev, 
pm_message_t msg)
         * so we have to start up their downstream HC-to-USB
         * interfaces manually by doing a bus (or "global") resume.
         */
-       if (!udev->parent)
+       if (!udev->parent) {
                rc = hcd_bus_resume(udev, msg);
-       else
-               rc = usb_port_resume(udev, msg);
+       } else {
+               /* See: usb_port_resume_work */
+               udev->pm_msg = msg;
+               usb_kick_resume(udev);
+               flush_work(&udev->resume_work);
+
+               rc = udev->pm_result;
+       }
+
        return rc;
 }
 
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 83abaecfabfe..e7b2efef537d 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -585,6 +585,11 @@ void usb_kick_khubd(struct usb_device *hdev)
                kick_khubd(hub);
 }
 
+void usb_kick_resume(struct usb_device *dev)
+{
+       queue_work(khubd_wq, &dev->resume_work);
+}
+
 /*
  * Let the USB core know that a USB 3.0 device has sent a Function Wake Device
  * Notification, which indicates it had initiated remote wakeup.
@@ -3186,10 +3191,6 @@ int usb_port_resume(struct usb_device *udev, 
pm_message_t msg)
        if (status == 0 && !port_is_suspended(hub, portstatus))
                goto SuspendCleared;
 
-       /* dev_dbg(hub->intfdev, "resume port %d\n", port1); */
-
-       set_bit(port1, hub->busy_bits);
-
        /* see 7.1.7.7; affects power usage, but not budgeting */
        if (hub_is_superspeed(hub->hdev))
                status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U0);
@@ -3229,8 +3230,6 @@ int usb_port_resume(struct usb_device *udev, pm_message_t 
msg)
                }
        }
 
-       clear_bit(port1, hub->busy_bits);
-
        status = check_port_resume_type(udev,
                        hub, port1, status, portchange, portstatus);
        if (status == 0)
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 4d1144990d4c..def10e4e1e0e 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -436,6 +436,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
        atomic_set(&dev->urbnum, 0);
 
        INIT_LIST_HEAD(&dev->ep0.urb_list);
+       INIT_WORK(&dev->resume_work, usb_port_resume_work);
        dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
        dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
        /* ep0 maxpacket comes later, from device descriptor */
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index c49383669cd8..ea060cce6a0c 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -50,6 +50,7 @@ static inline unsigned usb_get_max_power(struct usb_device 
*udev,
 }
 
 extern void usb_kick_khubd(struct usb_device *dev);
+extern void usb_kick_resume(struct usb_device *dev);
 extern int usb_match_one_id_intf(struct usb_device *dev,
                                 struct usb_host_interface *intf,
                                 const struct usb_device_id *id);
@@ -79,6 +80,7 @@ extern int usb_resume_complete(struct device *dev);
 
 extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg);
 extern int usb_port_resume(struct usb_device *dev, pm_message_t msg);
+extern void usb_port_resume_work(struct work_struct *w);
 
 #else
 
@@ -92,6 +94,10 @@ static inline int usb_port_resume(struct usb_device *udev, 
pm_message_t msg)
        return 0;
 }
 
+static inline void usb_port_resume_work(struct work_struct *w)
+{
+}
+
 #endif
 
 #ifdef CONFIG_PM_RUNTIME
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 851917571a9e..fecd02495269 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -536,6 +536,9 @@ struct usb_device {
        char **rawdescriptors;
 
        unsigned short bus_mA;
+       struct work_struct resume_work;
+       pm_message_t pm_msg;
+       int pm_result;
        u8 portnum;
        u8 level;
 

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to