~kris/9p

9hist

164113f7232a3903dc8635e74af8277750472475 — David du Colombier 24 years ago abed1fa
Plan 9 from Bell Labs 2002-03-15
10 files changed, 1189 insertions(+), 25 deletions(-)

M pc/dat.h
M pc/etherelnk3.c
A port/edf.c
A port/edf.h
M port/portclock.c
M port/portdat.h
M port/proc.c
M port/qlock.c
M port/sysproc.c
M port/taslock.c
M pc/dat.h => pc/dat.h +2 -1
@@ 29,8 29,9 @@ struct Lock
	ulong	key;
	ulong	sr;
	ulong	pc;
	Proc	*p;
	Proc	*	p;
	ushort	isilock;
	Proc	*	edfwaiting;
};

struct Label

M pc/etherelnk3.c => pc/etherelnk3.c +2 -1
@@ 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));

A port/edf.c => port/edf.c +981 -0
@@ 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);
}

A port/edf.h => port/edf.h +125 -0
@@ 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);

M port/portclock.c => port/portclock.c +3 -1
@@ 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();
	}

M port/portdat.h => port/portdat.h +15 -1
@@ 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 */

M port/proc.c => port/proc.c +34 -14
@@ 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);

M port/qlock.c => port/qlock.c +7 -0
@@ 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();
}

M port/sysproc.c => port/sysproc.c +3 -0
@@ 4,6 4,7 @@
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	"../port/edf.h"

#include	<a.out.h>



@@ 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();

M port/taslock.c => port/taslock.c +17 -7
@@ 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