Cc Alan On Fri, Jan 31, 2025, Roy Luo wrote: > On Mon, Jan 27, 2025 at 5:44 PM Thinh Nguyen <Thinh.Nguyen@xxxxxxxxxxxx> wrote: > > > > On Wed, Jan 22, 2025, Roy Luo wrote: > > > `dwc3_gadget_soft_disconnect` function, called as part of > > > > The dwc3_gadget_soft_disconnect() isn't directly part of > > device_del(&gadget->dev). It should be part of disconnect. > > > > Can you provide the full sequence of events so I can have more context? > > The handling of the flushing of gadget->work should not be part of the > > dwc3. > > > Yes, it's a part of disconnect, and disconnect is a part of gadget unbind. > Let me try my best to explain. Here's the call stack for usb_del_gadget: > -> usb_del_gadget > -> flush_work(&gadget->work) > -> device_del > -> bus_remove_device > -> device_release_driver > -> gadget_unbind_driver > -> usb_gadget_disconnect_locked > -> dwc3_gadget_pullup > -> dwc3_gadget_soft_disconnect > -> usb_gadget_set_state > -> schedule_work(&gadget->work) > > Then when usb_put_gadget is called, gadget could be freed before > gadget->work is executed. > -> usb_put_gadget > -> put_device > -> kobject_put > -> device_release > -> dwc_gadget_release > -> kfree(gadget) > Thanks for the details! The UDC core is initiating and handling the gadget->work, so the flushing of the gadget->work should also be handled there. Since the usb_gadget_disconnect_locked() may trigger a state change work on unbind, the flushing of the gadget->work should to be moved to gadget_unbind_driver() instead: diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index f8c1ef465e45..9e4abd6e40f8 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1568,7 +1568,6 @@ void usb_del_gadget(struct usb_gadget *gadget) kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); sysfs_remove_link(&udc->dev.kobj, "gadget"); - flush_work(&gadget->work); device_del(&gadget->dev); ida_free(&gadget_id_numbers, gadget->id_number); cancel_work_sync(&udc->vbus_work); @@ -1694,6 +1693,8 @@ static void gadget_unbind_driver(struct device *dev) synchronize_irq(gadget->irq); mutex_unlock(&udc->connect_lock); + flush_work(&gadget->work); + udc->driver->unbind(gadget); mutex_lock(&udc->connect_lock); BR, Thinh