* Re: [PATCH] usb: gadget: tegra-xudc: drain EP pipeline before DMA unmap
[not found] <6a226993.a5745248.34e478.a9cd@mx.google.com>
@ 2026-06-05 6:35 ` Greg KH
0 siblings, 0 replies; 4+ messages in thread
From: Greg KH @ 2026-06-05 6:35 UTC (permalink / raw)
To: vishalmimani008
Cc: linux-usb, linux-tegra, stable, thierry.reding, jonathanh, digetx
On Thu, Jun 04, 2026 at 11:15:47PM -0700, vishalmimani008@gmail.com wrote:
> RnJvbTogVmlzaGFsIEt1bWFyIDx2aXNoYWxtaW1hbmkwMDhAZ21haWwuY29tPgpEYXRlOiBGcmks
> IDUgSnVuIDIwMjYgMTQ6MDg6NTQgKzA5MDAKU3ViamVjdDogW1BBVENIXSB1c2I6IGdhZGdldDog
<snip>
Something went wrong :(
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH] usb: gadget: tegra-xudc: drain EP pipeline before DMA unmap
@ 2026-06-05 7:27 Vishal Kumar
2026-06-05 14:59 ` Greg KH
0 siblings, 1 reply; 4+ messages in thread
From: Vishal Kumar @ 2026-06-05 7:27 UTC (permalink / raw)
To: linux-usb; +Cc: linux-tegra, stable, gregkh, thierry.reding, jonathanh, digetx
From: Vishal Kumar <vishalmimani008@gmail.com>
Date: Fri, 5 Jun 2026 14:08:54 +0900
Subject: [PATCH] usb: gadget: tegra-xudc: drain EP pipeline before DMA
unmap
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
On Tegra186/194/234 the XUDC appears to post a transfer-completion
event when the DMA write is dispatched to the AXI interconnect, before
the store is committed to memory. Under SMMU strict mode dma_unmap()
synchronously removes the IOVA TLB entry. If an in-flight AXI write
to that IOVA has not yet been committed, the SMMU raises a translation
fault (fsr=0x402) that permanently wedges the bulk endpoint; the host
cdc_ncm TX queue stalls and fires NETDEV WATCHDOG after 5 s.
Fix for non-control endpoints: poll EP_THREAD_ACTIVE until the endpoint
sequencer goes idle before calling dma_unmap(). Follow the poll with an
MMIO read-back that orders prior CPU writes to device memory. Only
after that does dma_unmap() invalidate the TLB entry.
On timeout, skip the dma_unmap to avoid triggering the SMMU fault. The
DMA mapping leaks, but the hardware is already in an unrecoverable state
at that point.
ep_wait_for_inactive() uses readl_poll_timeout_atomic() (1 µs poll,
100 µs timeout), already called from IRQ context in
__tegra_xudc_ep_dequeue(). Change its return type from void to int so
both call sites can detect and report a timeout.
Control endpoints (EP0) are excluded: their completions go through the
control-transfer state machine where the DMA is fully committed before
req_done is called.
Fixes: d720f0f7bfa0 ("usb: gadget: Add Tegra XUSB device mode controller driver")
Cc: stable@vger.kernel.org
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: Jonathan Hunter <jonathanh@nvidia.com>
Cc: Dmitry Osipenko <digetx@gmail.com>
Cc: linux-tegra@vger.kernel.org
Signed-off-by: Vishal Kumar <vishalmimani008@gmail.com>
---
drivers/usb/gadget/udc/tegra-xudc.c | 35 ++++++++++++++++++++++++-----
1 file changed, 30 insertions(+), 5 deletions(-)
diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index e9d33be02..8f1d52af0 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -1026,9 +1026,9 @@ static void ep_wait_for_stopped(struct tegra_xudc *xudc, unsigned int ep)
xudc_writel(xudc, BIT(ep), EP_STOPPED);
}
-static void ep_wait_for_inactive(struct tegra_xudc *xudc, unsigned int ep)
+static int ep_wait_for_inactive(struct tegra_xudc *xudc, unsigned int ep)
{
- xudc_readl_poll(xudc, EP_THREAD_ACTIVE, BIT(ep), 0);
+ return xudc_readl_poll(xudc, EP_THREAD_ACTIVE, BIT(ep), 0);
}
static void tegra_xudc_req_done(struct tegra_xudc_ep *ep,
@@ -1049,8 +1049,31 @@ static void tegra_xudc_req_done(struct tegra_xudc_ep *ep,
(xudc->setup_state ==
DATA_STAGE_XFER));
} else {
- usb_gadget_unmap_request(&xudc->gadget, &req->usb_req,
- usb_endpoint_dir_in(ep->desc));
+ /*
+ * Drain the endpoint DMA pipeline before unmapping.
+ *
+ * Under SMMU strict mode dma_unmap() synchronously
+ * invalidates the IOVA TLB entry. On Tegra186/194/234 the
+ * XUDC appears to post the completion event when the DMA
+ * write is dispatched to the AXI interconnect, before the
+ * store is committed to memory. A subsequent dma_unmap()
+ * can remove the IOVA translation while the write is still
+ * in-flight, triggering a translation fault (fsr=0x402) that
+ * permanently wedges the bulk endpoint.
+ *
+ * Wait for EP_THREAD_ACTIVE to clear (endpoint sequencer
+ * idle). On timeout skip the unmap to avoid the SMMU fault;
+ * the DMA mapping leaks but the hardware is already in an
+ * unrecoverable state.
+ */
+ if (!WARN_ONCE(ep_wait_for_inactive(xudc, ep->index),
+ "ep%u: DMA drain timed out; skipping dma_unmap\n",
+ ep->index)) {
+ /* MMIO read-back orders prior CPU writes to device memory. */
+ xudc_readl(xudc, EP_THREAD_ACTIVE);
+ usb_gadget_unmap_request(&xudc->gadget, &req->usb_req,
+ usb_endpoint_dir_in(ep->desc));
+ }
}
spin_unlock(&xudc->lock);
@@ -1451,7 +1474,9 @@ __tegra_xudc_ep_dequeue(struct tegra_xudc_ep *ep,
/* Halt DMA for this endpoint. */
if (ep_ctx_read_state(ep->context) == EP_STATE_RUNNING) {
ep_pause(xudc, ep->index);
- ep_wait_for_inactive(xudc, ep->index);
+ if (ep_wait_for_inactive(xudc, ep->index))
+ dev_warn(xudc->dev, "ep%u: DMA drain timed out during dequeue\n",
+ ep->index);
}
deq_trb = trb_phys_to_virt(ep, ep_ctx_read_deq_ptr(ep->context));
--
2.25.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] usb: gadget: tegra-xudc: drain EP pipeline before DMA unmap
2026-06-05 7:27 [PATCH] usb: gadget: tegra-xudc: drain EP pipeline before DMA unmap Vishal Kumar
@ 2026-06-05 14:59 ` Greg KH
0 siblings, 0 replies; 4+ messages in thread
From: Greg KH @ 2026-06-05 14:59 UTC (permalink / raw)
To: Vishal Kumar
Cc: linux-usb, linux-tegra, stable, thierry.reding, jonathanh, digetx
On Fri, Jun 05, 2026 at 12:27:19AM -0700, Vishal Kumar wrote:
> From: Vishal Kumar <vishalmimani008@gmail.com>
> Date: Fri, 5 Jun 2026 14:08:54 +0900
> Subject: [PATCH] usb: gadget: tegra-xudc: drain EP pipeline before DMA
> unmap
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: 8bit
Something went wrong here :(
thanks,
greg k-h
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH] usb: gadget: tegra-xudc: drain EP pipeline before dma_unmap
@ 2026-06-06 2:40 Vishal Kumar
0 siblings, 0 replies; 4+ messages in thread
From: Vishal Kumar @ 2026-06-06 2:40 UTC (permalink / raw)
To: linux-usb
Cc: gregkh, thierry.reding, jonathanh, linux-tegra, linux-kernel,
Vishal Kumar, stable
On Tegra186/194/234 the XUDC posts a transfer-completion event when the
DMA write is dispatched to the AXI interconnect, before the store is
committed to memory. Under SMMU strict mode dma_unmap() synchronously
invalidates the IOVA TLB entry. An in-flight AXI write to the
just-unmapped IOVA triggers a translation fault (fsr=0x402) that
permanently wedges the bulk-OUT endpoint.
Observed on Tegra234 (Jetson Orin Nano) at ~170 MB/s USB-NCM transfers:
arm-smmu 8000000.iommu: Unhandled context fault: fsr=0x402,
iova=0xfffb5000, cbfrsynra=0x100f, cb=3
tegra-mc 2c00000.memory-controller: EMEM address decode error
cbfrsynra=0x100f identifies XUDC (StreamID 0x0f per DT), cb=3 is iommu
group 4 (3550000.usb). fsr=0x402 is a translation fault on a DMA write.
Fix: poll EP_THREAD_ACTIVE before calling usb_gadget_unmap_request() for
non-control endpoints. EP_THREAD_ACTIVE clearing is the hardware's
guarantee that the endpoint sequencer is idle and all AXI transactions
have completed, so the subsequent TLB invalidation cannot race an
in-flight write.
Also change ep_wait_for_inactive() to return the readl_poll_timeout()
status so callers can detect a timeout. On timeout in the completion
path, skip dma_unmap() to avoid the translation fault and force
req->usb_req.status = -EIO so the gadget driver does not treat the
transfer as successful or requeue the still-mapped buffer. On timeout
in the dequeue path, emit a warning.
Fixes: 49d6f3dd4abe ("usb: gadget: add tegra xusb device mode driver")
Cc: <stable@vger.kernel.org>
Signed-off-by: Vishal Kumar <vishalmimani008@gmail.com>
---
drivers/usb/gadget/udc/tegra-xudc.c | 47 ++++++++++++++++++++++------
1 file changed, 38 insertions(+), 9 deletions(-)
diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 0b63b8c0a..3f18beddf 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -1023,9 +1023,9 @@ static void ep_wait_for_stopped(struct tegra_xudc *xudc, unsigned int ep)
xudc_writel(xudc, BIT(ep), EP_STOPPED);
}
-static void ep_wait_for_inactive(struct tegra_xudc *xudc, unsigned int ep)
+static int ep_wait_for_inactive(struct tegra_xudc *xudc, unsigned int ep)
{
- xudc_readl_poll(xudc, EP_THREAD_ACTIVE, BIT(ep), 0);
+ return xudc_readl_poll(xudc, EP_THREAD_ACTIVE, BIT(ep), 0);
}
static void tegra_xudc_req_done(struct tegra_xudc_ep *ep,
@@ -1046,8 +1046,39 @@ static void tegra_xudc_req_done(struct tegra_xudc_ep *ep,
(xudc->setup_state ==
DATA_STAGE_XFER));
} else {
- usb_gadget_unmap_request(&xudc->gadget, &req->usb_req,
- usb_endpoint_dir_in(ep->desc));
+ /*
+ * Drain the endpoint DMA pipeline before unmapping.
+ *
+ * Under SMMU strict mode dma_unmap() synchronously
+ * invalidates the IOVA TLB entry. On Tegra186/194/234 the
+ * XUDC appears to post the completion event when the DMA
+ * write is dispatched to the AXI interconnect, before the
+ * store is committed to memory. A subsequent dma_unmap()
+ * can remove the IOVA translation while the write is still
+ * in-flight, triggering a translation fault (fsr=0x402) that
+ * permanently wedges the bulk endpoint.
+ *
+ * Wait for EP_THREAD_ACTIVE to clear (endpoint sequencer
+ * idle). On timeout skip the unmap to avoid the SMMU fault;
+ * the DMA mapping leaks but the hardware is already in an
+ * unrecoverable state.
+ */
+ if (!WARN_ONCE(ep_wait_for_inactive(xudc, ep->index),
+ "ep%u: DMA drain timed out; skipping dma_unmap\n",
+ ep->index)) {
+ /* Read-back completes the poll barrier; EP_THREAD_ACTIVE=0 guarantees DMA is idle. */
+ xudc_readl(xudc, EP_THREAD_ACTIVE);
+ usb_gadget_unmap_request(&xudc->gadget, &req->usb_req,
+ usb_endpoint_dir_in(ep->desc));
+ } else {
+ /*
+ * Timeout: mapping is intentionally leaked to avoid the
+ * SMMU fault. Force -EIO so the gadget driver does not
+ * treat this as a successful transfer and reuse the
+ * still-mapped buffer.
+ */
+ req->usb_req.status = -EIO;
+ }
}
spin_unlock(&xudc->lock);
@@ -1443,10 +1474,12 @@ __tegra_xudc_ep_dequeue(struct tegra_xudc_ep *ep,
return 0;
}
- /* Halt DMA for this endpiont. */
+ /* Halt DMA for this endpoint. */
if (ep_ctx_read_state(ep->context) == EP_STATE_RUNNING) {
ep_pause(xudc, ep->index);
- ep_wait_for_inactive(xudc, ep->index);
+ if (ep_wait_for_inactive(xudc, ep->index))
+ dev_warn(xudc->dev, "ep%u: DMA drain timed out during dequeue\n",
+ ep->index);
}
deq_trb = trb_phys_to_virt(ep, ep_ctx_read_deq_ptr(ep->context));
2.39.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-06-06 2:40 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-05 7:27 [PATCH] usb: gadget: tegra-xudc: drain EP pipeline before DMA unmap Vishal Kumar
2026-06-05 14:59 ` Greg KH
-- strict thread matches above, loose matches on Subject: below --
2026-06-06 2:40 [PATCH] usb: gadget: tegra-xudc: drain EP pipeline before dma_unmap Vishal Kumar
[not found] <6a226993.a5745248.34e478.a9cd@mx.google.com>
2026-06-05 6:35 ` [PATCH] usb: gadget: tegra-xudc: drain EP pipeline before DMA unmap Greg KH
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.