Skip to content

Commit 56da991

Browse files
committed
schedule: zephyr_ll: grant LL thread access to per-task semaphores
In CONFIG_SOF_USERSPACE_LL builds the LL scheduler thread runs unprivileged, so every Zephyr kernel object it accesses must be explicitly granted to it. In commit ffa52df ("schedule: ll: dynamically allocate the semaphore"), task semaphores were converted to dynamically allocated objects. Only the bootstrap task's semaphore was granted to the LL thread (in zephyr_ll_init_context()); tasks created later (e.g. chain_dma) were not, so pausing/stopping such a task while it was running crashed the DSP. Fix the issue by grant the LL scheduling thread access to the task's semaphore at allocation time, from the syscall implementation which runs in privileged context. Add zephyr_domain_thread_tid_for_core() to look up the LL thread for an explicit core without relying on cpu_get_id(), and without dereferencing user-accessible task struct. Fixes: ffa52df ("schedule: ll: dynamically allocate the semaphore") Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
1 parent b8d5999 commit 56da991

3 files changed

Lines changed: 47 additions & 0 deletions

File tree

‎src/include/sof/schedule/ll_schedule_domain.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ struct ll_schedule_domain *zephyr_domain_init(int clk);
328328
#define timer_domain_init(timer, clk) zephyr_domain_init(clk)
329329
#ifdef CONFIG_SOF_USERSPACE_LL
330330
struct k_thread *zephyr_domain_thread_tid(struct ll_schedule_domain *domain);
331+
struct k_thread *zephyr_domain_thread_tid_for_core(int core);
331332
struct k_mem_domain *zephyr_ll_mem_domain(void);
332333
#endif /* CONFIG_SOF_USERSPACE_LL */
333334
#ifdef CONFIG_SOF_FULL_ZEPHYR_APPLICATION

‎src/schedule/zephyr_domain.c‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,14 @@ static int zephyr_domain_unregister(struct ll_schedule_domain *domain,
289289

290290
#else /* CONFIG_SOF_USERSPACE_LL */
291291

292+
/*
293+
* Kernel-owned per-core LL thread table. Populated from the privileged
294+
* domain-thread init path and consulted by z_impl_zephyr_ll_task_sem_alloc()
295+
* so that privileged code never has to traverse the user-accessible
296+
* scheduler/domain objects to find the LL thread of a given core.
297+
*/
298+
static struct k_thread *ll_thread_tid[CONFIG_CORE_COUNT];
299+
292300
/*
293301
* Privileged thread initialization for userspace LL scheduling.
294302
* Creates the scheduling thread, sets up timer, grants access to kernel
@@ -343,6 +351,9 @@ static int zephyr_domain_thread_init(struct ll_schedule_domain *domain,
343351
INT_TO_POINTER(core), NULL, CONFIG_LL_THREAD_PRIORITY,
344352
K_USER, K_FOREVER);
345353

354+
/* record in the kernel-only table for syscall-context lookups */
355+
ll_thread_tid[core] = dt->ll_thread;
356+
346357
#ifdef CONFIG_SCHED_CPU_MASK
347358
k_thread_cpu_pin(thread, core);
348359
#endif
@@ -477,6 +488,7 @@ static void zephyr_domain_thread_free(struct ll_schedule_domain *domain,
477488
k_thread_abort(dt->ll_thread);
478489
k_object_free(dt->ll_thread);
479490
dt->ll_thread = NULL;
491+
ll_thread_tid[core] = NULL;
480492
}
481493

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

513+
/*
514+
* Return the LL scheduling thread for an explicitly given core.
515+
*
516+
* Reads a kernel-only table keyed by core, so it is safe to call from a
517+
* privileged syscall context without dereferencing any user-accessible
518+
* scheduler or domain object, and without relying on cpu_get_id().
519+
*/
520+
struct k_thread *zephyr_domain_thread_tid_for_core(int core)
521+
{
522+
if (core < 0 || core >= CONFIG_CORE_COUNT)
523+
return NULL;
524+
525+
return ll_thread_tid[core];
526+
}
527+
501528
#endif /* CONFIG_SOF_USERSPACE_LL */
502529

503530
#if CONFIG_CROSS_CORE_STREAM

‎src/schedule/zephyr_ll.c‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,25 @@ int z_impl_zephyr_ll_task_sem_alloc(struct task *task)
474474

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

477+
#if CONFIG_SOF_USERSPACE_LL
478+
/*
479+
* The per-task semaphore is signalled from zephyr_ll_task_done(),
480+
* which runs in the (unprivileged) LL scheduler thread when a task is
481+
* freed while it is still running. k_object_alloc() only grants access
482+
* to the calling thread (the IPC handler that creates the task), so the
483+
* LL thread must be granted access explicitly, otherwise its
484+
* k_sem_give() traps with a userspace permission fault.
485+
*
486+
* Resolve the LL thread from kernel-only per-core state keyed by the
487+
* task's target core; never traverse the user-accessible scheduler or
488+
* domain objects from privileged context.
489+
*/
490+
struct k_thread *ll_tid = zephyr_domain_thread_tid_for_core(task->core);
491+
492+
if (ll_tid)
493+
k_thread_access_grant(ll_tid, ts->sem);
494+
#endif
495+
477496
ts->task = task;
478497
pdata->sem_p = ts->sem;
479498
/* List is protected by IPC serialization */

0 commit comments

Comments
 (0)