From b2695767985fd58ff6050ac35b19cee6c1de671f Mon Sep 17 00:00:00 2001 From: Rohith Kollalsi Date: Fri, 3 Sep 2021 16:08:36 +0530 Subject: [PATCH] usb: dwc3: Don't perform pm rt sync operations in atomic context For mass storage function driver ep_disable will be called in worker thread context. Making sure controller is active and then calling ep_disable and then putting controller back to lpm is very difficult to achieve in mass storage driver. The reason is that fsg_disable raises an exception which actaully waits for the completion callback to come from gadget driver and if for some reason (like command timeout error) if it doesn't, then do_set_iterface will not be called which will do put_sync. This can prevent controller from going to lpm. To solve above problem, commit ad8d260c33a2 ("usb: dwc3: gadget: Check controller status with endpoint enable/disable") moved pm_runtime_sync operations from mass storage driver to gadget driver. But there is a chance that pm_runtime get_sync and put sync operations in gadget driver can be called in atomic context (taking spin_lock in function driver and then calling) and lead to crash. Adding in_lpm check and performing these operations only if controller is in lpm will ensure that these operations will be done only for mass storage driver and not be done in atomic context. For other function drivers where ep_disable will be called in atomic context, there is no chance that ep_disable will be called when controller is in lpm as bh_work or resume work will be called and will ensure controller to be out of LPM before ep_disable is called. Change-Id: I37635fd75e44a67deadbd4796b757d85d604aed4 Signed-off-by: Rohith Kollalsi --- drivers/usb/dwc3/gadget.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c14bbcdf122c..eb63ed21b638 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -951,6 +951,7 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep) struct dwc3 *dwc; unsigned long flags; int ret; + bool call_rpm_put = false; if (!ep) { pr_debug("dwc3: invalid parameters\n"); @@ -965,13 +966,18 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep) dep->name)) return 0; - pm_runtime_get_sync(dwc->sysdev); + if (atomic_read(&dwc->in_lpm)) { + pm_runtime_get_sync(dwc->sysdev); + call_rpm_put = true; + } spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_disable(dep); dbg_event(dep->number, "DISABLE", ret); spin_unlock_irqrestore(&dwc->lock, flags); - pm_runtime_mark_last_busy(dwc->sysdev); - pm_runtime_put_autosuspend(dwc->sysdev); + if (call_rpm_put) { + pm_runtime_mark_last_busy(dwc->sysdev); + pm_runtime_put_autosuspend(dwc->sysdev); + } return ret; }