usb: cdns3: gadget: fix possible memory leak
authorPeter Chen <peter.chen@nxp.com>
Fri, 21 Aug 2020 02:55:47 +0000 (10:55 +0800)
committerFelipe Balbi <balbi@kernel.org>
Fri, 2 Oct 2020 06:57:42 +0000 (09:57 +0300)
If cdns3_gadget_start is failed, it never frees cdns3_device structure.
Meanwhile, there is no release function for gadget device, it causes
there is no sync with driver core.

To fix this, we add release function for gadget device, and free
cdns3_device structure at there. Meanwhile, With the new UDC core
APIs, we could work with driver core better to handle memory leak
issue.

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
drivers/usb/cdns3/gadget.c

index d3f079af4a00858358624d0f1399c971c9634763..26ba4170067262742074f47c194bdb31b58c72ac 100644 (file)
@@ -2989,6 +2989,14 @@ err:
        return -ENOMEM;
 }
 
+static void cdns3_gadget_release(struct device *dev)
+{
+       struct cdns3_device *priv_dev = container_of(dev,
+                       struct cdns3_device, gadget.dev);
+
+       kfree(priv_dev);
+}
+
 void cdns3_gadget_exit(struct cdns3 *cdns)
 {
        struct cdns3_device *priv_dev;
@@ -2999,7 +3007,7 @@ void cdns3_gadget_exit(struct cdns3 *cdns)
        pm_runtime_mark_last_busy(cdns->dev);
        pm_runtime_put_autosuspend(cdns->dev);
 
-       usb_del_gadget_udc(&priv_dev->gadget);
+       usb_del_gadget(&priv_dev->gadget);
        devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev);
 
        cdns3_free_all_eps(priv_dev);
@@ -3020,7 +3028,7 @@ void cdns3_gadget_exit(struct cdns3 *cdns)
                          priv_dev->setup_dma);
 
        kfree(priv_dev->zlp_buf);
-       kfree(priv_dev);
+       usb_put_gadget(&priv_dev->gadget);
        cdns->gadget_dev = NULL;
        cdns3_drd_gadget_off(cdns);
 }
@@ -3035,6 +3043,8 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
        if (!priv_dev)
                return -ENOMEM;
 
+       usb_initialize_gadget(cdns->dev, &priv_dev->gadget,
+                       cdns3_gadget_release);
        cdns->gadget_dev = priv_dev;
        priv_dev->sysdev = cdns->dev;
        priv_dev->dev = cdns->dev;
@@ -3122,10 +3132,9 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
        }
 
        /* add USB gadget device */
-       ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget);
+       ret = usb_add_gadget(&priv_dev->gadget);
        if (ret < 0) {
-               dev_err(priv_dev->dev,
-                       "Failed to register USB device controller\n");
+               dev_err(priv_dev->dev, "Failed to add gadget\n");
                goto err4;
        }
 
@@ -3138,6 +3147,7 @@ err3:
 err2:
        cdns3_free_all_eps(priv_dev);
 err1:
+       usb_put_gadget(&priv_dev->gadget);
        cdns->gadget_dev = NULL;
        return ret;
 }