From a5e69085100dca8095a8f2e84a41759ce3965b6f Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Tue, 21 Sep 2021 19:12:18 -0700 Subject: [PATCH] BACKPORT: FROMGIT: usb: dwc3: gadget: Avoid starting DWC3 gadget during UDC unbind There is a race present where the DWC3 runtime resume runs in parallel to the UDC unbind sequence. This will eventually lead to a possible scenario where we are enabling the run/stop bit, without a valid composition defined. Thread#1 (handling UDC unbind): usb_gadget_remove_driver() -->usb_gadget_disconnect() -->dwc3_gadget_pullup(0) --> continue UDC unbind sequence -->Thread#2 is running in parallel here Thread#2 (handing next cable connect) __dwc3_set_mode() -->pm_runtime_get_sync() -->dwc3_gadget_resume() -->dwc->gadget_driver is NOT NULL yet -->dwc3_gadget_run_stop(1) --> _dwc3gadget_start() ... Fix this by tracking the pullup disable routine, and avoiding resuming of the DWC3 gadget. Once the UDC is re-binded, that will trigger the pullup enable routine, which would handle enabling the DWC3 gadget. Acked-by: Felipe Balbi Signed-off-by: Wesley Cheng Link: https://lore.kernel.org/r/20210917021852.2037-1-wcheng@codeaurora.org Signed-off-by: Greg Kroah-Hartman Bug: 200287549 (cherry picked from commit 8217f07a50236779880f13e87f99224cd9117f83 https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing) [wcheng: Fixed KMI breakage by moving softconnect to a new parent structure] Change-Id: I9554933826710cc68136b08176290584f9ab74df Signed-off-by: Wesley Cheng Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 6 ++++-- drivers/usb/dwc3/core.h | 10 ++++++++++ drivers/usb/dwc3/gadget.c | 5 ++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index e4bc26e9b4b8..487a98d76d97 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1531,15 +1531,17 @@ static int dwc3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res, dwc_res; + struct dwc3_vendor *vdwc; struct dwc3 *dwc; int ret; void __iomem *regs; - dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL); - if (!dwc) + vdwc = devm_kzalloc(dev, sizeof(*vdwc), GFP_KERNEL); + if (!vdwc) return -ENOMEM; + dwc = &vdwc->dwc; dwc->dev = dev; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 591c7927797e..d52d4ecf7597 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1320,6 +1320,16 @@ struct dwc3 { ANDROID_KABI_RESERVE(4); }; +/** + * struct dwc3_vendor - contains parameters without modifying the format of DWC3 core + * @dwc: contains dwc3 core reference + * @softconnect: true when gadget connect is called, false when disconnect runs + */ +struct dwc3_vendor { + struct dwc3 dwc; + unsigned softconnect:1; +}; + #define INCRX_BURST_MODE 0 #define INCRX_UNDEF_LENGTH_BURST_MODE 1 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index eecd51757707..77a5a0a3760d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2457,9 +2457,11 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) { struct dwc3 *dwc = gadget_to_dwc(g); + struct dwc3_vendor *vdwc = container_of(dwc, struct dwc3_vendor, dwc); int ret; is_on = !!is_on; + vdwc->softconnect = is_on; /* * Per databook, when we want to stop the gadget, if a control transfer @@ -4427,9 +4429,10 @@ int dwc3_gadget_suspend(struct dwc3 *dwc) int dwc3_gadget_resume(struct dwc3 *dwc) { + struct dwc3_vendor *vdwc = container_of(dwc, struct dwc3_vendor, dwc); int ret; - if (!dwc->gadget_driver) + if (!dwc->gadget_driver || !vdwc->softconnect) return 0; ret = __dwc3_gadget_start(dwc);