ref: 04b419c602ed75deaed50b89fa164660fd679e8f
parent: 5567d0285c57c557345a8253748fb2d262d7261b
author: 9ferno <[email protected]>
date: Sat Jan 22 19:52:22 EST 2022
more changes from 9front
--- a/os/pc/devarch.c
+++ b/os/pc/devarch.c
@@ -923,7 +923,7 @@
return (*arch->fastclock)(hz);
}
-u64
+u32
µs(void)
{
return fastticks2us((*arch->fastclock)(nil));
--- a/os/pc/irq.c
+++ b/os/pc/irq.c
@@ -71,7 +71,7 @@
if(up->delaysched)
sched();
} else {
- preemption(0);
+ preempted();
}
}
return 1;
--- a/os/pc/sdiahci.c
+++ b/os/pc/sdiahci.c
@@ -1294,7 +1294,7 @@
i = led2[8*p->led + seq%8];
if(i != p->ledbits){
p->ledbits = i;
- ledprint("ledstate %,.011ub %ud\n", p->ledbits, seq);
+ ledprint("ledstate %,.011ud %ud\n", p->ledbits, seq);
return 1;
}
return 0;
--- a/os/pc64/bindings.s
+++ b/os/pc64/bindings.s
@@ -164,7 +164,7 @@
PUSH(TOP) /* ( -- n a fd n n ) */
MOVQ CX, TOP /* ( n a fd -- n a fd n a ) */
- CALL validatebuffer(SB)
+ CALL validatebuffer(SB) /* ( n a fd n a -- n a fd ) */
MOVQ UP, 24(SP)
F_TO_C_3
--- a/os/pc64/l.s
+++ b/os/pc64/l.s
@@ -972,7 +972,7 @@
* The size of each entry in the vector table (6 bytes) is known in trapinit0().
* Volume 3A 6-14
* Stack is (high -> low)
- * SS, RSP, RFLAGS, CS, RIP, Error Code (if any), Vectortable(SB) return PC
+ * SS, RSP, RFLAGS, CS, RIP, Error Code (if any), vectortable(SB) return PC
* (SP) = Vectortable(SB) return PC. The first byte of this return PC will be the
* byte we used to identify the trap type.
* Removed the nested check that 9front does as we are not using different code
@@ -980,15 +980,21 @@
*/
TEXT _strayintr(SB), 1, $-4 /* no error code pushed */
- PUSHQ AX /* save AX, some junk to fill the ecode slot in the stack */
- MOVQ 8(SP), AX /* vectortable(SB) PC */
+ PUSHQ AX /* some value to fill the ecode slot in the stack and also store AX value */
+ MOVQ 8(SP), AX /* vectortable(SB) PC */
+ /* Stack is SS, RSP, RFLAGS, CS, RIP, vectortable(SB) Return PC, AX
+ AX = vectortable(SB) return PC */
JMP _intrcommon
TEXT _strayintrx(SB), 1, $-4/* error code pushed */
- XCHGQ AX, (SP) /* exchange AX with pointer to trap type */
+ XCHGQ AX, (SP) /* exchange AX with pointer to trap type */
+ /* Stack is SS, RSP, RFLAGS, CS, RIP, Error Code, AX
+ AX = vectortable(SB) return PC */
_intrcommon:
- MOVBQZX (AX), AX /* extract trap type from the vectortable(SB) return PC -> AX */
- XCHGQ AX, (SP) /* exchange vectortable(SB) return PC with the trap type in AX */
+ MOVBQZX (AX), AX /* extract trap type from the vectortable(SB) return PC -> AX */
+ XCHGQ AX, (SP) /* exchange vectortable(SB) return PC with the trap type in AX */
+ /* Stack is SS, RSP, RFLAGS, CS, RIP, Error Code or vectortable(SB) Return PC, trap type
+ AX = AX */
PUSHW GS
PUSHW FS
--- a/os/pc64/trap.c
+++ b/os/pc64/trap.c
@@ -200,7 +200,7 @@
if(ctl->isintr){
if(up && ctl->irq != IrqTIMER && ctl->irq != IrqCLOCK)
- preemption(0);
+ preempted();
}
}
else if(vno <= nelem(excname) && up->type == Interp){
@@ -528,9 +528,7 @@
p->kparg = arg;
}
-
-
-ulong
+uintptr
dbgpc(Proc *p)
{
Ureg *ureg;
--- a/os/port/edf.c
+++ b/os/port/edf.c
@@ -1,5 +1,5 @@
/* EDF scheduling */
-#include "u.h"
+#include <u.h>
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
@@ -9,10 +9,13 @@
#include <trace.h>
/* debugging */
-int edfprint = 0;
-#define DPRINT if(edfprint)print
+enum {
+ Dontprint = 1,
+};
-static vlong now;
+#define DPRINT if(Dontprint){}else print
+
+static long now; /* Low order 32 bits of time in µs */
extern ulong delayedscheds;
extern Schedq runq[Nrq];
extern int nrdy;
@@ -21,7 +24,6 @@
/* Statistics stuff */
ulong nilcount;
ulong scheds;
-vlong edfruntime;
ulong edfnrun;
int misseddeadlines;
@@ -39,11 +41,10 @@
static Proc *qschedulability;
enum {
- Onemicrosecond = 1000ULL,
- Onemillisecond = 1000000ULL,
- Onesecond = 1000000000ULL,
- OneRound = Onemillisecond/2LL,
- MilliRound = Onemicrosecond/2LL,
+ Onemicrosecond = 1,
+ Onemillisecond = 1000,
+ Onesecond = 1000000,
+ OneRound = Onemillisecond/2,
};
static int
@@ -57,8 +58,8 @@
case 'U':
t = va_arg(f->args, uvlong);
break;
- case 't': // vlong in nanoseconds
- t = va_arg(f->args, vlong);
+ case 't': /* vlong in nanoseconds */
+ t = va_arg(f->args, long);
break;
default:
return fmtstrcpy(f, "(timeconv)");
@@ -71,17 +72,18 @@
sign = "";
if (t > Onesecond){
t += OneRound;
- sprint(buf, "%s%d.%.3ds", sign, (int)(t / Onesecond), (int)(t % Onesecond)/1000000);
- }else if (t > Onemillisecond){
- t += MilliRound;
- sprint(buf, "%s%d.%.3dms", sign, (int)(t / Onemillisecond), (int)(t % Onemillisecond)/1000);
- }else if (t > Onemicrosecond)
- sprint(buf, "%s%d.%.3dµs", sign, (int)(t / Onemicrosecond), (int)(t % Onemicrosecond));
+ sprint(buf, "%s%d.%.3ds", sign, (int)(t / Onesecond),
+ (int)(t % Onesecond)/Onemillisecond);
+ }else if (t > Onemillisecond)
+ sprint(buf, "%s%d.%.3dms", sign, (int)(t / Onemillisecond),
+ (int)(t % Onemillisecond));
else
- sprint(buf, "%s%dns", sign, (int)t);
+ sprint(buf, "%s%dµs", sign, (int)t);
return fmtstrcpy(f, buf);
}
+long edfcycles;
+
Edf*
edflock(Proc *p)
{
@@ -90,8 +92,12 @@
if (p->edf == nil)
return nil;
ilock(&thelock);
- if ((e = p->edf) && (e->flags & Admitted)){
- now = todget(nil);
+ if((e = p->edf) && (e->flags & Admitted)){
+ thelock.pc = getcallerpc(&p);
+#ifdef EDFCYCLES
+ edfcycles -= lcycles();
+#endif
+ now = µs();
return e;
}
iunlock(&thelock);
@@ -101,7 +107,10 @@
void
edfunlock(void)
{
- edfruntime += todget(nil) - now;
+
+#ifdef EDFCYCLES
+ edfcycles += lcycles();
+#endif
edfnrun++;
iunlock(&thelock);
}
@@ -113,8 +122,8 @@
fmtinstall('t', timeconv);
edfinited++;
}
- now = todget(nil);
- DPRINT("%t edfinit %lud[%s]\n", now, p->pid, statename[p->state]);
+ now = µs();
+ DPRINT("%lud edfinit %lud[%s]\n", now, p->pid, statename[p->state]);
p->edf = malloc(sizeof(Edf));
if(p->edf == nil)
error(Enomem);
@@ -133,8 +142,8 @@
return;
p = t->ta;
-
- DPRINT("%t deadlineintr %lud[%s]\n", todget(nil), p->pid, statename[p->state]);
+ now = µs();
+ DPRINT("%lud deadlineintr %lud[%s]\n", now, p->pid, statename[p->state]);
/* If we're interrupting something other than the proc pointed to by t->a,
* we've already achieved recheduling, so we need not do anything
* Otherwise, we must cause a reschedule, but if we call sched()
@@ -142,9 +151,8 @@
* Instead, we cause the resched to happen when the interrupted proc
* returns to user space
*/
- if (p == up){
- pt = proctrace;
- if(up->trace && pt)
+ if(p == up){
+ if(up->trace && (pt = proctrace))
pt(up, SInts, 0);
up->delaysched++;
delayedscheds++;
@@ -157,18 +165,26 @@
/* Called with edflock held */
Edf *e;
void (*pt)(Proc*, int, vlong);
+ long n;
+ vlong nowns;
e = p->edf;
e->flags &= ~Yield;
- if (e->d < now){
+ if(e->d - now < 0){
e->periods++;
e->r = now;
- if ((e->flags & Sporadic) == 0){
- /* Non sporadic processes stay true to their period;
- * calculate next release time
+ if((e->flags & Sporadic) == 0){
+ /*
+ * Non sporadic processes stay true to their period;
+ * calculate next release time.
+ * Second test limits duration of while loop.
*/
- while(e->t <= now)
- e->t += e->T;
+ if((n = now - e->t) > 0){
+ if(n < e->T)
+ e->t += e->T;
+ else
+ e->t = now + e->T - (n % e->T);
+ }
}else{
/* Sporadic processes may not be released earlier than
* one period after this release
@@ -177,20 +193,21 @@
}
e->d = e->r + e->D;
e->S = e->C;
- DPRINT("%t release %lud[%s], r=%t, d=%t, t=%t, S=%t\n",
+ DPRINT("%lud release %lud[%s], r=%lud, d=%lud, t=%lud, S=%lud\n",
now, p->pid, statename[p->state], e->r, e->d, e->t, e->S);
- if (pt = proctrace){
- pt(p, SRelease, e->r);
- pt(p, SDeadline, e->d);
+ if(pt = proctrace){
+ nowns = todget(nil);
+ pt(p, SRelease, nowns);
+ pt(p, SDeadline, nowns + 1000LL*e->D);
}
}else{
- DPRINT("%t release %lud[%s], too late t=%t, called from 0x%lux\n",
+ DPRINT("%lud release %lud[%s], too late t=%lud, called from %#p\n",
now, p->pid, statename[p->state], e->t, getcallerpc(&p));
}
}
static void
-releaseintr(Ureg*, Timer *t)
+releaseintr(Ureg *u, Timer *t)
{
Proc *p;
extern int panicking;
@@ -202,7 +219,7 @@
p = t->ta;
if((edflock(p)) == nil)
return;
- DPRINT("%t releaseintr %lud[%s]\n", now, p->pid, statename[p->state]);
+ DPRINT("%lud releaseintr %lud[%s]\n", now, p->pid, statename[p->state]);
switch(p->state){
default:
edfunlock();
@@ -209,8 +226,8 @@
return;
case Ready:
/* remove proc from current runq */
- rq = &runq[p->pri];
- if (dequeueproc(rq, p) != p){
+ rq = &runq[p->priority];
+ if(dequeueproc(rq, p) != p){
DPRINT("releaseintr: can't find proc or lock race\n");
release(p); /* It'll start best effort */
edfunlock();
@@ -221,8 +238,11 @@
case Waitrelease:
release(p);
edfunlock();
+ if(p->state == Wakeme){
+ iprint("releaseintr: wakeme\n");
+ }
ready(p);
- if (up){
+ if(up){
up->delaysched++;
delayedscheds++;
}
@@ -234,10 +254,8 @@
case Wakeme:
release(p);
edfunlock();
- if (p->trend)
- wakeup(p->trend);
- p->trend = nil;
- if (up){
+ twakeup(u, t);
+ if(up){
up->delaysched++;
delayedscheds++;
}
@@ -249,7 +267,7 @@
void
edfrecord(Proc *p)
{
- vlong used;
+ long used;
Edf *e;
void (*pt)(Proc*, int, vlong);
@@ -256,15 +274,15 @@
if((e = edflock(p)) == nil)
return;
used = now - e->s;
- if (e->d <= now)
+ if(e->d - now <= 0)
e->edfused += used;
else
e->extraused += used;
- if (e->S > 0){
- if (e->S <= used){
+ if(e->S > 0){
+ if(e->S <= used){
if(pt = proctrace)
- pt(p, SSlice, now);
- DPRINT("%t edfrecord slice used up\n", now);
+ pt(p, SSlice, 0);
+ DPRINT("%lud edfrecord slice used up\n", now);
e->d = now;
e->S = 0;
}else
@@ -279,11 +297,13 @@
{
Edf *e;
void (*pt)(Proc*, int, vlong);
+ long tns;
e = p->edf;
/* Called with edflock held */
if(edfpri){
- if (e->d <= now || e->S == 0){
+ tns = e->d - now;
+ if(tns <= 0 || e->S == 0){
/* Deadline reached or resources exhausted,
* deschedule forthwith
*/
@@ -292,17 +312,19 @@
e->s = now;
return;
}
- e->tns = now + e->S;
- if (e->d < e->tns)
- e->tns = e->d;
+ if(e->S < tns)
+ tns = e->S;
+ if(tns < 20)
+ tns = 20;
+ e->tns = 1000LL * tns; /* µs to ns */
if(e->tt == nil || e->tf != deadlineintr){
- DPRINT("%t edfrun, deadline=%t\n", now, e->tns);
+ DPRINT("%lud edfrun, deadline=%lud\n", now, tns);
}else{
DPRINT("v");
}
if(p->trace && (pt = proctrace))
- pt(p, SInte, e->tns);
- e->tmode = Tabsolute;
+ pt(p, SInte, todget(nil) + e->tns);
+ e->tmode = Trelative;
e->tf = deadlineintr;
e->ta = p;
timeradd(e);
@@ -320,6 +342,7 @@
int i;
Proc *r;
void (*pt)(Proc*, int, vlong);
+ long tns;
e = p->edf;
if (e->flags & Admitted)
@@ -346,8 +369,8 @@
edflock(p);
- if(pt = proctrace)
- pt(p, SAdmit, now);
+ if(p->trace && (pt = proctrace))
+ pt(p, SAdmit, 0);
/* Look for another proc with the same period to synchronize to */
SET(r);
@@ -366,13 +389,13 @@
e->d = 0;
release(p);
if (p == up){
- DPRINT("%t edfadmit self %lud[%s], release now: r=%t d=%t t=%t\n",
+ DPRINT("%lud edfadmit self %lud[%s], release now: r=%lud d=%lud t=%lud\n",
now, p->pid, statename[p->state], e->r, e->d, e->t);
/* We're already running */
edfrun(p, 1);
}else{
/* We're releasing another proc */
- DPRINT("%t edfadmit other %lud[%s], release now: r=%t d=%t t=%t\n",
+ DPRINT("%lud edfadmit other %lud[%s], release now: r=%lud d=%lud t=%lud\n",
now, p->pid, statename[p->state], e->r, e->d, e->t);
p->ta = p;
edfunlock();
@@ -384,19 +407,22 @@
/* Release in synch to something else */
e->t = r->edf->t;
if (p == up){
- DPRINT("%t edfadmit self %lud[%s], release at %t\n",
+ DPRINT("%lud edfadmit self %lud[%s], release at %lud\n",
now, p->pid, statename[p->state], e->t);
edfunlock();
qunlock(&edfschedlock);
return nil;
}else{
- DPRINT("%t edfadmit other %lud[%s], release at %t\n",
+ DPRINT("%lud edfadmit other %lud[%s], release at %lud\n",
now, p->pid, statename[p->state], e->t);
if(e->tt == nil){
e->tf = releaseintr;
e->ta = p;
- e->tns = e->t;
- e->tmode = Tabsolute;
+ tns = e->t - now;
+ if(tns < 20)
+ tns = 20;
+ e->tns = 1000LL * tns;
+ e->tmode = Trelative;
timeradd(e);
}
}
@@ -412,13 +438,12 @@
Edf *e;
void (*pt)(Proc*, int, vlong);
- if (e = edflock(p)){
- DPRINT("%t edfstop %lud[%s]\n", now, p->pid, statename[p->state]);
- if(pt = proctrace)
- pt(p, SExpel, now);
+ if(e = edflock(p)){
+ DPRINT("%lud edfstop %lud[%s]\n", now, p->pid, statename[p->state]);
+ if(p->trace && (pt = proctrace))
+ pt(p, SExpel, 0);
e->flags &= ~Admitted;
- if (e->tt)
- timerdel(e);
+ timerdel(e);
edfunlock();
}
}
@@ -426,7 +451,8 @@
static int
yfn(void *)
{
- return up->trend == nil || todget(nil) >= up->edf->r;
+ now = µs();
+ return up->trend == nil || now - up->edf->r >= 0;
}
void
@@ -435,27 +461,38 @@
/* sleep until next release */
Edf *e;
void (*pt)(Proc*, int, vlong);
+ long n;
if((e = edflock(up)) == nil)
return;
- if(pt = proctrace)
- pt(up, SYield, now);
- while(e->t < now)
- e->t += e->T;
+ if(up->trace && (pt = proctrace))
+ pt(up, SYield, 0);
+ if((n = now - e->t) > 0){
+ if(n < e->T)
+ e->t += e->T;
+ else
+ e->t = now + e->T - (n % e->T);
+ }
e->r = e->t;
e->flags |= Yield;
e->d = now;
- if (up->tt == nil){
- up->tns = e->t;
- up->tf = releaseintr;
- up->tmode = Tabsolute;
- up->ta = up;
- up->trend = &up->sleep;
- timeradd(up);
- }else if(up->tf != releaseintr)
- print("edfyield: surprise! 0x%lux\n", up->tf);
+ n = e->t - now;
+ if(n < 20)
+ n = 20;
+ up->tns = 1000LL * n;
+ up->tf = releaseintr;
+ up->tmode = Trelative;
+ up->ta = up;
+ up->trend = &up->sleep;
+ timeradd(up);
edfunlock();
+ if(waserror()){
+ up->trend = nil;
+ timerdel(up);
+ nexterror();
+ }
sleep(&up->sleep, yfn, nil);
+ poperror();
}
int
@@ -465,25 +502,40 @@
Schedq *rq;
Proc *l, *pp;
void (*pt)(Proc*, int, vlong);
+ long n;
if((e = edflock(p)) == nil)
return 0;
- if (e->d <= now){
+
+ if(p->state == Wakeme && p->r){
+ iprint("edfready: wakeme\n");
+ }
+ if(e->d - now <= 0){
/* past deadline, arrange for next release */
- if ((e->flags & Sporadic) == 0){
- /* Non sporadic processes stay true to their period, calculate next release time */
- while(e->t < now)
- e->t += e->T;
- }
- if (now < e->t){
+ if((e->flags & Sporadic) == 0){
+ /*
+ * Non sporadic processes stay true to their period;
+ * calculate next release time.
+ */
+ if((n = now - e->t) > 0){
+ if(n < e->T)
+ e->t += e->T;
+ else
+ e->t = now + e->T - (n % e->T);
+ }
+ }
+ if(now - e->t < 0){
/* Next release is in the future, schedule it */
- if (e->tt == nil || e->tf != releaseintr){
- e->tns = e->t;
- e->tmode = Tabsolute;
+ if(e->tt == nil || e->tf != releaseintr){
+ n = e->t - now;
+ if(n < 20)
+ n = 20;
+ e->tns = 1000LL * n;
+ e->tmode = Trelative;
e->tf = releaseintr;
e->ta = p;
timeradd(e);
- DPRINT("%t edfready %lud[%s], release=%t\n",
+ DPRINT("%lud edfready %lud[%s], release=%lud\n",
now, p->pid, statename[p->state], e->t);
}
if(p->state == Running && (e->flags & (Yield|Yieldonblock)) == 0 && (e->flags & Extratime)){
@@ -493,17 +545,18 @@
* best effort
*/
DPRINT(">");
- p->pri = PriExtra;
+ p->basepri = PriExtra;
+ p->fixedpri = 1;
edfunlock();
return 0; /* Stick on runq[PriExtra] */
}
- DPRINT("%t edfready %lud[%s] wait release at %t\n",
+ DPRINT("%lud edfready %lud[%s] wait release at %lud\n",
now, p->pid, statename[p->state], e->t);
p->state = Waitrelease;
edfunlock();
return 1; /* Make runnable later */
}
- DPRINT("%t edfready %lud %s release now\n", now, p->pid, statename[p->state]);
+ DPRINT("%lud edfready %lud %s release now\n", now, p->pid, statename[p->state]);
/* release now */
release(p);
}
@@ -528,12 +581,12 @@
rq->n++;
nrdy++;
runvec |= 1 << PriEdf;
- p->pri = PriEdf;
+ p->priority = PriEdf;
p->readytime = m->ticks;
p->state = Ready;
unlock(runq);
- if(pt = proctrace)
- pt(p, SReady, now);
+ if(p->trace && (pt = proctrace))
+ pt(p, SReady, 0);
return 1;
}
@@ -553,7 +606,7 @@
SET(xp);
for (xpp = &qschedulability; *xpp; xpp = &xp->edf->testnext) {
xp = *xpp;
- if (e->testtime < xp->edf->testtime
+ if (e->testtime - xp->edf->testtime < 0
|| (e->testtime == xp->edf->testtime && e->testtype < xp->edf->testtype)){
e->testnext = xp;
*xpp = p;
@@ -568,7 +621,7 @@
testschedulability(Proc *theproc)
{
Proc *p;
- vlong H, G, Cb, ticks;
+ long H, G, Cb, ticks;
int steps, i;
/* initialize */
@@ -595,7 +648,7 @@
case Dl:
H += p->edf->C;
Cb = 0;
- DPRINT("\tStep %3d, Ticks %t, pid %lud, deadline, H += %t → %t, Cb = %t\n",
+ DPRINT("\tStep %3d, Ticks %lud, pid %lud, deadline, H += %lud → %lud, Cb = %lud\n",
steps, ticks, p->pid, p->edf->C, H, Cb);
if (H+Cb>ticks){
DPRINT("not schedulable\n");
@@ -606,7 +659,7 @@
testenq(p);
break;
case Rl:
- DPRINT("\tStep %3d, Ticks %t, pid %lud, release, G %t, C%t\n",
+ DPRINT("\tStep %3d, Ticks %lud, pid %lud, release, G %lud, C%lud\n",
steps, ticks, p->pid, p->edf->C, G);
if(ticks && G <= ticks){
DPRINT("schedulable\n");
--- a/os/port/edf.h
+++ b/os/port/edf.h
@@ -16,29 +16,30 @@
typedef struct Edf Edf;
struct Edf {
+ /* All times in µs */
/* time intervals */
- vlong D; /* Deadline */
- vlong Delta; /* Inherited deadline */
- vlong T; /* period */
- vlong C; /* Cost */
- vlong S; /* Slice: time remaining in this period */
- /* times */
- vlong r; /* (this) release time */
- vlong d; /* (this) deadline */
- vlong t; /* Start of next period, t += T at release */
- vlong s; /* Time at which this proc was last scheduled */
+ long D; /* Deadline */
+ long Delta; /* Inherited deadline */
+ long T; /* period */
+ long C; /* Cost */
+ long S; /* Slice: time remaining in this period */
+ /* times (only low-order bits of absolute time) */
+ long r; /* (this) release time */
+ long d; /* (this) deadline */
+ long t; /* Start of next period, t += T at release */
+ long s; /* Time at which this proc was last scheduled */
/* for schedulability testing */
- vlong testDelta;
- int testtype; /* Release or Deadline */
- vlong testtime;
+ long testDelta;
+ int testtype; /* Release or Deadline */
+ long testtime;
Proc *testnext;
/* other */
ushort flags;
Timer;
/* Stats */
- vlong edfused;
- vlong extraused;
- vlong aged;
+ long edfused;
+ long extraused;
+ long aged;
ulong periods;
ulong missed;
};
@@ -45,7 +46,7 @@
extern Lock edftestlock; /* for atomic admitting/expelling */
-#pragma varargck type "t" vlong
+#pragma varargck type "t" long
#pragma varargck type "U" uvlong
/* Interface: */
--- a/os/port/pgrp.c
+++ b/os/port/pgrp.c
@@ -33,6 +33,7 @@
if(mh == nil)
return;
+ USED(s);
dumpchan(" from ", mh->from);
print(" to\n");
m = mh->mount;
--- a/os/port/portdat.h
+++ b/os/port/portdat.h
@@ -508,7 +508,7 @@
enum {
/* Mode */
Trelative, /* timer programmed in ns from now */
- Tabsolute, /* timer programmed in ns since epoch */
+ Tabsolute, /* timer programmed in ns since epoch - not used in 9front */
Tperiodic, /* periodic timer, period in ns */
};
@@ -548,6 +548,7 @@
Proc_exitme,
Proc_traceme,
Proc_exitbig,
+ Proc_tracesyscall,
TUser = 0, /* Proc.time */
TSys,
@@ -556,7 +557,7 @@
TCSys,
TCReal,
- NERR = 30,
+ NERR = 64,
NNOTE = 5,
Unknown = 0,
@@ -565,18 +566,23 @@
Forth,
BusyGC,
- PriLock = 0, /* Holding Spin lock */
- PriEdf, /* active edf processes */
- PriRelease, /* released edf processes */
- PriRealtime, /* Video telephony */
- PriHicodec, /* MPEG codec */
- PriLocodec, /* Audio codec */
- PriHi, /* Important task */
- PriNormal,
- PriLo,
- PriBackground,
- PriExtra, /* edf processes we don't care about */
- Nrq
+ Npriq = 20, /* number of scheduler priority levels */
+ Nrq = Npriq+2, /* number of priority levels including real time */
+ PriRelease = Npriq, /* released edf processes */
+ PriEdf = Npriq+1, /* active edf processes */
+ PriNormal = 10, /* base priority for normal processes */
+ PriExtra = Npriq-1, /* edf processes at high best-effort pri */
+ PriKproc = 13, /* base priority for kernel processes */
+ PriRoot = 13, /* base priority for root processes */
+
+ /* used by inferno, remove them at some point when /prog is removed */
+ PriLock = Npriq, /* Holding Spin lock */
+ PriRealtime = Npriq+1, /* Video telephony */
+ PriHicodec = Npriq, /* MPEG codec */
+ PriLocodec = Npriq, /* Audio codec */
+ PriHi = Npriq, /* Important task */
+ PriLo = 7,
+ PriBackground = 4,
};
struct Schedq
@@ -604,10 +610,6 @@
Proc *rnext; /* next process in run queue */
Proc *qnext; /* next process on queue for a QLock */
- /* check notes in proc.c for how these 2 fields are used */
- void *blockinglock; /* address of QLock or RWLock being queued for, DEBUG */
- /* not in 9front as we can reason that info from qpc */
- uintptr qpc; /* last call that blocked in QLock or RWLock */
char *psstate; /* What /proc/#/status reports */
s32 state;
@@ -634,10 +636,10 @@
/* Fgrp *closingfgrp;*/ /* used during teardown */
-/* int insyscall;*/
+ int insyscall;
u32 time[6]; /* User, Sys, Real; child U, S, R */
- uvlong kentry; /* Kernel entry time stamp (for profiling) */
+ u64 kentry; /* Kernel entry time stamp (for profiling) */
/*
* pcycles: cycles spent in this process (updated on procswitch)
* when this is the current proc and we're in the kernel
@@ -646,7 +648,7 @@
* when this is not the current process or we're in user mode
* (procrestores and procsaves balance), it is pcycles.
*/
- vlong pcycles;
+ s64 pcycles;
QLock debug; /* to access debugging elements of User */
Proc *pdbg; /* the debugging process */
@@ -720,6 +722,7 @@
Edf *edf; /* if non-null, real-time proc, edf contains scheduling params */
int trace; /* process being traced? */
+ uintptr qpc; /* pc calling last blocking qlock */
QLock *eql; /* interruptable eqlock */
void *ureg; /* User registers for notes */
@@ -740,7 +743,7 @@
Osenv *env;
Osenv defenv;
s32 swipend; /* software interrupt pending for Prog TODO replace with notepending? */
- Lock sysio; /* note handler lock */
+ Lock sysio; /* note handler lock */
/* inferno specific fields that are obsolete? */
int fpstate;
@@ -752,8 +755,8 @@
/* forth specific fields */
Proc *fprev, *fnext; /* forth processes linked list */
void *fmem; /* forth process memory - sandboxed except for macro primitives */
- void *shm; /* for devshm */
- void *readyfds; /* for devready.c */
+ void *shm; /* for devshm. move this into Osenv or maybe remove Osenv at some point to get more in sync with 9front */
+ void *canread; /* for devready.c */
u8 fstarted; /* 0 while waiting for the pctl message */
};
@@ -1080,3 +1083,32 @@
uint n;
char buf[16384];
} kmesg;
+
+/*
+ * shared memory data structures. Modeled after Egrp. used by devshm.
+ */
+typedef struct Svalue Svalue;
+struct Svalue
+{
+ Ref;
+ RWlock;
+ char *name;
+ char *value;
+ s32 len;
+ s32 vers;
+ u8 dead; /* set by remove() */
+};
+
+typedef struct Sgrp Sgrp;
+struct Sgrp
+{
+ Ref;
+ RWlock;
+ union{ /* array of Svalue pointers */
+ Svalue **entries;
+ Svalue **ent;
+ };
+ int nent; /* number of entries used */
+ int ment; /* maximum number of entries */
+ u32 vers; /* of Sgrp */
+};
--- a/os/port/portfns.h
+++ b/os/port/portfns.h
@@ -29,6 +29,7 @@
void ccloseq(Chan*);
void closeegrp(Egrp*);
void closefgrp(Fgrp*);
+void closergrp(Rgrp*);
void closemount(Mount*);
void closepgrp(Pgrp*);
void closesigs(Skeyset*);
@@ -51,8 +52,10 @@
void cursoron(void);
void cursoroff(void);
void cwrite(Chan*, uchar*, int, vlong);
+uintptr dbgpc(Proc*);
void debugkey(Rune, char *, void(*)(), int);
s32 decref(Ref*);
+Proc* dequeueproc(Schedq*, Proc*);
Chan* devattach(int, char*);
Block* devbread(Chan*, s32, u32);
s32 devbwrite(Chan*, Block*, u32);
@@ -82,6 +85,13 @@
void dumppgrp(char *s, Pgrp *p);
void dumpstack(void);
Fgrp* dupfgrp(Fgrp*);
+void edfinit(Proc*);
+char* edfadmit(Proc*);
+int edfready(Proc*);
+void edfrecord(Proc*);
+void edfrun(Proc*, int);
+void edfstop(Proc*);
+void edfyield(void);
void egrpcpy(Egrp*, Egrp*);
int emptystr(char*);
int eqchan(Chan*, Chan*, int);
@@ -102,7 +112,6 @@
Chan* fdtochan(Fgrp*, int, int, int, int);
int findmount(Chan**, Mhead**, int, int, Qid);
void forceclosefgrp(void);
-void forthmain(void *);
void free(void*);
void freeb(Block*);
void freeblist(Block*);
@@ -223,10 +232,11 @@
int preempted(void);
int preemption(int);
void printinit(void);
-void procctl(Proc*);
+void procctl(void);
void procdump(void);
int procindex(ulong);
void procinit(void);
+void procpriority(Proc*, int, int);
Proc* proctab(int);
void (*proctrace)(Proc*, int, vlong);
int progfdprint(Chan*, int, int, char*, int);
@@ -295,6 +305,8 @@
int setpri(int);
void setrealloctag(void*, uintptr);
void setupwatchpts(Proc*, Watchpt*, int);
+Sgrp* shmgrpnew(void);
+void shmgrpclose(Sgrp *g);
void showconf(void);
void shownotes(Proc*);
char* skipslash(char*);
@@ -322,6 +334,7 @@
int tready(void*);
Block* trimblock(Block*, int, int);
void tsleep(Rendez*, int (*)(void*), void*, int);
+void twakeup(Ureg*, Timer *);
int uartctl(Uart*, char*);
int uartgetc(void);
void uartkick(void*);
@@ -359,3 +372,5 @@
u64 nhgetv(void*);
u32 nhgetl(void*);
u16 nhgets(void*);
+u32 µs(void);
+long lcycles(void);
--- a/os/port/proc.c
+++ b/os/port/proc.c
@@ -4,26 +4,26 @@
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
+#include "edf.h"
+#include <trace.h>
+#include "tos.h"
#include <interp.h>
+#include "ureg.h"
static int debug = 0;
-/* Ref pidalloc; */
int schedgain = 30; /* units in seconds */
int nrdy;
+void updatecpu(Proc*);
+int reprioritize(Proc*);
+
ulong delayedscheds; /* statistics */
ulong skipscheds;
ulong preempts;
+ulong pload;
s32 prevpid;
-/* bitmap of priorities of procs ready to run. When any process is
- * queued, the bit corresponding to that priority gets set.
- * when there are no more processes to run at a priority, then the
- * corresponding bit gets cleared.
- */
-static u32 occupied;
-
static struct Procalloc
{
Lock;
@@ -31,12 +31,19 @@
Proc* free;
} procalloc;
+enum
+{
+ Q=10,
+ DQ=4,
+ Scaling=2,
+};
+
/* multiple run queues by priority, different from Brian's book
* Per Brian's book, it is the struct Procs == struct Schedq.
* Now, inferno maintains multiple Schedq based on priority.
*/
-static Schedq runq[Nrq];
-int nrdy;
+Schedq runq[Nrq];
+ulong runvec;
char *statename[] =
{ /* BUG: generate automatically */
@@ -55,6 +62,7 @@
"Waitrelease",
};
+static void rebalance(void);
static void pidinit(void);
static void pidfree(Proc*);
@@ -89,9 +97,6 @@
the running process's state and picks the next process to run.
By using m->sched, we are creating a loop from sched() to schedinit()
after every process switch.
-
- inferno does not change the process priorities. So, ignoring updatecpu().
- process priorities are set with setpri()
*/
/*
* Always splhi()'ed.
@@ -99,16 +104,17 @@
void
schedinit(void) /* never returns */
{
+ Edf *e;
+
setlabel(&m->sched);
if(up != nil) {
- if(up->pid == 28 || up->pid == 0)
- print("schedinit up->pid = %d up->state %d\n", up->pid, up->state);
-/*
if((e = up->edf) && (e->flags & Admitted))
edfrecord(up);
-*/
m->proc = nil;
switch(up->state){
+ default:
+ updatecpu(up);
+ break;
case Running:
/*
* process state from Runnning -> Ready
@@ -118,13 +124,14 @@
break;
case Moribund:
up->state = Dead;
-/*
edfstop(up);
- if(up->edf){
+ if(up->edf != nil){
free(up->edf);
up->edf = nil;
}
-*/
+
+ /* mmurelease(up); linear memory on 9front */
+
lock(&procalloc);
up->mach = nil;
up->qnext = procalloc.free;
@@ -145,6 +152,36 @@
sched();
}
+/*
+int
+kenter(Ureg *ureg)
+{
+ int user;
+
+ user = userureg(ureg);
+ if(user){
+ up->dbgreg = ureg;
+ cycles(&up->kentry);
+ }
+ return user;
+}
+
+void
+kexit(Ureg*)
+{
+ uvlong t;
+ Tos *tos;
+
+ cycles(&t);
+
+ \/* precise time accounting, kernel exit *\/
+ tos = (Tos*)(USTKTOP-sizeof(Tos));
+ tos->kcycles += t - up->kentry;
+ tos->pcycles = t + up->pcycles;
+ tos->pid = up->pid;
+}
+*/
+
static void
procswitch(void)
{
@@ -154,6 +191,7 @@
m->cs++;
cycles(&t);
+ up->kentry -= t;
up->pcycles += t;
procsave(up);
@@ -165,6 +203,7 @@
procrestore(up);
cycles(&t);
+ up->kentry += t;
up->pcycles -= t;
}
@@ -202,9 +241,8 @@
if(up->nlocks)
if(up->state != Moribund)
if(up->delaysched < 20
-/* || palloc.Lock.p == up
- || fscache.Lock.p == up
-*/
+ /*|| palloc.Lock.p == up
+ || fscache.Lock.p == up */
|| procalloc.Lock.p == up){
up->delaysched++;
delayedscheds++;
@@ -221,6 +259,13 @@
* first time entering schedinit()
*/
p = runproc();
+ if(p->edf == nil){
+ updatecpu(p);
+ p->priority = reprioritize(p);
+ }
+ if(p != m->readied)
+ m->schedticks = m->ticks + HZ/10;
+ m->readied = nil;
up = p;
up->state = Running;
up->mach = MACHP(m->machno);
@@ -228,39 +273,175 @@
gotolabel(&up->sched);
}
+int
+anyready(void)
+{
+ return runvec;
+}
+
+int
+anyhigher(void)
+{
+ return runvec & ~((1<<(up->priority+1))-1);
+}
+
/*
- * ready(p) is simpler as we do not change process priorities
- * puts the current process at the end of the run queue
- * p->state = Running -> Ready
+ * here once per clock tick to see if we should resched
*/
void
-ready(Proc *p)
+hzsched(void)
{
- int s;
- Schedq *rq;
+ /* once a second, rebalance will reprioritize ready procs */
+ if(m->machno == 0)
+ rebalance();
- if(p->state == Ready){
- print("double ready %s %ud pc %p\n", p->text, p->pid, getcallerpc(&p));
- return;
+ /* unless preempted, get to run for at least 100ms */
+ if(anyhigher()
+ || (!up->fixedpri && (long)(m->ticks - m->schedticks) > 0 && anyready())){
+ m->readied = nil; /* avoid cooperative scheduling */
+ up->delaysched++;
}
+}
- s = splhi();
/*
- if(edfready(p)){
- splx(s);
+ * here at the end of non-clock interrupts to see if we should preempt the
+ * current process. Returns 1 if preempted, 0 otherwise.
+ */
+int
+preempted(void)
+{
+ if(up != nil && up->state == Running)
+ if(up->preempted == 0)
+ if(anyhigher())
+ if(!active.exiting){
+ m->readied = nil; /* avoid cooperative scheduling */
+ up->preempted = 1;
+ sched();
+ splhi();
+ up->preempted = 0;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Update the cpu time average for this particular process,
+ * which is about to change from up -> not up or vice versa.
+ * p->lastupdate is the last time an updatecpu happened.
+ *
+ * The cpu time average is a decaying average that lasts
+ * about D clock ticks. D is chosen to be approximately
+ * the cpu time of a cpu-intensive "quick job". A job has to run
+ * for approximately D clock ticks before we home in on its
+ * actual cpu usage. Thus if you manage to get in and get out
+ * quickly, you won't be penalized during your burst. Once you
+ * start using your share of the cpu for more than about D
+ * clock ticks though, your p->cpu hits 1000 (1.0) and you end up
+ * below all the other quick jobs. Interactive tasks, because
+ * they basically always use less than their fair share of cpu,
+ * will be rewarded.
+ *
+ * If the process has not been running, then we want to
+ * apply the filter
+ *
+ * cpu = cpu * (D-1)/D
+ *
+ * n times, yielding
+ *
+ * cpu = cpu * ((D-1)/D)^n
+ *
+ * but D is big enough that this is approximately
+ *
+ * cpu = cpu * (D-n)/D
+ *
+ * so we use that instead.
+ *
+ * If the process has been running, we apply the filter to
+ * 1 - cpu, yielding a similar equation. Note that cpu is
+ * stored in fixed point (* 1000).
+ *
+ * Updatecpu must be called before changing up, in order
+ * to maintain accurate cpu usage statistics. It can be called
+ * at any time to bring the stats for a given proc up-to-date.
+ */
+void
+updatecpu(Proc *p)
+{
+ ulong t, ocpu, n, D;
+
+ if(p->edf != nil)
return;
+
+ t = MACHP(0)->ticks*Scaling + Scaling/2;
+ n = t - p->lastupdate;
+ if(n == 0)
+ return;
+ p->lastupdate = t;
+
+ D = schedgain*HZ*Scaling;
+ if(n > D)
+ n = D;
+
+ ocpu = p->cpu;
+ if(p != up)
+ p->cpu = (ocpu*(D-n))/D;
+ else{
+ t = 1000 - ocpu;
+ t = (t*(D-n))/D;
+ p->cpu = 1000 - t;
}
-*/
+//iprint("pid %lud %s for %lud cpu %lud -> %lud\n", p->pid,p==up?"active":"inactive",n, ocpu,p->cpu);
+}
- /* 9front does this. Not sure what it does yet
- if(up != p && (p->wired == nil || p->wired == MACHP(m->machno)))
- m->readied = p; *//* group scheduling */
+/*
+ * On average, p has used p->cpu of a cpu recently.
+ * Its fair share is conf.nmach/m->load of a cpu. If it has been getting
+ * too much, penalize it. If it has been getting not enough, reward it.
+ * I don't think you can get much more than your fair share that
+ * often, so most of the queues are for using less. Having a priority
+ * of 3 means you're just right. Having a higher priority (up to p->basepri)
+ * means you're not using as much as you could.
+ */
+int
+reprioritize(Proc *p)
+{
+ int fairshare, n, load, ratio;
- /* add to the end of the run queue */
- rq = &runq[p->priority];
+ load = MACHP(0)->load;
+ if(load == 0)
+ return p->basepri;
+
+ /*
+ * fairshare = 1.000 * conf.nmach * 1.000/load,
+ * except the decimal point is moved three places
+ * on both load and fairshare.
+ */
+ fairshare = (conf.nmach*1000*1000)/load;
+ n = p->cpu;
+ if(n == 0)
+ n = 1;
+ ratio = (fairshare+n/2) / n;
+ if(ratio > p->basepri)
+ ratio = p->basepri;
+ if(ratio < 0)
+ panic("reprioritize");
+//iprint("pid %lud cpu %lud load %d fair %d pri %d\n", p->pid, p->cpu, load, fairshare, ratio);
+ return ratio;
+}
+
+/*
+ * add a process to a scheduling queue
+ */
+void
+queueproc(Schedq *rq, Proc *p)
+{
+ int pri;
+
+ pri = rq - runq;
lock(runq);
- p->rnext = 0;
- if(rq->tail)
+ p->priority = pri;
+ p->rnext = nil;
+ if(rq->tail != nil)
rq->tail->rnext = p;
else
rq->head = p;
@@ -267,63 +448,171 @@
rq->tail = p;
rq->n++;
nrdy++;
- occupied |= 1<<p->priority;
- p->state = Ready;
+ runvec |= 1<<pri;
unlock(runq);
- splx(s);
}
-int
-anyready(void)
+/*
+ * try to remove a process from a scheduling queue (called splhi)
+ */
+Proc*
+dequeueproc(Schedq *rq, Proc *tp)
{
- /* same priority only */
- return occupied & (1<<up->priority);
+ Proc *l, *p;
+
+ if(!canlock(runq))
+ return nil;
+
+ /*
+ * the queue may have changed before we locked runq,
+ * refind the target process.
+ */
+ l = nil;
+ for(p = rq->head; p != nil; p = p->rnext){
+ if(p == tp)
+ break;
+ l = p;
+ }
+
+ /*
+ * p->mach==0 only when process state is saved
+ */
+ if(p == nil || p->mach != nil){
+ unlock(runq);
+ return nil;
+ }
+ if(p->rnext == nil)
+ rq->tail = l;
+ if(l != nil)
+ l->rnext = p->rnext;
+ else
+ rq->head = p->rnext;
+ if(rq->head == nil)
+ runvec &= ~(1<<(rq-runq));
+ rq->n--;
+ nrdy--;
+ if(p->state != Ready)
+ print("dequeueproc %s %ud %s\n", p->text, p->pid, statename[p->state]);
+
+ unlock(runq);
+ return p;
}
/*
- * the higher the priority, the lower the number
- * unlike 9front
+ * ready(p) picks a new priority for a process and sticks it in the
+ * runq for that priority.
*/
-int
-anyhigher(void)
+void
+ready(Proc *p)
{
- return occupied & ((1<<up->priority)-1);
+ int s, pri;
+ Schedq *rq;
+ void (*pt)(Proc*, int, vlong);
+
+ if(p->state == Ready){
+ print("double ready %s %ud pc %p\n", p->text, p->pid, getcallerpc(&p));
+ return;
+ }
+
+ s = splhi();
+ if(edfready(p)){
+ splx(s);
+ return;
+ }
+
+ if(up != p && (p->wired == nil || p->wired == MACHP(m->machno)))
+ m->readied = p; /* group scheduling */
+
+ updatecpu(p);
+ pri = reprioritize(p);
+ p->priority = pri;
+ rq = &runq[pri];
+ p->state = Ready;
+ queueproc(rq, p);
+ pt = proctrace;
+ if(pt != nil)
+ pt(p, SReady, 0);
+ splx(s);
}
/*
- * here at the end of non-clock interrupts to see if we should preempt the
- * current process. Returns 1 if preempted, 0 otherwise.
- * similar to 9front's preempted()
+ * yield the processor and drop our priority
*/
-int
-preemption(int tick)
+void
+yield(void)
{
- if(up != nil && up->state == Running &&
- up->preempted == 0 &&
- active.exiting == 0 &&
- (anyhigher() || tick && anyready())){
- up->preempted = 1;
+ if(anyready()){
+ /* pretend we just used 1/2 tick */
+ up->lastupdate -= Scaling/2;
sched();
- splhi();
- up->preempted = 0;
- return 1;
}
- return 0;
}
+/*
+ * recalculate priorities once a second. We need to do this
+ * since priorities will otherwise only be recalculated when
+ * the running process blocks.
+ */
+ulong balancetime;
+
+static void
+rebalance(void)
+{
+ int pri, npri, x;
+ Schedq *rq;
+ Proc *p;
+ ulong t;
+
+ t = m->ticks;
+ if(t - balancetime < HZ)
+ return;
+ balancetime = t;
+
+ for(pri=0, rq=runq; pri<Npriq; pri++, rq++){
+another:
+ p = rq->head;
+ if(p == nil)
+ continue;
+ if(pri == p->basepri)
+ continue;
+ updatecpu(p);
+ npri = reprioritize(p);
+ if(npri != pri){
+ x = splhi();
+ p = dequeueproc(rq, p);
+ if(p != nil)
+ queueproc(&runq[npri], p);
+ splx(x);
+ goto another;
+ }
+ }
+}
+
+/*
+ * pick a process to run
+ */
Proc*
runproc(void)
{
- Schedq *rq, *erq;
- Proc *p, *tp, *last;
- u64 start, now;
+ Schedq *rq;
+ Proc *p;
+ ulong start, now;
int i;
-/* void (*pt)(Proc*, int, vlong); */
+ void (*pt)(Proc*, int, vlong);
start = perfticks();
+
+ /* cooperative scheduling until the clock ticks */
+ if((p = m->readied) != nil && p->mach == nil && p->state == Ready
+ && (p->wired == nil || p->wired == MACHP(m->machno))
+ && runq[Nrq-1].head == nil && runq[Nrq-2].head == nil){
+ skipscheds++;
+ rq = &runq[p->priority];
+ goto found;
+ }
+
preempts++;
- erq = runq + Nrq - 1;
loop:
/*
* find a process that last ran on this processor (affinity),
@@ -332,15 +621,14 @@
spllo();
for(i = 0;; i++){
/*
- * find the highest priority target process that this
- * processor can run given affinity constraints.
- * when i == 0, thats where we pick the associated procs
- * after this, we take anyone even from other cores
+ * find the highest priority target process that this
+ * processor can run given affinity constraints.
+ *
*/
- for(rq = runq; rq <= erq; rq++){
- for(tp = rq->head; tp != nil; tp = tp->rnext){
- if(tp->mp == nil || tp->mp == MACHP(m->machno)
- || (tp->wired == nil && i > 0))
+ for(rq = &runq[Nrq-1]; rq >= runq; rq--){
+ for(p = rq->head; p != nil; p = p->rnext){
+ if(p->mp == nil || p->mp == MACHP(m->machno)
+ || (p->wired == nil && i > 0))
goto found;
}
}
@@ -355,82 +643,21 @@
}
found:
-/* print("runproc\n");
- procdump();
-*/
splhi();
- /*
- * try to remove the process from a scheduling queue
- * similar to 9front's dequeueproc()
- */
- if(!canlock(runq))
+ p = dequeueproc(rq, p);
+ if(p == nil)
goto loop;
- /*
- * the queue may have changed before we locked runq,
- * refind the target process.
- */
- last = nil;
- for(p = rq->head; p != nil; p = p->rnext){
- if(p == tp)
- break;
- last = p;
- }
-
- /*
- * p->mach==0 only when process state is saved
- */
- if(p == nil || p->mach != nil){
- unlock(runq);
- goto loop;
- }
- /* if p is the last in the run queue
- * update run queue tail to point to the last */
- if(p->rnext == nil)
- rq->tail = last;
- /* remove p from the linked list */
- if(last != nil)
- last->rnext = p->rnext;
- else
- rq->head = p->rnext;
- /* no other procs in the run queue */
- if(rq->head == nil){
- rq->tail = nil;
- occupied &= ~(1<<p->priority);
- }
- rq->n--;
- nrdy--;
- if(p->dbgstop){
- p->state = Stopped;
- unlock(runq);
- goto loop;
- }
- if(p->state != Ready)
- print("runproc %s %ud %s\n", p->text, p->pid, statename[p->state]);
- unlock(runq);
-
p->state = Scheding;
p->mp = MACHP(m->machno);
-/* if(edflock(p)){
- edfrun(p, rq == &runq[PriEdf]); *//* start deadline timer and do admin *//*
+ if(edflock(p)){
+ edfrun(p, rq == &runq[PriEdf]); /* start deadline timer and do admin */
edfunlock();
}
pt = proctrace;
if(pt != nil)
pt(p, SRun, 0);
-*/
-/* for debugging */
-/* if(p->pid != prevpid){
- prevpid = p->pid;
- if(p->type == Interp && p->iprog != nil){
- print(" %d:%s,%d %s ",
- p->pid, p->text, ((Prog*)p->iprog)->pid, ((Prog*)p->iprog)->R.M->m->path);
- }else
- print(" %d:%s", p->pid, p->text);
- }else
- print(".");
-*/
return p;
}
@@ -460,15 +687,10 @@
p->dbgreg = nil;
p->nerrlab = 0;
p->type = Unknown;
- p->state = Scheding;
- p->priority = PriNormal;
- p->mach = 0;
- p->qnext = 0;
- p->kp = 0;
- p->killed = 0; /* TODO replace these 2 with notepending */
- p->swipend = 0;
p->nlocks = 0;
p->delaysched = 0;
+ p->trace = 0;
+
memset(&p->defenv, 0, sizeof(p->defenv));
p->env = &p->defenv;
kstrdup(&p->env->user, "*nouser");
@@ -475,18 +697,13 @@
p->env->errstr = p->env->errbuf0;
p->env->syserrstr = p->env->errbuf1;
- /* clear any previous notes */
- p->nnote = 0;
- p->notify = nil;
- p->notified = 0;
- p->notepending = 0;
-
/* sched params */
p->mp = 0;
p->wired = 0;
+ procpriority(p, PriNormal, 0);
+ p->cpu = 0;
+ p->lastupdate = MACHP(0)->ticks*Scaling;
p->edf = nil;
- p->noteid = 0;
- p->trace = 0;
pidalloc(p);
if(p->pid == 0)
@@ -532,19 +749,23 @@
p->mp = p->wired;
}
-int
-setpri(int priority)
+void
+procpriority(Proc *p, int pri, int fixed)
{
- int p;
-
- /* called by up so not on run queue */
- p = up->priority;
- up->priority = priority;
- if(up->state == Running && anyhigher())
- sched();
- return p;
+ if(pri >= Npriq)
+ pri = Npriq - 1;
+ else if(pri < 0)
+ pri = 0;
+ p->basepri = pri;
+ p->priority = pri;
+ if(fixed){
+ p->fixedpri = 1;
+ } else {
+ p->fixedpri = 0;
+ }
}
+/* called procinit0 on 9front */
void
procinit(void)
{
@@ -560,8 +781,6 @@
}
procalloc.arena = p;
procalloc.free = p;
-
- p = procalloc.free;
for(i=0; i<conf.nproc-1; i++,p++)
p->qnext = p+1;
p->qnext = 0;
@@ -582,7 +801,7 @@
sleep(Rendez *r, int (*f)(void*), void *arg)
{
int s;
-/* void (*pt)(Proc*, int, vlong);*/
+ void (*pt)(Proc*, int, vlong);
if(up == nil)
panic("sleep() not in process (%zux)", getcallerpc(&r));
@@ -603,7 +822,6 @@
if(r->p != nil){
print("double sleep called from %#p, %ud %ud\n", getcallerpc(&r), r->p->pid, up->pid);
dumpstack();
- panic("sleep");
}
/*
@@ -627,9 +845,9 @@
* now we are committed to
* change state and call scheduler
*/
-/* pt = proctrace;
+ pt = proctrace;
if(pt != nil)
- pt(up, SSleep, 0); */
+ pt(up, SSleep, 0);
up->state = Wakeme;
up->r = r; /* for swiproc */
unlock(&up->rlock);
@@ -867,11 +1085,69 @@
}
/*
- * 9front maintains broken processes. Not bothering with them
- * as there should not be any broken proc's in inferno
+ * weird thing: keep at most NBROKEN around
*/
+#define NBROKEN 4
+struct
+{
+ QLock;
+ int n;
+ Proc *p[NBROKEN];
+}broken;
+static void
+addbroken(void)
+{
+ qlock(&broken);
+ if(broken.n == NBROKEN) {
+ ready(broken.p[0]);
+ memmove(&broken.p[0], &broken.p[1], sizeof(Proc*)*(NBROKEN-1));
+ --broken.n;
+ }
+ broken.p[broken.n++] = up;
+ qunlock(&broken);
+
+ edfstop(up);
+ up->state = Broken;
+ up->psstate = nil;
+ sched();
+}
+
void
+unbreak(Proc *p)
+{
+ int b;
+
+ qlock(&broken);
+ for(b=0; b < broken.n; b++)
+ if(broken.p[b] == p) {
+ broken.n--;
+ memmove(&broken.p[b], &broken.p[b+1],
+ sizeof(Proc*)*(NBROKEN-(b+1)));
+ ready(p);
+ break;
+ }
+ qunlock(&broken);
+}
+
+int
+freebroken(void)
+{
+ int i, n;
+
+ qlock(&broken);
+ n = broken.n;
+ for(i=0; i<n; i++) {
+ ready(broken.p[i]);
+ broken.p[i] = nil;
+ }
+ broken.n = 0;
+ qunlock(&broken);
+ return n;
+}
+
+
+void
notkilled(void)
{
lock(&up->rlock);
@@ -885,12 +1161,12 @@
Proc *p;
/* Segment **s;*/
ulong utime, stime;
-/* Waitq *wq;*/
+ Waitq *wq;
Fgrp *fgrp;
Egrp *egrp;
Rgrp *rgrp;
Pgrp *pgrp;
-/* Chan *dot;*/
+ Chan *dot;
void (*pt)(Proc*, int, vlong);
up->fpstate &= ~FPillegal;
@@ -897,8 +1173,8 @@
up->alarm = 0;
timerdel(up);
pt = proctrace;
-/* if(pt != nil)
- pt(up, SDead, 0);*/
+ if(pt != nil)
+ pt(up, SDead, 0);
/* nil out all the resources under lock (free later) */
qlock(&up->debug);
@@ -908,10 +1184,10 @@
up->env->egrp = nil;
rgrp = up->env->rgrp;
up->env->rgrp = nil;
+/* dot = up->env->pgrp->dot;
+ up->env->pgrp->dot = nil;*/
pgrp = up->env->pgrp;
up->env->pgrp = nil;
-/* dot = up->dot;
- up->dot = nil;*/
qunlock(&up->debug);
if(fgrp != nil)
@@ -918,20 +1194,19 @@
closefgrp(fgrp);
if(egrp != nil)
closeegrp(egrp);
-/* if(rgrp != nil)
+ if(rgrp != nil)
closergrp(rgrp);
- if(dot != nil)
- cclose(dot);*/
+/* if(dot != nil)
+ cclose(dot); closepgrp() takes care of this */
if(pgrp != nil)
closepgrp(pgrp);
-
-/* if(up->parentpid == 0){
+ if(up->parentpid == 0){
if(exitstr == nil)
exitstr = "unknown";
panic("boot process died: %s", exitstr);
- }*/
+ }
-/* p = up->parent;
+ p = up->parent;
if(p != nil && p->pid == up->parentpid && p->state != Broken){
wq = smalloc(sizeof(Waitq));
wq->w.pid = up->pid;
@@ -941,25 +1216,25 @@
wq->w.time[TSys] = tk2ms(stime);
wq->w.time[TReal] = tk2ms(MACHP(0)->ticks - up->time[TReal]);
if(exitstr != nil && exitstr[0])
- snprint(wq->w.msg, sizeof(wq->w.msg), "%s %lud: %s", up->text, up->pid, exitstr);
+ snprint(wq->w.msg, sizeof(wq->w.msg), "%s %ud: %s", up->text, up->pid, exitstr);
else
wq->w.msg[0] = '\0';
lock(&p->exl);
- \/*
+ /*
* Check that parent is still alive.
- *\/
+ */
if(p->pid == up->parentpid && p->state != Broken) {
p->nchild--;
p->time[TCUser] += utime;
p->time[TCSys] += stime;
- \/*
+ /*
* If there would be more than 128 wait records
* processes for my parent, then don't leave a wait
* record behind. This helps prevent badly written
* daemon processes from accumulating lots of wait
* records.
- *\/
+ */
if(p->nwait < 128) {
wq->next = p->waitq;
p->waitq = wq;
@@ -975,10 +1250,10 @@
if(!freemem)
addbroken();
-*/
+
qlock(&up->debug);
-/* lock(&up->exl); \/* Prevent my children from leaving waits *\/
+ lock(&up->exl); /* Prevent my children from leaving waits */
pidfree(up);
up->parent = nil;
up->nchild = up->nwait = 0;
@@ -989,16 +1264,16 @@
up->waitq = wq->next;
free(wq);
}
-*/
+
/* release debuggers */
if(up->pdbg != nil) {
wakeup(&up->pdbg->sleep);
up->pdbg = nil;
}
-/* if(up->syscalltrace != nil) {
+ if(up->syscalltrace != nil) {
free(up->syscalltrace);
up->syscalltrace = nil;
- }*/
+ }
if(up->watchpt != nil){
free(up->watchpt);
up->watchpt = nil;
@@ -1015,12 +1290,58 @@
}
qunlock(&up->seglock);*/
-/* edfstop(up); */
+ edfstop(up);
up->state = Moribund;
sched();
panic("pexit");
}
+static int
+haswaitq(void *x)
+{
+ return ((Proc*)x)->waitq != nil;
+}
+
+ulong
+pwait(Waitmsg *w)
+{
+ ulong cpid;
+ Waitq *wq;
+
+ if(!canqlock(&up->qwaitr))
+ error(Einuse);
+
+ if(waserror()) {
+ qunlock(&up->qwaitr);
+ nexterror();
+ }
+
+ lock(&up->exl);
+ while(up->waitq == nil) {
+ if(up->nchild == 0) {
+ unlock(&up->exl);
+ error(Enochild);
+ }
+ unlock(&up->exl);
+ sleep(&up->waitr, haswaitq, up);
+ lock(&up->exl);
+ }
+ wq = up->waitq;
+ up->waitq = wq->next;
+ up->nwait--;
+ unlock(&up->exl);
+
+ qunlock(&up->qwaitr);
+ poperror();
+
+ if(w != nil)
+ memmove(w, &wq->w, sizeof(Waitmsg));
+ cpid = wq->w.pid;
+ free(wq);
+ return cpid;
+}
+
+
/* macro for speed? */
Proc*
proctab(int i)
@@ -1032,19 +1353,23 @@
void
dumpaproc(Proc *p)
{
+ /*ulong bss; 9front shows this */
char *s;
- char tmp[14];
+ if(p == nil)
+ return;
+
+ /*bss = 0;
+ if(p->seg[BSEG] != nil)
+ bss = p->seg[BSEG]->top;*/
+
s = p->psstate;
if(s == nil)
- s = "kproc";
- if(p->state == Wakeme)
- snprint(tmp, sizeof(tmp), " /%.8lux", p->r);
- else
- *tmp = '\0';
- print("%p:%3ud:%14s pc %.8zux %s/%s qpc %.8zux priority %d%s\n",
- p, p->pid, p->text, p->pc, s, statename[p->state], p->qpc,
- p->priority, tmp);
+ s = statename[p->state];
+ print("%3ud:%10s pc %#p dbgpc %#p %8s (%s) ut %d st %d bss %ux qpc %#p nl %d nd %ud lpc %#p pri %ud\n",
+ p->pid, p->text, p->pc, dbgpc(p), s, statename[p->state],
+ p->time[0], p->time[1], 0/*bss*/, p->qpc, p->nlocks, p->delaysched,
+ p->lastlock ? p->lastlock->pc : 0, p->priority);
}
void
@@ -1063,6 +1388,7 @@
}
}
+/* different from 9front, need to change after dis is removed */
void
kproc(char *name, void (*func)(void *), void *arg, int flags)
{
@@ -1072,19 +1398,11 @@
Egrp *eg;
while((p = newproc()) == nil){
-/* TODO freebroken(); */
+ freebroken();
resrcwait("no procs for kproc");
}
qlock(&p->debug);
- p->psstate = 0;
- p->kp = 1;
-
- p->fpsave = up->fpsave;
-/* p->scallnr = up->scallnr; */
- p->nerrlab = 0;
-
- kstrdup(&p->env->user, up->env->user);
if(flags & KPDUPPG) {
pg = up->env->pgrp;
incref(pg);
@@ -1119,7 +1437,11 @@
/* this does all of the above 3 lines */
kprocchild(p, func, arg);
- strcpy(p->text, name);
+ kstrdup(&p->env->user, up->env->user);
+ kstrdup(&p->text, name);
+ kstrdup(&p->args, "");
+ p->nargs = 0;
+ p->setargs = 0;
/* if(kpgrp == nil)
kpgrp = newpgrp();
@@ -1126,21 +1448,77 @@
p->pgrp = kpgrp;
incref(kpgrp);*/
+ p->kp = 1;
+
memset(p->time, 0, sizeof(p->time));
p->time[TReal] = MACHP(0)->ticks;
-/* cycles(&p->kentry);
- p->pcycles = -p->kentry;*/
+ cycles(&p->kentry);
+ p->pcycles = -p->kentry;
+ /* pidalloc(p); is done in newproc() */
+
+ p->fpsave = up->fpsave;
+/* p->scallnr = up->scallnr; */
+ p->nerrlab = 0;
+
qunlock(&p->debug);
- /* leaving the priority at PriNormal */
-/* procpriority(p, PriKproc, 0);*/
+ procpriority(p, PriKproc, 0);
p->psstate = nil;
ready(p);
}
+/*
+ * called splhi() by notify(). See comment in notify for the
+ * reasoning.
+ */
void
+procctl(void)
+{
+ char *state;
+ ulong s;
+
+ switch(up->procctl) {
+ case Proc_exitbig:
+ spllo();
+ up->fpstate &= ~FPillegal;
+ pprint("Killed: Insufficient physical memory\n");
+ pexit("Killed: Insufficient physical memory", 1);
+
+ case Proc_exitme:
+ spllo(); /* pexit has locks in it */
+ pexit("Killed", 1);
+
+ case Proc_traceme:
+ if(up->nnote == 0)
+ return;
+ /* No break */
+
+ case Proc_stopme:
+ up->procctl = 0;
+ state = up->psstate;
+ up->psstate = "Stopped";
+ /* free a waiting debugger */
+ s = spllo();
+ qlock(&up->debug);
+ if(up->pdbg != nil) {
+ wakeup(&up->pdbg->sleep);
+ up->pdbg = nil;
+ }
+ qunlock(&up->debug);
+ splhi();
+ up->state = Stopped;
+ sched();
+ up->psstate = state;
+ splx(s);
+ return;
+ }
+}
+
+#include "errstr.h"
+
+void
errorf(char *fmt, ...)
{
va_list arg;
@@ -1174,6 +1552,7 @@
if(err == nil)
panic("error: nil parameter");
kstrcpy(up->env->errstr, err, ERRMAX);
+ setlabel(&up->errlab[NERR-1]);
if(emptystr(err) == 1){
DBG("error nil error err %s caller 0x%p up->pid %d\n", err, getcallerpc(&err), up->pid);
up->env->errpc = 0;
@@ -1197,7 +1576,6 @@
gotolabel(&up->errlab[--up->nerrlab]);
}
-#include "errstr.h"
/* Set kernel error string */
void
@@ -1278,24 +1656,6 @@
error(buf);
}
-/*
- * change ownership to 'new' of all processes owned by 'old'. Used when
- * eve changes.
- */
-void
-renameuser(char *old, char *new)
-{
- Proc *p, *ep;
- Osenv *o;
-
- ep = procalloc.arena+conf.nproc;
- for(p = procalloc.arena; p < ep; p++) {
- o = &p->defenv;
- if(o->user != nil && strcmp(o->user, old) == 0)
- kstrdup(&o->user, new);
- }
-}
-
int
return0(void*)
{
@@ -1403,6 +1763,84 @@
}
/*
+ * change ownership to 'new' of all processes owned by 'old'. Used when
+ * eve changes.
+ */
+void
+renameuser(char *old, char *new)
+{
+ Proc *p;
+ int i;
+
+ for(i = 0; i < conf.nproc; i++){
+ p = proctab(i);
+ qlock(&p->debug);
+ if(p->user != nil && strcmp(old, p->user) == 0)
+ kstrdup(&p->user, new);
+ qunlock(&p->debug);
+ }
+}
+
+void
+procsetuser(char *new)
+{
+ qlock(&up->debug);
+ kstrdup(&up->user, new);
+ up->basepri = PriNormal;
+ qunlock(&up->debug);
+}
+
+/*
+ * time accounting called by clock() splhi'd
+ */
+void
+accounttime(void)
+{
+ Proc *p;
+ ulong n, per;
+ static ulong nrun;
+
+ p = m->proc;
+ if(p != nil) {
+ nrun++;
+ p->time[p->insyscall]++;
+ }
+
+ /* calculate decaying duty cycles */
+ n = perfticks();
+ per = n - m->perf.last;
+ m->perf.last = n;
+ per = ((uvlong)m->perf.period*(HZ-1) + per)/HZ;
+ if(per != 0)
+ m->perf.period = per;
+
+ m->perf.avg_inidle = ((uvlong)m->perf.avg_inidle*(HZ-1)+m->perf.inidle)/HZ;
+ m->perf.inidle = 0;
+
+ m->perf.avg_inintr = ((uvlong)m->perf.avg_inintr*(HZ-1)+m->perf.inintr)/HZ;
+ m->perf.inintr = 0;
+
+ /* only one processor gets to compute system load averages */
+ if(m->machno != 0)
+ return;
+
+ /*
+ * calculate decaying load average.
+ * if we decay by (n-1)/n then it takes
+ * n clock ticks to go from load L to .36 L once
+ * things quiet down. it takes about 5 n clock
+ * ticks to go to zero. so using HZ means this is
+ * approximately the load over the last second,
+ * with a tail lasting about 5 seconds.
+ */
+ n = nrun;
+ nrun = 0;
+ n = (nrdy+n)*1000*100;
+ pload = ((uvlong)pload*(HZ-1)+n)/HZ;
+ m->load = pload/100;
+}
+
+/*
* A Pid structure is a reference counted hashtable entry
* with "pid" being the key and "procindex" being the value.
* A entry is allocated atomically by changing the key from
@@ -1550,7 +1988,6 @@
return p->noteid = i->pid;
}
-/*
static ulong
setparentpid(Proc *p, Proc *pp)
{
@@ -1558,7 +1995,7 @@
i = pidadd(pp->pid);
return p->parentpid = i->pid;
-} */
+}
/*
* allocate pid, noteid and parentpid to a process
@@ -1569,8 +2006,8 @@
Pid *i;
/* skip for the boot process */
-/* if(up != nil)
- setparentpid(p, up);*/
+ if(up != nil)
+ setparentpid(p, up);
i = pidadd(0);
i->procindex = (int)(p - procalloc.arena);
@@ -1598,10 +2035,8 @@
i = pidlookup(p->noteid);
piddel(i);
-/* if(p->parentpid != 0)
+ if(p->parentpid != 0)
piddel(pidlookup(p->parentpid));
p->pid = p->noteid = p->parentpid = 0;
-*/
- p->pid = p->noteid = 0;
}
--- a/os/port/qlock.c
+++ b/os/port/qlock.c
@@ -13,24 +13,6 @@
ulong qlockq;
} rwstats;
-/*
- *lock()
- blockinglock = qpc = nil
- *unlock()
- blockinglock = qpc = nil
-
- *lock()
- blockinglock = qpc = nil
- placed in the queue
- blockinglock = lock address
- qpc = pc that called lock()
- out of the queue, ready to run
- blockinglock = nil
- qpc = pc that called lock()
- *unlock()
- blockinglock = qpc = nil
- */
-
void
eqlock(QLock *q)
{
@@ -66,20 +48,17 @@
else
p->qnext = up;
q->tail = up;
- up->qnext = nil;
- up->blockinglock = q;
up->eql = q;
+ up->qnext = nil;
up->qpc = getcallerpc(&q);
up->state = Queueing;
unlock(&q->use);
sched();
- up->blockinglock = nil;
if(up->eql == nil){
up->notepending = 0;
interrupted();
}
up->eql = nil;
- up->qpc = 0;
}
void
@@ -110,9 +89,7 @@
q->head = up;
else
p->qnext = up;
- up->qnext = nil;
q->tail = up;
- up->blockinglock = q;
up->eql = nil;
up->state = Queueing;
up->qpc = getcallerpc(&q);
@@ -134,7 +111,6 @@
return 1;
}
-/* blockinglock should not be nil */
void
qunlock(QLock *q)
{
@@ -144,11 +120,6 @@
if (q->locked == 0)
print("qunlock called with qlock not held, from %#p\n",
getcallerpc(&q));
- if (up != nil && up->blockinglock != nil)
- print("qunlock called with blockinglock %#p, from %#p\n",
- up->blockinglock, getcallerpc(&q));
- if (up != nil)
- up->qpc = 0;
p = q->head;
if(p != nil) {
/* some other process is waiting for this lock */
@@ -155,7 +126,6 @@
q->head = p->qnext;
if(q->head == nil)
q->tail = nil;
- p->blockinglock = nil;
unlock(&q->use);
ready(p);
return;
@@ -189,8 +159,6 @@
p->qnext = up;
q->tail = up;
up->qnext = nil;
- up->blockinglock = q;
- up->eql = nil;
up->state = QueueingR;
up->qpc = getcallerpc(&q);
unlock(&q->use);
@@ -197,22 +165,6 @@
sched();
}
-/* same as rlock but punts if there are any writers waiting */
-int
-canrlock(RWlock *q)
-{
- lock(&q->use);
- rwstats.rlock++;
- if(q->writer == 0 && q->head == nil){
- /* no writer, go for it */
- q->readers++;
- unlock(&q->use);
- return 1;
- }
- unlock(&q->use);
- return 0;
-}
-
void
runlock(RWlock *q)
{
@@ -219,11 +171,6 @@
Proc *p;
lock(&q->use);
- if (up != nil && up->blockinglock != nil)
- print("runlock called with blockinglock %#p, from %#p\n",
- up->blockinglock, getcallerpc(&q));
- if (up != nil)
- up->qpc = 0;
p = q->head;
if(--(q->readers) > 0 || p == nil){
unlock(&q->use);
@@ -237,7 +184,6 @@
if(q->head == nil)
q->tail = nil;
q->writer = 1;
- p->blockinglock = nil;
unlock(&q->use);
ready(p);
}
@@ -269,10 +215,7 @@
p->qnext = up;
q->tail = up;
up->qnext = nil;
- up->blockinglock = q;
- up->eql = nil;
up->state = QueueingW;
- up->qpc = getcallerpc(&q);
unlock(&q->use);
sched();
}
@@ -283,11 +226,6 @@
Proc *p;
lock(&q->use);
- if (up != nil && up->blockinglock != nil)
- print("runlock called with blockinglock %#p, from %#p\n",
- up->blockinglock, getcallerpc(&q));
- if (up != nil)
- up->qpc = 0;
p = q->head;
if(p == nil){
q->writer = 0;
@@ -299,7 +237,6 @@
q->head = p->qnext;
if(q->head == nil)
q->tail = nil;
- p->blockinglock = nil;
unlock(&q->use);
ready(p);
return;
@@ -313,7 +250,6 @@
p = q->head;
q->head = p->qnext;
q->readers++;
- p->blockinglock = nil;
ready(p);
}
if(q->head == nil)
@@ -320,4 +256,21 @@
q->tail = nil;
q->writer = 0;
unlock(&q->use);
+}
+
+
+/* same as rlock but punts if there are any writers waiting */
+int
+canrlock(RWlock *q)
+{
+ lock(&q->use);
+ rwstats.rlock++;
+ if(q->writer == 0 && q->head == nil){
+ /* no writer, go for it */
+ q->readers++;
+ unlock(&q->use);
+ return 1;
+ }
+ unlock(&q->use);
+ return 0;
}