Per discussion[1], to avoid UDC driver possible freeing gadget device memory before device core finishes using it, we add wait-complete mechanism at usb_del_gadget_udc and gadget device .release callback. After that, usb_del_gadget_udc will not return back until device core finishes using gadget device. For UDC drivers who have own .release callback, it needs to call complete(&gadget->done) by themselves, if not, the UDC core will handle it by default .release callback usb_gadget_release. [1] https://www.spinics.net/lists/linux-usb/msg198790.html Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> Cc: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> Suggested-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> Signed-off-by: Peter Chen <peter.chen@xxxxxxx> --- If this RFC patch is ok, I will create the formal patches which will change UDC drivers who have their own .release function. drivers/usb/gadget/udc/core.c | 14 +++++++++++--- include/linux/usb/gadget.h | 2 ++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index ee226ad802a4..ed141e1a0dcf 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1138,9 +1138,15 @@ static void usb_udc_release(struct device *dev) static const struct attribute_group *usb_udc_attr_groups[]; -static void usb_udc_nop_release(struct device *dev) +static void usb_gadget_release(struct device *dev) { + struct usb_gadget *gadget; + dev_vdbg(dev, "%s\n", __func__); + + gadget = container_of(dev, struct usb_gadget, dev); + complete(&gadget->done); + memset(dev, 0x0, sizeof(*dev)); } /* should be called with udc_lock held */ @@ -1184,7 +1190,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, if (release) gadget->dev.release = release; else - gadget->dev.release = usb_udc_nop_release; + gadget->dev.release = usb_gadget_release; device_initialize(&gadget->dev); @@ -1324,6 +1330,7 @@ void usb_del_gadget_udc(struct usb_gadget *gadget) dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); mutex_lock(&udc_lock); + init_completion(&gadget->done); list_del(&udc->list); if (udc->driver) { @@ -1338,7 +1345,8 @@ void usb_del_gadget_udc(struct usb_gadget *gadget) flush_work(&gadget->work); device_unregister(&udc->dev); device_unregister(&gadget->dev); - memset(&gadget->dev, 0x00, sizeof(gadget->dev)); + /* Wait gadget release() is done */ + wait_for_completion(&gadget->done); } EXPORT_SYMBOL_GPL(usb_del_gadget_udc); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 298b334e2951..ae346b524591 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -378,6 +378,7 @@ struct usb_gadget_ops { * @lpm_capable: If the gadget max_speed is FULL or HIGH, this flag * indicates that it supports LPM as per the LPM ECN & errata. * @irq: the interrupt number for device controller. + * @done: gadget device's release() is done * * Gadgets have a mostly-portable "gadget driver" implementing device * functions, handling all usb configurations and interfaces. Gadget @@ -433,6 +434,7 @@ struct usb_gadget { unsigned connected:1; unsigned lpm_capable:1; int irq; + struct completion done; }; #define work_to_gadget(w) (container_of((w), struct usb_gadget, work)) -- 2.17.1