Re: [PATCH v2 61/64] dmaengine: dw: defer callbacks via channel BH
[email protected] Mon, 27 Jul 2026 21:31:17 +0000
| Newsgroups | org.kernel.vger.dmaengine |
|---|---|
| Message-ID | <[email protected]> |
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Concurrent list modification between dwc_scan_descriptors() and dwc_terminate_all() leads to an infinite loop and list corruption. -- --- Patch [61]: [PATCH v2 61/64] dmaengine: dw: defer callbacks via channel BH --- commit d2107b5998f3b825a629b786e29b948ab851a743 Author: Allen Pais <[email protected]> Subject: dmaengine: dw: defer callbacks via channel BH Queue completed descriptors on the channel and invoke their callbacks from per-channel BH work instead of the controller tasklet. Add a synchronize hook so termination can wait for deferred callbacks before their descriptor storage is released. > diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c > index dd75f97a33b3d..e780116a944bc 100644 > --- a/drivers/dma/dw/core.c > +++ b/drivers/dma/dw/core.c > @@ -236,25 +236,24 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, > struct dma_async_tx_descriptor *txd = &desc->txd; > struct dw_desc *child; > unsigned long flags; > > dev_vdbg(chan2dev(&dwc->chan), "descriptor %u complete\n", txd->cookie); > > spin_lock_irqsave(&dwc->lock, flags); > dma_cookie_complete(txd); > - if (callback_required) > - dmaengine_desc_get_callback(txd, &cb); > - else > - memset(&cb, 0, sizeof(cb)); > + list_del_init(&desc->desc_node); > > /* async_tx_ack */ > list_for_each_entry(child, &desc->tx_list, desc_node) > async_tx_ack(&child->txd); > async_tx_ack(&desc->txd); > - dwc_desc_put(dwc, desc); > + if (callback_required) { > + list_add_tail(&desc->desc_node, &dwc->completed_list); [Severity: High] Can this concurrent list modification lead to an infinite loop during channel termination? If dwc_tx_status() races with dwc_terminate_all(), dwc_scan_descriptors() temporarily drops dwc->lock while iterating over dwc->active_list. If dwc_terminate_all() runs concurrently, it acquires the lock, splices active_list to a local list, drops the lock, and begins locklessly iterating the local list: drivers/dma/dw/core.c:dwc_terminate_all() { ... list_splice_init(&dwc->queue, &list); list_splice_init(&dwc->active_list, &list); spin_unlock_irqrestore(&dwc->lock, flags); /* Flush all pending and queued descriptors */ list_for_each_entry_safe(desc, _desc, &list, desc_node) dwc_descriptor_complete(dwc, desc, false); } When dwc_scan_descriptors() resumes, it calls dwc_descriptor_complete() which now modifies desc->desc_node by moving it to dwc->completed_list. Because dwc_terminate_all()'s lockless list_for_each_entry_safe() traversal may have already prefetched a descriptor that is subsequently moved, its iteration could jump into dwc->completed_list. This would prevent the loop termination condition from ever matching the stack-allocated list head in dwc_terminate_all(), causing an infinite loop. Could this sequence happen in practice? > + dma_chan_schedule_bh(&dwc->chan); > + } else { > + dwc_desc_put(dwc, desc); > + } > spin_unlock_irqrestore(&dwc->lock, flags); > - > - dmaengine_desc_callback_invoke(&cb, NULL); > } -- Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=61