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:

committed by
Alistair Delva

parent
0671bafa24
commit
bb13ff0598
@@ -1530,15 +1530,17 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct resource *res, dwc_res;
|
struct resource *res, dwc_res;
|
||||||
|
struct dwc3_vendor *vdwc;
|
||||||
struct dwc3 *dwc;
|
struct dwc3 *dwc;
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
|
|
||||||
dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
|
vdwc = devm_kzalloc(dev, sizeof(*vdwc), GFP_KERNEL);
|
||||||
if (!dwc)
|
if (!vdwc)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
dwc = &vdwc->dwc;
|
||||||
|
|
||||||
dwc->dev = dev;
|
dwc->dev = dev;
|
||||||
|
|
||||||
|
@@ -1319,6 +1319,16 @@ struct dwc3 {
|
|||||||
ANDROID_KABI_RESERVE(4);
|
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_BURST_MODE 0
|
||||||
#define INCRX_UNDEF_LENGTH_BURST_MODE 1
|
#define INCRX_UNDEF_LENGTH_BURST_MODE 1
|
||||||
|
|
||||||
|
@@ -2411,10 +2411,12 @@ static int __dwc3_gadget_start(struct dwc3 *dwc);
|
|||||||
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
||||||
{
|
{
|
||||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||||
|
struct dwc3_vendor *vdwc = container_of(dwc, struct dwc3_vendor, dwc);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
is_on = !!is_on;
|
is_on = !!is_on;
|
||||||
|
vdwc->softconnect = is_on;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Per databook, when we want to stop the gadget, if a control transfer
|
* 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)
|
int dwc3_gadget_resume(struct dwc3 *dwc)
|
||||||
{
|
{
|
||||||
|
struct dwc3_vendor *vdwc = container_of(dwc, struct dwc3_vendor, dwc);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!dwc->gadget_driver)
|
if (!dwc->gadget_driver || !vdwc->softconnect)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = __dwc3_gadget_start(dwc);
|
ret = __dwc3_gadget_start(dwc);
|
||||||
|
Reference in New Issue
Block a user