From 3b43833a61c958cc61a2907cd5f52fb91726da2e Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 3 Sep 2018 14:57:46 +0800 Subject: [PATCH] pipe: fix a infinite loop issue in schedule module There are two paths to delete a task in shedule list. A task would be deleted when task is completed or cancelled. So it maybe deleted twice. there is a circle in the schedule list after a sequence of append-delete operations. This makes a infinite loop in schedule function Now all of list items are deleted in one place and cancelled when task are complete Signed-off-by: Pan Xiuli Signed-off-by: Rander Wang Signed-off-by: Liam Girdwood --- src/audio/pipeline.c | 7 +++++- src/include/sof/schedule.h | 6 +++++ src/lib/schedule.c | 49 +++++++++++++++++++++++++++----------- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/audio/pipeline.c b/src/audio/pipeline.c index ee8cf6805431..b0839e0f4f99 100644 --- a/src/audio/pipeline.c +++ b/src/audio/pipeline.c @@ -1131,7 +1131,12 @@ void pipeline_schedule_copy_idle(struct pipeline *p) void pipeline_schedule_cancel(struct pipeline *p) { - schedule_task_complete(&p->pipe_task); + int err; + + /* cancel and wait for pipeline to complete */ + err = schedule_task_cancel(&p->pipe_task, 1); + if (err < 0) + trace_pipe_error("pC0"); } static void pipeline_task(void *arg) diff --git a/src/include/sof/schedule.h b/src/include/sof/schedule.h index 83d834545424..5d5227e12862 100644 --- a/src/include/sof/schedule.h +++ b/src/include/sof/schedule.h @@ -38,6 +38,7 @@ #include #include #include +#include struct sof; @@ -55,6 +56,8 @@ struct sof; #define TASK_PRI_MED 0 #define TASK_PRI_HIGH -20 +/* maximun task time slice in microseconds */ +#define SCHEDULE_TASK_MAX_TIME_SLICE 5000 /* task descriptor */ struct task { @@ -72,6 +75,7 @@ struct task { /* runtime duration in scheduling clock base */ uint64_t max_rtime; /* max time taken to run */ + completion_t complete; }; void schedule(void); @@ -80,6 +84,8 @@ void schedule_task(struct task *task, uint64_t start, uint64_t deadline); void schedule_task_idle(struct task *task, uint64_t deadline); +int schedule_task_cancel(struct task *task, int wait); + void schedule_task_complete(struct task *task); static inline void schedule_task_init(struct task *task, void (*func)(void *), diff --git a/src/lib/schedule.c b/src/lib/schedule.c index b98934d2bdda..c4bcedd65f73 100644 --- a/src/lib/schedule.c +++ b/src/lib/schedule.c @@ -174,6 +174,7 @@ static struct task *schedule_edf(void) struct task *task; struct task *future_task = NULL; uint64_t current; + uint32_t flags; tracev_pipe("edf"); @@ -196,7 +197,15 @@ static struct task *schedule_edf(void) } else { /* yes, run current task */ task->start = current; + + /* init task for running */ + wait_init(&task->complete); + spin_lock_irq(&sch->lock, flags); task->state = TASK_STATE_RUNNING; + list_item_del(&task->list); + spin_unlock_irq(&sch->lock, flags); + + /* now run task at correct run level */ arch_run_task(task); } @@ -204,33 +213,43 @@ static struct task *schedule_edf(void) return future_task; } -#if 0 /* FIXME: is this needed ? */ -/* delete task from scheduler */ -static int schedule_task_del(struct task *task) +/* cancel and delete task from scheduler - won't stop it if already running */ +int schedule_task_cancel(struct task *task, int wait) { uint32_t flags; int ret = 0; tracev_pipe("del"); - /* add task to list */ spin_lock_irq(&sch->lock, flags); - /* is task already running ? */ - if (task->state == TASK_STATE_RUNNING) { - ret = -EAGAIN; - goto out; + /* check current task state */ + switch (task->state) { + case TASK_STATE_QUEUED: + /* delete task */ + task->state = TASK_STATE_CANCEL; + list_item_del(&task->list); + break; + case TASK_STATE_RUNNING: + /* already running, nothing we can do about it atm */ + if (wait) { + task->complete.timeout = SCHEDULE_TASK_MAX_TIME_SLICE; + spin_unlock_irq(&sch->lock, flags); + ret = wait_for_completion_timeout(&task->complete); + goto out; + } else { + ret = -EBUSY; + } + break; + default: + break; } - list_item_del(&task->list); - task->state = TASK_STATE_COMPLETED; + spin_unlock_irq(&sch->lock, flags); out: - spin_unlock_irq(&sch->lock, flags); return ret; } -#endif - static int _schedule_task(struct task *task, uint64_t start, uint64_t deadline) { @@ -310,9 +329,11 @@ void schedule_task_complete(struct task *task) tracev_pipe("com"); spin_lock_irq(&sch->lock, flags); - list_item_del(&task->list); task->state = TASK_STATE_COMPLETED; spin_unlock_irq(&sch->lock, flags); + + /* tell any waiter that task has completed */ + wait_completed(&task->complete); } static void scheduler_run(void *unused)