From 31277bdac0006b90a1f81244939f4377c14d26cf Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Thu, 10 Sep 2026 12:23:41 +0300 Subject: [PATCH] ipc: userspace: fix IPC serialization with multiple cores Fix a race in IPC serialization with multi-core. One sequence observed: - MOD_SET_DX IPC to power up core 1 - IPC reply to host - CREATE_PIPELINE IPC (routed via core 0 to core 1) - core 1 IPC thread starts, signals ipc_user->sem semaphore - core 0 does NOT wait for thread as ipc_user->init_needed is set late - ipc_user->sem signal for thread start is handled as indication that IPC is handled (this is wrong) - IPC reply to host (before CREATE_PIPELINE is handled) - INIT_INSTANCE IPC (routed via core 0 to core 1) - core 0 sees init_needed, but it is already signaled so execution continues -> DSP panic as IPC mailbox is modified while still in use Additional complication is that there is no hard requirement for host to send an IPC destinated to a particular core x, just after MOD_SET_DX was sent to power up this specific core. This is the normal sequence, but FW needs to at least gracefully handle alternative sequences. Fix the serialization issue by moving IPC thread startup synchronization to MOD_SET_DX handling. When a secondary core is booted up the first time after last primary core boot, additional setup steps are done. Reset "init_needed[]" after each primary core boot, and make MOD_SET_DX synchronous when a new secondary core is booted up, not sending a response back to host until the secondary core is booted up and the one-time initialization is done. Note that after this, secondary cores may be powered down and up many times, but the initialization (and related synchronization) is no longer needed. With these changes, there is no longer need to synchronize with secondary core when forwarding IPC messages (in ipc_user_forward_cmd()). It is now guaranteed the target core is running and set up correctly. If any failures happen, these are reported already at MOD_SET_DX. Signed-off-by: Kai Vehmanen --- src/ipc/ipc-common.c | 19 ++++++------------- src/ipc/ipc4/handler-kernel.c | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/ipc/ipc-common.c b/src/ipc/ipc-common.c index d05895a3f13a..831ba9e8d47d 100644 --- a/src/ipc/ipc-common.c +++ b/src/ipc/ipc-common.c @@ -437,15 +437,6 @@ int ipc_user_forward_cmd(uint32_t primary, uint32_t extension, unsigned int core pdata->ipc_msg_ext = extension; pdata->ipc = ipc; - /* - * Forwarding the first IPC to this core, wait for its userspace IPC - * thread to start - */ - if (pdata->init_needed[core]) { - pdata->init_needed[core] = false; - k_sem_take(pdata->sem, K_FOREVER); - } - /* Prevent host completion until user thread finishes */ key = k_spin_lock(&ipc->lock); ipc->task_mask |= IPC_TASK_IN_THREAD; @@ -598,9 +589,6 @@ __cold int ipc_user_init_secondary(unsigned int core) } k_thread_access_grant(ipc_user->thread[core], ipc_user->audio_thread[core]); - ipc_user->init_needed[core] = true; - - /* Wait for user thread startup — consumes the initial k_sem_give from thread */ return 0; } @@ -619,7 +607,7 @@ __cold static void ipc_user_init(void) struct ipc_user *ipc_user = sof_heap_alloc(sof_sys_user_heap_get(), SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*ipc_user), 0); - int ret; + int ret, core; if (!ipc_user) { LOG_ERR("user IPC pdata alloc failed"); @@ -628,6 +616,11 @@ __cold static void ipc_user_init(void) assert_can_be_cold(); + for (core = 0; core < CONFIG_CORE_COUNT; core++) { + if (core != PLATFORM_PRIMARY_CORE_ID) + ipc_user->init_needed[core] = true; + } + ipc_user->sem = k_object_alloc(K_OBJ_SEM); if (!ipc_user->sem) { LOG_ERR("user IPC sem alloc failed"); diff --git a/src/ipc/ipc4/handler-kernel.c b/src/ipc/ipc4/handler-kernel.c index 03c2e6c81e86..041b0fba14f2 100644 --- a/src/ipc/ipc4/handler-kernel.c +++ b/src/ipc/ipc4/handler-kernel.c @@ -328,6 +328,23 @@ __cold static int ipc4_module_process_d0ix(struct ipc4_message_request *ipc4) return 0; } +/* block until core has powered-up (in user-ll builds) */ +__cold static void ipc_sec_core_sync_boot(uint32_t core_id) +{ +#ifdef CONFIG_SOF_USERSPACE_LL + struct ipc *ipc = ipc_get(); + struct ipc_user *ipc_user = ipc->ipc_user_pdata; + + assert(core_id != PLATFORM_PRIMARY_CORE_ID); + + if (ipc_user->init_needed[core_id]) { + /* wait for IPC thread (ipc_user_thread_fn()) */ + k_sem_take(ipc_user->sem, K_FOREVER); + ipc_user->init_needed[core_id] = false; + } +#endif +} + /* enable/disable cores according to the state mask */ __cold static int ipc4_module_process_dx(struct ipc4_message_request *ipc4) { @@ -385,6 +402,7 @@ __cold static int ipc4_module_process_dx(struct ipc4_message_request *ipc4) ipc_cmd_err(&ipc_tr, "failed to enable core %d", core_id); return IPC4_FAILURE; } + ipc_sec_core_sync_boot(core_id); } else { cpu_disable_core(core_id); if (cpu_is_core_enabled(core_id)) {