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.williams@xxxxxxxxx> --- 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html