diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2012-02-14 05:37:21 -0500 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2012-02-14 06:44:53 -0500 |
commit | 6e4b74e4690dd03b5664fa4895c3db0607d64742 (patch) | |
tree | 607c5bd1c23be029b589971e512458fde91ed0fa /drivers/usb/renesas_usbhs | |
parent | d5261286949fc9ada701c7e30bf89e08a6dbf4de (diff) |
usb: renesas: fix scheduling in atomic context bug
The current renesas_usbhs driver triggers
BUG: scheduling while atomic: ksoftirqd/0/3/0x00000102
with enabled CONFIG_DEBUG_ATOMIC_SLEEP, by submitting DMA transfers from
an atomic (tasklet) context, which is not supported by the shdma dmaengine
driver. Fix it by switching to a work.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/renesas_usbhs')
-rw-r--r-- | drivers/usb/renesas_usbhs/fifo.c | 18 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/fifo.h | 3 |
2 files changed, 8 insertions, 13 deletions
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index fb2a88c5c210..3648c82a17fe 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c | |||
@@ -765,9 +765,9 @@ static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map) | |||
765 | } | 765 | } |
766 | 766 | ||
767 | static void usbhsf_dma_complete(void *arg); | 767 | static void usbhsf_dma_complete(void *arg); |
768 | static void usbhsf_dma_prepare_tasklet(unsigned long data) | 768 | static void xfer_work(struct work_struct *work) |
769 | { | 769 | { |
770 | struct usbhs_pkt *pkt = (struct usbhs_pkt *)data; | 770 | struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work); |
771 | struct usbhs_pipe *pipe = pkt->pipe; | 771 | struct usbhs_pipe *pipe = pkt->pipe; |
772 | struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe); | 772 | struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe); |
773 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); | 773 | struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); |
@@ -847,11 +847,8 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) | |||
847 | 847 | ||
848 | pkt->trans = len; | 848 | pkt->trans = len; |
849 | 849 | ||
850 | tasklet_init(&fifo->tasklet, | 850 | INIT_WORK(&pkt->work, xfer_work); |
851 | usbhsf_dma_prepare_tasklet, | 851 | schedule_work(&pkt->work); |
852 | (unsigned long)pkt); | ||
853 | |||
854 | tasklet_schedule(&fifo->tasklet); | ||
855 | 852 | ||
856 | return 0; | 853 | return 0; |
857 | 854 | ||
@@ -941,11 +938,8 @@ static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done) | |||
941 | 938 | ||
942 | pkt->trans = len; | 939 | pkt->trans = len; |
943 | 940 | ||
944 | tasklet_init(&fifo->tasklet, | 941 | INIT_WORK(&pkt->work, xfer_work); |
945 | usbhsf_dma_prepare_tasklet, | 942 | schedule_work(&pkt->work); |
946 | (unsigned long)pkt); | ||
947 | |||
948 | tasklet_schedule(&fifo->tasklet); | ||
949 | 943 | ||
950 | return 0; | 944 | return 0; |
951 | 945 | ||
diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index f68609c0f489..c31731a843d1 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h | |||
@@ -19,6 +19,7 @@ | |||
19 | 19 | ||
20 | #include <linux/interrupt.h> | 20 | #include <linux/interrupt.h> |
21 | #include <linux/sh_dma.h> | 21 | #include <linux/sh_dma.h> |
22 | #include <linux/workqueue.h> | ||
22 | #include <asm/dma.h> | 23 | #include <asm/dma.h> |
23 | #include "pipe.h" | 24 | #include "pipe.h" |
24 | 25 | ||
@@ -31,7 +32,6 @@ struct usbhs_fifo { | |||
31 | u32 ctr; /* xFIFOCTR */ | 32 | u32 ctr; /* xFIFOCTR */ |
32 | 33 | ||
33 | struct usbhs_pipe *pipe; | 34 | struct usbhs_pipe *pipe; |
34 | struct tasklet_struct tasklet; | ||
35 | 35 | ||
36 | struct dma_chan *tx_chan; | 36 | struct dma_chan *tx_chan; |
37 | struct dma_chan *rx_chan; | 37 | struct dma_chan *rx_chan; |
@@ -53,6 +53,7 @@ struct usbhs_pkt { | |||
53 | struct usbhs_pkt_handle *handler; | 53 | struct usbhs_pkt_handle *handler; |
54 | void (*done)(struct usbhs_priv *priv, | 54 | void (*done)(struct usbhs_priv *priv, |
55 | struct usbhs_pkt *pkt); | 55 | struct usbhs_pkt *pkt); |
56 | struct work_struct work; | ||
56 | dma_addr_t dma; | 57 | dma_addr_t dma; |
57 | void *buf; | 58 | void *buf; |
58 | int length; | 59 | int length; |