From 63998ffb8f0bd26703347b37bc825ffc5c180386 Mon Sep 17 00:00:00 2001 From: Serhiy Katsyuba Date: Tue, 12 Mar 2024 18:08:03 +0100 Subject: [PATCH] kpb: Fix draining task and LL conflict KPB draining task is executed by low priority preemptible EDF thread. The task accesses KPB sink buffer and calls comp_copy() for component connected to sink. If LL thread preempts draining task that could result in broken state of sink buffer or component connected to sink. This fix prevents LL from preempting draining task in bad moment. Signed-off-by: Serhiy Katsyuba --- src/audio/kpb.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/audio/kpb.c b/src/audio/kpb.c index 6d0507a033d1..1120a74eb07f 100644 --- a/src/audio/kpb.c +++ b/src/audio/kpb.c @@ -1742,13 +1742,22 @@ static enum task_state kpb_draining_task(void *arg) uint64_t current_time; size_t period_bytes = 0; size_t period_bytes_limit = draining_data->pb_limit; - size_t period_copy_start = sof_cycle_get_64(); + size_t period_copy_start; size_t time_taken; size_t *rt_stream_update = &draining_data->buffered_while_draining; struct comp_data *kpb = comp_get_drvdata(draining_data->dev); bool sync_mode_on = draining_data->sync_mode_on; bool pm_is_active; + /* + * WORKAROUND: The code below accesses KPB sink buffer and calls comp_copy() on + * component connected to sink. EDF task thread has preemptable low priority and + * so can be preempted by LL thread. This could result in broken state of sink buffer + * or component connected to sink. + * Hence k_sched_lock() is used temporarly to block LL from preempting EDF task thread. + */ + k_sched_lock(); + comp_cl_info(&comp_kpb, "kpb_draining_task(), start."); pm_is_active = pm_runtime_is_active(PM_RUNTIME_DSP, PLATFORM_PRIMARY_CORE_ID); @@ -1760,8 +1769,17 @@ static enum task_state kpb_draining_task(void *arg) kpb_change_state(kpb, KPB_STATE_DRAINING); draining_time_start = sof_cycle_get_64(); + period_copy_start = draining_time_start; while (drain_req > 0) { + /* + * Draining task usually runs for quite a lot of time (could be few seconds). + * LL should not be blocked for such a long time. + */ + k_sched_unlock(); + k_yield(); + k_sched_lock(); + /* Have we received reset request? */ if (kpb->state == KPB_STATE_RESETTING) { kpb_change_state(kpb, KPB_STATE_RESET_FINISHING); @@ -1868,6 +1886,9 @@ static enum task_state kpb_draining_task(void *arg) comp_cl_info(&comp_kpb, "KPB: kpb_draining_task(), done. %u drained in > %u ms", drained, UINT_MAX); + /* Restore original EDF thread priority */ + k_sched_unlock(); + return SOF_TASK_STATE_COMPLETED; }