Ausam/sys/ken/slp.c
#
/*
*/
#include "../defines.h"
#include "../param.h"
#include "../user.h"
#include "../proc.h"
#include "../text.h"
#include "../file.h"
#include "../inode.h"
#include "../buf.h"
#ifdef AUSAML
#include "../lnode.h"
#endif AUSAML
#include "../systm.h"
#ifdef ONCE
#include "../seg.h"
#endif ONCE
#ifdef MAX_PROC
struct proc *max_proc &proc[0];
#endif MAX_PROC
#ifdef PROCESS_QUEUES
struct proc *runq;
#endif PROCESS_QUEUES
/*
* 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<0 a signal cannot disturb the sleep;
* if pri>=0 signals will be processed.
* Callers of this routine must be prepared for
* premature return, and check that the reason for
* sleeping has gone away.
*/
#ifdef PROCESS_QUEUES
sleep(chan, pri)
{
register *rp, s;
#ifdef BETTER_PANIC
extern char *panicstr;
#endif BETTER_PANIC
s = PS->integ;
#ifdef BETTER_PANIC
if(panicstr)
{
/*
* panic has been called
* Refuse to reschedule
*/
spl0();
idle();
PS->integ = s;
return;
}
#endif BETTER_PANIC
rp = u.u_procp;
spl6();
rp->p_stat = SSLEEP;
rp->p_wchan = chan;
rp->p_pri = pri;
if(pri >= 0)
{
if(issig())
{
rp->p_wchan = 0;
rp->p_stat = SRUN;
spl0();
goto psig;
}
spl0();
swtch();
if(issig())
goto psig;
}
else
{
spl0();
swtch();
}
PS->integ = s;
return;
/*
* If priority was low (>=0) and
* there has been a signal,
* execute non-local goto to
* the qsav location.
* (see trap1/trap.c)
*/
psig:
aretu(u.u_qsav);
}
#else (PROCESS_QUEUES)
sleep(chan, pri)
{
register *rp, s;
s = PS->integ;
rp = u.u_procp;
if(pri >= 0) {
if(issig())
goto psig;
spl6();
rp->p_wchan = chan;
rp->p_stat = SWAIT;
rp->p_pri = pri;
spl0();
if(runin != 0) {
runin = 0;
setrun(&proc[0]); /* no need for wakeup */
}
swtch();
if(issig())
goto psig;
} else {
spl6();
rp->p_wchan = chan;
rp->p_stat = SSLEEP;
rp->p_pri = pri;
spl0();
swtch();
}
PS->integ = s;
return;
/*
* If priority was low (>=0) and
* there has been a signal,
* execute non-local goto to
* the qsav location.
* (see trap1/trap.c)
*/
psig:
aretu(u.u_qsav);
}
#endif PROCESS_QUEUES
/*
* Wake up all processes sleeping on chan.
*/
wakeup(chan)
{
register struct proc *p;
register c, i;
c = chan;
p = &proc[0];
#ifdef MAX_PROC
i = max_proc - p + 1;
#else
i = NPROC;
#endif MAX_PROC
do
{
#ifdef PROCESS_QUEUES
if(p->p_wchan == c && p->p_stat == SSLEEP)
#else
if(p->p_wchan == c)
#endif PROCESS_QUEUES
setrun(p);
p++;
}
while(--i);
}
#ifdef PROCESS_QUEUES
/*
* Put a process on the runable queue.
* The process is always put on the front of the queue,
* but round-robin scheduling is assured by swtch, which
* takes the last of eqivalent processes.
*/
setrq(p)
register struct proc *p;
{
register struct proc *q;
register s;
s = PS->integ;
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:
PS->integ = s;
}
#endif PROCESS_QUEUES
/*
* Set the process running;
* arrange for it to be swapped in if necessary.
* The rescheduling flag (runrun)
* is set if the priority is better
* than the currently running process.
*/
setrun(p)
register struct proc *p;
{
p->p_wchan = 0;
p->p_stat = SRUN;
#ifdef PROCESS_QUEUES
setrq(p);
#endif PROCESS_QUEUES
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++;
}
/*
* Set user priority.
*
* PRIORATE (the rate at which priority is aged
* for cpu-bound processes) can be found in param.h
*/
setpri(up)
register struct proc *up; /* fix000 */
{
register p;
p = (up->p_cpu & 0377)/PRIORATE;
p =+ PUSER+up->p_nice;
if(p > 127)
p = 127;
up->p_pri = p;
return(p); /* fix025 See trap.c */
}
/*
* 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 awakend when the
* core situation changes and in any event once per second.
*/
#ifndef PROCESS_QUEUES
sched()
{
struct proc *p1;
register struct proc *rp;
register a, n;
/*
* find user to swap in
* of users ready, select one out longest
*/
goto loop;
sloop:
spl6(); /* fix019 */
runin++;
sleep(&runin, PSWP);
spl0(); /* fix019 */
loop:
/* spl6(); /* fix019 */
n = -1;
#ifndef MAX_PROC
for(rp = &proc[0]; rp < &proc[NPROC]; rp++)
#else
for(rp = &proc[0]; rp <= max_proc; rp++)
#endif MAX_PROC
if(rp->p_stat==SRUN && (rp->p_flag&SLOAD)==0 && rp->p_time > n) {
p1 = rp;
n = rp->p_time;
}
if(n == -1) {
spl6(); /* fix019 */
runout++;
sleep(&runout, PSWP);
spl0(); /* fix019 */
goto loop;
}
/*
* see if there is core for that process
*/
/* spl0(); /* fix019 */
rp = p1;
a = rp->p_size;
if((rp=rp->p_textp) != NULL)
{
#ifdef LOWER_TEXT_SWAPS | SHARED_DATA
xlock(rp);
#endif LOWER_TEXT_SWAPS | SHARED_DATA
if(rp->x_ccount == 0)
a =+ rp->x_size;
}
if((a=malloc(coremap, a)) != NULL)
goto found2;
/*
* none found,
* look around for easy core
*/
/* spl6(); /* fix019 */
#ifndef MAX_PROC
for(rp = &proc[0]; rp < &proc[NPROC]; rp++)
#else
for(rp = &proc[0]; rp <= max_proc; rp++)
#endif MAX_PROC
if((rp->p_flag&(SSYS|SLOCK|SLOAD))==SLOAD &&
(rp->p_stat == SWAIT || rp->p_stat==SSTOP))
goto found1;
/*
* no easy core,
* if this process is deserving,
* look around for
* oldest process in core
*/
if(n < 3)
goto sloop;
n = -1;
#ifndef MAX_PROC
for(rp = &proc[0]; rp < &proc[NPROC]; rp++)
#else
for(rp = &proc[0]; rp <= max_proc; rp++)
#endif MAX_PROC
if((rp->p_flag&(SSYS|SLOCK|SLOAD))==SLOAD &&
(rp->p_stat==SRUN || rp->p_stat==SSLEEP) &&
rp->p_time > n) {
p1 = rp;
n = rp->p_time;
}
if(n < 2)
goto sloop;
rp = p1;
/*
* swap user out
*/
found1:
/* spl0(); /* fix019 */
rp->p_flag =& ~SLOAD;
xswap(rp, 1, 0);
goto loop;
/*
* swap user in
*/
found2:
if(rp != NULL) {
if(rp->x_ccount == 0) {
#ifdef LOWER_TEXT_SWAPS | SHARED_DATA
rp->x_flag =| TXTBUSY;
#endif LOWER_TEXT_SWAPS | SHARED_DATA
swap(rp->x_daddr, a, rp->x_size, B_READ);
rp->x_caddr = a;
a =+ rp->x_size;
#ifdef LOWER_TEXT_SWAPS | SHARED_DATA
xrele(rp);
#endif LOWER_TEXT_SWAPS | SHARED_DATA
}
rp->x_ccount++;
}
rp = p1;
swap(rp->p_addr, a, rp->p_size, B_READ);
mfree(swapmap, (rp->p_size+7)/8, rp->p_addr);
rp->p_addr = a;
rp->p_flag =| SLOAD;
rp->p_time = 0;
goto loop;
}
#else
sched()
{
register struct proc *rp, *p;
register outage, inage;
int swapri, a;
/*
* find user to swap in;
* of users ready, select one out longest
*/
loop:
spl6();
outage = -1;
for(rp = runq; rp != NULL; rp = rp->p_link)
{
if((rp->p_flag&SLOAD) == 0 && rp->p_time > outage)
{
p = rp;
outage = rp->p_time;
}
}
/*
* If there is no one there, wait.
*/
if(outage < 0)
{
runout++;
sleep(&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, so search for a candidate for swapping.
*/
/* spl6(); /* fix019 */
swapri = 0; /* this is a tunable value (max = 0) */
inage = 0;
#ifdef MAX_PROC
for(rp = &proc[0]; rp <= max_proc; rp++)
#else
for(rp = &proc[0]; rp < &proc[NPROC]; rp++)
#endif MAX_PROC
{
if((rp->p_flag&(SSYS|SLOCK|SLOAD)) != SLOAD)
continue;
#ifdef ZOMBIE
if(rp->p_stat == SZOMB)
continue;
#endif ZOMBIE
#ifdef SHARED_DATA | LOWER_TEXT_SWAPS
if(rp->p_textp && rp->p_textp->x_flag&TXTBUSY)
continue;
#endif SHARED_DATA | LOWER_TEXT_SWAPS
if(rp->p_stat == SSLEEP || rp->p_stat == SSTOP)
{
a = (rp->p_pri>>1) + rp->p_time;
if(a > swapri)
{
p = rp;
swapri = a;
}
}
else if(swapri <= 0 && rp->p_stat == SRUN && rp->p_time > inage)
{
p = rp;
inage = rp->p_time;
}
}
/* spl0(); /* fix019 */
/*
* Swap found user out if sleeping,
* or if he has spent at least 4 seconds in core and
* the swapped-out process has spent at 2 seconds out.
* Otherwise wait a bit and try again.
*/
if(swapri > 0 || (outage >= 2 && inage >= 4))
{
p->p_flag =& ~SLOAD;
xswap(p, 1, 0);
goto loop;
}
spl6();
runin++;
sleep(&runin, PSWP);
goto loop;
}
/*
* Swap a process in.
* Allocate data and possible text separately.
* It would be better to do largest first.
*/
swapin(p)
register struct proc *p;
{
register struct text *xp;
register int a;
int x;
if((a = malloc(coremap, p->p_size)) == NULL)
return(0);
if(xp = p->p_textp)
{
#ifdef SHARED_DATA | LOWER_TEXT_SWAPS
xlock(xp);
#endif SHARED_DATA | LOWER_TEXT_SWAPS
if(xp->x_ccount == 0)
{
if((x = malloc(coremap, xp->x_size)) == NULL)
{
mfree(coremap, p->p_size, a);
return(0);
}
#ifdef SHARED_DATA | LOWER_TEXT_SWAPS
xp->x_flag =| TXTBUSY;
#endif SHARED_DATA | LOWER_TEXT_SWAPS
xp->x_caddr = x;
swap(xp->x_daddr, x, xp->x_size, B_READ);
#ifdef SHARED_DATA | LOWER_TEXT_SWAPS
xrele(xp);
#endif SHARED_DATA | LOWER_TEXT_SWAPS
}
xp->x_ccount++;
}
swap(p->p_addr, a, p->p_size, B_READ);
mfree(swapmap, (p->p_size+7)/8, p->p_addr);
p->p_addr = a;
p->p_flag =| SLOAD;
p->p_time = 0;
return(1);
}
#endif PROCESS_QUEUES
#ifdef PROCESS_QUEUES
qswtch()
{
setrq(u.u_procp);
swtch();
}
#endif PROCESS_QUEUES
/*
* 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.
*/
#ifdef SWITCH_DISPLAY
int swi_count;
int lst_count;
#endif SWITCH_DISPLAY
#ifdef PROCESS_QUEUES
swtch()
{
register n;
register struct proc *p, *q;
static struct proc *pp, *pq;
#ifdef SWITCH_DISPLAY
swi_count++;
#endif SWITCH_DISPLAY
/*
* Remember stack of caller
* and switch to schedulers stack.
*/
savu(u.u_rsav);
retu(proc[0].p_addr);
loop:
spl6();
runrun = 0;
pp = NULL;
q = NULL;
n = 128;
/*
* Search for highest-priority runnable process
*/
for(p = runq; p != NULL; p = p->p_link)
{
if(p->p_flag&SLOAD)
{
/*
* Choose the last on queue
* with equivalent p_pri
*/
if(p->p_pri <= n)
{
pp = p;
pq = q;
n = p->p_pri;
}
}
q = p;
}
/*
* If no process is runnable, idle.
*/
p = pp;
if(p == NULL)
{
spl0();
idle();
goto loop;
}
q = pq;
if(q == NULL)
runq = p->p_link;
else
q->p_link = p->p_link;
curpri = n;
spl0();
/*
* Switch to stack of the new process and set up
* his segmentation registers.
*/
retu(p->p_addr);
sureg();
/*
* If the new process paused because it was
* swapped out, set the stack level to the last call
* to savu(u_ssav). This means that the return
* which is executed immediately after the call to aretu
* actually returns from the last routine which did
* the savu.
*/
if(p->p_flag&SSWAP)
{
p->p_flag =& ~SSWAP;
aretu(u.u_ssav);
}
/*
* The value returned here has many subtle implications.
* See the newproc comments.
*/
return(1);
}
#else
swtch()
{
static struct proc *p;
register i, n;
register struct proc *rp;
#ifdef BETTER_PANIC
extern char *panicstr;
if(panicstr)
{
/* panicing... refuse to reschedule */
idle();
return;
}
#endif BETTER_PANIC
#ifdef SWITCH_DISPLAY
swi_count++;
#endif SWITCH_DISPLAY
#ifndef MAX_PROC
if(p == NULL)
#endif MAX_PROC
#ifdef MAX_PROC
if( (p == NULL) || (p > max_proc) )
#endif MAX_PROC
p = &proc[0];
/*
* Remember stack of caller
*/
savu(u.u_rsav);
/*
* Switch to scheduler's stack
*/
retu(proc[0].p_addr);
loop:
runrun = 0;
rp = p;
p = NULL;
n = 128;
/*
* Search for highest-priority runnable process
*/
#ifndef MAX_PROC
i = NPROC;
#endif MAX_PROC
#ifdef MAX_PROC
i = max_proc - &proc[0] + 1;
#endif MAX_PROC
do {
rp++;
#ifndef MAX_PROC
if(rp >= &proc[NPROC])
#endif MAX_PROC
#ifdef MAX_PROC
if(rp > max_proc)
#endif MAX_PROC
rp = &proc[0];
if(rp->p_stat==SRUN && (rp->p_flag&SLOAD)!=0) {
if(rp->p_pri < n) {
p = rp;
n = rp->p_pri;
}
}
} while(--i);
/*
* If no process is runnable, idle.
*/
if(p == NULL) {
p = rp;
idle();
goto loop;
}
rp = p;
curpri = n;
/*
* Switch to stack of the new process and set up
* his segmentation registers.
*/
retu(rp->p_addr);
sureg();
/*
* If the new process paused because it was
* swapped out, set the stack level to the last call
* to savu(u_ssav). This means that the return
* which is executed immediately after the call to aretu
* actually returns from the last routine which did
* the savu.
*
* You are not expected to understand this.
*/
if(rp->p_flag&SSWAP) {
rp->p_flag =& ~SSWAP;
aretu(u.u_ssav);
}
/*
* The value returned here has many subtle implications.
* See the newproc comments.
*/
return(1);
}
#endif PROCESS_QUEUES
/*
* Create a new process-- the internal version of
* sys fork.
* It returns 1 in the new process.
* How this happens is rather hard to understand.
* The essential fact is that the new process is created
* in such a way that appears to have started executing
* in the same call to newproc as the parent;
* but in fact the code that runs is that of swtch.
* The subtle implication of the returned value of swtch
* (see above) is that this is the value that newproc's
* caller in the new process sees.
*/
newproc()
{
int a1, a2;
struct proc *p, *up;
register struct proc *rpp;
register *rip, n;
p = NULL;
/*
* 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.
*/
retry:
mpid++;
if(mpid < 0) {
mpid = 0;
goto retry;
}
for(rpp = &proc[0]; rpp < &proc[NPROC]; rpp++) {
if(rpp->p_stat == NULL && p==NULL)
p = rpp;
if (rpp->p_pid==mpid)
goto retry;
}
if ((rpp = p)==NULL)
panic("no procs");
/*
* make proc entry for new proc
*/
rip = u.u_procp;
up = rip;
rpp->p_stat = SRUN;
rpp->p_flag = SLOAD;
rpp->p_uid = rip->p_uid;
rpp->p_ttyp = rip->p_ttyp;
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 = rip->p_time; /* fix024 */
rpp->p_cpu = rip->p_cpu; /* fix024 */
#ifdef MEW_SLEEP
rpp->p_stl = 0;
#endif MEW_SLEEP
#ifdef AUSAML
rpp->p_lnode = rip->p_lnode;
#endif AUSAML
#ifdef TIME_LIMITS
rpp->p_rtl = rip->p_rtl;
#endif TIME_LIMITS
#ifdef IGNORE_SIGNALS
rpp->p_ignsig = rip->p_ignsig;
#endif IGNORE_SIGNALS
#ifdef MAX_PROC
if( rpp > max_proc )
{
max_proc = rpp;
# ifdef HIGH_PROC
if( rpp > high_proc )
{
high_proc = rpp;
high_nproc = rpp - &proc[0] + 1;
}
# endif HIGH_PROC
}
#endif MAX_PROC
/*
* make duplicate entries
* where needed
*/
for(rip = &u.u_ofile[0]; rip < &u.u_ofile[NOFILE];)
if((rpp = *rip++) != NULL)
rpp->f_count++;
if((rpp=up->p_textp) != NULL) {
rpp->x_count++;
rpp->x_ccount++;
}
u.u_cdir->i_count++;
/*
* Partially simulate the environment
* of the new process so that when it is actually
* created (by copying) it will look right.
*/
savu(u.u_rsav);
rpp = p;
u.u_procp = rpp;
rip = up;
n = rip->p_size;
a1 = rip->p_addr;
rpp->p_size = n;
a2 = malloc(coremap, n);
/*
* If there is not enough core for the
* new process, swap out the current process to generate the
* copy.
*/
if(a2 == NULL) {
rip->p_stat = SIDL;
rpp->p_addr = a1;
savu(u.u_ssav);
xswap(rpp, 0, 0);
rpp->p_flag =| SSWAP;
rip->p_stat = SRUN;
} else {
/*
* There is core, so just copy.
*/
rpp->p_addr = a2;
while(n--)
copyseg(a1++, a2++);
}
u.u_procp = rip;
#ifdef PROCESS_QUEUES
setrq(rpp);
#endif PROCESS_QUEUES
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.
* Because of the ssave and SSWAP flags, control will
* resume after the swap in swtch, which executes the return
* from this stack level.
*
* After the expansion, the caller will take care of copying
* the user's stack towards or away from the data area.
*/
expand(newsize)
{
int i, n;
register *p, a1, a2;
p = u.u_procp;
n = p->p_size;
p->p_size = newsize;
a1 = p->p_addr;
if(n >= newsize) {
mfree(coremap, n-newsize, a1+newsize);
return;
}
savu(u.u_rsav);
a2 = malloc(coremap, newsize);
if(a2 == NULL) {
savu(u.u_ssav);
xswap(p, 1, n);
p->p_flag =| SSWAP;
#ifdef PROCESS_QUEUES
qswtch();
#else
swtch();
#endif PROCESS_QUEUES
/* no return */
}
p->p_addr = a2;
for(i=0; i<n; i++)
copyseg(a1+i, a2++);
mfree(coremap, n, a1);
retu(p->p_addr);
sureg();
}
#ifdef ONCE
#include "../estabur.h"
#endif