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; } diff --git a/src/include/sof/schedule/ll_schedule_domain.h b/src/include/sof/schedule/ll_schedule_domain.h index 4a2339c467b2..cd3c718b1954 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); @@ -43,9 +43,11 @@ 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 num_clients; /**< number of registered cores */ + 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 */ @@ -86,14 +88,35 @@ 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); - atomic_init(&domain->num_clients, 0); + atomic_init(&domain->registered_cores, 0); + atomic_init(&domain->enabled_cores, 0); 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) @@ -104,51 +127,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/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/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/ll_schedule.c b/src/schedule/ll_schedule.c index 85c67f71ba27..c3416c9f0bd4 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,21 +161,18 @@ 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->num_clients, 1); - domain_enable(domain, i); - domain->enabled[i] = true; - } + if (domain->registered[i] && !domain->enabled[i]) + domain_enable(sch->domain, i); } } -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)) { @@ -181,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; } } @@ -190,9 +201,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 num_clients; -#endif uint32_t flags; tr_dbg(&ll_tr, "timer interrupt on core %d, at %u, previous next_tick %u", @@ -200,27 +208,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 - */ - num_clients = atomic_sub(&domain->num_clients, 1) - 1; - if (!num_clients) - domain_clear(domain); -#endif - - - spin_unlock(&domain->lock); - perf_cnt_init(&sch->pcd); notifier_event(sch, NOTIFIER_ID_LL_PRE_RUN, @@ -237,14 +226,27 @@ 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()); + + /* 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); + + /* 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; - /* re-enable domain only if all clients are done */ -#ifndef __ZEPHYR__ - /* Under Zephyr each client has to be enabled separately */ - if (!num_clients) -#endif schedule_ll_clients_enable(sch); + } spin_unlock(&domain->lock); @@ -257,24 +259,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); @@ -312,18 +312,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->num_clients, 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, @@ -332,41 +325,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->num_clients, 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) 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); } 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)