From 92a2be0f359dbc8a36bfa9f2b603c1f639fe564a Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 9 Jan 2026 12:56:48 +0100 Subject: [PATCH 01/10] dp: application: restore double mapping for userspace Since Zephyr has removed double mapping per Kconfig switch we need to restore it in SOF. Next we should try to optimize mappings to only use the ones we really need. Signed-off-by: Guennadi Liakhovetski --- src/schedule/zephyr_dp_schedule.h | 2 ++ src/schedule/zephyr_dp_schedule_application.c | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/schedule/zephyr_dp_schedule.h b/src/schedule/zephyr_dp_schedule.h index 8c0557ced7fc..2df6f2a83f62 100644 --- a/src/schedule/zephyr_dp_schedule.h +++ b/src/schedule/zephyr_dp_schedule.h @@ -25,7 +25,9 @@ struct scheduler_dp_data { enum sof_dp_part_type { SOF_DP_PART_HEAP, + SOF_DP_PART_HEAP_CACHE, SOF_DP_PART_CFG, + SOF_DP_PART_CFG_CACHE, SOF_DP_PART_TYPE_COUNT, }; diff --git a/src/schedule/zephyr_dp_schedule_application.c b/src/schedule/zephyr_dp_schedule_application.c index 521c32b4af1d..ea42498ebf5d 100644 --- a/src/schedule/zephyr_dp_schedule_application.c +++ b/src/schedule/zephyr_dp_schedule_application.c @@ -391,7 +391,9 @@ static void scheduler_dp_domain_free(struct task_dp_pdata *pdata) llext_manager_rm_domain(pmod->dev->ipc_config.id, mdom); k_mem_domain_remove_partition(mdom, pdata->mpart + SOF_DP_PART_HEAP); + k_mem_domain_remove_partition(mdom, pdata->mpart + SOF_DP_PART_HEAP_CACHE); k_mem_domain_remove_partition(mdom, pdata->mpart + SOF_DP_PART_CFG); + k_mem_domain_remove_partition(mdom, pdata->mpart + SOF_DP_PART_CFG_CACHE); pmod->mdom = NULL; objpool_free(&dp_mdom_head, mdom); @@ -534,12 +536,22 @@ int scheduler_dp_task_init(struct task **task, const struct sof_uuid_entry *uid, .size = size, .attr = K_MEM_PARTITION_P_RW_U_RW, }; + pdata->mpart[SOF_DP_PART_HEAP_CACHE] = (struct k_mem_partition){ + .start = (uintptr_t)sys_cache_cached_ptr_get((void *)start), + .size = size, + .attr = K_MEM_PARTITION_P_RW_U_RW | XTENSA_MMU_CACHED_WB, + }; /* Host mailbox partition for additional IPC parameters: read-only */ pdata->mpart[SOF_DP_PART_CFG] = (struct k_mem_partition){ - .start = (uintptr_t)MAILBOX_HOSTBOX_BASE, + .start = (uintptr_t)sys_cache_uncached_ptr_get((void *)MAILBOX_HOSTBOX_BASE), .size = 4096, .attr = K_MEM_PARTITION_P_RO_U_RO, }; + pdata->mpart[SOF_DP_PART_CFG_CACHE] = (struct k_mem_partition){ + .start = (uintptr_t)MAILBOX_HOSTBOX_BASE, + .size = 4096, + .attr = K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB, + }; for (pidx = 0; pidx < SOF_DP_PART_TYPE_COUNT; pidx++) { ret = k_mem_domain_add_partition(mdom, pdata->mpart + pidx); From d52aeb1add58772c7c74bbf20ae42c6e4b3ff845 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 13 Jan 2026 15:09:40 +0100 Subject: [PATCH 02/10] dp: don't schedule paused tasks DP tasks don't need to be rescheduled when pause is released. Default handling works correctly in that case too. Signed-off-by: Guennadi Liakhovetski --- src/include/sof/audio/component_ext.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/sof/audio/component_ext.h b/src/include/sof/audio/component_ext.h index f0a1ad7e8512..d2bbf87a7764 100644 --- a/src/include/sof/audio/component_ext.h +++ b/src/include/sof/audio/component_ext.h @@ -158,9 +158,9 @@ static inline int comp_trigger_local(struct comp_dev *dev, int cmd) /* schedule or cancel task */ switch (cmd) { case COMP_TRIGGER_START: - case COMP_TRIGGER_RELEASE: ret = schedule_task(dev->task, 0, dev->period); break; + case COMP_TRIGGER_RELEASE: case COMP_TRIGGER_XRUN: case COMP_TRIGGER_PAUSE: case COMP_TRIGGER_STOP: From 23b0a00fa1e5e913028d799fa8c91e961520c9a9 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 22 Jan 2026 10:29:10 +0100 Subject: [PATCH 03/10] objpool: fix flag initialisation We compare flags on repeated allocations from an existing pool, but initialisation got forgotten in the process. Restore it. Fixes: d6e6ac57a626 ("Add a object pool allocator") Signed-off-by: Guennadi Liakhovetski --- src/lib/objpool.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/objpool.c b/src/lib/objpool.c index 76a5b20a6594..e99825f0ccfa 100644 --- a/src/lib/objpool.c +++ b/src/lib/objpool.c @@ -94,6 +94,7 @@ void *objpool_alloc(struct objpool_head *head, size_t size, uint32_t flags) unsigned int new_n; if (list_is_empty(&head->list)) { + head->flags = flags; new_n = 2; } else { /* Check the last one */ From 85323f94f4912a2923d3ee58d08f07b6545524d6 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 22 Jan 2026 10:34:23 +0100 Subject: [PATCH 04/10] dp: user: fix objpool allocation error handling When an objpool allocation fails an error should be returned. Fix the missing error code assignment. Fixes: fc73f9dde25d ("sp: application: switch memory domains to object pools") Signed-off-by: Guennadi Liakhovetski --- src/schedule/zephyr_dp_schedule_application.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/schedule/zephyr_dp_schedule_application.c b/src/schedule/zephyr_dp_schedule_application.c index ea42498ebf5d..3c063559a9a6 100644 --- a/src/schedule/zephyr_dp_schedule_application.c +++ b/src/schedule/zephyr_dp_schedule_application.c @@ -518,8 +518,11 @@ int scheduler_dp_task_init(struct task **task, const struct sof_uuid_entry *uid, struct k_mem_domain *mdom = objpool_alloc(&dp_mdom_head, sizeof(*mdom), SOF_MEM_FLAG_COHERENT); - if (!mdom) + if (!mdom) { + tr_err(&dp_tr, "objpool allocation failed"); + ret = -ENOMEM; goto e_thread; + } mod->mdom = mdom; From c8576548d321afaee2676e2b364b67fd2d63e9ba Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 6 Feb 2026 12:04:08 +0100 Subject: [PATCH 05/10] audio: module-adapter: fix SRC DP regression A recent module-adapter feature addition broke SRC DP support. Fix it by adding a test for incomplete initialization data. Signed-off-by: Guennadi Liakhovetski --- src/audio/module_adapter/module_adapter_ipc4.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/audio/module_adapter/module_adapter_ipc4.c b/src/audio/module_adapter/module_adapter_ipc4.c index a6fab69bbd86..37184a5a5587 100644 --- a/src/audio/module_adapter/module_adapter_ipc4.c +++ b/src/audio/module_adapter/module_adapter_ipc4.c @@ -159,7 +159,8 @@ int module_adapter_init_data(struct comp_dev *dev, } } - if (!config->ipc_extended_init) { + if (!config->ipc_extended_init || + (!dst->ext_data->dp_data && !dst->ext_data->module_data)) { dst->init_data = cfg; /* legacy API */ dst->avail = true; } From 99fc1c264a767862813d4b0fa1de09dc26be1421 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 6 Feb 2026 17:17:10 +0100 Subject: [PATCH 06/10] Revert "audio: module-adapter: use objpool for allocation containers" This reverts commit 8acc9ca504b3536f2fbc572296067c82452710c5. It seems to break certain capture flows, revert it until fixed. Signed-off-by: Guennadi Liakhovetski --- posix/include/rtos/alloc.h | 5 +- src/audio/module_adapter/module/generic.c | 117 ++++++++++++------ .../sof/audio/module_adapter/module/generic.h | 7 +- src/platform/library/lib/alloc.c | 5 - test/cmocka/src/audio/eq_fir/CMakeLists.txt | 1 - test/cmocka/src/audio/eq_iir/CMakeLists.txt | 1 - test/cmocka/src/audio/mixer/CMakeLists.txt | 1 - test/cmocka/src/audio/mux/CMakeLists.txt | 1 - test/cmocka/src/audio/volume/CMakeLists.txt | 1 - test/cmocka/src/common_mocks.c | 5 - zephyr/lib/alloc.c | 2 + 11 files changed, 86 insertions(+), 60 deletions(-) diff --git a/posix/include/rtos/alloc.h b/posix/include/rtos/alloc.h index b8efac0d3112..424a6d520cfa 100644 --- a/posix/include/rtos/alloc.h +++ b/posix/include/rtos/alloc.h @@ -140,7 +140,10 @@ static inline void k_heap_init(struct k_heap *heap, void *mem, size_t bytes) void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, size_t alignment); void sof_heap_free(struct k_heap *heap, void *addr); -struct k_heap *sof_sys_heap_get(void); +static inline struct k_heap *sof_sys_heap_get(void) +{ + return NULL; +} /** * Calculates length of the null-terminated string. diff --git a/src/audio/module_adapter/module/generic.c b/src/audio/module_adapter/module/generic.c index f9bf5d52bef3..d2b86bb40d08 100644 --- a/src/audio/module_adapter/module/generic.c +++ b/src/audio/module_adapter/module/generic.c @@ -12,8 +12,6 @@ */ #include -#include -#include #include #include #include @@ -83,10 +81,10 @@ int module_load_config(struct comp_dev *dev, const void *cfg, size_t size) void mod_resource_init(struct processing_module *mod) { struct module_data *md = &mod->priv; - /* Init memory list */ - list_init(&md->resources.objpool.list); - md->resources.objpool.heap = md->resources.heap; + list_init(&md->resources.res_list); + list_init(&md->resources.free_cont_list); + list_init(&md->resources.cont_chunk_list); md->resources.heap_usage = 0; md->resources.heap_high_water_mark = 0; } @@ -143,14 +141,43 @@ int module_init(struct processing_module *mod) return 0; } +struct container_chunk { + struct list_item chunk_list; + struct module_resource containers[CONFIG_MODULE_MEMORY_API_CONTAINER_CHUNK_SIZE]; +}; + static struct module_resource *container_get(struct processing_module *mod) { - return objpool_alloc(&mod->priv.resources.objpool, sizeof(struct module_resource), 0); + struct module_resources *res = &mod->priv.resources; + struct k_heap *mod_heap = res->heap; + struct module_resource *container; + + if (list_is_empty(&res->free_cont_list)) { + struct container_chunk *chunk = sof_heap_alloc(mod_heap, 0, sizeof(*chunk), 0); + int i; + + if (!chunk) { + comp_err(mod->dev, "allocating more containers failed"); + return NULL; + } + + memset(chunk, 0, sizeof(*chunk)); + + list_item_append(&chunk->chunk_list, &res->cont_chunk_list); + for (i = 0; i < ARRAY_SIZE(chunk->containers); i++) + list_item_append(&chunk->containers[i].list, &res->free_cont_list); + } + + container = list_first_item(&res->free_cont_list, struct module_resource, list); + list_item_del(&container->list); + return container; } static void container_put(struct processing_module *mod, struct module_resource *container) { - objpool_free(&mod->priv.resources.objpool, container); + struct module_resources *res = &mod->priv.resources; + + list_item_append(&container->list, &res->free_cont_list); } #if CONFIG_USERSPACE @@ -208,6 +235,7 @@ void *mod_balloc_align(struct processing_module *mod, size_t size, size_t alignm container->ptr = ptr; container->size = size; container->type = MOD_RES_HEAP; + list_item_prepend(&container->list, &res->res_list); res->heap_usage += size; if (res->heap_usage > res->heap_high_water_mark) @@ -258,6 +286,7 @@ void *z_impl_mod_alloc_ext(struct processing_module *mod, uint32_t flags, size_t container->ptr = ptr; container->size = size; container->type = MOD_RES_HEAP; + list_item_prepend(&container->list, &res->res_list); res->heap_usage += size; if (res->heap_usage > res->heap_high_water_mark) @@ -277,7 +306,7 @@ EXPORT_SYMBOL(z_impl_mod_alloc_ext); #if CONFIG_COMP_BLOB struct comp_data_blob_handler *mod_data_blob_handler_new(struct processing_module *mod) { - struct module_resources * __maybe_unused res = &mod->priv.resources; + struct module_resources *res = &mod->priv.resources; struct comp_data_blob_handler *bhp; struct module_resource *container; @@ -296,6 +325,7 @@ struct comp_data_blob_handler *mod_data_blob_handler_new(struct processing_modul container->bhp = bhp; container->size = 0; container->type = MOD_RES_BLOB_HANDLER; + list_item_prepend(&container->list, &res->res_list); return bhp; } @@ -332,6 +362,7 @@ const void *z_impl_mod_fast_get(struct processing_module *mod, const void * cons container->sram_ptr = ptr; container->size = 0; container->type = MOD_RES_FAST_GET; + list_item_prepend(&container->list, &res->res_list); return ptr; } @@ -363,29 +394,6 @@ static int free_contents(struct processing_module *mod, struct module_resource * return -EINVAL; } -struct mod_res_cb_arg { - struct processing_module *mod; - const void *ptr; -}; - -static bool mod_res_free(void *data, void *arg) -{ - struct mod_res_cb_arg *cb_arg = arg; - struct module_resource *container = data; - - if (cb_arg->ptr && container->ptr != cb_arg->ptr) - return false; - - int ret = free_contents(cb_arg->mod, container); - - if (ret < 0) - comp_err(cb_arg->mod->dev, "Cannot free allocation %p", cb_arg->ptr); - - container_put(cb_arg->mod, container); - - return true; -} - /** * Frees the memory block removes it from module's book keeping. * @param mod Pointer to module this memory block was allocated for. @@ -394,19 +402,28 @@ static bool mod_res_free(void *data, void *arg) int z_impl_mod_free(struct processing_module *mod, const void *ptr) { struct module_resources *res = &mod->priv.resources; + struct module_resource *container; + struct list_item *res_list; MEM_API_CHECK_THREAD(res); if (!ptr) return 0; - /* Find which container holds this memory */ - struct mod_res_cb_arg cb_arg = {mod, ptr}; - int ret = objpool_iterate(&res->objpool, mod_res_free, &cb_arg); + /* Find which container keeps this memory */ + list_for_item(res_list, &res->res_list) { + container = container_of(res_list, struct module_resource, list); + if (container->ptr == ptr) { + int ret = free_contents(mod, container); + + list_item_del(&container->list); + container_put(mod, container); + return ret; + } + } - if (ret < 0) - comp_err(mod->dev, "error: could not find memory pointed by %p", ptr); + comp_err(mod->dev, "error: could not find memory pointed by %p", ptr); - return ret; + return -EINVAL; } EXPORT_SYMBOL(z_impl_mod_free); @@ -667,14 +684,32 @@ int module_reset(struct processing_module *mod) void mod_free_all(struct processing_module *mod) { struct module_resources *res = &mod->priv.resources; + struct k_heap *mod_heap = res->heap; + struct list_item *list; + struct list_item *_list; MEM_API_CHECK_THREAD(res); - /* Free all contents found in used containers */ - struct mod_res_cb_arg cb_arg = {mod, NULL}; + list_for_item(list, &res->res_list) { + struct module_resource *container = + container_of(list, struct module_resource, list); + + free_contents(mod, container); + } - objpool_iterate(&res->objpool, mod_res_free, &cb_arg); - objpool_prune(&res->objpool); + /* + * We do not need to remove the containers from res_list in + * the loop above or go through free_cont_list as all the + * containers are anyway freed in the loop below, and the list + * heads are reinitialized when mod_resource_init() is called. + */ + list_for_item_safe(list, _list, &res->cont_chunk_list) { + struct container_chunk *chunk = + container_of(list, struct container_chunk, chunk_list); + + list_item_del(&chunk->chunk_list); + sof_heap_free(mod_heap, chunk); + } /* Make sure resource lists and accounting are reset */ mod_resource_init(mod); diff --git a/src/include/sof/audio/module_adapter/module/generic.h b/src/include/sof/audio/module_adapter/module/generic.h index 3334c198b7f3..f23bb602a28e 100644 --- a/src/include/sof/audio/module_adapter/module/generic.h +++ b/src/include/sof/audio/module_adapter/module/generic.h @@ -13,9 +13,8 @@ #ifndef __SOF_AUDIO_MODULE_GENERIC__ #define __SOF_AUDIO_MODULE_GENERIC__ -#include -#include #include +#include #include #include #include "module_interface.h" @@ -129,7 +128,9 @@ struct module_param { * when the module unloads. */ struct module_resources { - struct objpool_head objpool; + struct list_item res_list; /**< Allocad resource containers */ + struct list_item free_cont_list; /**< Unused memory containers */ + struct list_item cont_chunk_list; /**< Memory container chunks */ size_t heap_usage; size_t heap_high_water_mark; struct k_heap *heap; diff --git a/src/platform/library/lib/alloc.c b/src/platform/library/lib/alloc.c index 74cb926e4aff..d733b58f8541 100644 --- a/src/platform/library/lib/alloc.c +++ b/src/platform/library/lib/alloc.c @@ -70,8 +70,3 @@ void heap_trace_all(int force) { heap_trace(NULL, 0); } - -struct k_heap *sof_sys_heap_get(void) -{ - return NULL; -} diff --git a/test/cmocka/src/audio/eq_fir/CMakeLists.txt b/test/cmocka/src/audio/eq_fir/CMakeLists.txt index 226128b220d2..305a6846966c 100644 --- a/test/cmocka/src/audio/eq_fir/CMakeLists.txt +++ b/test/cmocka/src/audio/eq_fir/CMakeLists.txt @@ -37,7 +37,6 @@ add_library(audio_for_eq_fir STATIC ${PROJECT_SOURCE_DIR}/src/ipc/ipc3/helper.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-common.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-helper.c - ${PROJECT_SOURCE_DIR}/src/lib/objpool.c ${PROJECT_SOURCE_DIR}/test/cmocka/src/notifier_mocks.c ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-graph.c ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-params.c diff --git a/test/cmocka/src/audio/eq_iir/CMakeLists.txt b/test/cmocka/src/audio/eq_iir/CMakeLists.txt index b5ff8770eec2..aa704a1af92b 100644 --- a/test/cmocka/src/audio/eq_iir/CMakeLists.txt +++ b/test/cmocka/src/audio/eq_iir/CMakeLists.txt @@ -40,7 +40,6 @@ add_library(audio_for_eq_iir STATIC ${PROJECT_SOURCE_DIR}/src/ipc/ipc3/helper.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-common.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-helper.c - ${PROJECT_SOURCE_DIR}/src/lib/objpool.c ${PROJECT_SOURCE_DIR}/test/cmocka/src/notifier_mocks.c ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-graph.c ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-params.c diff --git a/test/cmocka/src/audio/mixer/CMakeLists.txt b/test/cmocka/src/audio/mixer/CMakeLists.txt index c0dbb8a0a4fc..ea8cad0bd79e 100644 --- a/test/cmocka/src/audio/mixer/CMakeLists.txt +++ b/test/cmocka/src/audio/mixer/CMakeLists.txt @@ -10,7 +10,6 @@ cmocka_test(mixer ${PROJECT_SOURCE_DIR}/src/ipc/ipc3/helper.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-common.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-helper.c - ${PROJECT_SOURCE_DIR}/src/lib/objpool.c ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module_adapter.c ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module_adapter_ipc3.c ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module/generic.c diff --git a/test/cmocka/src/audio/mux/CMakeLists.txt b/test/cmocka/src/audio/mux/CMakeLists.txt index a4a72613fd6b..67b10f77270d 100644 --- a/test/cmocka/src/audio/mux/CMakeLists.txt +++ b/test/cmocka/src/audio/mux/CMakeLists.txt @@ -25,7 +25,6 @@ add_library( ${PROJECT_SOURCE_DIR}/src/math/numbers.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc3/helper.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-helper.c - ${PROJECT_SOURCE_DIR}/src/lib/objpool.c ${PROJECT_SOURCE_DIR}/test/cmocka/src/notifier_mocks.c ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module_adapter.c ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module_adapter_ipc3.c diff --git a/test/cmocka/src/audio/volume/CMakeLists.txt b/test/cmocka/src/audio/volume/CMakeLists.txt index 0385441e5878..d89927578222 100644 --- a/test/cmocka/src/audio/volume/CMakeLists.txt +++ b/test/cmocka/src/audio/volume/CMakeLists.txt @@ -34,7 +34,6 @@ add_library(audio_for_volume STATIC ${PROJECT_SOURCE_DIR}/src/ipc/ipc3/helper.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-common.c ${PROJECT_SOURCE_DIR}/src/ipc/ipc-helper.c - ${PROJECT_SOURCE_DIR}/src/lib/objpool.c ${PROJECT_SOURCE_DIR}/test/cmocka/src/notifier_mocks.c ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-graph.c ${PROJECT_SOURCE_DIR}/src/audio/pipeline/pipeline-params.c diff --git a/test/cmocka/src/common_mocks.c b/test/cmocka/src/common_mocks.c index 60b6215c4cd5..ef996b8f3500 100644 --- a/test/cmocka/src/common_mocks.c +++ b/test/cmocka/src/common_mocks.c @@ -124,11 +124,6 @@ int WEAK mod_free(struct processing_module *mod, const void *ptr) return 0; } -struct k_heap * WEAK sof_sys_heap_get(void) -{ - return NULL; -} - void WEAK *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, size_t alignment) { diff --git a/zephyr/lib/alloc.c b/zephyr/lib/alloc.c index f95f66fbdb7f..8a2fed79ee41 100644 --- a/zephyr/lib/alloc.c +++ b/zephyr/lib/alloc.c @@ -9,10 +9,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include From 1b01c02f9f6cdbc89b27defd561a25d0e25cdb77 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 23 Jan 2026 11:07:43 +0100 Subject: [PATCH 07/10] audio: fast-get: fast_get() and fast_put() should not be syscalls Userspace modules should only call mod_fast_get() and mod_fast_put(), which already can cross into the kernel space on their own, so fast_get() and fast_put() themselves don't need to be syscalls. Remove their syscall implementations. Signed-off-by: Guennadi Liakhovetski --- src/include/sof/lib/fast-get.h | 14 ++------------ zephyr/lib/fast-get.c | 33 ++++----------------------------- 2 files changed, 6 insertions(+), 41 deletions(-) diff --git a/src/include/sof/lib/fast-get.h b/src/include/sof/lib/fast-get.h index 36232cfbb3d6..fa34ecea128d 100644 --- a/src/include/sof/lib/fast-get.h +++ b/src/include/sof/lib/fast-get.h @@ -12,17 +12,7 @@ struct k_heap; -#if defined(__ZEPHYR__) && defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION) -#include - -__syscall const void *fast_get(struct k_heap *heap, const void * const dram_ptr, size_t size); -__syscall void fast_put(struct k_heap *heap, const void *sram_ptr); -#include -#else -const void *z_impl_fast_get(struct k_heap *heap, const void * const dram_ptr, size_t size); -void z_impl_fast_put(struct k_heap *heap, const void *sram_ptr); -#define fast_get z_impl_fast_get -#define fast_put z_impl_fast_put -#endif /* __ZEPHYR__ */ +const void *fast_get(struct k_heap *heap, const void * const dram_ptr, size_t size); +void fast_put(struct k_heap *heap, const void *sram_ptr); #endif /* __SOF_LIB_FAST_GET_H__ */ diff --git a/zephyr/lib/fast-get.c b/zephyr/lib/fast-get.c index 4f08d111e8b9..a5ec260822b5 100644 --- a/zephyr/lib/fast-get.c +++ b/zephyr/lib/fast-get.c @@ -81,7 +81,7 @@ static struct sof_fast_get_entry *fast_get_find_entry(struct sof_fast_get_data * return NULL; } -const void *z_impl_fast_get(struct k_heap *heap, const void *dram_ptr, size_t size) +const void *fast_get(struct k_heap *heap, const void *dram_ptr, size_t size) { struct sof_fast_get_data *data = &fast_get_data; struct sof_fast_get_entry *entry; @@ -133,7 +133,7 @@ const void *z_impl_fast_get(struct k_heap *heap, const void *dram_ptr, size_t si return ret; } -EXPORT_SYMBOL(z_impl_fast_get); +EXPORT_SYMBOL(fast_get); static struct sof_fast_get_entry *fast_put_find_entry(struct sof_fast_get_data *data, const void *sram_ptr) @@ -148,7 +148,7 @@ static struct sof_fast_get_entry *fast_put_find_entry(struct sof_fast_get_data * return NULL; } -void z_impl_fast_put(struct k_heap *heap, const void *sram_ptr) +void fast_put(struct k_heap *heap, const void *sram_ptr) { struct sof_fast_get_data *data = &fast_get_data; struct sof_fast_get_entry *entry; @@ -170,29 +170,4 @@ void z_impl_fast_put(struct k_heap *heap, const void *sram_ptr) entry ? entry->size : 0, entry ? entry->refcount : 0); k_spin_unlock(&data->lock, key); } -EXPORT_SYMBOL(z_impl_fast_put); - -#ifdef CONFIG_USERSPACE -#include -void z_vrfy_fast_put(struct k_heap *heap, const void *sram_ptr) -{ - K_OOPS(K_SYSCALL_MEMORY_WRITE(heap, sizeof(*heap))); - /* - * FIXME: we don't know how much SRAM has been allocated, so cannot - * check. Should fast_put() be changed to pass a size argument? - */ - - z_impl_fast_put(heap, sram_ptr); -} -#include - -const void *z_vrfy_fast_get(struct k_heap *heap, const void *dram_ptr, size_t size) -{ - K_OOPS(K_SYSCALL_MEMORY_WRITE(heap, sizeof(*heap))); - /* We cannot (easily) verify the actual heapp memory */ - K_OOPS(K_SYSCALL_MEMORY_READ(dram_ptr, size)); - - return z_impl_fast_get(heap, dram_ptr, size); -} -#include -#endif +EXPORT_SYMBOL(fast_put); From 46ac04500ee384b826e11d15109a46dad34e963c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 23 Jan 2026 11:58:02 +0100 Subject: [PATCH 08/10] fast_get: replace tr_dbg() and tr_err() with LOG_*() fast_get.c doesn't have a "trace context" - it doesn't have a DECLARE_TR_CTX() call. Hencs all calls to tr_err() and friends are wrong. Replace them with respective LOG_*() analogs. Signed-off-by: Guennadi Liakhovetski --- zephyr/lib/fast-get.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/zephyr/lib/fast-get.c b/zephyr/lib/fast-get.c index a5ec260822b5..9a31265fed5c 100644 --- a/zephyr/lib/fast-get.c +++ b/zephyr/lib/fast-get.c @@ -16,6 +16,15 @@ #include #include +#ifdef __ZEPHYR__ +#include +#else +#define LOG_DBG(...) do {} while (0) +#define LOG_INF(...) do {} while (0) +#define LOG_WRN(...) do {} while (0) +#define LOG_ERR(...) do {} while (0) +#endif + struct sof_fast_get_entry { const void *dram_ptr; void *sram_ptr; @@ -101,7 +110,7 @@ const void *fast_get(struct k_heap *heap, const void *dram_ptr, size_t size) if (entry->sram_ptr) { if (entry->size != size || entry->dram_ptr != dram_ptr) { - tr_err(fast_get, "size %u != %u or ptr %p != %p mismatch", + LOG_ERR("size %u != %u or ptr %p != %p mismatch", entry->size, size, entry->dram_ptr, dram_ptr); ret = NULL; goto out; @@ -128,8 +137,7 @@ const void *fast_get(struct k_heap *heap, const void *dram_ptr, size_t size) entry->refcount = 1; out: k_spin_unlock(&data->lock, key); - tr_dbg(fast_get, "get %p, %p, size %u, refcnt %u", dram_ptr, ret, size, - entry ? entry->refcount : 0); + LOG_DBG("get %p, %p, size %u, refcnt %u", dram_ptr, ret, size, entry ? entry->refcount : 0); return ret; } @@ -157,7 +165,7 @@ void fast_put(struct k_heap *heap, const void *sram_ptr) key = k_spin_lock(&fast_get_data.lock); entry = fast_put_find_entry(data, sram_ptr); if (!entry) { - tr_err(fast_get, "Put called to unknown address %p", sram_ptr); + LOG_ERR("Put called to unknown address %p", sram_ptr); goto out; } entry->refcount--; @@ -166,8 +174,8 @@ void fast_put(struct k_heap *heap, const void *sram_ptr) memset(entry, 0, sizeof(*entry)); } out: - tr_dbg(fast_get, "put %p, DRAM %p size %u refcnt %u", sram_ptr, entry ? entry->dram_ptr : 0, - entry ? entry->size : 0, entry ? entry->refcount : 0); + LOG_DBG("put %p, DRAM %p size %u refcnt %u", sram_ptr, entry ? entry->dram_ptr : 0, + entry ? entry->size : 0, entry ? entry->refcount : 0); k_spin_unlock(&data->lock, key); } EXPORT_SYMBOL(fast_put); From fe86c2b5ffc88abaffbea272b1bbe1e82c988ee8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 27 Jan 2026 14:24:39 +0100 Subject: [PATCH 09/10] fast-get: enable sharing between userspace threads If a module fast-gets memory for a specific its instance, running in userspace and then another usuerspace instance wants to share it, access has to be granted to that object by that thread too. This patch does that while also restricting object sharing to "large" objects (currently larger than half a page), and smaller objects are just copied. Signed-off-by: Guennadi Liakhovetski --- src/audio/module_adapter/module_adapter.c | 2 +- zephyr/lib/fast-get.c | 135 ++++++++++++++++++++-- 2 files changed, 129 insertions(+), 8 deletions(-) diff --git a/src/audio/module_adapter/module_adapter.c b/src/audio/module_adapter/module_adapter.c index e42b05daf8c6..0fb976566fda 100644 --- a/src/audio/module_adapter/module_adapter.c +++ b/src/audio/module_adapter/module_adapter.c @@ -53,7 +53,7 @@ struct comp_dev *module_adapter_new(const struct comp_driver *drv, #if CONFIG_MM_DRV #define PAGE_SZ CONFIG_MM_DRV_PAGE_SIZE #else -#include +#include #define PAGE_SZ HOST_PAGE_SIZE #endif diff --git a/zephyr/lib/fast-get.c b/zephyr/lib/fast-get.c index 9a31265fed5c..a7d23f999120 100644 --- a/zephyr/lib/fast-get.c +++ b/zephyr/lib/fast-get.c @@ -28,6 +28,9 @@ struct sof_fast_get_entry { const void *dram_ptr; void *sram_ptr; +#if CONFIG_USERSPACE + struct k_thread *thread; +#endif size_t size; unsigned int refcount; }; @@ -90,16 +93,71 @@ static struct sof_fast_get_entry *fast_get_find_entry(struct sof_fast_get_data * return NULL; } +#if CONFIG_MM_DRV +#define PAGE_SZ CONFIG_MM_DRV_PAGE_SIZE +#define FAST_GET_MAX_COPY_SIZE (PAGE_SZ / 2) +#else +#include +#define PAGE_SZ HOST_PAGE_SIZE +#define FAST_GET_MAX_COPY_SIZE 0 +#endif + +#if CONFIG_USERSPACE +static bool fast_get_domain_exists(struct k_thread *thread, void *start, size_t size) +{ + struct k_mem_domain *domain = thread->mem_domain_info.mem_domain; + + for (unsigned int i = 0; i < domain->num_partitions; i++) { + struct k_mem_partition *dpart = &domain->partitions[i]; + + if (dpart->start == (uintptr_t)start && dpart->size == size) + return true; + } + + return false; +} + +static int fast_get_access_grant(k_tid_t thread, void *addr, size_t size) +{ + struct k_mem_partition part = { + .start = (uintptr_t)addr, + .size = ALIGN_UP(size, CONFIG_MM_DRV_PAGE_SIZE), + .attr = K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB, + }; + + LOG_DBG("add %#zx @ %p", part.size, addr); + return k_mem_domain_add_partition(thread->mem_domain_info.mem_domain, &part); +} +#endif /* CONFIG_USERSPACE */ + const void *fast_get(struct k_heap *heap, const void *dram_ptr, size_t size) { struct sof_fast_get_data *data = &fast_get_data; + uint32_t alloc_flags = SOF_MEM_FLAG_USER; struct sof_fast_get_entry *entry; + size_t alloc_size, alloc_align; + const void *alloc_ptr; k_spinlock_key_t key; void *ret; key = k_spin_lock(&data->lock); + if (IS_ENABLED(CONFIG_USERSPACE) && size > FAST_GET_MAX_COPY_SIZE) { + alloc_size = ALIGN_UP(size, PAGE_SZ); + alloc_align = PAGE_SZ; + alloc_flags |= SOF_MEM_FLAG_LARGE_BUFFER; + } else { + alloc_size = size; + alloc_align = PLATFORM_DCACHE_ALIGN; + } + + if (size > FAST_GET_MAX_COPY_SIZE || !IS_ENABLED(CONFIG_USERSPACE)) + alloc_ptr = dram_ptr; + else + /* When userspace is enabled only share large buffers */ + alloc_ptr = NULL; + do { - entry = fast_get_find_entry(data, dram_ptr); + entry = fast_get_find_entry(data, alloc_ptr); if (!entry) { if (fast_get_realloc(data)) { ret = NULL; @@ -108,6 +166,12 @@ const void *fast_get(struct k_heap *heap, const void *dram_ptr, size_t size) } } while (!entry); +#if CONFIG_USERSPACE + LOG_DBG("userspace %u part %#zx bytes alloc %p entry %p DRAM %p", + k_current_get()->mem_domain_info.mem_domain->num_partitions, size, + alloc_ptr, entry->sram_ptr, dram_ptr); +#endif + if (entry->sram_ptr) { if (entry->size != size || entry->dram_ptr != dram_ptr) { LOG_ERR("size %u != %u or ptr %p != %p mismatch", @@ -117,22 +181,65 @@ const void *fast_get(struct k_heap *heap, const void *dram_ptr, size_t size) } ret = entry->sram_ptr; + +#if CONFIG_USERSPACE + /* We only get there for large buffers */ + if (k_current_get()->mem_domain_info.mem_domain->num_partitions > 1) { + /* A userspace thread makes the request */ + if (k_current_get() != entry->thread && + !fast_get_domain_exists(k_current_get(), ret, + ALIGN_UP(size, CONFIG_MM_DRV_PAGE_SIZE))) { + LOG_DBG("grant access to thread %p first was %p", k_current_get(), + entry->thread); + int err = fast_get_access_grant(k_current_get(), ret, size); + + if (err < 0) { + ret = NULL; + goto out; + } + /* + * The data is constant, so it's safe to use cached access to + * it, but initially we have to invalidate caches + */ + dcache_invalidate_region((__sparse_force void __sparse_cache *)ret, + size); + } else { + LOG_WRN("Repeated access request by thread"); + } + } +#endif + entry->refcount++; - /* - * The data is constant, so it's safe to use cached access to - * it, but initially we have to invalidate cached - */ - dcache_invalidate_region((__sparse_force void __sparse_cache *)ret, size); goto out; } - ret = sof_heap_alloc(heap, SOF_MEM_FLAG_USER, size, PLATFORM_DCACHE_ALIGN); + /* + * If a userspace threads is the first user to fast-get the buffer, an + * SRAM copy will be allocated on its own heap, so it will have access + * to it + */ + ret = sof_heap_alloc(heap, alloc_flags, alloc_size, alloc_align); if (!ret) goto out; entry->size = size; entry->sram_ptr = ret; memcpy_s(entry->sram_ptr, entry->size, dram_ptr, size); dcache_writeback_region((__sparse_force void __sparse_cache *)entry->sram_ptr, size); + +#if CONFIG_USERSPACE + entry->thread = k_current_get(); + if (size > FAST_GET_MAX_COPY_SIZE) { + /* Otherwise we've allocated on thread's heap, so it already has access */ + int err = fast_get_access_grant(entry->thread, ret, size); + + if (err < 0) { + sof_heap_free(NULL, ret); + ret = NULL; + goto out; + } + } +#endif /* CONFIG_USERSPACE */ + entry->dram_ptr = dram_ptr; entry->refcount = 1; out: @@ -169,6 +276,20 @@ void fast_put(struct k_heap *heap, const void *sram_ptr) goto out; } entry->refcount--; + +#if CONFIG_USERSPACE + if (entry->size > FAST_GET_MAX_COPY_SIZE && entry->thread) { + struct k_mem_partition part = { + .start = (uintptr_t)entry->sram_ptr, + .size = ALIGN_UP(entry->size, CONFIG_MM_DRV_PAGE_SIZE), + .attr = K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB, + }; + + LOG_DBG("remove %#zx @ %p", part.size, entry->sram_ptr); + k_mem_domain_remove_partition(entry->thread->mem_domain_info.mem_domain, &part); + } +#endif + if (!entry->refcount) { sof_heap_free(heap, entry->sram_ptr); memset(entry, 0, sizeof(*entry)); From d94651743bdbbcd4ec5e1be3d36cff44458f75a9 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 6 Jan 2026 16:27:24 +0100 Subject: [PATCH 10/10] dp: nocodec: switch SRC to DP by default Switch both SRC instances in the nocodec topology on PTL to DP mode by default. Signed-off-by: Guennadi Liakhovetski --- tools/topology/topology2/development/tplg-targets.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/topology/topology2/development/tplg-targets.cmake b/tools/topology/topology2/development/tplg-targets.cmake index 7986eb2bad11..b382dc1b4bc1 100644 --- a/tools/topology/topology2/development/tplg-targets.cmake +++ b/tools/topology/topology2/development/tplg-targets.cmake @@ -60,9 +60,9 @@ NHLT_BIN=nhlt-sof-lnl-nocodec-fpga-4ch.bin,PASSTHROUGH=true,DMIC_IO_CLK=19200000 # HDA topology with passthrough analog codec pipelines using CHAIN_DMA "sof-hda-generic\;sof-hda-passthrough-chain-dma\;HDA_CONFIG=passthrough,CODEC_HDA_CHAIN_DMA=true" -# SSP topology for PTL +# SSP topology for PTL, includes Data Processing SRC "cavs-nocodec\;sof-ptl-nocodec\;PLATFORM=ptl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ -PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-nocodec.bin" +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-nocodec.bin,SRC_DOMAIN=DP" # SSP topology for PTL with 96 kHz DMIC "cavs-nocodec\;sof-ptl-nocodec-dmic-4ch-96k\;PLATFORM=ptl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ @@ -310,9 +310,9 @@ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-mtl-nocodec.bin,SRC_DOMAIN=DP" # SSP test topology for Data Processing SRC on LNL "cavs-nocodec\;sof-lnl-nocodec-dp-test\;PLATFORM=lnl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-lnl-nocodec.bin,SRC_DOMAIN=DP" -# SSP test topology for Data Processing SRC on PTL +# SSP test topology for PTL with no DP "cavs-nocodec\;sof-ptl-nocodec-dp-test\;PLATFORM=ptl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\ -PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-nocodec.bin,SRC_DOMAIN=DP" +PREPROCESS_PLUGINS=nhlt,NHLT_BIN=nhlt-sof-ptl-nocodec.bin" # SSP test topology for Data Processing on core 1 SRC for MTL "cavs-nocodec\;sof-mtl-nocodec-dp-core-test\;PLATFORM=mtl,NUM_DMICS=4,PDM1_MIC_A_ENABLE=1,PDM1_MIC_B_ENABLE=1,\