Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions src/schedule/zephyr_ll_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ LOG_MODULE_DECLARE(ll_schedule, CONFIG_SOF_LOG_LEVEL);
*
* This structure encapsulates the memory management resources required for the
* low-latency (LL) scheduler in userspace mode. It provides memory isolation
* and heap management for LL scheduler threads.
* and heap management for LL scheduler threads. Only kernel accessible.
*/
struct zephyr_ll_mem_resources {
struct k_mem_domain mem_domain; /**< Memory domain for LL thread isolation */
Expand All @@ -26,17 +26,15 @@ struct zephyr_ll_mem_resources {

static struct zephyr_ll_mem_resources ll_mem_resources;

/* Heap allocator for LL scheduler memory (user accessible pointer) */
APP_SYSUSER_DATA static struct k_heap *zephyr_ll_heap;

static struct k_heap *zephyr_ll_heap_init(void)
{
struct k_heap *heap = module_driver_heap_init();
struct k_heap *heap = sys_user_heap_init();
struct k_mem_partition mem_partition;
int ret;

/*
* TODO: the size of LL heap should be independently configurable and
* not tied to CONFIG_SOF_ZEPHYR_USERSPACE_MODULE_HEAP_SIZE
*/

if (!heap) {
tr_err(&ll_tr, "heap alloc fail");
k_panic();
Expand All @@ -53,13 +51,15 @@ static struct k_heap *zephyr_ll_heap_init(void)
if (ret)
k_panic();

#ifdef CONFIG_CACHE_HAS_MIRRORED_MEMORY_REGIONS
mem_partition.start = (uintptr_t)sys_cache_uncached_ptr_get(heap->heap.init_mem);
mem_partition.attr = K_MEM_PARTITION_P_RW_U_RW;
ret = k_mem_domain_add_partition(&ll_mem_resources.mem_domain, &mem_partition);
tr_dbg(&ll_tr, "init ll heap %p, size %u (uncached), ret %d",
(void *)mem_partition.start, heap->heap.init_bytes, ret);
if (ret)
k_panic();
#endif

return heap;
}
Expand All @@ -70,13 +70,17 @@ void zephyr_ll_user_resources_init(void)

ll_mem_resources.heap = zephyr_ll_heap_init();

/* store a user-accessible pointer */
zephyr_ll_heap = ll_mem_resources.heap;

/* attach common partition to LL domain */
user_memory_attach_common_partition(zephyr_ll_mem_domain());
user_memory_attach_system_user_partition(zephyr_ll_mem_domain());
Comment on lines +73 to +78
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing user_memory_attach() calls don't check the errors, so I prefer to leave them out here as well. We get decent errors for access permissions nowadays, so if this fails, we get a good error anyways.

}

struct k_heap *zephyr_ll_user_heap(void)
{
return ll_mem_resources.heap;
return zephyr_ll_heap;
}

struct k_mem_domain *zephyr_ll_mem_domain(void)
Expand Down
11 changes: 11 additions & 0 deletions zephyr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ config SOF_ZEPHYR_USERSPACE_MODULE_HEAP_SIZE
module has its own independent heap to which only it has access. This heap is
shared between instances of the same module.

config SOF_ZEPHYR_SYS_USER_HEAP_SIZE
hex "Size of the shared LL user-space heap"
default 0x20000
depends on SOF_USERSPACE_LL
help
The size of the shared heap used by the low-latency (LL) scheduler
user-space thread. This heap is shared across all pipelines running
on the LL thread and must be large enough to hold all host DMA
buffers, chain DMA data, SG elements, and module adapter buffers
for all simultaneously active pipelines.

config SOF_USERSPACE_PROXY
bool "Use userspace proxy to support userspace modules"
select SOF_USERSPACE_USE_DRIVER_HEAP
Expand Down
21 changes: 21 additions & 0 deletions zephyr/include/rtos/userspace_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,27 @@ struct userspace_context;
*/
struct k_heap *module_driver_heap_init(void);

/**
* Initialize the shared LL user-space heap (SOF_ZEPHYR_SYS_USER_HEAP_SIZE)
* with embedded k_heap metadata.
* @return pointer to the k_heap structure.
*
* @note
* Unlike module_driver_heap_init(), this function embeds the k_heap
* struct at the start of the page-aligned backing buffer. The
* init_mem / init_bytes fields cover the full allocation, so a
* memory partition derived from them automatically includes the
* k_heap metadata (required for userspace syscall verification).
* Must be freed with sys_user_heap_remove().
*/
struct k_heap *sys_user_heap_init(void);

/**
* Free the shared LL user-space heap allocated by sys_user_heap_init().
* @param heap pointer to the k_heap structure.
*/
void sys_user_heap_remove(struct k_heap *heap);

/**
* Attach common userspace memory partition to a module memory domain.
* @param dom - memory domain to attach the common partition to.
Expand Down
45 changes: 45 additions & 0 deletions zephyr/lib/userspace_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,51 @@ void module_driver_heap_remove(struct k_heap *mod_drv_heap)
}
}

struct k_heap *sys_user_heap_init(void)
{
const size_t alloc_size = CONFIG_SOF_ZEPHYR_SYS_USER_HEAP_SIZE;
const size_t prefix_size = ALIGN_UP(sizeof(struct k_heap), sizeof(uintptr_t));
const size_t kheap_size = alloc_size - prefix_size;
Comment on lines +74 to +76
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in V2.


BUILD_ASSERT(CONFIG_SOF_ZEPHYR_SYS_USER_HEAP_SIZE % CONFIG_MM_DRV_PAGE_SIZE == 0);

if (alloc_size <= prefix_size)
return NULL;

/*
* Allocate a single page-aligned buffer for both the k_heap
* metadata and the heap backing memory. Placing the k_heap
* struct inside the same allocation means the memory partition
* (which uses init_mem / init_bytes) automatically covers the
* k_heap struct, making it accessible from userspace syscall
* verification wrappers such as z_vrfy_mod_free().
*/
void *mem = rballoc_align(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, alloc_size,
CONFIG_MM_DRV_PAGE_SIZE);
if (!mem)
return NULL;

struct k_heap *inline_heap = (struct k_heap *)mem;
void *heap_buf = (uint8_t *)mem + prefix_size;

k_heap_init(inline_heap, heap_buf, kheap_size);

/* init_mem / init_bytes track the full allocation so that
* partition setup and sys_user_heap_remove()
* cover and free the entire region including the k_heap struct.
*/
inline_heap->heap.init_mem = mem;
inline_heap->heap.init_bytes = alloc_size;

return inline_heap;
}

void sys_user_heap_remove(struct k_heap *sys_user_heap)
{
if (sys_user_heap)
rfree(sys_user_heap->heap.init_mem);
}

void *user_stack_allocate(size_t stack_size, uint32_t options)
{
return k_thread_stack_alloc(stack_size, options & K_USER);
Expand Down
Loading