[RFC] xhci: Add a debugfs file to control bulk ring size.

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

 



Add a new debufs file parameter that controls the number of segments used
when allocating a bulk endpoint ring.  Matrix Vision has a libusb program
for their USB 2.0 cameras that submits a number of very large transfers.
The xHCI driver ends up rejecting some transfers because it doesn't have
enough room on the one bulk ring segment.  By experimentation, their
program works if 20 bulk segments are allocated.  Point Grey also has a
USB 3.0 webcam that needs more than one bulk segment.

We really need to fix this by adding dynamic ring segment allocation, but
that code is a bit complex and will take a while to write.  To ensure
Matrix Vision's libusb program works when their device is connected under
an xHCI host, add a debufs file to allow users to increase the number
of bulk endpoint ring segments.  This is a temporary fix, and should be
removed once the xHCI driver has dynamic segment allocation.

The userspace program will need to change the number of bulk segments
allocated before the user plugs in the webcam.  Each xHCI host controller
will have one directory in the debugfs usb directory (usually mounted at
/sys/kernel/debug/usb/), and the name of that directory will be based on
the PCI bus name.

The userspace program will need to echo the number of bulk segments to the
file, like so:

root@xanatos:/sys/kernel/debug/usb# echo 2 > xhci/0000\:05\:00.0/num_bulk_segments

You can also cat the file to see the current number of bulk ring segments
that the driver is using:

root@xanatos:/sys/kernel/debug/usb# cat xhci/0000\:05\:00.0/num_bulk_segments
2

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
Cc: Kruno Mrak <kruno.mrak@xxxxxxxxxxxxxxxx>
Cc: Tim Vlaar <Tvlaar@xxxxxxxxxx>
---

Hi Tim and Kruno,

Can you please test out this patch?

I wasn't quite sure what the upper limit on bulk ring segments should
be.  I don't want to allow userspace (even as root) to be able to eat up
all of the DMA'able memory, so I arbitrarily picked the limit of 100
bulk segments (which will amount to about 100KB allocated per bulk
endpoint).  If that limit needs to be something higher let me know.

Sarah Sharp

 drivers/usb/host/xhci-dbg.c |   95 +++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/xhci-mem.c |    4 ++
 drivers/usb/host/xhci.c     |   13 +++++-
 drivers/usb/host/xhci.h     |    7 +++
 4 files changed, 118 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c
index e9b0f04..737d86b 100644
--- a/drivers/usb/host/xhci-dbg.c
+++ b/drivers/usb/host/xhci-dbg.c
@@ -21,6 +21,7 @@
  */
 
 #include "xhci.h"
+#include <linux/debugfs.h>
 
 #define XHCI_INIT_VALUE 0x0
 
@@ -574,3 +575,97 @@ void xhci_dbg_ctx(struct xhci_hcd *xhci,
 	xhci_dbg_slot_ctx(xhci, ctx);
 	xhci_dbg_ep_ctx(xhci, ctx, last_ep);
 }
+
+/*********************		Debugfs methods		**********************/
+static struct dentry *xhci_debug_root;
+
+static int xhci_bulk_segments_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static int xhci_bulk_segments_close(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static ssize_t xhci_bulk_segments_read(struct file *file, char __user *user_buf,
+				   size_t len, loff_t *offset)
+{
+	struct xhci_hcd *xhci;
+	char buf[50];
+	int ret;
+
+	xhci = file->private_data;
+	ret = sprintf(buf, "%u\n", xhci->num_bulk_segs);
+	if (ret <= 0)
+		return ret;
+
+	return simple_read_from_buffer(user_buf, len, offset, buf, ret);
+}
+
+static ssize_t xhci_bulk_segments_write(struct file *file, const char __user *user_buf,
+			      size_t count, loff_t *ppos)
+{
+	struct xhci_hcd *xhci;
+	char buf[50];
+	size_t len;
+	long long num_bulk_segs = 0;
+
+	xhci = file->private_data;
+	len = simple_write_to_buffer(buf, 50, ppos, user_buf, count);
+	buf[len] = '\0';
+	if (len > 0 && buf[len - 1] == '\n')
+		buf[len - 1] = '\0';
+
+	num_bulk_segs = simple_strtoll(buf, NULL, 10);
+	if (num_bulk_segs <=  0 || num_bulk_segs > XHCI_BULK_SEGS_LIMIT)
+		return -EINVAL;
+
+	xhci->num_bulk_segs = num_bulk_segs;
+	return len;
+}
+
+static const struct file_operations xhci_bulk_segments_fops = {
+	.owner		= THIS_MODULE,
+	.open		= xhci_bulk_segments_open,
+	.read		= xhci_bulk_segments_read,
+	.write		= xhci_bulk_segments_write,
+	.release	= xhci_bulk_segments_close,
+	.llseek		= noop_llseek,
+};
+
+int xhci_create_debug_dir(void) {
+	xhci_debug_root = debugfs_create_dir("xhci", usb_debug_root);
+	if (!xhci_debug_root)
+		return -ENOENT;
+	return 0;
+}
+
+void xhci_remove_debug_dir(void) {
+	debugfs_remove(xhci_debug_root);
+}
+
+inline void xhci_remove_debug_files(struct xhci_hcd *xhci)
+{
+	debugfs_remove_recursive(xhci->debug_dir);
+}
+
+inline int xhci_create_debug_files(struct xhci_hcd *xhci)
+{
+	struct usb_bus *bus = &xhci_to_hcd(xhci)->self;
+
+	xhci->debug_dir = debugfs_create_dir(bus->bus_name, xhci_debug_root);
+	if (!xhci->debug_dir)
+		return -ENOMEM;
+
+	if (!debugfs_create_file("num_bulk_segments", S_IRUGO|S_IWUSR,
+				xhci->debug_dir, xhci,
+				&xhci_bulk_segments_fops)) {
+		xhci_remove_debug_files(xhci);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 94a8b28..e7ac4fb 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1316,6 +1316,10 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
 	if (usb_endpoint_xfer_isoc(&ep->desc))
 		virt_dev->eps[ep_index].new_ring =
 			xhci_ring_alloc(xhci, 8, true, mem_flags);
+	else if (usb_endpoint_xfer_bulk(&ep->desc))
+		virt_dev->eps[ep_index].new_ring =
+			xhci_ring_alloc(xhci, xhci->num_bulk_segs,
+					true, mem_flags);
 	else
 		virt_dev->eps[ep_index].new_ring =
 			xhci_ring_alloc(xhci, 1, true, mem_flags);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 54cb762..a6783de 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -322,6 +322,8 @@ int xhci_init(struct usb_hcd *hcd)
 	} else {
 		xhci_dbg(xhci, "xHCI doesn't need link TRB QUIRK\n");
 	}
+	/* Can be changed later via debugfs */
+	xhci->num_bulk_segs = 1;
 	retval = xhci_mem_init(xhci, GFP_KERNEL);
 	xhci_dbg(xhci, "Finished xhci_init\n");
 
@@ -425,6 +427,9 @@ int xhci_run(struct usb_hcd *hcd)
 	if (!usb_hcd_is_primary_hcd(hcd))
 		return xhci_run_finished(xhci);
 
+	if (xhci_create_debug_files(xhci))
+		return -ENOMEM;
+
 	xhci_dbg(xhci, "xhci_run\n");
 	/* unregister the legacy interrupt */
 	if (hcd->irq)
@@ -573,6 +578,7 @@ void xhci_stop(struct usb_hcd *hcd)
 	xhci_mem_cleanup(xhci);
 	xhci_dbg(xhci, "xhci_stop completed - status = %x\n",
 		    xhci_readl(xhci, &xhci->op_regs->status));
+	xhci_remove_debug_files(xhci);
 }
 
 /*
@@ -3647,9 +3653,13 @@ MODULE_LICENSE("GPL");
 
 static int __init xhci_hcd_init(void)
 {
-#ifdef CONFIG_PCI
 	int retval = 0;
 
+	if (xhci_create_debug_dir())
+		return -ENOENT;
+
+#ifdef CONFIG_PCI
+
 	retval = xhci_register_pci();
 
 	if (retval < 0) {
@@ -3684,5 +3694,6 @@ static void __exit xhci_hcd_cleanup(void)
 #ifdef CONFIG_PCI
 	xhci_unregister_pci();
 #endif
+	xhci_remove_debug_dir();
 }
 module_exit(xhci_hcd_cleanup);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 35140ac..bc7c09c 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1449,6 +1449,8 @@ struct xhci_hcd {
 #define	XHCI_SW_BW_CHECKING	(1 << 8)
 	unsigned int		num_active_eps;
 	unsigned int		limit_active_eps;
+#define	XHCI_BULK_SEGS_LIMIT	100
+	unsigned int		num_bulk_segs;
 	/* There are two roothubs to keep track of bus suspend info for */
 	struct xhci_bus_state   bus_state[2];
 	/* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */
@@ -1459,6 +1461,7 @@ struct xhci_hcd {
 	/* Array of pointers to USB 2.0 PORTSC registers */
 	__le32 __iomem		**usb2_ports;
 	unsigned int		num_usb2_ports;
+	struct dentry		*debug_dir;
 };
 
 /* convert between an HCD pointer and the corresponding EHCI_HCD */
@@ -1551,6 +1554,10 @@ char *xhci_get_slot_state(struct xhci_hcd *xhci,
 void xhci_dbg_ep_rings(struct xhci_hcd *xhci,
 		unsigned int slot_id, unsigned int ep_index,
 		struct xhci_virt_ep *ep);
+inline int xhci_create_debug_dir(void);
+void xhci_remove_debug_dir(void);
+inline int xhci_create_debug_files(struct xhci_hcd *xhci);
+inline void xhci_remove_debug_files(struct xhci_hcd *xhci);
 
 /* xHCI memory management */
 void xhci_mem_cleanup(struct xhci_hcd *xhci);
-- 
1.7.4.1

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