From a9a4f474d752ddcd66ca404649512bd48828945f Mon Sep 17 00:00:00 2001 From: William Wu Date: Wed, 16 Nov 2022 09:44:58 +0800 Subject: [PATCH] usb: dwc3: gadget: fix deadlock in kick transfer If kick transfer command fail and the return value isn't EAGAIN, the current code will issue end transfer command to stop the active transfer, and only set dep->flags with DWC3_EP_END_TRANSFER_PENDING if the end transfer command is done successfully. If the DWC3_EP_END_TRANSFER_PENDING isn't set, it cleanup the cancelled requests and give the request back to the gadget layer immediately. For uvc gadget, the uvc gadget driver hold spinlock and then call usb_ep_queue() to submit uvc usb request to dwc3 controller. The dwc3 controller may kick transfer for the request sequentially, if the kick transfer command and the end transfer command all failed, the dwc3 will give the request back to the uvc gadget driver via the request complete function uvc_video_complete(), in this function, it try to get the spinlock again that lead to deadlock. This case always happens with the following warning log: WARNING: CPU: 0 PID: 14450 at drivers/usb/dwc3/gadget.c:1839 __dwc3_gadget_kick_transfer+0x3a0/0x3b0 ... Workqueue: events uvcg_video_pump ... Call trace: __dwc3_gadget_kick_transfer+0x3a0/0x3b0 __dwc3_gadget_ep_queue+0x128/0x1f0 dwc3_gadget_ep_queue+0x40/0x6c usb_ep_queue+0x44/0x100 uvcg_video_pump+0xd0/0x1d4 process_one_work+0x1f4/0x490 worker_thread+0x278/0x4dc kthread+0x13c/0x344 ret_from_fork+0x10/0x30 Fixes: 0ec00e864a87 ("UPSTREAM: usb: dwc3: gadget: move cmd_endtransfer to extra function") Change-Id: I43d455a45d542efcaa9234de60e37277611b3c47 Signed-off-by: William Wu --- drivers/usb/dwc3/gadget.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2b7900241ae8..637bb2ea78f7 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1851,7 +1851,7 @@ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int if (!interrupt) dep->flags &= ~DWC3_EP_TRANSFER_STARTED; - else if (!ret) + else dep->flags |= DWC3_EP_END_TRANSFER_PENDING; return ret; @@ -2032,8 +2032,11 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep) * status, issue END_TRANSFER command and retry on the next XferNotReady * event. */ - if (ret == -EAGAIN) + if (ret == -EAGAIN) { ret = __dwc3_stop_active_transfer(dep, false, true); + if (ret) + dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; + } return ret; }