From 0c8adf7ecd0f6a08b57d5657c3721af584b9edc4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 9 Oct 2026 21:56:45 +0100 Subject: [PATCH 1/5] schedule: zephyr_dp_schedule: use a mutex for the per-core DP lock The DP lock is taken by the LL thread on every tick in scheduler_dp_ll_tick() and by each DP thread after every run. It was a k_sem, which has no priority inheritance: when a DP thread holding it was preempted by other DP threads, the cooperative high priority LL thread blocked until the holder got CPU time again. With several busy DP modules (3 MFCC + 3 MWW on cAVS 2.5) single LL ticks took up to ~40 ms, overrunning the DMIC DMAs of unrelated streams. With a k_mutex the holder inherits the LL priority: LL max drops from ~1.5M to <30k timer cycles, with no LL overruns or DMA xruns. Co-developed-by: Liam Girdwood Signed-off-by: Liam Girdwood Signed-off-by: Peter Ujfalusi --- src/schedule/zephyr_dp_schedule.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/schedule/zephyr_dp_schedule.c b/src/schedule/zephyr_dp_schedule.c index 89a830eb14a6..9246180d7749 100644 --- a/src/schedule/zephyr_dp_schedule.c +++ b/src/schedule/zephyr_dp_schedule.c @@ -36,7 +36,8 @@ SOF_DEFINE_REG_UUID(dp_sched); /* unused with Zephyr, generates no output */ DECLARE_TR_CTX(dp_tr, SOF_UUID(dp_sched_uuid), LOG_LEVEL_INFO); -#define DP_LOCK_INIT(i, _) Z_SEM_INITIALIZER(dp_lock[i], 1, 1) +/* k_mutex, not k_sem: priority inheritance keeps a preempted DP holder from stalling LL */ +#define DP_LOCK_INIT(i, _) Z_MUTEX_INITIALIZER(dp_lock[i]) #define DP_LOCK_INIT_LIST LISTIFY(CONFIG_MP_MAX_NUM_CPUS, DP_LOCK_INIT, (,)) /* User threads don't need access to this array. Access is performed from @@ -44,7 +45,7 @@ DECLARE_TR_CTX(dp_tr, SOF_UUID(dp_sched_uuid), LOG_LEVEL_INFO); * to be qualified as initialized by the gen_kobject_list.py script. */ static -STRUCT_SECTION_ITERABLE_ARRAY(k_sem, dp_lock, CONFIG_MP_MAX_NUM_CPUS) = { DP_LOCK_INIT_LIST }; +STRUCT_SECTION_ITERABLE_ARRAY(k_mutex, dp_lock, CONFIG_MP_MAX_NUM_CPUS) = { DP_LOCK_INIT_LIST }; /* Each per-core instance of DP scheduler has separate structures; hence, locks are per-core. * @@ -52,13 +53,13 @@ STRUCT_SECTION_ITERABLE_ARRAY(k_sem, dp_lock, CONFIG_MP_MAX_NUM_CPUS) = { DP_LOC */ unsigned int scheduler_dp_lock(uint16_t core) { - k_sem_take(&dp_lock[core], K_FOREVER); + k_mutex_lock(&dp_lock[core], K_FOREVER); return core; } void scheduler_dp_unlock(unsigned int key) { - k_sem_give(&dp_lock[key]); + k_mutex_unlock(&dp_lock[key]); } void scheduler_dp_grant(k_tid_t thread_id, uint16_t core) From 94b7380d06462e42135ed236a54813308b0f051e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 9 Oct 2026 21:56:45 +0100 Subject: [PATCH 2/5] platform: intel: cavs: initialize the DP scheduler CONFIG_ZEPHYR_DP_SCHEDULER can be enabled on cAVS, but only the ACE platform_init() registered the DP scheduler. Creating a DP module on cAVS then failed with "unable to bind scheduler to task (type 3)" and module init returned -ENODEV. Call scheduler_dp_init() after the LL scheduler is set up, as ACE does. Co-developed-by: Liam Girdwood Signed-off-by: Liam Girdwood Signed-off-by: Peter Ujfalusi --- src/platform/intel/cavs/platform.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/platform/intel/cavs/platform.c b/src/platform/intel/cavs/platform.c index 8a1a7c59c3b5..4c386ac7a55c 100644 --- a/src/platform/intel/cavs/platform.c +++ b/src/platform/intel/cavs/platform.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +123,12 @@ int platform_init(struct sof *sof) sof->platform_timer_domain = timer_domain_init(sof->platform_timer, 0); scheduler_init_ll(sof->platform_timer_domain); +#if CONFIG_ZEPHYR_DP_SCHEDULER + ret = scheduler_dp_init(); + if (ret < 0) + return ret; +#endif + /* init the system agent */ trace_point(TRACE_BOOT_PLATFORM_AGENT); sa_init(sof, CONFIG_SYSTICK_PERIOD); From 537c4103cbbdf571b7ede019798e32ad53bf3971 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 14 Oct 2026 17:26:17 +0100 Subject: [PATCH 3/5] schedule: edf_schedule: cancel delayed work on task free When freeing an EDF scheduled task, ensure any pending delayed work associated with the task is cancelled before marking the task free and clearing callbacks. This prevents delayed work from triggering on freed task data during pipeline resets or teardown. Co-developed-by: Peter Ujfalusi Signed-off-by: Peter Ujfalusi Signed-off-by: Liam Girdwood --- zephyr/edf_schedule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/zephyr/edf_schedule.c b/zephyr/edf_schedule.c index f00ad28b217a..c418b73e92e1 100644 --- a/zephyr/edf_schedule.c +++ b/zephyr/edf_schedule.c @@ -86,6 +86,7 @@ static int schedule_edf_task_running(void *data, struct task *task) static int schedule_edf_task_free(void *data, struct task *task) { + k_work_cancel_delayable(&task->z_delayed_work); task->state = SOF_TASK_STATE_FREE; task->ops.run = NULL; task->data = NULL; From 8cbec563d3c53757ef330c79913784b3e9b4ba22 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 14 Oct 2026 17:26:29 +0100 Subject: [PATCH 4/5] audio: copier: guard against NULL pointers in copier_dai_free In copier_dai_free(), guard cd->dd[i] against NULL before accessing cd->dd[i]->gain_data and calling dai_common_free(). This prevents a NULL pointer dereference if endpoint allocation failed during initialization or during teardown of partially configured copier instances. Also reset pointers to NULL after freeing. Co-developed-by: Peter Ujfalusi Signed-off-by: Peter Ujfalusi Signed-off-by: Liam Girdwood --- src/audio/copier/copier_dai.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/audio/copier/copier_dai.c b/src/audio/copier/copier_dai.c index 3861e010e902..64cbc6820dee 100644 --- a/src/audio/copier/copier_dai.c +++ b/src/audio/copier/copier_dai.c @@ -420,13 +420,21 @@ __cold void copier_dai_free(struct processing_module *mod) assert_can_be_cold(); for (int i = 0; i < cd->endpoint_num; i++) { - dai_common_free(cd->dd[i]); - mod_free(mod, cd->dd[i]->gain_data); - mod_free(mod, cd->dd[i]); + if (cd->dd[i]) { + if (cd->dd[i]->gain_data) { + mod_free(mod, cd->dd[i]->gain_data); + cd->dd[i]->gain_data = NULL; + } + dai_common_free(cd->dd[i]); + mod_free(mod, cd->dd[i]); + cd->dd[i] = NULL; + } } /* only dai have multi endpoint case */ - if (cd->multi_endpoint_buffer) + if (cd->multi_endpoint_buffer) { buffer_free(cd->multi_endpoint_buffer); + cd->multi_endpoint_buffer = NULL; + } } int copier_dai_prepare(struct comp_dev *dev, struct copier_data *cd) From 610f3188842c3fdca218805ba8f8ce942a92a011 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 14 Oct 2026 17:26:40 +0100 Subject: [PATCH 5/5] audio: pipeline: log component ID when comp_prepare fails When comp_prepare() returns a negative error code in pipeline_comp_prepare(), emit a pipe_err message including the component ID and error code. This greatly accelerates diagnosing topology and IPC parameter configuration failures during pipeline setup. Co-developed-by: Peter Ujfalusi Signed-off-by: Peter Ujfalusi Signed-off-by: Liam Girdwood --- src/audio/pipeline/pipeline-params.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/audio/pipeline/pipeline-params.c b/src/audio/pipeline/pipeline-params.c index ed32fc9e3b02..9c276d26fa65 100644 --- a/src/audio/pipeline/pipeline-params.c +++ b/src/audio/pipeline/pipeline-params.c @@ -283,8 +283,13 @@ static int pipeline_comp_prepare(struct comp_dev *current, } err = comp_prepare(current); - if (err < 0 || err == PPL_STATUS_PATH_STOP) + if (err < 0 || err == PPL_STATUS_PATH_STOP) { + if (err < 0) { + pipe_err(current->pipeline, "comp_prepare for comp 0x%x failed %d", + dev_comp_id(current), err); + } return err; + } return pipeline_for_each_comp(current, ctx, dir); }