From e5ca01cde503c229d56892ead3ab3b340b94c396 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 10 Dec 2025 16:26:29 +0000 Subject: [PATCH] module: memory: partition out memory APIs from generic.c Move all the module memory APIs into common and heap specific files with no other changes. This is to enable easier abstraction and partitioning around memory for modules and userspace going forward. Signed-off-by: Liam Girdwood --- src/audio/module_adapter/CMakeLists.txt | 6 +- src/audio/module_adapter/module/generic.c | 413 --------------- .../module_adapter/module/memory-common.c | 83 +++ src/audio/module_adapter/module/memory-heap.c | 488 ++++++++++++++++++ src/audio/module_adapter/module_adapter.c | 109 ---- .../sof/audio/module_adapter/module/generic.h | 4 + test/cmocka/src/audio/eq_fir/CMakeLists.txt | 2 + test/cmocka/src/audio/eq_iir/CMakeLists.txt | 2 + test/cmocka/src/audio/mixer/CMakeLists.txt | 2 + test/cmocka/src/audio/mux/CMakeLists.txt | 2 + test/cmocka/src/audio/volume/CMakeLists.txt | 2 + 11 files changed, 589 insertions(+), 524 deletions(-) create mode 100644 src/audio/module_adapter/module/memory-common.c create mode 100644 src/audio/module_adapter/module/memory-heap.c diff --git a/src/audio/module_adapter/CMakeLists.txt b/src/audio/module_adapter/CMakeLists.txt index 7400e27f7edd..bc27495958b5 100644 --- a/src/audio/module_adapter/CMakeLists.txt +++ b/src/audio/module_adapter/CMakeLists.txt @@ -1,9 +1,11 @@ # SPDX-License-Identifier: BSD-3-Clause if(CONFIG_IPC_MAJOR_3) - add_local_sources(sof module_adapter.c module_adapter_ipc3.c module/generic.c) + add_local_sources(sof module_adapter.c module_adapter_ipc3.c module/generic.c + module/memory-common.c module/memory-heap.c) elseif(CONFIG_IPC_MAJOR_4) - add_local_sources(sof module_adapter.c module_adapter_ipc4.c module/generic.c) + add_local_sources(sof module_adapter.c module_adapter_ipc4.c module/generic.c + module/memory-common.c module/memory-heap.c) endif() is_zephyr(zephyr) diff --git a/src/audio/module_adapter/module/generic.c b/src/audio/module_adapter/module/generic.c index 5b8eb8e43ee6..c491cb00a6a9 100644 --- a/src/audio/module_adapter/module/generic.c +++ b/src/audio/module_adapter/module/generic.c @@ -71,351 +71,6 @@ int module_load_config(struct comp_dev *dev, const void *cfg, size_t size) return ret; } -static void mod_resource_init(struct processing_module *mod) -{ - struct module_data *md = &mod->priv; - /* Init memory list */ - 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; -} - -int module_init(struct processing_module *mod) -{ - int ret; - struct comp_dev *dev = mod->dev; - const struct module_interface *const interface = dev->drv->adapter_ops; - - comp_dbg(dev, "entry"); - -#if CONFIG_IPC_MAJOR_3 - if (mod->priv.state == MODULE_INITIALIZED) - return 0; - if (mod->priv.state > MODULE_INITIALIZED) - return -EPERM; -#endif - if (!interface) { - comp_err(dev, "module interface not defined"); - return -EIO; - } - - /* check interface, there must be one and only one of processing procedure */ - if (!interface->init || - (!!interface->process + !!interface->process_audio_stream + - !!interface->process_raw_data < 1)) { - comp_err(dev, "comp is missing mandatory interfaces"); - return -EIO; - } - - mod_resource_init(mod); -#if CONFIG_MODULE_MEMORY_API_DEBUG && defined(__ZEPHYR__) - mod->priv.resources.rsrc_mngr = k_current_get(); -#endif - /* Now we can proceed with module specific initialization */ - ret = interface->init(mod); - if (ret) { - comp_err(dev, "error %d: module specific init failed", ret); - mod_free_all(mod); - return ret; - } - - comp_dbg(dev, "done"); -#if CONFIG_IPC_MAJOR_3 - mod->priv.state = MODULE_INITIALIZED; -#endif - - 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) -{ - 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) -{ - struct module_resources *res = &mod->priv.resources; - - list_item_append(&container->list, &res->free_cont_list); -} - -/** - * Allocates aligned buffer memory block for module. - * @param mod Pointer to the module this memory block is allocated for. - * @param bytes Size in bytes. - * @param alignment Alignment in bytes. - * @return Pointer to the allocated memory or NULL if failed. - * - * The allocated memory is automatically freed when the module is - * unloaded. The back-end, rballoc(), always aligns the memory to - * PLATFORM_DCACHE_ALIGN at the minimum. - */ -void *mod_balloc_align(struct processing_module *mod, size_t size, size_t alignment) -{ - struct module_resources *res = &mod->priv.resources; - struct module_resource *container; - - MEM_API_CHECK_THREAD(res); - - container = container_get(mod); - if (!container) - return NULL; - - if (!size) { - comp_err(mod->dev, "requested allocation of 0 bytes."); - container_put(mod, container); - return NULL; - } - - /* Allocate buffer memory for module */ - void *ptr = sof_heap_alloc(res->heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_LARGE_BUFFER, - size, alignment); - - if (!ptr) { - comp_err(mod->dev, "Failed to alloc %zu bytes %zu alignment for comp %#x.", - size, alignment, dev_comp_id(mod->dev)); - container_put(mod, container); - return NULL; - } - /* Store reference to allocated memory */ - 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) - res->heap_high_water_mark = res->heap_usage; - - return ptr; -} -EXPORT_SYMBOL(mod_balloc_align); - -/** - * Allocates aligned memory block with flags for module. - * @param mod Pointer to the module this memory block is allocated for. - * @param flags Allocator flags. - * @param bytes Size in bytes. - * @param alignment Alignment in bytes. - * @return Pointer to the allocated memory or NULL if failed. - * - * The allocated memory is automatically freed when the module is unloaded. - */ -void *mod_alloc_ext(struct processing_module *mod, uint32_t flags, size_t size, size_t alignment) -{ - struct module_resources *res = &mod->priv.resources; - struct module_resource *container; - - MEM_API_CHECK_THREAD(res); - - container = container_get(mod); - if (!container) - return NULL; - - if (!size) { - comp_err(mod->dev, "requested allocation of 0 bytes."); - container_put(mod, container); - return NULL; - } - - /* Allocate memory for module */ - void *ptr = sof_heap_alloc(res->heap, flags, size, alignment); - - if (!ptr) { - comp_err(mod->dev, "Failed to alloc %zu bytes %zu alignment for comp %#x.", - size, alignment, dev_comp_id(mod->dev)); - container_put(mod, container); - return NULL; - } - /* Store reference to allocated memory */ - 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) - res->heap_high_water_mark = res->heap_usage; - - return ptr; -} -EXPORT_SYMBOL(mod_alloc_ext); - -/** - * Creates a blob handler and releases it when the module is unloaded - * @param mod Pointer to module this memory block is allocated for. - * @return Pointer to the created data blob handler - * - * Like comp_data_blob_handler_new() but the handler is automatically freed. - */ -#if CONFIG_COMP_BLOB -struct comp_data_blob_handler *mod_data_blob_handler_new(struct processing_module *mod) -{ - struct module_resources *res = &mod->priv.resources; - struct comp_data_blob_handler *bhp; - struct module_resource *container; - - MEM_API_CHECK_THREAD(res); - - container = container_get(mod); - if (!container) - return NULL; - - bhp = comp_data_blob_handler_new_ext(mod->dev, false, NULL, NULL); - if (!bhp) { - container_put(mod, container); - return NULL; - } - - container->bhp = bhp; - container->size = 0; - container->type = MOD_RES_BLOB_HANDLER; - list_item_prepend(&container->list, &res->res_list); - - return bhp; -} -EXPORT_SYMBOL(mod_data_blob_handler_new); -#endif - -/** - * Make a module associated shared SRAM copy of DRAM read-only data. - * @param mod Pointer to module this copy is allocated for. - * @return Pointer to the SRAM copy. - * - * Like fast_get() but the handler is automatically freed. - */ -#if CONFIG_FAST_GET -const void *mod_fast_get(struct processing_module *mod, const void * const dram_ptr, size_t size) -{ - struct module_resources *res = &mod->priv.resources; - struct module_resource *container; - const void *ptr; - - MEM_API_CHECK_THREAD(res); - - container = container_get(mod); - if (!container) - return NULL; - - ptr = fast_get(res->heap, dram_ptr, size); - if (!ptr) { - container_put(mod, container); - return NULL; - } - - container->sram_ptr = ptr; - container->size = 0; - container->type = MOD_RES_FAST_GET; - list_item_prepend(&container->list, &res->res_list); - - return ptr; -} -EXPORT_SYMBOL(mod_fast_get); -#endif - -static int free_contents(struct processing_module *mod, struct module_resource *container) -{ - struct module_resources *res = &mod->priv.resources; - - switch (container->type) { - case MOD_RES_HEAP: - sof_heap_free(res->heap, container->ptr); - res->heap_usage -= container->size; - return 0; -#if CONFIG_COMP_BLOB - case MOD_RES_BLOB_HANDLER: - comp_data_blob_handler_free(container->bhp); - return 0; -#endif -#if CONFIG_FAST_GET - case MOD_RES_FAST_GET: - fast_put(res->heap, container->sram_ptr); - return 0; -#endif - default: - comp_err(mod->dev, "Unknown resource type: %d", container->type); - } - return -EINVAL; -} - -/** - * Frees the memory block removes it from module's book keeping. - * @param mod Pointer to module this memory block was allocated for. - * @param ptr Pointer to the memory block. - */ -int 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 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; - } - } - - comp_err(mod->dev, "error: could not find memory pointed by %p", ptr); - - return -EINVAL; -} -EXPORT_SYMBOL(mod_free); - -#if CONFIG_COMP_BLOB -void mod_data_blob_handler_free(struct processing_module *mod, struct comp_data_blob_handler *dbh) -{ - mod_free(mod, (void *)dbh); -} -EXPORT_SYMBOL(mod_data_blob_handler_free); -#endif - -#if CONFIG_FAST_GET -void mod_fast_put(struct processing_module *mod, const void *sram_ptr) -{ - mod_free(mod, sram_ptr); -} -EXPORT_SYMBOL(mod_fast_put); -#endif - int module_prepare(struct processing_module *mod, struct sof_source **sources, int num_of_sources, struct sof_sink **sinks, int num_of_sinks) @@ -580,74 +235,6 @@ int module_reset(struct processing_module *mod) return 0; } -/** - * Frees all the resources registered for this module - * @param mod Pointer to module that should have its resource freed. - * - * This function is called automatically when the module is unloaded. - */ -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 */ - list_for_item(list, &res->res_list) { - struct module_resource *container = - container_of(list, struct module_resource, list); - - free_contents(mod, container); - } - - /* - * 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); -} -EXPORT_SYMBOL(mod_free_all); - -int module_free(struct processing_module *mod) -{ - const struct module_interface *const ops = mod->dev->drv->adapter_ops; - struct module_data *md = &mod->priv; - int ret = 0; - - if (ops->free) { - ret = ops->free(mod); - if (ret) - comp_warn(mod->dev, "error: %d", ret); - } - - /* Free all memory shared by module_adapter & module */ - md->cfg.avail = false; - md->cfg.size = 0; - rfree(md->cfg.data); - md->cfg.data = NULL; - if (md->runtime_params) { - rfree(md->runtime_params); - md->runtime_params = NULL; - } -#if CONFIG_IPC_MAJOR_3 - md->state = MODULE_DISABLED; -#endif - return ret; -} - /* * \brief Set module configuration - Common method to assemble large configuration message * \param[in] mod - struct processing_module pointer diff --git a/src/audio/module_adapter/module/memory-common.c b/src/audio/module_adapter/module/memory-common.c new file mode 100644 index 000000000000..01aa7756ab49 --- /dev/null +++ b/src/audio/module_adapter/module/memory-common.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Marcin Rajwa + +/* + * \file memory-common.c + * \brief Generic Codec Memory API common functions + * \author Marcin Rajwa + * + */ + +#include +#include +#include +#include + +LOG_MODULE_DECLARE(module_adapter, CONFIG_SOF_LOG_LEVEL); + +int module_init(struct processing_module *mod) +{ + int ret; + struct comp_dev *dev = mod->dev; + const struct module_interface *const interface = dev->drv->adapter_ops; + + comp_dbg(dev, "entry"); + +#if CONFIG_IPC_MAJOR_3 + if (mod->priv.state == MODULE_INITIALIZED) + return 0; + if (mod->priv.state > MODULE_INITIALIZED) + return -EPERM; +#endif + if (!interface) { + comp_err(dev, "module interface not defined"); + return -EIO; + } + + /* check interface, there must be one and only one of processing procedure */ + if (!interface->init || + (!!interface->process + !!interface->process_audio_stream + + !!interface->process_raw_data < 1)) { + comp_err(dev, "comp is missing mandatory interfaces"); + return -EIO; + } + + mod_resource_init(mod); +#if CONFIG_MODULE_MEMORY_API_DEBUG && defined(__ZEPHYR__) + mod->priv.resources.rsrc_mngr = k_current_get(); +#endif + /* Now we can proceed with module specific initialization */ + ret = interface->init(mod); + if (ret) { + comp_err(dev, "error %d: module specific init failed", ret); + mod_free_all(mod); + return ret; + } + + comp_dbg(dev, "done"); +#if CONFIG_IPC_MAJOR_3 + mod->priv.state = MODULE_INITIALIZED; +#endif + + return 0; +} + +#if CONFIG_COMP_BLOB +void mod_data_blob_handler_free(struct processing_module *mod, struct comp_data_blob_handler *dbh) +{ + mod_free(mod, (void *)dbh); +} +EXPORT_SYMBOL(mod_data_blob_handler_free); +#endif + +#if CONFIG_FAST_GET +void mod_fast_put(struct processing_module *mod, const void *sram_ptr) +{ + mod_free(mod, sram_ptr); +} +EXPORT_SYMBOL(mod_fast_put); +#endif + diff --git a/src/audio/module_adapter/module/memory-heap.c b/src/audio/module_adapter/module/memory-heap.c new file mode 100644 index 000000000000..8eeb57442d2c --- /dev/null +++ b/src/audio/module_adapter/module/memory-heap.c @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Marcin Rajwa + +/* + * \file memory-heap.c + * \brief Generic Codec Memory API heap functions + * \author Marcin Rajwa + * + */ + +#include + +#include +#include +#include + +/* The __ZEPHYR__ condition is to keep cmocka tests working */ +#if CONFIG_MODULE_MEMORY_API_DEBUG && defined(__ZEPHYR__) +#define MEM_API_CHECK_THREAD(res) __ASSERT((res)->rsrc_mngr == k_current_get(), \ + "Module memory API operation from wrong thread") +#else +#define MEM_API_CHECK_THREAD(res) +#endif + +LOG_MODULE_DECLARE(module_adapter, CONFIG_SOF_LOG_LEVEL); + +struct container_chunk { + struct list_item chunk_list; + struct module_resource containers[CONFIG_MODULE_MEMORY_API_CONTAINER_CHUNK_SIZE]; +}; + +void mod_resource_init(struct processing_module *mod) +{ + struct module_data *md = &mod->priv; + /* Init memory list */ + 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; +} + +static struct module_resource *container_get(struct processing_module *mod) +{ + 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) +{ + struct module_resources *res = &mod->priv.resources; + + list_item_append(&container->list, &res->free_cont_list); +} + +/** + * Allocates aligned buffer memory block for module. + * @param mod Pointer to the module this memory block is allocated for. + * @param bytes Size in bytes. + * @param alignment Alignment in bytes. + * @return Pointer to the allocated memory or NULL if failed. + * + * The allocated memory is automatically freed when the module is + * unloaded. The back-end, rballoc(), always aligns the memory to + * PLATFORM_DCACHE_ALIGN at the minimum. + */ +void *mod_balloc_align(struct processing_module *mod, size_t size, size_t alignment) +{ + struct module_resources *res = &mod->priv.resources; + struct module_resource *container; + + MEM_API_CHECK_THREAD(res); + + container = container_get(mod); + if (!container) + return NULL; + + if (!size) { + comp_err(mod->dev, "requested allocation of 0 bytes."); + container_put(mod, container); + return NULL; + } + + /* Allocate buffer memory for module */ + void *ptr = sof_heap_alloc(res->heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_LARGE_BUFFER, + size, alignment); + + if (!ptr) { + comp_err(mod->dev, "Failed to alloc %zu bytes %zu alignment for comp %#x.", + size, alignment, dev_comp_id(mod->dev)); + container_put(mod, container); + return NULL; + } + /* Store reference to allocated memory */ + 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) + res->heap_high_water_mark = res->heap_usage; + + return ptr; +} +EXPORT_SYMBOL(mod_balloc_align); + +/** + * Allocates aligned memory block with flags for module. + * @param mod Pointer to the module this memory block is allocated for. + * @param flags Allocator flags. + * @param bytes Size in bytes. + * @param alignment Alignment in bytes. + * @return Pointer to the allocated memory or NULL if failed. + * + * The allocated memory is automatically freed when the module is unloaded. + */ +void *mod_alloc_ext(struct processing_module *mod, uint32_t flags, size_t size, size_t alignment) +{ + struct module_resources *res = &mod->priv.resources; + struct module_resource *container; + + MEM_API_CHECK_THREAD(res); + + container = container_get(mod); + if (!container) + return NULL; + + if (!size) { + comp_err(mod->dev, "requested allocation of 0 bytes."); + container_put(mod, container); + return NULL; + } + + /* Allocate memory for module */ + void *ptr = sof_heap_alloc(res->heap, flags, size, alignment); + + if (!ptr) { + comp_err(mod->dev, "Failed to alloc %zu bytes %zu alignment for comp %#x.", + size, alignment, dev_comp_id(mod->dev)); + container_put(mod, container); + return NULL; + } + /* Store reference to allocated memory */ + 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) + res->heap_high_water_mark = res->heap_usage; + + return ptr; +} +EXPORT_SYMBOL(mod_alloc_ext); + +/** + * Creates a blob handler and releases it when the module is unloaded + * @param mod Pointer to module this memory block is allocated for. + * @return Pointer to the created data blob handler + * + * Like comp_data_blob_handler_new() but the handler is automatically freed. + */ +#if CONFIG_COMP_BLOB +struct comp_data_blob_handler *mod_data_blob_handler_new(struct processing_module *mod) +{ + struct module_resources *res = &mod->priv.resources; + struct comp_data_blob_handler *bhp; + struct module_resource *container; + + MEM_API_CHECK_THREAD(res); + + container = container_get(mod); + if (!container) + return NULL; + + bhp = comp_data_blob_handler_new_ext(mod->dev, false, NULL, NULL); + if (!bhp) { + container_put(mod, container); + return NULL; + } + + container->bhp = bhp; + container->size = 0; + container->type = MOD_RES_BLOB_HANDLER; + list_item_prepend(&container->list, &res->res_list); + + return bhp; +} +EXPORT_SYMBOL(mod_data_blob_handler_new); +#endif + +/** + * Make a module associated shared SRAM copy of DRAM read-only data. + * @param mod Pointer to module this copy is allocated for. + * @return Pointer to the SRAM copy. + * + * Like fast_get() but the handler is automatically freed. + */ +#if CONFIG_FAST_GET +const void *mod_fast_get(struct processing_module *mod, const void * const dram_ptr, size_t size) +{ + struct module_resources *res = &mod->priv.resources; + struct module_resource *container; + const void *ptr; + + MEM_API_CHECK_THREAD(res); + + container = container_get(mod); + if (!container) + return NULL; + + ptr = fast_get(res->heap, dram_ptr, size); + if (!ptr) { + container_put(mod, container); + return NULL; + } + + container->sram_ptr = ptr; + container->size = 0; + container->type = MOD_RES_FAST_GET; + list_item_prepend(&container->list, &res->res_list); + + return ptr; +} +EXPORT_SYMBOL(mod_fast_get); +#endif + +static int free_contents(struct processing_module *mod, struct module_resource *container) +{ + struct module_resources *res = &mod->priv.resources; + + switch (container->type) { + case MOD_RES_HEAP: + sof_heap_free(res->heap, container->ptr); + res->heap_usage -= container->size; + return 0; +#if CONFIG_COMP_BLOB + case MOD_RES_BLOB_HANDLER: + comp_data_blob_handler_free(container->bhp); + return 0; +#endif +#if CONFIG_FAST_GET + case MOD_RES_FAST_GET: + fast_put(res->heap, container->sram_ptr); + return 0; +#endif + default: + comp_err(mod->dev, "Unknown resource type: %d", container->type); + } + return -EINVAL; +} + +/** + * Frees the memory block removes it from module's book keeping. + * @param mod Pointer to module this memory block was allocated for. + * @param ptr Pointer to the memory block. + */ +int 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 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; + } + } + + comp_err(mod->dev, "error: could not find memory pointed by %p", ptr); + + return -EINVAL; +} +EXPORT_SYMBOL(mod_free); + +/** + * Frees all the resources registered for this module + * @param mod Pointer to module that should have its resource freed. + * + * This function is called automatically when the module is unloaded. + */ +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 */ + list_for_item(list, &res->res_list) { + struct module_resource *container = + container_of(list, struct module_resource, list); + + free_contents(mod, container); + } + + /* + * 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); +} +EXPORT_SYMBOL(mod_free_all); + +int module_free(struct processing_module *mod) +{ + const struct module_interface *const ops = mod->dev->drv->adapter_ops; + struct module_data *md = &mod->priv; + int ret = 0; + + if (ops->free) { + ret = ops->free(mod); + if (ret) + comp_warn(mod->dev, "error: %d", ret); + } + + /* Free all memory shared by module_adapter & module */ + md->cfg.avail = false; + md->cfg.size = 0; + rfree(md->cfg.data); + md->cfg.data = NULL; + if (md->runtime_params) { + rfree(md->runtime_params); + md->runtime_params = NULL; + } +#if CONFIG_IPC_MAJOR_3 + md->state = MODULE_DISABLED; +#endif + return ret; +} + +#if CONFIG_MM_DRV +#define PAGE_SZ CONFIG_MM_DRV_PAGE_SIZE +#else +#include +#define PAGE_SZ HOST_PAGE_SIZE +#endif + +static struct k_heap *module_adapter_dp_heap_new(const struct comp_ipc_config *config) +{ + /* src-lite with 8 channels has been seen allocating 14k in one go */ + /* FIXME: the size will be derived from configuration */ + const size_t heap_size = 20 * 1024; + + /* Keep uncached to match the default SOF heap! */ + uint8_t *mod_heap_mem = rballoc_align(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, + heap_size, PAGE_SZ); + + if (!mod_heap_mem) + return NULL; + + struct k_heap *mod_heap = (struct k_heap *)mod_heap_mem; + const size_t heap_prefix_size = ALIGN_UP(sizeof(*mod_heap), 8); + void *mod_heap_buf = mod_heap_mem + heap_prefix_size; + + k_heap_init(mod_heap, mod_heap_buf, heap_size - heap_prefix_size); + +#ifdef __ZEPHYR__ + mod_heap->heap.init_mem = mod_heap_buf; + mod_heap->heap.init_bytes = heap_size - heap_prefix_size; +#endif + return mod_heap; +} + +struct processing_module *module_adapter_mem_alloc(const struct comp_driver *drv, + const struct comp_ipc_config *config) +{ + struct k_heap *mod_heap; + /* + * For DP shared modules the struct processing_module object must be + * accessible from all cores. Unfortunately at this point there's no + * information of components the module will be bound to. So we need to + * allocate shared memory for each DP module. + * To be removed when pipeline 2.0 is ready. + */ + uint32_t flags = config->proc_domain == COMP_PROCESSING_DOMAIN_DP ? + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT : SOF_MEM_FLAG_USER; + + if (config->proc_domain == COMP_PROCESSING_DOMAIN_DP && IS_ENABLED(CONFIG_USERSPACE) && + !IS_ENABLED(CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP)) { + mod_heap = module_adapter_dp_heap_new(config); + if (!mod_heap) { + comp_cl_err(drv, "Failed to allocate DP module heap"); + return NULL; + } + } else { + mod_heap = drv->user_heap; + } + + struct processing_module *mod = sof_heap_alloc(mod_heap, flags, sizeof(*mod), 0); + + if (!mod) { + comp_cl_err(drv, "failed to allocate memory for module"); + goto emod; + } + + memset(mod, 0, sizeof(*mod)); + mod->priv.resources.heap = mod_heap; + + /* + * Would be difficult to optimize the allocation to use cache. Only if + * the whole currently active topology is running on the primary core, + * then it can be cached. Effectively it can be only cached in + * single-core configurations. + */ + struct comp_dev *dev = sof_heap_alloc(mod_heap, SOF_MEM_FLAG_COHERENT, sizeof(*dev), 0); + + if (!dev) { + comp_cl_err(drv, "failed to allocate memory for comp_dev"); + goto err; + } + + memset(dev, 0, sizeof(*dev)); + comp_init(drv, dev, sizeof(*dev)); + dev->ipc_config = *config; + mod->dev = dev; + dev->mod = mod; + + return mod; + +err: + sof_heap_free(mod_heap, mod); +emod: + if (mod_heap != drv->user_heap) + rfree(mod_heap); + + return NULL; +} + +void module_adapter_mem_free(struct processing_module *mod) +{ + struct k_heap *mod_heap = mod->priv.resources.heap; + +#if CONFIG_IPC_MAJOR_4 + sof_heap_free(mod_heap, mod->priv.cfg.input_pins); +#endif + sof_heap_free(mod_heap, mod->dev); + sof_heap_free(mod_heap, mod); +} + diff --git a/src/audio/module_adapter/module_adapter.c b/src/audio/module_adapter/module_adapter.c index 7edc018dabb4..5dc37f6ec2e7 100644 --- a/src/audio/module_adapter/module_adapter.c +++ b/src/audio/module_adapter/module_adapter.c @@ -45,115 +45,6 @@ struct comp_dev *module_adapter_new(const struct comp_driver *drv, return module_adapter_new_ext(drv, config, spec, NULL, NULL); } -#if CONFIG_MM_DRV -#define PAGE_SZ CONFIG_MM_DRV_PAGE_SIZE -#else -#include -#define PAGE_SZ HOST_PAGE_SIZE -#endif - -static struct k_heap *module_adapter_dp_heap_new(const struct comp_ipc_config *config) -{ - /* src-lite with 8 channels has been seen allocating 14k in one go */ - /* FIXME: the size will be derived from configuration */ - const size_t heap_size = 20 * 1024; - - /* Keep uncached to match the default SOF heap! */ - uint8_t *mod_heap_mem = rballoc_align(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, - heap_size, PAGE_SZ); - - if (!mod_heap_mem) - return NULL; - - struct k_heap *mod_heap = (struct k_heap *)mod_heap_mem; - const size_t heap_prefix_size = ALIGN_UP(sizeof(*mod_heap), 8); - void *mod_heap_buf = mod_heap_mem + heap_prefix_size; - - k_heap_init(mod_heap, mod_heap_buf, heap_size - heap_prefix_size); -#ifdef __ZEPHYR__ - mod_heap->heap.init_mem = mod_heap_buf; - mod_heap->heap.init_bytes = heap_size - heap_prefix_size; -#endif - - return mod_heap; -} - -static struct processing_module *module_adapter_mem_alloc(const struct comp_driver *drv, - const struct comp_ipc_config *config) -{ - struct k_heap *mod_heap; - /* - * For DP shared modules the struct processing_module object must be - * accessible from all cores. Unfortunately at this point there's no - * information of components the module will be bound to. So we need to - * allocate shared memory for each DP module. - * To be removed when pipeline 2.0 is ready. - */ - uint32_t flags = config->proc_domain == COMP_PROCESSING_DOMAIN_DP ? - SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT : SOF_MEM_FLAG_USER; - - if (config->proc_domain == COMP_PROCESSING_DOMAIN_DP && IS_ENABLED(CONFIG_USERSPACE) && - !IS_ENABLED(CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP)) { - mod_heap = module_adapter_dp_heap_new(config); - if (!mod_heap) { - comp_cl_err(drv, "Failed to allocate DP module heap"); - return NULL; - } - } else { - mod_heap = drv->user_heap; - } - - struct processing_module *mod = sof_heap_alloc(mod_heap, flags, sizeof(*mod), 0); - - if (!mod) { - comp_cl_err(drv, "failed to allocate memory for module"); - goto emod; - } - - memset(mod, 0, sizeof(*mod)); - mod->priv.resources.heap = mod_heap; - - /* - * Would be difficult to optimize the allocation to use cache. Only if - * the whole currently active topology is running on the primary core, - * then it can be cached. Effectively it can be only cached in - * single-core configurations. - */ - struct comp_dev *dev = sof_heap_alloc(mod_heap, SOF_MEM_FLAG_COHERENT, sizeof(*dev), 0); - - if (!dev) { - comp_cl_err(drv, "failed to allocate memory for comp_dev"); - goto err; - } - - memset(dev, 0, sizeof(*dev)); - comp_init(drv, dev, sizeof(*dev)); - dev->ipc_config = *config; - mod->dev = dev; - dev->mod = mod; - - return mod; - -err: - sof_heap_free(mod_heap, mod); -emod: - if (mod_heap != drv->user_heap) - rfree(mod_heap); - - return NULL; -} - -static void module_adapter_mem_free(struct processing_module *mod) -{ - struct k_heap *mod_heap = mod->priv.resources.heap; - -#if CONFIG_IPC_MAJOR_4 - sof_heap_free(mod_heap, mod->priv.cfg.input_pins); -#endif - sof_heap_free(mod_heap, mod->dev); - sof_heap_free(mod_heap, mod); -} - /* * \brief Create a module adapter component. * \param[in] drv - component driver pointer. diff --git a/src/include/sof/audio/module_adapter/module/generic.h b/src/include/sof/audio/module_adapter/module/generic.h index f339ac158ab0..fb4df938e346 100644 --- a/src/include/sof/audio/module_adapter/module/generic.h +++ b/src/include/sof/audio/module_adapter/module/generic.h @@ -227,6 +227,9 @@ static inline void *mod_zalloc(struct processing_module *mod, size_t size) } int mod_free(struct processing_module *mod, const void *ptr); +void module_adapter_mem_free(struct processing_module *mod); +struct processing_module *module_adapter_mem_alloc(const struct comp_driver *drv, + const struct comp_ipc_config *config); #if CONFIG_COMP_BLOB struct comp_data_blob_handler *mod_data_blob_handler_new(struct processing_module *mod); void mod_data_blob_handler_free(struct processing_module *mod, struct comp_data_blob_handler *dbh); @@ -235,6 +238,7 @@ void mod_data_blob_handler_free(struct processing_module *mod, struct comp_data_ const void *mod_fast_get(struct processing_module *mod, const void * const dram_ptr, size_t size); void mod_fast_put(struct processing_module *mod, const void *sram_ptr); #endif +void mod_resource_init(struct processing_module *mod); void mod_free_all(struct processing_module *mod); int module_prepare(struct processing_module *mod, struct sof_source **sources, int num_of_sources, diff --git a/test/cmocka/src/audio/eq_fir/CMakeLists.txt b/test/cmocka/src/audio/eq_fir/CMakeLists.txt index 305a6846966c..90eeff6cb80e 100644 --- a/test/cmocka/src/audio/eq_fir/CMakeLists.txt +++ b/test/cmocka/src/audio/eq_fir/CMakeLists.txt @@ -24,6 +24,8 @@ add_library(audio_for_eq_fir STATIC ${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 + ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module/memory-common.c + ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module/memory-heap.c ${PROJECT_SOURCE_DIR}/src/audio/buffers/comp_buffer.c ${PROJECT_SOURCE_DIR}/src/audio/buffers/audio_buffer.c ${PROJECT_SOURCE_DIR}/src/audio/source_api_helper.c diff --git a/test/cmocka/src/audio/eq_iir/CMakeLists.txt b/test/cmocka/src/audio/eq_iir/CMakeLists.txt index aa704a1af92b..a0af9662694a 100644 --- a/test/cmocka/src/audio/eq_iir/CMakeLists.txt +++ b/test/cmocka/src/audio/eq_iir/CMakeLists.txt @@ -27,6 +27,8 @@ add_library(audio_for_eq_iir STATIC ${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 + ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module/memory-common.c + ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module/memory-heap.c ${PROJECT_SOURCE_DIR}/src/audio/buffers/comp_buffer.c ${PROJECT_SOURCE_DIR}/src/audio/buffers/audio_buffer.c ${PROJECT_SOURCE_DIR}/src/audio/source_api_helper.c diff --git a/test/cmocka/src/audio/mixer/CMakeLists.txt b/test/cmocka/src/audio/mixer/CMakeLists.txt index ea8cad0bd79e..ef80cf741a27 100644 --- a/test/cmocka/src/audio/mixer/CMakeLists.txt +++ b/test/cmocka/src/audio/mixer/CMakeLists.txt @@ -13,6 +13,8 @@ cmocka_test(mixer ${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 + ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module/memory-common.c + ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module/memory-heap.c ${PROJECT_SOURCE_DIR}/src/audio/buffers/comp_buffer.c ${PROJECT_SOURCE_DIR}/src/audio/buffers/audio_buffer.c ${PROJECT_SOURCE_DIR}/src/audio/source_api_helper.c diff --git a/test/cmocka/src/audio/mux/CMakeLists.txt b/test/cmocka/src/audio/mux/CMakeLists.txt index 67b10f77270d..827a2aef740c 100644 --- a/test/cmocka/src/audio/mux/CMakeLists.txt +++ b/test/cmocka/src/audio/mux/CMakeLists.txt @@ -29,6 +29,8 @@ add_library( ${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 + ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module/memory-common.c + ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module/memory-heap.c ) sof_append_relative_path_definitions(audio_mux) diff --git a/test/cmocka/src/audio/volume/CMakeLists.txt b/test/cmocka/src/audio/volume/CMakeLists.txt index d89927578222..78cead46f5cf 100644 --- a/test/cmocka/src/audio/volume/CMakeLists.txt +++ b/test/cmocka/src/audio/volume/CMakeLists.txt @@ -23,6 +23,8 @@ add_library(audio_for_volume STATIC ${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 + ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module/memory-common.c + ${PROJECT_SOURCE_DIR}/src/audio/module_adapter/module/memory-heap.c ${PROJECT_SOURCE_DIR}/src/audio/buffers/comp_buffer.c ${PROJECT_SOURCE_DIR}/src/audio/buffers/audio_buffer.c ${PROJECT_SOURCE_DIR}/src/audio/source_api_helper.c