|
|
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 = 0; /* ticks elapsed since bootup */ ! 72: ! 73: int timedelta = 0; ! 74: int tickdelta = 0; ! 75: ! 76: #if HZ > 500 ! 77: int tickadj = 1; /* can adjust HZ usecs per second */ ! 78: #else ! 79: int tickadj = 500 / HZ; /* can adjust 100 usecs per second */ ! 80: #endif ! 81: int bigadj = 1000000; /* adjust 10*tickadj if adjustment ! 82: > bigadj */ ! 83: ! 84: /* ! 85: * This update protocol, with a check value, allows ! 86: * do { ! 87: * secs = mtime->seconds; ! 88: * usecs = mtime->microseconds; ! 89: * } while (secs != mtime->check_seconds); ! 90: * to read the time correctly. (On a multiprocessor this assumes ! 91: * that processors see each other's writes in the correct order. ! 92: * We may have to insert fence operations.) ! 93: */ ! 94: ! 95: mapped_time_value_t *mtime = 0; ! 96: ! 97: #define update_mapped_time(time) \ ! 98: MACRO_BEGIN \ ! 99: if (mtime != 0) { \ ! 100: mtime->check_seconds = (time)->seconds; \ ! 101: mtime->microseconds = (time)->microseconds; \ ! 102: mtime->seconds = (time)->seconds; \ ! 103: } \ ! 104: MACRO_END ! 105: ! 106: decl_simple_lock_data(, timer_lock) /* lock for ... */ ! 107: timer_elt_data_t timer_head; /* ordered list of timeouts */ ! 108: /* (doubles as end-of-list) */ ! 109: ! 110: /* ! 111: * Handle clock interrupts. ! 112: * ! 113: * The clock interrupt is assumed to be called at a (more or less) ! 114: * constant rate. The rate must be identical on all CPUS (XXX - fix). ! 115: * ! 116: * Usec is the number of microseconds that have elapsed since the ! 117: * last clock tick. It may be constant or computed, depending on ! 118: * the accuracy of the hardware clock. ! 119: * ! 120: */ ! 121: void clock_interrupt(usec, usermode, basepri) ! 122: register int usec; /* microseconds per tick */ ! 123: boolean_t usermode; /* executing user code */ ! 124: boolean_t basepri; /* at base priority */ ! 125: { ! 126: register int my_cpu = cpu_number(); ! 127: register thread_t thread = current_thread(); ! 128: ! 129: counter(c_clock_ticks++); ! 130: counter(c_threads_total += c_threads_current); ! 131: counter(c_stacks_total += c_stacks_current); ! 132: ! 133: #if STAT_TIME ! 134: /* ! 135: * Increment the thread time, if using ! 136: * statistical timing. ! 137: */ ! 138: if (usermode) { ! 139: timer_bump(&thread->user_timer, usec); ! 140: } ! 141: else { ! 142: timer_bump(&thread->system_timer, usec); ! 143: } ! 144: #endif STAT_TIME ! 145: ! 146: /* ! 147: * Increment the CPU time statistics. ! 148: */ ! 149: { ! 150: extern void thread_quantum_update(); /* in priority.c */ ! 151: register int state; ! 152: ! 153: if (usermode) ! 154: state = CPU_STATE_USER; ! 155: else if (!cpu_idle(my_cpu)) ! 156: state = CPU_STATE_SYSTEM; ! 157: else ! 158: state = CPU_STATE_IDLE; ! 159: ! 160: machine_slot[my_cpu].cpu_ticks[state]++; ! 161: ! 162: /* ! 163: * Adjust the thread's priority and check for ! 164: * quantum expiration. ! 165: */ ! 166: ! 167: thread_quantum_update(my_cpu, thread, 1, state); ! 168: } ! 169: ! 170: #if MACH_SAMPLE ! 171: /* ! 172: * Take a sample of pc for the user if required. ! 173: * This had better be MP safe. It might be interesting ! 174: * to keep track of cpu in the sample. ! 175: */ ! 176: if (usermode) { ! 177: take_pc_sample_macro(thread, SAMPLED_PC_PERIODIC); ! 178: } ! 179: #endif /* MACH_PCSAMPLE */ ! 180: ! 181: /* ! 182: * Time-of-day and time-out list are updated only ! 183: * on the master CPU. ! 184: */ ! 185: if (my_cpu == master_cpu) { ! 186: ! 187: register spl_t s; ! 188: register timer_elt_t telt; ! 189: boolean_t needsoft = FALSE; ! 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: needsoft = TRUE; ! 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: * Schedule soft-interupt for timeout if needed ! 237: */ ! 238: if (needsoft) { ! 239: if (basepri) { ! 240: (void) splsoftclock(); ! 241: softclock(); ! 242: } ! 243: else { ! 244: setsoftclock(); ! 245: } ! 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: while (TRUE) { ! 283: s = splsched(); ! 284: simple_lock(&timer_lock); ! 285: telt = (timer_elt_t) queue_first(&timer_head.chain); ! 286: if (telt->ticks > elapsed_ticks) { ! 287: simple_unlock(&timer_lock); ! 288: splx(s); ! 289: break; ! 290: } ! 291: fcn = telt->fcn; ! 292: param = telt->param; ! 293: ! 294: remqueue(&timer_head.chain, (queue_entry_t)telt); ! 295: telt->set = TELT_UNSET; ! 296: simple_unlock(&timer_lock); ! 297: splx(s); ! 298: ! 299: assert(fcn != 0); ! 300: (*fcn)(param); ! 301: } ! 302: } ! 303: ! 304: /* ! 305: * Set timeout. ! 306: * ! 307: * Parameters: ! 308: * telt timer element. Function and param are already set. ! 309: * interval time-out interval, in hz. ! 310: */ ! 311: void set_timeout(telt, interval) ! 312: register timer_elt_t telt; /* already loaded */ ! 313: register unsigned int interval; ! 314: { ! 315: spl_t s; ! 316: register timer_elt_t next; ! 317: ! 318: s = splsched(); ! 319: simple_lock(&timer_lock); ! 320: ! 321: interval += elapsed_ticks; ! 322: ! 323: for (next = (timer_elt_t)queue_first(&timer_head.chain); ! 324: ; ! 325: next = (timer_elt_t)queue_next((queue_entry_t)next)) { ! 326: ! 327: if (next->ticks > interval) ! 328: break; ! 329: } ! 330: telt->ticks = interval; ! 331: /* ! 332: * Insert new timer element before 'next' ! 333: * (after 'next'->prev) ! 334: */ ! 335: insque((queue_entry_t) telt, ((queue_entry_t)next)->prev); ! 336: telt->set = TELT_SET; ! 337: simple_unlock(&timer_lock); ! 338: splx(s); ! 339: } ! 340: ! 341: boolean_t reset_timeout(telt) ! 342: register timer_elt_t telt; ! 343: { ! 344: spl_t s; ! 345: ! 346: s = splsched(); ! 347: simple_lock(&timer_lock); ! 348: if (telt->set) { ! 349: remqueue(&timer_head.chain, (queue_entry_t)telt); ! 350: telt->set = TELT_UNSET; ! 351: simple_unlock(&timer_lock); ! 352: splx(s); ! 353: return TRUE; ! 354: } ! 355: else { ! 356: simple_unlock(&timer_lock); ! 357: splx(s); ! 358: return FALSE; ! 359: } ! 360: } ! 361: ! 362: void init_timeout() ! 363: { ! 364: simple_lock_init(&timer_lock); ! 365: queue_init(&timer_head.chain); ! 366: timer_head.ticks = ~0; /* MAXUINT - sentinel */ ! 367: ! 368: elapsed_ticks = 0; ! 369: } ! 370: ! 371: /* ! 372: * Read the time. ! 373: */ ! 374: kern_return_t ! 375: host_get_time(host, current_time) ! 376: host_t host; ! 377: time_value_t *current_time; /* OUT */ ! 378: { ! 379: if (host == HOST_NULL) ! 380: return(KERN_INVALID_HOST); ! 381: ! 382: do { ! 383: current_time->seconds = mtime->seconds; ! 384: current_time->microseconds = mtime->microseconds; ! 385: } while (current_time->seconds != mtime->check_seconds); ! 386: ! 387: return (KERN_SUCCESS); ! 388: } ! 389: ! 390: /* ! 391: * Set the time. Only available to privileged users. ! 392: */ ! 393: kern_return_t ! 394: host_set_time(host, new_time) ! 395: host_t host; ! 396: time_value_t new_time; ! 397: { ! 398: spl_t s; ! 399: ! 400: if (host == HOST_NULL) ! 401: return(KERN_INVALID_HOST); ! 402: ! 403: #if NCPUS > 1 ! 404: /* ! 405: * Switch to the master CPU to synchronize correctly. ! 406: */ ! 407: thread_bind(current_thread(), master_processor); ! 408: if (current_processor() != master_processor) ! 409: thread_block((void (*)) 0); ! 410: #endif NCPUS > 1 ! 411: ! 412: s = splhigh(); ! 413: time = new_time; ! 414: update_mapped_time(&time); ! 415: resettodr(); ! 416: splx(s); ! 417: ! 418: #if NCPUS > 1 ! 419: /* ! 420: * Switch off the master CPU. ! 421: */ ! 422: thread_bind(current_thread(), PROCESSOR_NULL); ! 423: #endif NCPUS > 1 ! 424: ! 425: return (KERN_SUCCESS); ! 426: } ! 427: ! 428: /* ! 429: * Adjust the time gradually. ! 430: */ ! 431: kern_return_t ! 432: host_adjust_time(host, new_adjustment, old_adjustment) ! 433: host_t host; ! 434: time_value_t new_adjustment; ! 435: time_value_t *old_adjustment; /* OUT */ ! 436: { ! 437: time_value_t oadj; ! 438: unsigned int ndelta; ! 439: spl_t s; ! 440: ! 441: if (host == HOST_NULL) ! 442: return (KERN_INVALID_HOST); ! 443: ! 444: ndelta = new_adjustment.seconds * 1000000 ! 445: + new_adjustment.microseconds; ! 446: ! 447: #if NCPUS > 1 ! 448: thread_bind(current_thread(), master_processor); ! 449: if (current_processor() != master_processor) ! 450: thread_block((void (*)) 0); ! 451: #endif NCPUS > 1 ! 452: ! 453: s = splclock(); ! 454: ! 455: oadj.seconds = timedelta / 1000000; ! 456: oadj.microseconds = timedelta % 1000000; ! 457: ! 458: if (timedelta == 0) { ! 459: if (ndelta > bigadj) ! 460: tickdelta = 10 * tickadj; ! 461: else ! 462: tickdelta = tickadj; ! 463: } ! 464: if (ndelta % tickdelta) ! 465: ndelta = ndelta / tickdelta * tickdelta; ! 466: ! 467: timedelta = ndelta; ! 468: ! 469: splx(s); ! 470: #if NCPUS > 1 ! 471: thread_bind(current_thread(), PROCESSOR_NULL); ! 472: #endif NCPUS > 1 ! 473: ! 474: *old_adjustment = oadj; ! 475: ! 476: return (KERN_SUCCESS); ! 477: } ! 478: ! 479: void mapable_time_init() ! 480: { ! 481: if (kmem_alloc_wired(kernel_map, (vm_offset_t *) &mtime, PAGE_SIZE) ! 482: != KERN_SUCCESS) ! 483: panic("mapable_time_init"); ! 484: bzero((char *)mtime, PAGE_SIZE); ! 485: update_mapped_time(&time); ! 486: } ! 487: ! 488: int timeopen() ! 489: { ! 490: return(0); ! 491: } ! 492: int timeclose() ! 493: { ! 494: return(0); ! 495: } ! 496: ! 497: /* ! 498: * Compatibility for device drivers. ! 499: * New code should use set_timeout/reset_timeout and private timers. ! 500: * These code can't use a zone to allocate timers, because ! 501: * it can be called from interrupt handlers. ! 502: */ ! 503: ! 504: #define NTIMERS 20 ! 505: ! 506: timer_elt_data_t timeout_timers[NTIMERS]; ! 507: ! 508: /* ! 509: * Set timeout. ! 510: * ! 511: * fcn: function to call ! 512: * param: parameter to pass to function ! 513: * interval: timeout interval, in hz. ! 514: */ ! 515: void timeout(fcn, param, interval) ! 516: int (*fcn)(/* char * param */); ! 517: char * param; ! 518: int interval; ! 519: { ! 520: spl_t s; ! 521: register timer_elt_t elt; ! 522: ! 523: s = splsched(); ! 524: simple_lock(&timer_lock); ! 525: for (elt = &timeout_timers[0]; elt < &timeout_timers[NTIMERS]; elt++) ! 526: if (elt->set == TELT_UNSET) ! 527: break; ! 528: if (elt == &timeout_timers[NTIMERS]) ! 529: panic("timeout"); ! 530: elt->fcn = fcn; ! 531: elt->param = param; ! 532: elt->set = TELT_ALLOC; ! 533: simple_unlock(&timer_lock); ! 534: splx(s); ! 535: ! 536: set_timeout(elt, (unsigned int)interval); ! 537: } ! 538: ! 539: /* ! 540: * Returns a boolean indicating whether the timeout element was found ! 541: * and removed. ! 542: */ ! 543: boolean_t untimeout(fcn, param) ! 544: register int (*fcn)(); ! 545: register char * param; ! 546: { ! 547: spl_t s; ! 548: register timer_elt_t elt; ! 549: ! 550: s = splsched(); ! 551: simple_lock(&timer_lock); ! 552: queue_iterate(&timer_head.chain, elt, timer_elt_t, chain) { ! 553: ! 554: if ((fcn == elt->fcn) && (param == elt->param)) { ! 555: /* ! 556: * Found it. ! 557: */ ! 558: remqueue(&timer_head.chain, (queue_entry_t)elt); ! 559: elt->set = TELT_UNSET; ! 560: ! 561: simple_unlock(&timer_lock); ! 562: splx(s); ! 563: return (TRUE); ! 564: } ! 565: } ! 566: simple_unlock(&timer_lock); ! 567: splx(s); ! 568: return (FALSE); ! 569: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.