From 9227d6f37bf9eb163ef5c8e436c4daccd1a3a22b Mon Sep 17 00:00:00 2001 From: Tobiasz Dryjanski Date: Wed, 26 Jun 2024 16:12:54 +0200 Subject: [PATCH 1/8] I/O performance monitor: add config for io performance measurements Adds config option for enabling I/O performance. Signed-off-by: Tobiasz Dryjanski --- src/debug/telemetry/Kconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/debug/telemetry/Kconfig b/src/debug/telemetry/Kconfig index 6aca1db92916..6e6a55d18f3c 100644 --- a/src/debug/telemetry/Kconfig +++ b/src/debug/telemetry/Kconfig @@ -18,3 +18,11 @@ config SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS Performance records are stored in the limited number of slots in Memory Window 3, so only a certain number (PERFORMANCE_DATA_ENTRIES_COUNT) of components can be measured. +config SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + bool "enable I/O performance measurements" + help + Enables IO performance measurements. Each data interface will have its data throughput + measured (IPC/IDC and GPIO will measure number of messages/state changes). + Disabled by default and enabled with IPC. Measurements can be extracted also by IPC. + Interfaces measured: IPC, IDC, DMIC, I2S, SNDW, HDA, USB, GPIO, I2c, I3C, UART, SPI, CSI_2, DTF. + From 007c4d9e15275c79e547588476656b4dd40e4633 Mon Sep 17 00:00:00 2001 From: Tobiasz Dryjanski Date: Wed, 26 Jun 2024 15:47:19 +0200 Subject: [PATCH 2/8] I/O performance monitor: Add I/O performance monitor backend Implements I/O performance measurement feature. It counts number of transmitted bytes or other events that happen in various interfaces. Signed-off-by: Tobiasz Dryjanski --- src/debug/telemetry/performance_monitor.c | 231 ++++++++++++++++++ .../sof/debug/telemetry/performance_monitor.h | 154 ++++++++++++ src/init/init.c | 5 + 3 files changed, 390 insertions(+) diff --git a/src/debug/telemetry/performance_monitor.c b/src/debug/telemetry/performance_monitor.c index c99cc5eb1193..850aa32fde96 100644 --- a/src/debug/telemetry/performance_monitor.c +++ b/src/debug/telemetry/performance_monitor.c @@ -38,6 +38,13 @@ struct perf_bitmap { size_t size; }; +struct io_perf_monitor_ctx { + struct k_spinlock lock; + enum ipc4_perf_measurements_state_set state; + struct perf_bitmap io_performance_data_bitmap; + struct io_perf_data_item *io_perf_data; +}; + struct perf_bitmap performance_data_bitmap; struct perf_data_item_comp *perf_data; @@ -397,3 +404,227 @@ int performance_monitor_init(void) /* init performance monitor using Zephyr */ SYS_INIT(performance_monitor_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + +SYS_BITARRAY_DEFINE_STATIC(io_performance_data_bit_array, PERFORMANCE_DATA_ENTRIES_COUNT); + +#define IO_PERFORMANCE_ALLOC_BYTES 0x1000 +#define IO_PERFORMANCE_MAX_ENTRIES (IO_PERFORMANCE_ALLOC_BYTES / sizeof(struct io_perf_data_item)) + +static struct io_perf_monitor_ctx perf_monitor_ctx; +static struct io_perf_data_item io_perf_data_items[IO_PERFORMANCE_MAX_ENTRIES]; + +int io_perf_monitor_init(void) +{ + int ret; + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + + k_spinlock_init(&self->lock); + k_spinlock_key_t key = k_spin_lock(&self->lock); + + self->io_perf_data = (struct io_perf_data_item *)io_perf_data_items; + self->state = IPC4_PERF_MEASUREMENTS_DISABLED; + + ret = perf_bitmap_init(&self->io_performance_data_bitmap, &io_performance_data_bit_array, + IO_PERFORMANCE_MAX_ENTRIES); + + k_spin_unlock(&self->lock, key); + return ret; +} + +static struct io_perf_data_item *io_perf_monitor_get_next_slot(struct io_perf_monitor_ctx *self) +{ + int 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; + /* 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; + + k_spin_unlock(&self->lock, key); + return &self->io_perf_data[idx]; +} + +int io_perf_monitor_release_slot(struct io_perf_data_item *item) +{ + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + int idx; + int ret; + k_spinlock_key_t key; + + if (!item) { + tr_err(&ipc_tr, "perf_data_item is null"); + return -EINVAL; + } + + item->is_removed = true; + + key = k_spin_lock(&self->lock); + idx = item - self->io_perf_data; + k_spin_unlock(&self->lock, key); + + /* we assign data items ourselves so neither of those should ever fail */ + ret = perf_bitmap_clearbit(&self->io_performance_data_bitmap, idx); + assert(!ret); + if (ret < 0) + return ret; + ret = perf_bitmap_free(&self->io_performance_data_bitmap, idx); + assert(!ret); + return ret; +} + +int +io_perf_monitor_get_performance_data(struct io_global_perf_data *io_global_perf_data) +{ + if (!io_global_perf_data) + return 0; + + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + k_spinlock_key_t key = k_spin_lock(&self->lock); + + size_t slot_idx = 0; + size_t slots_count = self->io_performance_data_bitmap.occupied; + size_t entries_cont = self->io_performance_data_bitmap.size; + + for (size_t idx = 0; idx < entries_cont && slot_idx < slots_count; ++idx) { + if (perf_bitmap_is_bit_clear(&self->io_performance_data_bitmap, idx)) + continue; + io_global_perf_data->perf_items[slot_idx] = self->io_perf_data[idx]; + ++slot_idx; + } + io_global_perf_data->perf_item_count = slots_count; + + k_spin_unlock(&self->lock, key); + return 0; +} + +static int +io_perf_monitor_disable(struct io_perf_monitor_ctx *self) +{ + return 0; +} + +static int +io_perf_monitor_stop(struct io_perf_monitor_ctx *self) +{ + size_t slot_idx = 0; + size_t slots_count = self->io_performance_data_bitmap.occupied; + size_t entries_cont = self->io_performance_data_bitmap.size; + + for (size_t idx = 0; idx < entries_cont && slot_idx < slots_count; ++idx) { + if (perf_bitmap_is_bit_clear(&self->io_performance_data_bitmap, idx)) + continue; + + self->io_perf_data[idx].data = 0; + ++slot_idx; + } + + return 0; +} + +static int +io_perf_monitor_start(struct io_perf_monitor_ctx *self) +{ + return 0; +} + +static int +io_perf_monitor_pause(struct io_perf_monitor_ctx *self) +{ + return 0; +} + +int io_perf_monitor_set_state(enum ipc4_perf_measurements_state_set state) +{ + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + int ret = 0; + k_spinlock_key_t key = k_spin_lock(&self->lock); + + switch (state) { + case IPC4_PERF_MEASUREMENTS_DISABLED: + ret = io_perf_monitor_disable(self); + break; + case IPC4_PERF_MEASUREMENTS_STOPPED: + ret = io_perf_monitor_stop(self); + break; + case IPC4_PERF_MEASUREMENTS_STARTED: + ret = io_perf_monitor_start(self); + break; + case IPC4_PERF_MEASUREMENTS_PAUSED: + ret = io_perf_monitor_pause(self); + break; + default: + ret = -EINVAL; + } + + if (ret == 0) + self->state = state; + + k_spin_unlock(&self->lock, key); + return ret; +} + +inline enum ipc4_perf_measurements_state_set io_perf_monitor_get_state(void) +{ + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + + return self->state; +} + +int io_perf_monitor_init_data(struct io_perf_data_item **slot_id, + struct io_perf_data_item *init_data) +{ + if (!slot_id) + return IPC4_ERROR_INVALID_PARAM; + + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + struct io_perf_data_item *new_slot = io_perf_monitor_get_next_slot(self); + + if (!new_slot) + return IPC4_FAILURE; + + new_slot->id = init_data->id; + new_slot->instance = init_data->instance; + new_slot->direction = init_data->direction; + new_slot->state = init_data->state; + new_slot->power_mode = init_data->power_mode; + new_slot->is_removed = false; + new_slot->data = 0; + + *slot_id = new_slot; + + return 0; +} + +void io_perf_monitor_update_data(struct io_perf_data_item *slot_id, uint32_t increment) +{ + if (!slot_id) + return; + + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + + /* this does not need a lock if each perf slot has only one user */ + if (self->state == IPC4_PERF_MEASUREMENTS_STARTED) + slot_id->data += increment; +} + +inline void io_perf_monitor_update_io_state(struct io_perf_data_item *slot_id, bool const power_up) +{ + slot_id->state = power_up; +} + +inline void io_perf_monitor_update_power_mode(struct io_perf_data_item *slot_id, + bool const power_mode) +{ + slot_id->power_mode = power_mode; +} +#endif /* CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS */ diff --git a/src/include/sof/debug/telemetry/performance_monitor.h b/src/include/sof/debug/telemetry/performance_monitor.h index ef2b02799d47..337e353dafc4 100644 --- a/src/include/sof/debug/telemetry/performance_monitor.h +++ b/src/include/sof/debug/telemetry/performance_monitor.h @@ -8,6 +8,8 @@ #ifndef __SOF_PERFORMANCE_MONITOR_H__ #define __SOF_PERFORMANCE_MONITOR_H__ +#include + /* to be moved to Zephyr */ #define WIN3_MBASE DT_REG_ADDR(DT_PHANDLE(DT_NODELABEL(mem_window3), memory)) #define ADSP_PMW ((volatile uint32_t *) \ @@ -138,4 +140,156 @@ static inline void disable_performance_counters(void) {} #endif +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + +struct io_perf_data_item { + /* ID of interface */ + uint32_t id : 8; + /* Instance of interface / bus */ + uint32_t instance : 8; + /* I/O direction from ACE perspective: 0 - Input, 1 - Output */ + uint32_t direction : 1; + /* I/O state: 0 - powered down / disabled, 1 - powered up / enabled */ + uint32_t state : 1; + /* Power Mode: 0 - D0, 1 - D0ix (clock gating enabled), */ + uint32_t power_mode : 2; + uint32_t rsvd : 11; + /* The component still exists (0) or has been already deleted (1) */ + uint32_t is_removed : 1; + /* Performance data */ + /* + * I/O (id) - ID - Units - Description + * + * Host IPC - 0 - Count - Counter of Host IPC messages incoming and outcoming + * IDC - 1 - Count - Counter of IDC messages incoming and outcoming per DSP core + * DMIC - 2 - Bytes - Counter of bytes transferred over DMIC interface + * I2S - 3 - Bytes - Counter of bytes transferred over I2S interface + * SoundWire - 4 - Bytes - Counter of bytes transferred over SoundWire interface + * HD/A - 5 - Bytes - Counter of bytes transferred over HD/A interface + * USB - 6 - Bytes - Counter of bytes transferred over USB interface + * GPIO - 7 - Count - Counter of GPIO interrupts or triggers + * I2C - 8 - Bytes - Counter of bytes transferred over I2C interface + * I3C - 9 - Bytes - Counter of bytes transferred over I3C interface + * I3C interrupt - 10 - Bytes - Counter of I3C interrupts + * UART - 11 - Bytes - Counter of bytes transferred over UART interface + * SPI - 12 - Bytes - Counter of bytes transferred over SPI interface + * CSI-2 - 13 - Bytes - Counter of bytes transferred over CSI-2 interface + * DTF - 14 - Bytes - Counter of bytes transferred over DTF interface + */ + uint64_t data; +} __packed; + +/* those below are used for bits in io_perf_data_item, it just uses 32bit vars specifically */ +enum io_perf_data_item_dir { + IO_PERF_INPUT_DIRECTION = 0, + IO_PERF_OUTPUT_DIRECTION = 1, +}; + +enum io_perf_data_item_state { + IO_PERF_POWERED_DOWN_DISABLED = 0, + IO_PERF_POWERED_UP_ENABLED = 1, +}; + +enum io_perf_data_item_power_mode { + IO_PERF_D0_POWER_MODE = 0, + IO_PERF_D0IX_POWER_MODE = 1, +}; + +enum io_perf_data_item_id { + IO_PERF_IPC_ID = 0, + IO_PERF_IDC_ID = 1, + IO_PERF_DMIC_ID = 2, + IO_PERF_I2S_ID = 3, + IO_PERF_SOUND_WIRE_ID = 4, + IO_PERF_HDA_ID = 5, + IO_PERF_USB_ID = 6, + IO_PERF_GPIO_ID = 7, + IO_PERF_I2C_ID = 8, + IO_PERF_I3C_ID = 9, + IO_PERF_I3C_INTERRUPT_ID = 10, + IO_PERF_UART_ID = 11, + IO_PERF_SPI_ID = 12, + IO_PERF_CSI_2_ID = 13, + IO_PERF_DTF_ID = 14, + IO_PERF_INVALID_ID = 0xFF +}; + +struct io_global_perf_data { + /* Number of statistics */ + uint32_t perf_item_count; + /* Performance statistics per I/O */ + struct io_perf_data_item perf_items[0]; +}; + +/* I/O Performance Monitor initialization. */ +/* + * @return ErrorCode + */ +int io_perf_monitor_init(void); + +/* Release slot */ +/* + * @param [in] slot_id pointer to io_perf_data_item + * @return ErrorCode + */ +int io_perf_monitor_release_slot(struct io_perf_data_item *slot_id); + +/* Get I/O performance data */ +/* + * @param [in] io_global_perf_data point to io_global_perf_data + * @return ErrorCode + */ +int +io_perf_monitor_get_performance_data(struct io_global_perf_data *io_global_perf_data); + +/* Set control state of I/o performance measurements process */ +/* + * @param [in] state a state to set + * @return ErrorCode + */ +int +io_perf_monitor_set_state(enum ipc4_perf_measurements_state_set state); + +/* Get control state of I/o performance measurements process */ +/* + * @return ipc4_perf_measurements_state_set + */ +enum ipc4_perf_measurements_state_set io_perf_monitor_get_state(void); + +/* Initialization of I/O performance data */ +/* + * @param [out] slot_id pointer to io_perf_data_item + * @param [in] init_data pointer to init data + */ +int io_perf_monitor_init_data(struct io_perf_data_item **slot_id, + struct io_perf_data_item *init_data); + +/* Update of I/O performance data */ +/* + * @param [in] slot_id pointer to io_perf_data_item + * @param [in] increment + * @note IMPORTANT: this function assumes each perf slot has only one user + * (use this only once per slot) + */ +void io_perf_monitor_update_data(struct io_perf_data_item *slot_id, + uint32_t increment); + +/* Update of I/0 state */ +/* + * @param [in] slot_id pointer to io_perf_data_item + * @param [in] power_up 0 - powered down / disabled, 1 - powered up / enabled + */ +void io_perf_monitor_update_io_state(struct io_perf_data_item *slot_id, + bool const power_up); + +/* Update of I/0 power mode */ +/* + * @param [in] slot_id pointer to io_perf_data_item + * @param [in] power_mode 0 - D0, 1 - D0ix (clock gating enabled) + */ +void io_perf_monitor_update_power_mode(struct io_perf_data_item *slot_id, + bool const power_mode); + +#endif //SUPPORTED(IO_PERFORMANCE_MEASUREMENTS) + #endif diff --git a/src/init/init.c b/src/init/init.c index e6a68dcc7f0f..77665faee975 100644 --- a/src/init/init.c +++ b/src/init/init.c @@ -303,6 +303,11 @@ static int primary_core_init(int argc, char *argv[], struct sof *sof) trace_point(TRACE_BOOT_SYS_POWER); pm_runtime_init(sof); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* init I/O performance before any I/O interfaces */ + io_perf_monitor_init(); +#endif + /* init the platform */ if (platform_init(sof) < 0) sof_panic(SOF_IPC_PANIC_PLATFORM); From 620c69bd2a77784d9ef3a38bcae511b431fc7b73 Mon Sep 17 00:00:00 2001 From: Tobiasz Dryjanski Date: Tue, 9 Jul 2024 18:15:45 +0200 Subject: [PATCH 3/8] I/O performance monitor: Add I/O performance related IPCs Adds IO_PERF_MEASUREMENTS_STATE and IO_GLOBAL_PERF_DATA IPCs. Those can be used to change state of I/O performance monitor and extract the measured data. Signed-off-by: Tobiasz Dryjanski --- src/audio/base_fw.c | 46 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/audio/base_fw.c b/src/audio/base_fw.c index 04df6fecfe1f..6fad9f5f5674 100644 --- a/src/audio/base_fw.c +++ b/src/audio/base_fw.c @@ -460,6 +460,45 @@ static int global_perf_data_get(uint32_t *data_off_size, char *data) #endif } +static int io_global_perf_state_get(uint32_t *data_off_size, char *data) +{ +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + *data = io_perf_monitor_get_state(); + *data_off_size = sizeof(enum ipc4_perf_measurements_state_set); + + return IPC4_SUCCESS; +#else + return IPC4_UNAVAILABLE; +#endif +} + +static int io_global_perf_data_get(uint32_t *data_off_size, char *data) +{ +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + int ret; + struct global_perf_data *perf_data = (struct global_perf_data *)data; + + ret = io_perf_monitor_get_performance_data(perf_data); + if (ret < 0) + return IPC4_ERROR_INVALID_PARAM; + *data_off_size = sizeof(*perf_data) + + perf_data->perf_item_count * sizeof(*perf_data->perf_items); + + return IPC4_SUCCESS; +#else + return IPC4_UNAVAILABLE; +#endif +} + +static int io_perf_monitor_state_set(const char *data) +{ +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + return io_perf_monitor_set_state((enum ipc4_perf_measurements_state_set)*data); +#else + return IPC4_UNAVAILABLE; +#endif +} + static int basefw_get_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, @@ -503,6 +542,11 @@ static int basefw_get_large_config(struct comp_dev *dev, return extended_global_perf_data_get(data_offset, data); case IPC4_GLOBAL_PERF_DATA: return global_perf_data_get(data_offset, data); + case IPC4_IO_PERF_MEASUREMENTS_STATE: + return io_global_perf_state_get(data_offset, data); + case IPC4_IO_GLOBAL_PERF_DATA: + return io_global_perf_data_get(data_offset, data); + /* TODO: add more support */ case IPC4_DSP_RESOURCE_STATE: case IPC4_NOTIFICATION_MASK: @@ -574,6 +618,8 @@ static int basefw_set_large_config(struct comp_dev *dev, return basefw_dma_control(first_block, last_block, data_offset, data); case IPC4_PERF_MEASUREMENTS_STATE: return set_perf_meas_state(data); + case IPC4_IO_PERF_MEASUREMENTS_STATE: + return io_perf_monitor_state_set(data); case IPC4_SYSTEM_TIME: return basefw_set_system_time(param_id, first_block, last_block, data_offset, data); From 2a1689c2a5af3614452abf320dbee3803d0c4bb2 Mon Sep 17 00:00:00 2001 From: Tobiasz Dryjanski Date: Wed, 10 Jul 2024 14:13:13 +0200 Subject: [PATCH 4/8] I/O performance monitor: add I/O performance counters to IPC interface Set up a measurement of number of input and output IPCs. Signed-off-by: Tobiasz Dryjanski --- src/audio/base_fw.c | 2 +- src/include/sof/ipc/common.h | 6 ++++++ src/ipc/ipc-common.c | 26 +++++++++++++++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/audio/base_fw.c b/src/audio/base_fw.c index 6fad9f5f5674..39da6b1c7492 100644 --- a/src/audio/base_fw.c +++ b/src/audio/base_fw.c @@ -476,7 +476,7 @@ static int io_global_perf_data_get(uint32_t *data_off_size, char *data) { #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS int ret; - struct global_perf_data *perf_data = (struct global_perf_data *)data; + struct io_global_perf_data *perf_data = (struct io_global_perf_data *)data; ret = io_perf_monitor_get_performance_data(perf_data); if (ret < 0) diff --git a/src/include/sof/ipc/common.h b/src/include/sof/ipc/common.h index 09bf075ff301..43ad023da1ef 100644 --- a/src/include/sof/ipc/common.h +++ b/src/include/sof/ipc/common.h @@ -70,6 +70,12 @@ struct ipc { /* processing task */ struct task ipc_task; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* io performance measurement */ + struct io_perf_data_item *io_perf_in_msg_count; + struct io_perf_data_item *io_perf_out_msg_count; +#endif + #ifdef __ZEPHYR__ struct k_work_delayable z_delayed_work; #endif diff --git a/src/ipc/ipc-common.c b/src/ipc/ipc-common.c index 0bb57b277ae6..bf94e0481ec0 100644 --- a/src/ipc/ipc-common.c +++ b/src/ipc/ipc-common.c @@ -32,6 +32,8 @@ #include #include +#include + LOG_MODULE_REGISTER(ipc, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(ipc); @@ -154,9 +156,14 @@ void ipc_send_queued_msg(void) msg = list_first_item(&ipc->msg_list, struct ipc_msg, list); - if (ipc_platform_send_msg(msg) == 0) + if (ipc_platform_send_msg(msg) == 0) { /* Remove the message from the list if it has been successfully sent. */ list_item_del(&msg->list); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Increment performance counters */ + io_perf_monitor_update_data(ipc->io_perf_out_msg_count, 1); +#endif + } out: k_spin_unlock(&ipc->lock, key); } @@ -274,6 +281,18 @@ int ipc_init(struct sof *sof) list_init(&sof->ipc->msg_list); list_init(&sof->ipc->comp_list); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + struct io_perf_data_item init_data = {IO_PERF_IPC_ID, + cpu_get_id(), + IO_PERF_INPUT_DIRECTION, + IO_PERF_POWERED_UP_ENABLED, + IO_PERF_D0IX_POWER_MODE, + 0, 0, 0 }; + io_perf_monitor_init_data(&sof->ipc->io_perf_in_msg_count, &init_data); + init_data.direction = IO_PERF_OUTPUT_DIRECTION; + io_perf_monitor_init_data(&sof->ipc->io_perf_out_msg_count, &init_data); +#endif + #ifdef __ZEPHYR__ k_work_init_delayable(&sof->ipc->z_delayed_work, ipc_work_handler); #endif @@ -317,6 +336,11 @@ static enum task_state ipc_do_cmd(void *data) { struct ipc *ipc = data; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Increment performance counters */ + io_perf_monitor_update_data(ipc->io_perf_in_msg_count, 1); +#endif + /* * 32-bit writes are atomic and at the moment no IPC processing is * taking place, so, no need for a lock. From 300d56e070c9bbd5a3730d06ab2310ca923b4ffb Mon Sep 17 00:00:00 2001 From: Tobiasz Dryjanski Date: Wed, 10 Jul 2024 18:52:32 +0200 Subject: [PATCH 5/8] I/O performance monitor: Add I/O measurements for IDC interface Set up a counter of input and output IDCs. Signed-off-by: Tobiasz Dryjanski --- src/idc/idc.c | 19 +++++++++++++++++++ src/idc/zephyr_idc.c | 12 ++++++++++++ zephyr/include/rtos/idc.h | 7 +++++++ 3 files changed, 38 insertions(+) diff --git a/src/idc/idc.c b/src/idc/idc.c index 69c7ad1f84bd..4ace39cf3fc9 100644 --- a/src/idc/idc.c +++ b/src/idc/idc.c @@ -34,6 +34,8 @@ #include #include +#include + LOG_MODULE_REGISTER(idc, CONFIG_SOF_LOG_LEVEL); /** \brief IDC message payload per core. */ @@ -481,6 +483,11 @@ static void idc_complete(void *data) uint32_t type = iTS(idc->received_msg.header); k_spinlock_key_t key; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Increment performance counters */ + io_perf_monitor_update_data(idc->io_perf_out_msg_count, 1); +#endif + switch (type) { case iTS(IDC_MSG_IPC): /* Signal the host */ @@ -511,6 +518,18 @@ int idc_init(void) /* initialize idc data */ (*idc)->payload = platform_shared_get(static_payload, sizeof(static_payload)); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + struct io_perf_data_item init_data = {IO_PERF_IDC_ID, + cpu_get_id(), + IO_PERF_INPUT_DIRECTION, + IO_PERF_POWERED_UP_ENABLED, + IO_PERF_D0IX_POWER_MODE, + 0, 0, 0 }; + io_perf_monitor_init_data(&(*idc)->io_perf_in_msg_count, &init_data); + init_data.direction = IO_PERF_OUTPUT_DIRECTION; + io_perf_monitor_init_data(&(*idc)->io_perf_out_msg_count, &init_data); +#endif + /* process task */ #ifndef __ZEPHYR__ schedule_task_init_edf(&(*idc)->idc_task, SOF_UUID(idc_cmd_task_uuid), diff --git a/src/idc/zephyr_idc.c b/src/idc/zephyr_idc.c index 74a0da0f5bab..664d09a260ca 100644 --- a/src/idc/zephyr_idc.c +++ b/src/idc/zephyr_idc.c @@ -33,6 +33,8 @@ #include #include +#include + LOG_MODULE_REGISTER(zephyr_idc, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(zephyr_idc); @@ -88,6 +90,11 @@ static void idc_handler(struct k_p4wq_work *work) idc->received_msg.header = msg->header; idc->received_msg.extension = msg->extension; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Increment performance counters */ + io_perf_monitor_update_data(idc->io_perf_in_msg_count, 1); +#endif + switch (msg->header) { case IDC_MSG_POWER_UP: /* Run the core initialisation? */ @@ -151,6 +158,11 @@ int idc_send_msg(struct idc_msg *msg, uint32_t mode) k_p4wq_submit(q_zephyr_idc + target_cpu, work); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Increment performance counters */ + io_perf_monitor_update_data(idc->io_perf_out_msg_count, 1); +#endif + switch (mode) { case IDC_BLOCKING: ret = k_p4wq_wait(work, K_USEC(IDC_TIMEOUT)); diff --git a/zephyr/include/rtos/idc.h b/zephyr/include/rtos/idc.h index 178048eb0cbd..8fdfb09f3c99 100644 --- a/zephyr/include/rtos/idc.h +++ b/zephyr/include/rtos/idc.h @@ -20,6 +20,8 @@ #include #include +#include + /** \brief IDC send blocking flag. */ #define IDC_BLOCKING 0 @@ -171,6 +173,11 @@ struct idc { struct task idc_task; /**< IDC processing task */ struct idc_payload *payload; int irq; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* io performance measurement */ + struct io_perf_data_item *io_perf_in_msg_count; + struct io_perf_data_item *io_perf_out_msg_count; +#endif }; /* idc trace context, used by multiple units */ From ef30a1d8364e212167f33f646464f3b3db226d32 Mon Sep 17 00:00:00 2001 From: Tobiasz Dryjanski Date: Wed, 17 Jul 2024 11:46:09 +0200 Subject: [PATCH 6/8] I/O performance monitor: Add I/O measurement for DAI Adds I/O performance measurement for audio unterfaces in DAI (SNDW, DMIC, SSP, HDA). Signed-off-by: Tobiasz Dryjanski --- src/audio/dai-zephyr.c | 52 ++++++++++++++++++++++++++++++++ src/include/sof/lib/dai-zephyr.h | 6 ++++ 2 files changed, 58 insertions(+) diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index aea7a64d1031..89185c3f466a 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -41,6 +41,8 @@ #include #include +#include + /* note: if this macro is not defined * then that means the HOST and the DSP * have the same view of the address space. @@ -383,6 +385,10 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, /* update host position (in bytes offset) for drivers */ dd->total_data_processed += bytes; } +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Increment performance counters */ + io_perf_monitor_update_data(dd->io_perf_bytes_count, bytes); +#endif return dma_status; } @@ -478,6 +484,48 @@ int dai_common_new(struct dai_data *dd, struct comp_dev *dev, dd->xrun = 0; dd->chan = NULL; + /* I/O performance init, keep it last so the function does not reach this in case + * of return on error, so that we do not waste a slot + */ +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + enum io_perf_data_item_id perf_type; + enum io_perf_data_item_dir perf_dir; + + switch (dai_cfg->type) { + case SOF_DAI_INTEL_SSP: + perf_type = IO_PERF_I2S_ID; + break; + case SOF_DAI_INTEL_ALH: + perf_type = IO_PERF_SOUND_WIRE_ID; + break; + case SOF_DAI_INTEL_DMIC: + perf_type = IO_PERF_DMIC_ID; + break; + case SOF_DAI_INTEL_HDA: + perf_type = IO_PERF_HDA_ID; + break; + + default: + perf_type = IO_PERF_INVALID_ID; + comp_warn(dev, "Unsupported DAI type"); + } + if (dai_cfg->direction == SOF_IPC_STREAM_PLAYBACK) + perf_dir = IO_PERF_OUTPUT_DIRECTION; + else + perf_dir = IO_PERF_INPUT_DIRECTION; + + /* 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(), + 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); + } +#endif + return 0; } @@ -523,6 +571,10 @@ static struct comp_dev *dai_new(const struct comp_driver *drv, void dai_common_free(struct dai_data *dd) { +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + io_perf_monitor_release_slot(dd->io_perf_bytes_count); +#endif + if (dd->group) dai_group_put(dd->group); diff --git a/src/include/sof/lib/dai-zephyr.h b/src/include/sof/lib/dai-zephyr.h index 797d1754080c..5af6f5aeec24 100644 --- a/src/include/sof/lib/dai-zephyr.h +++ b/src/include/sof/lib/dai-zephyr.h @@ -35,6 +35,8 @@ #include #include +#include + /** \addtogroup sof_dai_drivers DAI Drivers * DAI Drivers API specification. * @{ @@ -157,6 +159,10 @@ struct dai_data { struct llp_slot_info slot_info; /* fast mode, use one byte memory to save repreated cycles */ bool fast_mode; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* io performance measurement */ + struct io_perf_data_item *io_perf_bytes_count; +#endif }; /* these 3 are here to satisfy clk.c and ssp.h interconnection, will be removed leter */ From aee690a05a58cee7658bd62b0b855dbb00ee41a7 Mon Sep 17 00:00:00 2001 From: Tobiasz Dryjanski Date: Wed, 26 Jun 2024 16:18:24 +0200 Subject: [PATCH 7/8] I/O performance monitor: enable I/O performance measurements Enable I/O performance for mtl and lnl Signed-off-by: Tobiasz Dryjanski --- app/boards/intel_adsp_ace15_mtpm.conf | 1 + app/boards/intel_adsp_ace20_lnl.conf | 1 + 2 files changed, 2 insertions(+) diff --git a/app/boards/intel_adsp_ace15_mtpm.conf b/app/boards/intel_adsp_ace15_mtpm.conf index 31009202196a..f4aa71fdc236 100644 --- a/app/boards/intel_adsp_ace15_mtpm.conf +++ b/app/boards/intel_adsp_ace15_mtpm.conf @@ -47,6 +47,7 @@ CONFIG_AMS=y CONFIG_COUNTER=y CONFIG_SOF_TELEMETRY=y CONFIG_SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS=y +CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS=y CONFIG_HEAP_MEM_POOL_SIZE=8192 CONFIG_L3_HEAP=y diff --git a/app/boards/intel_adsp_ace20_lnl.conf b/app/boards/intel_adsp_ace20_lnl.conf index a5d7ce9ce269..9c7f843af67c 100644 --- a/app/boards/intel_adsp_ace20_lnl.conf +++ b/app/boards/intel_adsp_ace20_lnl.conf @@ -41,6 +41,7 @@ CONFIG_AMS=y CONFIG_COUNTER=y CONFIG_SOF_TELEMETRY=y CONFIG_SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS=y +CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS=y CONFIG_HEAP_MEM_POOL_SIZE=8192 CONFIG_L3_HEAP=y From cade98adb1c0ac8782c3999a5aa9dd5933aa4ec6 Mon Sep 17 00:00:00 2001 From: Tobiasz Dryjanski Date: Mon, 16 Sep 2024 18:12:34 +0200 Subject: [PATCH 8/8] performance_monitor: fix issue with freeing bitmap Zephyr's bitarray had a problem when clearing the bit before freeing it. Not clearing it changes nothing in overall logic so we can just not do it. Also fixes and issue with computing bitmap idx. Signed-off-by: Tobiasz Dryjanski --- src/debug/telemetry/performance_monitor.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/debug/telemetry/performance_monitor.c b/src/debug/telemetry/performance_monitor.c index 850aa32fde96..dc5e80c7c253 100644 --- a/src/debug/telemetry/performance_monitor.c +++ b/src/debug/telemetry/performance_monitor.c @@ -100,6 +100,7 @@ static int perf_bitmap_setbit(struct perf_bitmap * const bitmap, size_t bit) return sys_bitarray_set_bit(bitmap->array, bit); } +__attribute__((unused)) static int perf_bitmap_clearbit(struct perf_bitmap * const bitmap, size_t bit) { return sys_bitarray_clear_bit(bitmap->array, bit); @@ -145,12 +146,8 @@ struct perf_data_item_comp *perf_data_getnext(void) int perf_data_free(struct perf_data_item_comp * const item) { /* find index of item */ - int idx = (item - perf_data) / sizeof(*item); - int ret = perf_bitmap_clearbit(&performance_data_bitmap, idx); - - if (ret < 0) - return ret; - ret = perf_bitmap_free(&performance_data_bitmap, idx); + int idx = (item - perf_data); + int ret = perf_bitmap_free(&performance_data_bitmap, idx); if (ret < 0) return ret; @@ -473,10 +470,6 @@ int io_perf_monitor_release_slot(struct io_perf_data_item *item) k_spin_unlock(&self->lock, key); /* we assign data items ourselves so neither of those should ever fail */ - ret = perf_bitmap_clearbit(&self->io_performance_data_bitmap, idx); - assert(!ret); - if (ret < 0) - return ret; ret = perf_bitmap_free(&self->io_performance_data_bitmap, idx); assert(!ret); return ret;