Linux USB
 help / color / mirror / Atom feed
* [PATCH v2 0/4] usb: xhci: prepare MSI path for secondary interrupters
@ 2026-05-12  3:14 Xu Rao
  2026-05-12  3:18 ` [PATCH v2 1/4] usb: xhci: store xhci pointer in xhci_interrupter Xu Rao
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Xu Rao @ 2026-05-12  3:14 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.

Changes in v2:
- Patch 3 disables the primary interrupter before freeing the MSI IRQ,
  so the interrupt source is quiesced before releasing the vector.
- Patch 3 exports xhci_disable_interrupter() for xhci-pci module builds.

Xu Rao (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  |  9 +++++----
 drivers/usb/host/xhci.c      |  1 +
 drivers/usb/host/xhci-ring.c | 26 ++++++++++++++++++--------
 drivers/usb/host/xhci.h      |  1 +
 5 files changed, 26 insertions(+), 12 deletions(-)

--
2.50.1

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [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

end of thread, other threads:[~2026-05-12  3:19 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox