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 <xiuli.pan@linux.intel.com>
Signed-off-by: Rander Wang <rander.wang@linux.intel.com>
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
This commit is contained in:
Rander Wang 2018-09-03 14:57:46 +08:00 committed by Pan Xiuli
parent 7be08fdbeb
commit d527b50816
3 changed files with 35 additions and 16 deletions

View File

@ -1275,7 +1275,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);
if (err < 0)
trace_pipe_error("pC0");
}
static void pipeline_task(void *arg)

View File

@ -38,6 +38,7 @@
#include <sof/lock.h>
#include <sof/list.h>
#include <sof/work.h>
#include <sof/wait.h>
struct schedule_data;
struct sof;
@ -56,6 +57,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 {
@ -73,6 +76,7 @@ struct task {
/* runtime duration in scheduling clock base */
uint64_t max_rtime; /* max time taken to run */
completion_t complete;
};
struct schedule_data **arch_schedule_get(void);
@ -83,6 +87,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);
void schedule_task_complete(struct task *task);
static inline void schedule_task_init(struct task *task, void (*func)(void *),

View File

@ -170,9 +170,11 @@ static uint64_t sch_work(void *data, uint64_t delay)
*/
static struct task *schedule_edf(void)
{
struct schedule_data *sch = *arch_schedule_get();
struct task *task;
struct task *future_task = NULL;
uint64_t current;
uint32_t flags;
tracev_pipe("edf");
@ -195,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 */
run_task(task);
}
@ -203,9 +213,8 @@ 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)
{
struct schedule_data *sch = *arch_schedule_get();
uint32_t flags;
@ -213,24 +222,21 @@ static int schedule_task_del(struct task *task)
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, delete it if it is queued
* if it is already running, nothing we can do about it atm
*/
if (task->state == TASK_STATE_QUEUED) {
/* delete task */
task->state = TASK_STATE_CANCEL;
list_item_del(&task->list);
}
list_item_del(&task->list);
task->state = TASK_STATE_COMPLETED;
out:
spin_unlock_irq(&sch->lock, flags);
return ret;
}
#endif
static int _schedule_task(struct task *task, uint64_t start, uint64_t deadline)
{
@ -312,9 +318,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)