From 6e65b88e7d27b64be382f4b626792ef9a0a7473f Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Fri, 29 Jun 2018 10:20:13 -0700 Subject: [PATCH] dwc3: gadget: Don't queue USB request if pull up is getting disabled There is possible race between pull up disable vs ep_queue() API. Pull up disable sends END transfer command to per endpoint, and give back started request and pending request back to function driver. dwc3_gadget_giveback() API does release spinlock which may allow ep_queue() or completion handling to queue next request. This results into TRB pending without invoking end transfer command, whereas request is being given back to function driver and being freed. Hence on setting next pull up enable, USB controller accesses previous stale TRB causing unmapped page fault. Fix this issue by checking pull up disable or not while handling ep_queue(). Change-Id: I391aed05cbfe183cd3e2fe2f1aa335e4fd9ec37c Signed-off-by: Mayank Rana Signed-off-by: Hemant Kumar Signed-off-by: Jack Pham --- drivers/usb/dwc3/ep0.c | 2 +- drivers/usb/dwc3/gadget.c | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index d9c5280f6548..83127b8a0e21 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -197,7 +197,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, int ret; spin_lock_irqsave(&dwc->lock, flags); - if (!dep->endpoint.desc) { + if (!dep->endpoint.desc || !dwc->pullups_connected) { dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", dep->name); ret = -ESHUTDOWN; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index efabf4226774..8e577fca954a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1541,7 +1541,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) { struct dwc3 *dwc = dep->dwc; - if (!dep->endpoint.desc) { + if (!dep->endpoint.desc || !dwc->pullups_connected) { dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", dep->name); return -ESHUTDOWN; @@ -1985,6 +1985,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) dwc->pullups_connected = true; } else { dwc3_gadget_disable_irq(dwc); + dwc->pullups_connected = false; __dwc3_gadget_ep_disable(dwc->eps[0]); __dwc3_gadget_ep_disable(dwc->eps[1]); @@ -2001,8 +2002,6 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) if (dwc->has_hibernation && !suspend) reg &= ~DWC3_DCTL_KEEP_CONNECT; - - dwc->pullups_connected = false; } dwc3_writel(dwc->regs, DWC3_DCTL, reg);