From 229f1cc3c0f0eb4396ba79e7b190999f9a836510 Mon Sep 17 00:00:00 2001 From: Janusz Jankowski Date: Tue, 4 Aug 2020 10:38:31 +0200 Subject: [PATCH 1/3] schedule: edf: cycles budget Add possibility to set budget for EDF tasks. It's needed to ensure that some long running task will not stall other tasks. Signed-off-by: Janusz Jankowski --- src/include/sof/schedule/edf_schedule.h | 10 +++++ src/schedule/edf_schedule.c | 54 ++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/include/sof/schedule/edf_schedule.h b/src/include/sof/schedule/edf_schedule.h index 5bf552a06969..04dfdd7be663 100644 --- a/src/include/sof/schedule/edf_schedule.h +++ b/src/include/sof/schedule/edf_schedule.h @@ -12,6 +12,7 @@ #include #include #include +#include #define edf_sch_set_pdata(task, data) \ (task->priv_data = data) @@ -20,6 +21,10 @@ struct edf_task_pdata { void *ctx; + uint32_t budget; /**< cycles per systick */ + uint64_t last_systick; /**< last systick in which budget + * was granted to task + */ }; int scheduler_init_edf(void); @@ -28,4 +33,9 @@ int schedule_task_init_edf(struct task *task, uint32_t uid, const struct task_ops *ops, void *data, uint16_t core, uint32_t flags); +int schedule_task_init_edf_with_budget(struct task *task, uint32_t uid, + const struct task_ops *ops, + void *data, uint16_t core, + uint32_t flags, uint32_t cycles_budget); + #endif /* __SOF_SCHEDULE_EDF_SCHEDULE_H__ */ diff --git a/src/schedule/edf_schedule.c b/src/schedule/edf_schedule.c index 41cd4fc0853e..fcb4f6cdeb47 100644 --- a/src/schedule/edf_schedule.c +++ b/src/schedule/edf_schedule.c @@ -55,6 +55,26 @@ static void schedule_edf_task_run(struct task *task, void *data) } } +static inline uint64_t edf_eval_task_deadline(struct task *task, + uint64_t systick) +{ + struct edf_task_pdata *edf_pdata = edf_sch_get_pdata(task); + + if (!edf_pdata->budget) + return task_get_deadline(task); + + /* check if task was already executed in current systick */ + if (edf_pdata->last_systick != systick) { + edf_pdata->last_systick = systick; + return task_get_deadline(task); + } + + /* when there are no other tasks to run, + * task can be executed even if it used systick's budget, + */ + return SOF_TASK_DEADLINE_ALMOST_IDLE; +} + static void edf_scheduler_run(void *data) { struct edf_schedule_data *edf_sch = data; @@ -64,9 +84,20 @@ static void edf_scheduler_run(void *data) struct task *task; uint64_t deadline; uint32_t flags; + uint64_t systick; tr_dbg(&edf_tr, "edf_scheduler_run()"); + timer_disable(cpu_timer_get(), edf_sch, cpu_get_id()); + + /* budget is given per systick, for example if systick is configured + * as 1ms and budget is 8000 cycles, then task will be able to use + * up to 8MCPS (or more if there are no other tasks) + */ + systick = platform_timer_get(timer_get()) / + (clock_ms_to_ticks(PLATFORM_DEFAULT_CLOCK, 1) + * (CONFIG_SYSTICK_PERIOD / 1000)); + irq_local_disable(flags); /* find next task to run */ @@ -77,7 +108,7 @@ static void edf_scheduler_run(void *data) task->state != SOF_TASK_STATE_RUNNING) continue; - deadline = task_get_deadline(task); + deadline = edf_eval_task_deadline(task, systick); if (deadline == SOF_TASK_DEADLINE_NOW) { /* task needs to be scheduled ASAP */ @@ -134,6 +165,15 @@ static int schedule_edf_task(void *data, struct task *task, uint64_t start, int schedule_task_init_edf(struct task *task, uint32_t uid, const struct task_ops *ops, void *data, uint16_t core, uint32_t flags) +{ + return schedule_task_init_edf_with_budget(task, uid, ops, data, + core, flags, 0); +} + +int schedule_task_init_edf_with_budget(struct task *task, uint32_t uid, + const struct task_ops *ops, + void *data, uint16_t core, + uint32_t flags, uint32_t cycles_budget) { struct edf_task_pdata *edf_pdata = NULL; int ret = 0; @@ -182,6 +222,7 @@ int schedule_task_init_edf(struct task *task, uint32_t uid, static int schedule_edf_task_running(void *data, struct task *task) { + struct edf_schedule_data *edf_sch = data; struct edf_task_pdata *edf_pdata = edf_sch_get_pdata(task); uint32_t flags; @@ -192,6 +233,13 @@ static int schedule_edf_task_running(void *data, struct task *task) task_context_set(edf_pdata->ctx); task->state = SOF_TASK_STATE_RUNNING; + /* if budget is not set then it's not budgeted task */ + if (edf_pdata->budget) { + timer_set(cpu_timer_get(), timer_get_system(cpu_timer_get()) + + edf_pdata->budget); + timer_enable(cpu_timer_get(), edf_sch, cpu_get_id()); + } + irq_local_enable(flags); return 0; @@ -278,6 +326,8 @@ int scheduler_init_edf(void) interrupt_register(edf_sch->irq, edf_scheduler_run, edf_sch); interrupt_enable(edf_sch->irq, edf_sch); + timer_register(cpu_timer_get(), edf_scheduler_run, edf_sch); + return 0; } @@ -288,6 +338,8 @@ static void scheduler_free_edf(void *data) irq_local_disable(flags); + timer_unregister(cpu_timer_get(), edf_sch); + /* disable and unregister EDF scheduler interrupt */ interrupt_disable(edf_sch->irq, edf_sch); interrupt_unregister(edf_sch->irq, edf_sch); From 20a6d02d64b3ad9d6c10c376ea698080ac03089b Mon Sep 17 00:00:00 2001 From: Janusz Jankowski Date: Tue, 4 Aug 2020 10:39:50 +0200 Subject: [PATCH 2/3] drivers: cavs: set budget for IPC task IPC task may be time consuming and has high priority, what may make other tasks stall. To prevent this it should have limited cycles budget per systick. Signed-off-by: Janusz Jankowski --- src/drivers/intel/Kconfig | 7 +++++++ src/drivers/intel/cavs/ipc.c | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/drivers/intel/Kconfig b/src/drivers/intel/Kconfig index aad635124ae9..ffbb172130bd 100644 --- a/src/drivers/intel/Kconfig +++ b/src/drivers/intel/Kconfig @@ -47,6 +47,13 @@ config INTEL_DMIC Select this to enable Intel DMIC driver. The DMIC driver provides as DAI the SoC direct attach digital microphones interface. +config INTEL_CAVS_IPC_TASK_BUDGET + int "Intel cycles budget for IPC task" + depends on CAVS + default 8000 + help + Cycles budget for IPC task per systick (see config SYSTICK). + if INTEL_DMIC choice diff --git a/src/drivers/intel/cavs/ipc.c b/src/drivers/intel/cavs/ipc.c index 7d335fce6a73..a689bcfefd7f 100644 --- a/src/drivers/intel/cavs/ipc.c +++ b/src/drivers/intel/cavs/ipc.c @@ -296,8 +296,10 @@ int platform_ipc_init(struct ipc *ipc) ipc_set_drvdata(ipc, NULL); /* schedule */ - schedule_task_init_edf(&ipc->ipc_task, SOF_UUID(ipc_task_uuid), - &ipc_task_ops, ipc, 0, 0); + schedule_task_init_edf_with_budget(&ipc->ipc_task, + SOF_UUID(ipc_task_uuid), + &ipc_task_ops, ipc, 0, 0, + CONFIG_INTEL_CAVS_IPC_TASK_BUDGET); /* configure interrupt */ irq = interrupt_get_irq(PLATFORM_IPC_INTERRUPT, From 980163e422370b7c05c856c357fdeaa995fe94af Mon Sep 17 00:00:00 2001 From: Janusz Jankowski Date: Tue, 4 Aug 2020 10:40:39 +0200 Subject: [PATCH 3/3] idc: set budget for IDC task IDC task may be time consuming and has high priority, what may make other tasks stall. To prevent this it should have limited cycles budget per systick. Signed-off-by: Janusz Jankowski --- Kconfig | 7 +++++++ src/idc/idc.c | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Kconfig b/Kconfig index 9ea7ff644881..1378d255279b 100644 --- a/Kconfig +++ b/Kconfig @@ -127,6 +127,13 @@ config INTERRUPT_LEVEL_5 rsource "src/Kconfig" +config IDC_TASK_BUDGET + int "Cycles budget for IDC task" + depends on MULTICORE + default 8000 + help + Cycles budget for IDC task per systick (see config SYSTICK). + choice prompt "Optimization" default OPTIMIZE_FOR_PERFORMANCE diff --git a/src/idc/idc.c b/src/idc/idc.c index 5f65662e2e5d..363b58af1113 100644 --- a/src/idc/idc.c +++ b/src/idc/idc.c @@ -325,8 +325,10 @@ int idc_init(void) (*idc)->payload = cache_to_uncache((struct idc_payload *)payload); /* process task */ - schedule_task_init_edf(&(*idc)->idc_task, SOF_UUID(idc_cmd_task_uuid), - &ops, *idc, cpu_get_id(), 0); + schedule_task_init_edf_with_budget(&(*idc)->idc_task, + SOF_UUID(idc_cmd_task_uuid), + &ops, *idc, cpu_get_id(), 0, + CONFIG_IDC_TASK_BUDGET); return platform_idc_init(); }