diff options
author | Anton Vorontsov <avorontsov@ru.mvista.com> | 2008-12-25 09:15:05 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-02-09 14:19:46 -0500 |
commit | a30551db66afa1b53a4fa7ceadddb7122bdcf491 (patch) | |
tree | bbd4f0468a7417067bf7ebb5001368b01f8c7e90 | |
parent | 94f341db3dd080851f918da37e84659ef760da26 (diff) |
USB: fsl_qe_udc: Fix recursive locking bug in ch9getstatus()
The call chain is this:
qe_udc_irq() <- grabs the udc->lock spinlock
rx_irq()
qe_ep0_rx()
ep0_setup_handle()
setup_received_handle()
ch9getstatus()
qe_ep_queue() <- tries to grab the udc->lock again
It seems unsafe to temporarily drop the lock in the ch9getstatus(),
so to fix that bug the lock-less __qe_ep_queue() function
implemented and used by the ch9getstatus().
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/gadget/fsl_qe_udc.c | 26 |
1 files changed, 18 insertions, 8 deletions
diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index e8f7862acb3d..064582fb6a87 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c | |||
@@ -1681,14 +1681,11 @@ static void qe_free_request(struct usb_ep *_ep, struct usb_request *_req) | |||
1681 | kfree(req); | 1681 | kfree(req); |
1682 | } | 1682 | } |
1683 | 1683 | ||
1684 | /* queues (submits) an I/O request to an endpoint */ | 1684 | static int __qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req) |
1685 | static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req, | ||
1686 | gfp_t gfp_flags) | ||
1687 | { | 1685 | { |
1688 | struct qe_ep *ep = container_of(_ep, struct qe_ep, ep); | 1686 | struct qe_ep *ep = container_of(_ep, struct qe_ep, ep); |
1689 | struct qe_req *req = container_of(_req, struct qe_req, req); | 1687 | struct qe_req *req = container_of(_req, struct qe_req, req); |
1690 | struct qe_udc *udc; | 1688 | struct qe_udc *udc; |
1691 | unsigned long flags; | ||
1692 | int reval; | 1689 | int reval; |
1693 | 1690 | ||
1694 | udc = ep->udc; | 1691 | udc = ep->udc; |
@@ -1732,7 +1729,7 @@ static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req, | |||
1732 | list_add_tail(&req->queue, &ep->queue); | 1729 | list_add_tail(&req->queue, &ep->queue); |
1733 | dev_vdbg(udc->dev, "gadget have request in %s! %d\n", | 1730 | dev_vdbg(udc->dev, "gadget have request in %s! %d\n", |
1734 | ep->name, req->req.length); | 1731 | ep->name, req->req.length); |
1735 | spin_lock_irqsave(&udc->lock, flags); | 1732 | |
1736 | /* push the request to device */ | 1733 | /* push the request to device */ |
1737 | if (ep_is_in(ep)) | 1734 | if (ep_is_in(ep)) |
1738 | reval = ep_req_send(ep, req); | 1735 | reval = ep_req_send(ep, req); |
@@ -1748,11 +1745,24 @@ static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req, | |||
1748 | if (ep->dir == USB_DIR_OUT) | 1745 | if (ep->dir == USB_DIR_OUT) |
1749 | reval = ep_req_receive(ep, req); | 1746 | reval = ep_req_receive(ep, req); |
1750 | 1747 | ||
1751 | spin_unlock_irqrestore(&udc->lock, flags); | ||
1752 | |||
1753 | return 0; | 1748 | return 0; |
1754 | } | 1749 | } |
1755 | 1750 | ||
1751 | /* queues (submits) an I/O request to an endpoint */ | ||
1752 | static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req, | ||
1753 | gfp_t gfp_flags) | ||
1754 | { | ||
1755 | struct qe_ep *ep = container_of(_ep, struct qe_ep, ep); | ||
1756 | struct qe_udc *udc = ep->udc; | ||
1757 | unsigned long flags; | ||
1758 | int ret; | ||
1759 | |||
1760 | spin_lock_irqsave(&udc->lock, flags); | ||
1761 | ret = __qe_ep_queue(_ep, _req); | ||
1762 | spin_unlock_irqrestore(&udc->lock, flags); | ||
1763 | return ret; | ||
1764 | } | ||
1765 | |||
1756 | /* dequeues (cancels, unlinks) an I/O request from an endpoint */ | 1766 | /* dequeues (cancels, unlinks) an I/O request from an endpoint */ |
1757 | static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) | 1767 | static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) |
1758 | { | 1768 | { |
@@ -2008,7 +2018,7 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value, | |||
2008 | udc->ep0_dir = USB_DIR_IN; | 2018 | udc->ep0_dir = USB_DIR_IN; |
2009 | 2019 | ||
2010 | /* data phase */ | 2020 | /* data phase */ |
2011 | status = qe_ep_queue(&ep->ep, &req->req, GFP_ATOMIC); | 2021 | status = __qe_ep_queue(&ep->ep, &req->req); |
2012 | 2022 | ||
2013 | if (status == 0) | 2023 | if (status == 0) |
2014 | return; | 2024 | return; |