Repository navigation
cavs: budgeting for EDF tasks #3268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ | |
| #include <sof/trace/trace.h> | ||
| #include <user/trace.h> | ||
| #include <stdint.h> | ||
| #include <stdbool.h> | ||
|
|
||
| #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 */ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also explain that bugdet can be zero, and the difference in behavior between zero and non-zero values. |
||
| 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__ */ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe clarify with a comment in the structure definition that a budget of zero will result in a specific behavior. It's also not clear to me if this is a feature or an error condition. |
||
| 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()) / | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we basing the budget on 8000 timer_ticks ? If so, I think this would be better as a per platform CONFIG_ variable (given that non CAVS platforms will have a different timer).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's just an example. This is defined as CONFIG and defaults to 8k.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, it's probably good to explain this in the Kconfig "help" for the option. I guess 8k is based on a % of total budget (based on max clock rate?). Just looking for detail so that the option can be easily tuned based on teh help.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well it's already in help in kconfig - that it's within 1 systick IDK if we need to add more there. |
||
| (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); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am pretty sure in the past we defined variable ticks for EDF. Would this scale here if the tick duration is changed dynamically. And I read in a comment below that we use '120k cycles in systick instead of 400k', so shouldn't there be multiple budget definitions, one per systick value?