SysIII/usr/src/uts/vax/os/slp.c
#include "sys/param.h"
#include "sys/dir.h"
#include "sys/user.h"
#include "sys/proc.h"
#include "sys/text.h"
#include "sys/systm.h"
#include "sys/sysinfo.h"
#include "sys/map.h"
#include "sys/file.h"
#include "sys/inode.h"
#include "sys/buf.h"
#include "sys/var.h"
#include "sys/page.h"
#include "sys/mtpr.h"
/*
* Give up the processor till a wakeup occurs
* on chan, at which time the process
* enters the scheduling queue at priority pri.
* The most important effect of pri is that when
* pri<=PZERO a signal cannot disturb the sleep;
* if pri>PZERO signals will be processed.
* Callers of this routine must be prepared for
* premature return, and check that the reason for
* sleeping has gone away.
*/
#define TZERO 6
sleep(chan, pri)
caddr_t chan;
{
register struct proc *rp;
register s;
rp = u.u_procp;
s = spl6();
rp->p_stat = SSLEEP;
rp->p_wchan = chan;
rp->p_pri = pri;
if (rp->p_time > TZERO)
rp->p_time = TZERO;
if(pri > PZERO) {
if(issig()) {
rp->p_wchan = 0;
rp->p_stat = SRUN;
spl0();
goto psig;
}
spl0();
if(runin != 0) {
runin = 0;
wakeup((caddr_t)&runin);
}
swtch();
if(issig())
goto psig;
} else {
spl0();
swtch();
}
splx(s);
return;
/*
* If priority was low (>PZERO) and
* there has been a signal,
* execute non-local goto to
* the qsav location.
* (see trap1/trap.c)
*/
psig:
longjmp(u.u_qsav);
}
/*
* Wake up all processes sleeping on chan.
*/
wakeup(chan)
register caddr_t chan;
{
register struct proc *p;
register struct proc *ep;
int s;
p = &proc[0];
ep = (struct proc *)v.ve_proc;
do {
if(p->p_wchan==chan && p->p_stat==SSLEEP) {
/*
* this code looks dumb, but
* there is a possible race due
* to interrupts.
*/
s = spl6();
if(p->p_wchan == chan)
setrun(p);
splx(s);
}
p++;
} while(p != ep);
}
setrq(p)
struct proc *p;
{
register struct proc *q;
register s;
s = spl6();
for(q=runq; q!=NULL; q=q->p_link)
if(q == p) {
printf("proc on q\n");
goto out;
}
p->p_link = runq;
runq = p;
out:
splx(s);
}
/*
* Set the process running;
* arrange for it to be swapped in if necessary.
*/
setrun(p)
register struct proc *p;
{
p->p_wchan = 0;
p->p_stat = SRUN;
setrq(p);
if ((p->p_flag&SLOAD)==0) {
p->p_time = 0;
if(runout != 0) {
runout = 0;
setrun(&proc[0]);
}
} else
if(p->p_pri < curpri)
runrun++;
}
/*
* The main loop of the scheduling (swapping)
* process.
* The basic idea is:
* see if anyone wants to be swapped in;
* swap out processes until there is room;
* swap him in;
* repeat.
* The runout flag is set whenever someone is swapped out.
* Sched sleeps on it awaiting work.
*
* Sched sleeps on runin whenever it cannot find enough
* core (by swapping out or otherwise) to fit the
* selected swapped process. It is awakened when the
* core situation changes and in any case once per second.
*/
sched()
{
register struct proc *rp, *p;
register outage, inage;
register struct proc *inp;
int maxbad;
int tmp;
/*
* find user to swap in;
* of users ready, select one out longest
*/
loop:
spl6();
outage = -20000;
for (rp = &proc[0]; rp < (struct proc *)v.ve_proc; rp++)
if (rp->p_stat==SRUN && (rp->p_flag&SLOAD)==0 &&
rp->p_time > outage) {
p = rp;
outage = rp->p_time;
}
/*
* If there is no one there, wait.
*/
if (outage == -20000) {
runout++;
sleep((caddr_t)&runout, PSWP);
goto loop;
}
spl0();
/*
* See if there is core for that process;
* if so, swap it in.
*/
if (swapin(p))
goto loop;
/*
* none found.
* look around for core.
* Select the largest of those sleeping
* at bad priority; if none, select the oldest.
*/
spl6();
inp = p; /* we are trying to swap this guy in */
p = NULL;
maxbad = 0;
inage = 0;
for (rp = &proc[0]; rp < (struct proc *)v.ve_proc; rp++) {
if (rp->p_stat==SZOMB)
continue;
if (rp == inp)
continue;
if ((rp->p_flag&(SSPART|SLOCK)) == SSPART) {
p = rp;
maxbad = 1;
break;
}
if ((rp->p_flag&(SSYS|SLOCK|SLOAD))!=SLOAD)
continue;
if (rp->p_textp && rp->p_textp->x_flag&XLOCK)
continue;
if (rp->p_stat==SSLEEP || rp->p_stat==SSTOP) {
tmp = rp->p_pri - PZERO + rp->p_time;
if (maxbad < tmp) {
p = rp;
maxbad = tmp;
}
} else if (maxbad<=0 && rp->p_stat==SRUN) {
tmp = rp->p_time + rp->p_nice - NZERO;
if (tmp > inage) {
p = rp;
inage = tmp;
}
}
}
spl0();
/*
* Swap found user out if sleeping at bad pri,
* or if he has spent at least 2 seconds in core and
* the swapped-out process has spent at least 2 seconds out.
* Otherwise wait a bit and try again.
*/
if (maxbad>0 || (outage>=2 && inage>=2)) {
int left;
p->p_flag &= ~SLOAD;
left = p->p_size;
if (p->p_flag & SSPART)
left -= p->p_swsize;
if (left > SWAPSIZE)
left = SWAPSIZE;
xswap(p, -left, 0, -1);
goto loop;
}
spl6();
runin++;
sleep((caddr_t)&runin, PSWP);
goto loop;
}
/*
* Swap a process in.
*/
swapin(p)
register struct proc *p;
{
extern int Swapmap[], swaputl[], Swap2map[], swap2utl[];
register struct text *xp;
register int i, *ip;
int x, pt, tsize, tmem;
if (xp = p->p_textp) {
xlock(xp);
tsize = xp->x_size;
tmem = xp->x_ccount == 0 ? tsize : 0;
} else {
tsize = p->p_tsize;
tmem = 0;
}
if ((p->p_flag & SSPART) == 0) {
pt = (p->p_size - UPAGES + tsize + 127)/128; /* number of pages of page table */
if (memall(Swapmap,UPAGES + pt) == 0)
goto nomem;
for (i=UPAGES+pt; --i>=0; ) { /* map u-area & page tables */
Swapmap[i] |= PG_V|PG_KW;
mtpr(TBIS, swaputl + (128 * i));
}
if(memall(&swaputl[(UPAGES*128)+tsize-tmem],p->p_size-UPAGES+tmem)==0){
memfree(Swapmap, UPAGES + pt); /* free u-area & page tables */
goto nomem;
}
ip = &swaputl[UPAGES*128];
for (i=tmem; --i>=0;)
*ip++ |= PG_V|PG_URKR;
ip = &swaputl[(UPAGES*128) + tsize];
for (i=p->p_size-UPAGES; --i>=0; )
*ip++ |= PG_V|PG_UW;
((struct user *)swaputl)->u_pcb.pcb_szpt = pt; /* for swap I/O */
x = p->p_swaddr; /* disk address */
for (i=0; i<UPAGES; i++)
p->p_addr[i] = Swapmap[i];
for (i=0; i<pt; i++)
((struct user *)swaputl)->u_ptable[i] = (Swapmap+UPAGES)[i] & PG_PFNUM;
} else {
ptaccess(p, Swapmap, swaputl);
pt = ((struct user *)swaputl)->u_pcb.pcb_szpt;
if (memall(&swaputl[(UPAGES*128)+tsize-tmem], p->p_swsize-UPAGES
+ tmem) == 0)
goto nomem;
ip = &swaputl[(UPAGES*128)];
for (i=tmem; --i>=0; )
*ip++ |= PG_V|PG_URKR;
ip = &swaputl[(UPAGES*128)+tsize];
for (i=p->p_swsize-UPAGES; --i>=0; )
*ip++ |= PG_V|PG_UW;
x = p->p_swaddr;
}
if (xp) {
if (xp->x_ccount==0) {
xp->x_caddr = p; /* make link to loaded proc */
if ((xp->x_flag&XLOAD)==0)
swap(p,xp->x_daddr,0,xp->x_size,B_READ,0);
} else {
if (xp->x_caddr == 0)
panic("lost text");
ptaccess(xp->x_caddr, Swap2map, swap2utl);
bcopy(swap2utl + 128*UPAGES, swaputl + 128*UPAGES, 4*xp->x_size);
}
xp->x_ccount++;
p->p_flag |= STEXT;
xunlock(xp);
}
if ((p->p_flag & SSPART) == 0)
swap(p, x, tsize, p->p_size, B_READ, 1);
else {
swap(p, x+UPAGES, tsize, p->p_swsize-UPAGES, B_READ, 0);
p->p_flag &= ~SSPART;
}
((struct user *)swaputl)->u_pcb.pcb_szpt = pt; /* may have shrunk */
((struct user *)swaputl)->u_pcb.pcb_p1br = (int)&u + UPAGES*512
+ pt*512 - 0x800000; /* if page table shrunk, its moved */
mfree(swapmap, ctod(p->p_size), x);
x = (UPAGES*128) + tsize + p->p_size - UPAGES;
for (i=1; i<=((struct user *)swaputl)->u_ssize; i++) {
swaputl[((UPAGES+pt)*128) - i] = swaputl[x - i];
if ( x != (UPAGES+pt)*128 )
swaputl[x - i] = 0;
}
p->p_flag |= SLOAD;
p->p_time = 0;
return(1);
nomem:
if (xp)
xunlock(xp);
return(0);
}
/*
* put the current process on
* the Q of running processes and
* call the scheduler.
*/
qswtch()
{
sysinfo.qswitch++;
setrq(u.u_procp);
swtch();
}
/*
* This routine is called to reschedule the CPU.
* if the calling process is not in RUN state,
* arrangements for it to restart must have
* been made elsewhere, usually by calling via sleep.
* There is a race here. A process may become
* ready after it has been examined.
* In this case, idle() will be called and
* will return in at most 1HZ time.
* i.e. its not worth putting an spl() in.
*/
swtch()
{
register n;
register struct proc *p, *q, *pp, *pq;
/*
* If not the idle process, resume the idle process.
*/
sysinfo.pswitch++;
if (u.u_procp != &proc[0]) {
if (save(u.u_rsav)) {
return;
}
resume(proc[0].p_addr, u.u_qsav);
}
/*
* The first save returns nonzero when proc 0 is resumed
* by another process (above); then the second is not done
* and the process-search loop is entered.
*
* The first save returns 0 when swtch is called in proc 0
* from sched(). The second save returns 0 immediately, so
* in this case too the process-search loop is entered.
* Thus when proc 0 is awakened by being made runnable, it will
* find itself and resume itself at rsav, and return to sched().
*/
if (save(u.u_qsav)==0 && save(u.u_rsav))
return;
loop:
spl6();
runrun = 0;
pp = NULL;
q = NULL;
n = 128;
/*
* Search for highest-priority runnable process
*/
if (p = runq) do {
if ((p->p_flag&SLOAD) && p->p_pri <= n) {
pp = p;
pq = q;
n = p->p_pri;
}
q = p;
} while (p = p->p_link);
/*
* If no process is runnable, idle.
*/
p = pp;
if(p == NULL) {
sysinfo.idle++;
curpri = PIDLE;
idle();
goto loop;
}
q = pq;
if(q == NULL)
runq = p->p_link;
else
q->p_link = p->p_link;
curpri = n;
spl0();
/*
* The rsav (ssav) contents are interpreted in the new address space
*/
n = p->p_flag&SSWAP;
p->p_flag &= ~SSWAP;
resume(p->p_addr, n? u.u_ssav: u.u_rsav);
}
/*
* Create a new process-- the internal version of
* sys fork.
* It returns 1 in the new process, 0 in the old.
*/
newproc()
{
register struct proc *rpp, *rip;
register n;
struct proc *pend;
/*
* First, just locate a slot for a process
* and copy the useful info from this process into it.
* The panic "cannot happen" because fork has already
* checked for the existence of a slot.
*/
rpp = NULL;
retry:
mpid++;
if(mpid >= MAXPID) {
mpid = 0;
goto retry;
}
rip = &proc[0];
n = v.v_proc;
do {
if(rip->p_stat == NULL) {
if(rpp == NULL)
rpp = rip;
} else
pend = rip;
if (rip->p_pid==mpid)
goto retry;
rip++;
} while(--n);
if (rpp==NULL)
panic("no procs");
if (rpp > pend)
pend = rpp;
pend++;
v.ve_proc = (char *)pend;
/*
* make proc entry for new proc
*/
rip = u.u_procp;
rpp->p_stat = SRUN;
rpp->p_clktim = 0;
rpp->p_flag = SLOAD;
rpp->p_uid = rip->p_uid;
rpp->p_pgrp = rip->p_pgrp;
rpp->p_nice = rip->p_nice;
rpp->p_textp = rip->p_textp;
rpp->p_pid = mpid;
rpp->p_ppid = rip->p_pid;
rpp->p_time = 0;
rpp->p_cpu = 0;
rpp->p_pri = PUSER + rip->p_nice - NZERO;
rpp->p_tsize = rip->p_tsize;
/*
* make duplicate entries
* where needed
*/
for(n=0; n<NOFILE; n++)
if(u.u_ofile[n] != NULL)
u.u_ofile[n]->f_count++;
if (rpp->p_textp != NULL) {
rpp->p_textp->x_count++;
rpp->p_textp->x_ccount++;
rpp->p_flag |= STEXT;
}
u.u_cdir->i_count++;
if (u.u_rdir)
u.u_rdir->i_count++;
/*
* Partially simulate the environment
* of the new process so that when it is actually
* created (by copying) it will look right.
*/
u.u_procp = rpp;
rpp->p_size = rip->p_size;
/*
* When the resume is executed for the new process,
* here's where it will resume.
*/
if (save(u.u_ssav)) {
return(1);
}
/*
* If there is not enough core for the
* new process, swap out the current process to generate the
* copy.
*/
if (procdup(rpp) == NULL) {
rip->p_stat = SIDL;
for(n=UPAGES; --n>=0; )
rpp->p_addr[n] = rip->p_addr[n];
xswap(rpp, 0, 0, -1);
rip->p_stat = SRUN;
}
u.u_procp = rip;
setrq(rpp);
rpp->p_flag |= SSWAP;
return(0);
}
/*
* Change the size of the data+stack regions of the process.
* If the size is shrinking, it's easy-- just release the extra core.
* If it's growing, and there is core, just allocate it
* and copy the image, taking care to reset registers to account
* for the fact that the system's stack has moved.
* If there is no core, arrange for the process to be swapped
* out after adjusting the size requirement-- when it comes
* in, enough core will be allocated.
*
* After the expansion, the caller will take care of copying
* the user's stack towards or away from the data area.
*/
expand(change,region)
{
int n,p0,p1,oldss;
register struct proc *p;
register new;
register struct pt_entry *base;
p = u.u_procp;
n = p->p_size;
oldss = 0x200000 - u.u_pcb.pcb_p1lr;
p->p_size += change;
if (change == 0)
return;
p0 = mfpr(P0BR) + 4 * mfpr(P0LR);
p1 = mfpr(P1BR) + 4 * mfpr(P1LR);
/* see if we must add another page to the user page table */
if ((new = (p0 - p1 + 4*change)/4) > 0) {
new = ptexpand((new + 127)/128);
}
if (region == P0BR) {
base = (struct pt_entry *)mfpr(P0BR);
base += (p0 = mfpr(P0LR)) + (change > 0 ? 0 : change);
} else {
base = (struct pt_entry *)mfpr(P1BR);
base += (p1 = mfpr(P1LR)) - (change > 0 ? change : 0);
}
if (change > 0) {
new = memall(base,change);
} else
memfree(base,-change);
if (region == P0BR) {
mtpr(P0LR,p0 + change);
u.u_pcb.pcb_p0lr = p0 + change | 0x04000000;
} else {
mtpr(P1LR,p1 - change);
u.u_pcb.pcb_p1lr = p1 - change;
}
if (change < 0 || new > 0) {
new = 1;
if (change < 0) {
change = -change;
new = 0;
}
while (--change >= 0) {
*(int *)base |= PG_UW;
base->pg_v = new;
base++;
}
return;
}
/*
* When the resume is executed for the new process
* here's where it will resume.
*/
if (save(u.u_ssav)) {
return;
}
xswap(p, 1, n, oldss);
p->p_flag |= SSWAP;
qswtch();
/* no return */
}