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/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, 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(); } 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/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/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++) 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);