xref: /plan9-contrib/sys/src/9k/port/edf.c (revision d46407a37b53d968b453d19c0e464c526df5e227)
19ef1f84bSDavid du Colombier /* EDF scheduling */
29ef1f84bSDavid du Colombier #include	"u.h"
39ef1f84bSDavid du Colombier #include	"../port/lib.h"
49ef1f84bSDavid du Colombier #include	"mem.h"
59ef1f84bSDavid du Colombier #include	"dat.h"
69ef1f84bSDavid du Colombier #include	"fns.h"
79ef1f84bSDavid du Colombier #include	"../port/error.h"
89ef1f84bSDavid du Colombier 
99ef1f84bSDavid du Colombier #include	"../port/edf.h"
10*d46407a3SDavid du Colombier #include	<ptrace.h>
119ef1f84bSDavid du Colombier 
129ef1f84bSDavid du Colombier /* debugging */
139ef1f84bSDavid du Colombier enum {
149ef1f84bSDavid du Colombier 	Dontprint = 1,
159ef1f84bSDavid du Colombier };
169ef1f84bSDavid du Colombier 
179ef1f84bSDavid du Colombier #define DPRINT	if(Dontprint){}else print
189ef1f84bSDavid du Colombier 
199ef1f84bSDavid du Colombier static long	now;	/* Low order 32 bits of time in µs */
209ef1f84bSDavid du Colombier extern ulong	delayedscheds;
219ef1f84bSDavid du Colombier extern Schedq	runq[Nrq];
229ef1f84bSDavid du Colombier extern int	nrdy;
239ef1f84bSDavid du Colombier extern ulong	runvec;
249ef1f84bSDavid du Colombier 
259ef1f84bSDavid du Colombier /* Statistics stuff */
269ef1f84bSDavid du Colombier ulong		nilcount;
279ef1f84bSDavid du Colombier ulong		scheds;
289ef1f84bSDavid du Colombier ulong		edfnrun;
299ef1f84bSDavid du Colombier int		misseddeadlines;
309ef1f84bSDavid du Colombier 
319ef1f84bSDavid du Colombier /* Edfschedlock protects modification of admission params */
329ef1f84bSDavid du Colombier int		edfinited;
339ef1f84bSDavid du Colombier QLock		edfschedlock;
349ef1f84bSDavid du Colombier static Lock	thelock;
359ef1f84bSDavid du Colombier 
369ef1f84bSDavid du Colombier enum{
379ef1f84bSDavid du Colombier 	Dl,	/* Invariant for schedulability test: Dl < Rl */
389ef1f84bSDavid du Colombier 	Rl,
399ef1f84bSDavid du Colombier };
409ef1f84bSDavid du Colombier 
419ef1f84bSDavid du Colombier static char *testschedulability(Proc*);
429ef1f84bSDavid du Colombier static Proc *qschedulability;
439ef1f84bSDavid du Colombier 
449ef1f84bSDavid du Colombier enum {
459ef1f84bSDavid du Colombier 	Onemicrosecond =	1,
469ef1f84bSDavid du Colombier 	Onemillisecond =	1000,
479ef1f84bSDavid du Colombier 	Onesecond =		1000000,
489ef1f84bSDavid du Colombier 	OneRound = 		Onemillisecond/2,
499ef1f84bSDavid du Colombier };
509ef1f84bSDavid du Colombier 
519ef1f84bSDavid du Colombier static int
timeconv(Fmt * f)529ef1f84bSDavid du Colombier timeconv(Fmt *f)
539ef1f84bSDavid du Colombier {
549ef1f84bSDavid du Colombier 	char buf[128], *sign;
559ef1f84bSDavid du Colombier 	vlong t;
569ef1f84bSDavid du Colombier 
579ef1f84bSDavid du Colombier 	buf[0] = 0;
589ef1f84bSDavid du Colombier 	switch(f->r) {
599ef1f84bSDavid du Colombier 	case 'U':
609ef1f84bSDavid du Colombier 		t = va_arg(f->args, uvlong);
619ef1f84bSDavid du Colombier 		break;
629ef1f84bSDavid du Colombier 	case 't':			/* vlong in nanoseconds */
639ef1f84bSDavid du Colombier 		t = va_arg(f->args, long);
649ef1f84bSDavid du Colombier 		break;
659ef1f84bSDavid du Colombier 	default:
669ef1f84bSDavid du Colombier 		return fmtstrcpy(f, "(timeconv)");
679ef1f84bSDavid du Colombier 	}
689ef1f84bSDavid du Colombier 	if (t < 0) {
699ef1f84bSDavid du Colombier 		sign = "-";
709ef1f84bSDavid du Colombier 		t = -t;
719ef1f84bSDavid du Colombier 	}
729ef1f84bSDavid du Colombier 	else
739ef1f84bSDavid du Colombier 		sign = "";
749ef1f84bSDavid du Colombier 	if (t > Onesecond){
759ef1f84bSDavid du Colombier 		t += OneRound;
76406c76faSDavid du Colombier 		snprint(buf, sizeof buf, "%s%d.%.3ds", sign,
77406c76faSDavid du Colombier 			(int)(t / Onesecond),
789ef1f84bSDavid du Colombier 			(int)(t % Onesecond)/Onemillisecond);
799ef1f84bSDavid du Colombier 	}else if (t > Onemillisecond)
80406c76faSDavid du Colombier 		snprint(buf, sizeof buf, "%s%d.%.3dms", sign,
81406c76faSDavid du Colombier 			(int)(t / Onemillisecond), (int)(t % Onemillisecond));
829ef1f84bSDavid du Colombier 	else
83406c76faSDavid du Colombier 		snprint(buf, sizeof buf, "%s%dµs", sign, (int)t);
849ef1f84bSDavid du Colombier 	return fmtstrcpy(f, buf);
859ef1f84bSDavid du Colombier }
869ef1f84bSDavid du Colombier 
879ef1f84bSDavid du Colombier long edfcycles;
889ef1f84bSDavid du Colombier 
899ef1f84bSDavid du Colombier Edf*
edflock(Proc * p)909ef1f84bSDavid du Colombier edflock(Proc *p)
919ef1f84bSDavid du Colombier {
929ef1f84bSDavid du Colombier 	Edf *e;
939ef1f84bSDavid du Colombier 
949ef1f84bSDavid du Colombier 	if (p->edf == nil)
959ef1f84bSDavid du Colombier 		return nil;
969ef1f84bSDavid du Colombier 	ilock(&thelock);
979ef1f84bSDavid du Colombier 	if((e = p->edf) && (e->flags & Admitted)){
989ef1f84bSDavid du Colombier 		thelock.pc = getcallerpc(&p);
999ef1f84bSDavid du Colombier #ifdef EDFCYCLES
1009ef1f84bSDavid du Colombier 		edfcycles -= lcycles();
1019ef1f84bSDavid du Colombier #endif
1029ef1f84bSDavid du Colombier 		now = µs();
1039ef1f84bSDavid du Colombier 		return e;
1049ef1f84bSDavid du Colombier 	}
1059ef1f84bSDavid du Colombier 	iunlock(&thelock);
1069ef1f84bSDavid du Colombier 	return nil;
1079ef1f84bSDavid du Colombier }
1089ef1f84bSDavid du Colombier 
1099ef1f84bSDavid du Colombier void
edfunlock(void)1109ef1f84bSDavid du Colombier edfunlock(void)
1119ef1f84bSDavid du Colombier {
1129ef1f84bSDavid du Colombier 
1139ef1f84bSDavid du Colombier #ifdef EDFCYCLES
1149ef1f84bSDavid du Colombier 	edfcycles += lcycles();
1159ef1f84bSDavid du Colombier #endif
1169ef1f84bSDavid du Colombier 	edfnrun++;
1179ef1f84bSDavid du Colombier 	iunlock(&thelock);
1189ef1f84bSDavid du Colombier }
1199ef1f84bSDavid du Colombier 
1209ef1f84bSDavid du Colombier void
edfinit(Proc * p)1219ef1f84bSDavid du Colombier edfinit(Proc*p)
1229ef1f84bSDavid du Colombier {
1239ef1f84bSDavid du Colombier 	if(!edfinited){
1249ef1f84bSDavid du Colombier 		fmtinstall('t', timeconv);
1259ef1f84bSDavid du Colombier 		edfinited++;
1269ef1f84bSDavid du Colombier 	}
1279ef1f84bSDavid du Colombier 	now = µs();
1289ef1f84bSDavid du Colombier 	DPRINT("%lud edfinit %d[%s]\n", now, p->pid, statename[p->state]);
1299ef1f84bSDavid du Colombier 	p->edf = malloc(sizeof(Edf));
1309ef1f84bSDavid du Colombier 	if(p->edf == nil)
1319ef1f84bSDavid du Colombier 		error(Enomem);
1329ef1f84bSDavid du Colombier 	return;
1339ef1f84bSDavid du Colombier }
1349ef1f84bSDavid du Colombier 
1359ef1f84bSDavid du Colombier static void
deadlineintr(Ureg *,Timer * t)1369ef1f84bSDavid du Colombier deadlineintr(Ureg*, Timer *t)
1379ef1f84bSDavid du Colombier {
1389ef1f84bSDavid du Colombier 	/* Proc reached deadline */
1399ef1f84bSDavid du Colombier 	extern int panicking;
1409ef1f84bSDavid du Colombier 	Proc *p;
141*d46407a3SDavid du Colombier 	void (*pt)(Proc*, int, vlong, vlong);
1429ef1f84bSDavid du Colombier 
1439ef1f84bSDavid du Colombier 	if(panicking || active.exiting)
1449ef1f84bSDavid du Colombier 		return;
1459ef1f84bSDavid du Colombier 
1469ef1f84bSDavid du Colombier 	p = t->ta;
1479ef1f84bSDavid du Colombier 	now = µs();
1489ef1f84bSDavid du Colombier 	DPRINT("%lud deadlineintr %d[%s]\n", now, p->pid, statename[p->state]);
1499ef1f84bSDavid du Colombier 	/* If we're interrupting something other than the proc pointed to by t->a,
1509ef1f84bSDavid du Colombier 	 * we've already achieved recheduling, so we need not do anything
1519ef1f84bSDavid du Colombier 	 * Otherwise, we must cause a reschedule, but if we call sched()
1529ef1f84bSDavid du Colombier 	 * here directly, the timer interrupt routine will not finish its business
1539ef1f84bSDavid du Colombier 	 * Instead, we cause the resched to happen when the interrupted proc
1549ef1f84bSDavid du Colombier 	 * returns to user space
1559ef1f84bSDavid du Colombier 	 */
1569ef1f84bSDavid du Colombier 	if(p == up){
1579ef1f84bSDavid du Colombier 		if(up->trace && (pt = proctrace))
158*d46407a3SDavid du Colombier 			pt(up, SInts, 0, 0);
1599ef1f84bSDavid du Colombier 		up->delaysched++;
1609ef1f84bSDavid du Colombier 		delayedscheds++;
1619ef1f84bSDavid du Colombier 	}
1629ef1f84bSDavid du Colombier }
1639ef1f84bSDavid du Colombier 
1649ef1f84bSDavid du Colombier static void
release(Proc * p)1659ef1f84bSDavid du Colombier release(Proc *p)
1669ef1f84bSDavid du Colombier {
1679ef1f84bSDavid du Colombier 	/* Called with edflock held */
1689ef1f84bSDavid du Colombier 	Edf *e;
169*d46407a3SDavid du Colombier 	void (*pt)(Proc*, int, vlong, vlong);
1709ef1f84bSDavid du Colombier 	long n;
1719ef1f84bSDavid du Colombier 	vlong nowns;
1729ef1f84bSDavid du Colombier 
1739ef1f84bSDavid du Colombier 	e = p->edf;
1749ef1f84bSDavid du Colombier 	e->flags &= ~Yield;
1759ef1f84bSDavid du Colombier 	if(e->d - now < 0){
1769ef1f84bSDavid du Colombier 		e->periods++;
1779ef1f84bSDavid du Colombier 		e->r = now;
1789ef1f84bSDavid du Colombier 		if((e->flags & Sporadic) == 0){
1799ef1f84bSDavid du Colombier 			/*
1809ef1f84bSDavid du Colombier 			 * Non sporadic processes stay true to their period;
1819ef1f84bSDavid du Colombier 			 * calculate next release time.
1829ef1f84bSDavid du Colombier 			 * Second test limits duration of while loop.
1839ef1f84bSDavid du Colombier 			 */
1849ef1f84bSDavid du Colombier 			if((n = now - e->t) > 0){
1859ef1f84bSDavid du Colombier 				if(n < e->T)
1869ef1f84bSDavid du Colombier 					e->t += e->T;
1879ef1f84bSDavid du Colombier 				else
1889ef1f84bSDavid du Colombier 					e->t = now + e->T - (n % e->T);
1899ef1f84bSDavid du Colombier 			}
1909ef1f84bSDavid du Colombier 		}else{
1919ef1f84bSDavid du Colombier 			/* Sporadic processes may not be released earlier than
1929ef1f84bSDavid du Colombier 			 * one period after this release
1939ef1f84bSDavid du Colombier 			 */
1949ef1f84bSDavid du Colombier 			e->t = e->r + e->T;
1959ef1f84bSDavid du Colombier 		}
1969ef1f84bSDavid du Colombier 		e->d = e->r + e->D;
1979ef1f84bSDavid du Colombier 		e->S = e->C;
1989ef1f84bSDavid du Colombier 		DPRINT("%lud release %d[%s], r=%lud, d=%lud, t=%lud, S=%lud\n",
1999ef1f84bSDavid du Colombier 			now, p->pid, statename[p->state], e->r, e->d, e->t, e->S);
2009ef1f84bSDavid du Colombier 		if(pt = proctrace){
2019ef1f84bSDavid du Colombier 			nowns = todget(nil);
202*d46407a3SDavid du Colombier 			pt(p, SRelease, nowns, 0);
203*d46407a3SDavid du Colombier 			pt(p, SDeadline, nowns + 1000LL*e->D, 0);
2049ef1f84bSDavid du Colombier 		}
2059ef1f84bSDavid du Colombier 	}else{
2069ef1f84bSDavid du Colombier 		DPRINT("%lud release %d[%s], too late t=%lud, called from %#p\n",
2079ef1f84bSDavid du Colombier 			now, p->pid, statename[p->state], e->t, getcallerpc(&p));
2089ef1f84bSDavid du Colombier 	}
2099ef1f84bSDavid du Colombier }
2109ef1f84bSDavid du Colombier 
2119ef1f84bSDavid du Colombier static void
releaseintr(Ureg *,Timer * t)2129ef1f84bSDavid du Colombier releaseintr(Ureg*, Timer *t)
2139ef1f84bSDavid du Colombier {
2149ef1f84bSDavid du Colombier 	Proc *p;
2159ef1f84bSDavid du Colombier 	extern int panicking;
2169ef1f84bSDavid du Colombier 	Schedq *rq;
2179ef1f84bSDavid du Colombier 
2189ef1f84bSDavid du Colombier 	if(panicking || active.exiting)
2199ef1f84bSDavid du Colombier 		return;
2209ef1f84bSDavid du Colombier 
2219ef1f84bSDavid du Colombier 	p = t->ta;
2229ef1f84bSDavid du Colombier 	if((edflock(p)) == nil)
2239ef1f84bSDavid du Colombier 		return;
2249ef1f84bSDavid du Colombier 	DPRINT("%lud releaseintr %d[%s]\n", now, p->pid, statename[p->state]);
2259ef1f84bSDavid du Colombier 	switch(p->state){
2269ef1f84bSDavid du Colombier 	default:
2279ef1f84bSDavid du Colombier 		edfunlock();
2289ef1f84bSDavid du Colombier 		return;
2299ef1f84bSDavid du Colombier 	case Ready:
2309ef1f84bSDavid du Colombier 		/* remove proc from current runq */
2319ef1f84bSDavid du Colombier 		rq = &runq[p->priority];
2329ef1f84bSDavid du Colombier 		if(dequeueproc(rq, p) != p){
2339ef1f84bSDavid du Colombier 			DPRINT("releaseintr: can't find proc or lock race\n");
2349ef1f84bSDavid du Colombier 			release(p);	/* It'll start best effort */
2359ef1f84bSDavid du Colombier 			edfunlock();
2369ef1f84bSDavid du Colombier 			return;
2379ef1f84bSDavid du Colombier 		}
2389ef1f84bSDavid du Colombier 		p->state = Waitrelease;
2399ef1f84bSDavid du Colombier 		/* fall through */
2409ef1f84bSDavid du Colombier 	case Waitrelease:
2419ef1f84bSDavid du Colombier 		release(p);
2429ef1f84bSDavid du Colombier 		edfunlock();
2439ef1f84bSDavid du Colombier 		if(p->state == Wakeme){
2449ef1f84bSDavid du Colombier 			iprint("releaseintr: wakeme\n");
2459ef1f84bSDavid du Colombier 		}
2469ef1f84bSDavid du Colombier 		ready(p);
2479ef1f84bSDavid du Colombier 		if(up){
2489ef1f84bSDavid du Colombier 			up->delaysched++;
2499ef1f84bSDavid du Colombier 			delayedscheds++;
2509ef1f84bSDavid du Colombier 		}
2519ef1f84bSDavid du Colombier 		return;
2529ef1f84bSDavid du Colombier 	case Running:
2539ef1f84bSDavid du Colombier 		release(p);
2549ef1f84bSDavid du Colombier 		edfrun(p, 1);
2559ef1f84bSDavid du Colombier 		break;
2569ef1f84bSDavid du Colombier 	case Wakeme:
2579ef1f84bSDavid du Colombier 		release(p);
2589ef1f84bSDavid du Colombier 		edfunlock();
2599ef1f84bSDavid du Colombier 		if(p->trend)
2609ef1f84bSDavid du Colombier 			wakeup(p->trend);
2619ef1f84bSDavid du Colombier 		p->trend = nil;
2629ef1f84bSDavid du Colombier 		if(up){
2639ef1f84bSDavid du Colombier 			up->delaysched++;
2649ef1f84bSDavid du Colombier 			delayedscheds++;
2659ef1f84bSDavid du Colombier 		}
2669ef1f84bSDavid du Colombier 		return;
2679ef1f84bSDavid du Colombier 	}
2689ef1f84bSDavid du Colombier 	edfunlock();
2699ef1f84bSDavid du Colombier }
2709ef1f84bSDavid du Colombier 
2719ef1f84bSDavid du Colombier void
edfrecord(Proc * p)2729ef1f84bSDavid du Colombier edfrecord(Proc *p)
2739ef1f84bSDavid du Colombier {
2749ef1f84bSDavid du Colombier 	long used;
2759ef1f84bSDavid du Colombier 	Edf *e;
276*d46407a3SDavid du Colombier 	void (*pt)(Proc*, int, vlong, vlong);
2779ef1f84bSDavid du Colombier 
2789ef1f84bSDavid du Colombier 	if((e = edflock(p)) == nil)
2799ef1f84bSDavid du Colombier 		return;
2809ef1f84bSDavid du Colombier 	used = now - e->s;
2819ef1f84bSDavid du Colombier 	if(e->d - now <= 0)
2829ef1f84bSDavid du Colombier 		e->edfused += used;
2839ef1f84bSDavid du Colombier 	else
2849ef1f84bSDavid du Colombier 		e->extraused += used;
2859ef1f84bSDavid du Colombier 	if(e->S > 0){
2869ef1f84bSDavid du Colombier 		if(e->S <= used){
2879ef1f84bSDavid du Colombier 			if(pt = proctrace)
288*d46407a3SDavid du Colombier 				pt(p, SSlice, 0, 0);
2899ef1f84bSDavid du Colombier 			DPRINT("%lud edfrecord slice used up\n", now);
2909ef1f84bSDavid du Colombier 			e->d = now;
2919ef1f84bSDavid du Colombier 			e->S = 0;
2929ef1f84bSDavid du Colombier 		}else
2939ef1f84bSDavid du Colombier 			e->S -= used;
2949ef1f84bSDavid du Colombier 	}
2959ef1f84bSDavid du Colombier 	e->s = now;
2969ef1f84bSDavid du Colombier 	edfunlock();
2979ef1f84bSDavid du Colombier }
2989ef1f84bSDavid du Colombier 
2999ef1f84bSDavid du Colombier void
edfrun(Proc * p,int edfpri)3009ef1f84bSDavid du Colombier edfrun(Proc *p, int edfpri)
3019ef1f84bSDavid du Colombier {
3029ef1f84bSDavid du Colombier 	Edf *e;
303*d46407a3SDavid du Colombier 	void (*pt)(Proc*, int, vlong, vlong);
3049ef1f84bSDavid du Colombier 	long tns;
3059ef1f84bSDavid du Colombier 
3069ef1f84bSDavid du Colombier 	e = p->edf;
3079ef1f84bSDavid du Colombier 	/* Called with edflock held */
3089ef1f84bSDavid du Colombier 	if(edfpri){
3099ef1f84bSDavid du Colombier 		tns = e->d - now;
3109ef1f84bSDavid du Colombier 		if(tns <= 0 || e->S == 0){
3119ef1f84bSDavid du Colombier 			/* Deadline reached or resources exhausted,
3129ef1f84bSDavid du Colombier 			 * deschedule forthwith
3139ef1f84bSDavid du Colombier 			 */
3149ef1f84bSDavid du Colombier 			p->delaysched++;
3159ef1f84bSDavid du Colombier 			delayedscheds++;
3169ef1f84bSDavid du Colombier 			e->s = now;
3179ef1f84bSDavid du Colombier 			return;
3189ef1f84bSDavid du Colombier 		}
3199ef1f84bSDavid du Colombier 		if(e->S < tns)
3209ef1f84bSDavid du Colombier 			tns = e->S;
3219ef1f84bSDavid du Colombier 		if(tns < 20)
3229ef1f84bSDavid du Colombier 			tns = 20;
3239ef1f84bSDavid du Colombier 		e->tns = 1000LL * tns;	/* µs to ns */
3249ef1f84bSDavid du Colombier 		if(e->tt == nil || e->tf != deadlineintr){
3259ef1f84bSDavid du Colombier 			DPRINT("%lud edfrun, deadline=%lud\n", now, tns);
3269ef1f84bSDavid du Colombier 		}else{
3279ef1f84bSDavid du Colombier 			DPRINT("v");
3289ef1f84bSDavid du Colombier 		}
3299ef1f84bSDavid du Colombier 		if(p->trace && (pt = proctrace))
330*d46407a3SDavid du Colombier 			pt(p, SInte, todget(nil) + e->tns, 0);
3319ef1f84bSDavid du Colombier 		e->tmode = Trelative;
3329ef1f84bSDavid du Colombier 		e->tf = deadlineintr;
3339ef1f84bSDavid du Colombier 		e->ta = p;
3349ef1f84bSDavid du Colombier 		timeradd(e);
3359ef1f84bSDavid du Colombier 	}else{
3369ef1f84bSDavid du Colombier 		DPRINT("<");
3379ef1f84bSDavid du Colombier 	}
3389ef1f84bSDavid du Colombier 	e->s = now;
3399ef1f84bSDavid du Colombier }
3409ef1f84bSDavid du Colombier 
3419ef1f84bSDavid du Colombier char *
edfadmit(Proc * p)3429ef1f84bSDavid du Colombier edfadmit(Proc *p)
3439ef1f84bSDavid du Colombier {
3449ef1f84bSDavid du Colombier 	char *err;
3459ef1f84bSDavid du Colombier 	Edf *e;
3469ef1f84bSDavid du Colombier 	int i;
3479ef1f84bSDavid du Colombier 	Proc *r;
348*d46407a3SDavid du Colombier 	void (*pt)(Proc*, int, vlong, vlong);
3499ef1f84bSDavid du Colombier 	long tns;
3509ef1f84bSDavid du Colombier 
3519ef1f84bSDavid du Colombier 	e = p->edf;
3529ef1f84bSDavid du Colombier 	if (e->flags & Admitted)
3539ef1f84bSDavid du Colombier 		return "task state";	/* should never happen */
3549ef1f84bSDavid du Colombier 
3559ef1f84bSDavid du Colombier 	/* simple sanity checks */
3569ef1f84bSDavid du Colombier 	if (e->T == 0)
3579ef1f84bSDavid du Colombier 		return "T not set";
3589ef1f84bSDavid du Colombier 	if (e->C == 0)
3599ef1f84bSDavid du Colombier 		return "C not set";
3609ef1f84bSDavid du Colombier 	if (e->D > e->T)
3619ef1f84bSDavid du Colombier 		return "D > T";
3629ef1f84bSDavid du Colombier 	if (e->D == 0)	/* if D is not set, set it to T */
3639ef1f84bSDavid du Colombier 		e->D = e->T;
3649ef1f84bSDavid du Colombier 	if (e->C > e->D)
3659ef1f84bSDavid du Colombier 		return "C > D";
3669ef1f84bSDavid du Colombier 
3679ef1f84bSDavid du Colombier 	qlock(&edfschedlock);
3689ef1f84bSDavid du Colombier 	if (err = testschedulability(p)){
3699ef1f84bSDavid du Colombier 		qunlock(&edfschedlock);
3709ef1f84bSDavid du Colombier 		return err;
3719ef1f84bSDavid du Colombier 	}
3729ef1f84bSDavid du Colombier 	e->flags |= Admitted;
3739ef1f84bSDavid du Colombier 
3749ef1f84bSDavid du Colombier 	edflock(p);
3759ef1f84bSDavid du Colombier 
3769ef1f84bSDavid du Colombier 	if(p->trace && (pt = proctrace))
377*d46407a3SDavid du Colombier 		pt(p, SAdmit, 0, 0);
3789ef1f84bSDavid du Colombier 
3799ef1f84bSDavid du Colombier 	/* Look for another proc with the same period to synchronize to */
3809ef1f84bSDavid du Colombier 	for(i=0; (r = psincref(i)) != nil; i++) {
3819ef1f84bSDavid du Colombier 		if(r->state == Dead || r == p){
3829ef1f84bSDavid du Colombier 			psdecref(r);
3839ef1f84bSDavid du Colombier 			continue;
3849ef1f84bSDavid du Colombier 		}
3859ef1f84bSDavid du Colombier 		if (r->edf == nil || (r->edf->flags & Admitted) == 0){
3869ef1f84bSDavid du Colombier 			psdecref(r);
3879ef1f84bSDavid du Colombier 			continue;
3889ef1f84bSDavid du Colombier 		}
3899ef1f84bSDavid du Colombier 		if (r->edf->T == e->T)
3909ef1f84bSDavid du Colombier 			break;
3919ef1f84bSDavid du Colombier 	}
3929ef1f84bSDavid du Colombier 	if (r == nil){
3939ef1f84bSDavid du Colombier 		/* Can't synchronize to another proc, release now */
3949ef1f84bSDavid du Colombier 		e->t = now;
3959ef1f84bSDavid du Colombier 		e->d = 0;
3969ef1f84bSDavid du Colombier 		release(p);
3979ef1f84bSDavid du Colombier 		if (p == up){
3989ef1f84bSDavid du Colombier 			DPRINT("%lud edfadmit self %d[%s], release now: r=%lud d=%lud t=%lud\n",
3999ef1f84bSDavid du Colombier 				now, p->pid, statename[p->state], e->r, e->d, e->t);
4009ef1f84bSDavid du Colombier 			/* We're already running */
4019ef1f84bSDavid du Colombier 			edfrun(p, 1);
4029ef1f84bSDavid du Colombier 		}else{
4039ef1f84bSDavid du Colombier 			/* We're releasing another proc */
4049ef1f84bSDavid du Colombier 			DPRINT("%lud edfadmit other %d[%s], release now: r=%lud d=%lud t=%lud\n",
4059ef1f84bSDavid du Colombier 				now, p->pid, statename[p->state], e->r, e->d, e->t);
4069ef1f84bSDavid du Colombier 			p->ta = p;
4079ef1f84bSDavid du Colombier 			edfunlock();
4089ef1f84bSDavid du Colombier 			qunlock(&edfschedlock);
4099ef1f84bSDavid du Colombier 			releaseintr(nil, p);
4109ef1f84bSDavid du Colombier 			return nil;
4119ef1f84bSDavid du Colombier 		}
4129ef1f84bSDavid du Colombier 	}else{
4139ef1f84bSDavid du Colombier 		/* Release in synch to something else */
4149ef1f84bSDavid du Colombier 		e->t = r->edf->t;
4159ef1f84bSDavid du Colombier 		psdecref(r);
4169ef1f84bSDavid du Colombier 		if (p == up){
4179ef1f84bSDavid du Colombier 			DPRINT("%lud edfadmit self %d[%s], release at %lud\n",
4189ef1f84bSDavid du Colombier 				now, p->pid, statename[p->state], e->t);
4199ef1f84bSDavid du Colombier 		}else{
4209ef1f84bSDavid du Colombier 			DPRINT("%lud edfadmit other %d[%s], release at %lud\n",
4219ef1f84bSDavid du Colombier 				now, p->pid, statename[p->state], e->t);
4229ef1f84bSDavid du Colombier 			if(e->tt == nil){
4239ef1f84bSDavid du Colombier 				e->tf = releaseintr;
4249ef1f84bSDavid du Colombier 				e->ta = p;
4259ef1f84bSDavid du Colombier 				tns = e->t - now;
4269ef1f84bSDavid du Colombier 				if(tns < 20)
4279ef1f84bSDavid du Colombier 					tns = 20;
4289ef1f84bSDavid du Colombier 				e->tns = 1000LL * tns;
4299ef1f84bSDavid du Colombier 				e->tmode = Trelative;
4309ef1f84bSDavid du Colombier 				timeradd(e);
4319ef1f84bSDavid du Colombier 			}
4329ef1f84bSDavid du Colombier 		}
4339ef1f84bSDavid du Colombier 	}
4349ef1f84bSDavid du Colombier 	edfunlock();
4359ef1f84bSDavid du Colombier 	qunlock(&edfschedlock);
4369ef1f84bSDavid du Colombier 	return nil;
4379ef1f84bSDavid du Colombier }
4389ef1f84bSDavid du Colombier 
4399ef1f84bSDavid du Colombier void
edfstop(Proc * p)4409ef1f84bSDavid du Colombier edfstop(Proc *p)
4419ef1f84bSDavid du Colombier {
4429ef1f84bSDavid du Colombier 	Edf *e;
443*d46407a3SDavid du Colombier 	void (*pt)(Proc*, int, vlong, vlong);
4449ef1f84bSDavid du Colombier 
4459ef1f84bSDavid du Colombier 	if(e = edflock(p)){
4469ef1f84bSDavid du Colombier 		DPRINT("%lud edfstop %d[%s]\n", now, p->pid, statename[p->state]);
4479ef1f84bSDavid du Colombier 		if(p->trace && (pt = proctrace))
448*d46407a3SDavid du Colombier 			pt(p, SExpel, 0, 0);
4499ef1f84bSDavid du Colombier 		e->flags &= ~Admitted;
4509ef1f84bSDavid du Colombier 		if(e->tt)
4519ef1f84bSDavid du Colombier 			timerdel(e);
4529ef1f84bSDavid du Colombier 		edfunlock();
4539ef1f84bSDavid du Colombier 	}
4549ef1f84bSDavid du Colombier }
4559ef1f84bSDavid du Colombier 
4569ef1f84bSDavid du Colombier static int
yfn(void *)4579ef1f84bSDavid du Colombier yfn(void *)
4589ef1f84bSDavid du Colombier {
4599ef1f84bSDavid du Colombier 	now = µs();
4609ef1f84bSDavid du Colombier 	return up->trend == nil || now - up->edf->r >= 0;
4619ef1f84bSDavid du Colombier }
4629ef1f84bSDavid du Colombier 
4639ef1f84bSDavid du Colombier void
edfyield(void)4649ef1f84bSDavid du Colombier edfyield(void)
4659ef1f84bSDavid du Colombier {
4669ef1f84bSDavid du Colombier 	/* sleep until next release */
4679ef1f84bSDavid du Colombier 	Edf *e;
468*d46407a3SDavid du Colombier 	void (*pt)(Proc*, int, vlong, vlong);
4699ef1f84bSDavid du Colombier 	long n;
4709ef1f84bSDavid du Colombier 
4719ef1f84bSDavid du Colombier 	if((e = edflock(up)) == nil)
4729ef1f84bSDavid du Colombier 		return;
4739ef1f84bSDavid du Colombier 	if(up->trace && (pt = proctrace))
474*d46407a3SDavid du Colombier 		pt(up, SYield, 0, 0);
4759ef1f84bSDavid du Colombier 	if((n = now - e->t) > 0){
4769ef1f84bSDavid du Colombier 		if(n < e->T)
4779ef1f84bSDavid du Colombier 			e->t += e->T;
4789ef1f84bSDavid du Colombier 		else
4799ef1f84bSDavid du Colombier 			e->t = now + e->T - (n % e->T);
4809ef1f84bSDavid du Colombier 	}
4819ef1f84bSDavid du Colombier 	e->r = e->t;
4829ef1f84bSDavid du Colombier 	e->flags |= Yield;
4839ef1f84bSDavid du Colombier 	e->d = now;
4849ef1f84bSDavid du Colombier 	if (up->tt == nil){
4859ef1f84bSDavid du Colombier 		n = e->t - now;
4869ef1f84bSDavid du Colombier 		if(n < 20)
4879ef1f84bSDavid du Colombier 			n = 20;
4889ef1f84bSDavid du Colombier 		up->tns = 1000LL * n;
4899ef1f84bSDavid du Colombier 		up->tf = releaseintr;
4909ef1f84bSDavid du Colombier 		up->tmode = Trelative;
4919ef1f84bSDavid du Colombier 		up->ta = up;
4929ef1f84bSDavid du Colombier 		up->trend = &up->sleep;
4939ef1f84bSDavid du Colombier 		timeradd(up);
4949ef1f84bSDavid du Colombier 	}else if(up->tf != releaseintr)
4959ef1f84bSDavid du Colombier 		print("edfyield: surprise! %#p\n", up->tf);
4969ef1f84bSDavid du Colombier 	edfunlock();
4979ef1f84bSDavid du Colombier 	sleep(&up->sleep, yfn, nil);
4989ef1f84bSDavid du Colombier }
4999ef1f84bSDavid du Colombier 
5009ef1f84bSDavid du Colombier int
edfready(Proc * p)5019ef1f84bSDavid du Colombier edfready(Proc *p)
5029ef1f84bSDavid du Colombier {
5039ef1f84bSDavid du Colombier 	Edf *e;
5049ef1f84bSDavid du Colombier 	Schedq *rq;
5059ef1f84bSDavid du Colombier 	Proc *l, *pp;
506*d46407a3SDavid du Colombier 	void (*pt)(Proc*, int, vlong, vlong);
5079ef1f84bSDavid du Colombier 	long n;
5089ef1f84bSDavid du Colombier 
5099ef1f84bSDavid du Colombier 	if((e = edflock(p)) == nil)
5109ef1f84bSDavid du Colombier 		return 0;
5119ef1f84bSDavid du Colombier 
5129ef1f84bSDavid du Colombier 	if(p->state == Wakeme && p->r){
5139ef1f84bSDavid du Colombier 		iprint("edfready: wakeme\n");
5149ef1f84bSDavid du Colombier 	}
5159ef1f84bSDavid du Colombier 	if(e->d - now <= 0){
5169ef1f84bSDavid du Colombier 		/* past deadline, arrange for next release */
5179ef1f84bSDavid du Colombier 		if((e->flags & Sporadic) == 0){
5189ef1f84bSDavid du Colombier 			/*
5199ef1f84bSDavid du Colombier 			 * Non sporadic processes stay true to their period;
5209ef1f84bSDavid du Colombier 			 * calculate next release time.
5219ef1f84bSDavid du Colombier 			 */
5229ef1f84bSDavid du Colombier 			if((n = now - e->t) > 0){
5239ef1f84bSDavid du Colombier 				if(n < e->T)
5249ef1f84bSDavid du Colombier 					e->t += e->T;
5259ef1f84bSDavid du Colombier 				else
5269ef1f84bSDavid du Colombier 					e->t = now + e->T - (n % e->T);
5279ef1f84bSDavid du Colombier 			}
5289ef1f84bSDavid du Colombier 		}
5299ef1f84bSDavid du Colombier 		if(now - e->t < 0){
5309ef1f84bSDavid du Colombier 			/* Next release is in the future, schedule it */
5319ef1f84bSDavid du Colombier 			if(e->tt == nil || e->tf != releaseintr){
5329ef1f84bSDavid du Colombier 				n = e->t - now;
5339ef1f84bSDavid du Colombier 				if(n < 20)
5349ef1f84bSDavid du Colombier 					n = 20;
5359ef1f84bSDavid du Colombier 				e->tns = 1000LL * n;
5369ef1f84bSDavid du Colombier 				e->tmode = Trelative;
5379ef1f84bSDavid du Colombier 				e->tf = releaseintr;
5389ef1f84bSDavid du Colombier 				e->ta = p;
5399ef1f84bSDavid du Colombier 				timeradd(e);
5409ef1f84bSDavid du Colombier 				DPRINT("%lud edfready %d[%s], release=%lud\n",
5419ef1f84bSDavid du Colombier 					now, p->pid, statename[p->state], e->t);
5429ef1f84bSDavid du Colombier 			}
5439ef1f84bSDavid du Colombier 			if(p->state == Running && (e->flags & (Yield|Yieldonblock)) == 0 && (e->flags & Extratime)){
5449ef1f84bSDavid du Colombier 				/* If we were running, we've overrun our CPU allocation
5459ef1f84bSDavid du Colombier 				 * or missed the deadline, continue running best-effort at low priority
5469ef1f84bSDavid du Colombier 				 * Otherwise we were blocked.  If we don't yield on block, we continue
5479ef1f84bSDavid du Colombier 				 * best effort
5489ef1f84bSDavid du Colombier 				 */
5499ef1f84bSDavid du Colombier 				DPRINT(">");
5509ef1f84bSDavid du Colombier 				p->basepri = PriExtra;
5519ef1f84bSDavid du Colombier 				p->fixedpri = 1;
5529ef1f84bSDavid du Colombier 				edfunlock();
5539ef1f84bSDavid du Colombier 				return 0;	/* Stick on runq[PriExtra] */
5549ef1f84bSDavid du Colombier 			}
5559ef1f84bSDavid du Colombier 			DPRINT("%lud edfready %d[%s] wait release at %lud\n",
5569ef1f84bSDavid du Colombier 				now, p->pid, statename[p->state], e->t);
5579ef1f84bSDavid du Colombier 			p->state = Waitrelease;
5589ef1f84bSDavid du Colombier 			edfunlock();
5599ef1f84bSDavid du Colombier 			return 1;	/* Make runnable later */
5609ef1f84bSDavid du Colombier 		}
5619ef1f84bSDavid du Colombier 		DPRINT("%lud edfready %d %s release now\n", now, p->pid, statename[p->state]);
5629ef1f84bSDavid du Colombier 		/* release now */
5639ef1f84bSDavid du Colombier 		release(p);
5649ef1f84bSDavid du Colombier 	}
5659ef1f84bSDavid du Colombier 	edfunlock();
5669ef1f84bSDavid du Colombier 	DPRINT("^");
5679ef1f84bSDavid du Colombier 	rq = &runq[PriEdf];
5689ef1f84bSDavid du Colombier 	/* insert in queue in earliest deadline order */
5699ef1f84bSDavid du Colombier 	lock(runq);
5709ef1f84bSDavid du Colombier 	l = nil;
5719ef1f84bSDavid du Colombier 	for(pp = rq->head; pp; pp = pp->rnext){
5729ef1f84bSDavid du Colombier 		if(pp->edf->d > e->d)
5739ef1f84bSDavid du Colombier 			break;
5749ef1f84bSDavid du Colombier 		l = pp;
5759ef1f84bSDavid du Colombier 	}
5769ef1f84bSDavid du Colombier 	p->rnext = pp;
5779ef1f84bSDavid du Colombier 	if (l == nil)
5789ef1f84bSDavid du Colombier 		rq->head = p;
5799ef1f84bSDavid du Colombier 	else
5809ef1f84bSDavid du Colombier 		l->rnext = p;
5819ef1f84bSDavid du Colombier 	if(pp == nil)
5829ef1f84bSDavid du Colombier 		rq->tail = p;
5839ef1f84bSDavid du Colombier 	rq->n++;
5849ef1f84bSDavid du Colombier 	nrdy++;
5859ef1f84bSDavid du Colombier 	runvec |= 1 << PriEdf;
5869ef1f84bSDavid du Colombier 	p->priority = PriEdf;
5879ef1f84bSDavid du Colombier 	p->readytime = m->ticks;
5889ef1f84bSDavid du Colombier 	p->state = Ready;
5899ef1f84bSDavid du Colombier 	unlock(runq);
5909ef1f84bSDavid du Colombier 	if(p->trace && (pt = proctrace))
591*d46407a3SDavid du Colombier 		pt(p, SReady, 0, 0);
5929ef1f84bSDavid du Colombier 	return 1;
5939ef1f84bSDavid du Colombier }
5949ef1f84bSDavid du Colombier 
5959ef1f84bSDavid du Colombier 
5969ef1f84bSDavid du Colombier static void
testenq(Proc * p)5979ef1f84bSDavid du Colombier testenq(Proc *p)
5989ef1f84bSDavid du Colombier {
5999ef1f84bSDavid du Colombier 	Proc *xp, **xpp;
6009ef1f84bSDavid du Colombier 	Edf *e;
6019ef1f84bSDavid du Colombier 
6029ef1f84bSDavid du Colombier 	e = p->edf;
6039ef1f84bSDavid du Colombier 	e->testnext = nil;
6049ef1f84bSDavid du Colombier 	if (qschedulability == nil) {
6059ef1f84bSDavid du Colombier 		qschedulability = p;
6069ef1f84bSDavid du Colombier 		return;
6079ef1f84bSDavid du Colombier 	}
6089ef1f84bSDavid du Colombier 	SET(xp);
6099ef1f84bSDavid du Colombier 	for (xpp = &qschedulability; *xpp; xpp = &xp->edf->testnext) {
6109ef1f84bSDavid du Colombier 		xp = *xpp;
6119ef1f84bSDavid du Colombier 		if (e->testtime - xp->edf->testtime < 0
6129ef1f84bSDavid du Colombier 		|| (e->testtime == xp->edf->testtime && e->testtype < xp->edf->testtype)){
6139ef1f84bSDavid du Colombier 			e->testnext = xp;
6149ef1f84bSDavid du Colombier 			*xpp = p;
6159ef1f84bSDavid du Colombier 			return;
6169ef1f84bSDavid du Colombier 		}
6179ef1f84bSDavid du Colombier 	}
6189ef1f84bSDavid du Colombier 	assert(xp->edf->testnext == nil);
6199ef1f84bSDavid du Colombier 	xp->edf->testnext = p;
6209ef1f84bSDavid du Colombier }
6219ef1f84bSDavid du Colombier 
6229ef1f84bSDavid du Colombier static char *
testschedulability(Proc * theproc)6239ef1f84bSDavid du Colombier testschedulability(Proc *theproc)
6249ef1f84bSDavid du Colombier {
6259ef1f84bSDavid du Colombier 	Proc *p;
6269ef1f84bSDavid du Colombier 	long H, G, Cb, ticks;
6279ef1f84bSDavid du Colombier 	int steps, i;
6289ef1f84bSDavid du Colombier 
6299ef1f84bSDavid du Colombier 	/* initialize */
6309ef1f84bSDavid du Colombier 	DPRINT("schedulability test %d\n", theproc->pid);
6319ef1f84bSDavid du Colombier 	qschedulability = nil;
6329ef1f84bSDavid du Colombier 	for(i=0; (p = psincref(i)) != nil; i++) {
6339ef1f84bSDavid du Colombier 		if(p->state == Dead){
6349ef1f84bSDavid du Colombier 			psdecref(p);
6359ef1f84bSDavid du Colombier 			continue;
6369ef1f84bSDavid du Colombier 		}
6379ef1f84bSDavid du Colombier 		if ((p->edf == nil || (p->edf->flags & Admitted) == 0) && p != theproc){
6389ef1f84bSDavid du Colombier 			psdecref(p);
6399ef1f84bSDavid du Colombier 			continue;
6409ef1f84bSDavid du Colombier 		}
6419ef1f84bSDavid du Colombier 		p->edf->testtype = Rl;
6429ef1f84bSDavid du Colombier 		p->edf->testtime = 0;
6439ef1f84bSDavid du Colombier 		DPRINT("\tInit: edfenqueue %d\n", p->pid);
6449ef1f84bSDavid du Colombier 		testenq(p);
6459ef1f84bSDavid du Colombier 		psdecref(p);
6469ef1f84bSDavid du Colombier 	}
6479ef1f84bSDavid du Colombier 	H=0;
6489ef1f84bSDavid du Colombier 	G=0;
6499ef1f84bSDavid du Colombier 	for(steps = 0; steps < Maxsteps; steps++){
6509ef1f84bSDavid du Colombier 		p = qschedulability;
6519ef1f84bSDavid du Colombier 		qschedulability = p->edf->testnext;
6529ef1f84bSDavid du Colombier 		ticks = p->edf->testtime;
6539ef1f84bSDavid du Colombier 		switch (p->edf->testtype){
6549ef1f84bSDavid du Colombier 		case Dl:
6559ef1f84bSDavid du Colombier 			H += p->edf->C;
6569ef1f84bSDavid du Colombier 			Cb = 0;
6579ef1f84bSDavid du Colombier 			DPRINT("\tStep %3d, Ticks %lud, pid %d, deadline, H += %lud → %lud, Cb = %lud\n",
6589ef1f84bSDavid du Colombier 				steps, ticks, p->pid, p->edf->C, H, Cb);
6599ef1f84bSDavid du Colombier 			if (H+Cb>ticks){
6609ef1f84bSDavid du Colombier 				DPRINT("not schedulable\n");
6619ef1f84bSDavid du Colombier 				return "not schedulable";
6629ef1f84bSDavid du Colombier 			}
6639ef1f84bSDavid du Colombier 			p->edf->testtime += p->edf->T - p->edf->D;
6649ef1f84bSDavid du Colombier 			p->edf->testtype = Rl;
6659ef1f84bSDavid du Colombier 			testenq(p);
6669ef1f84bSDavid du Colombier 			break;
6679ef1f84bSDavid du Colombier 		case Rl:
6689ef1f84bSDavid du Colombier 			DPRINT("\tStep %3d, Ticks %lud, pid %d, release, G  %lud, C%lud\n",
6699ef1f84bSDavid du Colombier 				steps, ticks, p->pid, p->edf->C, G);
6709ef1f84bSDavid du Colombier 			if(ticks && G <= ticks){
6719ef1f84bSDavid du Colombier 				DPRINT("schedulable\n");
6729ef1f84bSDavid du Colombier 				return nil;
6739ef1f84bSDavid du Colombier 			}
6749ef1f84bSDavid du Colombier 			G += p->edf->C;
6759ef1f84bSDavid du Colombier 			p->edf->testtime += p->edf->D;
6769ef1f84bSDavid du Colombier 			p->edf->testtype = Dl;
6779ef1f84bSDavid du Colombier 			testenq(p);
6789ef1f84bSDavid du Colombier 			break;
6799ef1f84bSDavid du Colombier 		default:
6809ef1f84bSDavid du Colombier 			assert(0);
6819ef1f84bSDavid du Colombier 		}
6829ef1f84bSDavid du Colombier 	}
6839ef1f84bSDavid du Colombier 	DPRINT("probably not schedulable\n");
6849ef1f84bSDavid du Colombier 	return "probably not schedulable";
6859ef1f84bSDavid du Colombier }
686