@@ 0,0 1,981 @@
+/* EDF scheduling */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "../port/devsched.h"
+#include "../port/edf.h"
+
+/* debugging */
+int edfprint = 0;
+char tabs[16] = " ";
+int ind;
+#define DPRINT if(edfprint)iprint
+#define DENTER ind++;if(edfprint)iprint
+#define DLEAVE ind--
+
+char *edf_statename[] = {
+ [EdfUnused] = "Unused",
+ [EdfExpelled] = "Expelled",
+ [EdfAdmitted] = "Admitted",
+ [EdfIdle] = "Idle",
+ [EdfAwaitrelease] = "Awaitrelease",
+ [EdfReleased] = "Released",
+ [EdfRunning] = "Running",
+ [EdfExtra] = "Extra",
+ [EdfPreempted] = "Preempted",
+ [EdfBlocked] = "Blocked",
+ [EdfDeadline] = "Deadline",
+};
+
+static Cycintr schedpoint; /* First scheduling point */
+static Ticks utilization; /* Current utilization */
+static int initialized;
+static uvlong fasthz;
+static Ticks now;
+QLock edfschedlock; /* schedulability, held for
+ */
+Lock edflock;
+
+Task tasks[Maxtasks];
+int ntasks;
+Resource resources[Maxresources];
+int nresources;
+int edf_stateupdate;
+
+enum{
+ Deadline, /* Invariant for schedulability test: Deadline < Release */
+ Release,
+};
+
+static int earlierrelease(Task *t1, Task *t2) {return t1->r < t2->r;}
+static int earlierdeadline(Task *t1, Task *t2) {return t1->d < t2->d;}
+
+/* Tasks waiting for release, head earliest release time */
+Taskq qwaitrelease = {{0}, nil, earlierrelease};
+
+/* Released tasks waiting to run, head earliest deadline */
+Taskq qreleased = {{0}, nil, earlierdeadline};
+
+/* Exhausted EDF tasks, append at end */
+Taskq qextratime;
+
+/* Tasks admitted waiting for first release */
+Taskq qadmit;
+
+/* Running/Preempted EDF tasks, head running, one stack per processor */
+Taskq edfstack[MAXMACH];
+
+void (*devsched)(Task*, Ticks, int);
+
+static void edf_intr(Ureg*, Cycintr*);
+static void edf_resched(Task *t);
+static void setΔ(void);
+static void testΔ(Task *thetask);
+static char * edf_testschedulability(Task *thetask);
+static void edf_setclock(void);
+
+void
+edf_init(void)
+{
+ if (initialized)
+ return;
+ ilock(&edflock);
+ if (initialized){
+ iunlock(&edflock);
+ return;
+ }
+ fastticks(&fasthz);
+ schedpoint.f = edf_intr;
+ schedpoint.a = &schedpoint;
+ schedpoint.when = 0;
+ initialized = 1;
+ iunlock(&edflock);
+}
+
+int
+isedf(Proc *p)
+{
+ return p && p->task && p->task->state >= EdfIdle;
+}
+
+int
+edf_anyready(void)
+{
+ /* If any edf tasks (with runnable procs in them) are released,
+ * at least one of them must be on the stack
+ */
+ return edfstack[m->machno].head != nil;
+}
+
+static void
+edfpush(Task *t)
+{
+ Taskq *q;
+
+ DENTER("%.*sedfpush, %s, %d\n", ind, tabs, edf_statename[t->state], t->runq.n);
+ q = edfstack + m->machno;
+ assert(t->runq.n || (up && up->task == t));
+ if (q->head){
+ assert(q->head->state == EdfRunning);
+ q->head->state = EdfPreempted;
+ if(devsched) devsched(q->head, now, SPreempt);
+ }
+ t->rnext = q->head;
+ if(devsched) devsched(t, now, SRun);
+ q->head = t;
+ DLEAVE;
+}
+
+static Task*
+edfpop(void)
+{
+ Task *t;
+ Taskq *q;
+
+ DENTER("%.*sedfpop\n", ind, tabs);
+ q = edfstack + m->machno;
+ if (t = q->head){
+ assert(t->state == EdfRunning);
+ q->head = t->rnext;
+ t->rnext = nil;
+ if (q->head){
+ assert(q->head->state == EdfPreempted);
+ q->head->state = EdfRunning;
+ if(devsched) devsched(q->head, now, SRun);
+ }
+ }
+ DLEAVE;
+ return t;
+}
+
+static Task*
+edfenqueue(Taskq *q, Task *t)
+{
+ Task *tt, **ttp;
+
+ ilock(q);
+ DENTER("%.*sedfenqueue, %s, %d\n", ind, tabs, edf_statename[t->state], t->runq.n);
+ t->rnext = nil;
+ if (q->head == nil) {
+ q->head = t;
+ DLEAVE;
+ iunlock(q);
+ return t;
+ }
+ SET(tt);
+ for (ttp = &q->head; *ttp; ttp = &tt->rnext) {
+ tt = *ttp;
+ if (q->before && q->before(t, tt)) {
+ t->rnext = tt;
+ *ttp = t;
+ break;
+ }
+ }
+ if (*ttp == nil)
+ tt->rnext = t;
+ if (t != q->head)
+ t = nil;
+ DLEAVE;
+ iunlock(q);
+ return t;
+}
+
+static Task*
+edfdequeue(Taskq *q)
+{
+ Task *t;
+
+ DENTER("%.*sedfdequeue\n", ind, tabs);
+ ilock(q);
+ if (t = q->head){
+ q->head = t->rnext;
+ t->rnext = nil;
+ }
+ iunlock(q);
+ DLEAVE;
+ return t;
+}
+
+static void
+edfqremove(Taskq *q, Task *t)
+{
+ Task **tp;
+
+ ilock(q);
+ DENTER("%.*sedfqremove, %s, %d\n", ind, tabs, edf_statename[t->state], t->runq.n);
+ for (tp = &q->head; *tp; tp = &(*tp)->rnext){
+ if (*tp == t){
+ *tp = t->rnext;
+ DLEAVE;
+ iunlock(q);
+ return;
+ }
+ }
+ DLEAVE;
+ iunlock(q);
+}
+
+void
+edf_block(Proc *p)
+{
+ Task *t, *pt;
+
+ /* The current proc has blocked */
+ ilock(&edflock);
+ t = p->task;
+ DENTER("%.*sedf_block, %s, %d\n", ind, tabs, edf_statename[t->state], t->runq.n);
+
+ assert(t);
+ assert(t->state == EdfRunning);
+ if (t->runq.n){
+ /* There's another runnable proc in the running task, leave task where it is */
+ iunlock(&edflock);
+ DLEAVE;
+ return;
+ }
+ pt = edfpop();
+ assert(pt == t);
+ t->state = EdfBlocked;
+ if(devsched) devsched(t, now, SBlock);
+ DLEAVE;
+ iunlock(&edflock);
+}
+
+static void
+edfdeadline(Proc *p, SEvent why)
+{
+ Task *t, *nt;
+
+ /* Task has reached its deadline, lock must be held */
+ DENTER("%.*sedfdeadline, %s, %d\n", ind, tabs, edf_statename[p->task->state], p->task->runq.n);
+ SET(nt);
+ if (p){
+ nt = p->task;
+ assert(nt);
+ assert(nt->state == EdfRunning);
+ }
+ t = edfpop();
+
+ if(p != nil && nt != t){
+ DPRINT("%.*sedfdeadline, %s, %d\n", ind, tabs, edf_statename[p->task->state], p->task->runq.n);
+ iunlock(&edflock);
+ assert(0 && p == nil || nt == t);
+ }
+
+ t->d = now;
+ t->state = EdfDeadline;
+ if(devsched) devsched(t, now, why);
+ edf_resched(t);
+ DLEAVE;
+}
+
+void
+edf_deadline(Proc *p)
+{
+ DENTER("%.*sedf_deadline\n", ind, tabs);
+ /* Task has reached its deadline */
+ ilock(&edflock);
+ now = fastticks(nil);
+ edfdeadline(p, SYield);
+ iunlock(&edflock);
+ DLEAVE;
+}
+
+char *
+edf_admit(Task *t)
+{
+ char *err;
+
+ if (t->state != EdfExpelled)
+ return "task state"; /* should never happen */
+ qlock(&edfschedlock);
+ if (err = edf_testschedulability(t)){
+ qunlock(&edfschedlock);
+ return err;
+ }
+ ilock(&edflock);
+ DENTER("%.*sedf_admit, %s, %d\n", ind, tabs, edf_statename[t->state], t->runq.n);
+ now = fastticks(nil);
+
+ t->state = EdfAdmitted;
+ if (up->task == t){
+ DPRINT("%.*sedf_admitting self\n", ind, tabs);
+ /* Admitting self, fake reaching deadline */
+ t->r = now;
+ t->t = now + t->T;
+ t->d = now + t->D;
+ if(devsched) devsched(t, t->d, SDeadline);
+ t->S = t->C;
+ t->scheduled = now;
+ t->state = EdfRunning;
+ if(devsched) devsched(t, now, SRun);
+ setΔ();
+ assert(t->runq.n > 0 || (up && up->task == t));
+ edfpush(t);
+ edf_setclock();
+ }else{
+ if (t->runq.n){
+ if (edfstack[m->machno].head == nil){
+ t->state = EdfAdmitted;
+ t->r = now;
+ edf_release(t);
+ setΔ();
+ edf_resched(t);
+ }else{
+ edfenqueue(&qadmit, t);
+ }
+ }
+ }
+ DLEAVE;
+ iunlock(&edflock);
+ qunlock(&edfschedlock);
+ return nil;
+}
+
+void
+edf_expel(Task *t)
+{
+ Task *tt;
+
+ qlock(&edfschedlock);
+ ilock(&edflock);
+ DENTER("%.*sedf_expel, %s, %d\n", ind, tabs, edf_statename[t->state], t->runq.n);
+ now = fastticks(nil);
+ switch(t->state){
+ case EdfUnused:
+ case EdfExpelled:
+ /* That was easy */
+ DLEAVE;
+ iunlock(&edflock);
+ qunlock(&edfschedlock);
+ return;
+ case EdfAdmitted:
+ case EdfIdle:
+ /* Just reset state */
+ break;
+ case EdfAwaitrelease:
+ edfqremove(&qwaitrelease, t);
+ break;
+ case EdfReleased:
+ edfqremove(&qreleased, t);
+ break;
+ case EdfRunning:
+ /* Task must be expelling itself */
+ tt = edfpop();
+ assert(t == tt);
+ break;
+ case EdfExtra:
+ edfqremove(&qextratime, t);
+ break;
+ case EdfPreempted:
+ edfqremove(edfstack + m->machno, t);
+ break;
+ case EdfBlocked:
+ case EdfDeadline:
+ break;
+ }
+ t->state = EdfExpelled;
+ if(devsched) devsched(t, now, SExpel);
+ setΔ();
+ DLEAVE;
+ iunlock(&edflock);
+ qunlock(&edfschedlock);
+ return;
+}
+
+static void
+edf_timer(void)
+{
+ Ticks used;
+ Task *t;
+
+ // If up is not set, we're running inside the scheduler
+ // for non-real-time processes.
+ if (up && isedf(up)) {
+ t = up->task;
+ assert(t->scheduled > 0);
+
+ used = now - t->scheduled;
+ t->scheduled = now;
+
+ if (t->r < now){
+ if (t->S <= used)
+ t->S = 0LL;
+ else
+ t->S -= used;
+
+ if (t->d <= now || t->S == 0LL){
+ /* Task has reached its deadline/slice, remove from queue */
+ edfdeadline(up, SSlice);
+ while (t = edfstack[m->machno].head){
+ if (now < t->d)
+ break;
+ edfdeadline(nil, SSlice);
+ }
+ }
+ }
+ }
+
+ while((t = qwaitrelease.head) && t->r <= now){
+ /* There's something waiting to be released and its time has come */
+ edfdequeue(&qwaitrelease);
+ edf_release(t);
+ }
+}
+
+static void
+edf_setclock(void)
+{
+ Ticks ticks;
+ Task *t;
+
+ DENTER("%.*sedf_setclock\n", ind, tabs);
+ ticks = ~0ULL;
+ if ((t = qwaitrelease.head) && t->r < ticks)
+ ticks = t->r;
+ if (t = edfstack[m->machno].head){
+ if (t->d < ticks)
+ ticks = t->d;
+ if (now + t->S < ticks)
+ ticks = now + t->S;
+ }
+ if (schedpoint.when > now && schedpoint.when <= ticks){
+ DLEAVE;
+ return;
+ }
+ if (schedpoint.when){
+ DPRINT("%.*scycintrdel %T\n", ind, tabs, ticks2time(schedpoint.when));
+ cycintrdel(&schedpoint);
+ schedpoint.when = 0;
+ }
+ if (ticks <= now){
+ DPRINT("%.*sedf_timer: %T too late\n", ind, tabs, ticks2time(now-ticks));
+ ticks = now;
+ }
+ if (ticks != ~0ULL) {
+ DPRINT("%.*sprogram timer in %T\n", ind, tabs, ticks2time(ticks-now));
+ schedpoint.when = ticks;
+ cycintradd(&schedpoint);
+ DPRINT("%.*scycintradd %T\n", ind, tabs, ticks2time(schedpoint.when-now));
+ }
+ clockintrsched();
+ DLEAVE;
+}
+
+static void
+edf_intr(Ureg *, Cycintr *cy)
+{
+
+ DENTER("%.*sedf_intr\n", ind, tabs);
+ /* Timer interrupt
+ * Timed events are:
+ * 1. release a task (look in qwaitrelease)
+ * 2. task reaches deadline
+ */
+ now = fastticks(nil);
+
+ assert(cy == &schedpoint && schedpoint.when <= now);
+
+ if(active.exiting)
+ return;
+
+ ilock(&edflock);
+ edf_timer();
+ edf_setclock();
+ iunlock(&edflock);
+ DLEAVE;
+ sched();
+ splhi();
+}
+
+void
+edf_bury(Proc *p)
+{
+ Task *t;
+ Proc **pp;
+
+ DPRINT("%.*sedf_bury\n", ind, tabs);
+ ilock(&edflock);
+ now = fastticks(nil);
+ if ((t = p->task) == nil){
+ /* race condition? */
+ iunlock(&edflock);
+ DPRINT("%.*sedf bury race, pid %lud\n", ind, tabs, p->pid);
+ return;
+ }
+ assert(edfstack[m->machno].head == t);
+ for (pp = t->procs; pp < t->procs + nelem(t->procs); pp++)
+ if (*pp == p){
+ t->nproc--;
+ *pp = nil;
+ }
+ if (t->runq.head == nil){
+ edfpop();
+ t->state = EdfBlocked;
+ }
+ if (t->nproc == 0){
+ assert(t->runq.head == nil);
+ t->state = EdfIdle;
+ }
+ if(devsched) devsched(t, now, SBlock);
+ p->task = nil;
+ iunlock(&edflock);
+}
+
+void
+edf_ready(Proc *p)
+{
+ Task *t;
+
+ ilock(&edflock);
+ DENTER("%.*sedf_ready, %s, %d\n", ind, tabs, edf_statename[p->task->state], p->task->runq.n);
+ if ((t = p->task) == nil){
+ /* Must be a race */
+ iunlock(&edflock);
+ DPRINT("%.*sedf ready race, pid %lud\n", ind, tabs, p->pid);
+ return;
+ }
+ p->rnext = 0;
+ p->readytime = m->ticks;
+ p->state = Ready;
+ t->runq.n++;
+ if(t->runq.tail){
+ t->runq.tail->rnext = p;
+ t->runq.tail = p;
+ }else{
+ t->runq.head = p;
+ t->runq.tail = p;
+
+ /* first proc to become runnable in this task */
+ now = fastticks(nil);
+ edf_resched(t);
+ }
+ DLEAVE;
+ iunlock(&edflock);
+}
+
+static void
+edf_resched(Task *t)
+{
+ Task *xt;
+
+ DENTER("%.*sedf_resched, %s, %d\n", ind, tabs, edf_statename[t->state], t->runq.n);
+ if (t->nproc == 0){
+ /* No member processes */
+ if (t->state > EdfIdle){
+ t->state = EdfIdle;
+ if(devsched) devsched(t, now, SBlock);
+ }
+ DLEAVE;
+ return;
+ }
+ if (t->runq.n == 0 && (up == nil || up->task != t)){
+ /* Member processes but none runnable */
+ DPRINT("%.*sedf_resched, nothing runnable\n", ind, tabs);
+ if (t->state == EdfRunning)
+ edfpop();
+
+ if (t->state >= EdfIdle && t->state != EdfBlocked){
+ t->state = EdfBlocked;
+ if(devsched) devsched(t, now, SBlock);
+ }
+ DLEAVE;
+ return;
+ }
+
+ /* There are runnable processes */
+
+ switch (t->state){
+ case EdfUnused:
+ iprint("%.*sattempt to schedule unused task\n", ind, tabs);
+ case EdfExpelled:
+ DLEAVE;
+ return; /* Not admitted */
+ case EdfIdle:
+ /* task was idle, schedule release now or later */
+ if (t->r < now){
+ if (t->t < now)
+ t->t = now + t->T;
+ t->r = t->t;
+ }
+ edf_release(t);
+ break;
+ case EdfAwaitrelease:
+ case EdfReleased:
+ case EdfExtra:
+ case EdfPreempted:
+ /* dealt with by timer */
+ break;
+ case EdfAdmitted:
+ /* test whether task can be started */
+ if (edfstack[m->machno].head != nil){
+ DLEAVE;
+ return;
+ }
+ /* fall through */
+ case EdfRunning:
+ if (t->r <= now){
+ if (t->t < now){
+ DPRINT("%.*sedf_resched, rerelease\n", ind, tabs);
+ /* Period passed, rerelease */
+ t->r = now;
+ xt = edfpop();
+ assert(xt == t);
+ edf_release(t);
+ DLEAVE;
+ return;
+ }
+ if (now < t->d){
+ if (t->S > 0){
+ DPRINT("%.*sedf_resched, resume\n", ind, tabs);
+ /* Running, not yet at deadline, leave it */
+ DLEAVE;
+ return;
+ }else
+ t->d = now;
+ }
+ /* Released, but deadline is past, release at t->t */
+ t->r = t->t;
+ }
+ DPRINT("%.*sedf_resched, schedule release\n", ind, tabs);
+ xt = edfpop();
+ assert(xt == t);
+ edfenqueue(&qwaitrelease, t);
+ t->state = EdfAwaitrelease;
+ edf_setclock();
+ break;
+ case EdfBlocked:
+ case EdfDeadline:
+ if (t->r <= now){
+ if (t->t < now){
+ DPRINT("%.*sedf_resched, rerelease\n", ind, tabs);
+ /* Period passed, rerelease */
+ t->r = now;
+ edf_release(t);
+ DLEAVE;
+ return;
+ }
+ if (now < t->d && (t->flags & Useblocking) == 0){
+ if (t->S > 0){
+ DPRINT("%.*sedf_resched, resume\n", ind, tabs);
+ /* Released, not yet at deadline, release (again) */
+ t->state = EdfReleased;
+ edfenqueue(&qreleased, t);
+ if(devsched) devsched(t, now, SResume);
+ DLEAVE;
+ return;
+ }else
+ t->d = now;
+ }
+ /* Released, but deadline is past, release at t->t */
+ t->r = t->t;
+ }
+ DPRINT("%.*sedf_resched, schedule release\n", ind, tabs);
+ edfenqueue(&qwaitrelease, t);
+ t->state = EdfAwaitrelease;
+ edf_setclock();
+ break;
+ }
+ DLEAVE;
+}
+
+void
+edf_release(Task *t)
+{
+ DENTER("%.*sedf_release, %s, %d\n", ind, tabs, edf_statename[t->state], t->runq.n);
+ assert(t->runq.n > 0 || (up && up->task == t));
+ t->t = t->r + t->T;
+ t->d = t->r + t->D;
+ if(devsched) devsched(t, t->d, SDeadline);
+ t->S = t->C;
+ t->state = EdfReleased;
+ edfenqueue(&qreleased, t);
+ if(devsched) devsched(t, now, SRelease);
+ edf_setclock();
+ DLEAVE;
+}
+
+Proc *
+edf_runproc(void)
+{
+ /* Return an edf proc to run or nil */
+
+ Task *t, *nt;
+ Proc *p;
+// Ticks when;
+ static ulong nilcount;
+
+ /* Figure out if the current proc should be preempted*/
+ ilock(&edflock);
+ assert(ind < nelem(tabs));
+ now = fastticks(nil);
+
+ /* first candidate is at the top of the stack of running procs */
+ t = edfstack[m->machno].head;
+
+ /* check out head of the release queue for a proc with a better deadline */
+ nt = qreleased.head;
+
+ if (t == nil && nt == nil){
+ nilcount++;
+ iunlock(&edflock);
+ return nil;
+ }
+ DENTER("edf_runproc %lud\n", nilcount);
+ if (nt && (t == nil || (nt->D < t->Δ && nt->d < t->d))){
+ /* released task is better than current */
+ DPRINT("%.*sedf_runproc: released\n", ind, tabs);
+ edfdequeue(&qreleased);
+ assert(nt->runq.n >= 1);
+ edfpush(nt);
+ nt->state = EdfRunning;
+ t = nt;
+ t->scheduled = now;
+ }else{
+ DPRINT("%.*sedf_runproc: current\n", ind, tabs);
+ }
+
+ assert (t->runq.n);
+
+ /* Get first proc off t's run queue
+ * No need to lock runq, edflock always held to access runq
+ */
+ t->state = EdfRunning;
+ p = t->runq.head;
+ if ((t->runq.head = p->rnext) == nil)
+ t->runq.tail = nil;
+ t->runq.n--;
+ p->state = Scheding;
+ if(p->mp != MACHP(m->machno))
+ p->movetime = MACHP(0)->ticks + HZ/10;
+ p->mp = MACHP(m->machno);
+ edf_setclock();
+ DLEAVE;
+ iunlock(&edflock);
+ return p;
+}
+
+static Lock waitlock;
+
+int
+edf_waitlock(Lock *l)
+{
+ iprint("edf_waitlock\n");
+ ilock(&waitlock); /* can't afford normal locks here */
+ if (l->key == 0){
+ /* race on lock, don't block, just return */
+ iunlock(&waitlock);
+ return 0;
+ }
+ edf_block(up);
+ up->rnext = l->edfwaiting; /* enqueue on lock */
+ l->edfwaiting = up;
+ up->state = Scheding;
+ up->lockwait = l;
+ iunlock(&waitlock);
+ return 1;
+}
+
+void
+edf_releaselock(Lock *l)
+{
+ Proc *p;
+
+ iprint("edf_releaselock\n");
+ ilock(&waitlock); /* can't afford normal locks here */
+ if(l->edfwaiting == nil){
+ iunlock(&waitlock);
+ return;
+ }
+ p = l->edfwaiting;
+ l->edfwaiting = p->rnext;
+ assert(p->lockwait == l);
+ if(p->state != Scheding)
+ print("edf_releaselock: %s %lud %s\n", p->text, p->pid, statename[p->state]);
+ p->lockwait = nil;
+ iunlock(&waitlock);
+ edf_ready(p);
+}
+
+
+/* Schedulability testing and its supporting routines */
+
+static void
+setΔ(void)
+{
+ Resource *r, **rr;
+ Task **tt, *t;
+
+ for (r = resources; r < resources + nelem(resources); r++){
+ if (r->name == nil)
+ continue;
+ r->Δ = ~0LL;
+ for (tt = r->tasks; tt < r->tasks + nelem(r->tasks); tt++)
+ if (*tt && (*tt)->D < r->Δ)
+ r->Δ = (*tt)->D;
+ }
+ for (t = tasks; t < tasks + nelem(tasks); t++){
+ if (t->state < EdfIdle)
+ continue;
+ t->Δ = t->D;
+ for (rr = t->res; rr < t->res + nelem(t->res); rr++)
+ if (*rr && (*rr)->Δ < t->Δ)
+ t->Δ = (*rr)->Δ;
+ }
+}
+
+static void
+testΔ(Task *thetask)
+{
+ Resource *r, **rr;
+ Task **tt, *t;
+
+ for (r = resources; r < resources + nelem(resources); r++){
+ if (r->name == nil)
+ continue;
+ r->testΔ = ~0ULL;
+ for (tt = r->tasks; tt < r->tasks + nelem(r->tasks); tt++)
+ if (*tt && (*tt)->D < r->testΔ)
+ r->testΔ = (*tt)->D;
+ }
+ for (t = tasks; t < tasks + nelem(tasks); t++){
+ if (t->state <= EdfExpelled && t != thetask)
+ continue;
+ t->testΔ = t->D;
+ for (rr = t->res; rr < t->res + nelem(t->res); rr++)
+ if (*rr && (*rr)->testΔ < t->testΔ)
+ t->testΔ = (*rr)->testΔ;
+ }
+}
+
+static Ticks
+blockcost(Ticks ticks, Task *thetask)
+{
+ Task *t;
+ Ticks Cb;
+
+ Cb = 0;
+ for (t = tasks; t < tasks + Maxtasks; t++){
+ if (t->state <= EdfExpelled && t != thetask)
+ continue;
+ if (t->testΔ <= ticks && ticks < t->D && Cb < t->C)
+ Cb = t->C;
+ }
+ return Cb;
+}
+
+static Task *qschedulability;
+
+static void
+testenq(Task *t)
+{
+ Task *tt, **ttp;
+
+ t->testnext = nil;
+ if (qschedulability == nil) {
+ qschedulability = t;
+ return;
+ }
+ SET(tt);
+ for (ttp = &qschedulability; *ttp; ttp = &tt->testnext) {
+ tt = *ttp;
+ if (t->testtime < tt->testtime
+ || (t->testtime == tt->testtime && t->testtype < tt->testtype)){
+ t->testnext = tt;
+ *ttp = t;
+ return;
+ }
+ }
+ assert(tt->testnext == nil);
+ tt->testnext = t;
+}
+
+static char *
+edf_testschedulability(Task *thetask)
+{
+ Task *t;
+ Ticks H, G, Cb, ticks;
+ int steps;
+
+ /* initialize */
+ testΔ(thetask);
+ if (thetask && (thetask->flags & Verbose))
+ pprint("schedulability test\n");
+ qschedulability = nil;
+ for (t = tasks; t < tasks + Maxtasks; t++){
+ if (t->state <= EdfExpelled && t != thetask)
+ continue;
+ t->testtype = Release;
+ t->testtime = 0;
+ if (thetask && (thetask->flags & Verbose))
+ pprint("\tInit: enqueue task %lud\n", t - tasks);
+ testenq(t);
+ }
+ H=0;
+ G=0;
+ ticks = 0;
+ for(steps = 0; steps < Maxsteps; steps++){
+ t = qschedulability;
+ qschedulability = t->testnext;
+ ticks = t->testtime;
+ switch (t->testtype){
+ case Deadline:
+ H += t->C;
+ Cb = blockcost(ticks, thetask);
+ if (thetask && (thetask->flags & Verbose))
+ pprint("\tStep %3d, Ticks %T, task %lud, deadline, H += %T → %T, Cb = %T\n",
+ steps, ticks2time(ticks), t - tasks,
+ ticks2time(t->C), ticks2time(H), ticks2time(Cb));
+ if (H+Cb>ticks)
+ return "not schedulable";
+ t->testtime += t->T - t->D;
+ t->testtype = Release;
+ testenq(t);
+ break;
+ case Release:
+ if (thetask && (thetask->flags & Verbose))
+ pprint("\tStep %3d, Ticks %T, task %lud, release, G %T, C%T\n",
+ steps, ticks2time(ticks), t - tasks,
+ ticks2time(t->C), ticks2time(G));
+ if(ticks && G <= ticks)
+ return nil;
+ G += t->C;
+ t->testtime += t->D;
+ t->testtype = Deadline;
+ testenq(t);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ return "probably not schedulable";
+}
+
+static uvlong
+uvmuldiv(uvlong x, ulong num, ulong den)
+{
+ /* multiply, then divide, avoiding overflow */
+ uvlong hi;
+
+ hi = (x & 0xffffffff00000000LL) >> 32;
+ x &= 0xffffffffLL;
+ hi *= num;
+ return (x*num + (hi%den << 32)) / den + (hi/den << 32);
+}
+
+Time
+ticks2time(Ticks ticks)
+{
+ assert(ticks >= 0);
+ return uvmuldiv(ticks, Onesecond, fasthz);
+}
+
+Ticks
+time2ticks(Time time)
+{
+ assert(time >= 0);
+ return uvmuldiv(time, fasthz, Onesecond);
+}
@@ 4,6 4,7 @@
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
+#include "../port/edf.h"
int nrdy;
Ref noteidalloc;
@@ 18,17 19,10 @@ static struct Procalloc
Proc* free;
} procalloc;
-typedef struct
-{
- Lock;
- Proc* head;
- Proc* tail;
- int n;
-} Schedq;
static Schedq runq[Nrq];
char *statename[] =
-{ /* BUG: generate automatically */
+{ /* BUG: generate automatically */
"Dead",
"Moribund",
"Ready",
@@ 41,6 35,7 @@ char *statename[] =
"Broken",
"Stopped",
"Rendez",
+ "Released",
};
static void pidhash(Proc*);
@@ 61,6 56,10 @@ schedinit(void) /* never returns */
break;
case Moribund:
up->state = Dead;
+
+ if (isedf(up))
+ edf_bury(up);
+
/*
* Holding locks from pexit:
* procalloc
@@ 76,7 75,7 @@ schedinit(void) /* never returns */
break;
}
up->mach = 0;
- up = 0;
+ up = nil;
}
sched();
}
@@ 113,7 112,7 @@ sched(void)
int
anyready(void)
{
- return nrdy;
+ return nrdy || edf_anyready();
}
int
@@ 127,7 126,7 @@ anyhigher(void)
for(rq = &runq[Nrq-1]; rq > &runq[up->priority]; rq--)
if(rq->head != nil)
return 1;
-
+
return 0;
}
@@ 144,6 143,11 @@ ready(Proc *p)
s = splhi();
+ if(isedf(p)){
+ edf_ready(p);
+ splx(s);
+ return;
+ }
if(p->fixedpri){
pri = p->basepri;
} else {
@@ 159,7 163,7 @@ ready(Proc *p)
pri = p->basepri - (pri/Squantum);
if(pri < 0)
pri = 0;
-
+
/* the only intersection between the classes is at PriNormal */
if(pri < PriNormal && p->basepri > PriNormal)
pri = PriNormal;
@@ 194,6 198,9 @@ runproc(void)
Proc *p, *l;
ulong rt;
+ if ((p = edf_runproc()) != nil)
+ return p;
+
loop:
/*
@@ 279,6 286,7 @@ found:
if(p->mp != MACHP(m->machno))
p->movetime = MACHP(0)->ticks + HZ/10;
p->mp = MACHP(m->machno);
+
return p;
}
@@ 356,6 364,8 @@ newproc(void)
if(p->kstack == 0)
p->kstack = smalloc(KSTACK);
+ p->task = nil;
+
return p;
}
@@ 459,7 469,7 @@ sleep(Rendez *r, int (*f)(void*), void *arg)
/* statistics */
m->cs++;
-
+
procsave(up);
if(setlabel(&up->sched)) {
/*
@@ 473,6 483,11 @@ sleep(Rendez *r, int (*f)(void*), void *arg)
*/
unlock(&up->rlock);
unlock(r);
+
+ // Behind unlock, we may call wakeup on ourselves.
+ if (isedf(up))
+ edf_block(up);
+
gotolabel(&m->sched);
}
}
@@ 566,7 581,6 @@ wakeup(Rendez *r)
ready(p);
unlock(&p->rlock);
}
-
unlock(r);
splx(s);
@@ 677,6 691,8 @@ addbroken(Proc *p)
broken.p[broken.n++] = p;
qunlock(&broken);
+ if (isedf(up))
+ edf_bury(up);
p->state = Broken;
p->psstate = 0;
sched();
@@ 844,6 860,8 @@ pexit(char *exitstr, int freemem)
lock(&procalloc);
lock(&palloc);
+ if (isedf(up))
+ edf_bury(up);
up->state = Moribund;
sched();
panic("pexit");
@@ 1096,6 1114,8 @@ procctl(Proc *p)
qunlock(&p->debug);
splhi();
p->state = Stopped;
+ if (isedf(up))
+ edf_block(up);
sched();
p->psstate = state;
splx(s);