kernel: add extra "New" process state to catch invalid state transitions

For ready() to check invalid state transitions,
we want to ensure that ready() is never called
on a dead proc (or one that is currently dying),
however when we create a new process,
its initial state was "Dead" until is is ready()d.

Instead, introduce the state "New", which is
set when the proc is removed from the freelist
by newproc(), making New -> Ready -> Running
state transition valid.

Also make sure we never leave notes to deadly
processes or ones that are in "Broken" state.
This commit is contained in:
cinap_lenrek 2024-01-04 04:45:56 +00:00
parent f16547d604
commit feda4b1ed2
4 changed files with 33 additions and 20 deletions

View file

@ -1296,7 +1296,7 @@ proctext(Chan *c, Proc *p)
qunlock(&p->seglock);
nexterror();
}
if(p->state == Dead || p->pid != PID(c->qid))
if(p->state <= New || p->pid != PID(c->qid))
error(Eprocdied);
if((s = p->seg[TSEG]) == nil)
error(Enonexist);
@ -1647,7 +1647,7 @@ procctlmemio(Chan *c, Proc *p, uintptr offset, void *a, long n, int read)
qunlock(&p->seglock);
nexterror();
}
if(p->state == Dead || p->pid != PID(c->qid))
if(p->state <= New || p->pid != PID(c->qid))
error(Eprocdied);
for(i = 0; i < NSEG; i++) {

View file

@ -372,7 +372,7 @@ edfadmit(Proc *p)
/* Look for another proc with the same period to synchronize to */
for(i=0; (r = proctab(i)) != nil; i++) {
if(r->state == Dead || r == p)
if(r->state <= New || r == p)
continue;
if(r->edf == nil || (r->edf->flags & Admitted) == 0)
continue;

View file

@ -607,6 +607,7 @@ enum
{
Dead = 0, /* Process states */
Moribund,
New,
Ready,
Scheding,
Running,

View file

@ -42,6 +42,7 @@ char *statename[] =
{ /* BUG: generate automatically */
"Dead",
"Moribund",
"New",
"Ready",
"Scheding",
"Running",
@ -80,15 +81,13 @@ schedinit(void) /* never returns */
ready(up);
break;
case Moribund:
mmurelease(up);
up->state = Dead;
edfstop(up);
if(up->edf != nil){
free(up->edf);
up->edf = nil;
}
mmurelease(up);
lock(&procalloc);
up->mach = nil;
up->qnext = procalloc.free;
@ -456,8 +455,20 @@ ready(Proc *p)
Schedq *rq;
void (*pt)(Proc*, int, vlong);
if(p->state == Ready){
print("double ready %s %lud pc %p\n", p->text, p->pid, getcallerpc(&p));
switch(p->state){
case Running:
if(p == up)
break;
/* wet floor */
case Dead:
case Moribund:
case Scheding:
print("ready %s %s %lud pc %p\n", statename[p->state],
p->text, p->pid, getcallerpc(&p));
return;
case Ready:
print("double ready %s %lud pc %p\n",
p->text, p->pid, getcallerpc(&p));
return;
}
@ -649,11 +660,13 @@ newproc(void)
p->index = procalloc.nextindex++;
procalloc.tab[p->index] = p;
}
assert(p->state == Dead);
procalloc.free = p->qnext;
p->qnext = nil;
unlock(&procalloc);
p->psstate = "New";
p->psstate = nil;
p->state = New;
p->fpstate = FPinit;
#ifdef KFPSTATE
p->kfpstate = FPinit;
@ -930,7 +943,8 @@ procinterrupt(Proc *p)
/* try for the second lock */
if(canlock(r)){
if(p->state != Wakeme || r->p != p)
panic("procinterrupt: state %d %d %d", r->p != p, p->r != r, p->state);
panic("procinterrupt: state %d %d %d",
r->p != p, p->r != r, p->state);
p->r = nil;
r->p = nil;
ready(p);
@ -1022,6 +1036,7 @@ popnote(Ureg *u)
if(u != nil && up->lastnote->ref == 1 && strncmp(up->lastnote->msg, "sys:", 4) == 0){
int l = strlen(up->lastnote->msg);
assert(l < ERRMAX);
assert(userureg(u));
snprint(up->lastnote->msg+l, ERRMAX-l, " pc=%#p", u->pc);
}
@ -1051,7 +1066,7 @@ mknote(char *msg, int flag)
int
pushnote(Proc *p, Note *n)
{
if(p->pid == 0){
if(p->state <= New || p->state == Broken || p->pid == 0){
freenote(n);
return 0;
}
@ -1095,12 +1110,10 @@ postnotepg(ulong noteid, char *msg, int flag)
n = mknote(msg, flag);
for(i = 0; (p = proctab(i)) != nil; i++){
if(p == up)
continue;
if(p->noteid != noteid || p->kp)
if(p == up || p->noteid != noteid || p->kp)
continue;
qlock(&p->debug);
if(p->noteid == noteid){
if(p->noteid == noteid && !p->kp){
incref(n);
pushnote(p, n);
}
@ -1424,7 +1437,7 @@ procflushmmu(int (*match)(Proc*, void*), void *a)
memset(await, 0, conf.nmach*sizeof(await[0]));
nwait = 0;
for(i = 0; (p = proctab(i)) != nil; i++){
if(p->state != Dead && (*match)(p, a)){
if(p->state > New && (*match)(p, a)){
p->newtlb = 1;
for(nm = 0; nm < conf.nmach; nm++){
if(MACHP(nm)->proc == p){
@ -1574,7 +1587,6 @@ kproc(char *name, void (*func)(void *), void *arg)
procpriority(p, PriKproc, 0);
p->psstate = nil;
ready(p);
}
@ -1606,7 +1618,7 @@ procctl(void)
case Proc_stopme:
up->procctl = 0;
state = up->psstate;
up->psstate = "Stopped";
up->psstate = statename[Stopped];
/* free a waiting debugger */
s = spllo();
qlock(&up->debug);
@ -1691,7 +1703,7 @@ killbig(char *why)
max = 0;
kp = nil;
for(i = 0; (p = proctab(i)) != nil; i++) {
if(p->state == Dead || p->kp || p->parentpid == 0)
if(p->state <= New || p->kp || p->parentpid == 0)
continue;
if((p->noswap || (p->procmode & 0222) == 0) && strcmp(eve, p->user) == 0)
continue;
@ -1706,7 +1718,7 @@ killbig(char *why)
print("%lud: %s killed: %s\n", kp->pid, kp->text, why);
qlock(&kp->seglock);
for(i = 0; (p = proctab(i)) != nil; i++) {
if(p->state == Dead || p->kp)
if(p->state <= New || p->kp)
continue;
if(p != kp && p->seg[BSEG] != nil && p->seg[BSEG] == kp->seg[BSEG])
p->procctl = Proc_exitbig;