diff --git a/include/kernel/kthread.h b/include/kernel/kthread.h index 112074aa675f2ff1295843b9330ff2f53baa5137..2f860ecc11909591d7ce3cca2dd3c0e8e3d3c35c 100644 --- a/include/kernel/kthread.h +++ b/include/kernel/kthread.h @@ -11,6 +11,7 @@ #include <list.h> #include <asm-generic/thread.h> #include <kernel/time.h> +#include <kernel/sched.h> @@ -35,14 +36,9 @@ struct remove_this_declaration { #define TASK_DEAD 0x0004 -enum sched_policy { - SCHED_RR, - SCHED_EDF, - SCHED_FIFO, - SCHED_OTHER, -}; -extern volatile int sched_edf; + +//extern volatile int sched_edf; struct task_struct { struct thread_info thread_info; @@ -68,11 +64,10 @@ struct task_struct { */ unsigned long stack_canary; - enum sched_policy policy; - unsigned long priority; - ktime period; /* wakeup period */ - ktime wcet; /* max runtime per period*/ - ktime deadline_rel; /* time to deadline from begin of wakeup*/ + + struct scheduler *sched; + + struct sched_attr attr; ktime runtime; /* remaining runtime in this period */ ktime wakeup; /* start of next period */ diff --git a/include/kernel/sched.h b/include/kernel/sched.h new file mode 100644 index 0000000000000000000000000000000000000000..fbb3ba55289737dd97c62173f6840d4ca373e030 --- /dev/null +++ b/include/kernel/sched.h @@ -0,0 +1,120 @@ +/** + * @file include/kernel/sched.h + */ + + +#ifndef _KERNEL_SCHED_H_ +#define _KERNEL_SCHED_H_ + + + + +enum sched_policy { + SCHED_RR, + SCHED_EDF, + SCHED_FIFO, + SCHED_OTHER, +}; + + +struct sched_attr { + enum sched_policy policy; + + /* static priority scheduling for RR, FIFO, ... */ + unsigned long priority; + + /* period based scheduling for EDF, RMS, ... */ + ktime period; /* wakeup period */ + ktime wcet; /* max runtime per period*/ + ktime deadline_rel; /* time to deadline from begin of wakeup */ +}; + + + + + +#if 0 +struct rq { + + /* runqueue lock: */ + raw_spinlock_t lock; + + struct dl_rq dl; +}; +#endif + + +struct task_queue { + struct list_head new; + struct list_head run; + struct list_head wake; + struct list_head dead; +}; + + + + + + + +#if 1 +struct scheduler { + + struct task_queue tq; + + const enum sched_policy policy; + + struct task_struct *(*pick_next_task)(struct task_queue *tq); + + /* XXX: sucks */ + void (*wake_next_task)(struct task_queue *tq); + void (*enqueue_task) (struct task_queue *tq, struct task_struct *task); + + ktime (*timeslice_ns) (struct task_struct *task); + ktime (*task_ready_ns) (struct task_queue *tq); + + int (*check_sched_attr) (struct sched_attr *attr); + + unsigned long sched_priority; /* scheduler priority */ + struct list_head node; +#if 0 + const struct sched_class *next; + + void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags); + void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags); + void (*yield_task) (struct rq *rq); + bool (*yield_to_task)(struct rq *rq, struct task_struct *p, bool preempt); + + void (*check_preempt_curr)(struct rq *rq, struct task_struct *p, int flags); + /* + * It is the responsibility of the pick_next_task() method that will + * return the next task to call put_prev_task() on the @prev task or + * something equivalent. + * + * May return RETRY_TASK when it finds a higher prio class has runnable + * tasks. + */ + struct task_struct * (*pick_next_task)(struct rq *rq, + struct task_struct *prev, + struct rq_flags *rf); +#endif +#if 0 + void (*put_prev_task)(struct rq *rq, struct task_struct *p); +#endif + +}; +#endif + + + + + +int sched_set_attr(struct task_struct *task, struct sched_attr *attr); +int sched_get_attr(struct task_struct *task, struct sched_attr *attr); + +int sched_set_policy_default(struct task_struct *task); +int sched_enqueue(struct task_struct *task); +int sched_register(struct scheduler *sched); + + +#endif /* _KERNEL_SCHED_H_ */ diff --git a/include/kernel/tick.h b/include/kernel/tick.h index e80edc0494ba4fbb15053b7605ce00d9211526e4..7f1b3d16e9a944bde2607c8c785eadc15f347208 100644 --- a/include/kernel/tick.h +++ b/include/kernel/tick.h @@ -36,5 +36,6 @@ void tick_check_device(struct clock_event_device *dev); int tick_set_mode(enum tick_mode mode); int tick_set_next_ns(unsigned long nanoseconds); int tick_set_next_ktime(struct timespec expires); +unsigned long tick_get_period_min_ns(void); #endif /* _KERNEL_TICK_H_ */ diff --git a/init/main.c b/init/main.c index a79bcf74420eef6abde99d213d6d8d598bd33684..1ab9e60dedc259cce0339860281a4277549fe082 100644 --- a/init/main.c +++ b/init/main.c @@ -49,8 +49,8 @@ int task1(void *data) { while (1) { - // printk("."); - // sched_yield(); + printk("."); + sched_yield(); } } @@ -58,11 +58,20 @@ int task1(void *data) int task2(void *data) { while (1) { - // printk("-"); - // sched_yield(); + printk("-"); + sched_yield(); } } +int task3(void *data) +{ + while (1) { + printk("x"); + sched_yield(); + } +} + + extern struct task_struct *kernel; @@ -150,7 +159,7 @@ int kernel_main(void) /* elevate boot thread */ kernel = kthread_init_main(); - +#if 0 /* * T1: (P=50, D=20, R=10) * @@ -177,19 +186,38 @@ int kernel_main(void) t = kthread_create(task1, NULL, KTHREAD_CPU_AFFINITY_NONE, "T7"); kthread_set_sched_edf(t, 50 * MSEC_PER_SEC, 6 * MSEC_PER_SEC, 46 * MSEC_PER_SEC); +#endif +#if 1 +{ + struct sched_attr attr; -#if 0 t = kthread_create(task1, NULL, KTHREAD_CPU_AFFINITY_NONE, "print"); - t->priority = 4; + sched_get_attr(t, &attr); + attr.priority = 4; + sched_set_attr(t, &attr); kthread_wake_up(t); t = kthread_create(task2, NULL, KTHREAD_CPU_AFFINITY_NONE, "print1"); - t->priority = 8; + sched_get_attr(t, &attr); + attr.priority = 8; + sched_set_attr(t, &attr); + kthread_wake_up(t); + + + t = kthread_create(task3, NULL, KTHREAD_CPU_AFFINITY_NONE, "edfprint"); + sched_get_attr(t, &attr); + attr.policy = SCHED_EDF; + attr.period = 9000000; + attr.deadline_rel = 1100000; + attr.wcet = 1000000; + sched_set_attr(t, &attr); kthread_wake_up(t); + +} #endif diff --git a/kernel/kthread.c b/kernel/kthread.c index dfa624245b4795a10ceb3a80189978fe49ad0af1..eefbb11e1a75c3d35be76f808f9576a8f23d753f 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -92,7 +92,7 @@ void sched_yield(void) struct task_struct *tsk; tsk = current_set[0]->task; - if (tsk->policy == SCHED_EDF) + if (tsk->attr.policy == SCHED_EDF) tsk->runtime = 0; schedule(); @@ -109,19 +109,19 @@ void sched_wake(struct task_struct *next, ktime now, int64_t slot_ns) task = list_entry(_kthreads.wake.next, struct task_struct, node); - if (task->policy == SCHED_EDF) { - if (next->policy == SCHED_EDF) + if (task->attr.policy == SCHED_EDF) { + if (next->attr.policy == SCHED_EDF) return; /* initially set current time as wakeup */ task->wakeup = ktime_add(now, slot_ns); - task->deadline = ktime_add(task->wakeup, task->deadline_rel); + task->deadline = ktime_add(task->wakeup, task->attr.deadline_rel); task->first_wake = task->wakeup; task->first_dead = task->deadline; list_move(&task->node, &_kthreads.run); } - if (task->policy == SCHED_RR) { + if (task->attr.policy == SCHED_RR) { task->state = TASK_RUN; list_move(&task->node, &_kthreads.run); } @@ -129,139 +129,7 @@ void sched_wake(struct task_struct *next, ktime now, int64_t slot_ns) } -#define MIN_SLICE 1000000LL /* 1 ms */ -#define OVERHEAD 0LL - -/* XXX */ -int64_t schedule_edf(ktime now); - -void schedule(void) -{ - struct task_struct *next; - struct task_struct *current; - int64_t slot_ns = MIN_SLICE; - - - - ktime now; - - - if (list_empty(&_kthreads.run)) - return; - - - - /* XXX: dummy; need solution for sched_yield() so timer is - * disabled either via disable_tick or need_resched variable - * (in thread structure maybe?) - * If we don't do this here, the timer may fire while interrupts - * are disabled, which may land us here yet again - */ - - - arch_local_irq_disable(); - - kthread_lock(); - - now = ktime_add(ktime_get(), OVERHEAD); - - tick_set_next_ns(1e9); - - - - current = current_set[0]->task; - - - - if (current->policy == SCHED_EDF) { - ktime d; - d = ktime_delta(now, current->exec_start); - BUG_ON(d < 0); - current->total = ktime_add(current->total, d); - current->slices++; - current->runtime = ktime_sub(current->runtime, d); - //printk("%lld %lld\n", d, current->runtime); - } - - - /** XXX not here, add cleanup thread */ - kthread_cleanup_dead(); - - -#if 1 - { - static int init; - if (!init) { /* dummy switch */ - tick_set_mode(TICK_MODE_ONESHOT); - init = 1; - } - } -#endif - - - - - - - - -#if 1 - slot_ns = schedule_edf(now); -#endif - - - /* round robin as before */ - do { - - next = list_entry(_kthreads.run.next, struct task_struct, node); - - BUG_ON(!next); - - if (next->state == TASK_RUN) { - list_move_tail(&next->node, &_kthreads.run); - break; - } - if (next->state == TASK_IDLE) { - list_move_tail(&next->node, &_kthreads.run); - continue; - } - - if (next->state == TASK_DEAD) - list_move_tail(&next->node, &_kthreads.dead); - - } while (!list_empty(&_kthreads.run)); - - - if (next->policy == SCHED_EDF) { - if (next->runtime <= slot_ns) - slot_ns = next->runtime; /* XXX must track actual time because of IRQs */ - } - - if (next->policy == SCHED_RR) - slot_ns = (ktime) next->priority * MIN_SLICE; - - - - if (slot_ns > 0xffffffff) - slot_ns = 0xffffffff; - - BUG_ON (slot_ns < 18000); - - - sched_wake(next, now, slot_ns); - - next->exec_start = ktime_get(); - - kthread_unlock(); - - tick_set_next_ns(slot_ns); - - prepare_arch_switch(1); - switch_to(next); - - arch_local_irq_enable(); -} __attribute__((unused)) /* static */ void kthread_set_sched_policy(struct task_struct *task, @@ -269,7 +137,7 @@ __attribute__((unused)) { arch_local_irq_disable(); kthread_lock(); - task->policy = policy; + task->attr.policy = policy; kthread_unlock(); arch_local_irq_enable(); } @@ -284,8 +152,9 @@ void kthread_wake_up(struct task_struct *task) BUG_ON(task->state != TASK_NEW); task->state = TASK_IDLE; - - list_move_tail(&task->node, &_kthreads.wake); + /** XXX **/ + sched_enqueue(task); + //list_move_tail(&task->node, &_kthreads.wake); kthread_unlock(); arch_local_irq_enable(); @@ -303,13 +172,13 @@ struct task_struct *kthread_init_main(void) return ERR_PTR(-ENOMEM); /* XXX accessors */ - task->policy = SCHED_RR; /* default */ - task->priority = 1; + task->attr.policy = SCHED_RR; /* default */ + task->attr.priority = 1; arch_promote_to_task(task); task->name = "KERNEL"; - task->policy = SCHED_RR; + BUG_ON(sched_set_policy_default(task)); arch_local_irq_disable(); kthread_lock(); @@ -320,7 +189,9 @@ struct task_struct *kthread_init_main(void) task->state = TASK_RUN; - list_add_tail(&task->node, &_kthreads.run); + + sched_enqueue(task); + /*list_add_tail(&task->node, &_kthreads.run);*/ @@ -380,9 +251,10 @@ static struct task_struct *kthread_create_internal(int (*thread_fn)(void *data), BUG_ON(!task->name); vsnprintf(task->name, 32, namefmt, args); - /* XXX accessors */ - task->policy = SCHED_RR; /* default */ - task->priority = 1; + if (sched_set_policy_default(task)) { + pr_crit("KTHREAD: must destroy task at this point\n"); + BUG(); + } task->total = 0; task->slices = 0; @@ -393,7 +265,8 @@ static struct task_struct *kthread_create_internal(int (*thread_fn)(void *data), arch_local_irq_disable(); kthread_lock(); - list_add_tail(&task->node, &_kthreads.new); + /** XXX **/ /*sched_enqueue(task); */ + //list_add_tail(&task->node, &_kthreads.new); kthread_unlock(); arch_local_irq_enable(); diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..0101ffdb2171727789140c4a81c9ea6a26404de2 --- /dev/null +++ b/kernel/sched/Makefile @@ -0,0 +1,3 @@ +obj-y += core.o +obj-y += rr.o +obj-y += edf.o diff --git a/kernel/sched/core.c b/kernel/sched/core.c new file mode 100644 index 0000000000000000000000000000000000000000..8edbc890a0169386a84a3b2ca84d35527be92f94 --- /dev/null +++ b/kernel/sched/core.c @@ -0,0 +1,247 @@ +/** + * @file kernel/sched/core.c + * + * @brief the core scheduling code + */ + + + +#include <kernel/err.h> +#include <kernel/kthread.h> +#include <kernel/sched.h> +#include <kernel/init.h> +#include <kernel/tick.h> +#include <asm-generic/irqflags.h> +#include <asm-generic/spinlock.h> +#include <asm/switch_to.h> +#include <string.h> + + + +#define MSG "SCHEDULER: " + +static LIST_HEAD(kernel_schedulers); + + +/* XXX: per-cpu */ +extern struct thread_info *current_set[1]; + + + +void schedule(void) +{ + struct scheduler *sched; + + struct task_struct *next = NULL; + + struct task_struct *current; + int64_t slot_ns; + int64_t wake_ns = 0; + + + + + arch_local_irq_disable(); + /* kthread_lock(); */ + + + /* XXX: for now, try to wake up any threads not running + * this is a waste of cycles and instruction space; should be + * done in the scheduler's code (somewhere) */ + list_for_each_entry(sched, &kernel_schedulers, node) { + sched->wake_next_task(&sched->tq); + } + + current = current_set[0]->task; + + /* XXX need sorted list: highest->lowest scheduler priority, e.g.: + * EDF -> RMS -> FIFO -> RR + * TODO: scheduler priority value + */ + list_for_each_entry(sched, &kernel_schedulers, node) { + + next = sched->pick_next_task(&sched->tq); + + /* check if we need to limit the next tasks timeslice; + * since our scheduler list is sorted by scheduler priority, + * only update the value if wake_next is not set; + * we assume that the timeslice is reasonable; if not fix it in + * the corresponding scheduler + */ + if (!wake_ns) + wake_ns = sched->task_ready_ns(&sched->tq); + + if (next) + break; + } + + + if (!next) { + /* nothing to do, check again later */ + if (wake_ns) + tick_set_next_ns(wake_ns); + else + tick_set_next_ns(1e9); /* XXX pause for a second */ + + goto exit; + } + + /* see if we can use a full slice or if we have to wake earlier */ + if (wake_ns) + slot_ns = wake_ns; + else + slot_ns = next->sched->timeslice_ns(next); + + /* statistics */ + next->exec_start = ktime_get(); + + /* kthread_unlock(); */ + + tick_set_next_ns(slot_ns); + + prepare_arch_switch(1); + switch_to(next); + +exit: + arch_local_irq_enable(); +} + + + + +/** + * @brief enqueue a task + */ + +int sched_enqueue(struct task_struct *task) +{ + if (!task->sched) { + pr_err(MSG "no scheduler configured for task %s\n", task->name); + return -EINVAL; + } + + /** XXX retval **/ + if (task->sched->check_sched_attr(&task->attr)) + return -EINVAL; + + task->sched->enqueue_task(&task->sched->tq, task); + + return 0; +} + + +/** + * @brief set a scheduling attribute for a task + * + * @returns 0 on success, < 0 on error + * + * XXX: should implement list of all threads, so we can use pid_t pid + * + * XXX: no error checking of attr params + */ + +int sched_set_attr(struct task_struct *task, struct sched_attr *attr) +{ + struct scheduler *sched; + + + if (!task) + goto error; + + if (!attr) + goto error; + + + list_for_each_entry(sched, &kernel_schedulers, node) { + + if (sched->policy == attr->policy) { + + memcpy(&task->attr, attr, sizeof(struct sched_attr)); + + if (sched->check_sched_attr(attr)) + goto error; + + task->sched = sched; + + /* XXX other stuff */ + + return 0; + } + } + + pr_crit(MSG "specified policy %d not available\n", attr->policy); + +error: + task->sched = NULL; + return -EINVAL; +} + + +/** + * @brief get a scheduling attribute for a task + * XXX: should implement list of all threads, so we can use pid_t pid + */ + +int sched_get_attr(struct task_struct *task, struct sched_attr *attr) +{ + + if (!task) + return -EINVAL; + + if (!attr) + return -EINVAL; + + + memcpy(attr, &task->attr, sizeof(struct sched_attr)); + + + return 0; +} + + +/** + * @brief set a task to the default scheduling policy + */ + +int sched_set_policy_default(struct task_struct *task) +{ + struct sched_attr attr = {.policy = SCHED_RR, + .priority = 1}; + + return sched_set_attr(task, &attr); +} + + +/** + * @brief register a new scheduler + */ + +int sched_register(struct scheduler *sched) +{ + /* XXX locks */ + + + /* XXX stupid */ + if (!sched->sched_priority) + list_add_tail(&sched->node, &kernel_schedulers); + else + list_add(&sched->node, &kernel_schedulers); + + return 0; +} + + +/** + * @brief scheduler initcall + * + * @note sets tick mode to oneshot + */ + +static int sched_init(void) +{ + tick_set_mode(TICK_MODE_ONESHOT); + tick_set_next_ns(1e9); /* XXX default to 1s ticks initially */ + + return 0; +} +late_initcall(sched_init); diff --git a/kernel/sched/edf.c b/kernel/sched/edf.c index 8bc3c15588431d7b0d1c787f6b604806e6593589..ee5de0d1ce1aba4836829ecabf50c8d9423493f9 100644 --- a/kernel/sched/edf.c +++ b/kernel/sched/edf.c @@ -20,11 +20,7 @@ #include <kernel/tick.h> -extern struct remove_this_declaration _kthreads; -void kthread_set_sched_policy(struct task_struct *task, - enum sched_policy policy); - - +#if 0 void sched_print_edf_list_internal(ktime now) { @@ -46,7 +42,7 @@ void sched_print_edf_list_internal(ktime now) printk("----------------------------------------------\n"); list_for_each_entry_safe(tsk, tmp, &_kthreads.run, node) { - if (tsk->policy == SCHED_RR) + if (tsk->attr.policy == SCHED_RR) continue; rel_deadline = ktime_delta(tsk->deadline, now); @@ -76,7 +72,7 @@ void sched_print_edf_list(void) { printk("avg: %lld\n", ktime_to_us(total/times)); } - +#endif /** * Our EDF task scheduling timeline: * @@ -125,7 +121,7 @@ static inline bool schedule_edf_can_execute(struct task_struct *tsk, ktime now) printk("%s violated, %lld %lld, %lld %lld\n", tsk->name, tsk->runtime, ktime_us_delta(tsk->deadline, now), tsk->deadline, now); - sched_print_edf_list_internal(now); + // sched_print_edf_list_internal(now); BUG(); return false; } @@ -137,7 +133,7 @@ static inline bool schedule_edf_can_execute(struct task_struct *tsk, ktime now) - if (tsk->wcet * to_deadline < tsk->period * tsk->runtime) + if (tsk->attr.wcet * to_deadline < tsk->attr.period * tsk->runtime) return true; @@ -152,18 +148,18 @@ static inline void schedule_edf_reinit_task(struct task_struct *tsk, ktime now) { tsk->state = TASK_IDLE; - tsk->wakeup = ktime_add(tsk->wakeup, tsk->period); + tsk->wakeup = ktime_add(tsk->wakeup, tsk->attr.period); #if 0 if (ktime_after(now, tsk->wakeup)) printk("%s delta %lld\n",tsk->name, ktime_us_delta(tsk->wakeup, now)); BUG_ON(ktime_after(now, tsk->wakeup)); /* deadline missed earlier? */ #endif - tsk->deadline = ktime_add(tsk->wakeup, tsk->deadline_rel); + tsk->deadline = ktime_add(tsk->wakeup, tsk->attr.deadline_rel); - tsk->runtime = tsk->wcet; + tsk->runtime = tsk->attr.wcet; } - +#if 0 #define SOME_DEFAULT_TICK_PERIOD_FOR_SCHED_MODE 10000000LL /* stupidly sort EDFs */ @@ -183,7 +179,7 @@ static inline void schedule_edf_reinit_task(struct task_struct *tsk, ktime now) list_for_each_entry_safe(tsk, tmp, &_kthreads.run, node) { - if (tsk->policy != SCHED_EDF) + if (tsk->attr.policy != SCHED_EDF) continue; /* time to wake up yet? */ @@ -226,7 +222,7 @@ static inline void schedule_edf_reinit_task(struct task_struct *tsk, ktime now) wake = ktime_add(now, slot); list_for_each_entry_safe(tsk, tmp, &_kthreads.run, node) { - if (tsk->policy != SCHED_EDF) + if (tsk->attr.policy != SCHED_EDF) break; /* all currently runnable task are at the top of the list */ @@ -267,6 +263,7 @@ static inline void schedule_edf_reinit_task(struct task_struct *tsk, ktime now) return slot; } +#endif /** * @@ -511,7 +508,7 @@ static inline void schedule_edf_reinit_task(struct task_struct *tsk, ktime now) * */ -static ktime hyperperiod(void) +static ktime edf_hyperperiod(struct task_queue *tq) { ktime lcm = 0; @@ -522,20 +519,20 @@ static ktime hyperperiod(void) struct task_struct *tmp; - if (list_empty(&_kthreads.new)) + if (list_empty(&tq->new)) return 0; - t0 = list_entry(_kthreads.new.next, struct task_struct, node); + t0 = list_entry(tq->new.next, struct task_struct, node); - lcm = t0->period; + lcm = t0->attr.period; - list_for_each_entry_safe(tsk, tmp, &_kthreads.new, node) { + list_for_each_entry_safe(tsk, tmp, &tq->new, node) { if (tsk == t0) continue; a = lcm; - b = tsk->period; + b = tsk->attr.period; /* already a multiple? */ if (a % b == 0) @@ -548,165 +545,386 @@ static ktime hyperperiod(void) b -= a; } - lcm = lcm * (tsk->period / a); + lcm = lcm * (tsk->attr.period / a); } return lcm; } -#define MIN(A,B) ((A) < (B) ? (A) : (B)) -void kthread_set_sched_edf(struct task_struct *task, unsigned long period_us, - unsigned long wcet_us, unsigned long deadline_rel_us) -{ - /* XXX schedulability tests */ +/**** NEW EDF ****/ - if (wcet_us >= period_us) { - printk("Cannot schedule EDF task with WCET %u >= PERIOD %u !\n", wcet_us, period_us); - return; - } +#include <kernel/init.h> - if (wcet_us >= deadline_rel_us) { - printk("Cannot schedule EDF task with WCET %u >= DEADLINE %u !\n", wcet_us, deadline_rel_us); - return; - } +#define MSG "SCHED_EDF: " - if (deadline_rel_us >= period_us) { - printk("Cannot schedule EDF task with DEADLINE %u >= PERIOD %u !\n", deadline_rel_us, period_us); - return; - } +/** + * @brief EDF schedulability test + * + * @returns 0 if schedulable, <0 otherwise + */ - arch_local_irq_disable(); - task->period = us_to_ktime(period_us); - task->wcet = us_to_ktime(wcet_us); - //task->runtime = ktime_sub(task->wcet, 7000LL); - task->runtime = task->wcet; - task->deadline_rel = us_to_ktime(deadline_rel_us); - arch_local_irq_enable(); +static int edf_schedulable(struct task_queue *tq, const struct task_struct *task) +{ - printk("\nvvvv EDF analysis vvvv\n\n"); - { - ktime p = hyperperiod(); - ktime h ; - ktime max = 0; - ktime uh, ut, f1; + ktime p = edf_hyperperiod(tq); + ktime h ; + ktime max = 0; - struct task_struct *t0 = NULL; - struct task_struct *tsk = NULL; - struct task_struct *tmp; + ktime uh, ut, f1; - if (p < 0) - printk("appears to be empty\n"); + struct task_struct *t0 = NULL; + struct task_struct *tsk = NULL; + struct task_struct *tmp; - list_for_each_entry_safe(tsk, tmp, &_kthreads.new, node) { + double u = 0.0; /* utilisation */ - if (tsk->period > max) { - t0 = tsk; - max = tsk->period; - } - } - BUG_ON(!t0); + static int64_t dmin = 0x7fffffffffffffLL; - BUG_ON(p < t0->period); - h = p / t0->period; + printk("\nvvvv EDF analysis vvvv (%lld) \n\n", p); - printk("Period factor %lld, duration %lld actual min period: %lld\n", h, ktime_to_ms(p), ktime_to_ms(t0->period)); + /* list_empty(....) */ + if (p <= 0) + printk("appears to be empty\n"); + list_for_each_entry_safe(tsk, tmp, &tq->new, node) { + printk("%s\n", tsk->name); + if (tsk->attr.period > max) { + t0 = tsk; + max = tsk->attr.period; + } + } - uh = h * (t0->deadline_rel - t0->wcet); - ut = h * (t0->period - t0->deadline_rel); - f1 = ut/h; + BUG_ON(!t0); + BUG_ON(p < t0->attr.period); + h = p / t0->attr.period; - printk("max UH: %lld, UT: %lld\n", ktime_to_ms(uh), ktime_to_ms(ut)); + printk("Period factor %lld, duration %lld actual min period: %lld\n", h, ktime_to_ms(p), ktime_to_ms(t0->attr.period)); - list_for_each_entry_safe(tsk, tmp, &_kthreads.new, node) { - ktime sh, st; - if (tsk == t0) - continue; + uh = h * (t0->attr.deadline_rel - t0->attr.wcet); + ut = h * (t0->attr.period - t0->attr.deadline_rel); + f1 = ut/h; - if (tsk->deadline_rel < t0->deadline_rel) { + printk("max UH: %lld, UT: %lld\n", ktime_to_ms(uh), ktime_to_ms(ut)); - /* slots before deadline of T0 */ - sh = h * tsk->wcet * t0->deadline_rel / tsk->period; - if (sh > uh) { - printk("NOT SCHEDULABLE in head: %s\n", tsk->name); - BUG(); - } - uh = uh - sh; + list_for_each_entry_safe(tsk, tmp, &tq->new, node) { + ktime sh, st; - } + if (tsk == t0) + continue; - /* slots after deadline of T0 */ - st = h * tsk->wcet * f1 / tsk->period; - printk("%s tail usage: %lld\n", tsk->name, ktime_to_ms(st)); - if (st > ut) { - printk("NOT SCHEDULABLE in tail: %s\n", tsk->name); - BUG(); - } - ut = ut - st; + if (tsk->attr.deadline_rel < t0->attr.deadline_rel) { - printk("UH: %lld, UT: %lld\n", ktime_to_ms(uh), ktime_to_ms(ut)); + /* slots before deadline of T0 */ + sh = h * tsk->attr.wcet * t0->attr.deadline_rel / tsk->attr.period; + if (sh > uh) { + printk("NOT SCHEDULABLE in head: %s\n", tsk->name); + BUG(); + } + uh = uh - sh; } - - } + /* slots after deadline of T0 */ + st = h * tsk->attr.wcet * f1 / tsk->attr.period; + printk("%s tail usage: %lld\n", tsk->name, ktime_to_ms(st)); + if (st > ut) { + printk("NOT SCHEDULABLE in tail: %s\n", tsk->name); + BUG(); + } + ut = ut - st; + printk("UH: %lld, UT: %lld\n", ktime_to_ms(uh), ktime_to_ms(ut)); - { - double u = 0.0; /* utilisation */ - struct task_struct *tsk; - struct task_struct *tmp; - static int64_t dmin = 0x7fffffffffffffLL; - if (dmin > task->deadline_rel) - dmin = task->deadline_rel; + if (dmin > task->attr.deadline_rel) + dmin = task->attr.deadline_rel; - u += (double) (int32_t) task->wcet / (double) (int32_t) task->period; + u += (double) (int32_t) task->attr.wcet / (double) (int32_t) task->attr.period; - list_for_each_entry_safe(tsk, tmp, &_kthreads.new, node) { + list_for_each_entry_safe(tsk, tmp, &tq->new, node) { - if (tsk->policy != SCHED_EDF) + if (tsk->attr.policy != SCHED_EDF) continue; - u += (double) (int32_t) tsk->wcet / (double) (int32_t) tsk->period; + u += (double) (int32_t) tsk->attr.wcet / (double) (int32_t) tsk->attr.period; } if (u > 1.0) { printk("I am NOT schedul-ableh: %g ", u); - kthread_set_sched_policy(task, SCHED_RR); + return -EINVAL; printk("changed task mode to RR\n", u); } else { printk("Utilisation %g\n", u); - kthread_set_sched_policy(task, SCHED_EDF); + return 0; + } + } + + + u = (double) (int32_t) task->attr.wcet / (double) (int32_t) task->attr.period; + + printk("was the first task %g\n", u); + + return 0; +} + +/** XXX **/ +static int64_t slot; + + + + +static struct task_struct *edf_pick_next(struct task_queue *tq) +{ +#define SOME_DEFAULT_TICK_PERIOD_FOR_SCHED_MODE 10000000LL + int64_t delta; + + + struct task_struct *go = NULL; + struct task_struct *tsk; + struct task_struct *tmp; + ktime now = ktime_get(); +slot = SOME_DEFAULT_TICK_PERIOD_FOR_SCHED_MODE; + + + list_for_each_entry_safe(tsk, tmp, &tq->run, node) { + + /* time to wake up yet? */ + delta = ktime_delta(tsk->wakeup, now); + if (delta >= 0) { + /* nope, just update minimum runtime for this slot */ + if (delta < slot) + slot = delta; + + continue; } + + /* if it's already running, see if there is time remaining */ + if (tsk->state == TASK_RUN) { + if (!schedule_edf_can_execute(tsk, now)) { + schedule_edf_reinit_task(tsk, now); + /* nope, update minimum runtime for this slot */ + delta = ktime_delta(tsk->wakeup, now); + if (delta < slot) + slot = delta; + continue; + } + + /* move to top */ + list_move(&tsk->node, &tq->run); + continue; + } + + /* time to wake up */ + if (tsk->state == TASK_IDLE) { + tsk->state = TASK_RUN; + /* move to top */ + list_move(&tsk->node, &tq->run); + go = tsk; + } + + } + + /** XXX **/ + //tsk = list_entry(tq->run.next, struct task_struct, node); + + if (tsk->state == TASK_RUN) + return tsk; + else + return go; +} + + +static void edf_wake_next(struct task_queue *tq) +{ + + struct task_struct *task; + + if (list_empty(&tq->wake)) + return; + + task = list_entry(tq->wake.next, struct task_struct, node); + +// if (next->attr.policy == SCHED_EDF) +// return; + /* initially set current time as wakeup */ + task->wakeup = ktime_add(ktime_get(), task->attr.period); + task->deadline = ktime_add(task->wakeup, task->attr.deadline_rel); + task->first_wake = task->wakeup; + task->first_dead = task->deadline; + + list_move(&task->node, &tq->run); +} + + + + +static void edf_enqueue(struct task_queue *tq, struct task_struct *task) +{ + /* reset runtime to full */ + task->runtime = task->attr.wcet; + + /* XXX */ + list_add_tail(&task->node, &tq->new); + + + if (task->sched->check_sched_attr(&task->attr)) + return; + + if (edf_schedulable(tq, task)) { + printk("---- NOT SCHEDUL-ABLE---\n"); + return; } - printk("\n^^^^ EDF analysis ^^^^\n"); + /** XXX **/ + if (task->state == TASK_RUN) + list_add_tail(&task->node, &tq->run); + else + list_add_tail(&task->node, &tq->wake); + +} + + +static ktime edf_timeslice_ns(struct task_struct *task) +{ + return 0; +} + +static int edf_check_sched_attr(struct sched_attr *attr) +{ + if (!attr) + goto error; + + if (attr->policy != SCHED_EDF) { + pr_err(MSG "attribute policy is %d, expected SCHED_EDF (%d)\n", + attr->policy, SCHED_EDF); + return -EINVAL; + } + + /* need only check WCET, all other times are longer */ + if (attr->wcet < (ktime) tick_get_period_min_ns()) { + pr_err(MSG "Cannot schedule EDF task with WCET of %lld ns, " + "minimum tick duration is %ld\n", attr->wcet, + tick_get_period_min_ns()); + goto error; + } + + if (attr->wcet >= attr->period) { + pr_err(MSG "Cannot schedule EDF task with WCET %u >= " + "PERIOD %u!\n", attr->wcet, attr->period); + goto error; + } + + if (attr->wcet >= attr->deadline_rel) { + pr_err(MSG "Cannot schedule EDF task with WCET %u >= " + "DEADLINE %u !\n", attr->wcet, attr->deadline_rel); + goto error; + } + + if (attr->deadline_rel >= attr->period) { + pr_err(MSG "Cannot schedule EDF task with DEADLINE %u >= " + "PERIOD %u !\n", attr->deadline_rel, attr->period); + goto error; + } + + + return 0; +error: + + return -EINVAL; +} + + +ktime edf_task_ready_ns(struct task_queue *tq) +{ + /* now find the closest relative deadline */ + int64_t delta; + + + struct task_struct *tsk; + struct task_struct *tmp; + ktime now = ktime_get(); + ktime wake; -// arch_local_irq_enable(); + wake = ktime_add(now, slot); + list_for_each_entry_safe(tsk, tmp, &tq->run, node) { + /* all currently runnable task are at the top of the list */ + if (tsk->state != TASK_RUN) + break; + + if (ktime_before(wake, tsk->deadline)) + continue; + + delta = ktime_delta(wake, tsk->deadline); + + if (delta < 0) { + delta = ktime_delta(now, tsk->deadline); + printk("\n [%lld] %s deadline violated by %lld us\n", ktime_to_ms(now), tsk->name, ktime_to_us(delta)); + } - printk("\n"); + if (delta < slot) { + if (delta) + slot = delta; + else + delta = tsk->runtime; + wake = ktime_add(now, slot); /* update next wakeup */ + /* move to top */ + list_move(&tsk->node, &tq->run); + BUG_ON(slot <= 0); + } + } + + slot = 1000000; + BUG_ON(slot < 0); + + return slot; } + +static struct scheduler sched_edf = { + .policy = SCHED_EDF, + .pick_next_task = edf_pick_next, + .wake_next_task = edf_wake_next, + .enqueue_task = edf_enqueue, + .timeslice_ns = edf_timeslice_ns, + .task_ready_ns = edf_task_ready_ns, + .check_sched_attr = edf_check_sched_attr, + .sched_priority = 1, +}; + + + +static int sched_edf_init(void) +{ + /* XXX */ + INIT_LIST_HEAD(&sched_edf.tq.new); + INIT_LIST_HEAD(&sched_edf.tq.run); + INIT_LIST_HEAD(&sched_edf.tq.wake); + INIT_LIST_HEAD(&sched_edf.tq.dead); + + sched_register(&sched_edf); + + return 0; +} +postcore_initcall(sched_edf_init); diff --git a/kernel/sched/rr.c b/kernel/sched/rr.c new file mode 100644 index 0000000000000000000000000000000000000000..232e19c5afe5faf6c1491d482bc3697efe01308e --- /dev/null +++ b/kernel/sched/rr.c @@ -0,0 +1,160 @@ +/** + * @file kernel/sched/round_robin.c + * + * @brief round-robin scheduler + */ + + +#include <errno.h> +#include <kernel/init.h> +#include <kernel/tick.h> +#include <kernel/kthread.h> + +#define MSG "SCHED_RR: " + + +static struct task_struct *rr_pick_next(struct task_queue *tq) +{ + struct task_struct *next = NULL; + + + while (!list_empty(&tq->run)) { + + next = list_entry(tq->run.next, struct task_struct, node); + + BUG_ON(!next); + + if (next->state == TASK_RUN) { + /* XXX: must pick head first, then move tail on put() + * following a scheduling event. for now, just force + * round robin + */ + list_move_tail(&next->node, &tq->run); + break; + } + + if (next->state == TASK_IDLE) + list_move_tail(&next->node, &tq->run); + + if (next->state == TASK_DEAD) + list_move_tail(&next->node, &tq->dead); + + } + + + return next; +} + + +/* this sucks, wrong place. keep for now */ +static void rr_wake_next(struct task_queue *tq) +{ + + struct task_struct *task; + + if (list_empty(&tq->wake)) + return; + + + task = list_entry(tq->wake.next, struct task_struct, node); + + BUG_ON(task->attr.policy != SCHED_RR); + /** XXX NO LOCKS */ + task->state = TASK_RUN; + list_move(&task->node, &tq->run); +} + + +static void rr_enqueue(struct task_queue *tq, struct task_struct *task) +{ + + /** XXX **/ + if (task->state == TASK_RUN) + list_add_tail(&task->node, &tq->run); + else + list_add_tail(&task->node, &tq->wake); +} + +/** + * @brief get the requested timeslice of a task + * + * @note RR timeslices are determined from their configured priority + * XXX: must not be 0 + * + * @note for now, just take the minimum tick period to fit as many RR slices + * as possible. This will jack up the IRQ rate, but RR tasks only run when + * the system is not otherwise busy; + * still, a larger (configurable) extra factor may be desirable + */ + +static ktime rr_timeslice_ns(struct task_struct *task) +{ + return (ktime) task->attr.priority * tick_get_period_min_ns(); +} + + + +/** + * @brief sanity-check sched_attr configuration + * + * @return 0 on success -EINVAL on error + */ + +static int rr_check_sched_attr(struct sched_attr *attr) +{ + if (attr->policy != SCHED_RR) { + pr_err(MSG "attribute policy is %d, expected SCHED_RR (%d)\n", + attr->policy, SCHED_RR); + return -EINVAL; + } + + if (!attr->priority) { + pr_warn(MSG "minimum priority is 1, adjusted\n"); + } + + return 0; +} + + + +/** + * @brief return the time until the the next task is ready + * + * @note RR tasks are always "ready" and they do not have deadlines, + * so this function always returns 0 + */ + +ktime rr_task_ready_ns(struct task_queue *tq) +{ + return (ktime) 0ULL; +} + + + + +static struct scheduler sched_rr = { + .policy = SCHED_RR, + .pick_next_task = rr_pick_next, + .wake_next_task = rr_wake_next, + .enqueue_task = rr_enqueue, + .timeslice_ns = rr_timeslice_ns, + .task_ready_ns = rr_task_ready_ns, + .check_sched_attr = rr_check_sched_attr, + .sched_priority = 0, +}; + + + +static int sched_rr_init(void) +{ + /* XXX */ + INIT_LIST_HEAD(&sched_rr.tq.new); + INIT_LIST_HEAD(&sched_rr.tq.run); + INIT_LIST_HEAD(&sched_rr.tq.wake); + INIT_LIST_HEAD(&sched_rr.tq.dead); + + sched_register(&sched_rr); + + return 0; +} +postcore_initcall(sched_rr_init); diff --git a/kernel/tick.c b/kernel/tick.c index cd0ba943e1a957d4ca02eaed1831b55a63260c66..5269e26892b0261606eeb0f71b71a31756367667 100644 --- a/kernel/tick.c +++ b/kernel/tick.c @@ -19,6 +19,9 @@ #define MSG "TICK: " +/* the minimum effective tick period; default to 1 ms */ +static unsigned long tick_period_min_ns = 1000000UL; + static struct clock_event_device *tick_device; @@ -100,6 +103,32 @@ static int tick_set_mode_oneshot(struct clock_event_device *dev) return 0; } +/** + * @brief calibrate the minimum processable tick length for this device + * + * XXX: + * what this will do: + * - disable scheduling + * - mask all interrupts except timer (maybe) + * - in tick_event_handler, record time between calls + * - keep decreasing tick length until time between calls does not decrement + * (i.e. interrupt response limit has been hit) + * - NOTE: check clockevents_timout_in_range() or somesuch to clamp to + * actual timer range (maybe add function to clockevents to + * return actual timer minimum + * - multiply tick length by some factor (2...10) + * - ??? + * - profit! + */ + +static void tick_calibrate_min(struct clock_event_device *dev) +{ +#define RANDOM_TICK_RATE_NS 18000UL + tick_period_min_ns = RANDOM_TICK_RATE_NS; +#define MIN_SLICE 1000000UL + tick_period_min_ns = MIN_SLICE; +} + /** * @brief configure the tick device @@ -107,13 +136,14 @@ static int tick_set_mode_oneshot(struct clock_event_device *dev) static void tick_setup_device(struct clock_event_device *dev) { -#define RANDOM_TICK_RATE_NS 18000 - clockevents_set_handler(dev, tick_event_handler); + tick_calibrate_min(dev); - /* FIXME: assume blindly for the moment */ + /* FIXME: assume blindly for the moment, should apply mode + * of previous clock device (if replaced) */ tick_set_mode_periodic(dev); - clockevents_program_timeout_ns(dev, RANDOM_TICK_RATE_NS); + clockevents_set_handler(dev, tick_event_handler); + clockevents_program_timeout_ns(dev, tick_period_min_ns); } @@ -141,6 +171,9 @@ void tick_check_device(struct clock_event_device *dev) tick_set_device(dev, 0); tick_setup_device(dev); + + /* XXX should inform scheduler to recalculate any deadline-related + * timeouts of tasks */ } @@ -171,6 +204,20 @@ int tick_set_mode(enum tick_mode mode) } +/** + * @brief get the (calibrated) minimum effective tick period + * + * @note This represents a safe-to-use value of the best-effort interrupt + * processing time of the system. + */ + +unsigned long tick_get_period_min_ns(void) +{ + return tick_period_min_ns; +} + + + /** * @brief configure next tick period in nanoseconds *