[Sound-open-firmware] [PATCH] cnl: scheduler: Add list of tasks per irq level
From: Tomasz Lauda tomasz.lauda@linux.intel.com
This patch adds list of tasks per each irq level and fixes the problem, where scheduler tried to schedule the same task two times.
Signed-off-by: Tomasz Lauda tomasz.lauda@linux.intel.com --- src/arch/xtensa/include/arch/task.h | 4 +- src/arch/xtensa/init.c | 2 +- src/arch/xtensa/task.c | 147 ++++++++++++++++++++++++++++-------- src/include/reef/schedule.h | 3 + src/lib/schedule.c | 26 +++---- 5 files changed, 136 insertions(+), 46 deletions(-)
diff --git a/src/arch/xtensa/include/arch/task.h b/src/arch/xtensa/include/arch/task.h index 1547751b..2ec5f25e 100644 --- a/src/arch/xtensa/include/arch/task.h +++ b/src/arch/xtensa/include/arch/task.h @@ -36,6 +36,8 @@ struct task;
void arch_run_task(struct task *task);
-int arch_init_tasks(void); +void arch_allocate_tasks(void); + +int arch_assign_tasks(void);
#endif diff --git a/src/arch/xtensa/init.c b/src/arch/xtensa/init.c index 04338540..24c99369 100644 --- a/src/arch/xtensa/init.c +++ b/src/arch/xtensa/init.c @@ -78,7 +78,7 @@ static void register_exceptions(void) int arch_init(struct reef *reef) { register_exceptions(); - arch_init_tasks(); + arch_assign_tasks(); return 0; }
diff --git a/src/arch/xtensa/task.c b/src/arch/xtensa/task.c index 349f8abb..0ffedeb9 100644 --- a/src/arch/xtensa/task.c +++ b/src/arch/xtensa/task.c @@ -34,12 +34,19 @@ #include <platform/platform.h> #include <reef/debug.h> #include <arch/task.h> +#include <reef/alloc.h> #include <stdint.h> #include <errno.h>
-static struct task *_irq_low_task = NULL; -static struct task *_irq_med_task = NULL; -static struct task *_irq_high_task = NULL; +struct irq_task { + spinlock_t lock; + struct list_item list; /* list of tasks per irq */ + uint32_t irq; +}; + +static struct irq_task *irq_low_task; +static struct irq_task *irq_med_task; +static struct irq_task *irq_high_task;
static inline uint32_t task_get_irq(struct task *task) { @@ -65,55 +72,106 @@ static inline void task_set_data(struct task *task) { switch (task->priority) { case TASK_PRI_MED + 1 ... TASK_PRI_LOW: - _irq_low_task = task; + list_item_append(&task->irq_low_list, &irq_low_task->list); break; case TASK_PRI_HIGH ... TASK_PRI_MED - 1: - _irq_high_task = task; + list_item_append(&task->irq_high_list, &irq_high_task->list); break; case TASK_PRI_MED: default: - _irq_med_task = task; + list_item_append(&task->irq_med_list, &irq_med_task->list); break; } }
static void _irq_low(void *arg) { - struct task *task = *(struct task **)arg; - uint32_t irq; + struct irq_task *irq_task = *(struct irq_task **)arg; + struct list_item *tlist; + struct list_item *clist; + struct task *task; + uint32_t flags;
- if (task->func) - task->func(task->data); + /* intentionally don't lock list to have task added from schedule irq */ + list_for_item(tlist, &irq_task->list) { + task = container_of(tlist, struct task, irq_low_list);
- schedule_task_complete(task); - irq = task_get_irq(task); - interrupt_clear(irq); + if (task->func) + task->func(task->data); + + schedule_task_complete(task); + } + + spin_lock_irq(&irq_task->lock, flags); + + list_for_item_safe(clist, tlist, &irq_task->list) { + task = container_of(clist, struct task, irq_low_list); + list_item_del(&task->irq_low_list); + } + + interrupt_clear(irq_task->irq); + + spin_unlock_irq(&irq_task->lock, flags); }
static void _irq_med(void *arg) { - struct task *task = *(struct task **)arg; - uint32_t irq; + struct irq_task *irq_task = *(struct irq_task **)arg; + struct list_item *tlist; + struct list_item *clist; + struct task *task; + uint32_t flags;
- if (task->func) - task->func(task->data); + /* intentionally don't lock list to have task added from schedule irq */ + list_for_item(tlist, &irq_task->list) { + task = container_of(tlist, struct task, irq_med_list);
- schedule_task_complete(task); - irq = task_get_irq(task); - interrupt_clear(irq); + if (task->func) + task->func(task->data); + + schedule_task_complete(task); + } + + spin_lock_irq(&irq_task->lock, flags); + + list_for_item_safe(clist, tlist, &irq_task->list) { + task = container_of(clist, struct task, irq_med_list); + list_item_del(&task->irq_med_list); + } + + interrupt_clear(irq_task->irq); + + spin_unlock_irq(&irq_task->lock, flags); }
static void _irq_high(void *arg) { - struct task *task = *(struct task **)arg; - uint32_t irq; + struct irq_task *irq_task = *(struct irq_task **)arg; + struct list_item *tlist; + struct list_item *clist; + struct task *task; + uint32_t flags;
- if (task->func) - task->func(task->data); + /* intentionally don't lock list to have task added from schedule irq */ + list_for_item(tlist, &irq_task->list) { + task = container_of(tlist, struct task, irq_high_list);
- schedule_task_complete(task); - irq = task_get_irq(task); - interrupt_clear(irq); + if (task->func) + task->func(task->data); + + schedule_task_complete(task); + } + + spin_lock_irq(&irq_task->lock, flags); + + list_for_item_safe(clist, tlist, &irq_task->list) { + task = container_of(clist, struct task, irq_high_list); + list_item_del(&task->irq_high_list); + } + + interrupt_clear(irq_task->irq); + + spin_unlock_irq(&irq_task->lock, flags); }
/* architecture specific method of running task */ @@ -126,15 +184,42 @@ void arch_run_task(struct task *task) interrupt_set(irq); }
-int arch_init_tasks(void) +void arch_allocate_tasks(void) +{ + /* irq low */ + irq_low_task = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, + sizeof(*irq_low_task)); + list_init(&irq_low_task->list); + spinlock_init(&irq_low_task->lock); + irq_low_task->irq = PLATFORM_IRQ_TASK_LOW; + + /* irq medium */ + irq_med_task = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, + sizeof(*irq_med_task)); + list_init(&irq_med_task->list); + spinlock_init(&irq_med_task->lock); + irq_med_task->irq = PLATFORM_IRQ_TASK_MED; + + /* irq high */ + irq_high_task = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, + sizeof(*irq_high_task)); + list_init(&irq_high_task->list); + spinlock_init(&irq_high_task->lock); + irq_high_task->irq = PLATFORM_IRQ_TASK_HIGH; +} + +int arch_assign_tasks(void) { - interrupt_register(PLATFORM_IRQ_TASK_LOW, _irq_low, &_irq_low_task); + /* irq low */ + interrupt_register(PLATFORM_IRQ_TASK_LOW, _irq_low, &irq_low_task); interrupt_enable(PLATFORM_IRQ_TASK_LOW);
- interrupt_register(PLATFORM_IRQ_TASK_MED, _irq_med, &_irq_med_task); + /* irq medium */ + interrupt_register(PLATFORM_IRQ_TASK_MED, _irq_med, &irq_med_task); interrupt_enable(PLATFORM_IRQ_TASK_MED);
- interrupt_register(PLATFORM_IRQ_TASK_HIGH, _irq_high, &_irq_high_task); + /* irq high */ + interrupt_register(PLATFORM_IRQ_TASK_HIGH, _irq_high, &irq_high_task); interrupt_enable(PLATFORM_IRQ_TASK_HIGH);
return 0; diff --git a/src/include/reef/schedule.h b/src/include/reef/schedule.h index 68aa0adf..26b3a935 100644 --- a/src/include/reef/schedule.h +++ b/src/include/reef/schedule.h @@ -64,6 +64,9 @@ struct task { uint64_t deadline; /* scheduling deadline */ uint32_t state; /* TASK_STATE_ */ struct list_item list; /* list in scheduler */ + struct list_item irq_low_list; /* list for low irq level */ + struct list_item irq_med_list; /* list for medium irq level */ + struct list_item irq_high_list; /* list for high irq level */
/* task function and private data */ void *data; diff --git a/src/lib/schedule.c b/src/lib/schedule.c index 77284196..c21d222b 100644 --- a/src/lib/schedule.c +++ b/src/lib/schedule.c @@ -172,7 +172,7 @@ static uint64_t sch_work(void *data, uint64_t delay) static struct task *schedule_edf(void) { struct task *task; - struct task *next_plus1_task = NULL; + struct task *future_task = NULL; uint64_t current;
tracev_pipe("edf"); @@ -192,18 +192,16 @@ static struct task *schedule_edf(void) /* can task be started now ? */ if (task->start > current) { /* no, then schedule wake up */ - next_plus1_task = task; + future_task = task; } else { - /* yes, get next task and run this one now */ - next_plus1_task = edf_get_next(current, task); - - /* run current task */ + /* yes, run current task */ task->start = current; + task->state = TASK_STATE_RUNNING; arch_run_task(task); }
- /* teall caller about next task (after current) */ - return next_plus1_task; + /* tell caller about future task */ + return future_task; }
#if 0 /* FIXME: is this needed ? */ @@ -319,15 +317,14 @@ void schedule_task_complete(struct task *task)
static void scheduler_run(void *unused) { - struct task *next_task; + struct task *future_task;
tracev_pipe("run");
/* EDF is only scheduler supported atm */ - next_task = schedule_edf(); - if (next_task) { - work_reschedule_default_at(&sch->work, next_task->start); - } + future_task = schedule_edf(); + if (future_task) + work_reschedule_default_at(&sch->work, future_task->start); }
/* run the scheduler */ @@ -382,5 +379,8 @@ int scheduler_init(struct reef *reef) interrupt_register(PLATFORM_SCHEDULE_IRQ, scheduler_run, NULL); interrupt_enable(PLATFORM_SCHEDULE_IRQ);
+ /* allocate arch tasks */ + arch_allocate_tasks(); + return 0; }
participants (1)
-
Liam Girdwood