Annotation of 43BSDReno/sys/kern/kern_synch.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (c) 1982, 1986, 1990 Regents of the University of California.
                      3:  * All rights reserved.  The Berkeley software License Agreement
                      4:  * specifies the terms and conditions for redistribution.
                      5:  *
                      6:  *     @(#)kern_synch.c        7.11 (Berkeley) 4/3/90
                      7:  */
                      8: 
                      9: #include "machine/pte.h"
                     10: #include "machine/psl.h"
                     11: #include "machine/mtpr.h"
                     12: 
                     13: #include "param.h"
                     14: #include "systm.h"
                     15: #include "user.h"
                     16: #include "proc.h"
                     17: #include "vm.h"
                     18: #include "kernel.h"
                     19: #include "buf.h"
                     20: 
                     21: /*
                     22:  * Force switch among equal priority processes every 100ms.
                     23:  */
                     24: roundrobin()
                     25: {
                     26: 
                     27:        runrun++;
                     28:        aston();
                     29:        timeout(roundrobin, (caddr_t)0, hz / 10);
                     30: }
                     31: 
                     32: /*
                     33:  * constants for digital decay and forget
                     34:  *     90% of (p_cpu) usage in 5*loadav time
                     35:  *     95% of (p_pctcpu) usage in 60 seconds (load insensitive)
                     36:  *          Note that, as ps(1) mentions, this can let percentages
                     37:  *          total over 100% (I've seen 137.9% for 3 processes).
                     38:  *
                     39:  * Note that hardclock updates p_cpu and p_cpticks independently.
                     40:  *
                     41:  * We wish to decay away 90% of p_cpu in (5 * loadavg) seconds.
                     42:  * That is, the system wants to compute a value of decay such
                     43:  * that the following for loop:
                     44:  *     for (i = 0; i < (5 * loadavg); i++)
                     45:  *             p_cpu *= decay;
                     46:  * will compute
                     47:  *     p_cpu *= 0.1;
                     48:  * for all values of loadavg:
                     49:  *
                     50:  * Mathematically this loop can be expressed by saying:
                     51:  *     decay ** (5 * loadavg) ~= .1
                     52:  *
                     53:  * The system computes decay as:
                     54:  *     decay = (2 * loadavg) / (2 * loadavg + 1)
                     55:  *
                     56:  * We wish to prove that the system's computation of decay
                     57:  * will always fulfill the equation:
                     58:  *     decay ** (5 * loadavg) ~= .1
                     59:  *
                     60:  * If we compute b as:
                     61:  *     b = 2 * loadavg
                     62:  * then
                     63:  *     decay = b / (b + 1)
                     64:  *
                     65:  * We now need to prove two things:
                     66:  *     1) Given factor ** (5 * loadavg) ~= .1, prove factor == b/(b+1)
                     67:  *     2) Given b/(b+1) ** power ~= .1, prove power == (5 * loadavg)
                     68:  *     
                     69:  * Facts:
                     70:  *         For x close to zero, exp(x) =~ 1 + x, since
                     71:  *              exp(x) = 0! + x**1/1! + x**2/2! + ... .
                     72:  *              therefore exp(-1/b) =~ 1 - (1/b) = (b-1)/b.
                     73:  *         For x close to zero, ln(1+x) =~ x, since
                     74:  *              ln(1+x) = x - x**2/2 + x**3/3 - ...     -1 < x < 1
                     75:  *              therefore ln(b/(b+1)) = ln(1 - 1/(b+1)) =~ -1/(b+1).
                     76:  *         ln(.1) =~ -2.30
                     77:  *
                     78:  * Proof of (1):
                     79:  *    Solve (factor)**(power) =~ .1 given power (5*loadav):
                     80:  *     solving for factor,
                     81:  *      ln(factor) =~ (-2.30/5*loadav), or
                     82:  *      factor =~ exp(-1/((5/2.30)*loadav) =~ exp(-1/(2*loadav)) =
                     83:  *          exp(-1/b) =~ (b-1)/b =~ b/(b+1).                    QED
                     84:  *
                     85:  * Proof of (2):
                     86:  *    Solve (factor)**(power) =~ .1 given factor == (b/(b+1)):
                     87:  *     solving for power,
                     88:  *      power*ln(b/(b+1)) =~ -2.30, or
                     89:  *      power =~ 2.3 * (b + 1) = 4.6*loadav + 2.3 =~ 5*loadav.  QED
                     90:  *
                     91:  * Actual power values for the implemented algorithm are as follows:
                     92:  *      loadav: 1       2       3       4
                     93:  *      power:  5.68    10.32   14.94   19.55
                     94:  */
                     95: 
                     96: /* calculations for digital decay to forget 90% of usage in 5*loadav sec */
                     97: #define        get_b(loadav)           (2 * (loadav))
                     98: #define        get_pcpu(b, cpu)        (((b) * ((cpu) & 0377)) / ((b) + FSCALE))
                     99: 
                    100: /* decay 95% of `p_pctcpu' in 60 seconds; see CCPU_SHIFT before changing */
                    101: fixpt_t        ccpu = 0.95122942450071400909 * FSCALE;         /* exp(-1/20) */
                    102: 
                    103: /*
                    104:  * If `ccpu' is not equal to `exp(-1/20)' and you still want to use the
                    105:  * faster/more-accurate formula, you'll have to estimate CCPU_SHIFT below
                    106:  * and possibly adjust FSHIFT in "param.h" so that (FSHIFT >= CCPU_SHIFT).
                    107:  *
                    108:  * To estimate CCPU_SHIFT for exp(-1/20), the following formula was used:
                    109:  *     1 - exp(-1/20) ~= 0.0487 ~= 0.0488 == 1 (fixed pt, *11* bits).
                    110:  *
                    111:  * If you dont want to bother with the faster/more-accurate formula, you
                    112:  * can set CCPU_SHIFT to (FSHIFT + 1) which will use a slower/less-accurate
                    113:  * (more general) method of calculating the %age of CPU used by a process.
                    114:  */
                    115: #define        CCPU_SHIFT      11
                    116: 
                    117: /*
                    118:  * Recompute process priorities, once a second
                    119:  */
                    120: schedcpu()
                    121: {
                    122:        register fixpt_t b = get_b(averunnable[0]);
                    123:        register struct proc *p;
                    124:        register int s, a;
                    125: 
                    126:        wakeup((caddr_t)&lbolt);
                    127:        for (p = allproc; p != NULL; p = p->p_nxt) {
                    128:                if (p->p_time != 127)
                    129:                        p->p_time++;
                    130:                if (p->p_stat==SSLEEP || p->p_stat==SSTOP)
                    131:                        if (p->p_slptime != 127)
                    132:                                p->p_slptime++;
                    133:                p->p_pctcpu = (p->p_pctcpu * ccpu) >> FSHIFT;
                    134:                /*
                    135:                 * If the process has slept the entire second,
                    136:                 * stop recalculating its priority until it wakes up.
                    137:                 */
                    138:                if (p->p_slptime > 1)
                    139:                        continue;
                    140:                /*
                    141:                 * p_pctcpu is only for ps.
                    142:                 */
                    143: #if    (FSHIFT >= CCPU_SHIFT)
                    144:                p->p_pctcpu += (hz == 100)?
                    145:                        ((fixpt_t) p->p_cpticks) << (FSHIFT - CCPU_SHIFT):
                    146:                        100 * (((fixpt_t) p->p_cpticks)
                    147:                                << (FSHIFT - CCPU_SHIFT)) / hz;
                    148: #else
                    149:                p->p_pctcpu += ((FSCALE - ccpu) *
                    150:                        (p->p_cpticks * FSCALE / hz)) >> FSHIFT;
                    151: #endif
                    152:                p->p_cpticks = 0;
                    153:                a = (int) get_pcpu(b, p->p_cpu) + p->p_nice;
                    154:                if (a < 0)
                    155:                        a = 0;
                    156:                if (a > 255)
                    157:                        a = 255;
                    158:                p->p_cpu = a;
                    159:                (void) setpri(p);
                    160:                s = splhigh();  /* prevent state changes */
                    161:                if (p->p_pri >= PUSER) {
                    162: #define        PPQ     (128 / NQS)
                    163:                        if ((p != u.u_procp || noproc) &&
                    164:                            p->p_stat == SRUN &&
                    165:                            (p->p_flag & SLOAD) &&
                    166:                            (p->p_pri / PPQ) != (p->p_usrpri / PPQ)) {
                    167:                                remrq(p);
                    168:                                p->p_pri = p->p_usrpri;
                    169:                                setrq(p);
                    170:                        } else
                    171:                                p->p_pri = p->p_usrpri;
                    172:                }
                    173:                splx(s);
                    174:        }
                    175:        vmmeter();
                    176:        if (runin!=0) {
                    177:                runin = 0;
                    178:                wakeup((caddr_t)&runin);
                    179:        }
                    180:        if (bclnlist != NULL)
                    181:                wakeup((caddr_t)&proc[2]);
                    182:        timeout(schedcpu, (caddr_t)0, hz);
                    183: }
                    184: 
                    185: /*
                    186:  * Recalculate the priority of a process after it has slept for a while.
                    187:  */
                    188: updatepri(p)
                    189:        register struct proc *p;
                    190: {
                    191:        register int a = p->p_cpu & 0377;
                    192:        register fixpt_t b = get_b(averunnable[0]);
                    193: 
                    194:        p->p_slptime--;         /* the first time was done in schedcpu */
                    195:        while (a && --p->p_slptime)
                    196:                a = (int) get_pcpu(b, a) /* + p->p_nice */;
                    197:        p->p_slptime = 0;
                    198:        if (a < 0)
                    199:                a = 0;
                    200:        if (a > 255)
                    201:                a = 255;
                    202:        p->p_cpu = a;
                    203:        (void) setpri(p);
                    204: }
                    205: 
                    206: #define SQSIZE 0100    /* Must be power of 2 */
                    207: #define HASH(x)        (( (int) x >> 5) & (SQSIZE-1))
                    208: struct slpque {
                    209:        struct proc *sq_head;
                    210:        struct proc **sq_tailp;
                    211: } slpque[SQSIZE];
                    212: 
                    213: /*
                    214:  * General sleep call.
                    215:  * Suspends current process until a wakeup is made on chan.
                    216:  * The process will then be made runnable with priority pri.
                    217:  * Sleeps at most timo/hz seconds (0 means no timeout).
                    218:  * If pri includes PCATCH flag, signals are checked
                    219:  * before and after sleeping, else signals are not checked.
                    220:  * Returns 0 if awakened, EWOULDBLOCK if the timeout expires.
                    221:  * If PCATCH is set and a signal needs to be delivered,
                    222:  * ERESTART is returned if the current system call should be restarted
                    223:  * if possible, and EINTR is returned if the system call should
                    224:  * be interrupted by the signal (return EINTR).
                    225:  */
                    226: tsleep(chan, pri, wmesg, timo)
                    227:        caddr_t chan;
                    228:        int pri;
                    229:        char *wmesg;
                    230:        int timo;
                    231: {
                    232:        register struct proc *rp;
                    233:        register struct slpque *qp;
                    234:        register s;
                    235:        int sig, catch = pri & PCATCH;
                    236:        extern int cold;
                    237:        int endtsleep();
                    238: 
                    239:        rp = u.u_procp;
                    240:        s = splhigh();
                    241:        if (cold || panicstr) {
                    242:                /*
                    243:                 * After a panic, or during autoconfiguration,
                    244:                 * just give interrupts a chance, then just return;
                    245:                 * don't run any other procs or panic below,
                    246:                 * in case this is the idle process and already asleep.
                    247:                 */
                    248:                (void) spl0();
                    249:                splx(s);
                    250:                return (0);
                    251:        }
                    252: #ifdef DIAGNOSTIC
                    253:        if (chan == 0 || rp->p_stat != SRUN || rp->p_rlink)
                    254:                panic("tsleep");
                    255: #endif
                    256:        rp->p_wchan = chan;
                    257:        rp->p_wmesg = wmesg;
                    258:        rp->p_slptime = 0;
                    259:        rp->p_pri = pri & PRIMASK;
                    260:        qp = &slpque[HASH(chan)];
                    261:        if (qp->sq_head == 0)
                    262:                qp->sq_head = rp;
                    263:        else
                    264:                *qp->sq_tailp = rp;
                    265:        *(qp->sq_tailp = &rp->p_link) = 0;
                    266:        /*
                    267:         * If we stop in CURSIG/issig(), wakeup may already
                    268:         * have happened when we return.
                    269:         * rp->p_wchan will then be 0.
                    270:         */
                    271:        if (catch) {
                    272:                if (sig = CURSIG(rp)) {
                    273:                        if (rp->p_wchan)
                    274:                                unsleep(rp);
                    275:                        rp->p_stat = SRUN;
                    276:                        splx(s);
                    277:                        if (u.u_sigintr & sigmask(sig))
                    278:                                return (EINTR);
                    279:                        return (ERESTART);
                    280:                }
                    281:                if (rp->p_wchan == 0) {
                    282:                        splx(s);
                    283:                        return (0);
                    284:                }
                    285:                rp->p_flag |= SSINTR;
                    286:        }
                    287:        rp->p_stat = SSLEEP;
                    288:        if (timo)
                    289:                timeout(endtsleep, (caddr_t)rp, timo);
                    290:        (void) spl0();
                    291:        u.u_ru.ru_nvcsw++;
                    292:        swtch();
                    293:        curpri = rp->p_usrpri;
                    294:        splx(s);
                    295:        rp->p_flag &= ~SSINTR;
                    296:        if (rp->p_flag & STIMO) {
                    297:                rp->p_flag &= ~STIMO;
                    298:                return (EWOULDBLOCK);
                    299:        }
                    300:        if (timo)
                    301:                untimeout(endtsleep, (caddr_t)rp);
                    302:        if (catch && (sig = CURSIG(rp))) {
                    303:                if (u.u_sigintr & sigmask(sig))
                    304:                        return (EINTR);
                    305:                return (ERESTART);
                    306:        }
                    307:        return (0);
                    308: }
                    309: 
                    310: /*
                    311:  * Implement timeout for tsleep.
                    312:  * If process hasn't been awakened (wchan non-zero),
                    313:  * set timeout flag and undo the sleep.  If proc
                    314:  * is stopped, just unsleep so it will remain stopped.
                    315:  */
                    316: endtsleep(p)
                    317:        register struct proc *p;
                    318: {
                    319:        int s = splhigh();
                    320: 
                    321:        if (p->p_wchan) {
                    322:                if (p->p_stat == SSLEEP)
                    323:                        setrun(p);
                    324:                else
                    325:                        unsleep(p);
                    326:                p->p_flag |= STIMO;
                    327:        }
                    328:        splx(s);
                    329: }
                    330: 
                    331: /*
                    332:  * Short-term, non-interruptable sleep.
                    333:  */
                    334: sleep(chan, pri)
                    335:        caddr_t chan;
                    336:        int pri;
                    337: {
                    338:        register struct proc *rp;
                    339:        register struct slpque *qp;
                    340:        register s;
                    341:        extern int cold;
                    342: 
                    343: #ifdef DIAGNOSTIC
                    344:        if (pri > PZERO) {
                    345:                printf("sleep called with pri %d > PZERO, wchan: %x\n",
                    346:                        pri, chan);
                    347:                panic("old sleep");
                    348:        }
                    349: #endif
                    350:        rp = u.u_procp;
                    351:        s = splhigh();
                    352:        if (cold || panicstr) {
                    353:                /*
                    354:                 * After a panic, or during autoconfiguration,
                    355:                 * just give interrupts a chance, then just return;
                    356:                 * don't run any other procs or panic below,
                    357:                 * in case this is the idle process and already asleep.
                    358:                 */
                    359:                (void) spl0();
                    360:                splx(s);
                    361:                return;
                    362:        }
                    363: #ifdef DIAGNOSTIC
                    364:        if (chan==0 || rp->p_stat != SRUN || rp->p_rlink)
                    365:                panic("sleep");
                    366: #endif
                    367:        rp->p_wchan = chan;
                    368:        rp->p_wmesg = NULL;
                    369:        rp->p_slptime = 0;
                    370:        rp->p_pri = pri;
                    371:        qp = &slpque[HASH(chan)];
                    372:        if (qp->sq_head == 0)
                    373:                qp->sq_head = rp;
                    374:        else
                    375:                *qp->sq_tailp = rp;
                    376:        *(qp->sq_tailp = &rp->p_link) = 0;
                    377:        rp->p_stat = SSLEEP;
                    378:        (void) spl0();
                    379:        u.u_ru.ru_nvcsw++;
                    380:        swtch();
                    381:        curpri = rp->p_usrpri;
                    382:        splx(s);
                    383: }
                    384: 
                    385: /*
                    386:  * Remove a process from its wait queue
                    387:  */
                    388: unsleep(p)
                    389:        register struct proc *p;
                    390: {
                    391:        register struct slpque *qp;
                    392:        register struct proc **hp;
                    393:        int s;
                    394: 
                    395:        s = splhigh();
                    396:        if (p->p_wchan) {
                    397:                hp = &(qp = &slpque[HASH(p->p_wchan)])->sq_head;
                    398:                while (*hp != p)
                    399:                        hp = &(*hp)->p_link;
                    400:                *hp = p->p_link;
                    401:                if (qp->sq_tailp == &p->p_link)
                    402:                        qp->sq_tailp = hp;
                    403:                p->p_wchan = 0;
                    404:        }
                    405:        splx(s);
                    406: }
                    407: 
                    408: /*
                    409:  * Wake up all processes sleeping on chan.
                    410:  */
                    411: wakeup(chan)
                    412:        register caddr_t chan;
                    413: {
                    414:        register struct slpque *qp;
                    415:        register struct proc *p, **q;
                    416:        int s;
                    417: 
                    418:        s = splhigh();
                    419:        qp = &slpque[HASH(chan)];
                    420: restart:
                    421:        for (q = &qp->sq_head; p = *q; ) {
                    422: #ifdef DIAGNOSTIC
                    423:                if (p->p_rlink || p->p_stat != SSLEEP && p->p_stat != SSTOP)
                    424:                        panic("wakeup");
                    425: #endif
                    426:                if (p->p_wchan==chan) {
                    427:                        p->p_wchan = 0;
                    428:                        *q = p->p_link;
                    429:                        if (qp->sq_tailp == &p->p_link)
                    430:                                qp->sq_tailp = q;
                    431:                        if (p->p_stat == SSLEEP) {
                    432:                                /* OPTIMIZED INLINE EXPANSION OF setrun(p) */
                    433:                                if (p->p_slptime > 1)
                    434:                                        updatepri(p);
                    435:                                p->p_stat = SRUN;
                    436:                                if (p->p_flag & SLOAD)
                    437:                                        setrq(p);
                    438:                                /*
                    439:                                 * Since curpri is a usrpri,
                    440:                                 * p->p_pri is always better than curpri.
                    441:                                 */
                    442:                                runrun++;
                    443:                                aston();
                    444:                                if ((p->p_flag&SLOAD) == 0) {
                    445:                                        if (runout != 0) {
                    446:                                                runout = 0;
                    447:                                                wakeup((caddr_t)&runout);
                    448:                                        }
                    449:                                        wantin++;
                    450:                                }
                    451:                                /* END INLINE EXPANSION */
                    452:                                goto restart;
                    453:                        }
                    454:                } else
                    455:                        q = &p->p_link;
                    456:        }
                    457:        splx(s);
                    458: }
                    459: 
                    460: /*
                    461:  * Initialize the (doubly-linked) run queues
                    462:  * to be empty.
                    463:  */
                    464: rqinit()
                    465: {
                    466:        register int i;
                    467: 
                    468:        for (i = 0; i < NQS; i++)
                    469:                qs[i].ph_link = qs[i].ph_rlink = (struct proc *)&qs[i];
                    470: }
                    471: 
                    472: /*
                    473:  * Set the process running;
                    474:  * arrange for it to be swapped in if necessary.
                    475:  */
                    476: setrun(p)
                    477:        register struct proc *p;
                    478: {
                    479:        register int s;
                    480: 
                    481:        s = splhigh();
                    482:        switch (p->p_stat) {
                    483: 
                    484:        case 0:
                    485:        case SWAIT:
                    486:        case SRUN:
                    487:        case SZOMB:
                    488:        default:
                    489:                panic("setrun");
                    490: 
                    491:        case SSTOP:
                    492:        case SSLEEP:
                    493:                unsleep(p);             /* e.g. when sending signals */
                    494:                break;
                    495: 
                    496:        case SIDL:
                    497:                break;
                    498:        }
                    499:        p->p_stat = SRUN;
                    500:        if (p->p_flag & SLOAD)
                    501:                setrq(p);
                    502:        splx(s);
                    503:        if (p->p_slptime > 1)
                    504:                updatepri(p);
                    505:        if (p->p_pri < curpri) {
                    506:                runrun++;
                    507:                aston();
                    508:        }
                    509:        if ((p->p_flag&SLOAD) == 0) {
                    510:                if (runout != 0) {
                    511:                        runout = 0;
                    512:                        wakeup((caddr_t)&runout);
                    513:                }
                    514:                wantin++;
                    515:        }
                    516: }
                    517: 
                    518: /*
                    519:  * Set user priority.
                    520:  * The rescheduling flag (runrun)
                    521:  * is set if the priority is better
                    522:  * than the currently running process.
                    523:  */
                    524: setpri(pp)
                    525:        register struct proc *pp;
                    526: {
                    527:        register int p;
                    528: 
                    529:        p = (pp->p_cpu & 0377)/4;
                    530:        p += PUSER + 2 * pp->p_nice;
                    531:        if (pp->p_rssize > pp->p_maxrss && freemem < desfree)
                    532:                p += 2*4;       /* effectively, nice(4) */
                    533:        if (p > 127)
                    534:                p = 127;
                    535:        if (p < curpri) {
                    536:                runrun++;
                    537:                aston();
                    538:        }
                    539:        pp->p_usrpri = p;
                    540:        return (p);
                    541: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.