[Sound-open-firmware] [PATCH 2/3] scheduler: Add support for 64bit timer driven scheduling.

Liam Girdwood liam.r.girdwood at linux.intel.com
Fri Sep 22 01:32:34 CEST 2017


Currently the scheduler will schedule task based on DAI DMA interrupts.
This patch also adds the option to also schedule based on a 64 bit timer.

The scheduler will now check the tasks in the queue and it will only
run the tasks in the current window otherwise it will set a timer to
call schedule() on the next task start time (in another window).

Signed-off-by: Liam Girdwood <liam.r.girdwood at linux.intel.com>
---
 src/audio/dai.c                   |   2 +-
 src/audio/pipeline.c              |  48 +++++++----
 src/include/reef/audio/pipeline.h |   4 +-
 src/include/reef/schedule.h       |  33 ++++---
 src/lib/schedule.c                | 175 ++++++++++++++++++++++++++++----------
 5 files changed, 182 insertions(+), 80 deletions(-)

diff --git a/src/audio/dai.c b/src/audio/dai.c
index e22d21d..2101e00 100644
--- a/src/audio/dai.c
+++ b/src/audio/dai.c
@@ -133,7 +133,7 @@ static void dai_dma_cb(void *data, uint32_t type, struct dma_sg_elem *next)
 	}
 
 	/* notify pipeline that DAI needs it's buffer processed */
-	pipeline_schedule_copy(dev->pipeline, dev);
+	pipeline_schedule_copy(dev->pipeline, 0);
 
 	return;
 }
diff --git a/src/audio/pipeline.c b/src/audio/pipeline.c
index c7b95f9..62c21b7 100644
--- a/src/audio/pipeline.c
+++ b/src/audio/pipeline.c
@@ -184,23 +184,29 @@ static void disconnect_downstream(struct pipeline *p, struct comp_dev *start,
 }
 
 /* update pipeline state based on cmd */
-static void pipeline_cmd_update(struct pipeline *p, int cmd)
+static void pipeline_cmd_update(struct pipeline *p, struct comp_dev *comp,
+	int cmd)
 {
+	if (p->sched_comp != comp)
+		return;
+
 	switch (cmd) {
 	case COMP_CMD_PAUSE:
-		break;
 	case COMP_CMD_STOP:
-		break;
-	case COMP_CMD_RELEASE:
-		p->xrun_bytes = 0;
+		pipeline_schedule_cancel(p);
 		break;
 	case COMP_CMD_START:
+	case COMP_CMD_RELEASE:
 		p->xrun_bytes = 0;
+
+		/* schedule pipeline */
+		if (p->ipc_pipe.timer)
+			pipeline_schedule_copy(p, 0);
+
 		break;
 	case COMP_CMD_SUSPEND:
 		break;
 	case COMP_CMD_RESUME:
-		p->xrun_bytes = 0;
 		break;
 	}
 }
@@ -222,7 +228,9 @@ struct pipeline *pipeline_new(struct sof_ipc_pipe_new *pipe_desc,
 
 	/* init pipeline */
 	p->sched_comp = cd;
-	task_init(&p->pipe_task, pipeline_task, p);
+	schedule_task_init(&p->pipe_task, pipeline_task, p);
+	schedule_task_config(&p->pipe_task, pipe_desc->priority,
+		pipe_desc->core);
 	list_init(&p->comp_list);
 	list_init(&p->buffer_list);
 	spinlock_init(&p->lock);
@@ -243,7 +251,7 @@ int pipeline_free(struct pipeline *p)
 	}
 
 	/* remove from any scheduling */
-	task_free(&p->pipe_task);
+	schedule_task_free(&p->pipe_task);
 
 	/* disconnect components */
 	disconnect_downstream(p, p->sched_comp, p->sched_comp);
@@ -337,7 +345,8 @@ static int component_op_downstream(struct op_data *op_data,
 		/* send command to the component and update pipeline state  */
 		err = comp_cmd(current, op_data->cmd, op_data->cmd_data);
 		if (err == 0)
-			pipeline_cmd_update(current->pipeline, op_data->cmd);
+			pipeline_cmd_update(current->pipeline, current,
+				op_data->cmd);
 		break;
 	case COMP_OPS_PREPARE:
 		/* prepare the component */
@@ -413,7 +422,8 @@ static int component_op_upstream(struct op_data *op_data,
 		/* send command to the component and update pipeline state  */
 		err = comp_cmd(current, op_data->cmd, op_data->cmd_data);
 		if (err == 0)
-			pipeline_cmd_update(current->pipeline, op_data->cmd);
+			pipeline_cmd_update(current->pipeline, current,
+				op_data->cmd);
 		break;
 	case COMP_OPS_PREPARE:
 		/* prepare the component */
@@ -957,15 +967,13 @@ void pipeline_xrun(struct pipeline *p, struct comp_dev *dev,
 }
 
 /* notify pipeline that this component requires buffers emptied/filled */
-void pipeline_schedule_copy(struct pipeline *p, struct comp_dev *dev)
+void pipeline_schedule_copy(struct pipeline *p, uint64_t start)
 {
-	schedule_task(&p->pipe_task, p->ipc_pipe.deadline,
-		p->ipc_pipe.priority, dev);
-
-	schedule();
+	if (p->sched_comp->state == COMP_STATE_ACTIVE)
+		schedule_task(&p->pipe_task, start, p->ipc_pipe.deadline);
 }
 
-void pipeline_schedule_cancel(struct pipeline *p, struct comp_dev *dev)
+void pipeline_schedule_cancel(struct pipeline *p)
 {
 	schedule_task_complete(&p->pipe_task);
 }
@@ -973,8 +981,7 @@ void pipeline_schedule_cancel(struct pipeline *p, struct comp_dev *dev)
 static void pipeline_task(void *arg)
 {
 	struct pipeline *p = arg;
-	struct task *task = &p->pipe_task;
-	struct comp_dev *dev = task->sdata;
+	struct comp_dev *dev = p->sched_comp;
 
 	tracev_pipe("PWs");
 
@@ -983,6 +990,11 @@ static void pipeline_task(void *arg)
 	pipeline_copy_to_downstream(dev, dev);
 
 	tracev_pipe("PWe");
+
+	/* now reschedule the task */
+	/* TODO: add in scheduling cost and any timer drift */
+	if (p->ipc_pipe.timer)
+		pipeline_schedule_copy(p, p->ipc_pipe.deadline);
 }
 
 /* init pipeline */
diff --git a/src/include/reef/audio/pipeline.h b/src/include/reef/audio/pipeline.h
index 29c509f..909f905 100644
--- a/src/include/reef/audio/pipeline.h
+++ b/src/include/reef/audio/pipeline.h
@@ -112,8 +112,8 @@ int init_static_pipeline(struct ipc *ipc);
 int init_pipeline(void);
 
 /* schedule a copy operation for this pipeline */
-void pipeline_schedule_copy(struct pipeline *p, struct comp_dev *dev);
-void pipeline_schedule_cancel(struct pipeline *p, struct comp_dev *dev);
+void pipeline_schedule_copy(struct pipeline *p, uint64_t start);
+void pipeline_schedule_cancel(struct pipeline *p);
 
 /* get time pipeline timestamps from host to dai */
 void pipeline_get_timestamp(struct pipeline *p, struct comp_dev *host_dev,
diff --git a/src/include/reef/schedule.h b/src/include/reef/schedule.h
index b22fb44..6d94788 100644
--- a/src/include/reef/schedule.h
+++ b/src/include/reef/schedule.h
@@ -37,6 +37,7 @@
 #include <reef/reef.h>
 #include <reef/lock.h>
 #include <reef/list.h>
+#include <reef/work.h>
 
 struct reef;
 
@@ -47,53 +48,59 @@ struct reef;
 #define TASK_STATE_PREEMPTED	3
 #define TASK_STATE_COMPLETED	4
 #define TASK_STATE_FREE		5
+#define TASK_STATE_CANCEL	6
 
 /* task priorities - values same as Linux processes, gives scope for future.*/
 #define TASK_PRI_LOW	19
 #define TASK_PRI_MED	0
 #define TASK_PRI_HIGH	-20
 
+
+/* task descriptor */
 struct task {
 	uint16_t core;			/* core id to run on */
 	int16_t priority;		/* scheduling priority TASK_PRI_ */
-	uint32_t deadline;		/* scheduling deadline */
-	uint32_t max_rtime;		/* max time taken to run */
+	uint64_t start;			/* scheduling earliest start time */
+	uint64_t deadline;		/* scheduling deadline */
 	uint32_t state;			/* TASK_STATE_ */
 	struct list_item list;		/* list in scheduler */
+
+	/* task function and private data */
 	void *data;
-	void *sdata;
 	void (*func)(void *arg);
+
+	/* runtime duration in scheduling clock base */
+	uint64_t max_rtime;		/* max time taken to run */
 };
 
 void schedule(void);
 
-void schedule_task(struct task *task, uint32_t deadline, uint16_t priority,
-		void *data);
-
-void schedule_task_core(struct task *task, uint32_t deadline,
-	uint16_t priority, uint16_t core, void *data);
+void schedule_task(struct task *task, uint64_t start, uint64_t deadline);
 
 void schedule_task_complete(struct task *task);
 
-static inline void task_init(struct task *task, void (*func)(void *),
+static inline void schedule_task_init(struct task *task, void (*func)(void *),
 	void *data)
 {
 	task->core = 0;
-	task->priority = TASK_PRI_MED;
 	task->state = TASK_STATE_INIT;
 	task->func = func;
 	task->data = data;
-	task->sdata = NULL;
 }
 
-static inline void task_free(struct task *task)
+static inline void schedule_task_free(struct task *task)
 {
 	task->state = TASK_STATE_FREE;
 	task->func = NULL;
 	task->data = NULL;
-	task->sdata = NULL;
 }
 
+static inline void schedule_task_config(struct task *task, uint16_t priority,
+	uint16_t core)
+{
+	task->priority = priority;
+	task->core = core;
+}
 
 int scheduler_init(struct reef *reef);
 
diff --git a/src/lib/schedule.c b/src/lib/schedule.c
index 147bfff..dbf278b 100644
--- a/src/lib/schedule.c
+++ b/src/lib/schedule.c
@@ -39,6 +39,7 @@
 #include <reef/debug.h>
 #include <reef/clock.h>
 #include <reef/schedule.h>
+#include <reef/work.h>
 #include <platform/timer.h>
 #include <platform/clk.h>
 #include <reef/audio/component.h>
@@ -49,67 +50,115 @@ struct schedule_data {
 	spinlock_t lock;
 	struct list_item list;	/* list of tasks in priority queue */
 	uint32_t clock;
+	struct work work;
 };
 
 static struct schedule_data *sch;
 
-/* get time delta */
-static inline uint32_t schedule_time_diff(uint32_t current, uint32_t pipe_time)
+#define SLOT_ALIGN_TRIES	10
+
+/*
+ * Simple rescheduler to calculate tasks new start time and deadline if
+ * prevoius deadline was missed. Tries to align at first with current task
+ * timing, but will just add onto current if too far behind current.
+ * XRUNs will be propagated upto the host if we have to reschedule.
+ */
+static inline void edf_reschedule(struct task *task, uint64_t current)
 {
-	uint32_t max = MAX_INT;
-
-	/* does work run in next cycle ? */
-	if (pipe_time < current) {
-		max -= current;
-		max += pipe_time;
-		return max;
-	} else
-		return pipe_time - current;
+	uint64_t delta = (task->deadline - task->start) << 1;
+	int i;
+
+	/* try and align task with current scheduling slots */
+	for (i = 0; i < SLOT_ALIGN_TRIES; i++) {
+
+		task->start += delta;
+
+		if (task->start > current + delta) {
+			task->deadline = task->start + delta;
+			return;
+		}
+	}
+
+	/* task has slipped a lot, so just add delay to current */
+	task->start = current + delta;
+	task->deadline = task->start + delta;
 }
 
 /*
- * Find the task with the earliest deadline. This may be the running task or a
- * queued task. TODO: Reduce cache invalidations by checking if the currently
+ * Find the first non running task with the earliest deadline.
+ * TODO: Reduce cache invalidations by checking if the currently
  * running task AND the earliest queued task will both complete before their
  * deadlines. If so, then schedule the earlier queued task after the currently
  * running task has completed.
  */
-static inline struct task *edf_get_next(void)
+static inline struct task *edf_get_next(uint64_t current,
+	struct task *ignore)
 {
 	struct task *task, *next_task = NULL;
-	struct list_item *clist;
-	uint32_t next_delta = MAX_INT, current, delta;
+	struct list_item *clist, *tlist;
+	uint64_t next_delta = UINT64_MAX, delta, deadline;
+	int reschedule = 0;
  
+	/* any tasks in the scheduler ? */
 	if (list_is_empty(&sch->list))
 		return NULL;
 
-	/* get the current time */
-	current = platform_timer_get(platform_timer);
-
 	/* check every queued or running task in list */
-	list_for_item(clist, &sch->list) {
+	list_for_item_safe(clist, tlist, &sch->list) {
 		task = container_of(clist, struct task, list);
 
+		/* only check queued tasks */
+		if (task->state != TASK_STATE_QUEUED)
+			continue;
+
+		/* include the length of task in deadline calc */
+		deadline = task->deadline - task->max_rtime;
+
 		/* get earliest deadline */
-		delta = schedule_time_diff(current, task->deadline);
-		if (delta < next_delta) {
-			next_delta = delta;
-			next_task = task;
+		if (current < deadline) {
+			delta = deadline - current;
+
+			if (delta < next_delta) {
+				next_delta = delta;
+				next_task = task;
+			}
+
+		} else {
+			/* missed scheduling - will be rescheduled */
+			trace_pipe("ed!");
+
+			/* have we already tried to rescheule ? */
+			if (reschedule++)
+				edf_reschedule(task, current);
+			else {
+				/* reschedule failed */
+				list_item_del(&task->list);
+				task->state = TASK_STATE_CANCEL;
+			}
 		}
 	}
 
 	return next_task;
 }
 
+/* work set in the future when next task can be scheduled */
+static uint32_t sch_work(void *data, uint32_t delay)
+{
+	tracev_pipe("wrk");
+	schedule();
+	return 0;
+}
+
 /*
  * EDF Scheduler - Earliest Deadline First Scheduler.
  *
  * Schedule task with the earliest deadline from task list.
  * Can run in IRQ context.
  */
-void schedule_edf(void)
+struct task *schedule_edf(void)
 {
-	struct task *task;
+	struct task *task, *next_plus1_task = NULL;
+	uint64_t current;
 	uint32_t flags;
 
 	tracev_pipe("edf");
@@ -117,19 +166,34 @@ void schedule_edf(void)
 	/* get next component scheduled  */
 	spin_lock_irq(&sch->lock, flags);
 
+	/* get the current time */
+	current = platform_timer_get(platform_timer);
+
 	/* get next task to be scheduled */
-	task = edf_get_next();
+	task = edf_get_next(current, NULL);
 
 	spin_unlock_irq(&sch->lock, flags);
 	interrupt_clear(PLATFORM_SCHEDULE_IRQ);
 
-	/* is task currently running ? */
-	if (task == NULL || task->state == TASK_STATE_RUNNING) {
-		trace_pipe("ed0");
-		return;
+	/* any tasks ? */
+	if (task == NULL)
+		return NULL;
+
+	/* can task be started now ? */
+	if (task->start > current) {
+		/* no, then schedule wake up */
+		next_plus1_task = task;
+	} else {
+		/* yes, get next task and run this one now */
+		next_plus1_task = edf_get_next(current, task);
+
+		/* run current task */
+		task->start = current;
+		arch_run_task(task);
 	}
 
-	arch_run_task(task);
+	/* teall caller about next task (after current) */
+	return next_plus1_task;
 }
 
 /* delete task from scheduler */
@@ -157,38 +221,50 @@ out:
 	return ret;
 }
 
-/* Add a new task to the scheduler to be run */
-void schedule_task(struct task *task, uint32_t deadline, uint16_t priority,
-		void *data)
+/*
+ * Add a new task to the scheduler to be run and define a scheduling
+ * window in time for the task to be ran. i.e. task will run between start and
+ * deadline times.
+ *
+ * start is in microseconds relative to last task start time.
+ * deadline is in microseconds relative to start.
+ */
+void schedule_task(struct task *task, uint64_t start, uint64_t deadline)
 {
-	uint32_t flags, time, current, ticks;
+	uint32_t flags;
+	uint64_t current;
 
-	tracev_pipe("add");
+	tracev_pipe("ad!");
 
 	spin_lock_irq(&sch->lock, flags);
 
 	/* is task already running ? - not enough MIPS to complete ? */
 	if (task->state == TASK_STATE_RUNNING) {
 		trace_pipe("tsk");
-		goto out;
+		spin_unlock_irq(&sch->lock, flags);
+		return;
 	}
 
 	/* get the current time */
 	current = platform_timer_get(platform_timer);
 
+	/* calculate start time - TODO: include MIPS */
+	if (start == 0)
+		task->start = current;
+	else
+		task->start = task->start + clock_us_to_ticks(sch->clock, start) -
+			PLATFORM_SCHEDULE_COST;
+
 	/* calculate deadline - TODO: include MIPS */
-	ticks = clock_us_to_ticks(sch->clock, deadline);
-	time = current + ticks - PLATFORM_SCHEDULE_COST;
+	task->deadline = task->start + clock_us_to_ticks(sch->clock, deadline);
 
 	/* add task to list */
-	task->deadline = time;
-	task->priority = priority;
-	task->sdata = data;
 	list_item_append(&task->list, &sch->list);
 	task->state = TASK_STATE_QUEUED;
-
-out:
 	spin_unlock_irq(&sch->lock, flags);
+
+	/* rerun scheduler */
+	schedule();
 }
 
 /* Remove a task from the scheduler when complete */
@@ -206,9 +282,15 @@ void schedule_task_complete(struct task *task)
 
 void scheduler_run(void *unused)
 {
+	struct task *next_task;
+
 	tracev_pipe("run");
+
 	/* EDF is only scheduler supported atm */
-	schedule_edf();
+	next_task = schedule_edf();
+	if (next_task) {
+		work_reschedule_default_at(&sch->work, next_task->start);
+	}
 }
 
 /* run the scheduler */
@@ -234,6 +316,7 @@ int scheduler_init(struct reef *reef)
 	list_init(&sch->list);
 	spinlock_init(&sch->lock);
 	sch->clock = PLATFORM_SCHED_CLOCK;
+	work_init(&sch->work, sch_work, sch, WORK_ASYNC);
 
 	/* configure scheduler interrupt */
 	interrupt_register(PLATFORM_SCHEDULE_IRQ, scheduler_run, NULL);
-- 
2.11.0



More information about the Sound-open-firmware mailing list