* [PATCH v10 0/2] usb:xhc:route device to secondary interrupters
@ 2026-01-26 8:55 raoxu
2026-01-26 8:58 ` [PATCH v10 1/2] usb: xhci: refactor IRQ/interrupter plumbing for raoxu
2026-01-26 8:58 ` [PATCH v10 2/2] usb: xhci: enable secondary interrupters and route raoxu
0 siblings, 2 replies; 6+ messages in thread
From: raoxu @ 2026-01-26 8:55 UTC (permalink / raw)
To: mathias.nyman
Cc: gregkh, linux-usb, michal.pecio, niklas.neronin, raoxu, zhanjun,
kenny
From: Xu Rao <raoxu@uniontech.com>
This series is split into two steps: patch 1/2 refactors xHCI IRQ and
interrupter handling to make multi-vector operation possible without
changing behavior; patch 2/2 then enables a small capped set of secondary
interrupters/vectors and routes transfer completions per device (slot) to
reduce contention on interrupter 0.
Xu Rao (2):
usb: xhci: refactor IRQ/interrupter plumbing for multi-vector support
usb: xhci: enable secondary interrupters and route transfers per slot
Signed-off-by: Xu Rao <raoxu@uniontech.com>
---
Changelog:
v1 -> v2:
- Bind interrupters to endpoints at enable time instead of selecting
per transfer.
- Store the selected interrupter in struct xhci_virt_ep and program
TRB_INTR_TARGET() from the bound interrupter.
- Use a single IRQ handler for both primary and secondary vectors,
with STS_EINT handling restricted to interrupter 0.
- Keep a common dev_id for IRQ registration to match the existing
xhci_cleanup_msix() teardown constraints and avoid dev_id
lifetime issues.
- Clarify secondary interrupter teardown to avoid double-free or
use-after-free during xHCI removal.
v2 -> v3:
- modify commit information
v3 -> v4:
- Bind interrupters per USB device (slot) via struct xhci_virt_device,
program TRB_INTR_TARGET() from vdev->interrupter for bulk/ctrl/isoc.
- Drop xfer_interrupters and unify on xhci->interrupters[] for both
primary and secondary event rings and IRQ routing.
- Allocate secondary interrupters in xhci_mem_init; on any allocation
failure, rollback and continue with primary interrupter only.
- Cap secondary interrupter creation with MAX_SECONDARY_INTRNUM,
defaulting to 4.
- Route xhci_msi_irq by irq handler_data token (intr_num + 1) to keep
correct interrupter selection across resume/power_lost.
- Apply MSI-X affinity hints for secondary vectors.
v4 -> v5:
- Fix min() signedness build error reported by 0day CI.
- Rebase onto v6.19-rc2.
v5 -> v6:
- Route secondary MSI/MSI-X IRQs by storing struct xhci_interrupter in
irq handler_data, instead of using an (intr_num + 1) token mapping.
- Program Slot Context Interrupter Target (tt_info[31:22]) from
vdev->interrupter to keep slot default routing aligned with TRB
TRB_INTR_TARGET() selection.
v6 -> v7:
- Add xhci_quiesce_interrupter() and use it for secondary
interrupters in xhci_stop() and the power_lost path of xhci_resume(),
ensuring IMAN.IP (RW1C) and ERDP.EHB are properly cleared.
v7 -> v8:
- Sync secondary MSI/MSI-X vectors in xhci_msix_sync_irqs() with
synchronize_irq().
- Fix build errors by adding missing header includes for the IRQ helper APIs.
v8 -> v9:
- Use PCI_IRQ_AFFINITY to let PCI core spread MSI/MSI-X vectors across CPUs.
- Route each MSI/MSI-X vector to its interrupter via per-vector irq_ctx dev_id.
- Fix modpost error: xhci_msix_set_handler_data undefined (0-day CI).
https://lore.kernel.org/oe-kbuild-all/202601171743.omq3DpnM-lkp@intel.com/
- Rebase onto v6.19-rc6.
v9 -> v10:
- refactor IRQ/interrupter plumbing for multi-vector support.
- add xhci_handle_slot_secondary_events to handle secondary event ring.
---
drivers/usb/host/xhci-mem.c | 48 +++++++++++
drivers/usb/host/xhci-pci.c | 57 ++++++++++---
drivers/usb/host/xhci-ring.c | 156 +++++++++++++++++++++++++++++------
drivers/usb/host/xhci.c | 37 ++++++---
drivers/usb/host/xhci.h | 26 +++++-
5 files changed, 274 insertions(+), 50 deletions(-)
---
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v10 1/2] usb: xhci: refactor IRQ/interrupter plumbing for
2026-01-26 8:55 [PATCH v10 0/2] usb:xhc:route device to secondary interrupters raoxu
@ 2026-01-26 8:58 ` raoxu
2026-01-26 9:06 ` Greg KH
2026-01-26 8:58 ` [PATCH v10 2/2] usb: xhci: enable secondary interrupters and route raoxu
1 sibling, 1 reply; 6+ messages in thread
From: raoxu @ 2026-01-26 8:58 UTC (permalink / raw)
To: raoxu
Cc: gregkh, kenny, linux-usb, mathias.nyman, michal.pecio,
niklas.neronin, zhanjun
Prepare xHCI for multiple IRQ vectors/interrupters without changing the
current behavior.
Introduce a per-vector irq context (hcd + intr_num) to use as request_irq()
dev_id, and track the active vector count in xhci->irqs_enabled. Use this
single bound to enable/disable interrupters consistently across run/stop/
resume and to sync/free IRQs.
Legacy IRQ fallback also keeps irqs_enabled >= 1 so interrupter 0 remains
functional when MSI/MSI-X is unavailable.
No functional change intended: still uses interrupter 0 only.
Signed-off-by: raoxu <raoxu@uniontech.com>
---
drivers/usb/host/xhci-mem.c | 17 +++++++++++++++++
drivers/usb/host/xhci-pci.c | 34 +++++++++++++++++++++++++---------
drivers/usb/host/xhci-ring.c | 9 +++++++--
drivers/usb/host/xhci.c | 23 +++++++++++++----------
drivers/usb/host/xhci.h | 16 +++++++++++++++-
5 files changed, 77 insertions(+), 22 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index c708bdd69f16..524c58a149d3 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -2402,6 +2402,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
dma_addr_t dma;
+ unsigned int i;
/*
* xHCI section 5.4.6 - Device Context array must be
@@ -2495,6 +2496,22 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
if (!xhci->interrupters[0])
goto fail;
+ /* Since only the main interrupt is used, secondary_irqs_alloc is set to 0. */
+ xhci->secondary_irqs_alloc = 0;
+
+ /*
+ * Initialize all allocated interrupters here.
+ * Use allocated count as loop bound to avoid touching non-allocated
+ * or non-operational interrupters.
+ */
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Add allocated interrupters");
+ for (i = 0; i < xhci->secondary_irqs_alloc + 1; i++) {
+ if (!xhci->interrupters[i])
+ continue;
+ xhci_add_interrupter(xhci, i);
+ xhci->interrupters[i]->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
+ }
+
if (scratchpad_alloc(xhci, flags))
goto fail;
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 585b2f3117b0..b3efd6d8fd9c 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -116,13 +116,12 @@ static const struct xhci_driver_overrides xhci_pci_overrides __initconst = {
static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
{
struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+ int i;
- if (hcd->msix_enabled) {
- struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
-
- /* for now, the driver only supports one primary interrupter */
- synchronize_irq(pci_irq_vector(pdev, 0));
- }
+ if (hcd->msix_enabled)
+ for (i = 0; i < xhci->irqs_enabled; i++)
+ synchronize_irq(pci_irq_vector(pdev, i));
}
/* Legacy IRQ is freed by usb_remove_hcd() or usb_hcd_pci_shutdown() */
@@ -130,11 +129,17 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci)
{
struct usb_hcd *hcd = xhci_to_hcd(xhci);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+ int i;
if (hcd->irq > 0)
return;
- free_irq(pci_irq_vector(pdev, 0), xhci_to_hcd(xhci));
+ for (i = 0; i < xhci->irqs_enabled; i++)
+ free_irq(pci_irq_vector(pdev, i), &xhci->irq_ctx[i]);
+
+ xhci->irqs_enabled = 0;
+ memset(xhci->irq_ctx, 0, sizeof(xhci->irq_ctx));
+
pci_free_irq_vectors(pdev);
hcd->msix_enabled = 0;
}
@@ -145,6 +150,7 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int ret;
+ struct xhci_irq_ctx *ctx;
/*
* Some Fresco Logic host controllers advertise MSI, but fail to
@@ -174,11 +180,17 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
goto legacy_irq;
}
- ret = request_irq(pci_irq_vector(pdev, 0), xhci_msi_irq, 0, "xhci_hcd",
- xhci_to_hcd(xhci));
+ memset(xhci->irq_ctx, 0, sizeof(xhci->irq_ctx));
+ xhci->irqs_enabled = 0;
+
+ ctx = &xhci->irq_ctx[0];
+ ctx->hcd = hcd;
+ ctx->intr_num = 0;
+ ret = request_irq(pci_irq_vector(pdev, 0), xhci_msi_irq, 0, "xhci_hcd", ctx);
if (ret)
goto free_irq_vectors;
+ xhci->irqs_enabled = 1;
hcd->msi_enabled = 1;
hcd->msix_enabled = pdev->msix_enabled;
return 0;
@@ -186,6 +198,7 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
free_irq_vectors:
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable %s interrupt",
pdev->msix_enabled ? "MSI-X" : "MSI");
+ xhci->irqs_enabled = 0;
pci_free_irq_vectors(pdev);
legacy_irq:
@@ -198,6 +211,9 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
hcd->driver->description, hcd->self.busnum);
+ /* legacy IRQ path still needs interrupter 0 */
+ xhci->irqs_enabled = 1;
+
/* fall back to legacy interrupt */
ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, hcd->irq_descr, hcd);
if (ret) {
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 9315ba18310d..3ea134c07c5f 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3220,9 +3220,14 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
return ret;
}
-irqreturn_t xhci_msi_irq(int irq, void *hcd)
+irqreturn_t xhci_msi_irq(int irq, void *data)
{
- return xhci_irq(hcd);
+ struct xhci_irq_ctx *ctx = data;
+
+ /* For now only vector 0 is requested; keep behavior unchanged. */
+ if (!ctx || !ctx->hcd)
+ return IRQ_NONE;
+ return xhci_irq(ctx->hcd);
}
EXPORT_SYMBOL_GPL(xhci_msi_irq);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index b3ba16b9718c..cbf96cb51583 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -576,10 +576,6 @@ static int xhci_init(struct usb_hcd *hcd)
/* Set USB 3.0 device notifications for function remote wake */
xhci_set_dev_notifications(xhci);
- /* Initialize the Primary interrupter */
- xhci_add_interrupter(xhci, 0);
- xhci->interrupters[0]->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
-
/* Initializing Compliance Mode Recovery Data If Needed */
if (xhci_compliance_mode_recovery_timer_quirk_check()) {
xhci->quirks |= XHCI_COMP_MODE_QUIRK;
@@ -594,9 +590,9 @@ static int xhci_init(struct usb_hcd *hcd)
static int xhci_run_finished(struct xhci_hcd *xhci)
{
- struct xhci_interrupter *ir = xhci->interrupters[0];
unsigned long flags;
u32 temp;
+ int i;
/*
* Enable interrupts before starting the host (xhci 4.2 and 5.5.2).
@@ -609,8 +605,10 @@ static int xhci_run_finished(struct xhci_hcd *xhci)
temp |= (CMD_EIE);
writel(temp, &xhci->op_regs->command);
- xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable primary interrupter");
- xhci_enable_interrupter(ir);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable all interrupters");
+ for (i = 0; i < xhci->irqs_enabled; i++)
+ if (xhci->interrupters[i])
+ xhci_enable_interrupter(xhci->interrupters[i]);
if (xhci_start(xhci)) {
xhci_halt(xhci);
@@ -707,7 +705,7 @@ void xhci_stop(struct usb_hcd *hcd)
{
u32 temp;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- struct xhci_interrupter *ir = xhci->interrupters[0];
+ int i;
mutex_lock(&xhci->mutex);
@@ -742,7 +740,9 @@ void xhci_stop(struct usb_hcd *hcd)
"// Disabling event ring interrupts");
temp = readl(&xhci->op_regs->status);
writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
- xhci_disable_interrupter(xhci, ir);
+ for (i = 0; i < xhci->irqs_enabled; i++)
+ if (xhci->interrupters[i])
+ xhci_disable_interrupter(xhci, xhci->interrupters[i]);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory");
xhci_mem_cleanup(xhci);
@@ -1087,6 +1087,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume)
bool comp_timer_running = false;
bool pending_portevent = false;
bool suspended_usb3_devs = false;
+ int i;
if (!hcd->state)
return 0;
@@ -1180,7 +1181,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume)
xhci_dbg(xhci, "// Disabling event ring interrupts\n");
temp = readl(&xhci->op_regs->status);
writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
- xhci_disable_interrupter(xhci, xhci->interrupters[0]);
+ for (i = 0; i < xhci->irqs_enabled; i++)
+ if (xhci->interrupters[i])
+ xhci_disable_interrupter(xhci, xhci->interrupters[i]);
xhci_dbg(xhci, "cleaning up memory\n");
xhci_mem_cleanup(xhci);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 2b0796f6d00e..7707fd7564c5 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -45,6 +45,9 @@
*/
#define MAX_HC_INTRS 128
+/* Software cap for secondary interrupters; not a hardware limit. */
+#define MAX_SECONDARY_INTRNUM 4
+
/*
* xHCI register interface.
* This corresponds to the eXtensible Host Controller Interface (xHCI)
@@ -1497,6 +1500,11 @@ struct xhci_hub {
u8 min_rev;
};
+struct xhci_irq_ctx {
+ struct usb_hcd *hcd;
+ u8 intr_num;
+};
+
/* There is one xhci_hcd structure per controller */
struct xhci_hcd {
struct usb_hcd *main_hcd;
@@ -1533,6 +1541,12 @@ struct xhci_hcd {
/* data structures */
struct xhci_device_context_array *dcbaa;
struct xhci_interrupter **interrupters;
+ /* Number of secondary interrupters successfully allocated */
+ unsigned int secondary_irqs_alloc;
+ /* Number of IRQ vectors successfully requested (includes vector 0) */
+ unsigned int irqs_enabled;
+ /* MSI/MSI-X vector contexts. Vector 0 uses [0], secondary use [1..] */
+ struct xhci_irq_ctx irq_ctx[MAX_SECONDARY_INTRNUM + 1];
struct xhci_ring *cmd_ring;
unsigned int cmd_ring_state;
#define CMD_RING_STATE_RUNNING (1 << 0)
@@ -1895,7 +1909,7 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);
int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume);
irqreturn_t xhci_irq(struct usb_hcd *hcd);
-irqreturn_t xhci_msi_irq(int irq, void *hcd);
+irqreturn_t xhci_msi_irq(int irq, void *data);
int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev);
int xhci_alloc_tt_info(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
--
2.50.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v10 2/2] usb: xhci: enable secondary interrupters and route
2026-01-26 8:55 [PATCH v10 0/2] usb:xhc:route device to secondary interrupters raoxu
2026-01-26 8:58 ` [PATCH v10 1/2] usb: xhci: refactor IRQ/interrupter plumbing for raoxu
@ 2026-01-26 8:58 ` raoxu
2026-01-26 9:10 ` Greg KH
2026-01-26 14:49 ` kernel test robot
1 sibling, 2 replies; 6+ messages in thread
From: raoxu @ 2026-01-26 8:58 UTC (permalink / raw)
To: raoxu
Cc: gregkh, kenny, linux-usb, mathias.nyman, michal.pecio,
niklas.neronin, zhanjun
Some xHCI hosts expose multiple MSI/MSI-X vectors and support multiple
interrupters with independent event rings, but Linux commonly routes all
transfer completions through interrupter 0.
Allocate a small capped set of secondary interrupters in xhci_mem_init()
(MAX_SECONDARY_INTRNUM, default 4) and request up to the matching number
of IRQ vectors (bounded by what PCI core provides). Dispatch each vector
to its interrupter via the per-vector irq_ctx.
Route transfers per USB device (slot) by assigning vdev->interrupter at
device allocation time and programming the interrupter target (slot
context + TRB_INTR_TARGET) from that selection, so completions land on the
selected event ring. Drain a slot's secondary event ring at selected
command completion boundaries to reduce late-event artifacts when teardown
happens on interrupter 0.
Signed-off-by: raoxu <raoxu@uniontech.com>
---
drivers/usb/host/xhci-mem.c | 35 +++++++-
drivers/usb/host/xhci-pci.c | 35 +++++---
drivers/usb/host/xhci-ring.c | 151 ++++++++++++++++++++++++++++-------
drivers/usb/host/xhci.c | 14 ++++
drivers/usb/host/xhci.h | 10 +++
5 files changed, 207 insertions(+), 38 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 524c58a149d3..e65f84bc1c8f 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1197,6 +1197,15 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
ep0_ctx->tx_info = cpu_to_le32(EP_AVG_TRB_LENGTH(8));
+ /* Match Slot Context interrupter target to vdev->interrupter. */
+ if (dev->interrupter) {
+ u32 tt = le32_to_cpu(slot_ctx->tt_info);
+
+ tt &= ~SLOT_INTR_TARGET_MASK;
+ tt |= SLOT_INTR_TARGET(dev->interrupter->intr_num);
+ slot_ctx->tt_info = cpu_to_le32(tt);
+ }
+
trace_xhci_setup_addressable_virt_device(dev);
/* Steps 7 and 8 were done in xhci_alloc_virt_device() */
@@ -2402,7 +2411,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
dma_addr_t dma;
- unsigned int i;
+ unsigned int secondary_intr_num;
+ int i;
/*
* xHCI section 5.4.6 - Device Context array must be
@@ -2496,9 +2506,30 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
if (!xhci->interrupters[0])
goto fail;
- /* Since only the main interrupt is used, secondary_irqs_alloc is set to 0. */
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Allocating secondary event ring");
xhci->secondary_irqs_alloc = 0;
+ if (xhci->max_interrupters > 1)
+ secondary_intr_num = min_t(unsigned int,
+ xhci->max_interrupters - 1,
+ (unsigned int)MAX_SECONDARY_INTRNUM);
+
+ for (i = 1; i <= secondary_intr_num; i++) {
+ if (xhci->interrupters[i])
+ continue;
+ xhci->interrupters[i] = xhci_alloc_interrupter(xhci, i, flags);
+ if (!xhci->interrupters[i]) {
+ while (--i >= 1) {
+ xhci_free_interrupter(xhci, xhci->interrupters[i]);
+ xhci->interrupters[i] = NULL;
+ }
+ xhci->secondary_irqs_alloc = 0;
+ break;
+ }
+ xhci->secondary_irqs_alloc++;
+ }
+
/*
* Initialize all allocated interrupters here.
* Use allocated count as loop bound to avoid touching non-allocated
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index b3efd6d8fd9c..4ff3e29070b1 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -150,6 +150,8 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int ret;
+ unsigned int irqs_num;
+ int i;
struct xhci_irq_ctx *ctx;
/*
@@ -173,7 +175,7 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
/* TODO: Check with MSI Soc for sysdev */
xhci->nvecs = pci_alloc_irq_vectors(pdev, 1, xhci->nvecs,
- PCI_IRQ_MSIX | PCI_IRQ_MSI);
+ PCI_IRQ_MSIX | PCI_IRQ_MSI | PCI_IRQ_AFFINITY);
if (xhci->nvecs < 0) {
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"failed to allocate IRQ vectors");
@@ -183,14 +185,30 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
memset(xhci->irq_ctx, 0, sizeof(xhci->irq_ctx));
xhci->irqs_enabled = 0;
- ctx = &xhci->irq_ctx[0];
- ctx->hcd = hcd;
- ctx->intr_num = 0;
- ret = request_irq(pci_irq_vector(pdev, 0), xhci_msi_irq, 0, "xhci_hcd", ctx);
- if (ret)
- goto free_irq_vectors;
+ /*
+ * Request up to 1 + secondary_irqs_alloc vectors (vector 0 + secondary),
+ * limited by what PCI core actually allocated.
+ */
+ irqs_num = min_t(unsigned int,
+ (unsigned int)xhci->nvecs,
+ (unsigned int)(1 + xhci->secondary_irqs_alloc));
+
+ for (i = 0; i < irqs_num; i++) {
+ ctx = &xhci->irq_ctx[i];
+ ctx->hcd = hcd;
+ ctx->intr_num = i;
+ ret = request_irq(pci_irq_vector(pdev, i), xhci_msi_irq, 0,
+ "xhci_hcd", ctx);
+ if (ret) {
+ while (--i >= 0)
+ free_irq(pci_irq_vector(pdev, i), &xhci->irq_ctx[i]);
+ xhci->irqs_enabled = 0;
+ memset(xhci->irq_ctx, 0, sizeof(xhci->irq_ctx));
+ goto free_irq_vectors;
+ }
+ xhci->irqs_enabled++;
+ }
- xhci->irqs_enabled = 1;
hcd->msi_enabled = 1;
hcd->msix_enabled = pdev->msix_enabled;
return 0;
@@ -198,7 +216,6 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
free_irq_vectors:
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable %s interrupt",
pdev->msix_enabled ? "MSI-X" : "MSI");
- xhci->irqs_enabled = 0;
pci_free_irq_vectors(pdev);
legacy_irq:
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 3ea134c07c5f..f16339f47ed4 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -64,6 +64,9 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
u32 field1, u32 field2,
u32 field3, u32 field4, bool command_must_succeed);
+static void xhci_handle_slot_secondary_events(struct xhci_hcd *xhci,
+ union xhci_trb *cmd_trb);
+
/*
* Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
* address of the TRB.
@@ -1859,6 +1862,9 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
}
}
+ /* Handle slot secondary events before command-specific teardown logic */
+ xhci_handle_slot_secondary_events(xhci, cmd_trb);
+
cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
switch (cmd_type) {
case TRB_ENABLE_SLOT:
@@ -3138,6 +3144,72 @@ static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir
return 0;
}
+/*
+ * Handle a slot's secondary event ring at command completion boundaries.
+ *
+ * With secondary interrupters, transfer events may lag behind command
+ * completions (handled on interrupter 0). Commands that stop/reset/disable
+ * endpoints/slots can reclaim TD state before those transfer events are
+ * processed, leading to "Spurious event dma" reports. Call this from
+ * handle_cmd_completion() before command-specific completion handling.
+ */
+static void xhci_handle_slot_secondary_events(struct xhci_hcd *xhci,
+ union xhci_trb *cmd_trb)
+{
+ u32 field3, cmd_type, slot_id;
+ struct xhci_virt_device *vdev;
+ struct xhci_interrupter *sec_ir;
+ unsigned int intr;
+
+ /* No secondary routing -> nothing to do */
+ if (xhci->irqs_enabled <= 1)
+ return;
+
+ field3 = le32_to_cpu(cmd_trb->generic.field[3]);
+ cmd_type = TRB_FIELD_TO_TYPE(field3);
+ slot_id = TRB_TO_SLOT_ID(field3);
+
+ if (!slot_id)
+ return;
+
+ /*
+ * Handle only for commands that can stop/reset/disable endpoints/slots
+ * and/or cause the driver to reclaim TD ownership.
+ */
+ switch (cmd_type) {
+ case TRB_STOP_RING:
+ case TRB_SET_DEQ:
+ case TRB_RESET_EP:
+ case TRB_RESET_DEV:
+ case TRB_DISABLE_SLOT:
+ break;
+ case TRB_CONFIG_EP:
+ /* Only needed for deconfigure (drop endpoints) */
+ if (!(field3 & TRB_DC))
+ return;
+ break;
+ default:
+ return;
+ }
+
+ vdev = xhci->devs[slot_id];
+ if (!vdev)
+ return;
+
+ intr = vdev->interrupter->intr_num;
+ if (!intr)
+ return; /* slot is on primary interrupter */
+
+ sec_ir = xhci->interrupters[intr];
+ if (!sec_ir || !sec_ir->event_ring)
+ return;
+
+ lockdep_assert_held(&xhci->lock);
+
+ /* Handle pending events normally to complete URB/TD bookkeeping. */
+ xhci_handle_events(xhci, sec_ir, false);
+}
+
/*
* Move the event ring dequeue pointer to skip events kept in the secondary
* event ring. This is used to ensure that pending events in the ring are
@@ -3169,14 +3241,8 @@ void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
xhci_handle_events(xhci, ir, true);
}
-/*
- * xHCI spec says we can get an interrupt, and if the HC has an error condition,
- * we might get bad data out of the event ring. Section 4.10.2.7 has a list of
- * indicators of an event TRB error, but we check the status *first* to be safe.
- */
-irqreturn_t xhci_irq(struct usb_hcd *hcd)
+static irqreturn_t xhci_irq_ir(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
irqreturn_t ret = IRQ_HANDLED;
u32 status;
@@ -3188,7 +3254,11 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
goto out;
}
- if (!(status & STS_EINT)) {
+ /*
+ * STS_EINT is only meaningful for the primary interrupter (0).
+ * Secondary vectors may interrupt without STS_EINT set.
+ */
+ if (ir->intr_num == 0 && !(status & STS_EINT)) {
ret = IRQ_NONE;
goto out;
}
@@ -3204,30 +3274,52 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
goto out;
}
- /*
- * Clear the op reg interrupt status first,
- * so we can receive interrupts from other MSI-X interrupters.
- * Write 1 to clear the interrupt status.
- */
- status |= STS_EINT;
- writel(status, &xhci->op_regs->status);
+ if (ir->intr_num == 0) {
+ /*
+ * Clear the op reg interrupt status first,
+ * so we can receive interrupts from other MSI-X interrupters.
+ * Write 1 to clear the interrupt status.
+ */
+ status |= STS_EINT;
+ writel(status, &xhci->op_regs->status);
+ }
- /* This is the handler of the primary interrupter */
- xhci_handle_events(xhci, xhci->interrupters[0], false);
+ /* Handle events for this interrupter. */
+ xhci_handle_events(xhci, ir, false);
out:
spin_unlock(&xhci->lock);
return ret;
}
+/*
+ * xHCI spec says we can get an interrupt, and if the HC has an error condition,
+ * we might get bad data out of the event ring. Section 4.10.2.7 has a list of
+ * indicators of an event TRB error, but we check the status *first* to be safe.
+ */
+irqreturn_t xhci_irq(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ return xhci_irq_ir(xhci, xhci->interrupters[0]);
+}
+
irqreturn_t xhci_msi_irq(int irq, void *data)
{
struct xhci_irq_ctx *ctx = data;
+ struct xhci_hcd *xhci;
+ struct xhci_interrupter *ir;
- /* For now only vector 0 is requested; keep behavior unchanged. */
+ /* All MSI/MSI-X vectors use ctx (dev_id) to select interrupter.*/
if (!ctx || !ctx->hcd)
return IRQ_NONE;
- return xhci_irq(ctx->hcd);
+
+ xhci = hcd_to_xhci(ctx->hcd);
+ ir = xhci->interrupters[ctx->intr_num];
+ if (!ir)
+ return IRQ_NONE;
+
+ return xhci_irq_ir(xhci, ir);
}
EXPORT_SYMBOL_GPL(xhci_msi_irq);
@@ -3629,6 +3721,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
int sent_len, ret;
u32 field, length_field, remainder;
u64 addr, send_addr;
+ struct xhci_interrupter *ir;
ring = xhci_urb_to_transfer_ring(xhci, urb);
if (!ring)
@@ -3669,6 +3762,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
start_trb = &ring->enqueue->generic;
start_cycle = ring->cycle_state;
send_addr = addr;
+ ir = xhci->devs[slot_id]->interrupter;
/* Queue the TRBs, even if they are zero-length */
for (enqd_len = 0; first_trb || enqd_len < full_len;
@@ -3729,7 +3823,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
length_field = TRB_LEN(trb_buff_len) |
TRB_TD_SIZE(remainder) |
- TRB_INTR_TARGET(0);
+ TRB_INTR_TARGET(ir->intr_num);
queue_trb(xhci, ring, more_trbs_coming | need_zero_pkt,
lower_32_bits(send_addr),
@@ -3761,7 +3855,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
urb_priv->td[1].end_trb = ring->enqueue;
urb_priv->td[1].end_seg = ring->enq_seg;
field = TRB_TYPE(TRB_NORMAL) | ring->cycle_state | TRB_IOC;
- queue_trb(xhci, ring, 0, 0, 0, TRB_INTR_TARGET(0), field);
+ queue_trb(xhci, ring, 0, 0, 0, TRB_INTR_TARGET(ir->intr_num), field);
}
check_trb_math(urb, enqd_len);
@@ -3783,6 +3877,9 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
u32 field;
struct urb_priv *urb_priv;
struct xhci_td *td;
+ struct xhci_interrupter *ir;
+
+ ir = xhci->devs[slot_id]->interrupter;
ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
if (!ep_ring)
@@ -3805,7 +3902,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
if (last_trb_on_seg(ep_ring->enq_seg, ep_ring->enqueue + 1)) {
field = TRB_TYPE(TRB_TR_NOOP) | ep_ring->cycle_state;
queue_trb(xhci, ep_ring, false, 0, 0,
- TRB_INTR_TARGET(0), field);
+ TRB_INTR_TARGET(ir->intr_num), field);
}
}
@@ -3856,7 +3953,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
queue_trb(xhci, ep_ring, true,
setup->bRequestType | setup->bRequest << 8 | le16_to_cpu(setup->wValue) << 16,
le16_to_cpu(setup->wIndex) | le16_to_cpu(setup->wLength) << 16,
- TRB_LEN(8) | TRB_INTR_TARGET(0),
+ TRB_LEN(8) | TRB_INTR_TARGET(ir->intr_num),
/* Immediate data in pointer */
field);
@@ -3886,7 +3983,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
urb, 1);
length_field = TRB_LEN(urb->transfer_buffer_length) |
TRB_TD_SIZE(remainder) |
- TRB_INTR_TARGET(0);
+ TRB_INTR_TARGET(ir->intr_num);
if (setup->bRequestType & USB_DIR_IN)
field |= TRB_DIR_IN;
queue_trb(xhci, ep_ring, true,
@@ -3909,7 +4006,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
queue_trb(xhci, ep_ring, false,
0,
0,
- TRB_INTR_TARGET(0),
+ TRB_INTR_TARGET(ir->intr_num),
/* Event on completion */
field | TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state);
@@ -4099,7 +4196,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
xep = &xhci->devs[slot_id]->eps[ep_index];
ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
- ir = xhci->interrupters[0];
+ ir = xhci->devs[slot_id]->interrupter;
num_tds = urb->number_of_packets;
if (num_tds < 1) {
@@ -4200,7 +4297,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
urb, more_trbs_coming);
length_field = TRB_LEN(trb_buff_len) |
- TRB_INTR_TARGET(0);
+ TRB_INTR_TARGET(ir->intr_num);
/* xhci 1.1 with ETE uses TD Size field for TBC */
if (first_trb && xep->use_extended_tbc)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index cbf96cb51583..96934a29e39b 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4123,6 +4123,7 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
virt_dev->eps[i].ep_state &= ~EP_STOP_CMD_PENDING;
virt_dev->udev = NULL;
xhci_disable_slot(xhci, udev->slot_id);
+ virt_dev->interrupter = NULL;
spin_lock_irqsave(&xhci->lock, flags);
xhci_free_virt_device(xhci, virt_dev, udev->slot_id);
@@ -4219,6 +4220,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
unsigned long flags;
int ret, slot_id;
struct xhci_command *command;
+ unsigned int sec_irqs, intr_num;
command = xhci_alloc_command(xhci, true, GFP_KERNEL);
if (!command)
@@ -4275,6 +4277,18 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
udev->slot_id = slot_id;
+ /*
+ * Spread devices across IRQ-backed secondary interrupters [1..].
+ * If only vector 0 is enabled, default to interrupter 0.
+ */
+ sec_irqs = (xhci->irqs_enabled > 1) ? (xhci->irqs_enabled - 1) : 0;
+ if (sec_irqs) {
+ intr_num = slot_id % sec_irqs;
+ vdev->interrupter = xhci->interrupters[intr_num + 1];
+ } else {
+ vdev->interrupter = xhci->interrupters[0];
+ }
+
xhci_debugfs_create_slot(xhci, slot_id);
/*
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 7707fd7564c5..b30ace66ff62 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -408,6 +408,11 @@ struct xhci_slot_ctx {
#define SLOT_STATE_ADDRESSED 2
#define SLOT_STATE_CONFIGURED 3
+/* Interrupter Target - tt_info[31:22] (xHCI Slot Context) */
+#define SLOT_INTR_TARGET_SHIFT 22
+#define SLOT_INTR_TARGET_MASK (0x3ffU << SLOT_INTR_TARGET_SHIFT)
+#define SLOT_INTR_TARGET(p) (((p) & 0x3ffU) << SLOT_INTR_TARGET_SHIFT)
+
/**
* struct xhci_ep_ctx
* @ep_info: endpoint state, streams, mult, and interval information.
@@ -767,6 +772,11 @@ struct xhci_virt_device {
void *debugfs_private;
/* set if this endpoint is controlled via sideband access*/
struct xhci_sideband *sideband;
+ /*
+ * Default transfer-event interrupter for this USB device.
+ * Queueing code programs TRB_INTR_TARGET() from vdev->interrupter.
+ */
+ struct xhci_interrupter *interrupter;
};
/*
--
2.50.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v10 1/2] usb: xhci: refactor IRQ/interrupter plumbing for
2026-01-26 8:58 ` [PATCH v10 1/2] usb: xhci: refactor IRQ/interrupter plumbing for raoxu
@ 2026-01-26 9:06 ` Greg KH
0 siblings, 0 replies; 6+ messages in thread
From: Greg KH @ 2026-01-26 9:06 UTC (permalink / raw)
To: raoxu
Cc: kenny, linux-usb, mathias.nyman, michal.pecio, niklas.neronin,
zhanjun
On Mon, Jan 26, 2026 at 04:58:12PM +0800, raoxu wrote:
> Prepare xHCI for multiple IRQ vectors/interrupters without changing the
> current behavior.
>
> Introduce a per-vector irq context (hcd + intr_num) to use as request_irq()
> dev_id, and track the active vector count in xhci->irqs_enabled. Use this
> single bound to enable/disable interrupters consistently across run/stop/
> resume and to sync/free IRQs.
>
> Legacy IRQ fallback also keeps irqs_enabled >= 1 so interrupter 0 remains
> functional when MSI/MSI-X is unavailable.
>
> No functional change intended: still uses interrupter 0 only.
>
> Signed-off-by: raoxu <raoxu@uniontech.com>
We need a real name, not an email alias please.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v10 2/2] usb: xhci: enable secondary interrupters and route
2026-01-26 8:58 ` [PATCH v10 2/2] usb: xhci: enable secondary interrupters and route raoxu
@ 2026-01-26 9:10 ` Greg KH
2026-01-26 14:49 ` kernel test robot
1 sibling, 0 replies; 6+ messages in thread
From: Greg KH @ 2026-01-26 9:10 UTC (permalink / raw)
To: raoxu
Cc: kenny, linux-usb, mathias.nyman, michal.pecio, niklas.neronin,
zhanjun
On Mon, Jan 26, 2026 at 04:58:28PM +0800, raoxu wrote:
> Some xHCI hosts expose multiple MSI/MSI-X vectors and support multiple
> interrupters with independent event rings, but Linux commonly routes all
> transfer completions through interrupter 0.
>
> Allocate a small capped set of secondary interrupters in xhci_mem_init()
> (MAX_SECONDARY_INTRNUM, default 4) and request up to the matching number
> of IRQ vectors (bounded by what PCI core provides). Dispatch each vector
> to its interrupter via the per-vector irq_ctx.
>
> Route transfers per USB device (slot) by assigning vdev->interrupter at
> device allocation time and programming the interrupter target (slot
> context + TRB_INTR_TARGET) from that selection, so completions land on the
> selected event ring. Drain a slot's secondary event ring at selected
> command completion boundaries to reduce late-event artifacts when teardown
> happens on interrupter 0.
This all says what is happening, but not why. Why does it matter to
spread out the interrupts? What is the downside of not doing so? What
is the benifit of doing so? What benchmarks or real tests show any
changes at all here?
Given that the USB data path is very slow, because the hardware is slow,
moving interrupts around to other processors feels like an odd thing to
do as it's normally only done for busses that have real data throughput
needs (i.e. keep the irqs next to the cpus where the data is being
processed, like the networking stack does). For USB this feels odd as
once the data is handled by the host controller driver, the cpu affinity
is totally lost so any benifit you were expecting from spreading
interrupts out is lost.
So again, why is any of this needed at all? What hardware and workloads
actually matter here?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v10 2/2] usb: xhci: enable secondary interrupters and route
2026-01-26 8:58 ` [PATCH v10 2/2] usb: xhci: enable secondary interrupters and route raoxu
2026-01-26 9:10 ` Greg KH
@ 2026-01-26 14:49 ` kernel test robot
1 sibling, 0 replies; 6+ messages in thread
From: kernel test robot @ 2026-01-26 14:49 UTC (permalink / raw)
To: raoxu
Cc: llvm, oe-kbuild-all, gregkh, kenny, linux-usb, mathias.nyman,
michal.pecio, niklas.neronin, zhanjun
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.19-rc7 next-20260123]
[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-refactor-IRQ-interrupter-plumbing-for/20260126-170049
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link: https://lore.kernel.org/r/1FCECDEA86461C52%2B20260126085828.803972-1-raoxu%40uniontech.com
patch subject: [PATCH v10 2/2] usb: xhci: enable secondary interrupters and route
config: i386-defconfig (https://download.01.org/0day-ci/archive/20260126/202601262208.UybEjc9X-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260126/202601262208.UybEjc9X-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/202601262208.UybEjc9X-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/usb/host/xhci-mem.c:2513:6: warning: variable 'secondary_intr_num' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
2513 | if (xhci->max_interrupters > 1)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/usb/host/xhci-mem.c:2518:19: note: uninitialized use occurs here
2518 | for (i = 1; i <= secondary_intr_num; i++) {
| ^~~~~~~~~~~~~~~~~~
drivers/usb/host/xhci-mem.c:2513:2: note: remove the 'if' if its condition is always true
2513 | if (xhci->max_interrupters > 1)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2514 | secondary_intr_num = min_t(unsigned int,
drivers/usb/host/xhci-mem.c:2414:33: note: initialize the variable 'secondary_intr_num' to silence this warning
2414 | unsigned int secondary_intr_num;
| ^
| = 0
1 warning generated.
vim +2513 drivers/usb/host/xhci-mem.c
2409
2410 int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
2411 {
2412 struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
2413 dma_addr_t dma;
2414 unsigned int secondary_intr_num;
2415 int i;
2416
2417 /*
2418 * xHCI section 5.4.6 - Device Context array must be
2419 * "physically contiguous and 64-byte (cache line) aligned".
2420 */
2421 xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, flags);
2422 if (!xhci->dcbaa)
2423 goto fail;
2424
2425 xhci->dcbaa->dma = dma;
2426 xhci_dbg_trace(xhci, trace_xhci_dbg_init,
2427 "Device context base array address = 0x%pad (DMA), %p (virt)",
2428 &xhci->dcbaa->dma, xhci->dcbaa);
2429
2430 /*
2431 * Initialize the ring segment pool. The ring must be a contiguous
2432 * structure comprised of TRBs. The TRBs must be 16 byte aligned,
2433 * however, the command ring segment needs 64-byte aligned segments
2434 * and our use of dma addresses in the trb_address_map radix tree needs
2435 * TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need.
2436 */
2437 if (xhci->quirks & XHCI_TRB_OVERFETCH)
2438 /* Buggy HC prefetches beyond segment bounds - allocate dummy space at the end */
2439 xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
2440 TRB_SEGMENT_SIZE * 2, TRB_SEGMENT_SIZE * 2, xhci->page_size * 2);
2441 else
2442 xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
2443 TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size);
2444 if (!xhci->segment_pool)
2445 goto fail;
2446
2447 /* See Table 46 and Note on Figure 55 */
2448 xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev, 2112, 64,
2449 xhci->page_size);
2450 if (!xhci->device_pool)
2451 goto fail;
2452
2453 /*
2454 * Linear stream context arrays don't have any boundary restrictions,
2455 * and only need to be 16-byte aligned.
2456 */
2457 xhci->small_streams_pool = dma_pool_create("xHCI 256 byte stream ctx arrays",
2458 dev, SMALL_STREAM_ARRAY_SIZE, 16, 0);
2459 if (!xhci->small_streams_pool)
2460 goto fail;
2461
2462 /*
2463 * Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE will be
2464 * allocated with dma_alloc_coherent().
2465 */
2466
2467 xhci->medium_streams_pool = dma_pool_create("xHCI 1KB stream ctx arrays",
2468 dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0);
2469 if (!xhci->medium_streams_pool)
2470 goto fail;
2471
2472 /*
2473 * refer to xhci rev1_2 protocol 5.3.3 max ports is 255.
2474 * refer to xhci rev1_2 protocol 6.4.3.14 port bandwidth buffer need
2475 * to be 16-byte aligned.
2476 */
2477 xhci->port_bw_pool = dma_pool_create("xHCI 256 port bw ctx arrays",
2478 dev, GET_PORT_BW_ARRAY_SIZE, 16, 0);
2479 if (!xhci->port_bw_pool)
2480 goto fail;
2481
2482 /* Set up the command ring to have one segments for now. */
2483 xhci->cmd_ring = xhci_ring_alloc(xhci, 1, TYPE_COMMAND, 0, flags);
2484 if (!xhci->cmd_ring)
2485 goto fail;
2486
2487 xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocated command ring at %p", xhci->cmd_ring);
2488 xhci_dbg_trace(xhci, trace_xhci_dbg_init, "First segment DMA is 0x%pad",
2489 &xhci->cmd_ring->first_seg->dma);
2490
2491 /*
2492 * Reserve one command ring TRB for disabling LPM.
2493 * Since the USB core grabs the shared usb_bus bandwidth mutex before
2494 * disabling LPM, we only need to reserve one TRB for all devices.
2495 */
2496 xhci->cmd_ring_reserved_trbs++;
2497
2498 /* Allocate and set up primary interrupter 0 with an event ring. */
2499 xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring");
2500 xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters),
2501 flags, dev_to_node(dev));
2502 if (!xhci->interrupters)
2503 goto fail;
2504
2505 xhci->interrupters[0] = xhci_alloc_interrupter(xhci, 0, flags);
2506 if (!xhci->interrupters[0])
2507 goto fail;
2508
2509 xhci_dbg_trace(xhci, trace_xhci_dbg_init,
2510 "Allocating secondary event ring");
2511 xhci->secondary_irqs_alloc = 0;
2512
> 2513 if (xhci->max_interrupters > 1)
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-01-26 14:49 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-26 8:55 [PATCH v10 0/2] usb:xhc:route device to secondary interrupters raoxu
2026-01-26 8:58 ` [PATCH v10 1/2] usb: xhci: refactor IRQ/interrupter plumbing for raoxu
2026-01-26 9:06 ` Greg KH
2026-01-26 8:58 ` [PATCH v10 2/2] usb: xhci: enable secondary interrupters and route raoxu
2026-01-26 9:10 ` Greg KH
2026-01-26 14:49 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox