summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorBenoit Goby <benoit@android.com>2010-12-02 16:11:15 -0800
committerBenoit Goby <benoit@android.com>2010-12-03 14:02:44 -0800
commit3cf8cfddc7d1f4a2893f9a5d892b1214140c7c00 (patch)
tree55c978c467ced15cb482b4167a6c487055d02144 /drivers
parent1538e53fbca5ce8b466ab067ab9c46151f0e51bd (diff)
usb: gadget: fsl_udc: Fix a race between ep_disable and ep_queue
Fixed a possible null pointer exception when an endpoint gets disabled while a request is being enqueued in parallel. Unmap the request buffer if we fail to enqueue the request. Change-Id: If94cc278c2e6ab58adcf170511e676348365f3f9 Signed-off-by: Benoit Goby <benoit@android.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c56
1 files changed, 40 insertions, 16 deletions
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index 20fab22cc13b..2fab37a2a094 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -846,9 +846,11 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
{
struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
struct fsl_req *req = container_of(_req, struct fsl_req, req);
- struct fsl_udc *udc;
+ struct fsl_udc *udc = ep->udc;
unsigned long flags;
+ enum dma_data_direction dir;
int is_iso = 0;
+ int status;
/* catch various bogus parameters */
if (!_req || !req->req.complete || !req->req.buf
@@ -856,17 +858,27 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
VDBG("%s, bad params", __func__);
return -EINVAL;
}
- if (unlikely(!_ep || !ep->desc)) {
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ if (unlikely(!ep->desc)) {
VDBG("%s, bad ep", __func__);
+ spin_unlock_irqrestore(&udc->lock, flags);
return -EINVAL;
}
+
if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
- if (req->req.length > ep->ep.maxpacket)
+ if (req->req.length > ep->ep.maxpacket) {
+ spin_unlock_irqrestore(&udc->lock, flags);
return -EMSGSIZE;
+ }
is_iso = 1;
}
- udc = ep->udc;
+ dir = ep_is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
@@ -874,18 +886,12 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
/* map virtual address to hardware */
if (req->req.dma == DMA_ADDR_INVALID) {
- req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
- req->req.buf,
- req->req.length, ep_is_in(ep)
- ? DMA_TO_DEVICE
- : DMA_FROM_DEVICE);
+ req->req.dma = dma_map_single(udc->gadget.dev.parent,
+ req->req.buf, req->req.length, dir);
req->mapped = 1;
} else {
- dma_sync_single_for_device(ep->udc->gadget.dev.parent,
- req->req.dma, req->req.length,
- ep_is_in(ep)
- ? DMA_TO_DEVICE
- : DMA_FROM_DEVICE);
+ dma_sync_single_for_device(udc->gadget.dev.parent,
+ req->req.dma, req->req.length, dir);
req->mapped = 0;
}
@@ -895,10 +901,19 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
/* build dtds and push them to device queue */
- if (fsl_req_to_dtd(req, gfp_flags))
- return -ENOMEM;
+ status = fsl_req_to_dtd(req, gfp_flags);
+ if (status)
+ goto err_unmap;
spin_lock_irqsave(&udc->lock, flags);
+
+ /* re-check if the ep has not been disabled */
+ if (unlikely(!ep->desc)) {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ status = -EINVAL;
+ goto err_unmap;
+ }
+
fsl_queue_td(ep, req);
/* Update ep0 state */
@@ -911,6 +926,15 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
+
+err_unmap:
+ if (req->mapped) {
+ dma_unmap_single(udc->gadget.dev.parent,
+ req->req.dma, req->req.length, dir);
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ }
+ return status;
}
/* dequeues (cancels, unlinks) an I/O request from an endpoint */