-
Notifications
You must be signed in to change notification settings - Fork 347
part 2 of #10287 #10302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
part 2 of #10287 #10302
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements part 2 of #10287, replacing CONFIG_USERSPACE conditional compilation with a new CONFIG_SOF_ZEPHYR_USE_SHARED_HEAP configuration option for SOF userspace modules. The changes introduce a more flexible heap management system with separate heap allocation functions and improved module memory management.
- Introduces CONFIG_SOF_ZEPHYR_USE_SHARED_HEAP configuration option to control shared heap usage
- Adds new heap allocation functions (sof_heap_alloc, sof_heap_free, sof_sys_heap_get) for better memory management
- Refactors module adapter memory allocation to support both shared heap and private heap configurations
Reviewed Changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| zephyr/lib/alloc.c | Replaces CONFIG_USERSPACE with CONFIG_SOF_ZEPHYR_USE_SHARED_HEAP and adds new heap allocation functions |
| zephyr/include/rtos/alloc.h | Adds function declarations for new heap allocation functions and updates conditional compilation |
| zephyr/edf_schedule.c | Adds heap assignment for EDF scheduler thread using new sof_sys_heap_get function |
| zephyr/Kconfig | Introduces CONFIG_SOF_ZEPHYR_USE_SHARED_HEAP configuration option with dependency on USERSPACE |
| zephyr/CMakeLists.txt | Updates compiler options condition to be more specific for xt-clang toolchain |
| src/include/sof/audio/module_adapter/module/generic.h | Adds heap fields to module_resources and updates function declarations |
| src/idc/zephyr_idc.c | Assigns SOF system heap to IDC thread for proper memory management |
| src/audio/module_adapter/module_adapter.c | Major refactoring of memory allocation with conditional compilation for different heap strategies |
| src/audio/module_adapter/module/generic.c | Updates memory allocation functions to use new heap management system |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| flags = 0; | ||
| } | ||
|
|
||
| struct processing_module *mod = mod = sof_heap_alloc(mod_heap, flags, sizeof(*mod), 0); |
Copilot
AI
Oct 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate assignment to mod variable. The second assignment mod = should be removed.
| struct processing_module *mod = mod = sof_heap_alloc(mod_heap, flags, sizeof(*mod), 0); | |
| struct processing_module *mod = sof_heap_alloc(mod_heap, flags, sizeof(*mod), 0); |
|
|
||
| /** | ||
| * Allocates aligned memory block for module. | ||
| * @param mod Pointer to the module this memory block is allocatd for. |
Copilot
AI
Oct 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected spelling of 'allocatd' to 'allocated'.
| * @param mod Pointer to the module this memory block is allocatd for. | |
| * @param mod Pointer to the module this memory block is allocated for. |
| /** | ||
| * Allocates aligned memory block for module. | ||
| * Allocates aligned memory block with flags for module. | ||
| * @param mod Pointer to the module this memory block is allocatd for. |
Copilot
AI
Oct 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected spelling of 'allocatd' to 'allocated'.
| * @param mod Pointer to the module this memory block is allocatd for. | |
| * @param mod Pointer to the module this memory block is allocated for. |
defa8c4 to
79debdf
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments, but no showstoppers. The PR title could be improved. Many of us look at lists of PRs and "part 2 of #10287" isn't very helpful. At least a brief hint at what this PR is related to, would be helpful in the summary.
| #else | ||
| #include <platform/platform.h> | ||
| #define PAGE_SZ HOST_PAGE_SIZE | ||
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This bit looks ugly in middle of module_adapter.c How about set HOST_PAGE_SIZE to CONFIG_MM_DRV_PAGE_SIZE if it is used, remove this bit and use HOST_PAGE_SIZE?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kv2019i HOST_PAGE_SIZE is host's page size, we should use DSP's page size - as long as available. And HOST_PAGE_SIZE is always defined, so I wouldn't redefine it.
| struct processing_module *mod = sof_heap_alloc(mod_heap, flags, sizeof(*mod), 0); | ||
|
|
||
| if (!mod) { | ||
| comp_cl_err(drv, "module_adapter_new(), failed to allocate memory for module"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need the function name in the log string?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lyakh I think we need to merge
- The module API update from Jyri
- The virtual regions
- Memory patches from this PR.
i.e. we should have an integration 1st.
zephyr/Kconfig
Outdated
| NOTE: Keep in mind that the heap size should not be greater than the physical | ||
| memory size of the system defined in DT (and this includes baseFW text/data). | ||
|
|
||
| config SOF_ZEPHYR_USE_SHARED_HEAP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: can we have USERSPACE in the name to make more obvious.
| /* | ||
| * To match the fall-back SOF main heap all private heaps should also be in the | ||
| * uncached address range. | ||
| */ | ||
| void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this ? What was the issue ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need struct processing_module and struct comp_dev instances for user-space modules to be accessible to those modules. And obviously we cannot use mod_alloc() yet because the instance doesn't exist yet. The sequence is the following:
- allocate heap for a new module
- allocate a module object (and a couple more things) on that heap
- start the module thread
Only after (3) we can use mod_alloc() so in (2) we need to use a function, specifying the heap explicitly
| struct list_item cont_chunk_list; /**< Memory container chunks */ | ||
| size_t heap_usage; | ||
| size_t heap_high_water_mark; | ||
| struct k_heap *heap; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will use the virtual region. Is this patch a dependency now ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean your page allocator PR? Well, it works as is now too - without it. We can merge this and migrate to the page allocator when it's merged too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two things are unclear:
Why does the option related to the shared heap also affect module_driver_heap? These are independent mechanisms and should be configured separately.
Why are we introducing another heap when we already have module_driver_heap available?
| if (cfgsz == (sizeof(*cfg) + pinsz)) { | ||
| dst->nb_input_pins = n_in; | ||
| dst->nb_output_pins = n_out; | ||
| #if CONFIG_SOF_ZEPHYR_USE_SHARED_HEAP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
module_driver_heap is independent from the shared heap, so if really needed, it should have its own #if rather than being grouped together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, can make 2 flags
zephyr/lib/alloc.c
Outdated
| #undef SHARED_BUFFER_HEAP_MEM_SIZE | ||
| #define SHARED_BUFFER_HEAP_MEM_SIZE ROUND_UP(CONFIG_SOF_ZEPHYR_SHARED_BUFFER_HEAP_SIZE, \ | ||
| HOST_PAGE_SIZE) | ||
| #if CONFIG_SOF_ZEPHYR_USE_SHARED_HEAP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you adding a new #if instead of modifying the condition above? This causes SHARED_BUFFER_HEAP_MEM_SIZE to be set, which in turn reduces the size of sof_heap by that value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean #if CONFIG_USERSPACE in line 104? Because I can keep it for this PR too, but I want to exclude the statically allocated shared heap
| size_t heap_usage; | ||
| size_t heap_high_water_mark; | ||
| struct k_heap *heap; | ||
| void *heap_mem; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The k_heap structure embeds a sys_heap, which already contains fields for the heap's base address and memory size. These can be reused instead of introducing new redundant field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it isn't redundant, it's the base address of the allocated memory area, including the heap structure, used for freeing. Currently the struct k_heap object is at the top of that memory, so these addresses are equal, but I decided to keep both of them to stay generic in case we need to put more auxiliary information there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently the struct
k_heapobject is at the top of that memory
Exactly. The pointer to heap equals heap_mem, so these fields are effectively redundant.
mod_heap = (struct k_heap *)mod_heap_mem;
zephyr/Kconfig
Outdated
| bool "Use shared heap for SOF userspace modules" | ||
| depends on USERSPACE | ||
| help | ||
| When set a shared heap will be used for all SOF userspace modules. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The description is misleading. Userspace modules do not use the shared heap directly. It's only used for buffers at the kernel-userspace boundary. The suggestion that all userspace modules will use only a single shared heap is simply false.
| return module_adapter_new_ext(drv, config, spec, NULL); | ||
| } | ||
|
|
||
| #if CONFIG_SOF_ZEPHYR_USE_SHARED_HEAP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
module_driver_heap and shared heap are independent mechanisms and should have separate configuration options. Grouping them under a single setting is misleading and doesn't reflect their distinct purposes.
|
|
||
| if (config->proc_domain == COMP_PROCESSING_DOMAIN_DP) { | ||
| /* src-lite with 8 channels has been seen allocating 14k in one go */ | ||
| const size_t heap_size = 20 * 1024; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently this value is meant to be passed via IPC and come from topology. Until that’s implemented, it would make more sense to move it to configuration rather than hardcoding it.
| const size_t heap_size = 20 * 1024; | ||
|
|
||
| /* Keep uncached to match the default SOF heap! */ | ||
| mod_heap_mem = rballoc_align(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"wow... heap on heap... I guess, why not" :)
| void *mem = mod->priv.resources.heap_mem; | ||
|
|
||
| #if CONFIG_IPC_MAJOR_4 | ||
| if (mod) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (mod) ??
| if (mod) | ||
| sof_heap_free(mod_heap, mod->priv.cfg.input_pins); | ||
| #endif | ||
| sof_heap_free(mod_heap, mod->dev); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we assume mod can be null and that's why it's checked earlier, then this line should also be inside that if to avoid a potential null pointer dereference.
| rfree(mod->priv.cfg.input_pins); | ||
| #endif | ||
|
|
||
| rfree(mod->stream_params); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The module_adapter_mem_free function will not free the stream_params.
Add a SOF_ZEPHYR_USE_SHARED_HEAP Kconfig option to explicitly enable or disable the shared heap. Signed-off-by: Guennadi Liakhovetski <[email protected]>
Add sof_heap_alloc() and sof_heap_free() to allocate and free memory on a private heap. Signed-off-by: Guennadi Liakhovetski <[email protected]>
Memory, allocated using the module_driver_heap_*() API, should be freed using module_driver_heap_free(), not rfree(). Signed-off-by: Guennadi Liakhovetski <[email protected]>
Allocate and free memory for module and device objects and for input pins to separate functions to support varying allocation methods. Signed-off-by: Guennadi Liakhovetski <[email protected]>
Add a definition of struct k_heap and SOF k_heap wrappers sof_heap_alloc() and sof_heap_free() for host native builds. Signed-off-by: Guennadi Liakhovetski <[email protected]>
We want to be able to serve all module memory allocations from a private heap. This commit creates such a heap for DP scheduled modules and moves struct comp_dev and struct processing_module to it. Signed-off-by: Guennadi Liakhovetski <[email protected]>
Allocate input and output pins on module heap for automatic freeing. Signed-off-by: Guennadi Liakhovetski <[email protected]>
Move mod_alloc() allocations, including the container pool, to the module local heap. Signed-off-by: Guennadi Liakhovetski <[email protected]>
Stream parameters are only used by respective modules, move them to the module's own heap. Signed-off-by: Guennadi Liakhovetski <[email protected]>
Add a new mod_alloc_ext() allocation function with support for allocator flags. Signed-off-by: Guennadi Liakhovetski <[email protected]>
Some Zephyr heap allocations use per-thread heap pointers. By default those allocations end up using the default Zephyr system heap, which is rather small in the SOF case. To overcome that assign the common SOF heap to EDF and IDC threads. Signed-off-by: Guennadi Liakhovetski <[email protected]>
s/allocatd/allocated/ Signed-off-by: Guennadi Liakhovetski <[email protected]>
|
@softwarecki can you re-check please? Most comments addressed, except for the hard-coded heap size, but I've added a comment to it to fix it ASAP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see the point of introducing another heap type. The only difference is that one is shared across module instances and the other is per-module. Adding #ifdefs and separate allocation functions across the codebase reduces readability.
In some places you call sof_heap_alloc even though module_driver_heap isn't used yet. It likely will be, requiring another #ifdef. This pattern will spreads across half of the sof.
It would be better to merge module_driver_heap with the new sof_heap. There's little difference between them. The module_driver_heap needs to be likely migrated from sys_heap to k_heap anyway due to missing synchronization. The allocation function is already separated. I wrote 'likely' because we can't use spinlock from userspace. Regardless of whether you plan to add a syscall or use a mutex instead, separating these heaps is artificial - they can use identical synchronization.
Once merged, during module creation, a single #ifdef decides whether to allocate a new heap and assign it to mod->priv.resources.heap, or reuse the drv->user_heap created with the driver. The rest of the code can use sof_heap_alloc without scattering #ifdefs everywhere.
You're artificially splitting two solutions that differ only in when the heap is allocated.
Minor notes:
stream_paramsstill aren't freed, even though #10323 leaves free calls.- Why not add
sof_heap_zallocorZEROflag? Instead, you introduce extra memset calls.
| sof_heap_free(mod_heap, mod); | ||
| rfree(mem); | ||
| } | ||
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After this much code, it's good to add a comment explaining what this block depends on, especially since there are more #ifdefs inside.
#endif /* CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP */
| size_t heap_usage; | ||
| size_t heap_high_water_mark; | ||
| struct k_heap *heap; | ||
| void *heap_mem; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently the struct
k_heapobject is at the top of that memory
Exactly. The pointer to heap equals heap_mem, so these fields are effectively redundant.
mod_heap = (struct k_heap *)mod_heap_mem;
Following up on #10291 this is the next part of #10287