[PATCH 3/7 v2] usb: gadget: mv_udc: avoid sleeping on spinlock

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



build_dtd() can be called when hold a spinlock, but GFP_KERNEL may cause
dma_pool_alloc() sleep, So we need use GFP_ATOMIC instead of GFP_KERNEL.
But using GFP_ATOMIC may cause failure when allocating memory, add error
handler to handle it.

Signed-off-by: Alexey Khoroshilov <khoroshilov@xxxxxxxxx>
Signed-off-by: Neil Zhang <zhangwm@xxxxxxxxxxx>
---
 drivers/usb/gadget/mv_udc_core.c |   52 ++++++++++++++++++++++++++++++--------
 1 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c
index c05d340..425a204 100644
--- a/drivers/usb/gadget/mv_udc_core.c
+++ b/drivers/usb/gadget/mv_udc_core.c
@@ -373,7 +373,7 @@ static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
 	 * Be careful that no _GFP_HIGHMEM is set,
 	 * or we can not use dma_to_virt
 	 */
-	dtd = dma_pool_alloc(udc->dtd_pool, GFP_KERNEL, dma);
+	dtd = dma_pool_alloc(udc->dtd_pool, GFP_ATOMIC, dma);
 	if (dtd == NULL)
 		return dtd;
 
@@ -706,6 +706,7 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
 	struct mv_req *req = container_of(_req, struct mv_req, req);
 	struct mv_udc *udc = ep->udc;
 	unsigned long flags;
+	int retval;
 
 	/* catch various bogus parameters */
 	if (!_req || !req->req.complete || !req->req.buf
@@ -753,15 +754,17 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
 
 	/* build dtds and push them to device queue */
 	if (!req_to_dtd(req)) {
-		int retval;
 		retval = queue_dtd(ep, req);
 		if (retval) {
 			spin_unlock_irqrestore(&udc->lock, flags);
-			return retval;
+			dev_err(&udc->dev->dev, "Failed to queue dtd\n");
+			goto err_unmap_dma;
 		}
 	} else {
 		spin_unlock_irqrestore(&udc->lock, flags);
-		return -ENOMEM;
+		dev_err(&udc->dev->dev, "Failed to dma_pool_alloc\n");
+		retval = -ENOMEM;
+		goto err_unmap_dma;
 	}
 
 	/* Update ep0 state */
@@ -773,6 +776,22 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
 	spin_unlock_irqrestore(&udc->lock, flags);
 
 	return 0;
+
+err_unmap_dma:
+	if (req->mapped) {
+		dma_unmap_single(ep->udc->gadget.dev.parent,
+				req->req.dma, req->req.length,
+				((ep_dir(ep) == EP_DIR_IN) ?
+				DMA_TO_DEVICE : DMA_FROM_DEVICE));
+		req->req.dma = DMA_ADDR_INVALID;
+		req->mapped = 0;
+	} else
+		dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
+				req->req.dma, req->req.length,
+				((ep_dir(ep) == EP_DIR_IN) ?
+				DMA_TO_DEVICE : DMA_FROM_DEVICE));
+
+	return retval;
 }
 
 static void mv_prime_ep(struct mv_ep *ep, struct mv_req *req)
@@ -1497,15 +1516,17 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
 	}
 
 	/* prime the data phase */
-	if (!req_to_dtd(req))
+	if (!req_to_dtd(req)) {
 		retval = queue_dtd(ep, req);
-	else{	/* no mem */
+		if (retval) {
+			dev_err(&udc->dev->dev,
+				"Failed to queue dtd when prime status\n");
+			goto out;
+		}
+	} else{	/* no mem */
 		retval = -ENOMEM;
-		goto out;
-	}
-
-	if (retval) {
-		dev_err(&udc->dev->dev, "response error on GET_STATUS request\n");
+		dev_err(&udc->dev->dev,
+			"Failed to dma_pool_alloc when prime status\n");
 		goto out;
 	}
 
@@ -1513,6 +1534,15 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
 
 	return 0;
 out:
+	if (req->mapped) {
+		dma_unmap_single(ep->udc->gadget.dev.parent,
+				req->req.dma, req->req.length,
+				((ep_dir(ep) == EP_DIR_IN) ?
+				DMA_TO_DEVICE : DMA_FROM_DEVICE));
+		req->req.dma = DMA_ADDR_INVALID;
+		req->mapped = 0;
+	}
+
 	return retval;
 }
 
-- 
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux