/* @(#)slp.c 1.8 */ #include "sys/param.h" #include "sys/config.h" #include "sys/mmu.h" #include "sys/types.h" #include "sys/sysmacros.h" #include "sys/dir.h" #include "sys/signal.h" #include "sys/user.h" #include "sys/proc.h" #include "sys/context.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/ipc.h" #include "sys/shm.h" #include "sys/errno.h" #include "sys/scat.h" typedef int mem_t; #define NHSQUE 64 /* must be power of 2 */ #define sqhash(X) (&hsque[((int)(X) >> 3) & (NHSQUE-1)]) struct proc *hsque[NHSQUE]; char runin, runout, runrun, curpri; struct proc *curproc, *runq; /* * sleep according to a cpu adjusted priority */ asleep(chan, pri) caddr_t chan; { return(sleep(chan, pri + ((u.u_procp->p_cpu&0xFF) >> 5))); } /* * 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 10 sleep(chan, disp) caddr_t chan; { register struct proc *rp = u.u_procp; register struct proc **q = sqhash(chan); register s; s = splhi(); if (panicstr) { SPL0(); splx(s); return(0); } rp->p_stat = SSLEEP; rp->p_wchan = chan; rp->p_link = *q; *q = rp; if (rp->p_time > TZERO) rp->p_time = TZERO; if ((rp->p_pri = (disp&PMASK)) > PZERO) { if (rp->p_sig && issig()) { rp->p_wchan = 0; rp->p_stat = SRUN; *q = rp->p_link; SPL0(); goto psig; } SPL0(); if (runin != 0) { runin = 0; wakeup((caddr_t)&runin); } swtch(); if (rp->p_sig && issig()) goto psig; } else { SPL0(); swtch(); } splx(s); return(0); /* * If priority was low (>PZERO) and there has been a signal, * if PCATCH is set, return 1, else * execute non-local goto to the qsav location. */ psig: splx(s); if (disp&PCATCH) return(1); #ifdef NONSCATLOAD resume(u.u_procp->p_addr, u.u_qsav); #else resume(ixtoc(u.u_procp->p_scat), u.u_qsav); #endif /* NOTREACHED */ } /* * Wake up all processes sleeping on chan. */ wakeup(chan) register caddr_t chan; { register struct proc *p; register struct proc **q; register s; s = splhi(); for (q = sqhash(chan); p = *q; ) if (p->p_wchan==chan && p->p_stat==SSLEEP) { p->p_stat = SRUN; p->p_wchan = 0; /* take off sleep queue, put on run queue */ *q = p->p_link; p->p_link = runq; runq = p; if (!(p->p_flag&SLOAD)) { p->p_time = 0; /* defer setrun to avoid breaking link chain */ if (runout > 0) runout = -runout; } else if (p->p_pri < curpri) runrun++; } else q = &p->p_link; if (runout < 0) { runout = 0; setrun(&proc[0]); } splx(s); } setrq(p) register struct proc *p; { register struct proc *q; register s; s = splhi(); 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; { register struct proc **q; register s; s = splhi(); if (p->p_stat == SSLEEP) { /* take off sleep queue */ for (q = sqhash(p->p_wchan); *q != p; q = &(*q)->p_link) ; *q = p->p_link; p->p_wchan = 0; } else if (p->p_stat == SRUN) { /* already on run queue - just return */ splx(s); return; } /* put on run queue */ p->p_stat = SRUN; p->p_link = runq; runq = p; if (!(p->p_flag&SLOAD)) { p->p_time = 0; if (runout > 0) { runout = 0; setrun(&proc[0]); } } else if (p->p_pri < curpri) runrun++; splx(s); } /* * 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 * memory (by swapping out or otherwise) to fit the * selected swapped process. It is awakened when the * memory situation changes and in any case once per second. */ sched() { register struct proc *rp, *p; register outage, inage; int maxbad; int tmp; /* * find user to swap in; * of users ready, select one out longest */ loop: SPL6(); outage = -20000; #ifdef NONSCATLOAD 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; } #else for (rp = &proc[0]; rp < (struct proc *)v.ve_proc; rp++) if ((rp->p_flag&(SSWAPIT|SLOAD)) == (SSWAPIT|SLOAD)) { p = rp; SPL0(); goto swapit; } else if (rp->p_stat==SRUN && (rp->p_flag&SLOAD) == 0 && rp->p_time > outage) { p = rp; outage = rp->p_time; } #endif /* * If there is no one there, wait. */ if (outage == -20000) { runout++; (void) sleep((caddr_t)&runout, PSWP); goto loop; } SPL0(); /* * See if there is memory for that process; * if so, swap it in. */ if (swapin(p)) goto loop; /* * none found. * look around for memory. * Select the largest of those sleeping * at bad priority; if none, select the oldest. */ SPL6(); p = NULL; maxbad = 0; inage = 0; for (rp = &proc[0]; rp < (struct proc *)v.ve_proc; rp++) { if (rp->p_stat==SZOMB || (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 memory 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)) { #ifndef NONSCATLOAD swapit: #endif p->p_flag &= ~SLOAD; xswap(p, 1, 0); goto loop; } SPL6(); runin++; (void) sleep((caddr_t)&runin, PSWP); goto loop; } /* * Swap a process in. */ #ifdef NONSCATLOAD swapin(p) register struct proc *p; { register struct text *xp; register int a, x; int ta; if ((a = malloc(coremap, p->p_size)) == NULL) return(0); if (xp = p->p_textp) { xlock(xp); if (!xmlink(xp) && xp->x_ccount==0) { if ((x = malloc(coremap, xp->x_size)) == NULL) { mfree(coremap, p->p_size, a); if ((x = malloc(coremap, xp->x_size)) == NULL) { xunlock(xp); return(0); } if ((a = malloc(coremap, p->p_size)) == NULL) { mfree(coremap, xp->x_size, x); xunlock(xp); return(0); } } xp->x_caddr = x; if ((xp->x_flag&XLOAD)==0) swap(xp->x_daddr, x, xp->x_size, B_READ); } xp->x_ccount++; xunlock(xp); } if (p->p_xaddr[0]) { ta = a; for (x=0; x < NSCATSWAP; x++) { if (p->p_xaddr[x] == 0) continue; swap(p->p_xaddr[x], a, p->p_xsize[x], B_READ); mfree(swapmap, ctod(p->p_xsize[x]), (int)p->p_xaddr[x]); a += p->p_xsize[x]; p->p_xaddr[x] = 0; } p->p_addr = ta; } else { swap((daddr_t)p->p_dkaddr, a, p->p_size, B_READ); mfree(swapmap, ctod(p->p_size), (int)p->p_dkaddr); p->p_addr = a; } cxrelse(p->p_context); p->p_flag |= SLOAD; p->p_time = 0; return(1); } #else swapin(p) register struct proc *p; { register struct text *xp; register int a, x; int ta; if (p->p_flag&SCONTIG) { if ((a = cmemalloc(p->p_size)) == NULL) return(0); } else if ((a = memalloc(p->p_size)) == NULL) return(0); if (xp = p->p_textp) { xlock(xp); if (!xmlink(xp) && xp->x_ccount==0) { if ((x = memalloc(xp->x_size)) == NULL) { memfree(a); xunlock(xp); return(0); } xp->x_scat = x; if ((xp->x_flag&XLOAD)==0) (void) swap(xp->x_daddr, x, xp->x_size, B_READ); } xp->x_ccount++; xunlock(xp); } p->p_flag |= SNOMMU; /* swapping in, do not set mmu registers */ if (p->p_xaddr[0]) { ta = a; for (x=0; x < NSCATSWAP; x++) { if (p->p_xaddr[x] == 0) continue; a = swap(p->p_xaddr[x], a, p->p_xsize[x], B_READ); mfree(swapmap, ctod(p->p_xsize[x]), (int)p->p_xaddr[x]); p->p_xaddr[x] = 0; } p->p_scat = ta; } else { (void) swap((daddr_t)p->p_dkaddr, a, p->p_size, B_READ); mfree(swapmap, ctod(p->p_size), (int)p->p_dkaddr); p->p_scat = a; } p->p_flag &= ~SNOMMU; p->p_addr = ixtoc(p->p_scat); cxrelse(p->p_context); p->p_flag |= SLOAD; p->p_time = 0; return(1); } #endif /* * 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; #ifdef mc68881 /* MC68881 floating-point coprocessor */ extern short fp881; /* is there an MC68881? */ #endif mc68881 /* * If not the idle process, resume the idle process. */ sysinfo.pswitch++; if (u.u_procp != &proc[0]) { if (save(u.u_rsav)) { sureg(); return; } #ifdef FLOAT /* sky floating point board */ if (u.u_fpinuse && u.u_fpsaved==0) { savfp(); u.u_fpsaved = 1; } #endif #ifdef mc68881 /* MC68881 floating-point coprocessor */ if (fp881) fpsave(); #endif mc68881 #ifdef NONSCATLOAD resume(proc[0].p_addr, u.u_qsav); #else resume(ixtoc(proc[0].p_scat), u.u_qsav); #endif } /* * 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; /* * Search for highest-priority runnable process */ if (p = runq) { q = NULL; pp = NULL; n = 128; 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); } else goto cont; /* * If no process is runnable, idle. */ if (pp == NULL) { cont: curpri = PIDLE; curproc = &proc[0]; idle(); goto loop; } p = pp; q = pq; if (q == NULL) runq = p->p_link; else q->p_link = p->p_link; curpri = n; curproc = p; SPL0(); /* * The rsav (ssav) contents are interpreted in the new address space */ n = p->p_flag&SSWAP; p->p_flag &= ~SSWAP; #ifdef NONSCATLOAD resume(p->p_addr, n? u.u_ssav: u.u_rsav); #else resume(ixtoc(p->p_scat), n? u.u_ssav: u.u_rsav); #endif } /* * Create a new process-- the internal version of * sys fork. * It returns 1 in the new process, 0 in the old. */ newproc(i) { register struct proc *rpp, *rip; register struct user *up; register n; register a; struct proc *pend; static mpid; /* * 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. */ up = &u; rpp = NULL; retry: mpid++; if (mpid >= MAXPID) { mpid = 0; goto retry; } rip = &proc[0]; n = (struct proc *)v.ve_proc - rip; a = 0; do { if (rip->p_stat == NULL) { if (rpp == NULL) rpp = rip; continue; } if (rip->p_pid==mpid) goto retry; if (rip->p_uid == up->u_ruid) a++; pend = rip; } while(rip++, --n); if (rpp==NULL) { if ((struct proc *)v.ve_proc >= &proc[(short)v.v_proc]) { if (i) { syserr.procovf++; up->u_error = EAGAIN; return(-1); } else panic("no procs"); } rpp = (struct proc *)v.ve_proc; } if (rpp > pend) pend = rpp; pend++; #ifdef lint v.ve_proc = pend; #else v.ve_proc = (char *)pend; #endif if (up->u_uid && up->u_ruid) { if (rpp == &proc[(short)(v.v_proc-1)] || a > v.v_maxup) { up->u_error = EAGAIN; return(-1); } } /* * make proc entry for new proc */ rip = up->u_procp; rpp->p_stat = SRUN; rpp->p_clktim = 0; rpp->p_flag = SLOAD; rpp->p_uid = rip->p_uid; rpp->p_suid = rip->p_suid; 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 = rip->p_cpu; rpp->p_sigign = rip->p_sigign; rpp->p_pri = PUSER + rip->p_nice - NZERO; #ifndef NONSCATLOAD rpp->p_scat = rip->p_scat; #endif rpp->p_addr = rip->p_addr; rpp->p_size = rip->p_size; /* * make duplicate entries * where needed */ for(n=0; nu_ofile[n] != NULL) up->u_ofile[n]->f_count++; if (rpp->p_textp != NULL) { rpp->p_textp->x_count++; rpp->p_textp->x_ccount++; } up->u_cdir->i_count++; if (up->u_rdir) up->u_rdir->i_count++; shmfork(rpp, rip); /* * Partially simulate the environment * of the new process so that when it is actually * created (by copying) it will look right. */ up->u_procp = rpp; curproc = rpp; /* * When the resume is executed for the new process, * here's where it will resume. */ if (save(up->u_ssav)) { sureg(); return(1); } /* * If there is not enough memory for the * new process, swap out the current process to generate the * copy. */ if (procdup(rpp) == NULL) { rip->p_stat = SIDL; xswap(rpp, 0, 0); rip->p_stat = SRUN; } up->u_procp = rip; curproc = rip; if (rip != &proc[0]) /* only do if not scheduler */ sureg(); setrq(rpp); rpp->p_flag |= SSWAP; up->u_rval1 = rpp->p_pid; /* parent returns pid of child */ 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 memory, arrange for the process to be swapped * out after adjusting the size requirement-- when it comes * in, enough memory will be allocated. * * After the expansion, the caller will take care of copying * the user's stack towards or away from the data area. */ #ifdef NONSCATLOAD expand(newsize) register newsize; { register struct proc *p; register a1, a2; register i, n; p = u.u_procp; n = p->p_size; p->p_size = newsize; if (n == newsize) return; a1 = p->p_addr; if (n >= newsize) { #ifdef EXPANDTRACE printf("expand:shrinking process by %d clicks\n", n-newsize); #endif mfree(coremap, (mem_t)(n-newsize), (mem_t)(a1+newsize)); return; } if (save(u.u_ssav)) { sureg(); return; } /* * See if can just expand in place */ loop: a2 = mallocat(coremap, newsize-n, (mem_t)(a1+n)); if (a2 != NULL) { #ifdef EXPANDTRACE printf("expanding in place by %d clicks at click %d\n", newsize-n, a1+n); #endif cxrelse(p->p_context); sureg(); return; } /* * Will we be releasing shared text space anyway. * If so, then release it now and try in place * expansion again. */ if ((a2 = domall(coremap, (mem_t)newsize)) == NULL) if (xmrelse()) goto loop; if (a2 == NULL && (a2 = malloc(coremap, (mem_t)newsize)) == NULL) { #ifdef EXPANDTRACE printf("expand:calling xswap\n"); #endif xswap(p, 1, n); p->p_flag |= SSWAP; qswtch(); /* no return */ } p->p_addr = a2; for(i=0; ip_context); resume((mem_t)a2, u.u_ssav); } #else expand(newsize) register newsize; { register struct scatter *s; register struct proc *p; register a1, a2; register i, n; int t; p = u.u_procp; n = p->p_size; p->p_size = newsize; if (n == newsize) return; s = scatmap; a1 = p->p_scat; if (n >= newsize) { /* * shrink memory */ for (i=1; ip_flag |= SSWAP; qswtch(); /* no return */ } #endif #ifndef NONSCATLOAD checkscat(s) char *s; { register struct proc *p; register struct text *xp; register i; i = countscat(scatfreelist.sc_index); printf("%s nscatfree=%d actual=%d\n", s, nscatfree, i); if (nscatfree < 30) { printf(" freelist chain is "); dumpscat(scatfreelist.sc_index); } for (p = &proc[0]; p < (struct proc *)v.ve_proc; p++) { if (p->p_stat==0) continue; xp = p->p_textp; printf(" pid=%d %d used (%d text)\n", p->p_pid, countscat(p->p_scat), xp ? countscat(xp->x_scat) : 0); dumpscat(p->p_scat); if (xp) { dumpscat(xp->x_scat); } } } dumpscat(a1) register a1; { register struct scatter *s; s = scatmap; printf(" "); while (a1 != SCATEND) { printf(" %d,%x", a1, ixtoc(a1)); a1 = s[a1].sc_index; } printf("\n"); } countscat(a1) register a1; { register struct scatter *s; register i; i = 0; s = scatmap; while (a1 != SCATEND) { a1 = s[a1].sc_index; i++; } return(i); } #endif