All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] iio: buffer-dmaengine: fix dma_vec building for coalesced sg tables
@ 2026-08-28 10:46 Nuno Sá
  2026-08-28 10:46 ` [PATCH v2 1/2] iio: buffer-dmaengine: fix sg entry iteration when building dma_vecs Nuno Sá
  2026-08-28 10:46 ` [PATCH v2 2/2] iio: buffer-dmaengine: drop dead max_size computation Nuno Sá
  0 siblings, 2 replies; 4+ messages in thread
From: Nuno Sá @ 2026-08-28 10:46 UTC (permalink / raw)
  To: linux-iio; +Cc: Paul Cercueil, Jonathan Cameron, David Lechner, Andy Shevchenko

Patch 1 fixes iio_dmaengine_buffer_submit_block() mixing up the CPU and the
DMA view of a DMABUF's scatterlist: it counted entries with
sg_nents_for_len() (CPU lengths) but consumed sg_dma_address()/sg_dma_len(),
which are only valid for the first sgt->nents entries. With an IOMMU
coalescing the mapping, the loop walks past the mapped set and hands a
garbage vec to the DMA engine, which wedges the buffer.

Patch 2 is the cleanup Jonathan spotted while reviewing v1: the max_size
computation at the top of the same function is dead, as only the fileio
branch consumes it and that branch computes it again where it is used.

---
Changes in v2:
- Patch 1:
  - Size the vec array with sgt->nents instead of sg_nents_for_dma().
  - Cut the new comment down to the invariant a reader needs.
  - Moved 'nents = i' up to right after the fill loop.
  - MOved `sgl = block->sg_table->sgl` to the place where we need sgl.
- Patch 2:
  - New patch dropping the dead max_size computation at the top of
    iio_dmaengine_buffer_submit_block() (Jonathan).
- Link to v1: https://patch.msgid.link/20260818-iio-buffer-dmabuf-iommu-fic-v1-1-4ff1e44a5073@analog.com

---
Michael Hennerich (1):
      iio: buffer-dmaengine: fix sg entry iteration when building dma_vecs

Nuno Sá (1):
      iio: buffer-dmaengine: drop dead max_size computation

 drivers/iio/buffer/industrialio-buffer-dmaengine.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)
---
base-commit: b756b143e5391151e577ae645b1378a43f93c2f5
change-id: 20260818-iio-buffer-dmabuf-iommu-fic-1b281f15e5a4
--

Thanks!
- Nuno Sá


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v2 1/2] iio: buffer-dmaengine: fix sg entry iteration when building dma_vecs
  2026-08-28 10:46 [PATCH v2 0/2] iio: buffer-dmaengine: fix dma_vec building for coalesced sg tables Nuno Sá
@ 2026-08-28 10:46 ` Nuno Sá
  2026-08-30  0:23   ` Jonathan Cameron
  2026-08-28 10:46 ` [PATCH v2 2/2] iio: buffer-dmaengine: drop dead max_size computation Nuno Sá
  1 sibling, 1 reply; 4+ messages in thread
From: Nuno Sá @ 2026-08-28 10:46 UTC (permalink / raw)
  To: linux-iio; +Cc: Paul Cercueil, Jonathan Cameron, David Lechner, Andy Shevchenko

From: Michael Hennerich <michael.hennerich@analog.com>

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")

Assisted-by: Claude:claude-fable-5
Fixes: 7a86d469983a ("iio: buffer-dmaengine: Support new DMABUF based userspace API")
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>

---

Note the Signed-off-by is just because I'm carrying Michael's patch!
---
 drivers/iio/buffer/industrialio-buffer-dmaengine.c | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index ecc02a427b92..376486be3f55 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -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

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v2 2/2] iio: buffer-dmaengine: drop dead max_size computation
  2026-08-28 10:46 [PATCH v2 0/2] iio: buffer-dmaengine: fix dma_vec building for coalesced sg tables Nuno Sá
  2026-08-28 10:46 ` [PATCH v2 1/2] iio: buffer-dmaengine: fix sg entry iteration when building dma_vecs Nuno Sá
@ 2026-08-28 10:46 ` Nuno Sá
  1 sibling, 0 replies; 4+ messages in thread
From: Nuno Sá @ 2026-08-28 10:46 UTC (permalink / raw)
  To: linux-iio; +Cc: Paul Cercueil, Jonathan Cameron, David Lechner, Andy Shevchenko

iio_dmaengine_buffer_submit_block() computes max_size unconditionally at
the top of the function, but only the fileio branch consumes it - and
that branch already computes it again right where it is used. Drop the
unconditional copy.

No functional change intended.

Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
 drivers/iio/buffer/industrialio-buffer-dmaengine.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index 376486be3f55..284d4ef92104 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -88,9 +88,6 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
 	unsigned int i;
 	int nents;
 
-	max_size = min(block->size, dmaengine_buffer->max_size);
-	max_size = round_down(max_size, dmaengine_buffer->align);
-
 	if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN)
 		dma_dir = DMA_DEV_TO_MEM;
 	else

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v2 1/2] iio: buffer-dmaengine: fix sg entry iteration when building dma_vecs
  2026-08-28 10:46 ` [PATCH v2 1/2] iio: buffer-dmaengine: fix sg entry iteration when building dma_vecs Nuno Sá
@ 2026-08-30  0:23   ` Jonathan Cameron
  0 siblings, 0 replies; 4+ messages in thread
From: Jonathan Cameron @ 2026-08-30  0:23 UTC (permalink / raw)
  To: Nuno Sá; +Cc: linux-iio, Paul Cercueil, David Lechner, Andy Shevchenko

On Fri, 28 Aug 2026 11:46:55 +0100
Nuno Sá <nuno.sa@analog.com> wrote:

> From: Michael Hennerich <michael.hennerich@analog.com>
> 
> 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")
> 
> Assisted-by: Claude:claude-fable-5
> Fixes: 7a86d469983a ("iio: buffer-dmaengine: Support new DMABUF based userspace API")
> Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
Applied to the fixes-togreg branch of iio.git and marked for stable.

Seems I can get away with taking patch 2 via the testing branch as
well :)  So done that.

Both those branches will get rebased so if anyone else has feedback on this
series it would be good to have.

Thanks,

Jonathan

> 
> ---
> 
> Note the Signed-off-by is just because I'm carrying Michael's patch!
> ---
>  drivers/iio/buffer/industrialio-buffer-dmaengine.c | 16 +++++++++++-----
>  1 file changed, 11 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
> index ecc02a427b92..376486be3f55 100644
> --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
> +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
> @@ -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
> 


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-08-30  0:23 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-28 10:46 [PATCH v2 0/2] iio: buffer-dmaengine: fix dma_vec building for coalesced sg tables Nuno Sá
2026-08-28 10:46 ` [PATCH v2 1/2] iio: buffer-dmaengine: fix sg entry iteration when building dma_vecs Nuno Sá
2026-08-30  0:23   ` Jonathan Cameron
2026-08-28 10:46 ` [PATCH v2 2/2] iio: buffer-dmaengine: drop dead max_size computation Nuno Sá

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.