Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Guennadi Liakhovetski and committed by
Felipe Balbi
6e4b74e4 d5261286

+8 -13
+6 -12
drivers/usb/renesas_usbhs/fifo.c
··· 765 765 } 766 766 767 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 771 struct usbhs_pipe *pipe = pkt->pipe; 772 772 struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe); 773 773 struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); ··· 847 847 848 848 pkt->trans = len; 849 849 850 - tasklet_init(&fifo->tasklet, 851 - usbhsf_dma_prepare_tasklet, 852 - (unsigned long)pkt); 853 - 854 - tasklet_schedule(&fifo->tasklet); 850 + INIT_WORK(&pkt->work, xfer_work); 851 + schedule_work(&pkt->work); 855 852 856 853 return 0; 857 854 ··· 938 941 939 942 pkt->trans = len; 940 943 941 - tasklet_init(&fifo->tasklet, 942 - usbhsf_dma_prepare_tasklet, 943 - (unsigned long)pkt); 944 - 945 - tasklet_schedule(&fifo->tasklet); 944 + INIT_WORK(&pkt->work, xfer_work); 945 + schedule_work(&pkt->work); 946 946 947 947 return 0; 948 948
+2 -1
drivers/usb/renesas_usbhs/fifo.h
··· 19 19 20 20 #include <linux/interrupt.h> 21 21 #include <linux/sh_dma.h> 22 + #include <linux/workqueue.h> 22 23 #include <asm/dma.h> 23 24 #include "pipe.h" 24 25 ··· 32 31 u32 ctr; /* xFIFOCTR */ 33 32 34 33 struct usbhs_pipe *pipe; 35 - struct tasklet_struct tasklet; 36 34 37 35 struct dma_chan *tx_chan; 38 36 struct dma_chan *rx_chan; ··· 53 53 struct usbhs_pkt_handle *handler; 54 54 void (*done)(struct usbhs_priv *priv, 55 55 struct usbhs_pkt *pkt); 56 + struct work_struct work; 56 57 dma_addr_t dma; 57 58 void *buf; 58 59 int length;