[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251104113248.223594-3-lec.jakub@gmail.com>
Date: Tue, 4 Nov 2025 12:32:47 +0100
From: Jakub Lecki <lec.jakub@...il.com>
To: linux-usb@...r.kernel.org,
linux-kernel@...r.kernel.org
Cc: Valentina Manea <valentina.manea.m@...il.com>,
Shuah Khan <shuah@...nel.org>,
Hongren Zheng <i@...ithal.me>,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
Jakub Lecki <lec.jakub@...il.com>
Subject: [PATCH 2/3] usbip: Convert CONFIG_USBIP_VHCI_HC_PORTS to a module parameter.
In workflows involving a greater number of remote
USB/IP devices, the default number of available virtual ports may be
insufficient, forcing user to recompile the module with greater number
of configured virtual host controllers and/or number of ports.
Allow a user to configure the number of ports per USB/IP virtual host
controller via a new 'hc_ports' module parameter to simplify the usage
of this module.
- Remove the USBIP_VHCI_HC_PORTS Kconfig option and replace it with a
module parameter.
- Fix the omitted BUILD_BUG_ON check for the upper limit of
'vhci_hc_ports' when switching from USB_MAXCHILDREN to
USB_SS_MAXPORTS.
- Resize related arrays to match VHCI_MAX_HC_PORTS = USB_SS_MAXPORTS to
support a dynamically configurable number of ports.
- Trim the value of the configured 'hc_ports' parameter if it
exceeds bounds, and emit a warning.
Signed-off-by: Jakub Lecki <lec.jakub@...il.com>
---
drivers/usb/usbip/Kconfig | 10 --------
drivers/usb/usbip/vhci.h | 22 ++++++++---------
drivers/usb/usbip/vhci_hcd.c | 43 +++++++++++++++++++++++-----------
drivers/usb/usbip/vhci_sysfs.c | 18 +++++++-------
4 files changed, 48 insertions(+), 45 deletions(-)
diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig
index bdcb6f4fdbec..e178867fb5ef 100644
--- a/drivers/usb/usbip/Kconfig
+++ b/drivers/usb/usbip/Kconfig
@@ -28,16 +28,6 @@ config USBIP_VHCI_HCD
To compile this driver as a module, choose M here: the
module will be called vhci-hcd.
-config USBIP_VHCI_HC_PORTS
- int "Number of ports per USB/IP virtual host controller"
- range 1 15
- default 8
- depends on USBIP_VHCI_HCD
- help
- To increase number of ports available for USB/IP virtual
- host controller driver, this defines number of ports per
- USB/IP virtual host controller.
-
config USBIP_HOST
tristate "Host driver"
depends on USBIP_CORE && USB
diff --git a/drivers/usb/usbip/vhci.h b/drivers/usb/usbip/vhci.h
index 30b8540e0b49..2772d923a8cb 100644
--- a/drivers/usb/usbip/vhci.h
+++ b/drivers/usb/usbip/vhci.h
@@ -72,15 +72,9 @@ enum hub_speed {
HUB_SPEED_SUPER,
};
-/* Number of supported ports. Value has an upperbound of USB_MAXCHILDREN */
-#ifdef CONFIG_USBIP_VHCI_HC_PORTS
-#define VHCI_HC_PORTS CONFIG_USBIP_VHCI_HC_PORTS
-#else
-#define VHCI_HC_PORTS 8
-#endif
-
-/* Each VHCI has 2 hubs (USB2 and USB3), each has VHCI_HC_PORTS ports */
-#define VHCI_PORTS (VHCI_HC_PORTS*2)
+/* Number of supported ports. Value has an upperbound of USB_SS_MAXPORTS */
+#define VHCI_DEFAULT_HC_PORTS 8
+#define VHCI_MAX_HC_PORTS USB_SS_MAXPORTS
#define VHCI_DEFAULT_NR_HCS 1
#define VHCI_MAX_NR_HCS 128
@@ -100,7 +94,7 @@ struct vhci {
struct vhci_hcd {
struct vhci *vhci;
- u32 port_status[VHCI_HC_PORTS];
+ u32 port_status[VHCI_MAX_HC_PORTS];
unsigned resuming:1;
unsigned long re_timeout;
@@ -112,13 +106,17 @@ struct vhci_hcd {
* wIndex shows the port number and begins from 1.
* But, the index of this array begins from 0.
*/
- struct vhci_device vdev[VHCI_HC_PORTS];
+ struct vhci_device vdev[VHCI_MAX_HC_PORTS];
};
extern unsigned int vhci_num_controllers;
+extern unsigned int vhci_hc_ports;
extern struct vhci *vhcis;
extern struct attribute_group vhci_attr_group;
+/* Each VHCI has 2 hubs (USB2 and USB3), each has vhci_hc_ports ports */
+#define VHCI_PORTS (vhci_hc_ports * 2)
+
/* vhci_hcd.c */
void rh_port_connect(struct vhci_device *vdev, enum usb_device_speed speed);
@@ -135,7 +133,7 @@ int vhci_tx_loop(void *data);
static inline __u32 port_to_rhport(__u32 port)
{
- return port % VHCI_HC_PORTS;
+ return port % vhci_hc_ports;
}
static inline int port_to_pdev_nr(__u32 port)
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 93c3fa3e1c53..1989a6f59107 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -51,6 +51,12 @@ MODULE_PARM_DESC(num_controllers, "Number of USB/IP virtual host controllers (ra
__MODULE_STRING(VHCI_MAX_NR_HCS) ", default: "
__MODULE_STRING(VHCI_DEFAULT_NR_HCS) ")");
+unsigned int vhci_hc_ports = VHCI_DEFAULT_HC_PORTS;
+module_param_named(hc_ports, vhci_hc_ports, uint, 0444);
+MODULE_PARM_DESC(hc_ports, "Number of ports per USB/IP virtual host controller (range: 1-"
+ __MODULE_STRING(VHCI_MAX_HC_PORTS) ", default: "
+ __MODULE_STRING(VHCI_DEFAULT_HC_PORTS) ")");
+
struct vhci *vhcis;
static const char * const bit_desc[] = {
@@ -236,7 +242,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
{
struct vhci_hcd *vhci_hcd = hcd_to_vhci_hcd(hcd);
struct vhci *vhci = vhci_hcd->vhci;
- int retval = DIV_ROUND_UP(VHCI_HC_PORTS + 1, 8);
+ int retval = DIV_ROUND_UP(vhci_hc_ports + 1, 8);
int rhport;
int changed = 0;
unsigned long flags;
@@ -250,7 +256,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
}
/* check pseudo status register for each port */
- for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) {
+ for (rhport = 0; rhport < vhci_hc_ports; rhport++) {
if ((vhci_hcd->port_status[rhport] & PORT_C_MASK)) {
/* The status of a port has been changed, */
usbip_dbg_vhci_rh("port %d status changed\n", rhport);
@@ -297,7 +303,7 @@ ss_hub_descriptor(struct usb_hub_descriptor *desc)
desc->bDescLength = 12;
desc->wHubCharacteristics = cpu_to_le16(
HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM);
- desc->bNbrPorts = VHCI_HC_PORTS;
+ desc->bNbrPorts = vhci_hc_ports;
desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/
desc->u.ss.DeviceRemovable = 0xffff;
}
@@ -311,8 +317,7 @@ static inline void hub_descriptor(struct usb_hub_descriptor *desc)
desc->wHubCharacteristics = cpu_to_le16(
HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM);
- desc->bNbrPorts = VHCI_HC_PORTS;
- BUILD_BUG_ON(VHCI_HC_PORTS > USB_MAXCHILDREN);
+ desc->bNbrPorts = vhci_hc_ports;
width = desc->bNbrPorts / 8 + 1;
desc->bDescLength = USB_DT_HUB_NONVAR_SIZE + 2 * width;
memset(&desc->u.hs.DeviceRemovable[0], 0, width);
@@ -329,7 +334,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
unsigned long flags;
bool invalid_rhport = false;
- u32 prev_port_status[VHCI_HC_PORTS];
+ u32 prev_port_status[VHCI_MAX_HC_PORTS];
if (!HCD_HW_ACCESSIBLE(hcd))
return -ETIMEDOUT;
@@ -344,17 +349,18 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
/*
* wIndex can be 0 for some request types (typeReq). rhport is
- * in valid range when wIndex >= 1 and < VHCI_HC_PORTS.
+ * in valid range when wIndex >= 1 and < vhci_hc_ports.
*
* Reference port_status[] only with valid rhport when
* invalid_rhport is false.
*/
- if (wIndex < 1 || wIndex > VHCI_HC_PORTS) {
+ if (wIndex < 1 || wIndex > vhci_hc_ports) {
invalid_rhport = true;
- if (wIndex > VHCI_HC_PORTS)
+ if (wIndex > vhci_hc_ports)
pr_err("invalid port number %d\n", wIndex);
- } else
+ } else {
rhport = wIndex - 1;
+ }
vhci_hcd = hcd_to_vhci_hcd(hcd);
vhci = vhci_hcd->vhci;
@@ -707,7 +713,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
struct vhci_device *vdev;
unsigned long flags;
- if (portnum > VHCI_HC_PORTS) {
+ if (portnum > vhci_hc_ports) {
pr_err("invalid port number %d\n", portnum);
return -ENODEV;
}
@@ -1188,7 +1194,7 @@ static int vhci_start(struct usb_hcd *hcd)
/* initialize private data of usb_hcd */
- for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) {
+ for (rhport = 0; rhport < vhci_hc_ports; rhport++) {
struct vhci_device *vdev = &vhci_hcd->vdev[rhport];
vhci_device_init(vdev);
@@ -1244,7 +1250,7 @@ static void vhci_stop(struct usb_hcd *hcd)
}
/* 2. shutdown all the ports of vhci_hcd */
- for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) {
+ for (rhport = 0; rhport < vhci_hc_ports; rhport++) {
struct vhci_device *vdev = &vhci_hcd->vdev[rhport];
usbip_event_add(&vdev->ud, VDEV_EVENT_REMOVED);
@@ -1441,7 +1447,7 @@ static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state)
spin_lock_irqsave(&vhci->lock, flags);
- for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) {
+ for (rhport = 0; rhport < vhci_hc_ports; rhport++) {
if (vhci->vhci_hcd_hs->port_status[rhport] &
USB_PORT_STAT_CONNECTION)
connected += 1;
@@ -1525,6 +1531,15 @@ static int __init vhci_hcd_init(void)
vhci_num_controllers = VHCI_MAX_NR_HCS;
}
+ if (vhci_hc_ports < 1) {
+ pr_warn("hc_ports less than 1, setting to 1\n");
+ vhci_hc_ports = 1;
+ } else if (vhci_hc_ports > VHCI_MAX_HC_PORTS) {
+ pr_warn("hc_ports too high, limiting to %d\n",
+ VHCI_MAX_HC_PORTS);
+ vhci_hc_ports = VHCI_MAX_HC_PORTS;
+ }
+
vhcis = kcalloc(vhci_num_controllers, sizeof(struct vhci), GFP_KERNEL);
if (vhcis == NULL)
return -ENOMEM;
diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c
index d5865460e82d..19d67717180e 100644
--- a/drivers/usb/usbip/vhci_sysfs.c
+++ b/drivers/usb/usbip/vhci_sysfs.c
@@ -81,7 +81,7 @@ static ssize_t status_show_vhci(int pdev_nr, char *out)
spin_lock_irqsave(&vhci->lock, flags);
- for (i = 0; i < VHCI_HC_PORTS; i++) {
+ for (i = 0; i < vhci_hc_ports; i++) {
struct vhci_device *vdev = &vhci->vhci_hcd_hs->vdev[i];
spin_lock(&vdev->ud.lock);
@@ -90,12 +90,12 @@ static ssize_t status_show_vhci(int pdev_nr, char *out)
spin_unlock(&vdev->ud.lock);
}
- for (i = 0; i < VHCI_HC_PORTS; i++) {
+ for (i = 0; i < vhci_hc_ports; i++) {
struct vhci_device *vdev = &vhci->vhci_hcd_ss->vdev[i];
spin_lock(&vdev->ud.lock);
port_show_vhci(&out, HUB_SPEED_SUPER,
- pdev_nr * VHCI_PORTS + VHCI_HC_PORTS + i, vdev);
+ pdev_nr * VHCI_PORTS + vhci_hc_ports + i, vdev);
spin_unlock(&vdev->ud.lock);
}
@@ -109,7 +109,7 @@ static ssize_t status_show_not_ready(int pdev_nr, char *out)
char *s = out;
int i = 0;
- for (i = 0; i < VHCI_HC_PORTS; i++) {
+ for (i = 0; i < vhci_hc_ports; i++) {
out += sprintf(out, "hs %04u %03u ",
(pdev_nr * VHCI_PORTS) + i,
VDEV_ST_NOTASSIGNED);
@@ -117,9 +117,9 @@ static ssize_t status_show_not_ready(int pdev_nr, char *out)
out += sprintf(out, "\n");
}
- for (i = 0; i < VHCI_HC_PORTS; i++) {
+ for (i = 0; i < vhci_hc_ports; i++) {
out += sprintf(out, "ss %04u %03u ",
- (pdev_nr * VHCI_PORTS) + VHCI_HC_PORTS + i,
+ (pdev_nr * VHCI_PORTS) + vhci_hc_ports + i,
VDEV_ST_NOTASSIGNED);
out += sprintf(out, "000 00000000 0000000000000000 0-0");
out += sprintf(out, "\n");
@@ -221,11 +221,11 @@ static int valid_port(__u32 *pdev_nr, __u32 *rhport)
}
*pdev_nr = array_index_nospec(*pdev_nr, vhci_num_controllers);
- if (*rhport >= VHCI_HC_PORTS) {
+ if (*rhport >= vhci_hc_ports) {
pr_err("rhport %u\n", *rhport);
return 0;
}
- *rhport = array_index_nospec(*rhport, VHCI_HC_PORTS);
+ *rhport = array_index_nospec(*rhport, vhci_hc_ports);
return 1;
}
@@ -255,7 +255,7 @@ static ssize_t detach_store(struct device *dev, struct device_attribute *attr,
usbip_dbg_vhci_sysfs("rhport %d\n", rhport);
- if ((port / VHCI_HC_PORTS) % 2)
+ if ((port / vhci_hc_ports) % 2)
vhci_hcd = hcd_to_vhci_hcd(hcd)->vhci->vhci_hcd_ss;
else
vhci_hcd = hcd_to_vhci_hcd(hcd)->vhci->vhci_hcd_hs;
--
2.43.0
Powered by blists - more mailing lists