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 <balbi@kernel.org>
Signed-off-by: Wesley Cheng <wcheng@codeaurora.org>
Link: https://lore.kernel.org/r/20210917021852.2037-1-wcheng@codeaurora.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

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 <wcheng@codeaurora.org>
This commit is contained in:
Wesley Cheng 2021-09-21 19:12:18 -07:00 committed by Alistair Delva
parent 0671bafa24
commit bb13ff0598
3 changed files with 18 additions and 3 deletions

View File

@ -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;

View File

@ -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

View File

@ -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);