[RFC PATCH 3/4] usb: reset resume in khubd context

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux