From aaa83c489fded4c5565cc82d1a50240ef437bfa6 Mon Sep 17 00:00:00 2001 From: Janusz Jankowski Date: Fri, 10 Jul 2020 13:53:18 +0200 Subject: [PATCH 1/4] xtensa: clock: change cpu timer to per-core timer Every core has it's own timer so cpu timer should be handled for each core independently. Signed-off-by: Janusz Jankowski --- src/include/sof/drivers/timer.h | 3 ++- src/include/sof/sof.h | 4 ++-- src/platform/baytrail/platform.c | 2 +- src/platform/haswell/platform.c | 2 +- src/platform/imx8/platform.c | 2 +- src/platform/imx8m/platform.c | 2 +- src/platform/intel/cavs/platform.c | 13 ++++++++----- 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/include/sof/drivers/timer.h b/src/include/sof/drivers/timer.h index 92370d48fd6e..41d458cc404b 100644 --- a/src/include/sof/drivers/timer.h +++ b/src/include/sof/drivers/timer.h @@ -9,6 +9,7 @@ #define __SOF_DRIVERS_TIMER_H__ #include +#include #include #include @@ -33,7 +34,7 @@ static inline struct timer *timer_get(void) static inline struct timer *cpu_timer_get(void) { - return sof_get()->cpu_timer; + return &(sof_get()->cpu_timers[cpu_get_id()]); } static inline int64_t timer_set(struct timer *timer, uint64_t ticks) diff --git a/src/include/sof/sof.h b/src/include/sof/sof.h index 9d9bedd38f88..b64f6b4b6b4f 100644 --- a/src/include/sof/sof.h +++ b/src/include/sof/sof.h @@ -60,8 +60,8 @@ struct sof { /* default platform timer */ struct timer *platform_timer; - /* cpu (arch) timer */ - struct timer *cpu_timer; + /* cpu (arch) timers - 1 per core */ + struct timer *cpu_timers; /* timer domain for driving timer LL scheduler */ struct ll_schedule_domain *platform_timer_domain; diff --git a/src/platform/baytrail/platform.c b/src/platform/baytrail/platform.c index 725c24bda3f8..9ea6cc24daa6 100644 --- a/src/platform/baytrail/platform.c +++ b/src/platform/baytrail/platform.c @@ -199,7 +199,7 @@ int platform_init(struct sof *sof) int ret; sof->platform_timer = &timer; - sof->cpu_timer = &timer; + sof->cpu_timers = &timer; /* clear mailbox for early trace and debug */ trace_point(TRACE_BOOT_PLATFORM_MBOX); diff --git a/src/platform/haswell/platform.c b/src/platform/haswell/platform.c index dca7a66ca9ca..e7e00b01ffda 100644 --- a/src/platform/haswell/platform.c +++ b/src/platform/haswell/platform.c @@ -186,7 +186,7 @@ int platform_init(struct sof *sof) int ret; sof->platform_timer = &timer; - sof->cpu_timer = &timer; + sof->cpu_timers = &timer; /* clear mailbox for early trace and debug */ trace_point(TRACE_BOOT_PLATFORM_MBOX); diff --git a/src/platform/imx8/platform.c b/src/platform/imx8/platform.c index 6710241af565..f555f0ec6469 100644 --- a/src/platform/imx8/platform.c +++ b/src/platform/imx8/platform.c @@ -168,7 +168,7 @@ int platform_init(struct sof *sof) int ret; sof->platform_timer = &timer; - sof->cpu_timer = &timer; + sof->cpu_timers = &timer; platform_interrupt_init(); platform_clock_init(sof); diff --git a/src/platform/imx8m/platform.c b/src/platform/imx8m/platform.c index f415fdb16964..37a690efae7f 100644 --- a/src/platform/imx8m/platform.c +++ b/src/platform/imx8m/platform.c @@ -167,7 +167,7 @@ int platform_init(struct sof *sof) int ret; sof->platform_timer = &timer; - sof->cpu_timer = &timer; + sof->cpu_timers = &timer; platform_interrupt_init(); platform_clock_init(sof); diff --git a/src/platform/intel/cavs/platform.c b/src/platform/intel/cavs/platform.c index cc16c4252153..f03b573e0d6c 100644 --- a/src/platform/intel/cavs/platform.c +++ b/src/platform/intel/cavs/platform.c @@ -265,10 +265,7 @@ SHARED_DATA struct timer timer = { .irq_name = irq_name_level2, }; -SHARED_DATA struct timer arch_timer = { - .id = TIMER1, /* internal timer */ - .irq = IRQ_NUM_TIMER2, -}; +SHARED_DATA struct timer arch_timers[PLATFORM_CORE_COUNT]; #if CONFIG_DW_SPI @@ -363,7 +360,13 @@ int platform_init(struct sof *sof) int i; sof->platform_timer = cache_to_uncache(&timer); - sof->cpu_timer = cache_to_uncache(&arch_timer); + sof->cpu_timers = (struct timer *)cache_to_uncache(&arch_timers); + + for (i = 0; i < PLATFORM_CORE_COUNT; i++) + sof->cpu_timers[i] = (struct timer) { + .id = TIMER1, /* internal timer */ + .irq = IRQ_NUM_TIMER2, + }; /* Turn off memory for all unused cores */ for (i = 0; i < PLATFORM_CORE_COUNT; i++) From c79338041a370f40baa8cb289fd6f48dcb1c1141 Mon Sep 17 00:00:00 2001 From: Janusz Jankowski Date: Fri, 5 Jun 2020 20:33:47 +0200 Subject: [PATCH 2/4] 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 | 56 ++++++++++++++++++++++++- 2 files changed, 65 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 8d90c5a3845e..0e0c792a4dad 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 */ @@ -143,6 +174,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; @@ -174,6 +214,8 @@ int schedule_task_init_edf(struct task *task, uint32_t uid, task->core, NULL, 0) < 0) goto error; + edf_pdata->budget = cycles_budget; + /* flush for slave core */ if (cpu_is_slave(task->core)) dcache_writeback_invalidate_region(edf_pdata, @@ -191,6 +233,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; @@ -201,6 +244,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; @@ -287,6 +337,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; } @@ -297,6 +349,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 7caf7bc3e273ac62a990b848870e4eb5801a2cac Mon Sep 17 00:00:00 2001 From: Janusz Jankowski Date: Tue, 16 Jun 2020 12:55:00 +0200 Subject: [PATCH 3/4] 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 8446b955a815..b9ef05732fd2 100644 --- a/src/drivers/intel/cavs/ipc.c +++ b/src/drivers/intel/cavs/ipc.c @@ -295,8 +295,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 8644d7feaf16d5c088b09bdb5a27a5bee6fc6b91 Mon Sep 17 00:00:00 2001 From: Janusz Jankowski Date: Mon, 13 Jul 2020 15:50:05 +0200 Subject: [PATCH 4/4] 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 df0098aa4190..dd16262a8ec4 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 SMP + 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(); }