aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIago Abal <mail@iagoabal.eu>2017-01-11 08:00:21 -0500
committerVinod Koul <vinod.koul@intel.com>2017-01-25 05:05:11 -0500
commit91539eb1fda2d530d3b268eef542c5414e54bf1a (patch)
tree960f5ca6342ad20837aff18aad6e8ecd7da32fd6
parent6610d0edf6dc7ee97e46ab3a538a565c79d26199 (diff)
dmaengine: pl330: fix double lock
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. Signed-off-by: Iago Abal <mail@iagoabal.eu> Reviewed-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
-rw-r--r--drivers/dma/pl330.c19
1 files changed, 6 insertions, 13 deletions
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 740bbb942594..7539f73df9e0 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -1699,7 +1699,6 @@ static bool _chan_ns(const struct pl330_dmac *pl330, int i)
1699static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330) 1699static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330)
1700{ 1700{
1701 struct pl330_thread *thrd = NULL; 1701 struct pl330_thread *thrd = NULL;
1702 unsigned long flags;
1703 int chans, i; 1702 int chans, i;
1704 1703
1705 if (pl330->state == DYING) 1704 if (pl330->state == DYING)
@@ -1707,8 +1706,6 @@ static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330)
1707 1706
1708 chans = pl330->pcfg.num_chan; 1707 chans = pl330->pcfg.num_chan;
1709 1708
1710 spin_lock_irqsave(&pl330->lock, flags);
1711
1712 for (i = 0; i < chans; i++) { 1709 for (i = 0; i < chans; i++) {
1713 thrd = &pl330->channels[i]; 1710 thrd = &pl330->channels[i];
1714 if ((thrd->free) && (!_manager_ns(thrd) || 1711 if ((thrd->free) && (!_manager_ns(thrd) ||
@@ -1726,8 +1723,6 @@ static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330)
1726 thrd = NULL; 1723 thrd = NULL;
1727 } 1724 }
1728 1725
1729 spin_unlock_irqrestore(&pl330->lock, flags);
1730
1731 return thrd; 1726 return thrd;
1732} 1727}
1733 1728
@@ -1745,7 +1740,6 @@ static inline void _free_event(struct pl330_thread *thrd, int ev)
1745static void pl330_release_channel(struct pl330_thread *thrd) 1740static void pl330_release_channel(struct pl330_thread *thrd)
1746{ 1741{
1747 struct pl330_dmac *pl330; 1742 struct pl330_dmac *pl330;
1748 unsigned long flags;
1749 1743
1750 if (!thrd || thrd->free) 1744 if (!thrd || thrd->free)
1751 return; 1745 return;
@@ -1757,10 +1751,8 @@ static void pl330_release_channel(struct pl330_thread *thrd)
1757 1751
1758 pl330 = thrd->dmac; 1752 pl330 = thrd->dmac;
1759 1753
1760 spin_lock_irqsave(&pl330->lock, flags);
1761 _free_event(thrd, thrd->ev); 1754 _free_event(thrd, thrd->ev);
1762 thrd->free = true; 1755 thrd->free = true;
1763 spin_unlock_irqrestore(&pl330->lock, flags);
1764} 1756}
1765 1757
1766/* Initialize the structure for PL330 configuration, that can be used 1758/* Initialize the structure for PL330 configuration, that can be used
@@ -2122,20 +2114,20 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
2122 struct pl330_dmac *pl330 = pch->dmac; 2114 struct pl330_dmac *pl330 = pch->dmac;
2123 unsigned long flags; 2115 unsigned long flags;
2124 2116
2125 spin_lock_irqsave(&pch->lock, flags); 2117 spin_lock_irqsave(&pl330->lock, flags);
2126 2118
2127 dma_cookie_init(chan); 2119 dma_cookie_init(chan);
2128 pch->cyclic = false; 2120 pch->cyclic = false;
2129 2121
2130 pch->thread = pl330_request_channel(pl330); 2122 pch->thread = pl330_request_channel(pl330);
2131 if (!pch->thread) { 2123 if (!pch->thread) {
2132 spin_unlock_irqrestore(&pch->lock, flags); 2124 spin_unlock_irqrestore(&pl330->lock, flags);
2133 return -ENOMEM; 2125 return -ENOMEM;
2134 } 2126 }
2135 2127
2136 tasklet_init(&pch->task, pl330_tasklet, (unsigned long) pch); 2128 tasklet_init(&pch->task, pl330_tasklet, (unsigned long) pch);
2137 2129
2138 spin_unlock_irqrestore(&pch->lock, flags); 2130 spin_unlock_irqrestore(&pl330->lock, flags);
2139 2131
2140 return 1; 2132 return 1;
2141} 2133}
@@ -2238,12 +2230,13 @@ static int pl330_pause(struct dma_chan *chan)
2238static void pl330_free_chan_resources(struct dma_chan *chan) 2230static void pl330_free_chan_resources(struct dma_chan *chan)
2239{ 2231{
2240 struct dma_pl330_chan *pch = to_pchan(chan); 2232 struct dma_pl330_chan *pch = to_pchan(chan);
2233 struct pl330_dmac *pl330 = pch->dmac;
2241 unsigned long flags; 2234 unsigned long flags;
2242 2235
2243 tasklet_kill(&pch->task); 2236 tasklet_kill(&pch->task);
2244 2237
2245 pm_runtime_get_sync(pch->dmac->ddma.dev); 2238 pm_runtime_get_sync(pch->dmac->ddma.dev);
2246 spin_lock_irqsave(&pch->lock, flags); 2239 spin_lock_irqsave(&pl330->lock, flags);
2247 2240
2248 pl330_release_channel(pch->thread); 2241 pl330_release_channel(pch->thread);
2249 pch->thread = NULL; 2242 pch->thread = NULL;
@@ -2251,7 +2244,7 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
2251 if (pch->cyclic) 2244 if (pch->cyclic)
2252 list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); 2245 list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool);
2253 2246
2254 spin_unlock_irqrestore(&pch->lock, flags); 2247 spin_unlock_irqrestore(&pl330->lock, flags);
2255 pm_runtime_mark_last_busy(pch->dmac->ddma.dev); 2248 pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
2256 pm_runtime_put_autosuspend(pch->dmac->ddma.dev); 2249 pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
2257} 2250}