* [PATCH 1/4] usb: xhci: store xhci pointer in xhci_interrupter
2026-04-30 6:38 [PATCH 0/4] usb: xhci: prepare MSI path for secondary interrupters Xu Rao
@ 2026-04-30 6:41 ` raoxu
2026-04-30 6:41 ` [PATCH 2/4] usb: xhci: route MSI through interrupter context raoxu
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: raoxu @ 2026-04-30 6:41 UTC (permalink / raw)
To: raoxu; +Cc: gregkh, linux-usb, mathias.nyman, michal.pecio, niklas.neronin
From: Xu Rao <raoxu@uniontech.com>
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] 7+ messages in thread* [PATCH 2/4] usb: xhci: route MSI through interrupter context
2026-04-30 6:38 [PATCH 0/4] usb: xhci: prepare MSI path for secondary interrupters Xu Rao
2026-04-30 6:41 ` [PATCH 1/4] usb: xhci: store xhci pointer in xhci_interrupter raoxu
@ 2026-04-30 6:41 ` raoxu
2026-04-30 6:42 ` [PATCH 3/4] usb: xhci-pci: use the interrupter entry as MSI dev_id raoxu
2026-04-30 6:42 ` [PATCH 4/4] usb: xhci: clear USBSTS EINT only for interrupter 0 raoxu
3 siblings, 0 replies; 7+ messages in thread
From: raoxu @ 2026-04-30 6:41 UTC (permalink / raw)
To: raoxu; +Cc: gregkh, linux-usb, mathias.nyman, michal.pecio, niklas.neronin
From: Xu Rao <raoxu@uniontech.com>
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] 7+ messages in thread* [PATCH 3/4] usb: xhci-pci: use the interrupter entry as MSI dev_id
2026-04-30 6:38 [PATCH 0/4] usb: xhci: prepare MSI path for secondary interrupters Xu Rao
2026-04-30 6:41 ` [PATCH 1/4] usb: xhci: store xhci pointer in xhci_interrupter raoxu
2026-04-30 6:41 ` [PATCH 2/4] usb: xhci: route MSI through interrupter context raoxu
@ 2026-04-30 6:42 ` raoxu
2026-05-05 9:53 ` Neronin, Niklas
2026-04-30 6:42 ` [PATCH 4/4] usb: xhci: clear USBSTS EINT only for interrupter 0 raoxu
3 siblings, 1 reply; 7+ messages in thread
From: raoxu @ 2026-04-30 6:42 UTC (permalink / raw)
To: raoxu; +Cc: gregkh, linux-usb, mathias.nyman, michal.pecio, niklas.neronin
From: Xu Rao <raoxu@uniontech.com>
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.
Signed-off-by: Xu Rao <raoxu@uniontech.com>
---
drivers/usb/host/xhci-pci.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 585b2f3117b0..6df2fe78ab47 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -134,7 +134,7 @@ 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));
+ free_irq(pci_irq_vector(pdev, 0), &xhci->interrupters[0]);
pci_free_irq_vectors(pdev);
hcd->msix_enabled = 0;
}
@@ -175,7 +175,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 +225,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" */
--
2.50.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH 3/4] usb: xhci-pci: use the interrupter entry as MSI dev_id
2026-04-30 6:42 ` [PATCH 3/4] usb: xhci-pci: use the interrupter entry as MSI dev_id raoxu
@ 2026-05-05 9:53 ` Neronin, Niklas
2026-05-06 5:11 ` Xu Rao
0 siblings, 1 reply; 7+ messages in thread
From: Neronin, Niklas @ 2026-05-05 9:53 UTC (permalink / raw)
To: raoxu; +Cc: gregkh, linux-usb, mathias.nyman, michal.pecio
On 30/04/2026 9.42, raoxu wrote:
> From: Xu Rao <raoxu@uniontech.com>
>
> 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.
>
...
>
> @@ -225,10 +225,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);
Won't this now free the MSI-X interrupt before it has been disabled?
Best Regards,
Niklas
> }
>
> /* called after powerup, by probe or system-pm "wakeup" */
> --
> 2.50.1
>
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH 3/4] usb: xhci-pci: use the interrupter entry as MSI dev_id
2026-05-05 9:53 ` Neronin, Niklas
@ 2026-05-06 5:11 ` Xu Rao
0 siblings, 0 replies; 7+ messages in thread
From: Xu Rao @ 2026-05-06 5:11 UTC (permalink / raw)
To: niklas.neronin; +Cc: gregkh, linux-usb, mathias.nyman, michal.pecio, raoxu
On 50/05/2026 17.54, Niklas wrote:
> > @@ -225,10 +225,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);
>
> Won't this now free the MSI-X interrupt before it has been disabled?
Yes, the reason for moving xhci_cleanup_msix() ahead of
xhci_stop() is that free_irq() now uses
&xhci->interrupters[0] as the dev_id, while xhci_stop()
reaches xhci_mem_cleanup(), which clears the interrupter
entries and later frees the interrupter array.
Without that reordering, xhci_cleanup_msix() could use an
already cleared interrupter entry.
You are right that moving xhci_cleanup_msix() ahead of
xhci_stop() alone is not enough.
I think xhci_cleanup_msix() should also quiesce the primary
interrupter before free_irq(), so that the interrupt source
is disabled before the MSI vector is released.
Would this look correct to you?
@@ -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;
}
thanks,
Xu Rao
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 4/4] usb: xhci: clear USBSTS EINT only for interrupter 0
2026-04-30 6:38 [PATCH 0/4] usb: xhci: prepare MSI path for secondary interrupters Xu Rao
` (2 preceding siblings ...)
2026-04-30 6:42 ` [PATCH 3/4] usb: xhci-pci: use the interrupter entry as MSI dev_id raoxu
@ 2026-04-30 6:42 ` raoxu
3 siblings, 0 replies; 7+ messages in thread
From: raoxu @ 2026-04-30 6:42 UTC (permalink / raw)
To: raoxu; +Cc: gregkh, linux-usb, mathias.nyman, michal.pecio, niklas.neronin
From: Xu Rao <raoxu@uniontech.com>
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] 7+ messages in thread