Message ID | 20210917021852.2037-1-wcheng@codeaurora.org (mailing list archive) |
---|---|
State | Accepted |
Commit | 8217f07a50236779880f13e87f99224cd9117f83 |
Headers | show |
Series | usb: dwc3: gadget: Avoid starting DWC3 gadget during UDC unbind | expand |
Hi, Wesley Cheng <wcheng@codeaurora.org> writes: > 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. > > Signed-off-by: Wesley Cheng <wcheng@codeaurora.org> This looks okay to me, but needs to be tested by a few folks ;-) Acked-by: Felipe Balbi <balbi@kernel.org>
Hi, On 9/16/2021 10:17 PM, Felipe Balbi wrote: > > Hi, > > Wesley Cheng <wcheng@codeaurora.org> writes: > >> 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. >> >> Signed-off-by: Wesley Cheng <wcheng@codeaurora.org> Thanks, Felipe! > > This looks okay to me, but needs to be tested by a few folks ;-) > > Acked-by: Felipe Balbi <balbi@kernel.org> > Yes, would be good to get some functions using usb_gadget_activate/deactivate(). It should be OK for those situations as well, but just to make sure :) Thanks Wesley Cheng
Wesley Cheng <wcheng@codeaurora.org> writes: > Hi, > > On 9/16/2021 10:17 PM, Felipe Balbi wrote: >> >> Hi, >> >> Wesley Cheng <wcheng@codeaurora.org> writes: >> >>> 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. >>> >>> Signed-off-by: Wesley Cheng <wcheng@codeaurora.org> > > Thanks, Felipe! > >> >> This looks okay to me, but needs to be tested by a few folks ;-) >> >> Acked-by: Felipe Balbi <balbi@kernel.org> >> > Yes, would be good to get some functions using > usb_gadget_activate/deactivate(). It should be OK for those situations > as well, but just to make sure :) IIRC, the UVC function relies on those. You could give it a shot ;-)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 324ca5f5e98d..ee854697c300 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1032,6 +1032,7 @@ struct dwc3_scratchpad_array { * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize * @hsphy_interface: "utmi" or "ulpi" * @connected: true when we're connected to a host, false otherwise + * @softconnect: true when gadget connect is called, false when disconnect runs * @delayed_status: true when gadget driver asks for delayed status * @ep0_bounced: true when we used bounce buffer * @ep0_expect_in: true when we expect a DATA IN transfer @@ -1252,6 +1253,7 @@ struct dwc3 { const char *hsphy_interface; unsigned connected:1; + unsigned softconnect:1; unsigned delayed_status:1; unsigned ep0_bounced:1; unsigned ep0_expect_in:1; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 804b50548163..32e01885b211 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2418,7 +2418,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) int ret; is_on = !!is_on; - + dwc->softconnect = is_on; /* * Per databook, when we want to stop the gadget, if a control transfer * is still in process, complete it and get the core into setup phase. @@ -4352,7 +4352,7 @@ int dwc3_gadget_resume(struct dwc3 *dwc) { int ret; - if (!dwc->gadget_driver) + if (!dwc->gadget_driver || !dwc->softconnect) return 0; ret = __dwc3_gadget_start(dwc);
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. Signed-off-by: Wesley Cheng <wcheng@codeaurora.org> --- drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/gadget.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-)