-
Notifications
You must be signed in to change notification settings - Fork 349
wip: alloc: virtual regions and vpage allocator. #10281
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?
Conversation
|
@jsarha fyi - this will be needed by your updates. |
zephyr/lib/pacovr.c
Outdated
| return; | ||
|
|
||
| /* simple free, just increment free count, this is for tuning only */ | ||
| p->batch_free_count++; |
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.
should this be decremented during allocator's alloc() call?
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.
no, intention here is we should be able to see if this is > 0 then there is room for heap optimization.
zephyr/lib/vpages.c
Outdated
|
|
||
| /* map the virtual blocks in virtual region to free physical blocks */ | ||
| ret = sys_mm_drv_map_region_safe(page_context.virtual_region, vaddr, | ||
| 0, pages * CONFIG_MM_DRV_PAGE_SIZE, SYS_MM_MEM_PERM_RW); |
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 is allocating physical pages from anywhere in L2 memory?
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.
Free physical pages from L2 SRAM
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.
Free physical pages from L2 SRAM
@lgirdwood right, but shouldn't this be allocating from a pre-allocated set of pages for this "heap" or am I misunderstanding the concept?
zephyr/lib/pacovr.c
Outdated
| /* allocate memory */ | ||
| ptr = p->batch_ptr; | ||
| p->batch_ptr += size; | ||
| p->batch_used += 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.
are "batch" and "scratch" allocator names commonly used? I found a couple of references to scratch allocators, e.g. https://stackoverflow.com/questions/74956010/what-are-the-differences-between-block-stack-and-scratch-allocators#75044083 but there it's defined as "once allocated, never freed," similar to this your batch allocator?
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.
and I've also found scratch memory as "temporary", but lets rename based on use. i.e. static and dynamic
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 should be pretty straight forward to integrate this with my IPC code and the latest version of mod_alloc() PR. Could not find anything wrong in the page allocator code either.
| switch (container->type) { | ||
| case MOD_RES_HEAP: | ||
| #if CONFIG_SOF_PACOVR | ||
| /* do we need to use the scratch heap or the batch 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.
I do not think this would work. Its not guaranteed that memory allocated in initialization phase is freed in also in initialization phase. For the moment there are modules freeing memory at initialized phase, that was allocated during init pahse.
It should not be too hard to write a single pacovr_free() that knows from the pointer's memory range if it was allocated from scratch or batch (=> dynamic or static) heap.
04bc5c1 to
987b31e
Compare
987b31e to
2146f88
Compare
|
@jsarha updated, can you try this with your topology patch for pipeline memory. Thanks ! |
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 introduces a new PACOVR (Pre Allocated COntiguous Virtual memory Region) memory allocator system for pipeline resource management. The system provides virtual page allocation with both dynamic heap and static linear allocators built on top.
- Adds virtual page allocator for contiguous memory regions
- Implements PACOVR allocator with dynamic heap and static allocation capabilities
- Integrates PACOVR into module allocation functions and pipeline lifecycle
Reviewed Changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| zephyr/lib/vpages.c | Virtual page allocator implementation with mapping/unmapping functionality |
| zephyr/lib/pacovr.c | PACOVR allocator with dynamic heap and static linear allocation |
| zephyr/include/sof/lib/vpage.h | API header for virtual page allocator |
| zephyr/include/sof/lib/pacovr.h | API header for PACOVR allocator |
| zephyr/Kconfig | Configuration options for PACOVR and virtual page elements |
| zephyr/CMakeLists.txt | Build system integration for new virtual memory files |
| src/include/sof/audio/pipeline.h | Pipeline structure extension to include PACOVR instance |
| src/audio/pipeline/pipeline-graph.c | Pipeline creation/destruction integration with PACOVR |
| src/audio/module_adapter/module/generic.c | Module allocation functions updated to use PACOVR |
| scripts/xtensa-build-zephyr.py | Additional symlink creation for firmware deployment |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
zephyr/lib/vpages.c
Outdated
| void vpage_free(void *ptr) | ||
| { | ||
| k_mutex_lock(&page_context.lock, K_FOREVER); | ||
| vpages_free_and_unmap((uintptr_t *)ptr); |
Copilot
AI
Oct 8, 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.
The variable 'ret' is not in scope here. The return value from vpages_free_and_unmap() should be captured and checked.
| vpages_free_and_unmap((uintptr_t *)ptr); | |
| int ret = vpages_free_and_unmap((uintptr_t *)ptr); |
| * @brief Free virtual pages | ||
| * Frees previously allocated virtual memory pages and unmaps them. | ||
| * | ||
| * @param ptr |
Copilot
AI
Oct 8, 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.
Missing documentation for the ptr parameter in the function comment block.
| * @param ptr | |
| * @param ptr Pointer to the start of the virtual memory pages to be freed. | |
| * Must be page-aligned and previously allocated by the virtual page allocator. |
zephyr/include/sof/lib/pacovr.h
Outdated
| * @param[in] batch_size Size of the PACOVR batch region. | ||
| * @param[in] scratch_size Size of the scratch heap. |
Copilot
AI
Oct 8, 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.
The parameter names in the documentation (batch_size, scratch_size) don't match the actual function parameter names (static_size, dynamic_size) in the implementation.
zephyr/lib/pacovr.c
Outdated
| size_t total_size; | ||
|
|
||
| if (!static_size || !dynamic_size) { | ||
| LOG_ERR("error: invalid pacovr static size %d or dynamic size %d", |
Copilot
AI
Oct 8, 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.
Using %d format specifier for size_t variables. Should use %zu for size_t to avoid potential format string issues.
| LOG_ERR("error: invalid pacovr static size %d or dynamic size %d", | |
| LOG_ERR("error: invalid pacovr static size %zu or dynamic size %zu", |
zephyr/lib/pacovr.c
Outdated
|
|
||
| /* check we have enough static space left */ | ||
| if (p->static_used + size > p->static_size) { | ||
| LOG_ERR("error: pacovr static alloc failed for %d bytes, only %d bytes free", |
Copilot
AI
Oct 8, 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.
Using %d format specifier for size_t variables. Should use %zu for size_t to avoid potential format string issues.
| LOG_ERR("error: pacovr static alloc failed for %d bytes, only %d bytes free", | |
| LOG_ERR("error: pacovr static alloc failed for %zu bytes, only %zu bytes free", |
zephyr/include/sof/lib/vpage.h
Outdated
| * | ||
| * @return Pointer to the allocated virtual memory region, or NULL on failure. | ||
| */ | ||
| void *vpage_alloc(uint32_t pages); |
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.
unsigned int?
| * @param ptr Pointer to store the address of allocated pages. | ||
| * @retval 0 if successful. | ||
| */ | ||
| static int vpages_alloc_and_map(uint32_t pages, void **ptr) |
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.
unsigned int
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 function returns an error code as a negative 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.
The function returns an error code as a negative value.
I meant for pages
| err, pages, page_context.total_pages, page_context.free_pages); | ||
| } | ||
| LOG_INF("vpage_alloc ptr %p pages %d free %d/%d", ptr, pages, page_context.free_pages, | ||
| page_context.total_pages); |
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.
else for this?
|
|
||
| /* allocate memory for bitmap bundles */ | ||
| bundles = rzalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, | ||
| bitmap_num_bundles * sizeof(uint32_t)); |
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 know the size from line 277, so bundles could be static?
2146f88 to
9a9c238
Compare
9a9c238 to
dd0601e
Compare
softwarecki
left a comment
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.
Alright, here’s my round of nitpicking:
- Looks like the ghost of memory zone is back, now wearing a fancy new name: region type. Quick question - will cacheability secretly depend on this type? Or is that just a plot twist waiting to happen?
- Consider moving page mapping logic into vregion. For lifetime allocators, mapping pages progressively as needed could help reduce memory usage.
- What is the purpose of the
textregion type? Whether vregion it's for a pipeline or a module, module executable code can be shared across multiple pipelines, so this seems wrong place. - Should memory management code be integrated into Zephyr? My understanding was that this was the intended direction for VMH.
- Function naming feels inconsistent:
*_vpagesvsvregion_*. Worth aligning these.
I submitted a change request because this design looks like it's meant to replace the virtual heap, but it's missing a critical feature for deep buffering: predefined buffer sizes. The buffer size provided via ipc is only a hint. The firmware tries to allocate the largest possible buffer, and if memory is tight, it falls back to a smaller one. If the requested size exceeds available memory, allocation should still create the largest feasible buffer. Without predefined buffers it will lead to saturate memory and block further allocations. With predefined buffer sizes, we can pick the largest available option while leaving others for future allocations.
| * @param ptr Pointer to store the address of allocated pages. | ||
| * @retval 0 if successful. | ||
| */ | ||
| static int vpages_alloc_and_map(uint32_t pages, void **ptr) |
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 function returns an error code as a negative value.
| /* store the size and virtual page number in first free alloc element, | ||
| * we have already checked for a free element before the mapping. | ||
| */ | ||
| for (int i = 0; i < VPAGE_MAX_ALLOCS; i++) { |
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.
Use page_context.num_elems as array index.
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've renamed the variable name, as this the number of elems in use rather than total number of elems to search.
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.
maybe rather use lists to avoid scanning the array each time. Can be a follow-up optimisation
zephyr/lib/vpages.c
Outdated
| * Allocates virtual memory pages from the virtual page allocator. | ||
| * | ||
| * @param pages Number of 4kB pages to allocate. | ||
| * @retval ptr to allocated pages if successful. |
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.
There are not the ptr param.
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.
fixed.
zephyr/lib/vpages.c
Outdated
| int ret; | ||
|
|
||
| /* check for valid ptr which must be page aligned */ | ||
| if (!ptr || ((uintptr_t)ptr % CONFIG_MM_DRV_PAGE_SIZE) != 0) { |
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.
IS_ALIGNED(ptr, CONFIG_MM_DRV_PAGE_SIZE)
- optional
|| !page_context.num_elems.
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.
fixed
| LOG_DBG("found allocation element %d pages %d vpage %d for ptr %p", | ||
| i, page_context.velems[i].pages, | ||
| page_context.velems[i].vpage, ptr); | ||
|
|
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.
To eliminate the loop during page allocation, I suggest replace the removed element with the last item in the array. When allocating, the new element is always added at the end of the array.
page_context.num_elems--;
page_context.velems[i] = page_context.velems[page_context.num_elems];
page_context.velems[page_context.num_elems].pages = 0;
page_context.velems[page_context.num_elems].vpage = 0;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.
See the comment above about renaming the variable name to make meaning clearer..
zephyr/lib/vregion.c
Outdated
| /* calculate new heap object size for object and alignments */ | ||
| heap_obj_size = aligned_ptr - heap->ptr + size; | ||
|
|
||
| /* check we have enough shared static space left */ |
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.
shared?
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.
fixed
zephyr/lib/vregion.c
Outdated
| * @param[in] vr Pointer to the virtual region instance. | ||
| * @param[in] ptr Pointer to the memory to free. | ||
| */ | ||
| void lifetime_free(struct vregion *vr, struct vlinear_heap *heap, void *ptr) |
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.
Missing static? lifetime_alloc is static.
Unused vr param.
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.
fixed
zephyr/lib/vregion.c
Outdated
| return interim_alloc(vr, &vr->interim_shared, size, alignment); | ||
| case VREGION_MEM_TYPE_LIFETIME_SHARED: | ||
| return lifetime_alloc(vr, &vr->lifetime_shared, size, | ||
| alignment < CONFIG_DCACHE_LINE_SIZE ? CONFIG_DCACHE_LINE_SIZE : alignment, |
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.
Move this logic to the lifetime_alloc function.
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 avoids the need to pass a shared flag today.
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.
MAX(alignment, CONFIG_DCACHE_LINE_SIZE)
| } | ||
|
|
||
| /* allocate module in correct vregion*/ | ||
| //TODO: add coherent flag for cross core DP 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.
Consider adding the ZERO flag as well to replace rzalloc and avoid introducing additional memset calls.
| return; | ||
| } | ||
|
|
||
| LOG_ERR("error: vregion free invalid pointer %p", ptr); |
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.
assert / panic?
No ghosts here, the old zone was tied to very limited memory types and capabilities from ancient HW e.g. to make sure we didn't squander things like DMAable memory etc. A region is partitioned into different areas tied to usage in our pipeline lifecycle. There is not type like DMA, LP, HP RAM etc its all just a virtual page. A region is partitioned this way as its easier to then assign attributes for sharing to different cores/domains and to also limit execution (all where permitted by HW).
What would moving the remapping logic gain us ? Keeping the page allocator in a separate file/API keeps things simpler and exposes a page allocator API that can be used as needed independently of regions. Agree, growing the lifetime allocators would save some pages if the runtime size < topology size, but this would lead to fragmentation. i.e. we have to track several virtual page (start, size) per pipeline. This could be something that can come later if we want to add more tracking, but keeping it simple is key at first.
So its an optional usage partition with main use being TEXT for 3P DP modules. This keeps domain tracking easier and these pages will get RO EXEC permission for that domain.
Not atm. We need to get this working for audio as first step. If the vpages part is useful and generic, yes then it can go to Zephyr. The vregion part is probably too specific for our use case.
These are 2 separate APIs hence the difference in naming.
This is factored in to the vregion size allocation for KPB and Deepbuffer like use cases today. i.e. SW has to include in the total memory budget. Its not perfect but I dont want to change too much here until pipeline 2.0 is completed (and at that point we can factor in the pipeline 2.0 changes around the buffers). |
dd0601e to
b35816b
Compare
969bcfb to
3849b37
Compare
57bcf94 to
582d804
Compare
Currently the symlink for sof-ipc4/platform/sof-basefw.ri is not created. Fix this so that deployable builds can be copied directly to target from staging directory. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Use a relative directory to script dir for default workspace if workspace is not set. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Determine workspace if environment variable not defined. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Add a simple virtual page allocator that uses the Zephyr memory mapping infrastructure to allocate pages from a virtual region and map them to physical pages. Due to simplicity, the number of allocations is limited to CONFIG_SOF_VPAGE_MAX_ALLOCS Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Add support for per pipeline and per module virtual memory regions. The intention is to provide a single virtual memory region per pipeline or per DP module that can simplify module/pipeline memory management. The region takes advantage of the way pipeline and modules are constructed, destroyed and used during their lifetimes. 1) memory tracking - 1 pointer/size per pipeline or DP module. 2) memory read/write/execute permissions 3) memory sharing across cores and domains. 4) cache utilization. Modules and pipelines will allocate from their region only and this will be abstracted via the allocation APIs. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
582d804 to
1bfa302
Compare
Split into a common and heap derived allocator files. This is in preparation for memory regions. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Allocate a vregion for pipeline module allocations. TODO: get the sizes from SW. TODO: abstract usage. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Make DP modules use their own vregion for allocations. TODO: abstract usage. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
In preparation for pipeline specific memory regions make sure we get the pipeline prior to module memory allocation and pass teh pipeline to the module allocation API. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Convert rmalloc and rballoc APIs to mod_alloc APIs. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Convert rmalloc and rballoc APIs to mod_alloc APIs. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Convert rmalloc and rballoc APIs to mod_alloc APIs. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Use mod_alloc() API internally within fast-get() and fast-put() so that resources will be tracked at the module level. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Add a module memory abstraction for virtual region usage. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Fix valgrind memory leak warnings by freeing resources. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Add support for cmocka usage of mod_alloc() APIs directly and indirectly via fast_get() APIs. This change means adding module API support into the fast-get-test since the module will track the resource usage. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
1bfa302 to
5ea392c
Compare
Kernel prints on IPC timeout, useful if oops is not generated. Developer aid. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
ipc4_audio_format_to_stream_params() will clear params and hence clear the previously set buffer size. Fix. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
No need to keep reallocating ipc4 stream params as IPC4 usage has a constant size with 0 extended data. Only allocate once. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
WIP: To be split into smaller patches.
Add a simple contiguous virtual page allocator.
Add a virtual region per pipeline/DP module.
Integrate into mod_alloc() and mod_free().
TODO: Make sure all pipeline resources exist within same virtual
region.