From: Jon Hunter <jonathanh@nvidia.com>
To: Vishal Kumar <vishalmimani008@gmail.com>, linux-usb@vger.kernel.org
Cc: gregkh@linuxfoundation.org, thierry.reding@gmail.com,
linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org
Subject: Re: [PATCH] usb: gadget: tegra-xudc: drain EP pipeline before dma_unmap
Date: Mon, 22 Jun 2026 14:24:40 +0100 [thread overview]
Message-ID: <169ef7cc-e1fe-46d6-95ca-0f3514e806c0@nvidia.com> (raw)
In-Reply-To: <20260606024011.1160110-1-vishalmimani008@gmail.com>
On 06/06/2026 03:40, Vishal Kumar wrote:
> 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);
The ep_wait_for_inactive() is reading the EP_THREAD_ACTIVE and so this
would appear to be redundant.
> + 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;
The above is confusing. Wouldn't it be simpler to have ...
if (WARN_ONCE(ep_wait_for_inactive())) {
req->usb_req.status = -EIO;
} else {
xudc_readl(xudc, EP_THREAD_ACTIVE);
usb_gadget_unmap_request(...);
}
Furthermore, it seems that if this now fails then we don't unmap the
buffer, but we still give back the request afterwards anyway.
> + }
> }
>
> 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);
Maybe it is better to put the warning in the ep_wait_for_inactive()
function.
> }
>
> deq_trb = trb_phys_to_virt(ep, ep_ctx_read_deq_ptr(ep->context));
>
> 2.39.0
--
nvpublic
next prev parent reply other threads:[~2026-06-22 13:24 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-06 2:40 [PATCH] usb: gadget: tegra-xudc: drain EP pipeline before dma_unmap Vishal Kumar
2026-06-22 13:24 ` Jon Hunter [this message]
[not found] <CAN+vipx-6gco_XMnV+JxbkRegJ=i8tSKFdBN4KcT16UceQduqQ@mail.gmail.com>
2026-06-25 14:53 ` [PATCH] usb: gadget: tegra-xudc: drain EP pipeline before DMA unmap Greg KH
2026-06-25 20:59 ` Jon Hunter
-- strict thread matches above, loose matches on Subject: below --
2026-06-05 7:27 Vishal Kumar
2026-06-05 14:59 ` Greg KH
[not found] <6a226993.a5745248.34e478.a9cd@mx.google.com>
2026-06-05 6:35 ` Greg KH
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=169ef7cc-e1fe-46d6-95ca-0f3514e806c0@nvidia.com \
--to=jonathanh@nvidia.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=stable@vger.kernel.org \
--cc=thierry.reding@gmail.com \
--cc=vishalmimani008@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.