Messages in this thread | | | Date | Wed, 26 Jul 2023 22:57:56 +0200 | Subject | Re: [PATCH] dmaengine: xgene: Fix potential deadlock on &chan->lock | From | Christophe JAILLET <> |
| |
Le 26/07/2023 à 13:16, Chengfeng Ye a écrit : > As xgene_dma_cleanup_descriptors() is invoked by both tasklet > xgene_dma_tasklet_cb() under softirq context and > xgene_dma_free_chan_resources() callback that executed under process > context, the lock aquicision of &chan->lock inside > xgene_dma_cleanup_descriptors() should disable irq otherwise deadlock > could happen if the tasklet softirq preempts the execution of process > context code while the lock is held in process context on the same CPU. > > Possible deadlock scenario: > xgene_dma_free_chan_resources() > -> xgene_dma_cleanup_descriptors() > -> spin_lock(&chan->lock) > <tasklet softirq> > -> xgene_dma_tasklet_cb() > -> xgene_dma_cleanup_descriptors() > -> spin_lock(&chan->lock) (deadlock here) > > This flaw was found by an experimental static analysis tool I am developing > for irq-related deadlock.
Hi,
first of all, for what I've seen from your numerous recent patches, all this look real great ! :) And your experimental tool looks really promising.
Even if I'm not always confident with my understanding of locking and related subtilities, I wonder if in the cases of <tasklet softirq>, like above, using spin_lock_bh() would be enough? It should be less agressive than spin_lock_irqsave() but still handle the use case you have spotted.
Just my 2c.
CJ
> > The tentative patch fixes the potential deadlock by spin_lock_irqsave() in > plx_dma_process_desc() to disable irq while lock is held. > > Signed-off-by: Chengfeng Ye <dg573847474@gmail.com> > --- > drivers/dma/xgene-dma.c | 5 +++-- > 1 file changed, 3 insertions(+), 2 deletions(-) > > diff --git a/drivers/dma/xgene-dma.c b/drivers/dma/xgene-dma.c > index 3589b4ef50b8..e766511badcf 100644 > --- a/drivers/dma/xgene-dma.c > +++ b/drivers/dma/xgene-dma.c > @@ -689,11 +689,12 @@ static void xgene_dma_cleanup_descriptors(struct xgene_dma_chan *chan) > struct xgene_dma_desc_sw *desc_sw, *_desc_sw; > struct xgene_dma_desc_hw *desc_hw; > struct list_head ld_completed; > + unsigned long flags; > u8 status; > > INIT_LIST_HEAD(&ld_completed); > > - spin_lock(&chan->lock); > + spin_lock_irqsave(&chan->lock, flags); > > /* Clean already completed and acked descriptors */ > xgene_dma_clean_completed_descriptor(chan); > @@ -762,7 +763,7 @@ static void xgene_dma_cleanup_descriptors(struct xgene_dma_chan *chan) > */ > xgene_chan_xfer_ld_pending(chan); > > - spin_unlock(&chan->lock); > + spin_unlock_irqrestore(&chan->lock, flags); > > /* Run the callback for each descriptor, in order */ > list_for_each_entry_safe(desc_sw, _desc_sw, &ld_completed, node) {
| |