From 4da3a94862a31dbbf54e697202b4d14025f03831 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Tue, 16 Jun 2026 20:24:14 +0300 Subject: [PATCH 1/2] schedule: add support for LL userspace tasks Add support for registering user-space LL tasks, and ability to use the task scheduling functions from user-space. The implementation splits scheduler list into kernel and user portions if SOF is built with CONFIG_SOF_USERSPACE_LL. A scheduler type can be either maintained in kernel or user, never both. With this patch, the SOF_SCHEDULE_LL_TIMER is moved to user managed if CONFIG_SOF_USERSPACE_LL is used. Signed-off-by: Kai Vehmanen --- src/include/sof/schedule/schedule.h | 16 ++++----- src/schedule/schedule.c | 21 ++++++++++- zephyr/schedule.c | 54 +++++++++++++++++++++++++++-- 3 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/include/sof/schedule/schedule.h b/src/include/sof/schedule/schedule.h index 9b16a3eff6b1..1e7d6b3842e9 100644 --- a/src/include/sof/schedule/schedule.h +++ b/src/include/sof/schedule/schedule.h @@ -180,6 +180,10 @@ struct schedulers { */ struct schedulers **arch_schedulers_get(void); +struct schedulers **arch_user_schedulers_get(void); + +struct schedulers **arch_user_schedulers_get_for_core(int core); + /** * Retrieves scheduler's data. * @param type SOF_SCHEDULE_ type. @@ -322,17 +326,13 @@ static inline void schedule_free(uint32_t flags) /** See scheduler_ops::scheduler_init_context */ static inline struct k_thread *scheduler_init_context(struct task *task) { - struct schedulers *schedulers = *arch_schedulers_get(); struct schedule_data *sch; - struct list_item *slist; - assert(schedulers); + assert(task && task->sch); + sch = task->sch; - list_for_item(slist, &schedulers->list) { - sch = container_of(slist, struct schedule_data, list); - if (task->type == sch->type && sch->ops->scheduler_init_context) - return sch->ops->scheduler_init_context(sch->data, task); - } + if (sch->ops->scheduler_init_context) + return sch->ops->scheduler_init_context(sch->data, task); return NULL; } diff --git a/src/schedule/schedule.c b/src/schedule/schedule.c index 1848b603145e..f3963eab22ad 100644 --- a/src/schedule/schedule.c +++ b/src/schedule/schedule.c @@ -22,12 +22,23 @@ SOF_DEFINE_REG_UUID(schedule); DECLARE_TR_CTX(sch_tr, SOF_UUID(schedule_uuid), LOG_LEVEL_INFO); +static inline bool scheduler_is_user(int type) +{ + /* + * currently only LL managed in user-space, but longterm + * goal is to move all audio application level scheduling + * to user-space and only keep Zephyr scheduler logic in + * kernel + */ + return type == SOF_SCHEDULE_LL_TIMER; +} + int schedule_task_init(struct task *task, const struct sof_uuid_entry *uid, uint16_t type, uint16_t priority, enum task_state (*run)(void *data), void *data, uint16_t core, uint32_t flags) { - struct schedulers *schedulers = *arch_schedulers_get(); + struct schedulers *schedulers; struct schedule_data *sch = NULL; struct list_item *slist; @@ -36,6 +47,11 @@ int schedule_task_init(struct task *task, return -EINVAL; } + if (IS_ENABLED(CONFIG_SOF_USERSPACE_LL) && scheduler_is_user(type)) + schedulers = *arch_user_schedulers_get_for_core(core); + else + schedulers = *arch_schedulers_get(); + if (!schedulers) return -ENODEV; @@ -69,6 +85,9 @@ static void scheduler_register(struct schedule_data *scheduler) { struct schedulers **sch = arch_schedulers_get(); + if (IS_ENABLED(CONFIG_SOF_USERSPACE_LL) && scheduler_is_user(scheduler->type)) + sch = arch_user_schedulers_get(); + if (!*sch) { /* init schedulers list */ *sch = rzalloc(SOF_MEM_FLAG_KERNEL, diff --git a/zephyr/schedule.c b/zephyr/schedule.c index 1e3971a33682..afc53995610f 100644 --- a/zephyr/schedule.c +++ b/zephyr/schedule.c @@ -14,7 +14,18 @@ #include #include -static APP_SYSUSER_BSS struct schedulers *_schedulers[CONFIG_CORE_COUNT]; +/* Kernel-only scheduler list — depending on how SOF is built, + * either holds all or subset of scheduler types. + * Not accessible from user-space threads. + */ +static struct schedulers *_k_schedulers[CONFIG_CORE_COUNT]; + +#if CONFIG_SOF_USERSPACE_LL +/* User-accessible scheduler list — holds the subset of scheduler + * types that are managed in user-space + */ +static APP_SYSUSER_BSS struct schedulers *_u_schedulers[CONFIG_CORE_COUNT]; +#endif /** * Retrieves registered schedulers. @@ -22,6 +33,45 @@ static APP_SYSUSER_BSS struct schedulers *_schedulers[CONFIG_CORE_COUNT]; */ struct schedulers **arch_schedulers_get(void) { - return _schedulers + cpu_get_id(); +#if CONFIG_SOF_USERSPACE_LL + /* user-space callers must use arch_user_schedulers_get() */ + assert(!k_is_user_context()); +#endif + return _k_schedulers + cpu_get_id(); } EXPORT_SYMBOL(arch_schedulers_get); + +/** + * Retrieves registered user schedulers for the current core. + * + * Relies on cpu_get_id(), so it may invoke privileged functions and + * must not be called from a user-space context. User-space callers + * should use arch_user_schedulers_get_for_core() instead. + * + * @return List of registered schedulers. + */ +struct schedulers **arch_user_schedulers_get(void) +{ +#ifdef CONFIG_SOF_USERSPACE_LL + return _u_schedulers + cpu_get_id(); +#else + return NULL; +#endif +} + +/** + * Retrieves registered user schedulers for the given core. + * + * Unlike arch_user_schedulers_get(), this takes the core explicitly and + * is therefore safe to call from a user-space context. + * + * @return List of registered schedulers. + */ +struct schedulers **arch_user_schedulers_get_for_core(int core) +{ +#ifdef CONFIG_SOF_USERSPACE_LL + return _u_schedulers + core; +#else + return NULL; +#endif +} From f398430a70a1cc87d45d4a93e099fe457143d69c Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Wed, 24 Jun 2026 13:45:27 +0300 Subject: [PATCH 2/2] schedule: allocate the scheduler objects with sof_heap_alloc() Ensure the scheduler objects and lists of schedulers are allocated such that they can be used with both kernel and user-space LL scheduler implementations. The SOF_MEM_FLAG_KERNEL flag is removed. This flag has been a no-op for a while, and given scheduler list is not always in kernel anymore, it would be highly confusing to keep it. When CONFIG_SOF_USERSPACE_LL is set, the context of all schedulers is managed in the LL user-space domain. Signed-off-by: Kai Vehmanen --- src/schedule/schedule.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/schedule/schedule.c b/src/schedule/schedule.c index f3963eab22ad..231bba864856 100644 --- a/src/schedule/schedule.c +++ b/src/schedule/schedule.c @@ -84,18 +84,21 @@ int schedule_task_init(struct task *task, static void scheduler_register(struct schedule_data *scheduler) { struct schedulers **sch = arch_schedulers_get(); + struct k_heap *heap = NULL; - if (IS_ENABLED(CONFIG_SOF_USERSPACE_LL) && scheduler_is_user(scheduler->type)) + if (IS_ENABLED(CONFIG_SOF_USERSPACE_LL) && scheduler_is_user(scheduler->type)) { sch = arch_user_schedulers_get(); + heap = sof_sys_user_heap_get(); + } if (!*sch) { /* init schedulers list */ - *sch = rzalloc(SOF_MEM_FLAG_KERNEL, - sizeof(**sch)); + *sch = sof_heap_alloc(heap, 0, sizeof(**sch), 0); if (!*sch) { tr_err(&sch_tr, "allocation failed"); return; } + memset(*sch, 0, sizeof(**sch)); list_init(&(*sch)->list); } @@ -105,16 +108,21 @@ static void scheduler_register(struct schedule_data *scheduler) void scheduler_init(int type, const struct scheduler_ops *ops, void *data) { struct schedule_data *sch; + struct k_heap *heap = NULL; + + if (IS_ENABLED(CONFIG_SOF_USERSPACE_LL) && scheduler_is_user(type)) + heap = sof_sys_user_heap_get(); if (!ops || !ops->schedule_task || !ops->schedule_task_cancel || !ops->schedule_task_free) return; - sch = rzalloc(SOF_MEM_FLAG_KERNEL, sizeof(*sch)); + sch = sof_heap_alloc(heap, SOF_MEM_FLAG_KERNEL, sizeof(*sch), 0); if (!sch) { tr_err(&sch_tr, "allocation failed"); sof_panic(SOF_IPC_PANIC_IPC); } + memset(sch, 0, sizeof(*sch)); list_init(&sch->list); sch->type = type; sch->ops = ops;