From 0851d457bf594323fd52373c0f759fa9d5efcbad Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 7 May 2021 12:31:24 +0800 Subject: [PATCH 1/7] schedule: add a core index for schedule_data A scheduler will run on a specific scheduler only, add a core index flag to schedule_data to denote that. To schedule or cancel a task, looking for the scheduler with correct core index to perform the action. Signed-off-by: Keyon Jie --- src/include/sof/schedule/schedule.h | 8 ++++++-- src/schedule/schedule.c | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/include/sof/schedule/schedule.h b/src/include/sof/schedule/schedule.h index 17098cf61b99..df214043dde4 100644 --- a/src/include/sof/schedule/schedule.h +++ b/src/include/sof/schedule/schedule.h @@ -10,6 +10,7 @@ #define __SOF_SCHEDULE_SCHEDULE_H__ #include +#include #include #include #include @@ -122,6 +123,7 @@ struct scheduler_ops { struct schedule_data { struct list_item list; /**< list of schedulers */ int type; /**< SOF_SCHEDULE_ type */ + uint32_t core; /**< the index of the core the scheduler run on */ const struct scheduler_ops *ops; /**< scheduler operations */ void *data; /**< pointer to private data */ }; @@ -210,7 +212,8 @@ static inline int schedule_task(struct task *task, uint64_t start, list_for_item(slist, &schedulers->list) { sch = container_of(slist, struct schedule_data, list); - if (task->type == sch->type && sch->ops->schedule_task) + if (task->type == sch->type && sch->core == cpu_get_id() && + sch->ops->schedule_task) return sch->ops->schedule_task(sch->data, task, start, period); } @@ -249,7 +252,8 @@ static inline int schedule_task_cancel(struct task *task) list_for_item(slist, &schedulers->list) { sch = container_of(slist, struct schedule_data, list); - if (task->type == sch->type && sch->ops->schedule_task_cancel) + if (task->type == sch->type && sch->core == cpu_get_id() && + sch->ops->schedule_task_cancel) return sch->ops->schedule_task_cancel(sch->data, task); } diff --git a/src/schedule/schedule.c b/src/schedule/schedule.c index 64a4d74d00fe..823487c39294 100644 --- a/src/schedule/schedule.c +++ b/src/schedule/schedule.c @@ -7,6 +7,7 @@ /* Generic scheduler */ #include +#include #include #include #include @@ -66,6 +67,7 @@ void scheduler_init(int type, const struct scheduler_ops *ops, void *data) sch->type = type; sch->ops = ops; sch->data = data; + sch->core = cpu_get_id(); scheduler_register(sch); } From a2188d838a6a55ca9dfef8e904cdfc863c1bfb1b Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 7 May 2021 12:46:41 +0800 Subject: [PATCH 2/7] cavs: timer: remove the wrong timer_disable The timer_disable() in the timer_register() is wrong, the interrupt_enable() calling handles the interrupt enabling already, remove the wrong timer_disable() calling to correct it. Signed-off-by: Keyon Jie --- src/drivers/intel/cavs/timer.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/drivers/intel/cavs/timer.c b/src/drivers/intel/cavs/timer.c index 801af6f033b4..b6b10de9af03 100644 --- a/src/drivers/intel/cavs/timer.c +++ b/src/drivers/intel/cavs/timer.c @@ -165,9 +165,6 @@ static int platform_timer_register(struct timer *timer, /* enable timer interrupt */ interrupt_enable(timer->logical_irq, arg); - /* disable timer interrupt on core level */ - timer_disable(timer, arg, cpu_get_id()); - return err; } From 79e2923faeff85d546958bbe2e6df3acfe7ad40e Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 26 Apr 2021 10:49:13 +0800 Subject: [PATCH 3/7] ll_schedule_domain: add return value of domain_unregister() Add return value of domain_unregister(), which will be used for the LL scheduler and the domain state management in the subsequent refinement. Signed-off-by: Keyon Jie --- src/include/sof/schedule/ll_schedule_domain.h | 4 ++-- src/schedule/dma_multi_chan_domain.c | 14 +++++++++----- src/schedule/dma_single_chan_domain.c | 17 ++++++++++------- src/schedule/timer_domain.c | 8 +++++--- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/include/sof/schedule/ll_schedule_domain.h b/src/include/sof/schedule/ll_schedule_domain.h index 4a2339c467b2..0105aceb7cfe 100644 --- a/src/include/sof/schedule/ll_schedule_domain.h +++ b/src/include/sof/schedule/ll_schedule_domain.h @@ -31,8 +31,8 @@ struct ll_schedule_domain_ops { int (*domain_register)(struct ll_schedule_domain *domain, uint64_t period, struct task *task, void (*handler)(void *arg), void *arg); - void (*domain_unregister)(struct ll_schedule_domain *domain, - struct task *task, uint32_t num_tasks); + int (*domain_unregister)(struct ll_schedule_domain *domain, + struct task *task, uint32_t num_tasks); void (*domain_enable)(struct ll_schedule_domain *domain, int core); void (*domain_disable)(struct ll_schedule_domain *domain, int core); void (*domain_set)(struct ll_schedule_domain *domain, uint64_t start); diff --git a/src/schedule/dma_multi_chan_domain.c b/src/schedule/dma_multi_chan_domain.c index 75ed86d817b5..049273210636 100644 --- a/src/schedule/dma_multi_chan_domain.c +++ b/src/schedule/dma_multi_chan_domain.c @@ -185,10 +185,11 @@ static void dma_multi_chan_domain_irq_unregister(struct dma_domain_data *data) * \param[in,out] domain Pointer to schedule domain. * \param[in,out] task Task to be unregistered from the domain.. * \param[in] num_tasks Number of currently scheduled tasks. + * \return Error code. */ -static void dma_multi_chan_domain_unregister(struct ll_schedule_domain *domain, - struct task *task, - uint32_t num_tasks) +static int dma_multi_chan_domain_unregister(struct ll_schedule_domain *domain, + struct task *task, + uint32_t num_tasks) { struct dma_domain *dma_domain = ll_sch_domain_get_pdata(domain); struct pipeline_task *pipe_task = pipeline_task_get(task); @@ -201,7 +202,7 @@ static void dma_multi_chan_domain_unregister(struct ll_schedule_domain *domain, /* check if task should be unregistered */ if (!pipe_task->registrable) - return; + return 0; for (i = 0; i < dma_domain->num_dma; ++i) { for (j = 0; j < dmas[i].plat_data.channels; ++j) { @@ -236,9 +237,12 @@ static void dma_multi_chan_domain_unregister(struct ll_schedule_domain *domain, else if (!dma_domain->channel_mask[i][core]) dma_multi_chan_domain_irq_unregister( dma_domain->arg[i][core]); - return; + return 0; } } + + /* task in running or unregistered at all, can't unregister it */ + return -EINVAL; } /** diff --git a/src/schedule/dma_single_chan_domain.c b/src/schedule/dma_single_chan_domain.c index 900f066f8666..e61ea6df4e58 100644 --- a/src/schedule/dma_single_chan_domain.c +++ b/src/schedule/dma_single_chan_domain.c @@ -355,10 +355,11 @@ static void dma_domain_unregister_owner(struct ll_schedule_domain *domain, * \param[in,out] domain Pointer to schedule domain. * \param[in,out] task Task to be unregistered from the domain. * \param[in] num_tasks Number of currently scheduled tasks. + * \return Error code. */ -static void dma_single_chan_domain_unregister(struct ll_schedule_domain *domain, - struct task *task, - uint32_t num_tasks) +static int dma_single_chan_domain_unregister(struct ll_schedule_domain *domain, + struct task *task, + uint32_t num_tasks) { struct dma_domain *dma_domain = ll_sch_domain_get_pdata(domain); struct pipeline_task *pipe_task = pipeline_task_get(task); @@ -370,27 +371,29 @@ static void dma_single_chan_domain_unregister(struct ll_schedule_domain *domain, /* check if task should be unregistered */ if (!pipe_task->registrable) - return; + return 0; /* channel not registered */ if (!data->channel) - return; + return -EINVAL; /* unregister domain owner */ if (dma_domain->owner == core) { dma_domain_unregister_owner(domain, data); - return; + return 0; } /* some channel still running, so return */ if (dma_chan_is_any_running(dmas, dma_domain->num_dma)) - return; + return -EBUSY; /* no more transfers scheduled on this core */ dma_single_chan_domain_irq_unregister(data); data->channel = NULL; notifier_unregister(domain, NULL, NOTIFIER_ID_DMA_DOMAIN_CHANGE); + + return 0; } /** diff --git a/src/schedule/timer_domain.c b/src/schedule/timer_domain.c index d84f640b5cb1..094575e7ad59 100644 --- a/src/schedule/timer_domain.c +++ b/src/schedule/timer_domain.c @@ -194,8 +194,8 @@ static int timer_domain_register(struct ll_schedule_domain *domain, return ret; } -static void timer_domain_unregister(struct ll_schedule_domain *domain, - struct task *task, uint32_t num_tasks) +static int timer_domain_unregister(struct ll_schedule_domain *domain, + struct task *task, uint32_t num_tasks) { struct timer_domain *timer_domain = ll_sch_domain_get_pdata(domain); int core = cpu_get_id(); @@ -204,7 +204,7 @@ static void timer_domain_unregister(struct ll_schedule_domain *domain, /* tasks still registered on this core */ if (!timer_domain->arg[core] || num_tasks) - return; + return 0; tr_info(&ll_tr, "timer_domain_unregister domain->type %d domain->clk %d", domain->type, domain->clk); @@ -212,6 +212,8 @@ static void timer_domain_unregister(struct ll_schedule_domain *domain, timer_unregister(timer_domain->timer, timer_domain->arg[core]); #endif timer_domain->arg[core] = NULL; + + return 0; } static void timer_domain_enable(struct ll_schedule_domain *domain, int core) From d17e28d7a93501df5e73b4cdfc82fd8481f1c4a7 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 27 Apr 2021 09:53:55 +0800 Subject: [PATCH 4/7] ll_schedule: rename num_clients to registered_cores Rename the 'num_clients' in struct ll_schedule_domain to 'registered_cores' to reflect the real use of it. Signed-off-by: Keyon Jie --- src/include/sof/schedule/ll_schedule_domain.h | 4 ++-- src/schedule/ll_schedule.c | 16 +++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/include/sof/schedule/ll_schedule_domain.h b/src/include/sof/schedule/ll_schedule_domain.h index 0105aceb7cfe..1305623c2c89 100644 --- a/src/include/sof/schedule/ll_schedule_domain.h +++ b/src/include/sof/schedule/ll_schedule_domain.h @@ -45,7 +45,7 @@ struct ll_schedule_domain { uint64_t next_tick; /**< ticks just set for next run */ spinlock_t lock; /**< standard lock */ atomic_t total_num_tasks; /**< total number of registered tasks */ - atomic_t num_clients; /**< number of registered cores */ + atomic_t registered_cores; /**< number of registered cores */ uint32_t ticks_per_ms; /**< number of clock ticks per ms */ int type; /**< domain type */ int clk; /**< source clock */ @@ -89,7 +89,7 @@ static inline struct ll_schedule_domain *domain_init spinlock_init(&domain->lock); atomic_init(&domain->total_num_tasks, 0); - atomic_init(&domain->num_clients, 0); + atomic_init(&domain->registered_cores, 0); return domain; } diff --git a/src/schedule/ll_schedule.c b/src/schedule/ll_schedule.c index 85c67f71ba27..1573a77cef87 100644 --- a/src/schedule/ll_schedule.c +++ b/src/schedule/ll_schedule.c @@ -150,7 +150,7 @@ static void schedule_ll_clients_enable(struct ll_schedule_data *sch) for (i = 0; i < CONFIG_CORE_COUNT; i++) { if (domain->registered[i] && !domain->enabled[i]) { - atomic_add(&domain->num_clients, 1); + atomic_add(&domain->registered_cores, 1); domain_enable(domain, i); domain->enabled[i] = true; } @@ -191,7 +191,7 @@ static void schedule_ll_tasks_run(void *data) struct ll_schedule_data *sch = data; struct ll_schedule_domain *domain = sch->domain; #ifndef __ZEPHYR__ - uint32_t num_clients; + uint32_t registered_cores; #endif uint32_t flags; @@ -213,12 +213,10 @@ static void schedule_ll_tasks_run(void *data) /* TODO: no need for atomic operations, * already protected by spin_lock */ - num_clients = atomic_sub(&domain->num_clients, 1) - 1; - if (!num_clients) + registered_cores = atomic_sub(domain->registered_cores, 1) - 1; + if (!registered_cores) domain_clear(domain); #endif - - spin_unlock(&domain->lock); perf_cnt_init(&sch->pcd); @@ -242,7 +240,7 @@ static void schedule_ll_tasks_run(void *data) /* re-enable domain only if all clients are done */ #ifndef __ZEPHYR__ /* Under Zephyr each client has to be enabled separately */ - if (!num_clients) + if (!registered_cores) #endif schedule_ll_clients_enable(sch); @@ -320,7 +318,7 @@ static int schedule_ll_domain_set(struct ll_schedule_data *sch, total = atomic_add(&domain->total_num_tasks, 1); if (total == 0 || !registered) { /* First task on core: count and enable it */ - atomic_add(&domain->num_clients, 1); + atomic_add(&domain->registered_cores, 1); domain_enable(domain, core); domain->enabled[core] = true; } @@ -353,7 +351,7 @@ static void schedule_ll_domain_clear(struct ll_schedule_data *sch, domain->registered[cpu_get_id()] = false; if (atomic_read(&domain->num_clients)) { - count = atomic_sub(&domain->num_clients, 1); + count = atomic_sub(&domain->registered_cores, 1); if (count == 1) domain_clear(domain); } From 72310dad446acc25acd80334b196b166af6f8d8a Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 26 Apr 2021 11:04:07 +0800 Subject: [PATCH 5/7] ll_schedule_domain: add flag to store the enabled cores The flag registered_cores was created to denote the number of registered cores, but it is also used as the number of the enabled cores today. Here add a new flag enabled_cores for the former purpose to remove the confusion and help for the subsequent domain tasks/cores management refinement. Signed-off-by: Keyon Jie --- src/include/sof/schedule/ll_schedule_domain.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/include/sof/schedule/ll_schedule_domain.h b/src/include/sof/schedule/ll_schedule_domain.h index 1305623c2c89..6ebddd28e7a7 100644 --- a/src/include/sof/schedule/ll_schedule_domain.h +++ b/src/include/sof/schedule/ll_schedule_domain.h @@ -46,6 +46,7 @@ struct ll_schedule_domain { spinlock_t lock; /**< standard lock */ atomic_t total_num_tasks; /**< total number of registered tasks */ atomic_t registered_cores; /**< number of registered cores */ + atomic_t enabled_cores; /**< number of enabled cores */ uint32_t ticks_per_ms; /**< number of clock ticks per ms */ int type; /**< domain type */ int clk; /**< source clock */ @@ -90,6 +91,7 @@ static inline struct ll_schedule_domain *domain_init spinlock_init(&domain->lock); atomic_init(&domain->total_num_tasks, 0); atomic_init(&domain->registered_cores, 0); + atomic_init(&domain->enabled_cores, 0); return domain; } From d3d290c4c3702d2d8863494569324a1677bb885c Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 26 Apr 2021 11:40:44 +0800 Subject: [PATCH 6/7] ll_schedule: domain tasks/cores refinement Refinement to make the domain tasks/cores management more clear: a. update the total_num_tasks and num_cores in the helper pair domain_register/unregister(). b. update the enabled[core] and enabled_cores in the helper pair domain_enable/disable(). c. for the completed task, do the domain client/task update in the new created helper schedule_ll_task_done(). d. cleanup and remove the tasks/cores management from the ll_schedule helpers schedule_ll_clients_enable(), schedule_ll_tasks_run(), schedule_ll_domain_set() and schedule_ll_domain_clear(). Without those refinement, the num_clients was not used correctly, we used it for both num_registered_cores and num_enabled_cores, with that we observed that it may be changed to '-1' when component/pipeline is asked for running on a slave core. This reverts changes in 'commit b500999477ca ("scheduler: guard against subdivision underflow on domain clear")' as it is not needed anymore. Signed-off-by: Keyon Jie --- src/include/sof/schedule/ll_schedule_domain.h | 77 ++++++---- src/schedule/ll_schedule.c | 132 +++++++----------- 2 files changed, 105 insertions(+), 104 deletions(-) diff --git a/src/include/sof/schedule/ll_schedule_domain.h b/src/include/sof/schedule/ll_schedule_domain.h index 6ebddd28e7a7..58506dd9bd56 100644 --- a/src/include/sof/schedule/ll_schedule_domain.h +++ b/src/include/sof/schedule/ll_schedule_domain.h @@ -96,6 +96,25 @@ static inline struct ll_schedule_domain *domain_init return domain; } +/* configure the next interrupt for domain */ +static inline void domain_set(struct ll_schedule_domain *domain, uint64_t start) +{ + if (domain->ops->domain_set) + domain->ops->domain_set(domain, start); + else + domain->next_tick = start; +} + +/* clear the interrupt for domain */ +static inline void domain_clear(struct ll_schedule_domain *domain) +{ + if (domain->ops->domain_clear) + domain->ops->domain_clear(domain); + + /* reset to denote no tick/interrupt is set */ + domain->next_tick = UINT64_MAX; +} + static inline int domain_register(struct ll_schedule_domain *domain, uint64_t period, struct task *task, void (*handler)(void *arg), void *arg) @@ -106,51 +125,57 @@ static inline int domain_register(struct ll_schedule_domain *domain, ret = domain->ops->domain_register(domain, period, task, handler, arg); + if (!ret) { + /* registered one more task, increase the count */ + atomic_add(&domain->total_num_tasks, 1); + + if (!domain->registered[cpu_get_id()]) { + /* first task of the core, new client registered */ + domain->registered[cpu_get_id()] = true; + atomic_add(&domain->registered_cores, 1); + } + } + return ret; } static inline void domain_unregister(struct ll_schedule_domain *domain, struct task *task, uint32_t num_tasks) { + int ret; + assert(domain->ops->domain_unregister); - domain->ops->domain_unregister(domain, task, num_tasks); + ret = domain->ops->domain_unregister(domain, task, num_tasks); + + if (!ret) { + /* unregistered the task, decrease the count */ + atomic_sub(&domain->total_num_tasks, 1); + /* the last task of the core, unregister the client/core */ + if (!num_tasks && domain->registered[cpu_get_id()]) { + domain->registered[cpu_get_id()] = false; + atomic_sub(&domain->registered_cores, 1); + } + } } static inline void domain_enable(struct ll_schedule_domain *domain, int core) { - if (domain->ops->domain_enable) + if (!domain->enabled[core] && domain->ops->domain_enable) { domain->ops->domain_enable(domain, core); - + domain->enabled[core] = true; + atomic_add(&domain->enabled_cores, 1); + } } static inline void domain_disable(struct ll_schedule_domain *domain, int core) { - if (domain->ops->domain_disable) + if (domain->enabled[core] && domain->ops->domain_disable) { domain->ops->domain_disable(domain, core); - -} - -/* configure the next interrupt for domain */ -static inline void domain_set(struct ll_schedule_domain *domain, uint64_t start) -{ - if (domain->ops->domain_set) - domain->ops->domain_set(domain, start); - else - domain->next_tick = start; - -} - -/* clear the interrupt for domain */ -static inline void domain_clear(struct ll_schedule_domain *domain) -{ - if (domain->ops->domain_clear) - domain->ops->domain_clear(domain); - - /* reset to denote no tick/interrupt is set */ - domain->next_tick = UINT64_MAX; - + domain->enabled[core] = false; + atomic_sub(&domain->enabled_cores, 1); + } } static inline bool domain_is_pending(struct ll_schedule_domain *domain, diff --git a/src/schedule/ll_schedule.c b/src/schedule/ll_schedule.c index 1573a77cef87..3033d7108557 100644 --- a/src/schedule/ll_schedule.c +++ b/src/schedule/ll_schedule.c @@ -95,14 +95,37 @@ static void schedule_ll_task_update_start(struct ll_schedule_data *sch, task->start += next; } +/* caller should hold the domain lock */ +static void schedule_ll_task_done(struct ll_schedule_data *sch, + struct task *task) +{ + list_item_del(&task->list); + + /* the task finished, decrease the count */ + atomic_sub(&sch->domain->total_num_tasks, 1); + + /* decrease task number of the core */ + atomic_sub(&sch->num_tasks, 1); + + /* the last task of the core, unregister the client/core */ + if (!atomic_read(&sch->num_tasks) && + sch->domain->registered[cpu_get_id()]) { + sch->domain->registered[cpu_get_id()] = false; + atomic_sub(&sch->domain->registered_cores, 1); + } + + tr_info(&ll_tr, "task complete %p %pU", task, task->uid); + tr_info(&ll_tr, "num_tasks %d total_num_tasks %d", + atomic_read(&sch->num_tasks), + atomic_read(&sch->domain->total_num_tasks)); +} + static void schedule_ll_tasks_execute(struct ll_schedule_data *sch) { struct ll_schedule_domain *domain = sch->domain; struct list_item *wlist; struct list_item *tlist; struct task *task; - int cpu = cpu_get_id(); - int count; /* check each task in the list for pending */ list_for_item_safe(wlist, tlist, &sch->tasks) { @@ -119,17 +142,7 @@ static void schedule_ll_tasks_execute(struct ll_schedule_data *sch) /* do we need to reschedule this task */ if (task->state == SOF_TASK_STATE_COMPLETED) { - list_item_del(&task->list); - atomic_sub(&domain->total_num_tasks, 1); - - /* don't enable irq, if no more tasks to do */ - count = atomic_sub(&sch->num_tasks, 1); - if (count == 1) - domain->registered[cpu] = false; - tr_info(&ll_tr, "task complete %p %pU", task, task->uid); - tr_info(&ll_tr, "num_tasks %d total_num_tasks %d", - atomic_read(&sch->num_tasks), - atomic_read(&domain->total_num_tasks)); + schedule_ll_task_done(sch, task); } else { /* update task's start time */ schedule_ll_task_update_start(sch, task); @@ -138,7 +151,6 @@ static void schedule_ll_tasks_execute(struct ll_schedule_data *sch) (uint32_t)domain->next_tick); } - spin_unlock(&domain->lock); } } @@ -149,11 +161,8 @@ static void schedule_ll_clients_enable(struct ll_schedule_data *sch) int i; for (i = 0; i < CONFIG_CORE_COUNT; i++) { - if (domain->registered[i] && !domain->enabled[i]) { - atomic_add(&domain->registered_cores, 1); - domain_enable(domain, i); - domain->enabled[i] = true; - } + if (domain->registered[i] && !domain->enabled[i]) + domain_enable(sch->domain, i); } } @@ -190,9 +199,6 @@ static void schedule_ll_tasks_run(void *data) { struct ll_schedule_data *sch = data; struct ll_schedule_domain *domain = sch->domain; -#ifndef __ZEPHYR__ - uint32_t registered_cores; -#endif uint32_t flags; tr_dbg(&ll_tr, "timer interrupt on core %d, at %u, previous next_tick %u", @@ -200,25 +206,8 @@ static void schedule_ll_tasks_run(void *data) (unsigned int)platform_timer_get_atomic(timer_get()), (unsigned int)domain->next_tick); - domain_disable(domain, cpu_get_id()); - irq_local_disable(flags); - spin_lock(&domain->lock); - - domain->enabled[cpu_get_id()] = false; - -#ifndef __ZEPHYR__ - /* clear domain only if all clients are done */ - /* TODO: no need for atomic operations, - * already protected by spin_lock - */ - registered_cores = atomic_sub(domain->registered_cores, 1) - 1; - if (!registered_cores) - domain_clear(domain); -#endif - spin_unlock(&domain->lock); - perf_cnt_init(&sch->pcd); notifier_event(sch, NOTIFIER_ID_LL_PRE_RUN, @@ -235,14 +224,17 @@ static void schedule_ll_tasks_run(void *data) spin_lock(&domain->lock); - schedule_ll_clients_reschedule(sch); + /* tasks on current core finished, disable domain on it */ + domain_disable(domain, cpu_get_id()); + + /* re-enable domain and reschedule clients only if all cores are done */ + if (!atomic_read(&domain->enabled_cores)) { + /* clear the domain/interrupts */ + domain_clear(domain); - /* re-enable domain only if all clients are done */ -#ifndef __ZEPHYR__ - /* Under Zephyr each client has to be enabled separately */ - if (!registered_cores) -#endif + schedule_ll_clients_reschedule(sch); schedule_ll_clients_enable(sch); + } spin_unlock(&domain->lock); @@ -255,24 +247,22 @@ static int schedule_ll_domain_set(struct ll_schedule_data *sch, { struct ll_schedule_domain *domain = sch->domain; int core = cpu_get_id(); - unsigned int total; uint64_t task_start_us; uint64_t task_start_ticks; uint64_t task_start; uint64_t offset; - bool registered; int ret; + spin_lock(&domain->lock); + ret = domain_register(domain, period, task, &schedule_ll_tasks_run, sch); if (ret < 0) { tr_err(&ll_tr, "schedule_ll_domain_set: cannot register domain %d", ret); - return ret; + goto done; } - spin_lock(&domain->lock); - tr_dbg(&ll_tr, "task->start %u next_tick %u", (unsigned int)task->start, (unsigned int)domain->next_tick); @@ -310,18 +300,11 @@ static int schedule_ll_domain_set(struct ll_schedule_data *sch, task->start = domain->next_tick; } - registered = domain->registered[core]; - total = atomic_add(&sch->num_tasks, 1); - if (total == 0) - domain->registered[core] = true; - - total = atomic_add(&domain->total_num_tasks, 1); - if (total == 0 || !registered) { - /* First task on core: count and enable it */ - atomic_add(&domain->registered_cores, 1); - domain_enable(domain, core); - domain->enabled[core] = true; - } + /* increase task number of the core */ + atomic_add(&sch->num_tasks, 1); + + /* make sure enable domain on the core */ + domain_enable(domain, core); tr_info(&ll_tr, "new added task->start %u at %u", (unsigned int)task->start, @@ -330,41 +313,34 @@ static int schedule_ll_domain_set(struct ll_schedule_data *sch, atomic_read(&sch->num_tasks), atomic_read(&domain->total_num_tasks)); - +done: spin_unlock(&domain->lock); - return 0; + return ret; } static void schedule_ll_domain_clear(struct ll_schedule_data *sch, struct task *task) { struct ll_schedule_domain *domain = sch->domain; - int count; spin_lock(&domain->lock); - atomic_sub(&domain->total_num_tasks, 1); + /* decrease task number of the core */ + atomic_sub(&sch->num_tasks, 1); - count = atomic_sub(&sch->num_tasks, 1); - if (count == 1) { - domain->registered[cpu_get_id()] = false; + /* disable domain on the core if needed */ + if (!atomic_read(&sch->num_tasks)) + domain_disable(domain, cpu_get_id()); - if (atomic_read(&domain->num_clients)) { - count = atomic_sub(&domain->registered_cores, 1); - if (count == 1) - domain_clear(domain); - } - } + /* unregister the task */ + domain_unregister(domain, task, (uint32_t)atomic_read(&sch->num_tasks)); tr_info(&ll_tr, "num_tasks %d total_num_tasks %d", atomic_read(&sch->num_tasks), atomic_read(&domain->total_num_tasks)); - spin_unlock(&domain->lock); - - domain_unregister(domain, task, atomic_read(&sch->num_tasks)); } static void schedule_ll_task_insert(struct task *task, struct list_item *tasks) From 2efcb00ae817facb32f0d637e0a5e76182465657 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 11 May 2021 18:53:45 +0800 Subject: [PATCH 7/7] ll_schedule: add new_target_tick to ll_schedule_domain Add a new_target_tick to store the new target tick for the next set, which is used during the reschedule stage. Update the new_target_tick on tasks done on each core, and do the final domain_set() at the point that tasks on all cores are finished. Signed-off-by: Keyon Jie --- src/include/sof/schedule/ll_schedule_domain.h | 2 ++ src/schedule/ll_schedule.c | 20 +++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/include/sof/schedule/ll_schedule_domain.h b/src/include/sof/schedule/ll_schedule_domain.h index 58506dd9bd56..cd3c718b1954 100644 --- a/src/include/sof/schedule/ll_schedule_domain.h +++ b/src/include/sof/schedule/ll_schedule_domain.h @@ -43,6 +43,7 @@ struct ll_schedule_domain_ops { struct ll_schedule_domain { uint64_t next_tick; /**< ticks just set for next run */ + uint64_t new_target_tick; /**< for the next set, used during the reschedule stage */ spinlock_t lock; /**< standard lock */ atomic_t total_num_tasks; /**< total number of registered tasks */ atomic_t registered_cores; /**< number of registered cores */ @@ -87,6 +88,7 @@ static inline struct ll_schedule_domain *domain_init domain->ops = ops; /* maximum value means no tick has been set to timer */ domain->next_tick = UINT64_MAX; + domain->new_target_tick = UINT64_MAX; spinlock_init(&domain->lock); atomic_init(&domain->total_num_tasks, 0); diff --git a/src/schedule/ll_schedule.c b/src/schedule/ll_schedule.c index 3033d7108557..c3416c9f0bd4 100644 --- a/src/schedule/ll_schedule.c +++ b/src/schedule/ll_schedule.c @@ -166,13 +166,13 @@ static void schedule_ll_clients_enable(struct ll_schedule_data *sch) } } -static void schedule_ll_clients_reschedule(struct ll_schedule_data *sch) +static void schedule_ll_client_reschedule(struct ll_schedule_data *sch) { struct list_item *wlist; struct list_item *tlist; struct task *task; struct task *task_take_dbg = NULL; - uint64_t next_tick = UINT64_MAX; + uint64_t next_tick = sch->domain->new_target_tick; /* rearm only if there is work to do */ if (atomic_read(&sch->domain->total_num_tasks)) { @@ -190,7 +190,9 @@ static void schedule_ll_clients_reschedule(struct ll_schedule_data *sch) tr_dbg(&ll_tr, "schedule_ll_clients_reschedule next_tick %u task_take %p", (unsigned int)next_tick, task_take_dbg); - domain_set(sch->domain, next_tick); + + /* update the target_tick */ + sch->domain->new_target_tick = next_tick; } } @@ -227,12 +229,22 @@ static void schedule_ll_tasks_run(void *data) /* tasks on current core finished, disable domain on it */ domain_disable(domain, cpu_get_id()); + /* update the next_target_tick according to tasks on current core */ + schedule_ll_client_reschedule(sch); + /* re-enable domain and reschedule clients only if all cores are done */ if (!atomic_read(&domain->enabled_cores)) { /* clear the domain/interrupts */ domain_clear(domain); - schedule_ll_clients_reschedule(sch); + /* set the next interrupt according to the next_target_tick */ + domain_set(sch->domain, sch->domain->new_target_tick); + tr_dbg(&ll_tr, "tasks on all cores done, new_target_tick %u set", + (unsigned int)sch->domain->new_target_tick); + + /* reset the next_target_tick */ + sch->domain->new_target_tick = UINT64_MAX; + schedule_ll_clients_enable(sch); }