|
@@ -1,1147 +1,587 @@
|
|
|
/* EDF scheduling */
|
|
|
-#include "u.h"
|
|
|
+#include <u.h>
|
|
|
#include "../port/lib.h"
|
|
|
#include "mem.h"
|
|
|
#include "dat.h"
|
|
|
#include "fns.h"
|
|
|
#include "../port/error.h"
|
|
|
-#include "realtime.h"
|
|
|
#include "../port/edf.h"
|
|
|
+#include <trace.h>
|
|
|
|
|
|
/* debugging */
|
|
|
-int edfprint = 0;
|
|
|
-#define DPRINT if(edfprint)iprint
|
|
|
-
|
|
|
-char *edfstatename[] = {
|
|
|
- [EdfUnused] = "Unused",
|
|
|
- [EdfExpelled] = "Expelled",
|
|
|
- [EdfAdmitted] = "Admitted",
|
|
|
- [EdfBestEffort] = "BestEffort",
|
|
|
- [EdfIdle] = "Idle",
|
|
|
- [EdfAwaitrelease] = "Awaitrelease",
|
|
|
- [EdfReleased] = "Released",
|
|
|
- [EdfRunning] = "Running",
|
|
|
- [EdfExtra] = "Extra",
|
|
|
- [EdfPreempted] = "Preempted",
|
|
|
- [EdfBlocked] = "Blocked",
|
|
|
- [EdfDeadline] = "Deadline",
|
|
|
-};
|
|
|
-
|
|
|
-static Timer deadlinetimer[MAXMACH]; /* Time of next deadline */
|
|
|
-static Timer releasetimer[MAXMACH]; /* Time of next release */
|
|
|
+int edfprint = 0;
|
|
|
+#define DPRINT if(edfprint)print
|
|
|
+
|
|
|
+static vlong now;
|
|
|
+extern ulong delayedscheds;
|
|
|
+extern Schedq runq[Nrq];
|
|
|
+extern int nrdy;
|
|
|
+extern ulong runvec;
|
|
|
+
|
|
|
+/* Statistics stuff */
|
|
|
+ulong nilcount;
|
|
|
+ulong scheds;
|
|
|
+vlong edfruntime;
|
|
|
+ulong edfnrun;
|
|
|
+int misseddeadlines;
|
|
|
|
|
|
-static int initialized;
|
|
|
-static Ticks now;
|
|
|
-/* Edfschedlock protects modification of sched params, including resources */
|
|
|
+/* Edfschedlock protects modification of admission params */
|
|
|
+int edfinited;
|
|
|
QLock edfschedlock;
|
|
|
-Lock edflock;
|
|
|
-
|
|
|
-Head tasks;
|
|
|
-Head resources;
|
|
|
-int edfstateupdate;
|
|
|
-int misseddeadlines;
|
|
|
+static Lock thelock;
|
|
|
|
|
|
enum{
|
|
|
- Deadline, /* Invariant for schedulability test: Deadline < Release */
|
|
|
- Release,
|
|
|
+ Dl, /* Invariant for schedulability test: Dl < Rl */
|
|
|
+ Rl,
|
|
|
};
|
|
|
|
|
|
-static int earlierrelease(Task *t1, Task *t2) {return t1->r < t2->r;}
|
|
|
-static int earlierdeadline(Task *t1, Task *t2) {return t1->d < t2->d;}
|
|
|
-static void edfrelease(Task *t);
|
|
|
-
|
|
|
-/* 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;
|
|
|
-
|
|
|
-/* Running/Preempted EDF tasks, head running, one stack per processor */
|
|
|
-Taskq edfstack[MAXMACH];
|
|
|
-
|
|
|
-static Task *qschedulability;
|
|
|
-
|
|
|
-void (*devrt)(Task*, Ticks, int);
|
|
|
-
|
|
|
-static void edfresched(Task*);
|
|
|
-static void setdelta(void);
|
|
|
-static void testdelta(Task*);
|
|
|
-static void edfreleaseintr(Ureg*, Timer*);
|
|
|
-static void edfdeadlineintr(Ureg*, Timer*);
|
|
|
-static char * edftestschedulability(Task*);
|
|
|
-static void resrelease(Task*);
|
|
|
-
|
|
|
-static void
|
|
|
-edfinit(void)
|
|
|
-{
|
|
|
- int i;
|
|
|
-
|
|
|
- if (initialized)
|
|
|
- return;
|
|
|
- ilock(&edflock);
|
|
|
- if (initialized){
|
|
|
- iunlock(&edflock);
|
|
|
- return;
|
|
|
- }
|
|
|
- for (i = 0; i < conf.nmach; i++){
|
|
|
- deadlinetimer[i].f = edfdeadlineintr;
|
|
|
- deadlinetimer[i].a = &deadlinetimer[i];
|
|
|
- deadlinetimer[i].when = 0;
|
|
|
- releasetimer[i].f = edfreleaseintr;
|
|
|
- releasetimer[i].a = &releasetimer[i];
|
|
|
- releasetimer[i].when = 0;
|
|
|
- }
|
|
|
- initialized = 1;
|
|
|
- iunlock(&edflock);
|
|
|
-}
|
|
|
+static char *testschedulability(Proc*);
|
|
|
+static Proc *qschedulability;
|
|
|
|
|
|
-static int
|
|
|
-isedf(Proc *p)
|
|
|
-{
|
|
|
- return p && p->task && p->task->state >= EdfIdle;
|
|
|
-}
|
|
|
+enum {
|
|
|
+ Onemicrosecond = 1000ULL,
|
|
|
+ Onemillisecond = 1000000ULL,
|
|
|
+ Onesecond = 1000000000ULL,
|
|
|
+ OneRound = Onemillisecond/2LL,
|
|
|
+ MilliRound = Onemicrosecond/2LL,
|
|
|
+};
|
|
|
|
|
|
static int
|
|
|
-edfanyready(void)
|
|
|
-{
|
|
|
- return edfstack[m->machno].head || qreleased.head;
|
|
|
-}
|
|
|
-
|
|
|
-static void
|
|
|
-edfpush(Task *t)
|
|
|
-{
|
|
|
- Taskq *q;
|
|
|
-
|
|
|
- DPRINT("%d edfpush, %s, %d\n", m->machno, edfstatename[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(devrt) devrt(q->head, now, SPreempt);
|
|
|
- }
|
|
|
- t->rnext = q->head;
|
|
|
- if(devrt) devrt(t, now, SRun);
|
|
|
- q->head = t;
|
|
|
-}
|
|
|
-
|
|
|
-static Task*
|
|
|
-edfpop(void)
|
|
|
+timeconv(Fmt *f)
|
|
|
{
|
|
|
- Task *t;
|
|
|
- Taskq *q;
|
|
|
+ char buf[128], *sign;
|
|
|
+ vlong t;
|
|
|
|
|
|
- DPRINT("%d edfpop\n", m->machno);
|
|
|
- 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(devrt) devrt(q->head, now, SRun);
|
|
|
- }
|
|
|
- }
|
|
|
- return t;
|
|
|
-}
|
|
|
-
|
|
|
-static Task*
|
|
|
-edfenqueue(Taskq *q, Task *t)
|
|
|
-{
|
|
|
- Task *tt, **ttp;
|
|
|
-
|
|
|
- DPRINT("%d edfenqueue, %s, %d\n", m->machno, edfstatename[t->state], t->runq.n);
|
|
|
- t->rnext = nil;
|
|
|
- if (q->head == nil) {
|
|
|
- q->head = t;
|
|
|
- return t;
|
|
|
+ buf[0] = 0;
|
|
|
+ switch(f->r) {
|
|
|
+ case 'U':
|
|
|
+ t = va_arg(f->args, uvlong);
|
|
|
+ break;
|
|
|
+ case 't': // vlong in nanoseconds
|
|
|
+ t = va_arg(f->args, vlong);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return fmtstrcpy(f, "(timeconv)");
|
|
|
}
|
|
|
- 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 (t < 0) {
|
|
|
+ sign = "-";
|
|
|
+ t = -t;
|
|
|
}
|
|
|
- if (*ttp == nil)
|
|
|
- tt->rnext = t;
|
|
|
- if (t != q->head)
|
|
|
- t = nil;
|
|
|
- return t;
|
|
|
+ else
|
|
|
+ sign = "";
|
|
|
+ if (t > Onesecond){
|
|
|
+ t += OneRound;
|
|
|
+ sprint(buf, "%s%d.%.3ds", sign, (int)(t / Onesecond), (int)(t % Onesecond)/1000000);
|
|
|
+ }else if (t > Onemillisecond){
|
|
|
+ t += MilliRound;
|
|
|
+ sprint(buf, "%s%d.%.3dms", sign, (int)(t / Onemillisecond), (int)(t % Onemillisecond)/1000);
|
|
|
+ }else if (t > Onemicrosecond)
|
|
|
+ sprint(buf, "%s%d.%.3dµs", sign, (int)(t / Onemicrosecond), (int)(t % Onemicrosecond));
|
|
|
+ else
|
|
|
+ sprint(buf, "%s%dns", sign, (int)t);
|
|
|
+ return fmtstrcpy(f, buf);
|
|
|
}
|
|
|
|
|
|
-static Task*
|
|
|
-edfdequeue(Taskq *q)
|
|
|
+void
|
|
|
+edflock(void)
|
|
|
{
|
|
|
- Task *t;
|
|
|
-
|
|
|
- DPRINT("%d edfdequeue\n", m->machno);
|
|
|
- if (t = q->head){
|
|
|
- q->head = t->rnext;
|
|
|
- t->rnext = nil;
|
|
|
- }
|
|
|
- return t;
|
|
|
+ ilock(&thelock);
|
|
|
+ now = todget(nil);
|
|
|
}
|
|
|
|
|
|
-static Task*
|
|
|
-edfqremove(Taskq *q, Task *t)
|
|
|
+void
|
|
|
+edfunlock(void)
|
|
|
{
|
|
|
- Task **tp;
|
|
|
-
|
|
|
- DPRINT("%d edfqremove, %s, %d\n", m->machno, edfstatename[t->state], t->runq.n);
|
|
|
- for (tp = &q->head; *tp; tp = &(*tp)->rnext){
|
|
|
- if (*tp == t){
|
|
|
- *tp = t->rnext;
|
|
|
- t = (tp == &q->head) ? q->head : nil;
|
|
|
- return t;
|
|
|
- }
|
|
|
- }
|
|
|
- return nil;
|
|
|
+ edfruntime += todget(nil) - now;
|
|
|
+ edfnrun++;
|
|
|
+ iunlock(&thelock);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
void
|
|
|
-edfreleasetimer(void)
|
|
|
+edfinit(Proc*p)
|
|
|
{
|
|
|
- Task *t;
|
|
|
-
|
|
|
- if ((t = qwaitrelease.head) == nil)
|
|
|
- return;
|
|
|
- DPRINT("edfreleasetimer clock\n");
|
|
|
- releasetimer[m->machno].when = t->r;
|
|
|
- if (releasetimer[m->machno].when <= now)
|
|
|
- releasetimer[m->machno].when = now;
|
|
|
- timeradd(&releasetimer[m->machno]);
|
|
|
+ if(!edfinited){
|
|
|
+ fmtinstall('t', timeconv);
|
|
|
+ edfinited++;
|
|
|
+ }
|
|
|
+ now = todget(nil);
|
|
|
+ DPRINT("%t edfinit %lud[%s]\n", now, p->pid, statename[p->state]);
|
|
|
+ p->edf = malloc(sizeof(Edf));
|
|
|
+ if(p->edf == nil)
|
|
|
+ error(Enomem);
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
-edfblock(Proc *p)
|
|
|
+deadlineintr(Ureg*, Timer *t)
|
|
|
{
|
|
|
- Task *t, *pt;
|
|
|
-
|
|
|
- /* The current proc has blocked */
|
|
|
- ilock(&edflock);
|
|
|
- t = p->task;
|
|
|
- assert(t);
|
|
|
- if (t->state != EdfRunning){
|
|
|
- /* called by a proc just joining the task */
|
|
|
- iunlock(&edflock);
|
|
|
- return;
|
|
|
- }
|
|
|
- DPRINT("%d edfblock, %s, %d\n", m->machno, edfstatename[t->state], t->runq.n);
|
|
|
+ /* Proc reached deadline */
|
|
|
+ extern int panicking;
|
|
|
+ Proc *p;
|
|
|
|
|
|
- if (t->runq.n){
|
|
|
- /* There's another runnable proc in the running task, leave task where it is */
|
|
|
- iunlock(&edflock);
|
|
|
+ if(panicking || active.exiting)
|
|
|
return;
|
|
|
- }
|
|
|
- pt = edfpop();
|
|
|
- assert(pt == t);
|
|
|
- t->state = EdfBlocked;
|
|
|
- if(devrt) devrt(t, now, SBlock);
|
|
|
- iunlock(&edflock);
|
|
|
-}
|
|
|
|
|
|
-static void
|
|
|
-deadline(Proc *p, SEvent why)
|
|
|
-{
|
|
|
- Task *t, *nt;
|
|
|
- Ticks used;
|
|
|
-
|
|
|
- /* Task has reached its deadline, lock must be held */
|
|
|
- DPRINT("%d deadline, %s, %d\n", m->machno, edfstatename[p->task->state], p->task->runq.n);
|
|
|
- SET(nt);
|
|
|
- if (p){
|
|
|
- nt = p->task;
|
|
|
- if (nt == nil || nt->state != EdfRunning)
|
|
|
- return;
|
|
|
- }
|
|
|
- t = edfpop();
|
|
|
-
|
|
|
- if(p != nil && nt != t){
|
|
|
- iprint("deadline, %s, %d\n", edfstatename[p->task->state], p->task->runq.n);
|
|
|
- iunlock(&edflock);
|
|
|
- assert(0 && p == nil || nt == t);
|
|
|
- }
|
|
|
- if (deadlinetimer[m->machno].when){
|
|
|
- timerdel(&deadlinetimer[m->machno]);
|
|
|
- deadlinetimer[m->machno].when = 0;
|
|
|
+ p = t->ta;
|
|
|
+ DPRINT("%t deadlineintr %lud[%s]\n", todget(nil), p->pid, statename[p->state]);
|
|
|
+ /* If we're interrupting something other than the proc pointed to by t->a,
|
|
|
+ * we've already achieved recheduling, so we need not do anything
|
|
|
+ * Otherwise, we must cause a reschedule, but if we call sched()
|
|
|
+ * here directly, the timer interrupt routine will not finish its business
|
|
|
+ * Instead, we cause the resched to happen when the interrupted proc
|
|
|
+ * returns to user space
|
|
|
+ */
|
|
|
+ if (p == up){
|
|
|
+ up->delaysched++;
|
|
|
+ delayedscheds++;
|
|
|
}
|
|
|
- used = now - t->scheduled;
|
|
|
- t->S -= used;
|
|
|
- t->scheduled = now;
|
|
|
- t->total += used;
|
|
|
- t->aged = (t->aged*31 + t->C - t->S) >> 5;
|
|
|
- t->d = now;
|
|
|
- t->state = EdfDeadline;
|
|
|
- if(devrt) devrt(t, now, why);
|
|
|
- edfresched(t);
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
-edfdeadline(Proc *p)
|
|
|
+release(Proc *p)
|
|
|
{
|
|
|
- DPRINT("%d edfdeadline\n", m->machno);
|
|
|
- /* Task has reached its deadline */
|
|
|
- ilock(&edflock);
|
|
|
- now = fastticks(nil);
|
|
|
- deadline(p, SYield);
|
|
|
- iunlock(&edflock);
|
|
|
-}
|
|
|
-
|
|
|
-static char *
|
|
|
-edfadmit(Task *t)
|
|
|
-{
|
|
|
- char *err, *p;
|
|
|
- static char csndump[512];
|
|
|
- CSN *c;
|
|
|
-
|
|
|
- /* Called with edfschedlock held */
|
|
|
- if (t->state != EdfExpelled)
|
|
|
- return "task state"; /* should never happen */
|
|
|
-
|
|
|
- /* simple sanity checks */
|
|
|
- if (t->T == 0)
|
|
|
- return "T not set";
|
|
|
- if (t->C == 0)
|
|
|
- return "C not set";
|
|
|
- if (t->D > t->T)
|
|
|
- return "D > T";
|
|
|
- if (t->D == 0) /* if D is not set, set it to T */
|
|
|
- t->D = t->T;
|
|
|
- if (t->C > t->D)
|
|
|
- return "C > D";
|
|
|
-
|
|
|
- resourcetimes(t, &t->csns);
|
|
|
-
|
|
|
- DEBUG("task %d: T %T, C %T, D %T, tΔ %T\n",
|
|
|
- t->taskno, ticks2time(t->T), ticks2time(t->C),
|
|
|
- ticks2time(t->D), ticks2time(t->testDelta));
|
|
|
- p = seprintresources(csndump, csndump+sizeof csndump);
|
|
|
- seprintcsn(p, csndump+sizeof csndump, &t->csns);
|
|
|
- DEBUG("%s\n", csndump);
|
|
|
-
|
|
|
- if (err = edftestschedulability(t)){
|
|
|
- return err;
|
|
|
- }
|
|
|
- ilock(&edflock);
|
|
|
- DPRINT("%d edfadmit, %s, %d\n", m->machno, edfstatename[t->state], t->runq.n);
|
|
|
- if (t->flags & BestEffort){
|
|
|
- t->state = EdfBestEffort;
|
|
|
- iunlock(&edflock);
|
|
|
- return nil;
|
|
|
- }
|
|
|
- now = fastticks(nil);
|
|
|
-
|
|
|
- t->state = EdfAdmitted;
|
|
|
- if(devrt) devrt(t, t->d, SAdmit);
|
|
|
- if (up->task == t){
|
|
|
- DPRINT("%d edfadmitting self\n", m->machno);
|
|
|
- /* Admitting self, fake reaching deadline */
|
|
|
- t->r = now;
|
|
|
- t->t = now + t->T;
|
|
|
- t->d = now + t->D;
|
|
|
- if(devrt) devrt(t, t->d, SDeadline);
|
|
|
- t->S = t->C;
|
|
|
- t->scheduled = now;
|
|
|
- t->state = EdfRunning;
|
|
|
- t->periods++;
|
|
|
- if(devrt) devrt(t, now, SRun);
|
|
|
- setdelta();
|
|
|
- for (c = (CSN*)t->csns.next; c; c = (CSN*)c->next){
|
|
|
- DEBUG("admit csn: C=%T\n", ticks2time(c->C));
|
|
|
- c->S = c->C;
|
|
|
+ /* Called with edflock held */
|
|
|
+ Edf *e;
|
|
|
+ void (*pt)(Proc*, int);
|
|
|
+
|
|
|
+ e = p->edf;
|
|
|
+ e->flags &= ~Yield;
|
|
|
+ if (e->d < now){
|
|
|
+ e->periods++;
|
|
|
+ e->r = now;
|
|
|
+ if ((e->flags & Sporadic) == 0){
|
|
|
+ /* Non sporadic processes stay true to their period;
|
|
|
+ * calculate next release time
|
|
|
+ */
|
|
|
+ while(e->t <= now)
|
|
|
+ e->t += e->T;
|
|
|
+ }else{
|
|
|
+ /* Sporadic processes may not be released earlier than
|
|
|
+ * one period after this release
|
|
|
+ */
|
|
|
+ e->t = e->r + e->T;
|
|
|
}
|
|
|
- assert(t->runq.n > 0 || (up && up->task == t));
|
|
|
- edfpush(t);
|
|
|
- deadlinetimer[m->machno].when = t->d;
|
|
|
- timeradd(&deadlinetimer[m->machno]);
|
|
|
+ e->d = e->r + e->D;
|
|
|
+ e->S = e->C;
|
|
|
+ DPRINT("%t release %lud[%s], r=%t, d=%t, t=%t, S=%t\n",
|
|
|
+ now, p->pid, statename[p->state], e->r, e->d, e->t, e->S);
|
|
|
+ if (pt = proctrace)
|
|
|
+ pt(p, SRelease);
|
|
|
}else{
|
|
|
- if (t->runq.n){
|
|
|
- if (edfstack[m->machno].head == nil){
|
|
|
- t->r = now;
|
|
|
- edfrelease(t);
|
|
|
- setdelta();
|
|
|
- edfresched(t);
|
|
|
- }
|
|
|
- }
|
|
|
+ DPRINT("%t release %lud[%s], too late t=%t, called from 0x%lux\n",
|
|
|
+ now, p->pid, statename[p->state], e->t, getcallerpc(&p));
|
|
|
}
|
|
|
- iunlock(&edflock);
|
|
|
- return nil;
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
-edfexpel(Task *t)
|
|
|
+releaseintr(Ureg*, Timer *t)
|
|
|
{
|
|
|
- Task *tt;
|
|
|
-
|
|
|
- /* Called with edfschedlock held */
|
|
|
- ilock(&edflock);
|
|
|
- DPRINT("%d edfexpel, %s, %d\n", m->machno, edfstatename[t->state], t->runq.n);
|
|
|
- now = fastticks(nil);
|
|
|
- switch(t->state){
|
|
|
- case EdfUnused:
|
|
|
- case EdfExpelled:
|
|
|
- /* That was easy */
|
|
|
- iunlock(&edflock);
|
|
|
- return;
|
|
|
- case EdfAdmitted:
|
|
|
- case EdfIdle:
|
|
|
- /* Just reset state */
|
|
|
- break;
|
|
|
- case EdfAwaitrelease:
|
|
|
- if (edfqremove(&qwaitrelease, t))
|
|
|
- edfreleasetimer();
|
|
|
- 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(devrt) devrt(t, now, SExpel);
|
|
|
- setdelta();
|
|
|
- iunlock(&edflock);
|
|
|
- return;
|
|
|
-}
|
|
|
-
|
|
|
-static void
|
|
|
-edfreleaseintr(Ureg*, Timer*)
|
|
|
-{
|
|
|
- Task *t;
|
|
|
+ Proc *p;
|
|
|
+ Edf *e;
|
|
|
extern int panicking;
|
|
|
-
|
|
|
- DPRINT("%d edfreleaseintr\n", m->machno);
|
|
|
+ Schedq *rq;
|
|
|
|
|
|
if(panicking || active.exiting)
|
|
|
return;
|
|
|
|
|
|
- now = fastticks(nil);
|
|
|
- ilock(&edflock);
|
|
|
- while((t = qwaitrelease.head) && t->r <= now){
|
|
|
- /* There's something waiting to be released and its time has come */
|
|
|
- edfdequeue(&qwaitrelease);
|
|
|
- edfreleasetimer();
|
|
|
- edfrelease(t);
|
|
|
- }
|
|
|
- iunlock(&edflock);
|
|
|
- sched();
|
|
|
- splhi();
|
|
|
-}
|
|
|
-
|
|
|
-static void
|
|
|
-edfdeadlineintr(Ureg*, Timer *)
|
|
|
-{
|
|
|
- /* Task reached deadline */
|
|
|
-
|
|
|
- Ticks used;
|
|
|
- Task *t;
|
|
|
- Resource *r;
|
|
|
- char buf[128];
|
|
|
- int noted;
|
|
|
- extern int panicking;
|
|
|
-
|
|
|
- DPRINT("%d edfdeadlineintr\n", m->machno);
|
|
|
-
|
|
|
- if(panicking || active.exiting)
|
|
|
+ p = t->ta;
|
|
|
+ e = p->edf;
|
|
|
+ if ((e->flags & Admitted) == 0)
|
|
|
return;
|
|
|
-
|
|
|
- now = fastticks(nil);
|
|
|
- ilock(&edflock);
|
|
|
- // If up is not set, we're running inside the scheduler
|
|
|
- // for non-real-time processes.
|
|
|
- noted = 0;
|
|
|
- if (up && isedf(up)) {
|
|
|
- t = up->task;
|
|
|
-
|
|
|
- assert(t->state == EdfRunning);
|
|
|
- assert(t->scheduled > 0);
|
|
|
-
|
|
|
- used = now - t->scheduled;
|
|
|
- t->scheduled = now;
|
|
|
- t->total += used;
|
|
|
-
|
|
|
- if (t->r < now){
|
|
|
- if (t->curcsn){
|
|
|
- if (t->curcsn->S <= used){
|
|
|
- t->curcsn->S = 0LL;
|
|
|
- resrelease(t);
|
|
|
- r = t->curcsn->i;
|
|
|
- noted++;
|
|
|
- snprint(buf, sizeof buf, "sys: deadline miss: resource %s", r->name);
|
|
|
- }else
|
|
|
- t->curcsn->S -= used;
|
|
|
- }
|
|
|
-
|
|
|
- if (t->S <= used){
|
|
|
- t->S = 0LL;
|
|
|
- if (!noted){
|
|
|
- noted++;
|
|
|
- snprint(buf, sizeof buf, "sys: deadline miss: runtime");
|
|
|
- }
|
|
|
- }else
|
|
|
- t->S -= used;
|
|
|
-
|
|
|
- if (t->d <= now || t->S == 0LL || t->curcsn == 0LL){
|
|
|
- /* Task has reached its deadline/slice, remove from queue */
|
|
|
- if (t->d <= now){
|
|
|
- t->missed++;
|
|
|
- misseddeadlines++;
|
|
|
- }
|
|
|
- deadline(up, SSlice);
|
|
|
-
|
|
|
- while (t = edfstack[m->machno].head){
|
|
|
- if (now < t->d)
|
|
|
- break;
|
|
|
- deadline(nil, SSlice);
|
|
|
- }
|
|
|
- }
|
|
|
+ edflock();
|
|
|
+ DPRINT("%t releaseintr %lud[%s]\n", now, p->pid, statename[p->state]);
|
|
|
+ switch(p->state){
|
|
|
+ default:
|
|
|
+ edfunlock();
|
|
|
+ return;
|
|
|
+ case Ready:
|
|
|
+ /* remove proc from current runq */
|
|
|
+ rq = &runq[p->priority];
|
|
|
+ if (dequeueproc(rq, p) != p){
|
|
|
+ print("releaseintr: can't find proc or lock race\n");
|
|
|
+ release(p); /* It'll start best effort */
|
|
|
+ edfunlock();
|
|
|
+ return;
|
|
|
}
|
|
|
- }
|
|
|
- iunlock(&edflock);
|
|
|
- if (noted)
|
|
|
- postnote(up, 0, buf, NUser);
|
|
|
- sched();
|
|
|
- splhi();
|
|
|
-}
|
|
|
-
|
|
|
-static void
|
|
|
-edfbury(Proc *p)
|
|
|
-{
|
|
|
- Task *t;
|
|
|
-
|
|
|
- DPRINT("%d edfbury\n", m->machno);
|
|
|
- ilock(&edflock);
|
|
|
- now = fastticks(nil);
|
|
|
- if ((t = p->task) == nil){
|
|
|
- /* race condition? */
|
|
|
- iunlock(&edflock);
|
|
|
- DPRINT("%d edf bury race, pid %lud\n", m->machno, p->pid);
|
|
|
+ p->state = Waitrelease;
|
|
|
+ /* fall through */
|
|
|
+ case Waitrelease:
|
|
|
+ release(p);
|
|
|
+ edfunlock();
|
|
|
+ ready(p);
|
|
|
+ sched();
|
|
|
+// if (up){
|
|
|
+// up->delaysched++;
|
|
|
+ // delayedscheds++;
|
|
|
+// }
|
|
|
return;
|
|
|
+ case Running:
|
|
|
+ release(p);
|
|
|
+ edfrun(p, 1);
|
|
|
+ break;
|
|
|
}
|
|
|
- assert(edfstack[m->machno].head == t);
|
|
|
- delist(&t->procs, p);
|
|
|
- if (t->runq.head == nil){
|
|
|
- edfpop();
|
|
|
- t->state = EdfBlocked;
|
|
|
- }
|
|
|
- if (t->procs.n == 0){
|
|
|
- assert(t->runq.head == nil);
|
|
|
- t->state = EdfIdle;
|
|
|
- }
|
|
|
- if(devrt) devrt(t, now, SBlock);
|
|
|
- p->task = nil;
|
|
|
- iunlock(&edflock);
|
|
|
+ edfunlock();
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
-edfready(Proc *p)
|
|
|
+void
|
|
|
+edfrecord(Proc *p)
|
|
|
{
|
|
|
- Task *t;
|
|
|
-
|
|
|
- ilock(&edflock);
|
|
|
- DPRINT("%d edfready, %s, %d\n", m->machno, edfstatename[p->task->state], p->task->runq.n);
|
|
|
- if ((t = p->task) == nil){
|
|
|
- /* Must be a race */
|
|
|
- iunlock(&edflock);
|
|
|
- DPRINT("%d edf ready race, pid %lud\n", m->machno, 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);
|
|
|
- edfresched(t);
|
|
|
- }
|
|
|
- iunlock(&edflock);
|
|
|
+ vlong used;
|
|
|
+ Edf *e;
|
|
|
+ void (*pt)(Proc*, int);
|
|
|
+
|
|
|
+ e = p->edf;
|
|
|
+ edflock();
|
|
|
+ used = now - e->s;
|
|
|
+ if (e->d <= now)
|
|
|
+ e->edfused += used;
|
|
|
+ else
|
|
|
+ e->extraused += used;
|
|
|
+ if (e->S > 0){
|
|
|
+ if (e->S <= used){
|
|
|
+ if(pt = proctrace)
|
|
|
+ pt(p, SDeadline);
|
|
|
+ DPRINT("%t edfrecord slice used up\n", now);
|
|
|
+ e->d = now;
|
|
|
+ e->S = 0;
|
|
|
+ }else
|
|
|
+ e->S -= used;
|
|
|
+ }
|
|
|
+ e->s = now;
|
|
|
+ edfunlock();
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
-edfresched(Task *t)
|
|
|
+void
|
|
|
+edfrun(Proc *p, int edfpri)
|
|
|
{
|
|
|
- Task *xt;
|
|
|
-
|
|
|
- DPRINT("%d edfresched, %s, %d\n", m->machno, edfstatename[t->state], t->runq.n);
|
|
|
- if (t->procs.n == 0){
|
|
|
- /* No member processes */
|
|
|
- if (t->state > EdfIdle){
|
|
|
- t->state = EdfIdle;
|
|
|
- if(devrt) devrt(t, now, SBlock);
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
- if (t->runq.n == 0 && (up == nil || up->task != t)){
|
|
|
- /* Member processes but none runnable */
|
|
|
- DPRINT("%d edfresched, nothing runnable\n", m->machno);
|
|
|
- if (t->state == EdfRunning)
|
|
|
- edfpop();
|
|
|
-
|
|
|
- if (t->state >= EdfIdle && t->state != EdfBlocked){
|
|
|
- t->state = EdfBlocked;
|
|
|
- if(devrt) devrt(t, now, SBlock);
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- /* There are runnable processes */
|
|
|
-
|
|
|
- switch (t->state){
|
|
|
- case EdfUnused:
|
|
|
- iprint("%d attempt to schedule unused task\n", m->machno);
|
|
|
- case EdfExpelled:
|
|
|
- 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;
|
|
|
- }
|
|
|
- edfrelease(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){
|
|
|
+ Edf *e;
|
|
|
+
|
|
|
+ e = p->edf;
|
|
|
+ /* Called with edflock held */
|
|
|
+ if(edfpri){
|
|
|
+ if (e->d <= now || e->S == 0){
|
|
|
+ /* Deadline reached or resources exhausted,
|
|
|
+ * deschedule forthwith
|
|
|
+ */
|
|
|
+ p->delaysched++;
|
|
|
+ delayedscheds++;
|
|
|
+ e->s = now;
|
|
|
return;
|
|
|
}
|
|
|
- /* fall through */
|
|
|
- case EdfRunning:
|
|
|
- if (t->r <= now){
|
|
|
- if (t->t < now){
|
|
|
- DPRINT("%d edfresched, rerelease\n", m->machno);
|
|
|
- /* Period passed, rerelease */
|
|
|
- t->r = now;
|
|
|
- xt = edfpop();
|
|
|
- assert(xt == t);
|
|
|
- edfrelease(t);
|
|
|
- return;
|
|
|
- }
|
|
|
- if (now < t->d){
|
|
|
- if (t->S > 0){
|
|
|
- DPRINT("%d edfresched, resume\n", m->machno);
|
|
|
- /* Running, not yet at deadline, leave it */
|
|
|
- return;
|
|
|
- }else
|
|
|
- t->d = now;
|
|
|
- }
|
|
|
- /* Released, but deadline is past, release at t->t */
|
|
|
- t->r = t->t;
|
|
|
+ p->tns = now + e->S;
|
|
|
+ if (e->d < p->tns)
|
|
|
+ p->tns = e->d;
|
|
|
+ if(p->tt == nil || p->tf != deadlineintr){
|
|
|
+ DPRINT("%t edfrun, deadline=%t\n", now, p->tns);
|
|
|
+ }else{
|
|
|
+ DPRINT("v");
|
|
|
}
|
|
|
- xt = edfpop();
|
|
|
- assert(xt == t);
|
|
|
- t->state = EdfAwaitrelease;
|
|
|
- if (edfenqueue(&qwaitrelease, t))
|
|
|
- edfreleasetimer();
|
|
|
- break;
|
|
|
- case EdfBlocked:
|
|
|
- case EdfDeadline:
|
|
|
- if (t->r <= now){
|
|
|
- if (t->t < now){
|
|
|
- DPRINT("%d edfresched, rerelease\n", m->machno);
|
|
|
- /* Period passed, rerelease */
|
|
|
- t->r = now;
|
|
|
- edfrelease(t);
|
|
|
- return;
|
|
|
- }
|
|
|
- if ((t->flags & Useblocking) == 0 && now < t->d){
|
|
|
- if (t->S > 0){
|
|
|
- DPRINT("%d edfresched, resume\n", m->machno);
|
|
|
- /* Released, not yet at deadline, release (again) */
|
|
|
- t->state = EdfReleased;
|
|
|
- edfenqueue(&qreleased, t);
|
|
|
- if(devrt) devrt(t, now, SResume);
|
|
|
- return;
|
|
|
- }else
|
|
|
- t->d = now;
|
|
|
- }
|
|
|
- /* Released, but deadline is past, release at t->t */
|
|
|
- t->r = t->t;
|
|
|
- }
|
|
|
- t->state = EdfAwaitrelease;
|
|
|
- if (edfenqueue(&qwaitrelease, t))
|
|
|
- edfreleasetimer();
|
|
|
- break;
|
|
|
+ p->tmode = Tabsolute;
|
|
|
+ p->tf = deadlineintr;
|
|
|
+ p->ta = p;
|
|
|
+ timeradd(p);
|
|
|
+ }else{
|
|
|
+ DPRINT("<");
|
|
|
}
|
|
|
+ e->s = now;
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
-edfrelease(Task *t)
|
|
|
+char *
|
|
|
+edfadmit(Proc *p)
|
|
|
{
|
|
|
- CSN *c;
|
|
|
+ char *err;
|
|
|
+ Edf *e;
|
|
|
+ int i;
|
|
|
+ Proc *r;
|
|
|
+ void (*pt)(Proc*, int);
|
|
|
|
|
|
- DPRINT("%d edfrelease, %s, %d\n", m->machno, edfstatename[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(devrt) devrt(t, t->d, SDeadline);
|
|
|
- t->S = t->C;
|
|
|
- for (c = (CSN*)t->csns.next; c; c = (CSN*)c->next){
|
|
|
- DEBUG("release csn: C=%T\n", ticks2time(c->C));
|
|
|
- c->S = c->C;
|
|
|
- }
|
|
|
- t->state = EdfReleased;
|
|
|
- edfenqueue(&qreleased, t);
|
|
|
- if(devrt) devrt(t, now, SRelease);
|
|
|
-}
|
|
|
+ e = p->edf;
|
|
|
+ if (e->flags & Admitted)
|
|
|
+ return "task state"; /* should never happen */
|
|
|
|
|
|
-static Proc *
|
|
|
-edfrunproc(void)
|
|
|
-{
|
|
|
- /* Return an edf proc to run or nil */
|
|
|
-
|
|
|
- Task *t, *nt, *xt;
|
|
|
- Proc *p;
|
|
|
- Ticks when;
|
|
|
- static ulong nilcount;
|
|
|
- int i;
|
|
|
+ /* simple sanity checks */
|
|
|
+ if (e->T == 0)
|
|
|
+ return "T not set";
|
|
|
+ if (e->C == 0)
|
|
|
+ return "C not set";
|
|
|
+ if (e->D > e->T)
|
|
|
+ return "D > T";
|
|
|
+ if (e->D == 0) /* if D is not set, set it to T */
|
|
|
+ e->D = e->T;
|
|
|
+ if (e->C > e->D)
|
|
|
+ return "C > D";
|
|
|
|
|
|
- if (edfstack[m->machno].head == nil && qreleased.head== nil){
|
|
|
- // quick way out
|
|
|
- nilcount++;
|
|
|
- return nil;
|
|
|
+ qlock(&edfschedlock);
|
|
|
+ if (err = testschedulability(p)){
|
|
|
+ qunlock(&edfschedlock);
|
|
|
+ return err;
|
|
|
}
|
|
|
+ edflock();
|
|
|
|
|
|
- /* Figure out if the current proc should be preempted*/
|
|
|
- ilock(&edflock);
|
|
|
- now = fastticks(nil);
|
|
|
-
|
|
|
- /* first candidate is at the top of the stack of running procs */
|
|
|
- t = edfstack[m->machno].head;
|
|
|
+ e->flags |= Admitted;
|
|
|
|
|
|
- /* check out head of the release queue for a proc with a better deadline */
|
|
|
- nt = qreleased.head;
|
|
|
+ if(pt = proctrace)
|
|
|
+ pt(p, SAdmit);
|
|
|
|
|
|
- if (t == nil && nt == nil){
|
|
|
- nilcount++;
|
|
|
- iunlock(&edflock);
|
|
|
- return nil;
|
|
|
- }
|
|
|
- DPRINT("edfrunproc %lud\n", nilcount);
|
|
|
- if (nt && (t == nil || (nt->d < t->d && nt->D < t->Delta))){
|
|
|
- if (conf.nmach > 1){
|
|
|
- for (i = 0; i < conf.nmach; i++){
|
|
|
- if (i == m->machno)
|
|
|
- continue;
|
|
|
- xt = edfstack[i].head;
|
|
|
- if (xt && xt->Delta <= nt->D){
|
|
|
- DPRINT("%d edfrunproc: interprocessor conflict, run current\n", m->machno);
|
|
|
- if (t)
|
|
|
- goto runt;
|
|
|
- nilcount++;
|
|
|
- iunlock(&edflock);
|
|
|
- return nil;
|
|
|
- }
|
|
|
- }
|
|
|
+ /* Look for another proc with the same period to synchronize to */
|
|
|
+ SET(r);
|
|
|
+ for(i=0; i<conf.nproc; i++) {
|
|
|
+ r = proctab(i);
|
|
|
+ if(r->state == Dead || r == p)
|
|
|
+ continue;
|
|
|
+ if (r->edf == nil || (r->edf->flags & Admitted) == 0)
|
|
|
+ continue;
|
|
|
+ if (r->edf->T == e->T)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (i == conf.nproc){
|
|
|
+ /* Can't synchronize to another proc, release now */
|
|
|
+ e->t = now;
|
|
|
+ e->d = 0;
|
|
|
+ release(p);
|
|
|
+ if (p == up){
|
|
|
+ DPRINT("%t edfadmit self %lud[%s], release now: r=%t d=%t t=%t\n",
|
|
|
+ now, p->pid, statename[p->state], e->r, e->d, e->t);
|
|
|
+ /* We're already running */
|
|
|
+ edfrun(p, 1);
|
|
|
+ }else{
|
|
|
+ /* We're releasing another proc */
|
|
|
+ DPRINT("%t edfadmit other %lud[%s], release now: r=%t d=%t t=%t\n",
|
|
|
+ now, p->pid, statename[p->state], e->r, e->d, e->t);
|
|
|
+ p->ta = p;
|
|
|
+ releaseintr(nil, p);
|
|
|
}
|
|
|
- /* released task is better than current */
|
|
|
- DPRINT("%d edfrunproc: released\n", m->machno);
|
|
|
- edfdequeue(&qreleased);
|
|
|
- assert(nt->runq.n >= 1 || (up && up->task == nt));
|
|
|
- edfpush(nt);
|
|
|
- t = nt;
|
|
|
- t->scheduled = now;
|
|
|
}else{
|
|
|
- DPRINT("%d edfrunproc: current\n", m->machno);
|
|
|
- }
|
|
|
-runt:
|
|
|
- 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;
|
|
|
- t->periods++;
|
|
|
- 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);
|
|
|
-
|
|
|
- when = now + t->S;
|
|
|
- if (t->d < when)
|
|
|
- when = t->d;
|
|
|
-
|
|
|
- if (when < now){
|
|
|
- DPRINT("%d edftimer: %T too late\n", m->machno, ticks2time(now-when));
|
|
|
- when = now;
|
|
|
- }
|
|
|
- if(deadlinetimer[m->machno].when == when){
|
|
|
- iunlock(&edflock);
|
|
|
- return p;
|
|
|
- }
|
|
|
- deadlinetimer[m->machno].when = when;
|
|
|
- timeradd(&deadlinetimer[m->machno]);
|
|
|
- iunlock(&edflock);
|
|
|
- return p;
|
|
|
-}
|
|
|
-
|
|
|
-/* Schedulability testing and its supporting routines */
|
|
|
-
|
|
|
-static void
|
|
|
-setdelta(void)
|
|
|
-{
|
|
|
- Resource *r;
|
|
|
- Task *t;
|
|
|
- int R;
|
|
|
- List *lr, *l;
|
|
|
- TaskLink *lt;
|
|
|
- CSN *c;
|
|
|
-
|
|
|
- for (lr = resources.next; lr; lr = lr->next){
|
|
|
- r = lr->i;
|
|
|
- assert(r);
|
|
|
- r->Delta = Infinity;
|
|
|
- R = 1;
|
|
|
- for (lt = (TaskLink*)r->tasks.next; lt; lt = (TaskLink*)lt->next){
|
|
|
- t = lt->i;
|
|
|
- assert(t);
|
|
|
- if (t->D < r->Delta){
|
|
|
- r->Delta = t->D;
|
|
|
+ /* Release in synch to something else */
|
|
|
+ e->t = r->edf->t;
|
|
|
+ if (p == up){
|
|
|
+ DPRINT("%t edfadmit self %lud[%s], release at %t\n",
|
|
|
+ now, p->pid, statename[p->state], e->t);
|
|
|
+ edfunlock();
|
|
|
+ qunlock(&edfschedlock);
|
|
|
+ edfyield();
|
|
|
+ return nil;
|
|
|
+ }else{
|
|
|
+ DPRINT("%t edfadmit other %lud[%s], release at %t\n",
|
|
|
+ now, p->pid, statename[p->state], e->t);
|
|
|
+ if(p->tt == nil){
|
|
|
+ p->tf = releaseintr;
|
|
|
+ p->ta = p;
|
|
|
+ p->tns = e->t;
|
|
|
+ p->tmode = Tabsolute;
|
|
|
+ timeradd(p);
|
|
|
}
|
|
|
- if (lt->R == 0){
|
|
|
- R = 0;
|
|
|
- }
|
|
|
- }
|
|
|
- if (R)
|
|
|
- r->Delta = Infinity; /* Read-only resource, no exclusion */
|
|
|
- }
|
|
|
-
|
|
|
- /* Enumerate the critical sections */
|
|
|
- for (l = tasks.next; l ; l = l->next){
|
|
|
- t = l->i;
|
|
|
- assert(t);
|
|
|
- if (t->state <= EdfExpelled)
|
|
|
- continue;
|
|
|
- t->Delta = Infinity;
|
|
|
- for (c = (CSN*)t->csns.next; c; c = (CSN*)c->next){
|
|
|
- r = c->i;
|
|
|
- assert(r);
|
|
|
- c->Delta = r->Delta;
|
|
|
- if (c->p && c->p->Delta < c->Delta)
|
|
|
- c->Delta = c->p->Delta;
|
|
|
- if (c->C == t->C && r->Delta < t->Delta)
|
|
|
- t->Delta = r->Delta;
|
|
|
}
|
|
|
}
|
|
|
+ edfunlock();
|
|
|
+ qunlock(&edfschedlock);
|
|
|
+ return nil;
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
-testdelta(Task *thetask)
|
|
|
+void
|
|
|
+edfstop(Proc *p)
|
|
|
{
|
|
|
- Resource *r;
|
|
|
- Task *t;
|
|
|
- int R;
|
|
|
- List *lr, *l;
|
|
|
- TaskLink *lt;
|
|
|
- CSN *c;
|
|
|
-
|
|
|
- for (lr = resources.next; lr; lr = lr->next){
|
|
|
- r = lr->i;
|
|
|
- assert(r);
|
|
|
- DEBUG("Resource %s: ", r->name);
|
|
|
- r->testDelta = Infinity;
|
|
|
- R = 1;
|
|
|
- for (lt = (TaskLink*)r->tasks.next; lt; lt = (TaskLink*)lt->next){
|
|
|
- t = lt->i;
|
|
|
- assert(t);
|
|
|
- if (t->D < r->testDelta){
|
|
|
- r->testDelta = t->D;
|
|
|
- DEBUG("%d→%T ", t->taskno, ticks2time(t->D));
|
|
|
- }
|
|
|
- if (lt->R == 0){
|
|
|
- DEBUG("%d→X ", t->taskno);
|
|
|
- R = 0;
|
|
|
- }
|
|
|
- }
|
|
|
- if (R)
|
|
|
- r->testDelta = Infinity; /* Read-only resource, no exclusion */
|
|
|
- DEBUG("tΔ = %T\n", ticks2time(r->testDelta));
|
|
|
- }
|
|
|
-
|
|
|
- /* Enumerate the critical sections */
|
|
|
- for (l = tasks.next; l ; l = l->next){
|
|
|
- t = l->i;
|
|
|
- assert(t);
|
|
|
- if (t->state <= EdfExpelled && t != thetask)
|
|
|
- continue;
|
|
|
- t->testDelta = Infinity;
|
|
|
- for (c = (CSN*)t->csns.next; c; c = (CSN*)c->next){
|
|
|
- r = c->i;
|
|
|
- assert(r);
|
|
|
- c->testDelta = r->testDelta;
|
|
|
- if (c->p && c->p->testDelta < c->testDelta)
|
|
|
- c->testDelta = c->p->testDelta;
|
|
|
- if (c->C == t->C && r->testDelta < t->testDelta)
|
|
|
- t->testDelta = r->testDelta;
|
|
|
- DEBUG("Task %d Resource %s: tΔ = %T\n",
|
|
|
- t->taskno, r->name, ticks2time(r->testDelta));
|
|
|
- }
|
|
|
+ Edf *e;
|
|
|
+ void (*pt)(Proc*, int);
|
|
|
+
|
|
|
+ if ((e = p->edf) && (e->flags & Admitted)){
|
|
|
+ edflock();
|
|
|
+ DPRINT("%t edfstop %lud[%s]\n", now, p->pid, statename[p->state]);
|
|
|
+ if(pt = proctrace)
|
|
|
+ pt(p, SExpel);
|
|
|
+ e->flags &= ~Admitted;
|
|
|
+ if (p->tt)
|
|
|
+ timerdel(p);
|
|
|
+ edfunlock();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static Ticks
|
|
|
-blockcost(Ticks ticks, Task *task, Task *thetask)
|
|
|
+void
|
|
|
+edfyield(void)
|
|
|
{
|
|
|
- Ticks Cb, Cbt;
|
|
|
- List *l;
|
|
|
- Resource *r;
|
|
|
- CSN *c, *lc;
|
|
|
- int R;
|
|
|
- Task *t;
|
|
|
-
|
|
|
- Cb = 0;
|
|
|
- /* for each resource in task t, find all CSNs that refer to the
|
|
|
- * resource. If their Δ <= ticks < D and c->C > current CB
|
|
|
- * Cb = c->C
|
|
|
- */
|
|
|
- DEBUG("blockcost task %d: ", task->taskno);
|
|
|
- for (c = (CSN*)task->csns.next; c; c = (CSN*)c->next){
|
|
|
- r = c->i;
|
|
|
- assert(r);
|
|
|
+ /* sleep until next release */
|
|
|
+ Edf *e;
|
|
|
+ void (*pt)(Proc*, int);
|
|
|
+
|
|
|
+ e = up->edf;
|
|
|
+ if(pt = proctrace)
|
|
|
+ pt(up, SYield);
|
|
|
+ now = todget(nil);
|
|
|
+ e->flags |= Yield;
|
|
|
+ e->d = now;
|
|
|
+ DPRINT("%t edfyield %lud\n", now, up->pid);
|
|
|
+ sched();
|
|
|
+}
|
|
|
|
|
|
- DEBUG("%s ", r->name);
|
|
|
- Cbt = Cb;
|
|
|
- R = 1; /* R == 1: resource is only used in read-only mode */
|
|
|
- for (l = tasks.next; l; l = l->next){
|
|
|
- t = l->i;
|
|
|
- if (t->state <= EdfExpelled && t != thetask)
|
|
|
- continue; /* csn belongs to an irrelevant task */
|
|
|
- for (lc = (CSN*)t->csns.next; lc; lc = (CSN*)lc->next){
|
|
|
- if (lc->i != r)
|
|
|
- continue; /* wrong resource */
|
|
|
- if (lc->R == 0)
|
|
|
- R = 0; /* Resource is used in exclusive mode */
|
|
|
- DEBUG("(%T≤%T<%T: %T) ",
|
|
|
- ticks2time(lc->testDelta), ticks2time(ticks), ticks2time(t->D),
|
|
|
- ticks2time(lc->C));
|
|
|
- if (lc->testDelta <= ticks && ticks < t->D && Cbt < lc->C)
|
|
|
- Cbt = lc->C;
|
|
|
- }
|
|
|
+int
|
|
|
+edfready(Proc *p)
|
|
|
+{
|
|
|
+ Edf *e;
|
|
|
+ Schedq *rq;
|
|
|
+ Proc *l, *pp;
|
|
|
+ void (*pt)(Proc*, int);
|
|
|
+
|
|
|
+ if ((e = p->edf) == nil || (e->flags & Admitted) == 0)
|
|
|
+ return 0; /* Not an edf process */
|
|
|
+
|
|
|
+ edflock();
|
|
|
+ if (e->d <= now){
|
|
|
+ /* past deadline, arrange for next release */
|
|
|
+ if ((e->flags & Sporadic) == 0){
|
|
|
+ /* Non sporadic processes stay true to their period, calculate next release time */
|
|
|
+ while(e->t < now)
|
|
|
+ e->t += e->T;
|
|
|
}
|
|
|
- if (R == 0){
|
|
|
- DEBUG("%T, ", ticks2time(Cbt));
|
|
|
- Cb = Cbt;
|
|
|
+ if (now < e->t){
|
|
|
+ /* Next release is in the future, schedule it */
|
|
|
+ if (p->tt == nil || p->tf != releaseintr){
|
|
|
+ p->tns = e->t;
|
|
|
+ p->tmode = Tabsolute;
|
|
|
+ p->tf = releaseintr;
|
|
|
+ p->ta = p;
|
|
|
+ timeradd(p);
|
|
|
+ DPRINT("%t edfready %lud[%s], release=%t\n",
|
|
|
+ now, p->pid, statename[p->state], e->t);
|
|
|
+ }
|
|
|
+ if(p->state == Running && (e->flags & (Yield|Yieldonblock)) == 0){
|
|
|
+ /* If we were running, we've overrun our CPU allocation
|
|
|
+ * or missed the deadline, continue running best-effort at low priority
|
|
|
+ * Otherwise we were blocked. If we don't yield on block, we continue
|
|
|
+ * best effort
|
|
|
+ */
|
|
|
+ DPRINT(">");
|
|
|
+ p->basepri = PriExtra;
|
|
|
+ p->fixedpri = 1;
|
|
|
+ edfunlock();
|
|
|
+ return 0; /* Stick on runq[PriExtra] */
|
|
|
+ }
|
|
|
+ DPRINT("%t edfready %lud[%s] wait release at %t\n",
|
|
|
+ now, p->pid, statename[p->state], e->t);
|
|
|
+ p->state = Waitrelease;
|
|
|
+ edfunlock();
|
|
|
+ return 1; /* Make runnable later */
|
|
|
}
|
|
|
- DEBUG("ro, ");
|
|
|
+ DPRINT("%t edfready %lud %s release now\n", now, p->pid, statename[p->state]);
|
|
|
+ /* release now */
|
|
|
+ release(p);
|
|
|
+ if(pt = proctrace)
|
|
|
+ pt(p, SRelease);
|
|
|
+ }
|
|
|
+ DPRINT("^");
|
|
|
+ rq = &runq[PriEdf];
|
|
|
+ /* insert in queue in earliest deadline order */
|
|
|
+ lock(runq);
|
|
|
+ l = nil;
|
|
|
+ for(pp = rq->head; pp; pp = pp->rnext){
|
|
|
+ if(pp->edf->d > e->d)
|
|
|
+ break;
|
|
|
+ l = pp;
|
|
|
}
|
|
|
- DEBUG("Cb = %T\n", ticks2time(Cb));
|
|
|
- return Cb;
|
|
|
+ p->rnext = pp;
|
|
|
+ if (l == nil)
|
|
|
+ rq->head = p;
|
|
|
+ else
|
|
|
+ l->rnext = p;
|
|
|
+ if(pp == nil)
|
|
|
+ rq->tail = p;
|
|
|
+ rq->n++;
|
|
|
+ nrdy++;
|
|
|
+ runvec |= 1 << PriEdf;
|
|
|
+ p->priority = PriEdf;
|
|
|
+ unlock(runq);
|
|
|
+ p->readytime = m->ticks;
|
|
|
+ p->state = Ready;
|
|
|
+ if(pt = proctrace)
|
|
|
+ pt(p, SReady);
|
|
|
+ edfunlock();
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
static void
|
|
|
-testenq(Task *t)
|
|
|
+testenq(Proc *p)
|
|
|
{
|
|
|
- Task *tt, **ttp;
|
|
|
+ Proc *xp, **xpp;
|
|
|
+ Edf *e;
|
|
|
|
|
|
- t->testnext = nil;
|
|
|
+ e = p->edf;
|
|
|
+ e->testnext = nil;
|
|
|
if (qschedulability == nil) {
|
|
|
- qschedulability = t;
|
|
|
+ qschedulability = p;
|
|
|
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;
|
|
|
+ SET(xp);
|
|
|
+ for (xpp = &qschedulability; *xpp; xpp = &xp->edf->testnext) {
|
|
|
+ xp = *xpp;
|
|
|
+ if (e->testtime < xp->edf->testtime
|
|
|
+ || (e->testtime == xp->edf->testtime && e->testtype < xp->edf->testtype)){
|
|
|
+ e->testnext = xp;
|
|
|
+ *xpp = p;
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
- assert(tt->testnext == nil);
|
|
|
- tt->testnext = t;
|
|
|
+ assert(xp->edf->testnext == nil);
|
|
|
+ xp->edf->testnext = p;
|
|
|
}
|
|
|
|
|
|
static char *
|
|
|
-edftestschedulability(Task *thetask)
|
|
|
+testschedulability(Proc *theproc)
|
|
|
{
|
|
|
- Task *t;
|
|
|
- Ticks H, G, Cb, ticks;
|
|
|
- int steps;
|
|
|
- List *l;
|
|
|
+ Proc *p;
|
|
|
+ vlong H, G, Cb, ticks;
|
|
|
+ int steps, i;
|
|
|
|
|
|
/* initialize */
|
|
|
- testdelta(thetask);
|
|
|
- if (thetask && (thetask->flags & Verbose))
|
|
|
- pprint("schedulability test for task %d\n", thetask->taskno);
|
|
|
+ DPRINT("schedulability test %lud\n", theproc->pid);
|
|
|
qschedulability = nil;
|
|
|
- for (l = tasks.next; l; l = l->next){
|
|
|
- t = l->i;
|
|
|
- assert(t);
|
|
|
- if (t->state <= EdfExpelled && t != thetask && (t->flags & BestEffort) == 0)
|
|
|
+ for(i=0; i<conf.nproc; i++) {
|
|
|
+ p = proctab(i);
|
|
|
+ if(p->state == Dead)
|
|
|
+ continue;
|
|
|
+ if ((p->edf == nil || (p->edf->flags & Admitted) == 0) && p != theproc)
|
|
|
continue;
|
|
|
- t->testtype = Release;
|
|
|
- t->testtime = 0;
|
|
|
- if (thetask && (thetask->flags & Verbose))
|
|
|
- pprint("\tInit: enqueue task %d\n", t->taskno);
|
|
|
- testenq(t);
|
|
|
+ p->edf->testtype = Rl;
|
|
|
+ p->edf->testtime = 0;
|
|
|
+ DPRINT("\tInit: edfenqueue %lud\n", p->pid);
|
|
|
+ testenq(p);
|
|
|
}
|
|
|
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, t, thetask);
|
|
|
- if (thetask && (thetask->flags & Verbose))
|
|
|
- pprint("\tStep %3d, Ticks %T, task %d, deadline, H += %T → %T, Cb = %T\n",
|
|
|
- steps, ticks2time(ticks), t->taskno,
|
|
|
- ticks2time(t->C), ticks2time(H), ticks2time(Cb));
|
|
|
+ p = qschedulability;
|
|
|
+ qschedulability = p->edf->testnext;
|
|
|
+ ticks = p->edf->testtime;
|
|
|
+ switch (p->edf->testtype){
|
|
|
+ case Dl:
|
|
|
+ H += p->edf->C;
|
|
|
+ Cb = 0;
|
|
|
+ DPRINT("\tStep %3d, Ticks %t, pid %lud, deadline, H += %t → %t, Cb = %t\n",
|
|
|
+ steps, ticks, p->pid, p->edf->C, H, Cb);
|
|
|
if (H+Cb>ticks){
|
|
|
- if (thetask && (thetask->flags & Verbose))
|
|
|
- pprint("task %d not schedulable: H=%T + Cb=%T > ticks=%T\n",
|
|
|
- thetask->taskno, ticks2time(H), ticks2time(Cb), ticks2time(ticks));
|
|
|
+ DPRINT("not schedulable\n");
|
|
|
return "not schedulable";
|
|
|
}
|
|
|
- t->testtime += t->T - t->D;
|
|
|
- t->testtype = Release;
|
|
|
- testenq(t);
|
|
|
+ p->edf->testtime += p->edf->T - p->edf->D;
|
|
|
+ p->edf->testtype = Rl;
|
|
|
+ testenq(p);
|
|
|
break;
|
|
|
- case Release:
|
|
|
- if (thetask && (thetask->flags & Verbose))
|
|
|
- pprint("\tStep %3d, Ticks %T, task %d, release, G %T, C%T\n",
|
|
|
- steps, ticks2time(ticks), t->taskno,
|
|
|
- ticks2time(t->C), ticks2time(G));
|
|
|
+ case Rl:
|
|
|
+ DPRINT("\tStep %3d, Ticks %t, pid %lud, release, G %t, C%t\n",
|
|
|
+ steps, ticks, p->pid, p->edf->C, G);
|
|
|
if(ticks && G <= ticks){
|
|
|
- if (thetask && (thetask->flags & Verbose))
|
|
|
- pprint("task %d schedulable: G=%T <= ticks=%T\n",
|
|
|
- thetask->taskno, ticks2time(G), ticks2time(ticks));
|
|
|
+ DPRINT("schedulable\n");
|
|
|
return nil;
|
|
|
}
|
|
|
- G += t->C;
|
|
|
- t->testtime += t->D;
|
|
|
- t->testtype = Deadline;
|
|
|
- testenq(t);
|
|
|
+ G += p->edf->C;
|
|
|
+ p->edf->testtime += p->edf->D;
|
|
|
+ p->edf->testtype = Dl;
|
|
|
+ testenq(p);
|
|
|
break;
|
|
|
default:
|
|
|
assert(0);
|
|
|
}
|
|
|
}
|
|
|
+ DPRINT("probably not schedulable\n");
|
|
|
return "probably not schedulable";
|
|
|
}
|
|
|
-
|
|
|
-static void
|
|
|
-resacquire(Task *t, CSN *c)
|
|
|
-{
|
|
|
- Ticks now, when, used;
|
|
|
-
|
|
|
- ilock(&edflock);
|
|
|
- now = fastticks(nil);
|
|
|
- used = now - t->scheduled;
|
|
|
- t->scheduled = now;
|
|
|
- t->total += used;
|
|
|
- if (t->flags & BestEffort)
|
|
|
- c->S = c->C;
|
|
|
- else
|
|
|
- t->S -= used;
|
|
|
- if (t->curcsn)
|
|
|
- t->curcsn->S -= used;
|
|
|
- when = now + c->S;
|
|
|
- if (deadlinetimer[m->machno].when == 0 || when < deadlinetimer[m->machno].when){
|
|
|
- deadlinetimer[m->machno].when = when;
|
|
|
- timeradd(&deadlinetimer[m->machno]);
|
|
|
- }
|
|
|
- t->Delta = c->Delta;
|
|
|
- t->curcsn = c;
|
|
|
- t->state = EdfRunning;
|
|
|
- iunlock(&edflock);
|
|
|
- if(devrt) devrt(t, now, SResacq);
|
|
|
- /* priority is going up, no need to reschedule */
|
|
|
-}
|
|
|
-
|
|
|
-static void
|
|
|
-resrelease(Task *t)
|
|
|
-{
|
|
|
- Ticks now, when, used;
|
|
|
- CSN *c;
|
|
|
-
|
|
|
- ilock(&edflock);
|
|
|
- c = t->curcsn;
|
|
|
- assert(c);
|
|
|
- t->curcsn = c->p;
|
|
|
- now = fastticks(nil);
|
|
|
- used = now - t->scheduled;
|
|
|
- t->scheduled = now;
|
|
|
- t->total += used;
|
|
|
- if ((t->flags & BestEffort) == 0)
|
|
|
- t->S -= used;
|
|
|
- c->S -= used;
|
|
|
- if (now + t->S > t->d)
|
|
|
- when = t->d;
|
|
|
- else
|
|
|
- when = now + t->S;
|
|
|
- if (t->curcsn){
|
|
|
- t->curcsn->S -= c->S; /* the sins of the fathers shall be visited upon the children */
|
|
|
- t->Delta = t->curcsn->Delta;
|
|
|
- if (when > now + t->curcsn->S)
|
|
|
- when = now + t->curcsn->S;
|
|
|
- }else{
|
|
|
- t->Delta = Infinity;
|
|
|
- if (t->flags & BestEffort)
|
|
|
- t->state = EdfBestEffort;
|
|
|
- }
|
|
|
- c->S = 0LL; /* don't allow reuse this period */
|
|
|
- deadlinetimer[m->machno].when = when;
|
|
|
- timeradd(&deadlinetimer[m->machno]);
|
|
|
- iunlock(&edflock);
|
|
|
-
|
|
|
- qunlock(&edfschedlock);
|
|
|
- if(devrt) devrt(t, now, SResrel);
|
|
|
- sched(); /* reschedule */
|
|
|
- qlock(&edfschedlock);
|
|
|
-}
|
|
|
-
|
|
|
-Edfinterface realedf = {
|
|
|
- .isedf = isedf,
|
|
|
- .edfbury = edfbury,
|
|
|
- .edfanyready = edfanyready,
|
|
|
- .edfready = edfready,
|
|
|
- .edfrunproc = edfrunproc,
|
|
|
- .edfblock = edfblock,
|
|
|
- .edfinit = edfinit,
|
|
|
- .edfexpel = edfexpel,
|
|
|
- .edfadmit = edfadmit,
|
|
|
- .edfdeadline = edfdeadline,
|
|
|
- .resacquire = resacquire,
|
|
|
- .resrelease = resrelease,
|
|
|
-};
|