[PATCH] usb: cdns2: allocate the trailing ZLP before queuing data

From: Slavin Liu

Date: Sun Sep 13 2026 - 08:56:22 EST


A trailing ZLP allocation currently happens after the data request
has been accepted. Allocate and check it first, before publishing the
data request. Free the unqueued ZLP if data mapping fails. The ZLP has
zero length, for which the mapping helper returns success without DMA
mapping; its enqueue has no fallible mapping step on this baseline.

Detected by static analysis and reviewed with AI-assisted source auditing.

Fixes: 3eb1f1efe204 ("usb: cdns2: Add main part of Cadence USBHS driver")
Assisted-by: LLM
Signed-off-by: Slavin Liu <bolin.liu@xxxxxxxxxx>
---
drivers/usb/gadget/udc/cdns2/cdns2-gadget.c | 26 ++++++++++++++-------
1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
index 308d3c468ab1..68fed31a85c5 100644
--- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
@@ -1696,7 +1696,7 @@ static int cdns2_ep_enqueue(struct cdns2_endpoint *pep,
static int cdns2_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags)
{
- struct usb_request *zlp_request;
+ struct usb_request *zlp_request = NULL;
struct cdns2_request *preq;
struct cdns2_endpoint *pep;
struct cdns2_device *pdev;
@@ -1717,21 +1717,31 @@ static int cdns2_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,

spin_lock_irqsave(&pdev->lock, flags);

- preq = to_cdns2_request(request);
- ret = cdns2_ep_enqueue(pep, preq, gfp_flags);
-
- if (ret == 0 && request->zero && request->length &&
- (request->length % ep->maxpacket == 0)) {
- struct cdns2_request *preq;
-
+ if (request->zero && request->length &&
+ request->length % ep->maxpacket == 0) {
zlp_request = cdns2_gadget_ep_alloc_request(ep, GFP_ATOMIC);
+ if (!zlp_request) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
zlp_request->buf = pdev->zlp_buf;
zlp_request->length = 0;
+ }
+
+ preq = to_cdns2_request(request);
+ ret = cdns2_ep_enqueue(pep, preq, gfp_flags);
+ if (ret) {
+ if (zlp_request)
+ cdns2_gadget_ep_free_request(ep, zlp_request);
+ goto out_unlock;
+ }

+ if (zlp_request) {
preq = to_cdns2_request(zlp_request);
ret = cdns2_ep_enqueue(pep, preq, gfp_flags);
}

+out_unlock:
spin_unlock_irqrestore(&pdev->lock, flags);
return ret;
}