Messages in this thread | | | Subject | Re: [PATCH] dmaengine: pl330: fix double lock | From | Marek Szyprowski <> | Date | Wed, 11 Jan 2017 14:20:06 +0100 |
| |
Hi Iago,
On 2017-01-11 14:00, iari@itu.dk wrote: > From: Iago Abal <mail@iagoabal.eu> > > 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'.
At the moment of initialization and releasing resources this is not a problem.
> 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>
> --- > drivers/dma/pl330.c | 19 ++++++------------- > 1 file changed, 6 insertions(+), 13 deletions(-) > > diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c > index 87fd015..3370e56 100644 > --- a/drivers/dma/pl330.c > +++ b/drivers/dma/pl330.c > @@ -1696,7 +1696,6 @@ static bool _chan_ns(const struct pl330_dmac *pl330, int i) > 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) > @@ -1704,8 +1703,6 @@ static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330) > > 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) || > @@ -1723,8 +1720,6 @@ static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330) > thrd = NULL; > } > > - spin_unlock_irqrestore(&pl330->lock, flags); > - > return thrd; > } > > @@ -1742,7 +1737,6 @@ static inline void _free_event(struct pl330_thread *thrd, int ev) > static void pl330_release_channel(struct pl330_thread *thrd) > { > struct pl330_dmac *pl330; > - unsigned long flags; > > if (!thrd || thrd->free) > return; > @@ -1754,10 +1748,8 @@ static void pl330_release_channel(struct pl330_thread *thrd) > > 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 > @@ -2117,20 +2109,20 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) > struct 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; > } > @@ -2228,12 +2220,13 @@ static int pl330_pause(struct dma_chan *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; > @@ -2241,7 +2234,7 @@ static void pl330_free_chan_resources(struct dma_chan *chan) > if (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); > }
Best regards -- Marek Szyprowski, PhD Samsung R&D Institute Poland
| |