From e6a77973411048c538588e606b7a5efa48032b30 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 6 Aug 2026 17:12:31 +0200 Subject: [PATCH] schedule: ll: dynamically allocate the semaphore The LL scheduler semaphore is used by the userspace, so it has to be allocated dynamically. On the other hand dynamic object freeing from the userspace is unsupported by design. To securely free the semaphore object we add two syscalls that guarantee, that object freeing cannot be abused. Signed-off-by: Guennadi Liakhovetski --- src/include/sof/schedule/ll_schedule_domain.h | 10 ++ src/schedule/zephyr_ll.c | 120 +++++++++++++++++- zephyr/CMakeLists.txt | 1 + 3 files changed, 126 insertions(+), 5 deletions(-) diff --git a/src/include/sof/schedule/ll_schedule_domain.h b/src/include/sof/schedule/ll_schedule_domain.h index f356566ca0aa..b4b58e6923e2 100644 --- a/src/include/sof/schedule/ll_schedule_domain.h +++ b/src/include/sof/schedule/ll_schedule_domain.h @@ -330,6 +330,16 @@ struct ll_schedule_domain *zephyr_domain_init(int clk); struct k_thread *zephyr_domain_thread_tid(struct ll_schedule_domain *domain); struct k_mem_domain *zephyr_ll_mem_domain(void); #endif /* CONFIG_SOF_USERSPACE_LL */ +#ifdef CONFIG_SOF_FULL_ZEPHYR_APPLICATION +__syscall int zephyr_ll_task_sem_alloc(struct task *task); +__syscall int zephyr_ll_task_sem_free(struct task *task); +#include +#else +int z_impl_zephyr_ll_task_sem_alloc(struct task *task); +int z_impl_zephyr_ll_task_sem_free(struct task *task); +#define zephyr_ll_task_sem_alloc z_impl_zephyr_ll_task_sem_alloc +#define zephyr_ll_task_sem_free z_impl_zephyr_ll_task_sem_free +#endif /* CONFIG_SOF_FULL_ZEPHYR_APPLICATION */ #endif /* __ZEPHYR__ */ struct ll_schedule_domain *dma_multi_chan_domain_init(struct dma *dma_array, diff --git a/src/schedule/zephyr_ll.c b/src/schedule/zephyr_ll.c index 85c7f80552c6..15a1846bcadf 100644 --- a/src/schedule/zephyr_ll.c +++ b/src/schedule/zephyr_ll.c @@ -41,7 +41,10 @@ struct zephyr_ll { struct zephyr_ll_pdata { bool run; bool freeing; + struct k_sem *sem_p; +#if !CONFIG_DYNAMIC_OBJECTS struct k_sem sem; +#endif }; #if CONFIG_SOF_USERSPACE_LL @@ -136,7 +139,7 @@ static void zephyr_ll_task_done(struct zephyr_ll *sch, * zephyr_ll_task_free() is trying to free this task. Complete * it and signal the semaphore to let the function proceed */ - k_sem_give(&pdata->sem); + k_sem_give(pdata->sem_p); tr_info(&ll_tr, "task complete %p %pU", task, task->uid); tr_info(&ll_tr, "num_tasks %d total_num_tasks %ld", @@ -448,6 +451,98 @@ static int zephyr_ll_task_schedule_after(void *data, struct task *task, uint64_t return zephyr_ll_task_schedule_common(sch, task, start, period, after, false); } +#if CONFIG_DYNAMIC_OBJECTS +static struct list_item zephyr_ll_task_sem_list = LIST_INIT(zephyr_ll_task_sem_list); + +struct zephyr_ll_task_sem { + struct task *task; + struct k_sem *sem; + struct list_item list; +}; + +int z_impl_zephyr_ll_task_sem_alloc(struct task *task) +{ + struct zephyr_ll_pdata *pdata = task->priv_data; + struct zephyr_ll_task_sem *ts = rmalloc(SOF_MEM_FLAG_COHERENT, sizeof(*ts)); + + if (!ts) + return -ENOMEM; + + ts->sem = k_object_alloc(K_OBJ_SEM); + if (!ts->sem) { + rfree(ts); + return -ENOMEM; + } + + k_sem_init(ts->sem, 0, 1); + + ts->task = task; + pdata->sem_p = ts->sem; + /* List is protected by IPC serialization */ + list_item_append(&ts->list, &zephyr_ll_task_sem_list); + + return 0; +} + +int z_impl_zephyr_ll_task_sem_free(struct task *task) +{ + struct zephyr_ll_pdata *pdata = task->priv_data; + struct list_item *list; + struct zephyr_ll_task_sem *ts; + bool found = false; + + /* List is protected by IPC serialization */ + list_for_item(list, &zephyr_ll_task_sem_list) { + ts = container_of(list, struct zephyr_ll_task_sem, list); + if (ts->task == task) { + found = true; + break; + } + } + + if (!found) + return -ENOENT; + + if (pdata->sem_p != ts->sem) + return -EINVAL; + + list_item_del(list); + k_object_free(ts->sem); + rfree(ts); + + return 0; +} + +#ifdef CONFIG_USERSPACE +#include +static inline int z_vrfy_zephyr_ll_task_sem_alloc(struct task *task) +{ + if (!task) + return -EINVAL; + K_OOPS(K_SYSCALL_MEMORY_WRITE(task, sizeof(*task))); + if (!task->priv_data) + return -EINVAL; + K_OOPS(K_SYSCALL_MEMORY_WRITE(task->priv_data, sizeof(struct zephyr_ll_pdata))); + + return z_impl_zephyr_ll_task_sem_alloc(task); +} +#include + +static inline int z_vrfy_zephyr_ll_task_sem_free(struct task *task) +{ + if (!task) + return -EINVAL; + K_OOPS(K_SYSCALL_MEMORY_WRITE(task, sizeof(*task))); + if (!task->priv_data) + return -EINVAL; + K_OOPS(K_SYSCALL_MEMORY_WRITE(task->priv_data, sizeof(struct zephyr_ll_pdata))); + + return z_impl_zephyr_ll_task_sem_free(task); +} +#include +#endif +#endif + /* * This is synchronous - after this returns the object can be destroyed! * Assertion: under Zephyr this is always called from a thread context! @@ -505,10 +600,13 @@ static int zephyr_ll_task_free(void *data, struct task *task) if (must_wait) /* Wait for up to 100 periods */ - k_sem_take(&pdata->sem, K_USEC(LL_TIMER_PERIOD_US * 100)); + k_sem_take(pdata->sem_p, K_USEC(LL_TIMER_PERIOD_US * 100)); /* Protect against racing with schedule_task() */ zephyr_ll_lock(sch, &flags); +#if CONFIG_DYNAMIC_OBJECTS + zephyr_ll_task_sem_free(task); +#endif task->priv_data = NULL; sof_heap_free(sch->heap, pdata); zephyr_ll_unlock(sch, &flags); @@ -573,6 +671,7 @@ static void zephyr_ll_scheduler_free(void *data, uint32_t flags) struct k_thread *zephyr_ll_init_context(void *data, struct task *task) { struct zephyr_ll *sch = data; + struct zephyr_ll_pdata *pdata = task->priv_data; int ret; /* @@ -587,7 +686,7 @@ struct k_thread *zephyr_ll_init_context(void *data, struct task *task) } assert(!k_is_user_context()); - k_thread_access_grant(zephyr_domain_thread_tid(sch->ll_domain), sch->lock); + k_thread_access_grant(zephyr_domain_thread_tid(sch->ll_domain), sch->lock, pdata->sem_p); tr_dbg(&ll_tr, "granting access to lock %p for thread %p", sch->lock, zephyr_domain_thread_tid(sch->ll_domain)); @@ -698,10 +797,21 @@ int zephyr_ll_task_init(struct task *task, memset(pdata, 0, sizeof(*pdata)); - k_sem_init(&pdata->sem, 0, 1); - task->priv_data = pdata; +#if CONFIG_DYNAMIC_OBJECTS + ret = zephyr_ll_task_sem_alloc(task); + if (ret < 0) { + sof_heap_free(heap, pdata); + task->priv_data = NULL; + return ret; + } +#else + pdata->sem_p = &pdata->sem; +#endif + + k_sem_init(pdata->sem_p, 0, 1); + return 0; } EXPORT_SYMBOL(zephyr_ll_task_init); diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 4b61a9517d46..0ed4a13ee742 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -623,6 +623,7 @@ zephyr_library_sources_ifdef(CONFIG_SHELL zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/audio/module_adapter/module/generic.h) zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/lib/fast-get.h) zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/ipc/ipc_reply.h) +zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/schedule/ll_schedule_domain.h) zephyr_syscall_header(${SOF_SRC_PATH}/include/ipc4/handler.h) zephyr_syscall_header(include/rtos/alloc.h) zephyr_library_sources_ifdef(CONFIG_SOF_USERSPACE_INTERFACE_ALLOC syscall/alloc.c)