[PATCH 1/1] coresight: tmc-etr: Sync the trace buffer for the device
Jie Gan
jie.gan at oss.qualcomm.com
Tue Sep 15 20:04:20 PDT 2026
Hi,
On 9/15/2026 9:05 PM, NoNine wrote:
> From: Min Chen <min.chen at siengine.com>
>
> The flat ETR buffer comes from dma_alloc_noncoherent(), which zeroes it
> with CPU stores. The DMA API requires the caller to sync the buffer for
> the device before the device writes into it, but the TMC driver only
> ever syncs for the CPU afterwards. On a non-coherent sink the zero fill
> is therefore still dirty in cache when the ETR starts writing, and its
> write-back lands on top of the trace data.
>
Agree, without the sync, the dirty data may overwrites the trace data.
> Add a sync_for_device() buffer operation and call it from
> __tmc_etr_enable_hw() just before the TMC is enabled. Only the flat
> buffer implements it. The ETR_SG and CATU data pages are synced by
> dma_map_page() when they are allocated; their remaining corner case, a
> barrier packet followed by a live-drain re-arm, is left for a separate
> change.
>
> Tested on an AD1000 EVB: five first windows on freshly allocated
> buffers, including the first capture of a boot, all without the
> previous all-zero-formatter-frame runs.
>
> Signed-off-by: Min Chen <min.chen at siengine.com>
> ---
> .../hwtracing/coresight/coresight-tmc-etr.c | 29 +++++++++++++++++++
> drivers/hwtracing/coresight/coresight-tmc.h | 1 +
> 2 files changed, 30 insertions(+)
>
> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
> index 76a8cb2..bf1d6c6 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
> +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
> @@ -689,10 +689,29 @@ static ssize_t tmc_etr_get_data_flat_buf(struct etr_buf *etr_buf,
> return len;
> }
>
> +/*
> + * tmc_etr_sync_flat_buf_for_device: Drop any CPU cache lines over the trace
> + * buffer before the ETR is allowed to write into it. The buffer is allocated
> + * with dma_alloc_noncoherent(), which zeroes it with CPU stores, and the DMA
> + * API requires a sync for the device before the device writes into the
> + * memory. Without it a non-coherent sink writes into memory while the zero
> + * fill is still dirty in cache, and the write-back lands on top of the trace
> + * data.
> + */
> +static void tmc_etr_sync_flat_buf_for_device(struct etr_buf *etr_buf)
> +{
> + struct etr_flat_buf *flat_buf = etr_buf->private;
> + struct device *real_dev = flat_buf->dev->parent;
> +
> + dma_sync_single_for_device(real_dev, flat_buf->daddr, etr_buf->size,
> + DMA_FROM_DEVICE);
> +}
> +
> static const struct etr_buf_operations etr_flat_buf_ops = {
> .alloc = tmc_etr_alloc_flat_buf,
> .free = tmc_etr_free_flat_buf,
> .sync = tmc_etr_sync_flat_buf,
> + .sync_for_device = tmc_etr_sync_flat_buf_for_device,
> .get_data = tmc_etr_get_data_flat_buf,
> };
>
> @@ -1113,6 +1132,16 @@ static int __tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
> writel_relaxed(ffcr, drvdata->base + TMC_FFCR);
>
> writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
> +
> + /*
> + * Hand the buffer over in a state the device can write into: drop
> + * any dirty CPU cache lines first, or they get written back over
> + * the trace data the ETR produces. Only the flat buffer needs
> + * this; the ETR_SG and CATU data pages are synced by
> + * dma_map_page() when they are allocated.
> + */
> + if (etr_buf->ops->sync_for_device)
> + etr_buf->ops->sync_for_device(etr_buf);
Can we add a condition to perform the synchronization only when flat_buf
mode is enabled, instead of introducing a new operation that is required
only for flat_buf mode?
if (etr_buf->mode == ETR_MODE_FLAT)
tmc_etr_sync_flat_buf_for_device(etr_buf);
Thanks,
Jie
> tmc_enable_hw(drvdata);
>
> CS_LOCK(drvdata->base);
> diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
> index 6541a27..3dd17da 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc.h
> +++ b/drivers/hwtracing/coresight/coresight-tmc.h
> @@ -277,6 +277,7 @@ struct etr_buf_operations {
> int (*alloc)(struct tmc_drvdata *drvdata, struct etr_buf *etr_buf,
> int node, void **pages);
> void (*sync)(struct etr_buf *etr_buf, u64 rrp, u64 rwp);
> + void (*sync_for_device)(struct etr_buf *etr_buf);
> ssize_t (*get_data)(struct etr_buf *etr_buf, u64 offset, size_t len,
> char **bufpp);
> void (*free)(struct etr_buf *etr_buf);
More information about the linux-arm-kernel
mailing list