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; }