From 299cebf6714dbd824e89d76b9abb27b9b7316988 Mon Sep 17 00:00:00 2001 From: Wojciech Jablonski Date: Tue, 31 Mar 2026 14:25:56 +0200 Subject: [PATCH 1/3] telemetry: Use DAI index as an instance ID CPU ID is meant to be used as a measurement identifier only for IO measurements related to IPC/IDC whereas for measuring DAIs IO, a DMA index /DAI index shall be used instead. This commit contains this small correction as well as cosmetic changes related to the DAI IO measurement. Signed-off-by: Wojciech Jablonski --- src/audio/dai-zephyr.c | 8 ++++---- src/include/sof/lib/dai-zephyr.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 06ac05507ff4..6dda66899c26 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -425,7 +425,7 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, } #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS /* Increment performance counters */ - io_perf_monitor_update_data(dd->io_perf_bytes_count, bytes); + io_perf_monitor_update_data(dd->io_perf_dai_byte_count, bytes); #endif return dma_status; @@ -558,12 +558,12 @@ __cold int dai_common_new(struct dai_data *dd, struct comp_dev *dev, /* ignore perf meas init on case of other dai types */ if (perf_type != IO_PERF_INVALID_ID) { struct io_perf_data_item init_data = {perf_type, - cpu_get_id(), + dai_cfg->dai_index, perf_dir, IO_PERF_POWERED_UP_ENABLED, IO_PERF_D0IX_POWER_MODE, 0, 0, 0 }; - io_perf_monitor_init_data(&dd->io_perf_bytes_count, &init_data); + io_perf_monitor_init_data(&dd->io_perf_dai_byte_count, &init_data); } #endif @@ -617,7 +617,7 @@ __cold void dai_common_free(struct dai_data *dd) assert_can_be_cold(); #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS - io_perf_monitor_release_slot(dd->io_perf_bytes_count); + io_perf_monitor_release_slot(dd->io_perf_dai_byte_count); #endif if (dd->group) diff --git a/src/include/sof/lib/dai-zephyr.h b/src/include/sof/lib/dai-zephyr.h index 8ff739fa42d5..cb996e147c4f 100644 --- a/src/include/sof/lib/dai-zephyr.h +++ b/src/include/sof/lib/dai-zephyr.h @@ -164,7 +164,7 @@ struct dai_data { #endif #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS /* io performance measurement */ - struct io_perf_data_item *io_perf_bytes_count; + struct io_perf_data_item *io_perf_dai_byte_count; #endif /* Copier gain params */ struct copier_gain_params *gain_data; From 390c08389ce4997bf11131638198bb362ffc166e Mon Sep 17 00:00:00 2001 From: Wojciech Jablonski Date: Tue, 31 Mar 2026 14:26:24 +0200 Subject: [PATCH 2/3] telemetry: add IO counter for a HDA Host interface Currently SOF can collect IO performance data only for HAD Link interface. The corresponding counter on Host side is missing. This commit adds the missing counter with DMA index used as the counter instance Signed-off-by: Wojciech Jablonski --- src/audio/copier/host_copier.h | 7 +++++++ src/audio/host-legacy.c | 32 ++++++++++++++++++++++++++++++++ src/audio/host-zephyr.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/src/audio/copier/host_copier.h b/src/audio/copier/host_copier.h index 28605ed9c3ab..9db9028a54d7 100644 --- a/src/audio/copier/host_copier.h +++ b/src/audio/copier/host_copier.h @@ -22,6 +22,10 @@ #include #include "copier.h" +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS +struct io_perf_data_item; +#endif + typedef void (*copy_callback_t)(struct comp_dev *dev, size_t bytes); struct host_data; @@ -112,6 +116,9 @@ struct host_data { uint64_t next_sync; uint64_t period_in_cycles; #endif +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + struct io_perf_data_item *io_perf_host_byte_count; +#endif }; int host_common_new(struct host_data *hd, struct comp_dev *dev, diff --git a/src/audio/host-legacy.c b/src/audio/host-legacy.c index a16b3f74e1c3..7f919dbd2eea 100644 --- a/src/audio/host-legacy.c +++ b/src/audio/host-legacy.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -250,6 +251,10 @@ void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t byt if (ret < 0) return; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + io_perf_monitor_update_data(hd->io_perf_host_byte_count, bytes); +#endif + hd->total_data_processed += bytes; /* new local period, update host buffer position blks @@ -599,6 +604,14 @@ static struct comp_dev *host_new(const struct comp_driver *drv, void host_common_free(struct host_data *hd) { +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Check for NULL just in case the params() step is omitted */ + if (hd->io_perf_host_byte_count) { + io_perf_monitor_release_slot(hd->io_perf_host_byte_count); + hd->io_perf_host_byte_count = NULL; + } +#endif + dma_put(hd->dma); ipc_msg_free(hd->msg); @@ -829,6 +842,25 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, pcm_get_conversion_function(audio_stream_get_frm_fmt(&hd->local_buffer->stream), audio_stream_get_frm_fmt(&hd->local_buffer->stream)); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + if (!hd->io_perf_host_byte_count) { + /* On the Host side, unlike the DAI side, the port direction values (INPUT/OUTPUT) + * match the stream direction enum values (CAPTURE/PLAYBACK), so we can directly + * use params->direction here. + */ + struct io_perf_data_item init_data = { + IO_PERF_HDA_ID, + hd->chan->index, + params->direction, + IO_PERF_POWERED_UP_ENABLED, + IO_PERF_D0IX_POWER_MODE, + 0, 0, 0 + }; + + io_perf_monitor_init_data(&hd->io_perf_host_byte_count, &init_data); + } +#endif + out: hd->cb_dev = dev; diff --git a/src/audio/host-zephyr.c b/src/audio/host-zephyr.c index bdb5c5759274..613b38bcd00f 100644 --- a/src/audio/host-zephyr.c +++ b/src/audio/host-zephyr.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -262,6 +263,10 @@ void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t byt return; } +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + io_perf_monitor_update_data(hd->io_perf_host_byte_count, bytes); +#endif + hd->total_data_processed += bytes; /* new local period, update host buffer position blks @@ -771,6 +776,14 @@ __cold void host_common_free(struct host_data *hd) { assert_can_be_cold(); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Check for NULL just in case the params() step is omitted */ + if (hd->io_perf_host_byte_count) { + io_perf_monitor_release_slot(hd->io_perf_host_byte_count); + hd->io_perf_host_byte_count = NULL; + } +#endif + sof_dma_put(hd->dma); ipc_msg_free(hd->msg); @@ -1074,6 +1087,24 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, hd->copy = hd->copy_type == COMP_COPY_ONE_SHOT ? host_copy_one_shot : host_copy_normal; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + if (!hd->io_perf_host_byte_count) { + /* On the Host side, unlike the DAI side, the port direction values (INPUT/OUTPUT) + * match the stream direction enum values (CAPTURE/PLAYBACK), so we can directly + * use params->direction here. + */ + struct io_perf_data_item init_data = { + IO_PERF_HDA_ID, + hd->chan->index, + params->direction, + IO_PERF_POWERED_UP_ENABLED, + IO_PERF_D0IX_POWER_MODE, + 0, 0, 0 + }; + io_perf_monitor_init_data(&hd->io_perf_host_byte_count, &init_data); + } +#endif + return 0; err_free_block_cfg: From b24b8cb5b222ab24f7bdb77830776b0a3a9b4517 Mon Sep 17 00:00:00 2001 From: Wojciech Jablonski Date: Thu, 2 Apr 2026 11:14:34 +0200 Subject: [PATCH 3/3] telemetry: fix lock leak on slot alloc failure During allocation of a counter slot, spinlock isn't released in case of failure. This could lead to deadlock in case of failed allocation. This change fixes that by adding the missing release operation. Signed-off-by: Wojciech Jablonski --- src/debug/telemetry/performance_monitor.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/debug/telemetry/performance_monitor.c b/src/debug/telemetry/performance_monitor.c index 250102edf5e1..b7aedc5ffa7c 100644 --- a/src/debug/telemetry/performance_monitor.c +++ b/src/debug/telemetry/performance_monitor.c @@ -128,7 +128,7 @@ static bool perf_bitmap_is_bit_clear(struct perf_bitmap * const bitmap, size_t b struct perf_data_item_comp *perf_data_getnext(void) { - int idx; + size_t idx; int ret = perf_bitmap_alloc(&performance_data_bitmap, &idx); if (ret < 0) @@ -138,8 +138,10 @@ struct perf_data_item_comp *perf_data_getnext(void) * ,and always set bit on bitmap alloc. */ ret = perf_bitmap_setbit(&performance_data_bitmap, idx); - if (ret < 0) + if (ret < 0) { + perf_bitmap_free(&performance_data_bitmap, idx); return NULL; + } return &perf_data[idx]; } @@ -445,24 +447,30 @@ int io_perf_monitor_init(void) static struct io_perf_data_item *io_perf_monitor_get_next_slot(struct io_perf_monitor_ctx *self) { - int idx; + size_t idx; int ret; k_spinlock_key_t key = k_spin_lock(&self->lock); ret = perf_bitmap_alloc(&self->io_performance_data_bitmap, &idx); if (ret < 0) - return NULL; + goto out_unlock; /* ref. FW did not set the bits, but here we do it to not have to use * isFree() check that the bitarray does not provide yet. Instead we will use isClear * ,and always set bit on bitmap alloc. */ ret = perf_bitmap_setbit(&self->io_performance_data_bitmap, idx); - if (ret < 0) - return NULL; + if (ret < 0) { + perf_bitmap_free(&self->io_performance_data_bitmap, idx); + goto out_unlock; + } k_spin_unlock(&self->lock, key); return &self->io_perf_data[idx]; + +out_unlock: + k_spin_unlock(&self->lock, key); + return NULL; } int io_perf_monitor_release_slot(struct io_perf_data_item *item)