@@ -126,7 +126,8 @@ static void zephyr_domain_thread_fn(void *p1, void *p2, void *p3)
126126 }
127127#endif
128128
129- dt -> handler (dt -> arg );
129+ if (dt -> handler )
130+ dt -> handler (dt -> arg );
130131
131132#ifdef CONFIG_SCHEDULE_LL_STATS_LOG
132133 cycles1 = k_cycle_get_32 ();
@@ -289,63 +290,65 @@ static int zephyr_domain_unregister(struct ll_schedule_domain *domain,
289290
290291#else /* CONFIG_SOF_USERSPACE_LL */
291292
292- /* User-space implementation for register/unregister */
293-
294- static int zephyr_domain_register_user (struct ll_schedule_domain * domain ,
295- struct task * task ,
296- void (* handler )(void * arg ), void * arg )
293+ /*
294+ * Privileged thread initialization for userspace LL scheduling.
295+ * Creates the scheduling thread, sets up timer, grants access to kernel
296+ * objects. Must be called from kernel context before any user-space
297+ * domain_register() calls.
298+ */
299+ static int zephyr_domain_thread_init (struct ll_schedule_domain * domain ,
300+ struct task * task )
297301{
298302 struct zephyr_domain * zephyr_domain = ll_sch_domain_get_pdata (domain );
299- int core = cpu_get_id ();
300- struct zephyr_domain_thread * dt = zephyr_domain -> domain_thread + core ;
303+ struct zephyr_domain_thread * dt ;
301304 char thread_name [] = "ll_thread0" ;
302305 k_tid_t thread ;
306+ int core = task -> core ;
303307
304- tr_dbg (& ll_tr , "entry" );
308+ tr_dbg (& ll_tr , "thread_init entry" );
305309
306- /* domain work only needs registered once on each core */
307- if (dt -> handler )
308- return 0 ;
310+ if (core >= CONFIG_CORE_COUNT )
311+ return - EINVAL ;
309312
310- __ASSERT_NO_MSG ( task -> core == core ) ;
313+ dt = zephyr_domain -> domain_thread + core ;
311314
312- dt -> handler = handler ;
313- dt -> arg = arg ;
315+ /* thread only needs to be created once per core */
316+ if (dt -> ll_thread )
317+ return 0 ;
318+
319+ dt -> handler = NULL ;
320+ dt -> arg = NULL ;
314321
315322 /* 10 is rather random, we better not accumulate 10 missed timer interrupts */
316323 k_sem_init (dt -> sem , 0 , 10 );
317324
318325 thread_name [sizeof (thread_name ) - 2 ] = '0' + core ;
319326
327+ /* Allocate thread structure dynamically */
328+ dt -> ll_thread = k_object_alloc (K_OBJ_THREAD );
320329 if (!dt -> ll_thread ) {
321- /* Allocate thread structure dynamically */
322- dt -> ll_thread = k_object_alloc (K_OBJ_THREAD );
323- if (!dt -> ll_thread ) {
324- tr_err (& ll_tr , "Failed to allocate thread object for core %d" , core );
325- dt -> handler = NULL ;
326- dt -> arg = NULL ;
327- return - ENOMEM ;
328- }
330+ tr_err (& ll_tr , "Failed to allocate thread object for core %d" , core );
331+ return - ENOMEM ;
332+ }
329333
330- thread = k_thread_create (dt -> ll_thread , ll_sched_stack [core ], ZEPHYR_LL_STACK_SIZE ,
331- zephyr_domain_thread_fn , zephyr_domain ,
332- INT_TO_POINTER (core ), NULL , CONFIG_LL_THREAD_PRIORITY ,
333- K_USER , K_FOREVER );
334+ thread = k_thread_create (dt -> ll_thread , ll_sched_stack [core ], ZEPHYR_LL_STACK_SIZE ,
335+ zephyr_domain_thread_fn , zephyr_domain ,
336+ INT_TO_POINTER (core ), NULL , CONFIG_LL_THREAD_PRIORITY ,
337+ K_USER , K_FOREVER );
334338
335339#ifdef CONFIG_SCHED_CPU_MASK
336- k_thread_cpu_mask_clear (thread );
337- k_thread_cpu_mask_enable (thread , core );
340+ k_thread_cpu_mask_clear (thread );
341+ k_thread_cpu_mask_enable (thread , core );
338342#endif
339- k_thread_name_set (thread , thread_name );
343+ k_thread_name_set (thread , thread_name );
340344
341- k_mem_domain_add_thread (zephyr_ll_mem_domain (), thread );
342- k_thread_access_grant (thread , dt -> sem , domain -> lock , zephyr_domain -> timer );
343- user_grant_dai_access_all (thread );
344- user_grant_dma_access_all (thread );
345- tr_dbg (& ll_tr , "granted LL access to thread %p (core %d)" , thread , core );
345+ k_mem_domain_add_thread (zephyr_ll_mem_domain (), thread );
346+ k_thread_access_grant (thread , dt -> sem , domain -> lock , zephyr_domain -> timer );
347+ user_grant_dai_access_all (thread );
348+ user_grant_dma_access_all (thread );
349+ tr_dbg (& ll_tr , "granted LL access to thread %p (core %d)" , thread , core );
346350
347- k_thread_start (thread );
348- }
351+ k_thread_start (thread );
349352
350353 k_mutex_lock (domain -> lock , K_FOREVER );
351354 if (!k_timer_user_data_get (zephyr_domain -> timer )) {
@@ -368,6 +371,43 @@ static int zephyr_domain_register_user(struct ll_schedule_domain *domain,
368371 return 0 ;
369372}
370373
374+ /*
375+ * User-space register: bookkeeping only. The privileged thread setup has
376+ * already been done by domain_thread_init() called from kernel context.
377+ */
378+ static int zephyr_domain_register_user (struct ll_schedule_domain * domain ,
379+ struct task * task ,
380+ void (* handler )(void * arg ), void * arg )
381+ {
382+ struct zephyr_domain * zephyr_domain = ll_sch_domain_get_pdata (domain );
383+ struct zephyr_domain_thread * dt ;
384+ int core ;
385+
386+ tr_dbg (& ll_tr , "register_user entry" );
387+
388+ if (task -> core >= CONFIG_CORE_COUNT )
389+ return - EINVAL ;
390+
391+ core = task -> core ;
392+ dt = zephyr_domain -> domain_thread + core ;
393+
394+ if (!dt -> ll_thread ) {
395+ tr_err (& ll_tr , "domain_thread_init() not called for core %d" , core );
396+ return - EINVAL ;
397+ }
398+
399+ __ASSERT_NO_MSG (!dt -> handler || dt -> handler == handler );
400+ if (dt -> handler )
401+ return 0 ;
402+
403+ dt -> handler = handler ;
404+ dt -> arg = arg ;
405+
406+ tr_info (& ll_tr , "task registered on core %d" , core );
407+
408+ return 0 ;
409+ }
410+
371411static int zephyr_domain_unregister_user (struct ll_schedule_domain * domain ,
372412 struct task * task , uint32_t num_tasks )
373413{
@@ -382,30 +422,58 @@ static int zephyr_domain_unregister_user(struct ll_schedule_domain *domain,
382422
383423 k_mutex_lock (domain -> lock , K_FOREVER );
384424
385- if (!atomic_read (& domain -> total_num_tasks )) {
386- /* Disable the watchdog */
425+ zephyr_domain -> domain_thread [core ].handler = NULL ;
426+
427+ k_mutex_unlock (domain -> lock );
428+
429+ /*
430+ * In this user thread implementation, the timer is left
431+ * running until privileged domain_thread_free() is called
432+ * to clean up resources.
433+ */
434+
435+ tr_dbg (& ll_tr , "exit" );
436+
437+ return 0 ;
438+ }
439+
440+ /*
441+ * Free resources acquired by zephyr_domain_thread_init().
442+ * Stops the timer, aborts the scheduling thread and frees the thread object.
443+ * Must be called from kernel context.
444+ */
445+ static void zephyr_domain_thread_free (struct ll_schedule_domain * domain ,
446+ uint32_t num_tasks )
447+ {
448+ struct zephyr_domain * zephyr_domain = ll_sch_domain_get_pdata (domain );
449+ int core = cpu_get_id ();
450+ struct zephyr_domain_thread * dt = zephyr_domain -> domain_thread + core ;
451+
452+ tr_dbg (& ll_tr , "thread_free entry, core %d, num_tasks %u" , core , num_tasks );
453+
454+ /* Still tasks on other cores, only clean up this core's thread */
455+ k_mutex_lock (domain -> lock , K_FOREVER );
456+
457+ if (!num_tasks && !atomic_read (& domain -> total_num_tasks )) {
458+ /* Last task globally: stop the timer and watchdog */
387459 watchdog_disable (core );
388460
389461 k_timer_stop (zephyr_domain -> timer );
390462 k_timer_user_data_set (zephyr_domain -> timer , NULL );
391463 }
392464
393- zephyr_domain -> domain_thread [core ].handler = NULL ;
465+ dt -> handler = NULL ;
466+ dt -> arg = NULL ;
394467
395468 k_mutex_unlock (domain -> lock );
396469
397- tr_info (& ll_tr , "domain->type %d domain->clk %d" ,
398- domain -> type , domain -> clk );
399-
400- /* Thread not removed here, only the timer is stopped.
401- * Thread object cleanup would require k_thread_abort() which cannot
402- * be safely called from this context. The thread remains allocated
403- * but dormant until next registration or system shutdown.
404- */
405-
406- tr_dbg (& ll_tr , "exit" );
470+ if (dt -> ll_thread ) {
471+ k_thread_abort (dt -> ll_thread );
472+ k_object_free (dt -> ll_thread );
473+ dt -> ll_thread = NULL ;
474+ }
407475
408- return 0 ;
476+ tr_info ( & ll_tr , "thread_free done, core %d" , core ) ;
409477}
410478
411479struct k_thread * zephyr_domain_thread_tid (struct ll_schedule_domain * domain )
@@ -450,6 +518,8 @@ APP_TASK_DATA static const struct ll_schedule_domain_ops zephyr_domain_ops = {
450518#ifdef CONFIG_SOF_USERSPACE_LL
451519 .domain_register = zephyr_domain_register_user ,
452520 .domain_unregister = zephyr_domain_unregister_user ,
521+ .domain_thread_init = zephyr_domain_thread_init ,
522+ .domain_thread_free = zephyr_domain_thread_free ,
453523#else
454524 .domain_register = zephyr_domain_register ,
455525 .domain_unregister = zephyr_domain_unregister ,
0 commit comments