[PATCH] usb: gadget: aspeed_udc: fix probe error cleanup

Jiawen Liu <[email protected]> Sun, 26 Jul 2026 09:52:15 +0400
Newsgroups org.ozlabs.lists.linux-aspeed,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-usb
Message-ID <[email protected]>
ast_udc_probe() jumps to one shared error label for failures after driver
data is installed.  That label calls ast_udc_remove(), but remove assumes
that the gadget has been registered successfully and that later probe
resources are initialized.

For example, failures before usb_add_gadget_udc() can call
usb_del_gadget_udc() through ast_udc_remove() even though gadget->udc has
not been set up by the UDC core.  Failures before or after DMA allocation
also take the same remove-style path.

Use staged probe error labels instead: free the endpoint DMA buffer only
after it has been allocated, and disable the clock only after it has been
enabled.  While doing this, also handle dma_alloc_coherent() failure
explicitly.

Signed-off-by: Jiawen Liu <[email protected]>
---
 drivers/usb/gadget/udc/aspeed_udc.c | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c
index 75f9c831b21a..877a9f7539f7 100644
--- a/drivers/usb/gadget/udc/aspeed_udc.c
+++ b/drivers/usb/gadget/udc/aspeed_udc.c
@@ -1474,6 +1474,7 @@ static int ast_udc_probe(struct platform_device *pdev)
 	enum usb_device_speed max_speed;
 	struct device *dev = &pdev->dev;
 	struct ast_udc_dev *udc;
+	size_t ep0_buf_size;
 	int rc;
 
 	udc = devm_kzalloc(&pdev->dev, sizeof(struct ast_udc_dev), GFP_KERNEL);
@@ -1516,10 +1517,14 @@ static int ast_udc_probe(struct platform_device *pdev)
 	/*
 	 * Allocate DMA buffers for all EPs in one chunk
 	 */
+	ep0_buf_size = AST_UDC_EP_DMA_SIZE * AST_UDC_NUM_ENDPOINTS;
 	udc->ep0_buf = dma_alloc_coherent(&pdev->dev,
-					  AST_UDC_EP_DMA_SIZE *
-					  AST_UDC_NUM_ENDPOINTS,
+					  ep0_buf_size,
 					  &udc->ep0_buf_dma, GFP_KERNEL);
+	if (!udc->ep0_buf) {
+		rc = -ENOMEM;
+		goto err_disable_clk;
+	}
 
 	udc->gadget.speed = USB_SPEED_UNKNOWN;
 	udc->gadget.max_speed = USB_SPEED_HIGH;
@@ -1550,20 +1555,20 @@ static int ast_udc_probe(struct platform_device *pdev)
 	udc->irq = platform_get_irq(pdev, 0);
 	if (udc->irq < 0) {
 		rc = udc->irq;
-		goto err;
+		goto err_free_ep0_buf;
 	}
 
 	rc = devm_request_irq(&pdev->dev, udc->irq, ast_udc_isr, 0,
 			      KBUILD_MODNAME, udc);
 	if (rc) {
 		dev_err(&pdev->dev, "Failed to request interrupt\n");
-		goto err;
+		goto err_free_ep0_buf;
 	}
 
 	rc = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
 	if (rc) {
 		dev_err(&pdev->dev, "Failed to add gadget udc\n");
-		goto err;
+		goto err_free_ep0_buf;
 	}
 
 	dev_info(&pdev->dev, "Initialized udc in USB%s mode\n",
@@ -1571,9 +1576,15 @@ static int ast_udc_probe(struct platform_device *pdev)
 
 	return 0;
 
+err_free_ep0_buf:
+	dma_free_coherent(&pdev->dev,
+			  ep0_buf_size, udc->ep0_buf,
+			  udc->ep0_buf_dma);
+	udc->ep0_buf = NULL;
+err_disable_clk:
+	clk_disable_unprepare(udc->clk);
 err:
 	dev_err(&pdev->dev, "Failed to udc probe, rc:0x%x\n", rc);
-	ast_udc_remove(pdev);
 
 	return rc;
 }
-- 
2.34.1