FAILED: patch "[PATCH] iio: buffer-dmaengine: fix sg entry iteration when building" failed to apply to 6.12-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 0d0ffbcc92e30a8d656ad76a6e278fa3400fed85
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026100621-baritone-yapping-1e8d@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 0d0ffbcc92e30a8d656ad76a6e278fa3400fed85 Mon Sep 17 00:00:00 2001
From: Michael Hennerich <[email protected]>
Date: Fri, 28 Aug 2026 11:46:55 +0100
Subject: [PATCH] iio: buffer-dmaengine: fix sg entry iteration when building
dma_vecs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
iio_dmaengine_buffer_submit_block() counts scatterlist entries with sg_nents_for_len(), which walks the CPU-side lengths (sg->length), but then consumes the DMA-side fields (sg_dma_address()/sg_dma_len()). After dma_map_sgtable() the two views may differ: an IOMMU can coalesce the mapping so that only the first sgt->nents entries carry valid DMA addresses, with nents < orig_nents.
On x86 with an IOMMU enabled, a DMABUF block backed by two 1 MiB system-heap chunks maps to a single 2 MiB IOVA range. The CPU-side count is 2, so the loop reads one entry past the mapped set and emits a garbage vec ({addr = ~0, len = 0}). The DMA engine driver rejects the vec array (prep returns NULL), the fence is signalled with -ENOMEM, which a userspace poller cannot observe, and the block is left in ACTIVE state so every further enqueue of it fails with -EBUSY. The visible symptom is a stream of zero-filled blocks followed by a wedged buffer.
Platforms without an IOMMU never hit this because nents == orig_nents.
Size the vec array with sgt->nents, i.e. the DMA-mapped view, and stop the fill loop once bytes_used is covered - which is allowed to be smaller than the block size - passing the number of vecs actually filled to dmaengine_prep_peripheral_dma_vec().
One vec per mapped entry is enough since coalescing can only ever reduce the number of entries. A single mapped entry longer than the device's maximum segment size would need more than one, but the DMA API already assumes no single segment exceeds it [1], and splitting a vec down to the hardware descriptor size is the DMA engine driver's job - which both current .device_prep_peripheral_dma_vec() implementations do.
[1]: commit ab2cbeb0ed30 ("iommu/dma: Handle SG length overflow better")
drivers/iio/buffer/industrialio-buffer-dmaengine.c +11 -5
@@ -104,10 +104,13 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,if (block->sg_table) {unsigned long flags; - sgl = block->sg_table->sgl; - nents =sg_nents_for_len (sgl, block->bytes_used); -if (nents <0 ) -return nents; +/* +* Only the first sgt->nents entries carry a valid +* sg_dma_address()/sg_dma_len() pair as mapping the table may +* have coalesced entries, in which case nents is smaller than +* orig_nents. +*/ + nents = block->sg_table->nents; vecs =kmalloc_array (nents,sizeof (*vecs),GFP_ATOMIC );if (!vecs)
@@ -115,7 +118,8 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, len_total = block->bytes_used; -for (i =0 ; i < nents; i++) { + sgl = block->sg_table->sgl; +for (i =0 ; i < nents && len_total; i++) { vecs[i].addr =sg_dma_address (sgl); vecs[i].len =min (sg_dma_len (sgl), len_total); len_total -= vecs[i].len;
@@ -123,6 +127,8 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, sgl =sg_next (sgl); } + nents = i; +if (block->cyclic) flags =DMA_PREP_REPEAT ;else
Sasha Levin
maintainerPATCH 1/1 iio: buffer-dmaengine: fix sg entry iteration when building dma_vecs
From: Michael Hennerich <[email protected]>
[ Upstream commit 0d0ffbcc92e30a8d656ad76a6e278fa3400fed85 ]
iio_dmaengine_buffer_submit_block() counts scatterlist entries with sg_nents_for_len(), which walks the CPU-side lengths (sg->length), but then consumes the DMA-side fields (sg_dma_address()/sg_dma_len()). After dma_map_sgtable() the two views may differ: an IOMMU can coalesce the mapping so that only the first sgt->nents entries carry valid DMA addresses, with nents < orig_nents.
On x86 with an IOMMU enabled, a DMABUF block backed by two 1 MiB system-heap chunks maps to a single 2 MiB IOVA range. The CPU-side count is 2, so the loop reads one entry past the mapped set and emits a garbage vec ({addr = ~0, len = 0}). The DMA engine driver rejects the vec array (prep returns NULL), the fence is signalled with -ENOMEM, which a userspace poller cannot observe, and the block is left in ACTIVE state so every further enqueue of it fails with -EBUSY. The visible symptom is a stream of zero-filled blocks followed by a wedged buffer.
Platforms without an IOMMU never hit this because nents == orig_nents.
Size the vec array with sgt->nents, i.e. the DMA-mapped view, and stop the fill loop once bytes_used is covered - which is allowed to be smaller than the block size - passing the number of vecs actually filled to dmaengine_prep_peripheral_dma_vec().
One vec per mapped entry is enough since coalescing can only ever reduce the number of entries. A single mapped entry longer than the device's maximum segment size would need more than one, but the DMA API already assumes no single segment exceeds it [1], and splitting a vec down to the hardware descriptor size is the DMA engine driver's job - which both current .device_prep_peripheral_dma_vec() implementations do.
[1]: commit ab2cbeb0ed30 ("iommu/dma: Handle SG length overflow better")
--- .../iio/buffer/industrialio-buffer-dmaengine.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-)
drivers/iio/buffer/industrialio-buffer-dmaengine.c +11 -5
@@ -82,10 +82,13 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, dma_dir =DMA_MEM_TO_DEV ;if (block->sg_table) { - sgl = block->sg_table->sgl; - nents =sg_nents_for_len (sgl, block->bytes_used); -if (nents <0 ) -return nents; +/* +* Only the first sgt->nents entries carry a valid +* sg_dma_address()/sg_dma_len() pair as mapping the table may +* have coalesced entries, in which case nents is smaller than +* orig_nents. +*/ + nents = block->sg_table->nents; vecs =kmalloc_array (nents,sizeof (*vecs),GFP_ATOMIC );if (!vecs)
@@ -93,7 +96,8 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, len_total = block->bytes_used; -for (i =0 ; i < nents; i++) { + sgl = block->sg_table->sgl; +for (i =0 ; i < nents && len_total; i++) { vecs[i].addr =sg_dma_address (sgl); vecs[i].len =min (sg_dma_len (sgl), len_total); len_total -= vecs[i].len;
@@ -101,6 +105,8 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, sgl =sg_next (sgl); } + nents = i; + desc =dmaengine_prep_peripheral_dma_vec (dmaengine_buffer->chan, vecs, nents, dma_dir,DMA_PREP_INTERRUPT );
Signature · 1 line
2.53.0