All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] dmaengine: qcom: bam_dma: Report the residue of packet
@ 2026-11-15 15:52 Nickolay Goppen
  2026-11-15 16:08 ` sashiko-bot
  0 siblings, 1 reply; 3+ messages in thread
From: Nickolay Goppen @ 2026-11-15 15:52 UTC (permalink / raw)
  To: Vinod Koul, Frank Li
  Cc: linux-arm-msm, dmaengine, linux-kernel, Nickolay Goppen

The BAM writes back the number of bytes actually received into the
descriptor that ended an RX transfer. Report this through the standard
dmaengine_result (residue = length - transferred) so clients get the
packet length from callback_result().

Assisted-by: LLM
Signed-off-by: Nickolay Goppen <setotau@mainlining.org>
---
IPA v2.x uses BAM to send and receive IP packets, to and from the AP.
However, unlike its predecessor BAM-DMUX, it doesn't send information
about the packet length, but ipa2 driver will need that. To find the 
length of the packet, one must instead read the difference between 
packet length and residue.
---
 drivers/dma/qcom/bam_dma.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c
index e62e411dc8f8..b5cfe3ce3406 100644
--- a/drivers/dma/qcom/bam_dma.c
+++ b/drivers/dma/qcom/bam_dma.c
@@ -65,6 +65,7 @@ struct bam_async_desc {
 
 	u32 num_desc;
 	u32 xfer_len;
+	size_t transferred;
 
 	/* transaction flags, EOT|EOB|NWD */
 	u16 flags;
@@ -447,6 +448,30 @@ static inline void __iomem *bam_addr(struct bam_device *bdev, u32 pipe,
 		r.ee_mult * bdev->ee;
 }
 
+/**
+ * bam_fifo_xfer_len - bytes transferred for a completed descriptor chunk
+ * @bchan: BAM channel the chunk was queued on
+ * @async_desc: BAM async descriptor owning the chunk
+ *
+ * For RX pipes the BAM overwrites the size field of the descriptor that
+ * ended the transfer with the number of bytes actually written to memory,
+ * which can be less than the buffer size (e.g. an IPA packet).  Those
+ * values live in the FIFO copy, not in async_desc->desc[].
+ */
+
+static u32 bam_fifo_xfer_len(struct bam_chan *bchan,
+			     const struct bam_async_desc *async_desc)
+	struct bam_desc_hw *fifo = PTR_ALIGN(bchan->fifo_virt,
+					     sizeof(struct bam_desc_hw));
+	u32 len = 0;
+	unsigned int i;
+
+	for (i = bchan->head; i < bchan->head + async_desc->xfer_len; i++)
+		len += le16_to_cpu(fifo[i % MAX_DESCRIPTORS].size);
+
+	return len;
+}
+
 /**
  * bam_reset() - reset and initialize BAM registers
  * @bdev: bam device
@@ -732,6 +757,8 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan,
 		} while (remainder > 0);
 	}
 
+	async_desc->vd.tx.metadata_ops = &metadata_ops;
+
 	return vchan_tx_prep(&bchan->vc, &async_desc->vd, flags);
 }
 
@@ -881,6 +908,11 @@ static u32 process_channel_irqs(struct bam_device *bdev)
 			if (avail < async_desc->xfer_len)
 				break;
 
+			async_desc->transferred +=
+				bam_fifo_xfer_len(bchan, async_desc);
+			async_desc->vd.tx_result.residue =
+				async_desc->length - async_desc->transferred;
+
 			/* manage FIFO */
 			bchan->head += async_desc->xfer_len;
 			bchan->head %= MAX_DESCRIPTORS;

---
base-commit: 2cc223d6ffadd48e25eff0c8a03108a5d7a61c41
change-id: 20261115-qcom-bam-dma-metadata-f34e592d400c

Best regards,
--  
Nickolay Goppen <setotau@mainlining.org>

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

* Re: [PATCH] dmaengine: qcom: bam_dma: Report the residue of packet
  2026-11-15 15:52 [PATCH] dmaengine: qcom: bam_dma: Report the residue of packet Nickolay Goppen
@ 2026-11-15 16:08 ` sashiko-bot
  2026-11-15 16:14   ` Nickolay Goppen
  0 siblings, 1 reply; 3+ messages in thread
From: sashiko-bot @ 2026-11-15 16:08 UTC (permalink / raw)
  To: Nickolay Goppen; +Cc: dmaengine, vkoul, Frank.Li

Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [High] dmaengine: qcom: bam_dma: missing opening brace in
  bam_fifo_xfer_len()
- [High] dmaengine: qcom: bam_dma: undeclared metadata_ops in
  bam_prep_slave_sg()
- [Medium] dmaengine: qcom: bam_dma: missing read barrier in
  process_channel_irqs()
It also noticed 2 related pre-existing issues this patch does not
introduce:
- [Critical] dmaengine: qcom: bam_dma: ring buffer space miscalculation
  in bam_start_dma() [1]
- [High] dmaengine: qcom: bam_dma: unclocked register access in
  bam_dma_irq() [2]
--

commit e40d68fe8bef09bdcbef42721a06c3fd7ad7197d
Author: Nickolay Goppen <setotau@mainlining.org>

dmaengine: qcom: bam_dma: Report the residue of packet

This commit calculates the number of bytes transferred from the BAM FIFO
descriptors and reports the transfer residue in async_desc->vd.tx_result.residue
upon descriptor completion.

> diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c
> index e62e411dc8f8b..b5cfe3ce34060 100644
> --- a/drivers/dma/qcom/bam_dma.c
> +++ b/drivers/dma/qcom/bam_dma.c

[ ... ]

> @@ -447,6 +448,30 @@ static inline void __iomem *bam_addr(struct bam_device *bdev, u32 pipe,
>  		r.ee_mult * bdev->ee;
>  }
>  
> +/**
> + * bam_fifo_xfer_len - bytes transferred for a completed descriptor chunk
> + * @bchan: BAM channel the chunk was queued on
> + * @async_desc: BAM async descriptor owning the chunk
> + *
> + * For RX pipes the BAM overwrites the size field of the descriptor that
> + * ended the transfer with the number of bytes actually written to memory,
> + * which can be less than the buffer size (e.g. an IPA packet).  Those
> + * values live in the FIFO copy, not in async_desc->desc[].
> + */
> +
> +static u32 bam_fifo_xfer_len(struct bam_chan *bchan,
> +			     const struct bam_async_desc *async_desc)
> +	struct bam_desc_hw *fifo = PTR_ALIGN(bchan->fifo_virt,
> +					     sizeof(struct bam_desc_hw));
> +	u32 len = 0;
> +	unsigned int i;
> +
> +	for (i = bchan->head; i < bchan->head + async_desc->xfer_len; i++)
> +		len += le16_to_cpu(fifo[i % MAX_DESCRIPTORS].size);
> +
> +	return len;
> +}

[Severity: High]
bam_fifo_xfer_len() is missing its opening '{' after the parameter list,
which breaks the build when CONFIG_QCOM_BAM_DMA is enabled.

[Severity: Critical]
This problem wasn't introduced by this patch, but in bam_start_dma(), the
producer side of the same descriptor FIFO ring buffer miscalculates
available space [1] and can overwrite in-flight DMA descriptors:

drivers/dma/qcom/bam_dma.c:bam_start_dma() {
	...
		avail = CIRC_SPACE(bchan->tail, bchan->head,
				   MAX_DESCRIPTORS + 1);
	...
		bchan->tail += async_desc->xfer_len;
		bchan->tail %= MAX_DESCRIPTORS;
}

CIRC_SPACE() is called with MAX_DESCRIPTORS + 1 (4096), which returns 4095
when the ring is empty (tail == head), while bchan->tail and bchan->head
wrap modulo MAX_DESCRIPTORS (4095).  Enqueuing 4095 descriptors wraps
bchan->tail back to bchan->head, making a full ring appear empty so
subsequent submissions overwrite active descriptors before hardware
processes them.

> @@ -732,6 +757,8 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan,
>  		} while (remainder > 0);
>  	}
>  
> +	async_desc->vd.tx.metadata_ops = &metadata_ops;
> +
>  	return vchan_tx_prep(&bchan->vc, &async_desc->vd, flags);
>  }
>  

[Severity: High]
metadata_ops is not declared anywhere in this file or its included headers,
which causes compilation to fail.

> @@ -881,6 +908,11 @@ static u32 process_channel_irqs(struct bam_device *bdev)
>  			if (avail < async_desc->xfer_len)
>  				break;
>  
> +			async_desc->transferred +=
> +				bam_fifo_xfer_len(bchan, async_desc);
> +			async_desc->vd.tx_result.residue =
> +				async_desc->length - async_desc->transferred;
> +
>  			/* manage FIFO */
>  			bchan->head += async_desc->xfer_len;
>  			bchan->head %= MAX_DESCRIPTORS;

[Severity: Medium]
Can bam_fifo_xfer_len() read stale fifo[].size values before the hardware
descriptor writeback is observed?

bchan->fifo_virt is allocated with dma_alloc_wc() (Normal Non-Cacheable
memory on ARM/ARM64, which allows speculative loads), and earlier in
process_channel_irqs() BAM_P_SW_OFSTS is read with readl_relaxed():

drivers/dma/qcom/bam_dma.c:process_channel_irqs() {
	...
		offset = readl_relaxed(bam_addr(bdev, i, BAM_P_SW_OFSTS)) &
				       P_SW_OFSTS_MASK;
}

Because readl_relaxed() has no read barrier and the address of
fifo[i % MAX_DESCRIPTORS].size does not depend on offset, the CPU can
speculatively load the initial buffer size written by bam_start_dma()
instead of the byte count written back by the BAM hardware, reporting the
wrong residue to clients.  Using readl() for BAM_P_SW_OFSTS or adding a
dma_rmb() before reading fifo[].size would order the reads.

[Severity: High]
This problem wasn't introduced by this patch, but bam_dma_irq() calls
process_channel_irqs() before acquiring a runtime PM reference [2]:

drivers/dma/qcom/bam_dma.c:bam_dma_irq() {
	...
	srcs |= process_channel_irqs(bdev);
	...
	ret = pm_runtime_get_sync(bdev->dev);
}

If a shared or spurious interrupt fires while the BAM controller is
runtime-suspended with its clocks disabled, the readl_relaxed() of
BAM_IRQ_SRCS_EE at the start of process_channel_irqs() accesses an
unclocked register and triggers a synchronous external abort.

[1] https://sashiko.dev/#/bug/linux-a877fd6c-244a-404b-88a7-7d77506711a9
[2] https://sashiko.dev/#/bug/linux-0ca05019-399c-4f9d-a7e1-d74c7ddced75

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261115-qcom-bam-dma-metadata-v1-1-52e4de54ffd3@mainlining.org?part=1

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

* Re: [PATCH] dmaengine: qcom: bam_dma: Report the residue of packet
  2026-11-15 16:08 ` sashiko-bot
@ 2026-11-15 16:14   ` Nickolay Goppen
  0 siblings, 0 replies; 3+ messages in thread
From: Nickolay Goppen @ 2026-11-15 16:14 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: dmaengine, vkoul, Frank.Li


15.11.2026 19:08, sashiko-bot@kernel.org пишет:
> Thank you for your contribution! Sashiko AI review found 3 potential
> issues to consider:
> - [High] dmaengine: qcom: bam_dma: missing opening brace in
>    bam_fifo_xfer_len()
> - [High] dmaengine: qcom: bam_dma: undeclared metadata_ops in
>    bam_prep_slave_sg()
> - [Medium] dmaengine: qcom: bam_dma: missing read barrier in
>    process_channel_irqs()
> It also noticed 2 related pre-existing issues this patch does not
> introduce:
> - [Critical] dmaengine: qcom: bam_dma: ring buffer space miscalculation
>    in bam_start_dma() [1]
> - [High] dmaengine: qcom: bam_dma: unclocked register access in
>    bam_dma_irq() [2]
> --
>
> commit e40d68fe8bef09bdcbef42721a06c3fd7ad7197d
> Author: Nickolay Goppen <setotau@mainlining.org>
>
> dmaengine: qcom: bam_dma: Report the residue of packet
>
> This commit calculates the number of bytes transferred from the BAM FIFO
> descriptors and reports the transfer residue in async_desc->vd.tx_result.residue
> upon descriptor completion.
>
>> diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c
>> index e62e411dc8f8b..b5cfe3ce34060 100644
>> --- a/drivers/dma/qcom/bam_dma.c
>> +++ b/drivers/dma/qcom/bam_dma.c
> [ ... ]
>
>> @@ -447,6 +448,30 @@ static inline void __iomem *bam_addr(struct bam_device *bdev, u32 pipe,
>>   		r.ee_mult * bdev->ee;
>>   }
>>   
>> +/**
>> + * bam_fifo_xfer_len - bytes transferred for a completed descriptor chunk
>> + * @bchan: BAM channel the chunk was queued on
>> + * @async_desc: BAM async descriptor owning the chunk
>> + *
>> + * For RX pipes the BAM overwrites the size field of the descriptor that
>> + * ended the transfer with the number of bytes actually written to memory,
>> + * which can be less than the buffer size (e.g. an IPA packet).  Those
>> + * values live in the FIFO copy, not in async_desc->desc[].
>> + */
>> +
>> +static u32 bam_fifo_xfer_len(struct bam_chan *bchan,
>> +			     const struct bam_async_desc *async_desc)
>> +	struct bam_desc_hw *fifo = PTR_ALIGN(bchan->fifo_virt,
>> +					     sizeof(struct bam_desc_hw));
>> +	u32 len = 0;
>> +	unsigned int i;
>> +
>> +	for (i = bchan->head; i < bchan->head + async_desc->xfer_len; i++)
>> +		len += le16_to_cpu(fifo[i % MAX_DESCRIPTORS].size);
>> +
>> +	return len;
>> +}
> [Severity: High]
> bam_fifo_xfer_len() is missing its opening '{' after the parameter list,
> which breaks the build when CONFIG_QCOM_BAM_DMA is enabled.
>
> [Severity: Critical]
> This problem wasn't introduced by this patch, but in bam_start_dma(), the
> producer side of the same descriptor FIFO ring buffer miscalculates
> available space [1] and can overwrite in-flight DMA descriptors:
>
> drivers/dma/qcom/bam_dma.c:bam_start_dma() {
> 	...
> 		avail = CIRC_SPACE(bchan->tail, bchan->head,
> 				   MAX_DESCRIPTORS + 1);
> 	...
> 		bchan->tail += async_desc->xfer_len;
> 		bchan->tail %= MAX_DESCRIPTORS;
> }
>
> CIRC_SPACE() is called with MAX_DESCRIPTORS + 1 (4096), which returns 4095
> when the ring is empty (tail == head), while bchan->tail and bchan->head
> wrap modulo MAX_DESCRIPTORS (4095).  Enqueuing 4095 descriptors wraps
> bchan->tail back to bchan->head, making a full ring appear empty so
> subsequent submissions overwrite active descriptors before hardware
> processes them.
>
>> @@ -732,6 +757,8 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan,
>>   		} while (remainder > 0);
>>   	}
>>   
>> +	async_desc->vd.tx.metadata_ops = &metadata_ops;
>> +
>>   	return vchan_tx_prep(&bchan->vc, &async_desc->vd, flags);
>>   }
>>   
> [Severity: High]
> metadata_ops is not declared anywhere in this file or its included headers,
> which causes compilation to fail.
>
>> @@ -881,6 +908,11 @@ static u32 process_channel_irqs(struct bam_device *bdev)
>>   			if (avail < async_desc->xfer_len)
>>   				break;
>>   
>> +			async_desc->transferred +=
>> +				bam_fifo_xfer_len(bchan, async_desc);
>> +			async_desc->vd.tx_result.residue =
>> +				async_desc->length - async_desc->transferred;
>> +
>>   			/* manage FIFO */
>>   			bchan->head += async_desc->xfer_len;
>>   			bchan->head %= MAX_DESCRIPTORS;
> [Severity: Medium]
> Can bam_fifo_xfer_len() read stale fifo[].size values before the hardware
> descriptor writeback is observed?
>
> bchan->fifo_virt is allocated with dma_alloc_wc() (Normal Non-Cacheable
> memory on ARM/ARM64, which allows speculative loads), and earlier in
> process_channel_irqs() BAM_P_SW_OFSTS is read with readl_relaxed():
>
> drivers/dma/qcom/bam_dma.c:process_channel_irqs() {
> 	...
> 		offset = readl_relaxed(bam_addr(bdev, i, BAM_P_SW_OFSTS)) &
> 				       P_SW_OFSTS_MASK;
> }
>
> Because readl_relaxed() has no read barrier and the address of
> fifo[i % MAX_DESCRIPTORS].size does not depend on offset, the CPU can
> speculatively load the initial buffer size written by bam_start_dma()
> instead of the byte count written back by the BAM hardware, reporting the
> wrong residue to clients.  Using readl() for BAM_P_SW_OFSTS or adding a
> dma_rmb() before reading fifo[].size would order the reads.
>
> [Severity: High]
> This problem wasn't introduced by this patch, but bam_dma_irq() calls
> process_channel_irqs() before acquiring a runtime PM reference [2]:
>
> drivers/dma/qcom/bam_dma.c:bam_dma_irq() {
> 	...
> 	srcs |= process_channel_irqs(bdev);
> 	...
> 	ret = pm_runtime_get_sync(bdev->dev);
> }
>
> If a shared or spurious interrupt fires while the BAM controller is
> runtime-suspended with its clocks disabled, the readl_relaxed() of
> BAM_IRQ_SRCS_EE at the start of process_channel_irqs() accesses an
> unclocked register and triggers a synchronous external abort.
>
> [1] https://sashiko.dev/#/bug/linux-a877fd6c-244a-404b-88a7-7d77506711a9
> [2] https://sashiko.dev/#/bug/linux-0ca05019-399c-4f9d-a7e1-d74c7ddced75

I will address the issues of the patch in next respin, but I don't know 
what to do with issues that |
wasn't introduced by my patch.

-- 
Best regards,
Nickolay

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

end of thread, other threads:[~2026-11-15 16:14 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-15 15:52 [PATCH] dmaengine: qcom: bam_dma: Report the residue of packet Nickolay Goppen
2026-11-15 16:08 ` sashiko-bot
2026-11-15 16:14   ` Nickolay Goppen

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.