* [PATCH 0/4] usb: xhci: prepare MSI path for secondary interrupters
@ 2026-04-30 6:38 Xu Rao
2026-04-30 6:41 ` [PATCH 1/4] usb: xhci: store xhci pointer in xhci_interrupter raoxu
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Xu Rao @ 2026-04-30 6:38 UTC (permalink / raw)
To: mathias.nyman; +Cc: gregkh, linux-usb, michal.pecio, niklas.neronin, raoxu
Hi,
This series prepares the xHCI MSI path for secondary interrupters.
Instead of using struct usb_hcd as the MSI IRQ dev_id and deriving
interrupter 0 in the handler, pass struct xhci_interrupter as the IRQ
dev_id. This makes the MSI entry path operate on the same object that
owns the event ring and interrupter registers.
The primary path remains unchanged. Interrupter 0 is still the only
interrupter used by the normal xHCI path, and this series does not
enable extra vectors, change IRQ affinity, or add any new routing
policy.
USBSTS.EINT handling is also kept on interrupter 0. The bit is
controller-scoped, while IMAN.IP is per interrupter, so secondary
interrupters should not clear the controller-wide EINT status.
This is only a preparation step for future secondary interrupter users
and does not change the current primary xHCI interrupt behaviour.
raoxu (4):
usb: xhci: store xhci_hcd pointer in xhci_interrupter
usb: xhci: route MSI through interrupter context
usb: xhci-pci: use the interrupter entry as MSI dev_id
usb: xhci: clear USBSTS EINT only for interrupter 0
drivers/usb/host/xhci-mem.c | 1 +
drivers/usb/host/xhci-pci.c | 8 ++++----
drivers/usb/host/xhci-ring.c | 26 ++++++++++++++++++--------
drivers/usb/host/xhci.h | 1 +
4 files changed, 24 insertions(+), 12 deletions(-)
--
2.50.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [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
* [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
* 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
end of thread, other threads:[~2026-05-06 5:12 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [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
2026-04-30 6:42 ` [PATCH 4/4] usb: xhci: clear USBSTS EINT only for interrupter 0 raoxu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox