History

usb: renesas_usbhs: add a workaround for a race condition of workqueue

The old commit 6e4b74e4690d ("usb: renesas: fix scheduling in atomic context bug") fixed an atomic issue by using workqueue for the shdmac dmaengine driver. However, this has a potential race condition issue between the work pending and usbhsg_ep_free_request() in gadget mode. When usbhsg_ep_free_request() is called while pending the queue, since the work_struct will be freed and then the work handler is called, kernel panic happens on process_one_work(). To fix the issue, if we could call cancel_work_sync() at somewhere before the free request, it could be easy. However, the usbhsg_ep_free_request() is called on atomic (e.g. f_ncm driver calls free request via gether_disconnect()). For now, almost all users are having "USB-DMAC" and the DMAengine driver can be used on atomic. So, this patch adds a workaround for a race condition to call the DMAengine APIs without the workqueue. This means we still have TODO on shdmac environment (SH7724), but since it doesn't have SMP, the race condition might not happen.

Yoshihiro Shimoda committed 7 years ago

Commit b2357831 parent 4aef7961 file · +22 −12Browse files

Unified · Split

drivers/usb/renesas_usbhs/fifo.c+22−12
@@ -803,9 +803,8 @@ static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map) · 802 unmodified lines
803803 }
804804
805805 static void usbhsf_dma_complete(void *arg);
806−static void xfer_work(struct work_struct *work)
806+static void usbhsf_dma_xfer_preparing(struct usbhs_pkt *pkt)
807807 {
808− struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work);
809808 struct usbhs_pipe *pipe = pkt->pipe;
810809 struct usbhs_fifo *fifo;
811810 struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
@@ -813,12 +812,10 @@ static void xfer_work(struct work_struct *work) · 1 unmodified line
813812 struct dma_chan *chan;
814813 struct device *dev = usbhs_priv_to_dev(priv);
815814 enum dma_transfer_direction dir;
816− unsigned long flags;
817815
818− usbhs_lock(priv, flags);
819816 fifo = usbhs_pipe_to_fifo(pipe);
820817 if (!fifo)
821− goto xfer_work_end;
818+ return;
822819
823820 chan = usbhsf_dma_chan_get(fifo, pkt);
824821 dir = usbhs_pipe_is_dir_in(pipe) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
@@ -827,7 +824,7 @@ static void xfer_work(struct work_struct *work) · 2 unmodified lines
827824 pkt->trans, dir,
828825 DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
829826 if (!desc)
830− goto xfer_work_end;
827+ return;
831828
832829 desc->callback = usbhsf_dma_complete;
833830 desc->callback_param = pipe;
@@ -835,7 +832,7 @@ static void xfer_work(struct work_struct *work) · 1 unmodified line
835832 pkt->cookie = dmaengine_submit(desc);
836833 if (pkt->cookie < 0) {
837834 dev_err(dev, "Failed to submit dma descriptor\n");
838− goto xfer_work_end;
835+ return;
839836 }
840837
841838 dev_dbg(dev, " %s %d (%d/ %d)\n",
@@ -846,8 +843,17 @@ static void xfer_work(struct work_struct *work) · 4 unmodified lines
846843 dma_async_issue_pending(chan);
847844 usbhsf_dma_start(pipe, fifo);
848845 usbhs_pipe_enable(pipe);
846+}
847+
848+static void xfer_work(struct work_struct *work)
849+{
850+ struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work);
851+ struct usbhs_pipe *pipe = pkt->pipe;
852+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
853+ unsigned long flags;
849854
850−xfer_work_end:
855+ usbhs_lock(priv, flags);
856+ usbhsf_dma_xfer_preparing(pkt);
851857 usbhs_unlock(priv, flags);
852858 }
853859
@@ -900,8 +906,13 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) · 46 unmodified lines
900906 pkt->trans = len;
901907
902908 usbhsf_tx_irq_ctrl(pipe, 0);
903− INIT_WORK(&pkt->work, xfer_work);
904− schedule_work(&pkt->work);
909+ /* FIXME: Workaound for usb dmac that driver can be used in atomic */
910+ if (usbhs_get_dparam(priv, has_usb_dmac)) {
911+ usbhsf_dma_xfer_preparing(pkt);
912+ } else {
913+ INIT_WORK(&pkt->work, xfer_work);
914+ schedule_work(&pkt->work);
915+ }
905916
906917 return 0;
907918
@@ -1007,8 +1018,7 @@ static int usbhsf_dma_prepare_pop_with_usb_dmac(struct usbhs_pkt *pkt, · 99 unmodified lines
10071018
10081019 pkt->trans = pkt->length;
10091020
1010− INIT_WORK(&pkt->work, xfer_work);
1011− schedule_work(&pkt->work);
1021+ usbhsf_dma_xfer_preparing(pkt);
10121022
10131023 return 0;
10141024

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t