From 164113f7232a3903dc8635e74af8277750472475 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Fri, 15 Mar 2002 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2002-03-15 --- pc/dat.h | 3 +- pc/etherelnk3.c | 3 +- port/edf.c | 981 +++++++++++++++++++++++++++++++++++++++++++++++ port/edf.h | 125 ++++++ port/portclock.c | 4 +- port/portdat.h | 16 +- port/proc.c | 48 ++- port/qlock.c | 7 + port/sysproc.c | 3 + port/taslock.c | 24 +- 10 files changed, 1189 insertions(+), 25 deletions(-) create mode 100644 port/edf.c create mode 100644 port/edf.h diff --git a/pc/dat.h b/pc/dat.h index 1c42ab85d94dd10591fa8127d4832fa14e83c751..cdacd2cbbd442d401edde0bdff90124e47ee3fd8 100644 --- a/pc/dat.h +++ b/pc/dat.h @@ -29,8 +29,9 @@ struct Lock ulong key; ulong sr; ulong pc; - Proc *p; + Proc * p; ushort isilock; + Proc * edfwaiting; }; struct Label diff --git a/pc/etherelnk3.c b/pc/etherelnk3.c index bd3a1f674cb8092f5549219291f7a24d4c2426bf..ef4ee9908079cb32e8ecc929e70e3e18da27b517 100644 --- a/pc/etherelnk3.c +++ b/pc/etherelnk3.c @@ -1833,6 +1833,7 @@ etherelnk3reset(Ether* ether) case 0x9051: case 0x9055: case 0x9200: + case 0x7646: /* 3CSOHO100-TX */ case 0x5157: /* 3C575 Cyclone */ if(BUSTYPE(ether->tbdf) != BusPCI) goto buggery; @@ -1909,7 +1910,7 @@ etherelnk3reset(Ether* ether) /* * forgive me, but i am weak */ - if(did == 0x9055 || did == 0x9200 || did == 0x5157){ + if(did == 0x9055 || did == 0x7646 || did == 0x9200 || did == 0x5157){ xcvr = xcvrMii; txrxreset(port); XCVRDEBUG("905[BC] reset ops 0x%uX\n", ins(port+ResetOp905B)); diff --git a/port/edf.c b/port/edf.c new file mode 100644 index 0000000000000000000000000000000000000000..1e146d30791e4ba5924000f8de5c02ab6bad08c7 --- /dev/null +++ b/port/edf.c @@ -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); +} diff --git a/port/edf.h b/port/edf.h new file mode 100644 index 0000000000000000000000000000000000000000..593401b058ce036c5257256623cc7fd4c0e68e7b --- /dev/null +++ b/port/edf.h @@ -0,0 +1,125 @@ +enum { + Nproc = 8, + Nres = 8, + Ntask = 8, + Maxtasks = 20, + Maxresources = 20, + Maxsteps = Maxtasks * 2 * 100, /* 100 periods of maximum # of tasks */ + + /* Edf.flags field */ + Verbose = 0x1, + Useblocking = 0x2, +}; + +typedef uvlong Ticks; + +typedef struct Task Task; +typedef struct Resource Resource; +typedef struct Edf Edf; +typedef struct Taskq Taskq; + +enum Edfstate { + EdfUnused, /* task structure not in use */ + EdfExpelled, /* in initialization, not yet admitted */ + EdfAdmitted, /* admitted, but not started */ + + EdfIdle, /* admitted, but no member processes */ + EdfAwaitrelease, /* released, but too early (on qwaitrelease) */ + EdfReleased, /* released, but not yet scheduled (on qreleased) */ + EdfRunning, /* one of this task's procs is running (on stack) */ + EdfExtra, /* one of this task's procs is running in extra time (off stack) */ + EdfPreempted, /* the running proc was preempted */ + EdfBlocked, /* none of the procs are runnable as a result of sleeping */ + EdfDeadline, /* none of the procs are runnable as a result of scheduling */ +}; +typedef enum Edfstate Edfstate; + +struct Edf { + /* time intervals */ + Ticks D; /* Deadline */ + Ticks Δ; /* Inherited deadline */ + Ticks T; /* period */ + Ticks C; /* Cost */ + Ticks S; /* Slice: time remaining in this period */ + /* times */ + Ticks r; /* (this) release time */ + Ticks d; /* (this) deadline */ + Ticks t; /* Start of next period, t += T at release */ + /* for schedulability testing */ + Ticks testΔ; + int testtype; /* Release or Deadline */ + Ticks testtime; + Task * testnext; + /* other */ + Edfstate state; +}; + +struct Task { + QLock; + Edf; + Ticks scheduled; + Schedq runq; /* Queue of runnable member procs */ + Proc * procs[Nproc]; /* List of member procs; may contain holes */ + int nproc; /* number of them */ + Resource* res[Nres]; /* List of resources; may contain holes */ + int nres; /* number of them */ + char *user; /* mallocated */ + Dirtab dir; + int flags; /* e.g., Verbose */ + Task *rnext; +}; + +struct Taskq +{ + Lock; + Task* head; + int (*before)(Task*, Task*); /* ordering function for queue (nil: fifo) */ +}; + +struct Resource +{ + char * name; + Task * tasks[Ntask]; /* may contain holes */ + int ntasks; + Ticks Δ; + /* for schedulability testing */ + Ticks testΔ; +}; + +extern Lock edftestlock; /* for atomic admitting/expelling */ +extern Task tasks[Maxtasks]; /* may contain holes */ +extern int ntasks; +extern Resource resources[Maxresources]; /* may contain holes */ +extern int nresources; +extern Lock edflock; +extern Taskq qwaitrelease; +extern Taskq qreleased; +extern Taskq qextratime; +extern Taskq edfstack[]; +extern int edf_stateupdate; +extern void (*devsched)(Task *, Ticks, int); +extern char * edf_statename[]; + +#pragma varargck type "T" Time +#pragma varargck type "U" Ticks + +/* Interface: */ +void edf_preempt(Task*); /* Stop current task, administrate run time, leave on stack */ +void edf_deadline(Proc*); /* Remove current task from edfstack, schedule its next release */ +void edf_release(Task*); /* Release a task */ +void edf_schedule(Task *); /* Run a released task: remove from qrelease, push onto edfstack */ +char * edf_admit(Task*); +void edf_expel(Task*); +void edf_bury(Proc*); /* Proc dies, update Task membership */ +void edf_sched(Task*); /* Figure out what to do with task (after a state change) */ +int isedf(Proc*); /* Proc must be edf scheduled */ +void edf_stop(Proc*); /* Just event generation; should probably be done differently */ +int edf_anyready(void); +void edf_ready(Proc*); +Proc * edf_runproc(void); +void edf_block(Proc*); /* Edf proc has blocked, do the admin */ +void edf_init(void); +int edf_waitlock(Lock*); +void edf_releaselock(Lock*); +Time ticks2time(Ticks); +Ticks time2ticks(Time); diff --git a/port/portclock.c b/port/portclock.c index edc1917470ba0fa0aba72db21aae37dea74f34bb..7a8fe5ef20e17e6e58c3b6796512f8548b25d6f1 100644 --- a/port/portclock.c +++ b/port/portclock.c @@ -5,6 +5,7 @@ #include "fns.h" #include "io.h" #include "ureg.h" +#include "../port/edf.h" void (*kproftimer)(ulong); @@ -75,7 +76,8 @@ portclock(Ureg *ur) if(up == 0 || up->state != Running) return; - if(anyready()){ + // i.e. don't schedule an EDF process here! + if(!isedf(up) && anyready()){ sched(); splhi(); } diff --git a/port/portdat.h b/port/portdat.h index 59711cf93e6fc642346017e488732045037e6831..327210619f039bf208ce66f146d63010ceb836de 100644 --- a/port/portdat.h +++ b/port/portdat.h @@ -37,8 +37,10 @@ typedef struct Rendez Rendez; typedef struct Rgrp Rgrp; typedef struct RWlock RWlock; typedef struct Sargs Sargs; +typedef struct Schedq Schedq; typedef struct Segment Segment; typedef struct Session Session; +typedef struct Task Task; typedef struct Talarm Talarm; typedef struct Uart Uart; typedef struct Waitq Waitq; @@ -538,6 +540,16 @@ enum PriRoot = 13, /* base priority for root processes */ }; +typedef uvlong Ticks; + +struct Schedq +{ + Lock; + Proc* head; + Proc* tail; + int n; +}; + struct Proc { Label sched; /* known to l.s */ @@ -623,7 +635,7 @@ struct Proc Note lastnote; int (*notify)(void*, char*); - int lockwait; /* waiting for lock to be released */ + Lock *lockwait; /* waiting for lock to be released */ Mach *wired; Mach *mp; /* machine this process last ran on */ @@ -637,6 +649,8 @@ struct Proc int preempted; /* true if this process hasn't finished the interrupt * that last preempted it */ + Task *task; /* if non-null, real-time proc, task contains scheduling params */ + ulong qpc; /* pc calling last blocking qlock */ void *ureg; /* User registers for notes */ diff --git a/port/proc.c b/port/proc.c index e7a32fb2b5d39c5ff358178d972b81ee6ee777be..aed0e92732f87791d18f2e0290c58cb0359c4d98 100644 --- a/port/proc.c +++ b/port/proc.c @@ -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); diff --git a/port/qlock.c b/port/qlock.c index 71418ed1144510304600d72fa2968f7771ff29b2..739df89dc8462562b2e358ea65b766f0cb1c164f 100644 --- a/port/qlock.c +++ b/port/qlock.c @@ -3,6 +3,7 @@ #include "mem.h" #include "dat.h" #include "fns.h" +#include "../port/edf.h" struct { ulong rlock; @@ -37,6 +38,8 @@ rwstats.qlockq++; q->tail = mp; mp->qnext = 0; mp->state = Queueing; + if (isedf(mp)) + edf_block(mp); up->qpc = getcallerpc(&q); unlock(&q->use); sched(); @@ -101,6 +104,8 @@ rwstats.rlockq++; q->tail = mp; mp->qnext = 0; mp->state = QueueingR; + if (isedf(mp)) + edf_block(mp); unlock(&q->use); sched(); } @@ -157,6 +162,8 @@ rwstats.wlockq++; q->tail = mp; mp->qnext = 0; mp->state = QueueingW; + if (isedf(mp)) + edf_block(mp); unlock(&q->use); sched(); } diff --git a/port/sysproc.c b/port/sysproc.c index e41c93bfc466d15d2f4c813ccc5b5f40c91edeb4..e7af822917a0d6f20564bc91803dfc2d95a1407a 100644 --- a/port/sysproc.c +++ b/port/sysproc.c @@ -4,6 +4,7 @@ #include "dat.h" #include "fns.h" #include "../port/error.h" +#include "../port/edf.h" #include @@ -824,6 +825,8 @@ sysrendezvous(ulong *arg) up->rendhash = *l; *l = up; up->state = Rendezvous; + if (isedf(up)) + edf_block(up); unlock(up->rgrp); sched(); diff --git a/port/taslock.c b/port/taslock.c index 04ef841131ecfe99701e9babfc9f136e4db15deb..a1f2de6088415079531d01489242f96a9a192769 100644 --- a/port/taslock.c +++ b/port/taslock.c @@ -4,6 +4,7 @@ #include "dat.h" #include "fns.h" #include "../port/error.h" +#include "../port/edf.h" struct { ulong locks; @@ -62,8 +63,8 @@ lock(Lock *l) cansched = up != nil && up->state == Running; if(cansched){ oldpri = up->priority; - up->lockwait = 1; up->priority = PriLock; + up->lockwait = l; } else oldpri = 0; @@ -71,8 +72,16 @@ lock(Lock *l) lockstats.inglare++; i = 0; while(l->key){ - if(conf.nmach < 2 && cansched){ - if(i++ > 1000){ + if (isedf(up)){ + /* Edf process waiting for a lock; process holding lock will not + * be scheduled unless we give up the processor. We give up + * the processor, but make sure we get awoken when the lock + * is released + */ + if (edf_waitlock(l)) + sched(); + } else if(conf.nmach < 2 && cansched){ + if (i++ > 1000){ i = 0; lockloop(l, pc); } @@ -89,7 +98,7 @@ lock(Lock *l) l->p = up; l->isilock = 0; if(cansched){ - up->lockwait = 0; + up->lockwait = nil; up->priority = oldpri; } return; @@ -120,7 +129,7 @@ ilock(Lock *l) cansched = up != nil && up->state == Running; if(cansched){ oldpri = up->priority; - up->lockwait = 1; + up->lockwait = l; up->priority = PriLock; } else oldpri = 0; @@ -142,7 +151,7 @@ dumplockmem("ilock:", l); l->p = up; l->isilock = 1; if(cansched){ - up->lockwait = 0; + up->lockwait = nil; up->priority = oldpri; } return; @@ -165,7 +174,6 @@ canlock(Lock *l) void unlock(Lock *l) { - if(l->key == 0) print("unlock: not locked: pc %luX\n", getcallerpc(&l)); if(l->isilock) @@ -173,6 +181,8 @@ unlock(Lock *l) l->pc = 0; l->key = 0; coherence(); + if (l->edfwaiting) + edf_releaselock(l); } void