From bb13ff05981f0ab5fe3c579cdc5c15ef391c5d48 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 --- 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 1d421c6557d6..34a08815bf18 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1530,15 +1530,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 d938f85c5847..f66fefaf401a 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1319,6 +1319,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 fc6508cb0f41..47b00f938ecc 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2411,10 +2411,12 @@ static int __dwc3_gadget_start(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); unsigned long flags; int ret; is_on = !!is_on; + vdwc->softconnect = is_on; /* * Per databook, when we want to stop the gadget, if a control transfer @@ -4362,9 +4364,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);