Skip to content

Commit 0f8ce76

Browse files
committed
schedule: ll: enable multicore userspace
Make scheduling LL thread and synchronisation objects per-core and forward IPCs and scheduling events accordingly. Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
1 parent af12485 commit 0f8ce76

4 files changed

Lines changed: 193 additions & 83 deletions

File tree

src/include/sof/ipc/common.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ extern struct tr_ctx ipc_tr;
5656
#define IPC_TASK_POWERDOWN BIT(3)
5757

5858
struct ipc_user {
59-
struct k_thread *thread;
59+
struct k_thread *thread[CONFIG_CORE_COUNT];
6060
struct k_sem *sem;
61-
struct k_event *event;
61+
struct k_event *event[CONFIG_CORE_COUNT];
6262
/** @brief Copy of IPC4 message primary word forwarded to user thread */
6363
uint32_t ipc_msg_pri;
6464
/** @brief Copy of IPC4 message extension word forwarded to user thread */
@@ -72,9 +72,10 @@ struct ipc_user {
7272
/** @brief Reply TX data pointer from user thread (e.g. LARGE_CONFIG_GET result) */
7373
void *reply_tx_data;
7474
struct ipc *ipc;
75-
struct k_thread *audio_thread;
75+
struct k_thread *audio_thread[CONFIG_CORE_COUNT];
7676
/** @brief Original kernel driver pointer for restoring dev->drv after create */
7777
const struct comp_driver *init_drv;
78+
bool init_needed[CONFIG_CORE_COUNT];
7879
/**
7980
* @brief User-accessible copy of comp_driver + tr_ctx for create().
8081
*
@@ -353,7 +354,8 @@ struct ipc4_message_request;
353354
* @param ipc4 Pointer to the IPC4 message request
354355
* @return Result from user thread processing
355356
*/
356-
int ipc_user_forward_cmd(struct ipc4_message_request *ipc4);
357+
int ipc_user_forward_cmd(struct ipc4_message_request *ipc4, unsigned int core);
358+
int ipc_user_init_secondary(unsigned int core);
357359
#endif
358360

359361
#endif /* __SOF_DRIVERS_IPC_H__ */

src/init/init.c

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <rtos/init.h>
1414
#include <rtos/interrupt.h>
1515
#include <sof/init.h>
16+
#include <sof/ipc/common.h>
1617
#include <sof/lib/cpu.h>
1718
#include <sof/lib/cpu-clk-manager.h>
1819
#include <sof/lib/memory.h>
@@ -112,6 +113,7 @@ static inline int secondary_core_restore(void) { return 0; };
112113

113114
__cold int secondary_core_init(struct sof *sof)
114115
{
116+
unsigned int core = cpu_get_id();
115117
int err;
116118
struct ll_schedule_domain *dma_domain;
117119

@@ -134,24 +136,18 @@ __cold int secondary_core_init(struct sof *sof)
134136
if (dma_domain)
135137
scheduler_init_ll(dma_domain);
136138

139+
#if CONFIG_SOF_USERSPACE_LL
140+
err = ipc_user_init_secondary(core);
141+
if (err < 0)
142+
return err;
143+
#endif
144+
137145
#if CONFIG_ZEPHYR_DP_SCHEDULER
138146
err = scheduler_dp_init();
139147
if (err < 0)
140148
return err;
141149
#endif /* CONFIG_ZEPHYR_DP_SCHEDULER */
142150

143-
#if CONFIG_SOF_USERSPACE_LL
144-
/* Create domain thread for this secondary core's LL scheduler */
145-
{
146-
struct task *task = zephyr_ll_task_alloc();
147-
148-
schedule_task_init_ll(task, SOF_UUID(sec_core_init_uuid),
149-
SOF_SCHEDULE_LL_TIMER,
150-
0, NULL, NULL, cpu_get_id(), 0);
151-
scheduler_init_context(task);
152-
}
153-
#endif
154-
155151
/* initialize IDC mechanism */
156152
trace_point(TRACE_BOOT_PLATFORM_IDC);
157153
err = idc_init();
@@ -164,7 +160,7 @@ __cold int secondary_core_init(struct sof *sof)
164160
return err;
165161
#endif
166162
#if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL
167-
err = core_kcps_adjust(cpu_get_id(), SECONDARY_CORE_BASE_CPS_USAGE);
163+
err = core_kcps_adjust(core, SECONDARY_CORE_BASE_CPS_USAGE);
168164
if (err < 0)
169165
return err;
170166
#endif

src/ipc/ipc-common.c

Lines changed: 92 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,8 @@ void ipc_schedule_process(struct ipc *ipc)
394394
#define IPC_USER_EVENT_CMD BIT(0)
395395
#define IPC_USER_EVENT_STOP BIT(1)
396396

397-
static K_THREAD_STACK_DEFINE(ipc_user_stack, CONFIG_SOF_IPC_USER_THREAD_STACK_SIZE);
397+
static K_THREAD_STACK_ARRAY_DEFINE(ipc_user_stack, CONFIG_CORE_COUNT,
398+
CONFIG_SOF_IPC_USER_THREAD_STACK_SIZE);
398399

399400
/**
400401
* @brief Forward an IPC4 command to the user-space thread.
@@ -408,7 +409,7 @@ static K_THREAD_STACK_DEFINE(ipc_user_stack, CONFIG_SOF_IPC_USER_THREAD_STACK_SI
408409
* @param ipc4 Pointer to the IPC4 message request
409410
* @return Result from user thread processing
410411
*/
411-
int ipc_user_forward_cmd(struct ipc4_message_request *ipc4)
412+
int ipc_user_forward_cmd(struct ipc4_message_request *ipc4, unsigned int core)
412413
{
413414
struct ipc *ipc = ipc_get();
414415
struct ipc_user *pdata = ipc->ipc_user_pdata;
@@ -422,13 +423,22 @@ int ipc_user_forward_cmd(struct ipc4_message_request *ipc4)
422423
pdata->ipc_msg_ext = ipc4->extension.dat;
423424
pdata->ipc = ipc;
424425

426+
/*
427+
* Forwarding the first IPC to this core, wait for its userspace IPC
428+
* thread to start
429+
*/
430+
if (pdata->init_needed[core]) {
431+
pdata->init_needed[core] = false;
432+
k_sem_take(pdata->sem, K_FOREVER);
433+
}
434+
425435
/* Prevent host completion until user thread finishes */
426436
key = k_spin_lock(&ipc->lock);
427437
ipc->task_mask |= IPC_TASK_IN_THREAD;
428438
k_spin_unlock(&ipc->lock, key);
429439

430440
/* Wake the user thread */
431-
k_event_set(pdata->event, IPC_USER_EVENT_CMD);
441+
k_event_set(pdata->event[core], IPC_USER_EVENT_CMD);
432442

433443
/* Wait for user thread to complete */
434444
ret = k_sem_take(pdata->sem, K_MSEC(100));
@@ -453,8 +463,8 @@ int ipc_user_forward_cmd(struct ipc4_message_request *ipc4)
453463
static void ipc_user_thread_fn(void *p1, void *p2, void *p3)
454464
{
455465
struct ipc_user *ipc_user = p1;
466+
unsigned int core = POINTER_TO_UINT(p2);
456467

457-
ARG_UNUSED(p2);
458468
ARG_UNUSED(p3);
459469

460470
__ASSERT(k_is_user_context(), "expected user context");
@@ -464,7 +474,7 @@ static void ipc_user_thread_fn(void *p1, void *p2, void *p3)
464474
LOG_INF("IPC user-space thread started");
465475

466476
for (;;) {
467-
uint32_t mask = k_event_wait_safe(ipc_user->event,
477+
uint32_t mask = k_event_wait_safe(ipc_user->event[core],
468478
IPC_USER_EVENT_CMD | IPC_USER_EVENT_STOP,
469479
false, K_MSEC(5000));
470480

@@ -616,55 +626,92 @@ static void ipc_user_thread_fn(void *p1, void *p2, void *p3)
616626
}
617627
}
618628

619-
__cold static int ipc_user_init_thread(struct ipc_user *ipc_user)
629+
__cold static int ipc_user_init_thread(struct ipc_user *ipc_user, unsigned int core)
620630
{
621631
char thread_name[] = "ll_user0";
622632
int ret;
623633

624634
assert_can_be_cold();
625635

626636
/* Allocate kernel objects for the user-space thread */
627-
ipc_user->event = k_object_alloc(K_OBJ_EVENT);
628-
if (!ipc_user->event) {
637+
ipc_user->event[core] = k_object_alloc(K_OBJ_EVENT);
638+
if (!ipc_user->event[core]) {
629639
LOG_ERR("user IPC event alloc failed");
630640
return -ENOMEM;
631641
}
632-
k_event_init(ipc_user->event);
642+
k_event_init(ipc_user->event[core]);
633643

634-
ipc_user->thread = k_object_alloc(K_OBJ_THREAD);
635-
if (!ipc_user->thread) {
644+
ipc_user->thread[core] = k_object_alloc(K_OBJ_THREAD);
645+
if (!ipc_user->thread[core]) {
636646
LOG_ERR("user IPC thread alloc failed");
637647
ret = -ENOMEM;
638648
goto e_event;
639649
}
640650

641-
k_thread_create(ipc_user->thread, ipc_user_stack,
651+
k_thread_create(ipc_user->thread[core], ipc_user_stack[core],
642652
CONFIG_SOF_IPC_USER_THREAD_STACK_SIZE,
643-
ipc_user_thread_fn, ipc_user, NULL, NULL,
653+
ipc_user_thread_fn, ipc_user, UINT_TO_POINTER(core), NULL,
644654
-1, K_USER, K_FOREVER);
645655

646-
k_thread_cpu_pin(ipc_user->thread, PLATFORM_PRIMARY_CORE_ID);
647-
k_thread_name_set(ipc_user->thread, thread_name);
656+
k_thread_cpu_pin(ipc_user->thread[core], core);
657+
thread_name[sizeof(thread_name) - 2] = '0' + core;
658+
k_thread_name_set(ipc_user->thread[core], thread_name);
648659

649660
/*
650661
* Each userspace IPC thread must be able to wait on its private event
651662
* and signal completion on the primary core semaphore
652663
*/
653-
k_thread_access_grant(ipc_user->thread, ipc_user->sem, ipc_user->event);
654-
user_grant_dai_access_all(ipc_user->thread);
655-
user_grant_dma_access_all(ipc_user->thread);
656-
pipeline_posn_grant_access(ipc_user->thread);
657-
k_mem_domain_add_thread(zephyr_ll_mem_domain(), ipc_user->thread);
658-
user_ll_grant_access(ipc_user->thread);
664+
k_thread_access_grant(ipc_user->thread[core], ipc_user->sem, ipc_user->event[core]);
665+
user_grant_dai_access_all(ipc_user->thread[core]);
666+
user_grant_dma_access_all(ipc_user->thread[core]);
667+
pipeline_posn_grant_access(ipc_user->thread[core]);
668+
k_mem_domain_add_thread(zephyr_ll_mem_domain(), ipc_user->thread[core]);
669+
user_ll_grant_access(ipc_user->thread[core]);
659670

660671
return 0;
661672

662673
e_event:
663-
k_object_free(ipc_user->event);
674+
k_object_free(ipc_user->event[core]);
664675

665676
return ret;
666677
}
667678

679+
__cold int ipc_user_init_secondary(unsigned int core)
680+
{
681+
struct ipc *ipc = ipc_get();
682+
struct ipc_user *ipc_user = ipc->ipc_user_pdata;
683+
int ret = ipc_user_init_thread(ipc_user, core);
684+
685+
if (ret < 0)
686+
return ret;
687+
688+
assert_can_be_cold();
689+
690+
k_thread_start(ipc_user->thread[core]);
691+
692+
struct task *task = zephyr_ll_task_alloc();
693+
694+
if (!task) {
695+
LOG_ERR("user LL task allocation failed");
696+
k_panic();
697+
}
698+
699+
schedule_task_init_ll(task, SOF_UUID(ipc_uuid), SOF_SCHEDULE_LL_TIMER,
700+
0, NULL, NULL, core, 0);
701+
702+
ipc_user->audio_thread[core] = scheduler_init_context(task);
703+
if (!ipc_user->audio_thread[core]) {
704+
LOG_ERR("user LL thread init failed");
705+
k_panic();
706+
}
707+
708+
k_thread_access_grant(ipc_user->thread[core], ipc_user->audio_thread[core]);
709+
ipc_user->init_needed[core] = true;
710+
711+
/* Wait for user thread startup — consumes the initial k_sem_give from thread */
712+
return 0;
713+
}
714+
668715
/* Primary core only */
669716
__cold static int ipc_user_init(void)
670717
{
@@ -674,6 +721,8 @@ __cold static int ipc_user_init(void)
674721
sizeof(*ipc_user), 0);
675722
int ret;
676723

724+
assert_can_be_cold();
725+
677726
ipc_user->sem = k_object_alloc(K_OBJ_SEM);
678727
if (!ipc_user->sem) {
679728
LOG_ERR("user IPC sem alloc failed");
@@ -713,40 +762,51 @@ __cold static int ipc_user_init(void)
713762
ret = k_mem_domain_add_partition(zephyr_ll_mem_domain(),
714763
&cold_part);
715764
if (ret < 0)
716-
LOG_WRN("cold rodata partition %#zx @ %#lx add failed: %d", cold_part.size,
717-
cold_part.start, ret);
765+
LOG_WRN("cold rodata partition %#zx @ %#lx add failed: %d",
766+
cold_part.size, cold_part.start, ret);
718767
}
719768
}
720769
#endif
721770

722771
k_sem_init(ipc_user->sem, 0, 1);
723772

724-
ret = ipc_user_init_thread(ipc_user);
773+
ret = ipc_user_init_thread(ipc_user, PLATFORM_PRIMARY_CORE_ID);
725774
if (ret < 0) {
726775
LOG_ERR("user IPC thread initialization failed");
727776
k_panic();
728777
}
729778

730-
user_access_to_mailbox(zephyr_ll_mem_domain(), ipc_user->thread);
779+
user_access_to_mailbox(zephyr_ll_mem_domain(), ipc_user->thread[PLATFORM_PRIMARY_CORE_ID]);
731780

732781
/* Store references in ipc struct so kernel handler can forward commands */
733782
ipc->ipc_user_pdata = ipc_user;
734783

735-
k_thread_start(ipc_user->thread);
736-
737784
struct task *task = zephyr_ll_task_alloc();
785+
786+
if (!task) {
787+
LOG_ERR("task allocation failed");
788+
k_panic();
789+
}
790+
738791
schedule_task_init_ll(task, SOF_UUID(ipc_uuid), SOF_SCHEDULE_LL_TIMER,
739-
0, NULL, NULL, cpu_get_id(), 0);
740-
ipc_user->audio_thread = scheduler_init_context(task);
792+
0, NULL, NULL, PLATFORM_PRIMARY_CORE_ID, 0);
793+
ipc_user->audio_thread[PLATFORM_PRIMARY_CORE_ID] = scheduler_init_context(task);
794+
if (!ipc_user->audio_thread[PLATFORM_PRIMARY_CORE_ID]) {
795+
LOG_ERR("user LL thread init failed");
796+
k_panic();
797+
}
741798

742799
/* Grant ipc_user thread permission on the audio thread object.
743800
* Needed so user-space dai_common_new() can call
744801
* k_thread_access_grant(audio_thread, dai_mutex) from user context.
745802
*/
746-
k_thread_access_grant(ipc_user->thread, ipc_user->audio_thread);
803+
k_thread_access_grant(ipc_user->thread[PLATFORM_PRIMARY_CORE_ID],
804+
ipc_user->audio_thread[PLATFORM_PRIMARY_CORE_ID]);
805+
806+
k_thread_start(ipc_user->thread[PLATFORM_PRIMARY_CORE_ID]);
747807

748808
/* Wait for user thread startup — consumes the initial k_sem_give from thread */
749-
k_sem_take(ipc->ipc_user_pdata->sem, K_FOREVER);
809+
k_sem_take(ipc_user->sem, K_FOREVER);
750810

751811
return 0;
752812
}

0 commit comments

Comments
 (0)