usb: renesas: fix scheduling in atomic context bug
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Tue, 14 Feb 2012 10:37:21 +0000 (11:37 +0100)
committerFelipe Balbi <balbi@ti.com>
Tue, 14 Feb 2012 11:44:53 +0000 (13:44 +0200)
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>
drivers/usb/renesas_usbhs/fifo.c
drivers/usb/renesas_usbhs/fifo.h

index fb2a88c5c210a2be5e239340cda32ac6bfdff666..3648c82a17fe3e20a5682f8fb1d42bcd06898f00 100644 (file)
@@ -765,9 +765,9 @@ static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
 }
 
 static void usbhsf_dma_complete(void *arg);
-static void usbhsf_dma_prepare_tasklet(unsigned long data)
+static void xfer_work(struct work_struct *work)
 {
-       struct usbhs_pkt *pkt = (struct usbhs_pkt *)data;
+       struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work);
        struct usbhs_pipe *pipe = pkt->pipe;
        struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
        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)
 
        pkt->trans = len;
 
-       tasklet_init(&fifo->tasklet,
-                    usbhsf_dma_prepare_tasklet,
-                    (unsigned long)pkt);
-
-       tasklet_schedule(&fifo->tasklet);
+       INIT_WORK(&pkt->work, xfer_work);
+       schedule_work(&pkt->work);
 
        return 0;
 
@@ -941,11 +938,8 @@ static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
 
        pkt->trans = len;
 
-       tasklet_init(&fifo->tasklet,
-                    usbhsf_dma_prepare_tasklet,
-                    (unsigned long)pkt);
-
-       tasklet_schedule(&fifo->tasklet);
+       INIT_WORK(&pkt->work, xfer_work);
+       schedule_work(&pkt->work);
 
        return 0;
 
index f68609c0f489b6df2c662cbc86e2adf85c193c98..c31731a843d12cefa1214fe17dad4a31b97dee94 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <linux/interrupt.h>
 #include <linux/sh_dma.h>
+#include <linux/workqueue.h>
 #include <asm/dma.h>
 #include "pipe.h"
 
@@ -31,7 +32,6 @@ struct usbhs_fifo {
        u32 ctr;        /* xFIFOCTR */
 
        struct usbhs_pipe       *pipe;
-       struct tasklet_struct   tasklet;
 
        struct dma_chan         *tx_chan;
        struct dma_chan         *rx_chan;
@@ -53,6 +53,7 @@ struct usbhs_pkt {
        struct usbhs_pkt_handle *handler;
        void (*done)(struct usbhs_priv *priv,
                     struct usbhs_pkt *pkt);
+       struct work_struct work;
        dma_addr_t dma;
        void *buf;
        int length;