#include "../h/param.h" #include "../h/systm.h" #include "../h/dir.h" #include "../h/user.h" #include "../h/proc.h" #include "../h/text.h" #include "../h/uba.h" #include "../h/map.h" #include "../h/file.h" #include "../h/inode.h" #include "../h/buf.h" #include "../h/page.h" #include "../h/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. */ 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(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: resume(u.u_procp->p_addr, u.u_qsav); } /* * Wake up all processes sleeping on chan. */ wakeup(chan) register caddr_t chan; { register struct proc *p; register i; int s; p = &proc[0]; i = NPROC; do { if(p->p_wchan==chan && p->p_stat!=SZOMB) { /* * 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(--i); } /* * when you are sure that it * is impossible to get the * 'proc on q' diagnostic, the * diagnostic loop can be removed. */ 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; { if (p->p_stat==0 || p->p_stat==SZOMB) panic("Running a dead proc"); p->p_wchan = 0; p->p_stat = SRUN; setrq(p); if(p->p_pri < curpri) runrun++; if(runout != 0 && (p->p_flag&SLOAD) == 0) { runout = 0; wakeup((caddr_t)&runout); } } /* * Set user priority. * The rescheduling flag (runrun) * is set if the priority is better * than the currently running process. */ setpri(pp) register struct proc *pp; { register p; p = (pp->p_cpu & 0377)/16; p += PUSER + pp->p_nice - NZERO; if(p > 127) p = 127; if(p < curpri) runrun++; pp->p_pri = p; return(p); } /* * 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, *inp; register outage, inage,memneed,memleft; int maxsize; extern int freemem; /* * find user to swap in; * of users ready, select one out longest */ loop: spl6(); outage = -20000; for (rp = &proc[0]; rp < &proc[NPROC]; rp++) if (rp->p_stat==SRUN && (rp->p_flag&SLOAD)==0 && rp->p_time - (rp->p_nice-NZERO)*8 > outage) { p = rp; outage = rp->p_time - (rp->p_nice-NZERO)*8; } /* * 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; maxsize = -1; inage = -1; for (rp = &proc[0]; rp < &proc[NPROC]; rp++) { if (rp->p_stat==SZOMB) continue; if (rp == inp) continue; if (rp->p_flag&SSPART) { /* we have found one partially swapped */ p = rp; maxsize = 0; break; } if ((rp->p_flag&(SSYS|SLOCK|SULOCK|SLOAD))!=SLOAD) continue; if (rp->p_textp && rp->p_textp->x_flag&XLOCK) continue; if (rp->p_stat==SSLEEP&&rp->p_pri>=PZERO || rp->p_stat==SSTOP) { if (maxsize < rp->p_size) { p = rp; maxsize = rp->p_size; } } else if (maxsize<0 && (rp->p_stat==SRUN||rp->p_stat==SSLEEP)) { if (rp->p_time+rp->p_nice-NZERO > inage) { p = rp; inage = rp->p_time+rp->p_nice-NZERO; } } } 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 3 seconds out. * Otherwise wait a bit and try again. */ if (maxsize>=0 || (outage>=3 && inage>=2)) { p->p_flag &= ~SLOAD; if (inp->p_flag & SSPART) memneed = inp->p_swsize - UPAGES; else memneed = inp->p_size; if (inp->p_textp) if (inp->p_textp->x_ccount == 0) memneed += inp->p_textp->x_size; memneed -= freemem; /* amount we must swap out */ memneed += 11 + SWAPSIZE - (memneed+11)%SWAPSIZE; memleft = p->p_size; if (p->p_flag & SSPART) memleft -= p->p_swsize; /* core left */ if ((memleft - memneed) < 2*SWAPSIZE) memneed = memleft; /* take it all */ xswap(p,-memneed,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; struct proc *prc; 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) /* must find the text */ for (prc= &proc[0]; prc<&proc[NPROC]; prc++) if ((prc->p_flag&SLOAD) && (prc->p_textp==xp) && (prc->p_flag&SLOCK)==0 && (prc!=p)){ xp->x_caddr = prc; /* found it */ break; } 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++; 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() { 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. */ 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 */ for(p=runq; p!=NULL; p=p->p_link) { if((p->p_stat==SRUN) && (p->p_flag&SLOAD)) { 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) { 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 *p, *up; register struct proc *rpp, *rip; register 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 >= 30000) { 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 || rpp->p_pgrp==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_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_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(up->p_textp != NULL) { up->p_textp->x_count++; up->p_textp->x_ccount++; } 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. */ rpp = p; u.u_procp = rpp; rip = up; 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 */ }