Annotation of OSKit-Mach/kern/mach_clock.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Mach Operating System
        !             3:  * Copyright (c) 1994-1988 Carnegie Mellon University.
        !             4:  * Copyright (c) 1993,1994 The University of Utah and
        !             5:  * the Computer Systems Laboratory (CSL).
        !             6:  * All rights reserved.
        !             7:  *
        !             8:  * Permission to use, copy, modify and distribute this software and its
        !             9:  * documentation is hereby granted, provided that both the copyright
        !            10:  * notice and this permission notice appear in all copies of the
        !            11:  * software, derivative works or modified versions, and any portions
        !            12:  * thereof, and that both notices appear in supporting documentation.
        !            13:  *
        !            14:  * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
        !            15:  * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
        !            16:  * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
        !            17:  * THIS SOFTWARE.
        !            18:  *
        !            19:  * Carnegie Mellon requests users of this software to return to
        !            20:  *
        !            21:  *  Software Distribution Coordinator  or  [email protected]
        !            22:  *  School of Computer Science
        !            23:  *  Carnegie Mellon University
        !            24:  *  Pittsburgh PA 15213-3890
        !            25:  *
        !            26:  * any improvements or extensions that they make and grant Carnegie Mellon
        !            27:  * the rights to redistribute these changes.
        !            28:  */
        !            29: /*
        !            30:  *     File:   clock_prim.c
        !            31:  *     Author: Avadis Tevanian, Jr.
        !            32:  *     Date:   1986
        !            33:  *
        !            34:  *     Clock primitives.
        !            35:  */
        !            36: #include <cpus.h>
        !            37: #include <mach_pcsample.h>
        !            38: #include <stat_time.h>
        !            39: 
        !            40: #include <mach/boolean.h>
        !            41: #include <mach/machine.h>
        !            42: #include <mach/time_value.h>
        !            43: #include <mach/vm_param.h>
        !            44: #include <mach/vm_prot.h>
        !            45: #include <kern/counters.h>
        !            46: #include "cpu_number.h"
        !            47: #include <kern/host.h>
        !            48: #include <kern/lock.h>
        !            49: #include <kern/mach_param.h>
        !            50: #include <kern/processor.h>
        !            51: #include <kern/sched.h>
        !            52: #include <kern/sched_prim.h>
        !            53: #include <kern/thread.h>
        !            54: #include <kern/time_out.h>
        !            55: #include <kern/time_stamp.h>
        !            56: #include <vm/vm_kern.h>
        !            57: #include <sys/time.h>
        !            58: #include <machine/mach_param.h>        /* HZ */
        !            59: #include <machine/machspl.h>
        !            60: 
        !            61: #if MACH_PCSAMPLE
        !            62: #include <kern/pc_sample.h>
        !            63: #endif
        !            64: 
        !            65: 
        !            66: void softclock();              /* forward */
        !            67: 
        !            68: int            hz = HZ;                /* number of ticks per second */
        !            69: int            tick = (1000000 / HZ);  /* number of usec per tick */
        !            70: time_value_t   time = { 0, 0 };        /* time since bootup (uncorrected) */
        !            71: unsigned long  elapsed_ticks;          /* ticks elapsed since bootup */
        !            72: unsigned long  timeout_tick;           /* tick value for softclock timeouts */
        !            73: 
        !            74: int            timedelta = 0;
        !            75: int            tickdelta = 0;
        !            76: 
        !            77: #if    HZ > 500
        !            78: int            tickadj = 1;            /* can adjust HZ usecs per second */
        !            79: #else
        !            80: int            tickadj = 500 / HZ;     /* can adjust 100 usecs per second */
        !            81: #endif
        !            82: int            bigadj = 1000000;       /* adjust 10*tickadj if adjustment
        !            83:                                           > bigadj */
        !            84: 
        !            85: /*
        !            86:  *     This update protocol, with a check value, allows
        !            87:  *             do {
        !            88:  *                     secs = mtime->seconds;
        !            89:  *                     usecs = mtime->microseconds;
        !            90:  *             } while (secs != mtime->check_seconds);
        !            91:  *     to read the time correctly.  (On a multiprocessor this assumes
        !            92:  *     that processors see each other's writes in the correct order.
        !            93:  *     We may have to insert fence operations.)
        !            94:  */
        !            95: 
        !            96: mapped_time_value_t *mtime = 0;
        !            97: 
        !            98: #define update_mapped_time(time)                               \
        !            99: MACRO_BEGIN                                                    \
        !           100:        if (mtime != 0) {                                       \
        !           101:                mtime->check_seconds = (time)->seconds;         \
        !           102:                mtime->microseconds = (time)->microseconds;     \
        !           103:                mtime->seconds = (time)->seconds;               \
        !           104:        }                                                       \
        !           105: MACRO_END
        !           106: 
        !           107: decl_simple_lock_data(,        timer_lock)     /* lock for ... */
        !           108: timer_elt_data_t       timer_head;     /* ordered list of timeouts */
        !           109:                                        /* (doubles as end-of-list) */
        !           110: 
        !           111: /*
        !           112:  *     Handle clock interrupts.
        !           113:  *
        !           114:  *     The clock interrupt is assumed to be called at a (more or less)
        !           115:  *     constant rate.  The rate must be identical on all CPUS (XXX - fix).
        !           116:  *
        !           117:  *     Usec is the number of microseconds that have elapsed since the
        !           118:  *     last clock tick.  It may be constant or computed, depending on
        !           119:  *     the accuracy of the hardware clock.
        !           120:  *
        !           121:  */
        !           122: void clock_interrupt(usec, usermode, basepri)
        !           123:        register int    usec;           /* microseconds per tick */
        !           124:        boolean_t       usermode;       /* executing user code */
        !           125:        boolean_t       basepri;        /* at base priority */
        !           126: {
        !           127:        register int            my_cpu = cpu_number();
        !           128:        register thread_t       thread = current_thread();
        !           129: 
        !           130:        counter(c_clock_ticks++);
        !           131:        counter(c_threads_total += c_threads_current);
        !           132:        counter(c_stacks_total += c_stacks_current);
        !           133: 
        !           134: #if    STAT_TIME
        !           135:        /*
        !           136:         *      Increment the thread time, if using
        !           137:         *      statistical timing.
        !           138:         */
        !           139:        if (usermode) {
        !           140:            timer_bump(&thread->user_timer, usec);
        !           141:        }
        !           142:        else {
        !           143:            timer_bump(&thread->system_timer, usec);
        !           144:        }
        !           145: #endif /* STAT_TIME */
        !           146: 
        !           147:        /*
        !           148:         *      Increment the CPU time statistics.
        !           149:         */
        !           150:        {
        !           151:            extern void thread_quantum_update(); /* in priority.c */
        !           152:            register int        state;
        !           153: 
        !           154:            if (usermode)
        !           155:                state = CPU_STATE_USER;
        !           156:            else if (!cpu_idle(my_cpu))
        !           157:                state = CPU_STATE_SYSTEM;
        !           158:            else
        !           159:                state = CPU_STATE_IDLE;
        !           160: 
        !           161:            machine_slot[my_cpu].cpu_ticks[state]++;
        !           162: 
        !           163:            /*
        !           164:             *  Adjust the thread's priority and check for
        !           165:             *  quantum expiration.
        !           166:             */
        !           167: 
        !           168:            thread_quantum_update(my_cpu, thread, 1, state);
        !           169:        }
        !           170: 
        !           171: #if    MACH_PCSAMPLE
        !           172:        /*
        !           173:         * Take a sample of pc for the user if required.
        !           174:         * This had better be MP safe.  It might be interesting
        !           175:         * to keep track of cpu in the sample.
        !           176:         */
        !           177:        if (usermode) {
        !           178:                take_pc_sample_macro(thread, SAMPLED_PC_PERIODIC);
        !           179:        }
        !           180: #endif /* MACH_PCSAMPLE */
        !           181: 
        !           182:        /*
        !           183:         *      Time-of-day and time-out list are updated only
        !           184:         *      on the master CPU.
        !           185:         */
        !           186:        if (my_cpu == master_cpu) {
        !           187: 
        !           188:            register spl_t s;
        !           189:            register timer_elt_t        telt;
        !           190: 
        !           191: #if    TS_FORMAT == 1
        !           192:            /*
        !           193:             *  Increment the tick count for the timestamping routine.
        !           194:             */
        !           195:            ts_tick_count++;
        !           196: #endif /* TS_FORMAT == 1 */
        !           197: 
        !           198:            /*
        !           199:             *  Update the tick count since bootup, and handle
        !           200:             *  timeouts.
        !           201:             */
        !           202: 
        !           203:            s = splsched();
        !           204:            simple_lock(&timer_lock);
        !           205: 
        !           206:            elapsed_ticks++;
        !           207: 
        !           208:            telt = (timer_elt_t)queue_first(&timer_head.chain);
        !           209:            if (telt->ticks <= elapsed_ticks)
        !           210:              timeout_tick = elapsed_ticks;
        !           211:            simple_unlock(&timer_lock);
        !           212:            splx(s);
        !           213: 
        !           214:            /*
        !           215:             *  Increment the time-of-day clock.
        !           216:             */
        !           217:            if (timedelta == 0) {
        !           218:                time_value_add_usec(&time, usec);
        !           219:            }
        !           220:            else {
        !           221:                register int    delta;
        !           222: 
        !           223:                if (timedelta < 0) {
        !           224:                    delta = usec - tickdelta;
        !           225:                    timedelta += tickdelta;
        !           226:                }
        !           227:                else {
        !           228:                    delta = usec + tickdelta;
        !           229:                    timedelta -= tickdelta;
        !           230:                }
        !           231:                time_value_add_usec(&time, delta);
        !           232:            }
        !           233:            update_mapped_time(&time);
        !           234: 
        !           235:            /*
        !           236:             * We always run softclock so it can deliver oskit clock ticks.
        !           237:             * The value of TIMEOUT_TICK tells softclock whether we detected
        !           238:             * a (Mach) timeout above.
        !           239:             */
        !           240:            if (basepri) {
        !           241:              (void) splsoftclock();
        !           242:              softclock();
        !           243:            }
        !           244:            else {
        !           245:              setsoftclock();
        !           246:            }
        !           247:        }
        !           248: }
        !           249: 
        !           250: /*
        !           251:  *     There is a nasty race between softclock and reset_timeout.
        !           252:  *     For example, scheduling code looks at timer_set and calls
        !           253:  *     reset_timeout, thinking the timer is set.  However, softclock
        !           254:  *     has already removed the timer but hasn't called thread_timeout
        !           255:  *     yet.
        !           256:  *
        !           257:  *     Interim solution:  We initialize timers after pulling
        !           258:  *     them out of the queue, so a race with reset_timeout won't
        !           259:  *     hurt.  The timeout functions (eg, thread_timeout,
        !           260:  *     thread_depress_timeout) check timer_set/depress_priority
        !           261:  *     to see if the timer has been cancelled and if so do nothing.
        !           262:  *
        !           263:  *     This still isn't correct.  For example, softclock pulls a
        !           264:  *     timer off the queue, then thread_go resets timer_set (but
        !           265:  *     reset_timeout does nothing), then thread_set_timeout puts the
        !           266:  *     timer back on the queue and sets timer_set, then
        !           267:  *     thread_timeout finally runs and clears timer_set, then
        !           268:  *     thread_set_timeout tries to put the timer on the queue again
        !           269:  *     and corrupts it.
        !           270:  */
        !           271: 
        !           272: void softclock()
        !           273: {
        !           274:        /*
        !           275:         *      Handle timeouts.
        !           276:         */
        !           277:        spl_t   s;
        !           278:        register timer_elt_t    telt;
        !           279:        register int    (*fcn)();
        !           280:        register char   *param;
        !           281: 
        !           282:        if (timeout_tick == elapsed_ticks)
        !           283:          while (TRUE) {
        !           284:            s = splsched();
        !           285:            simple_lock(&timer_lock);
        !           286:            telt = (timer_elt_t) queue_first(&timer_head.chain);
        !           287:            if (telt->ticks > elapsed_ticks) {
        !           288:              simple_unlock(&timer_lock);
        !           289:              splx(s);
        !           290:              break;
        !           291:            }
        !           292:            fcn = telt->fcn;
        !           293:            param = telt->param;
        !           294: 
        !           295:            remqueue(&timer_head.chain, (queue_entry_t)telt);
        !           296:            telt->set = TELT_UNSET;
        !           297:            simple_unlock(&timer_lock);
        !           298:            splx(s);
        !           299: 
        !           300:            assert(fcn != 0);
        !           301:            (*fcn)(param);
        !           302:          }
        !           303: 
        !           304:        /* Give the oskit a clock tick.  */
        !           305:        softclock_oskit ();
        !           306: }
        !           307: 
        !           308: /*
        !           309:  *     Set timeout.
        !           310:  *
        !           311:  *     Parameters:
        !           312:  *             telt     timer element.  Function and param are already set.
        !           313:  *             interval time-out interval, in hz.
        !           314:  */
        !           315: void set_timeout(telt, interval)
        !           316:        register timer_elt_t    telt;   /* already loaded */
        !           317:        register unsigned int   interval;
        !           318: {
        !           319:        spl_t                   s;
        !           320:        register timer_elt_t    next;
        !           321: 
        !           322:        s = splsched();
        !           323:        simple_lock(&timer_lock);
        !           324: 
        !           325:        interval += elapsed_ticks;
        !           326: 
        !           327:        for (next = (timer_elt_t)queue_first(&timer_head.chain);
        !           328:             ;
        !           329:             next = (timer_elt_t)queue_next((queue_entry_t)next)) {
        !           330: 
        !           331:            if (next->ticks > interval)
        !           332:                break;
        !           333:        }
        !           334:        telt->ticks = interval;
        !           335:        /*
        !           336:         * Insert new timer element before 'next'
        !           337:         * (after 'next'->prev)
        !           338:         */
        !           339:        insque((queue_entry_t) telt, ((queue_entry_t)next)->prev);
        !           340:        telt->set = TELT_SET;
        !           341:        simple_unlock(&timer_lock);
        !           342:        splx(s);
        !           343: }
        !           344: 
        !           345: boolean_t reset_timeout(telt)
        !           346:        register timer_elt_t    telt;
        !           347: {
        !           348:        spl_t   s;
        !           349: 
        !           350:        s = splsched();
        !           351:        simple_lock(&timer_lock);
        !           352:        if (telt->set) {
        !           353:            remqueue(&timer_head.chain, (queue_entry_t)telt);
        !           354:            telt->set = TELT_UNSET;
        !           355:            simple_unlock(&timer_lock);
        !           356:            splx(s);
        !           357:            return TRUE;
        !           358:        }
        !           359:        else {
        !           360:            simple_unlock(&timer_lock);
        !           361:            splx(s);
        !           362:            return FALSE;
        !           363:        }
        !           364: }
        !           365: 
        !           366: void init_timeout()
        !           367: {
        !           368:        simple_lock_init(&timer_lock);
        !           369:        queue_init(&timer_head.chain);
        !           370:        timer_head.ticks = ~0;  /* MAXUINT - sentinel */
        !           371: 
        !           372:        elapsed_ticks = 0;
        !           373: }
        !           374: 
        !           375: /*
        !           376:  * Record a timestamp in STAMP.
        !           377:  */
        !           378: void
        !           379: record_time_stamp (time_value_t *stamp)
        !           380: {
        !           381:        do {
        !           382:                stamp->seconds = mtime->seconds;
        !           383:                stamp->microseconds = mtime->microseconds;
        !           384:        } while (stamp->seconds != mtime->check_seconds);
        !           385: }
        !           386: 
        !           387: 
        !           388: /*
        !           389:  * Read the time.
        !           390:  */
        !           391: kern_return_t
        !           392: host_get_time(host, current_time)
        !           393:        host_t          host;
        !           394:        time_value_t    *current_time;  /* OUT */
        !           395: {
        !           396:        if (host == HOST_NULL)
        !           397:                return(KERN_INVALID_HOST);
        !           398: 
        !           399:        do {
        !           400:                current_time->seconds = mtime->seconds;
        !           401:                current_time->microseconds = mtime->microseconds;
        !           402:        } while (current_time->seconds != mtime->check_seconds);
        !           403: 
        !           404:        return (KERN_SUCCESS);
        !           405: }
        !           406: 
        !           407: /*
        !           408:  * Set the time.  Only available to privileged users.
        !           409:  */
        !           410: kern_return_t
        !           411: host_set_time(host, new_time)
        !           412:        host_t          host;
        !           413:        time_value_t    new_time;
        !           414: {
        !           415:        spl_t   s;
        !           416: 
        !           417:        if (host == HOST_NULL)
        !           418:                return(KERN_INVALID_HOST);
        !           419: 
        !           420: #if    NCPUS > 1
        !           421:        /*
        !           422:         * Switch to the master CPU to synchronize correctly.
        !           423:         */
        !           424:        thread_bind(current_thread(), master_processor);
        !           425:        if (current_processor() != master_processor)
        !           426:            thread_block((void (*)) 0);
        !           427: #endif /* NCPUS > 1 */
        !           428: 
        !           429:        s = splhigh();
        !           430:        time = new_time;
        !           431:        update_mapped_time(&time);
        !           432:        resettodr();
        !           433:        splx(s);
        !           434: 
        !           435: #if    NCPUS > 1
        !           436:        /*
        !           437:         * Switch off the master CPU.
        !           438:         */
        !           439:        thread_bind(current_thread(), PROCESSOR_NULL);
        !           440: #endif /* NCPUS > 1 */
        !           441: 
        !           442:        return (KERN_SUCCESS);
        !           443: }
        !           444: 
        !           445: /*
        !           446:  * Adjust the time gradually.
        !           447:  */
        !           448: kern_return_t
        !           449: host_adjust_time(host, new_adjustment, old_adjustment)
        !           450:        host_t          host;
        !           451:        time_value_t    new_adjustment;
        !           452:        time_value_t    *old_adjustment;        /* OUT */
        !           453: {
        !           454:        time_value_t    oadj;
        !           455:        unsigned int    ndelta;
        !           456:        spl_t           s;
        !           457: 
        !           458:        if (host == HOST_NULL)
        !           459:                return (KERN_INVALID_HOST);
        !           460: 
        !           461:        ndelta = new_adjustment.seconds * 1000000
        !           462:                + new_adjustment.microseconds;
        !           463: 
        !           464: #if    NCPUS > 1
        !           465:        thread_bind(current_thread(), master_processor);
        !           466:        if (current_processor() != master_processor)
        !           467:            thread_block((void (*)) 0);
        !           468: #endif /* NCPUS > 1 */
        !           469: 
        !           470:        s = splclock();
        !           471: 
        !           472:        oadj.seconds = timedelta / 1000000;
        !           473:        oadj.microseconds = timedelta % 1000000;
        !           474: 
        !           475:        if (timedelta == 0) {
        !           476:            if (ndelta > bigadj)
        !           477:                tickdelta = 10 * tickadj;
        !           478:            else
        !           479:                tickdelta = tickadj;
        !           480:        }
        !           481:        if (ndelta % tickdelta)
        !           482:            ndelta = ndelta / tickdelta * tickdelta;
        !           483: 
        !           484:        timedelta = ndelta;
        !           485: 
        !           486:        splx(s);
        !           487: #if    NCPUS > 1
        !           488:        thread_bind(current_thread(), PROCESSOR_NULL);
        !           489: #endif /* NCPUS > 1 */
        !           490: 
        !           491:        *old_adjustment = oadj;
        !           492: 
        !           493:        return (KERN_SUCCESS);
        !           494: }
        !           495: 
        !           496: void mapable_time_init()
        !           497: {
        !           498:        if (kmem_alloc_wired(kernel_map, (vm_offset_t *) &mtime, PAGE_SIZE)
        !           499:                                                != KERN_SUCCESS)
        !           500:                panic("mapable_time_init");
        !           501:        bzero((char *)mtime, PAGE_SIZE);
        !           502:        update_mapped_time(&time);
        !           503: }
        !           504: 
        !           505: int timeopen()
        !           506: {
        !           507:        return(0);
        !           508: }
        !           509: int timeclose()
        !           510: {
        !           511:        return(0);
        !           512: }
        !           513: 
        !           514: /*
        !           515:  *     Compatibility for device drivers.
        !           516:  *     New code should use set_timeout/reset_timeout and private timers.
        !           517:  *     These code can't use a zone to allocate timers, because
        !           518:  *     it can be called from interrupt handlers.
        !           519:  */
        !           520: 
        !           521: #define NTIMERS                20
        !           522: 
        !           523: timer_elt_data_t timeout_timers[NTIMERS];
        !           524: 
        !           525: /*
        !           526:  *     Set timeout.
        !           527:  *
        !           528:  *     fcn:            function to call
        !           529:  *     param:          parameter to pass to function
        !           530:  *     interval:       timeout interval, in hz.
        !           531:  */
        !           532: void timeout(fcn, param, interval)
        !           533:        int     (*fcn)(/* char * param */);
        !           534:        char *  param;
        !           535:        int     interval;
        !           536: {
        !           537:        spl_t   s;
        !           538:        register timer_elt_t elt;
        !           539: 
        !           540:        s = splsched();
        !           541:        simple_lock(&timer_lock);
        !           542:        for (elt = &timeout_timers[0]; elt < &timeout_timers[NTIMERS]; elt++)
        !           543:            if (elt->set == TELT_UNSET)
        !           544:                break;
        !           545:        if (elt == &timeout_timers[NTIMERS])
        !           546:            panic("timeout");
        !           547:        elt->fcn = fcn;
        !           548:        elt->param = param;
        !           549:        elt->set = TELT_ALLOC;
        !           550:        simple_unlock(&timer_lock);
        !           551:        splx(s);
        !           552: 
        !           553:        set_timeout(elt, (unsigned int)interval);
        !           554: }
        !           555: 
        !           556: /*
        !           557:  * Returns a boolean indicating whether the timeout element was found
        !           558:  * and removed.
        !           559:  */
        !           560: boolean_t untimeout(fcn, param)
        !           561:        register int    (*fcn)();
        !           562:        register char * param;
        !           563: {
        !           564:        spl_t   s;
        !           565:        register timer_elt_t elt;
        !           566: 
        !           567:        s = splsched();
        !           568:        simple_lock(&timer_lock);
        !           569:        queue_iterate(&timer_head.chain, elt, timer_elt_t, chain) {
        !           570: 
        !           571:            if ((fcn == elt->fcn) && (param == elt->param)) {
        !           572:                /*
        !           573:                 *      Found it.
        !           574:                 */
        !           575:                remqueue(&timer_head.chain, (queue_entry_t)elt);
        !           576:                elt->set = TELT_UNSET;
        !           577: 
        !           578:                simple_unlock(&timer_lock);
        !           579:                splx(s);
        !           580:                return (TRUE);
        !           581:            }
        !           582:        }
        !           583:        simple_unlock(&timer_lock);
        !           584:        splx(s);
        !           585:        return (FALSE);
        !           586: }

unix.superglobalmegacorp.com

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