[RFC 10/15] usb: Make core allocate resources per PCI-device.

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

 



Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd).  This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.

Only one pointer can be stored in the PCI device's private pointer, so
designate one roothub as the "main" hcd.  For xHCI, this will always be
the USB 3.0 roothub.  Introduce usb_hcd_is_main_hcd(), a way to check
whether a usb_hcd structure pointer matches the one stored in the
hcd->self.controller device's private pointer.

Make sure that only one interrupt is requested for the whole PCI device,
by using usb_hcd_is_main_hcd().

Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs.  It will act just like usb_create_hcd() did if the
shared_hcd pointer argument is NULL.  If it is passed a non-NULL
shared_hcd pointer, it sets usb_hcd->shared_hcd, so each roothub can
access the other partner in the pair.

The bandwidth_mutex needs to be shared across both xHCI roothubs, so make
usb_create_shared_hcd() only allocate the mutex once.  When it is passed a
valid shared_hcd pointer, it stores the mutex pointer from the partner in
the new roothub.  The bandwidth mutex is only deallocated when the
shared_hcd pointer is NULL.  The first shared roothub that is released
will set shared_hcd to NULL.  The second shared roothub that is released
(or a USB host controller driver that doesn't set up shared roothubs) will
deallocate the bandwidth mutex.

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
---
 drivers/usb/core/hcd.c  |   86 +++++++++++++++++++++++++++++++++++++----------
 include/linux/usb/hcd.h |    5 +++
 2 files changed, 73 insertions(+), 18 deletions(-)

diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 6883fe9..091da8e 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2130,10 +2130,14 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
 /*-------------------------------------------------------------------------*/
 
 /**
- * usb_create_hcd - create and initialize an HCD structure
+ * usb_create_shared_hcd - create and initialize an HCD structure
  * @driver: HC driver that will use this hcd
  * @dev: device for this HC, stored in hcd->self.controller
  * @bus_name: value to store in hcd->self.bus_name
+ * @shared_hcd: a pointer to the usb_hcd structure that is sharing the
+ *              PCI device.  Used for allocating two usb_hcd structures
+ *              under one xHCI host controller PCI device (a USB 2.0 bus
+ *              and a USB 3.0 bus).
  * Context: !in_interrupt()
  *
  * Allocate a struct usb_hcd, with extra space at the end for the
@@ -2142,8 +2146,9 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
  *
  * If memory is unavailable, returns NULL.
  */
-struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
-		struct device *dev, const char *bus_name)
+struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
+		struct device *dev, const char *bus_name,
+		struct usb_hcd *shared_hcd)
 {
 	struct usb_hcd *hcd;
 
@@ -2152,16 +2157,23 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
 		dev_dbg (dev, "hcd alloc failed\n");
 		return NULL;
 	}
-	hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),
-			GFP_KERNEL);
-	if (!hcd->bandwidth_mutex) {
-		kfree(hcd);
-		dev_dbg(dev, "hcd bandwidth mutex alloc failed\n");
-		return NULL;
+	if (shared_hcd == NULL) {
+		hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),
+				GFP_KERNEL);
+		if (!hcd->bandwidth_mutex) {
+			kfree(hcd);
+			dev_dbg(dev, "hcd bandwidth mutex alloc failed\n");
+			return NULL;
+		}
+		mutex_init(hcd->bandwidth_mutex);
+		dev_set_drvdata(dev, hcd);
+	} else {
+		hcd->bandwidth_mutex = shared_hcd->bandwidth_mutex;
+		/* Don't overwrite the device data if we're sharing usb_hcds */
+		hcd->shared_hcd = shared_hcd;
+		shared_hcd->shared_hcd = hcd;
 	}
-	mutex_init(hcd->bandwidth_mutex);
 
-	dev_set_drvdata(dev, hcd);
 	kref_init(&hcd->kref);
 
 	usb_bus_init(&hcd->self);
@@ -2181,13 +2193,41 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
 			"USB Host Controller";
 	return hcd;
 }
+EXPORT_SYMBOL_GPL(usb_create_shared_hcd);
+
+/**
+ * usb_create_hcd - create and initialize an HCD structure
+ * @driver: HC driver that will use this hcd
+ * @dev: device for this HC, stored in hcd->self.controller
+ * @bus_name: value to store in hcd->self.bus_name
+ * @shared_hcd: a pointer to the usb_hcd structure that is sharing the
+ *              PCI device.  Used for allocating two usb_hcd structures
+ *              under one xHCI host controller PCI device (a USB 2.0 bus
+ *              and a USB 3.0 bus).
+ * Context: !in_interrupt()
+ *
+ * Allocate a struct usb_hcd, with extra space at the end for the
+ * HC driver's private data.  Initialize the generic members of the
+ * hcd structure.
+ *
+ * If memory is unavailable, returns NULL.
+ */
+struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
+		struct device *dev, const char *bus_name)
+{
+	return usb_create_shared_hcd(driver, dev, bus_name, NULL);
+}
 EXPORT_SYMBOL_GPL(usb_create_hcd);
 
 static void hcd_release (struct kref *kref)
 {
 	struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
 
-	kfree(hcd->bandwidth_mutex);
+	/* Last freed roothub frees the shared bandwidth mutex */
+	if (hcd->shared_hcd)
+		hcd->shared_hcd->shared_hcd = NULL;
+	else
+		kfree(hcd->bandwidth_mutex);
 	kfree(hcd);
 }
 
@@ -2206,6 +2246,12 @@ void usb_put_hcd (struct usb_hcd *hcd)
 }
 EXPORT_SYMBOL_GPL(usb_put_hcd);
 
+inline int usb_hcd_is_main_hcd(struct usb_hcd *hcd)
+{
+	return hcd == dev_get_drvdata(hcd->self.controller);
+}
+EXPORT_SYMBOL_GPL(usb_hcd_is_main_hcd);
+
 static int usb_hcd_request_irqs(struct usb_hcd *hcd,
 		unsigned int irqnum, unsigned long irqflags)
 {
@@ -2319,9 +2365,11 @@ int usb_add_hcd(struct usb_hcd *hcd,
 		dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
 
 	/* enable irqs just before we start the controller */
-	retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
-	if (retval)
-		goto err_request_irq;
+	if (usb_hcd_is_main_hcd(hcd)) {
+		retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
+		if (retval)
+			goto err_request_irq;
+	}
 
 	if ((retval = hcd->driver->start(hcd)) < 0) {
 		dev_err(hcd->self.controller, "startup error %d\n", retval);
@@ -2365,7 +2413,7 @@ err_register_root_hub:
 	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
 	del_timer_sync(&hcd->rh_timer);
 err_hcd_driver_start:
-	if (hcd->irq >= 0)
+	if (usb_hcd_is_main_hcd(hcd) && hcd->irq >= 0)
 		free_irq(irqnum, hcd);
 err_request_irq:
 err_hcd_driver_setup:
@@ -2428,8 +2476,10 @@ void usb_remove_hcd(struct usb_hcd *hcd)
 	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
 	del_timer_sync(&hcd->rh_timer);
 
-	if (hcd->irq >= 0)
-		free_irq(hcd->irq, hcd);
+	if (usb_hcd_is_main_hcd(hcd)) {
+		if (hcd->irq >= 0)
+			free_irq(hcd->irq, hcd);
+	}
 
 	usb_put_dev(hcd->self.root_hub);
 	usb_deregister_bus(&hcd->self);
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 5a29e4e..e7de392 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -138,6 +138,7 @@ struct usb_hcd {
 	 * to the device, or resetting the bandwidth after a failed attempt.
 	 */
 	struct mutex		*bandwidth_mutex;
+	struct usb_hcd		*shared_hcd;
 
 
 #define HCD_BUFFER_POOLS	4
@@ -346,8 +347,12 @@ extern int usb_hcd_get_frame_number(struct usb_device *udev);
 
 extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
 		struct device *dev, const char *bus_name);
+extern struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
+		struct device *dev, const char *bus_name,
+		struct usb_hcd *shared_hcd);
 extern struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd);
 extern void usb_put_hcd(struct usb_hcd *hcd);
+extern inline int usb_hcd_is_main_hcd(struct usb_hcd *hcd);
 extern int usb_add_hcd(struct usb_hcd *hcd,
 		unsigned int irqnum, unsigned long irqflags);
 extern void usb_remove_hcd(struct usb_hcd *hcd);
-- 
1.6.3.3

--
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