mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
iio: buffer-dmaengine: fix sg entry iteration when building dma_vecs
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 ab2cbeb0ed ("iommu/dma: Handle SG length overflow better")
Assisted-by: Claude:claude-fable-5
Fixes: 7a86d46998 ("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>
Cc: stable@vger.kernel.org
Signed-off-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
This commit is contained in:
parent
4daa9ca557
commit
0d0ffbcc92
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user