drivers/usb/host/xhci-debugfs.c | 93 +++++++++++++++++++++++++++++++++ drivers/usb/host/xhci-mem.c | 8 +++ drivers/usb/host/xhci-ring.c | 14 +++++ drivers/usb/host/xhci.c | 28 ++++++++++ drivers/usb/host/xhci.h | 9 ++++ 5 files changed, 152 insertions(+)
From: Xu Rao <raoxu@uniontech.com>
In many projects, you need to obtain the available bandwidth of the
xhci roothub port. Refer to xhci rev1_2 and use the TRB_GET_BW
command to obtain it.
hardware tested:
03:00.3 USB controller: Advanced Micro Devices, Inc. [AMD] Raven USB 3.1
(prog-if 30 [XHCI])
Subsystem: Huawei Technologies Co., Ltd. Raven USB 3.1
Flags: bus master, fast devsel, latency 0, IRQ 30
Memory at c0300000 (64-bit, non-prefetchable) [size=1M]
Capabilities: [48] Vendor Specific Information: Len=08 <?>
Capabilities: [50] Power Management version 3
Capabilities: [64] Express Endpoint, MSI 00
Capabilities: [a0] MSI: Enable- Count=1/8 Maskable- 64bit+
Capabilities: [c0] MSI-X: Enable+ Count=8 Masked-
Kernel driver in use: xhci_hcd
test progress:
1. cd /sys/kernel/debug/usb/xhci/0000:03:00.3/port_bandwidth# ls
FS_BW HS_BW SS_BW
2. test fs speed device
cat FS_BW
port[1] available bw: 90%.
port[2] available bw: 90%.
port[3] available bw: 90%.
port[4] available bw: 90%.
port[5] available bw: 0%.
port[6] available bw: 0%.
port[7] available bw: 0%.
port[8] available bw: 0%.
plug in fs usb audio ID 0d8c:013c
cat FS_BW
port[1] available bw: 76%.
port[2] available bw: 76%.
port[3] available bw: 76%.
port[4] available bw: 76%.
port[5] available bw: 0%.
port[6] available bw: 0%.
port[7] available bw: 0%.
port[8] available bw: 0%.
3. test hs speed device
cat HS_BW
port[1] available bw: 79%.
port[2] available bw: 79%.
port[3] available bw: 79%.
port[4] available bw: 79%.
port[5] available bw: 0%.
port[6] available bw: 0%.
port[7] available bw: 0%.
port[8] available bw: 0%.
plug in hs usb video ID 0408:1040
cat HS_BW
port[1] available bw: 39%.
port[2] available bw: 39%.
port[3] available bw: 39%.
port[4] available bw: 39%.
port[5] available bw: 0%.
port[6] available bw: 0%.
port[7] available bw: 0%.
port[8] available bw: 0%.
4.cat SS_BW
port[1] available bw: 0%.
port[2] available bw: 0%.
port[3] available bw: 0%.
port[4] available bw: 0%.
port[5] available bw: 90%.
port[6] available bw: 90%.
port[7] available bw: 90%.
port[8] available bw: 90%.
Signed-off-by: Xu Rao <raoxu@uniontech.com>
---
Changelog:
*v1->v2: modify the patch subject no code changes.
v2->v3: separate files in debugfs for each speed (SS HS FS).
queue one command for each speed not queuing three commands on one file.
print value from context not array on stack.
---
drivers/usb/host/xhci-debugfs.c | 93 +++++++++++++++++++++++++++++++++
drivers/usb/host/xhci-mem.c | 8 +++
drivers/usb/host/xhci-ring.c | 14 +++++
drivers/usb/host/xhci.c | 28 ++++++++++
drivers/usb/host/xhci.h | 9 ++++
5 files changed, 152 insertions(+)
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index 1f5ef174abea..5751065d199c 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -631,6 +631,97 @@ static void xhci_debugfs_create_ports(struct xhci_hcd *xhci,
}
}
+static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed,
+ struct seq_file *s)
+{
+ unsigned int num_ports;
+ unsigned int i;
+ int ret;
+ struct xhci_container_ctx *ctx;
+
+ num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
+ ctx = xhci->get_bw_command->in_ctx;
+
+ /* get roothub port bandwidth */
+ ret = xhci_get_port_bandwidth(xhci, dev_speed);
+ if (ret)
+ return ret;
+
+ /* print all roothub ports available bandwidth */
+ for (i = 1; i < num_ports+1; i++)
+ seq_printf(s, "port[%d] available bw: %d%%.\n", i,
+ ctx->bytes[i]);
+
+ return ret;
+}
+
+static int xhci_ss_bw_show(struct seq_file *s, void *unused)
+{
+ int ret;
+ struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
+
+ ret = xhci_port_bw_show(xhci, USB_SPEED_SUPER, s);
+ return ret;
+}
+
+static int xhci_hs_bw_show(struct seq_file *s, void *unused)
+{
+ int ret;
+ struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
+
+ ret = xhci_port_bw_show(xhci, USB_SPEED_HIGH, s);
+ return ret;
+}
+
+static int xhci_fs_bw_show(struct seq_file *s, void *unused)
+{
+ int ret;
+ struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
+
+ ret = xhci_port_bw_show(xhci, USB_SPEED_FULL, s);
+ return ret;
+}
+
+static struct xhci_file_map bw_context_files[] = {
+ {"SS_BW", xhci_ss_bw_show, },
+ {"HS_BW", xhci_hs_bw_show, },
+ {"FS_BW", xhci_fs_bw_show, },
+};
+
+static int bw_context_open(struct inode *inode, struct file *file)
+{
+ int i;
+ struct xhci_file_map *f_map;
+ const char *file_name = file_dentry(file)->d_iname;
+
+ for (i = 0; i < ARRAY_SIZE(bw_context_files); i++) {
+ f_map = &bw_context_files[i];
+
+ if (strcmp(f_map->name, file_name) == 0)
+ break;
+ }
+
+ return single_open(file, f_map->show, inode->i_private);
+}
+
+static const struct file_operations bw_fops = {
+ .open = bw_context_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void xhci_debugfs_create_bandwidth(struct xhci_hcd *xhci,
+ struct dentry *parent)
+{
+ parent = debugfs_create_dir("port_bandwidth", parent);
+
+ xhci_debugfs_create_files(xhci, bw_context_files,
+ ARRAY_SIZE(bw_context_files),
+ xhci,
+ parent, &bw_fops);
+}
+
void xhci_debugfs_init(struct xhci_hcd *xhci)
{
struct device *dev = xhci_to_hcd(xhci)->self.controller;
@@ -681,6 +772,8 @@ void xhci_debugfs_init(struct xhci_hcd *xhci)
xhci->debugfs_slots = debugfs_create_dir("devices", xhci->debugfs_root);
xhci_debugfs_create_ports(xhci, xhci->debugfs_root);
+
+ xhci_debugfs_create_bandwidth(xhci, xhci->debugfs_root);
}
void xhci_debugfs_exit(struct xhci_hcd *xhci)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 92703efda1f7..66f08a96d6f9 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1875,6 +1875,10 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
}
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed interrupters");
+ if (xhci->get_bw_command)
+ xhci_free_command(xhci, xhci->get_bw_command);
+ xhci->get_bw_command = NULL;
+
if (xhci->cmd_ring)
xhci_ring_free(xhci, xhci->cmd_ring);
xhci->cmd_ring = NULL;
@@ -2490,6 +2494,10 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
*/
xhci->cmd_ring_reserved_trbs++;
+ xhci->get_bw_command = xhci_alloc_command_with_ctx(xhci, true, flags);
+ if (!xhci->get_bw_command)
+ goto fail;
+
val = readl(&xhci->cap_regs->db_off);
val &= DBOFF_MASK;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 965bffce301e..af1cd4f8ace9 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1867,6 +1867,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
case TRB_NEC_GET_FW:
xhci_handle_cmd_nec_get_fw(xhci, event);
break;
+ case TRB_GET_BW:
+ break;
default:
/* Skip over unknown commands on the event ring */
xhci_info(xhci, "INFO unknown command type %d\n", cmd_type);
@@ -4414,6 +4416,18 @@ int xhci_queue_configure_endpoint(struct xhci_hcd *xhci,
command_must_succeed);
}
+/* Queue a get root hub port bandwidth command TRB */
+int xhci_queue_get_rh_port_bw(struct xhci_hcd *xhci,
+ struct xhci_command *cmd, dma_addr_t in_ctx_ptr,
+ u8 dev_speed, u32 slot_id, bool command_must_succeed)
+{
+ return queue_command(xhci, cmd, lower_32_bits(in_ctx_ptr),
+ upper_32_bits(in_ctx_ptr), 0,
+ TRB_TYPE(TRB_GET_BW) | DEV_SPEED_FOR_TRB(dev_speed) |
+ SLOT_ID_FOR_TRB(slot_id),
+ command_must_succeed);
+}
+
/* Queue an evaluate context command TRB */
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd,
dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 45653114ccd7..123596e85d9c 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -3088,6 +3088,34 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
}
EXPORT_SYMBOL_GPL(xhci_reset_bandwidth);
+/* Get the available bandwidth of the ports under the xhci roothub */
+int xhci_get_port_bandwidth(struct xhci_hcd *xhci, u8 dev_speed)
+{
+ unsigned int num_ports;
+ int ret;
+ unsigned long flags;
+ struct xhci_container_ctx *ctx;
+
+ num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
+ ctx = xhci->get_bw_command->in_ctx;
+
+ /* get xhci hub port bandwidth */
+ /* refer to xhci rev1_2 protocol 4.6.15*/
+ spin_lock_irqsave(&xhci->lock, flags);
+ ret = xhci_queue_get_rh_port_bw(xhci, xhci->get_bw_command, ctx->dma,
+ dev_speed, 0, false);
+ if (ret < 0) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return ret;
+ }
+ xhci_ring_cmd_db(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ wait_for_completion(xhci->get_bw_command->completion);
+
+ return ret;
+}
+
static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci,
struct xhci_container_ctx *in_ctx,
struct xhci_container_ctx *out_ctx,
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 8c164340a2c3..a7cefa8ca7cd 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -999,6 +999,9 @@ enum xhci_setup_dev {
/* bits 16:23 are the virtual function ID */
/* bits 24:31 are the slot ID */
+/* bits 19:16 are the dev speed */
+#define DEV_SPEED_FOR_TRB(p) ((p) << 16)
+
/* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */
#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23)
#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23)
@@ -1549,6 +1552,8 @@ struct xhci_hcd {
struct xhci_virt_device *devs[MAX_HC_SLOTS];
/* For keeping track of bandwidth domains per roothub. */
struct xhci_root_port_bw_info *rh_bw;
+ /* For get xhci roothub port bandwidth. */
+ struct xhci_command *get_bw_command;
/* DMA pools */
struct dma_pool *device_pool;
@@ -1907,6 +1912,10 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
int xhci_queue_configure_endpoint(struct xhci_hcd *xhci,
struct xhci_command *cmd, dma_addr_t in_ctx_ptr, u32 slot_id,
bool command_must_succeed);
+int xhci_queue_get_rh_port_bw(struct xhci_hcd *xhci,
+ struct xhci_command *cmd, dma_addr_t in_ctx_ptr,
+ u8 dev_speed, u32 slot_id, bool command_must_succeed);
+int xhci_get_port_bandwidth(struct xhci_hcd *xhci, u8 dev_speed);
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd,
dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed);
int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
--
2.43.4
Hi raoxu,
kernel test robot noticed the following build warnings:
[auto build test WARNING on usb/usb-testing]
[also build test WARNING on usb/usb-next usb/usb-linus linus/master v6.14-rc5 next-20250305]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/raoxu/usb-xhci-Add-debugfs-support-for-xHCI-port-bandwidth/20250305-165606
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link: https://lore.kernel.org/r/20250305085118.28826-1-raoxu%40uniontech.com
patch subject: [PATCH V3] usb: xhci: Add debugfs support for xHCI port bandwidth
config: arc-randconfig-002-20250306 (https://download.01.org/0day-ci/archive/20250306/202503061205.wtVpmjvj-lkp@intel.com/config)
compiler: arceb-elf-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250306/202503061205.wtVpmjvj-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503061205.wtVpmjvj-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/usb/host/xhci.c: In function 'xhci_get_port_bandwidth':
>> drivers/usb/host/xhci.c:3094:41: warning: variable 'num_ports' set but not used [-Wunused-but-set-variable]
3094 | unsigned int num_ports;
| ^~~~~~~~~
vim +/num_ports +3094 drivers/usb/host/xhci.c
3090
3091 /* Get the available bandwidth of the ports under the xhci roothub */
3092 int xhci_get_port_bandwidth(struct xhci_hcd *xhci, u8 dev_speed)
3093 {
> 3094 unsigned int num_ports;
3095 int ret;
3096 unsigned long flags;
3097 struct xhci_container_ctx *ctx;
3098
3099 num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
3100 ctx = xhci->get_bw_command->in_ctx;
3101
3102 /* get xhci hub port bandwidth */
3103 /* refer to xhci rev1_2 protocol 4.6.15*/
3104 spin_lock_irqsave(&xhci->lock, flags);
3105 ret = xhci_queue_get_rh_port_bw(xhci, xhci->get_bw_command, ctx->dma,
3106 dev_speed, 0, false);
3107 if (ret < 0) {
3108 spin_unlock_irqrestore(&xhci->lock, flags);
3109 return ret;
3110 }
3111 xhci_ring_cmd_db(xhci);
3112 spin_unlock_irqrestore(&xhci->lock, flags);
3113
3114 wait_for_completion(xhci->get_bw_command->completion);
3115
3116 return ret;
3117 }
3118
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2026 Red Hat, Inc.