* [PATCH v2 1/4] usb: xhci: store xhci pointer in xhci_interrupter
2026-05-12 3:14 [PATCH v2 0/4] usb: xhci: prepare MSI path for secondary interrupters Xu Rao
@ 2026-05-12 3:18 ` Xu Rao
2026-05-12 3:18 ` [PATCH v2 2/4] usb: xhci: route MSI through interrupter context Xu Rao
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Xu Rao @ 2026-05-12 3:18 UTC (permalink / raw)
To: raoxu; +Cc: gregkh, linux-usb, mathias.nyman, michal.pecio, niklas.neronin
Secondary interrupters keep event ring state in
struct xhci_interrupter, but the MSI path needs the owning
host controller as well.
Store the xhci_hcd pointer when an interrupter is initialized.
This prepares the interrupt path to use interrupter context
directly.
No functional change.
Signed-off-by: Xu Rao <raoxu@uniontech.com>
---
drivers/usb/host/xhci-mem.c | 1 +
drivers/usb/host/xhci.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 997fe90f54e5..73be00cdd98f 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -2331,6 +2331,7 @@ void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num)
u32 erst_size;
ir = xhci->interrupters[intr_num];
+ ir->xhci = xhci;
ir->intr_num = intr_num;
ir->ir_set = &xhci->run_regs->ir_set[intr_num];
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index aeecd301f207..96cd3e751067 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1445,6 +1445,7 @@ struct xhci_bus_state {
};
struct xhci_interrupter {
+ struct xhci_hcd *xhci;
struct xhci_ring *event_ring;
struct xhci_erst erst;
struct xhci_intr_reg __iomem *ir_set;
--
2.50.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v2 2/4] usb: xhci: route MSI through interrupter context
2026-05-12 3:14 [PATCH v2 0/4] usb: xhci: prepare MSI path for secondary interrupters Xu Rao
2026-05-12 3:18 ` [PATCH v2 1/4] usb: xhci: store xhci pointer in xhci_interrupter Xu Rao
@ 2026-05-12 3:18 ` Xu Rao
2026-05-12 3:18 ` [PATCH v2 3/4] usb: xhci-pci: use the interrupter entry as MSI dev_id Xu Rao
2026-05-12 3:19 ` [PATCH v2 4/4] usb: xhci: clear USBSTS EINT only for interrupter 0 Xu Rao
3 siblings, 0 replies; 5+ messages in thread
From: Xu Rao @ 2026-05-12 3:18 UTC (permalink / raw)
To: raoxu; +Cc: gregkh, linux-usb, mathias.nyman, michal.pecio, niklas.neronin
The event handler already processes one interrupter at a
time, but xhci_msi_irq() still enters through usb_hcd and
then selects interrupter 0.
Move the shared MSI logic into an internal handler that takes
xhci_interrupter directly. Keep xhci_irq() as the wrapper for
the primary interrupt path.
Signed-off-by: Xu Rao <raoxu@uniontech.com>
---
drivers/usb/host/xhci-ring.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index e47e644b296e..63b97c393153 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3174,9 +3174,9 @@ void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
* 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_handler(struct xhci_interrupter *ir)
{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_hcd *xhci = ir->xhci;
irqreturn_t ret = IRQ_HANDLED;
u32 status;
@@ -3212,17 +3212,25 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
*/
writel(STS_EINT, &xhci->op_regs->status);
- /* This is the handler of the primary interrupter */
- xhci_handle_events(xhci, xhci->interrupters[0], false);
+ xhci_handle_events(xhci, ir, false);
out:
spin_unlock(&xhci->lock);
return ret;
}
-irqreturn_t xhci_msi_irq(int irq, void *hcd)
+irqreturn_t xhci_irq(struct usb_hcd *hcd)
{
- return xhci_irq(hcd);
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ return xhci_irq_handler(xhci->interrupters[0]);
+}
+
+irqreturn_t xhci_msi_irq(int irq, void *data)
+{
+ struct xhci_interrupter *ir = *(struct xhci_interrupter **)data;
+
+ return xhci_irq_handler(ir);
}
EXPORT_SYMBOL_GPL(xhci_msi_irq);
--
2.50.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v2 3/4] usb: xhci-pci: use the interrupter entry as MSI dev_id
2026-05-12 3:14 [PATCH v2 0/4] usb: xhci: prepare MSI path for secondary interrupters Xu Rao
2026-05-12 3:18 ` [PATCH v2 1/4] usb: xhci: store xhci pointer in xhci_interrupter Xu Rao
2026-05-12 3:18 ` [PATCH v2 2/4] usb: xhci: route MSI through interrupter context Xu Rao
@ 2026-05-12 3:18 ` Xu Rao
2026-05-12 3:19 ` [PATCH v2 4/4] usb: xhci: clear USBSTS EINT only for interrupter 0 Xu Rao
3 siblings, 0 replies; 5+ messages in thread
From: Xu Rao @ 2026-05-12 3:18 UTC (permalink / raw)
To: raoxu; +Cc: gregkh, linux-usb, mathias.nyman, michal.pecio, niklas.neronin
After routing xhci_msi_irq() through xhci_interrupter, PCI
MSI setup still registers the primary vector with usb_hcd as
the request_irq() dev_id.
Use &xhci->interrupters[0] instead, pass the same dev_id to
free_irq(), and release the IRQ before xhci_stop() tears
down the interrupter array. Disable the primary interrupter
before free_irq() so the interrupt source is quiesced before
the MSI vector is released. Export xhci_disable_interrupter()
for xhci-pci when built as a separate module.
Signed-off-by: Xu Rao <raoxu@uniontech.com>
---
drivers/usb/host/xhci-pci.c | 9 +++++----
drivers/usb/host/xhci.c | 1 +
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 585b2f3117b0..9a9c17232b02 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -134,7 +134,8 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci)
if (hcd->irq > 0)
return;
- free_irq(pci_irq_vector(pdev, 0), xhci_to_hcd(xhci));
+ xhci_disable_interrupter(xhci, xhci->interrupters[0]);
+ free_irq(pci_irq_vector(pdev, 0), &xhci->interrupters[0]);
pci_free_irq_vectors(pdev);
hcd->msix_enabled = 0;
}
@@ -175,7 +176,7 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
}
ret = request_irq(pci_irq_vector(pdev, 0), xhci_msi_irq, 0, "xhci_hcd",
- xhci_to_hcd(xhci));
+ &xhci->interrupters[0]);
if (ret)
goto free_irq_vectors;
@@ -225,10 +226,10 @@ static void xhci_pci_stop(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- xhci_stop(hcd);
-
if (usb_hcd_is_primary_hcd(hcd))
xhci_cleanup_msix(xhci);
+
+ xhci_stop(hcd);
}
/* called after powerup, by probe or system-pm "wakeup" */
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index a54f5b57f205..4edb2ce6715a 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -345,6 +345,7 @@ int xhci_disable_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
return 0;
}
+EXPORT_SYMBOL_GPL(xhci_disable_interrupter);
/* interrupt moderation interval imod_interval in nanoseconds */
int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
--
2.50.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v2 4/4] usb: xhci: clear USBSTS EINT only for interrupter 0
2026-05-12 3:14 [PATCH v2 0/4] usb: xhci: prepare MSI path for secondary interrupters Xu Rao
` (2 preceding siblings ...)
2026-05-12 3:18 ` [PATCH v2 3/4] usb: xhci-pci: use the interrupter entry as MSI dev_id Xu Rao
@ 2026-05-12 3:19 ` Xu Rao
3 siblings, 0 replies; 5+ messages in thread
From: Xu Rao @ 2026-05-12 3:19 UTC (permalink / raw)
To: raoxu; +Cc: gregkh, linux-usb, mathias.nyman, michal.pecio, niklas.neronin
USBSTS.EINT is controller-wide, while IMAN.IP is tracked per
interrupter.
If primary interrupter 0 and secondary interrupter 1 both
have pending events, xHC can set USBSTS.EINT, IR0.IMAN.IP,
and IR1.IMAN.IP at the same time. If the secondary handler
clears USBSTS.EINT first, the primary handler can return
IRQ_NONE without servicing the primary event ring.
Limit the USBSTS.EINT test and clear to interrupter 0. Only
interrupter 0 has an IRQ today, so this does not change the
current primary interrupt path.
Signed-off-by: Xu Rao <raoxu@uniontech.com>
---
drivers/usb/host/xhci-ring.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 63b97c393153..9aa5cd63febb 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3188,7 +3188,7 @@ static irqreturn_t xhci_irq_handler(struct xhci_interrupter *ir)
goto out;
}
- if (!(status & STS_EINT)) {
+ if (ir->intr_num == 0 && !(status & STS_EINT)) {
ret = IRQ_NONE;
goto out;
}
@@ -3209,8 +3209,10 @@ static irqreturn_t xhci_irq_handler(struct xhci_interrupter *ir)
* Clear the op reg interrupt status first,
* so we can receive interrupts from other MSI-X interrupters.
* USBSTS bits are write 1 to clear.
+ * USBSTS.EINT is controller-wide, so only interrupter 0 clears it.
*/
- writel(STS_EINT, &xhci->op_regs->status);
+ if (ir->intr_num == 0)
+ writel(STS_EINT, &xhci->op_regs->status);
xhci_handle_events(xhci, ir, false);
out:
--
2.50.1
^ permalink raw reply related [flat|nested] 5+ messages in thread