Skip to content
Open
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
1 change: 1 addition & 0 deletions src/include/sof/schedule/ll_schedule_domain.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ struct ll_schedule_domain *zephyr_domain_init(int clk);
#define timer_domain_init(timer, clk) zephyr_domain_init(clk)
#ifdef CONFIG_SOF_USERSPACE_LL
struct k_thread *zephyr_domain_thread_tid(struct ll_schedule_domain *domain);
struct k_thread *zephyr_domain_thread_tid_for_core(int core);
struct k_mem_domain *zephyr_ll_mem_domain(void);
#endif /* CONFIG_SOF_USERSPACE_LL */
#ifdef CONFIG_SOF_FULL_ZEPHYR_APPLICATION
Expand Down
27 changes: 27 additions & 0 deletions src/schedule/zephyr_domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,14 @@ static int zephyr_domain_unregister(struct ll_schedule_domain *domain,

#else /* CONFIG_SOF_USERSPACE_LL */

/*
* Kernel-owned per-core LL thread table. Populated from the privileged
* domain-thread init path and consulted by z_impl_zephyr_ll_task_sem_alloc()
* so that privileged code never has to traverse the user-accessible
* scheduler/domain objects to find the LL thread of a given core.
*/
static struct k_thread *ll_thread_tid[CONFIG_CORE_COUNT];

/*
* Privileged thread initialization for userspace LL scheduling.
* Creates the scheduling thread, sets up timer, grants access to kernel
Expand Down Expand Up @@ -343,6 +351,9 @@ static int zephyr_domain_thread_init(struct ll_schedule_domain *domain,
INT_TO_POINTER(core), NULL, CONFIG_LL_THREAD_PRIORITY,
K_USER, K_FOREVER);

/* record in the kernel-only table for syscall-context lookups */
ll_thread_tid[core] = dt->ll_thread;

#ifdef CONFIG_SCHED_CPU_MASK
k_thread_cpu_pin(thread, core);
#endif
Expand Down Expand Up @@ -477,6 +488,7 @@ static void zephyr_domain_thread_free(struct ll_schedule_domain *domain,
k_thread_abort(dt->ll_thread);
k_object_free(dt->ll_thread);
dt->ll_thread = NULL;
ll_thread_tid[core] = NULL;
}

if (dt->sem) {
Expand All @@ -498,6 +510,21 @@ struct k_thread *zephyr_domain_thread_tid(struct ll_schedule_domain *domain)
return dt->ll_thread;
}

/*
* Return the LL scheduling thread for an explicitly given core.
*
* Reads a kernel-only table keyed by core, so it is safe to call from a
* privileged syscall context without dereferencing any user-accessible
* scheduler or domain object, and without relying on cpu_get_id().
*/
struct k_thread *zephyr_domain_thread_tid_for_core(int core)
{
if (core < 0 || core >= CONFIG_CORE_COUNT)
return NULL;

return ll_thread_tid[core];
}

#endif /* CONFIG_SOF_USERSPACE_LL */

#if CONFIG_CROSS_CORE_STREAM
Expand Down
19 changes: 19 additions & 0 deletions src/schedule/zephyr_ll.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,25 @@ int z_impl_zephyr_ll_task_sem_alloc(struct task *task)

k_sem_init(ts->sem, 0, 1);

#if CONFIG_SOF_USERSPACE_LL
/*
* The per-task semaphore is signalled from zephyr_ll_task_done(),
* which runs in the (unprivileged) LL scheduler thread when a task is
* freed while it is still running. k_object_alloc() only grants access
* to the calling thread (the IPC handler that creates the task), so the
* LL thread must be granted access explicitly, otherwise its
* k_sem_give() traps with a userspace permission fault.
*
* Resolve the LL thread from kernel-only per-core state keyed by the
* task's target core; never traverse the user-accessible scheduler or
* domain objects from privileged context.
*/
struct k_thread *ll_tid = zephyr_domain_thread_tid_for_core(task->core);

@lyakh lyakh Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

isn't k_thread deprecated and we have to use k_tid_t?

@kv2019i kv2019i Aug 17, 2026

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.

@lyakh Is it deprecated? The value we return here is passed to k_object_access_grant() which takes "struct k_thread *" as argument. But OTOH, upstream Zephyr test code now passes k_current_get() to k_object_access_grant() (and k_current_get() returns k_tid_t). The function name has "thread_tid_for_core", so maybe I'll change for k_tid_t...
UPDATE: I think I'll keep it like this. We only store "struct k_thread*" in zephyr_domain.c. There is no function to convert a "struct k_thread *" to k_tid_t (they are the same thing, it's just a cast in zephyr thread.c). We could of course add another field to store also the k_tid_t in "struct zephyr_domain_thread", but that seems a bit silly (as the values are always the same). I think as long as Zephyr's thread.c just casts the types, we can do the same.

@lyakh lyakh Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@lyakh Is it deprecated? The value we return here is passed to k_object_access_grant() which takes "struct k_thread " as argument. But OTOH, upstream Zephyr test code now passes k_current_get() to k_object_access_grant() (and k_current_get() returns k_tid_t). The function name has "thread_tid_for_core", so maybe I'll change for k_tid_t... UPDATE: I think I'll keep it like this. We only store "struct k_thread" in zephyr_domain.c. There is no function to convert a "struct k_thread *" to k_tid_t (they are the same thing, it's just a cast in zephyr thread.c). We could of course add another field to store also the k_tid_t in "struct zephyr_domain_thread", but that seems a bit silly (as the values are always the same). I think as long as Zephyr's thread.c just casts the types, we can do the same.

@kv2019i ah, oh, sorry, not really. It's only deprecated in one specific case https://github.com/zephyrproject-rtos/zephyr/blob/6b0261c4f6761b4250faf598acd8b21d36de31b4/include/zephyr/kernel.h#L4911 - that bit me at some point but then I forgot what exactly it was...


if (ll_tid)
k_thread_access_grant(ll_tid, ts->sem);
#endif

ts->task = task;
pdata->sem_p = ts->sem;
/* List is protected by IPC serialization */
Expand Down
Loading