Patch "dmaengine: pl330: fix double lock" has been added to the 4.9-stable tree
Subsystems (2)
This is a note to let you know that I've just added the patch titled
dmaengine: pl330: fix double lock
to the 4.9-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
dmaengine-pl330-fix-double-lock.patch
and it can be found in the queue-4.9 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From foo@baz Wed Dec 6 17:39:55 CET 2017
From: Iago Abal <[email protected]>
Date: Wed, 11 Jan 2017 14:00:21 +0100
Subject: dmaengine: pl330: fix double lock
From: Iago Abal <[email protected]>
[ Upstream commit 91539eb1fda2d530d3b268eef542c5414e54bf1a ]
The static bug finder EBA (http://www.iagoabal.eu/eba/) reported the following double-lock bug:
Double lock:
1. spin_lock_irqsave(pch->lock, flags) at pl330_free_chan_resources:2236;
2. call to function `pl330_release_channel' immediately after;
3. call to function `dma_pl330_rqcb' in line 1753;
4. spin_lock_irqsave(pch->lock, flags) at dma_pl330_rqcb:1505.
I have fixed it as suggested by Marek Szyprowski.
First, I have replaced pch->lock' with pl330->lock' in functions pl330_alloc_chan_resources' and pl330_free_chan_resources'. This avoids the double-lock by acquiring a different lock than `dma_pl330_rqcb'.
NOTE that, as a result, pl330_free_chan_resources' executes list_splice_tail_init' on pch->work_list' under lock pl330->lock', whereas in the rest of the code pch->work_list' is protected by pch->lock'. I don't know if this may cause race conditions. Similarly pch->cyclic' is written by pl330_alloc_chan_resources' under pl330->lock' but read by pl330_tx_submit' under `pch->lock'.
Second, I have removed locking from pl330_request_channel' and pl330_release_channel' functions. Function pl330_request_channel' is only called from pl330_alloc_chan_resources', so the lock is already held. Function pl330_release_channel' is called from pl330_free_chan_resources', which already holds the lock, and from pl330_del'. Function pl330_del' is called in an error path of pl330_probe' and at the end of pl330_remove', but I assume that there cannot be concurrent accesses to the protected data at those points.
--- drivers/dma/pl330.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-)
drivers/dma/pl330.c +6 -13
@@ -1694,7 +1694,6 @@ static bool _chan_ns(const struct pl330_static struct pl330_thread *pl330_request_channel (struct pl330_dmac *pl330) {struct pl330_thread *thrd =NULL ; -unsigned long flags;int chans, i;if (pl330->state ==DYING )
@@ -1702,8 +1701,6 @@ static struct pl330_thread *pl330_reques chans = pl330->pcfg.num_chan; -spin_lock_irqsave (&pl330->lock, flags); -for (i =0 ; i < chans; i++) { thrd = &pl330->channels[i]; if ((thrd->free) && (!_manager_ns(thrd) ||
@@ -1721,8 +1718,6 @@ static struct pl330_thread *pl330_reques thrd =NULL ; } -spin_unlock_irqrestore (&pl330->lock, flags); -return thrd; }
@@ -1740,7 +1735,6 @@ static inline void _free_event(struct plstatic void pl330_release_channel (struct pl330_thread *thrd) {struct pl330_dmac *pl330; -unsigned long flags;if (!thrd || thrd->free)return ;
@@ -1752,10 +1746,8 @@ static void pl330_release_channel(struct pl330 = thrd->dmac; -spin_lock_irqsave (&pl330->lock, flags);_free_event (thrd, thrd->ev); thrd->free = true; -spin_unlock_irqrestore (&pl330->lock, flags); }/* Initialize the structure for PL330 configuration, that can be used
@@ -2120,20 +2112,20 @@ static int pl330_alloc_chan_resources(ststruct pl330_dmac *pl330 = pch->dmac;unsigned long flags; -spin_lock_irqsave (&pch->lock, flags); +spin_lock_irqsave (&pl330->lock, flags);dma_cookie_init (chan); pch->cyclic = false; pch->thread =pl330_request_channel (pl330);if (!pch->thread) { -spin_unlock_irqrestore (&pch->lock, flags); +spin_unlock_irqrestore (&pl330->lock, flags);return -ENOMEM ; }tasklet_init (&pch->task, pl330_tasklet, (unsigned long ) pch); -spin_unlock_irqrestore (&pch->lock, flags); +spin_unlock_irqrestore (&pl330->lock, flags);return 1 ; }
@@ -2236,12 +2228,13 @@ static int pl330_pause(struct dma_chan *static void pl330_free_chan_resources (struct dma_chan *chan) {struct dma_pl330_chan *pch =to_pchan (chan); +struct pl330_dmac *pl330 = pch->dmac;unsigned long flags;tasklet_kill (&pch->task);pm_runtime_get_sync (pch->dmac->ddma.dev); -spin_lock_irqsave (&pch->lock, flags); +spin_lock_irqsave (&pl330->lock, flags);pl330_release_channel (pch->thread); pch->thread =NULL ;
@@ -2249,7 +2242,7 @@ static void pl330_free_chan_resources(stif (pch->cyclic)list_splice_tail_init (&pch->work_list, &pch->dmac->desc_pool); -spin_unlock_irqrestore (&pch->lock, flags); +spin_unlock_irqrestore (&pl330->lock, flags);pm_runtime_mark_last_busy (pch->dmac->ddma.dev);pm_runtime_put_autosuspend (pch->dmac->ddma.dev); }
Patches currently in stable-queue which might be from [email protected] are
queue-4.9/dmaengine-pl330-fix-double-lock.patch