|
|
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: */
1.1.1.4 ! root 36:
! 37: #include <string.h>
1.1 root 38:
39: #include <mach/boolean.h>
40: #include <mach/machine.h>
41: #include <mach/time_value.h>
42: #include <mach/vm_param.h>
43: #include <mach/vm_prot.h>
44: #include <kern/counters.h>
45: #include "cpu_number.h"
1.1.1.4 ! root 46: #include <kern/debug.h>
1.1 root 47: #include <kern/host.h>
48: #include <kern/lock.h>
1.1.1.4 ! root 49: #include <kern/mach_clock.h>
1.1 root 50: #include <kern/processor.h>
1.1.1.4 ! root 51: #include <kern/queue.h>
1.1 root 52: #include <kern/sched.h>
53: #include <kern/sched_prim.h>
54: #include <kern/thread.h>
55: #include <kern/time_stamp.h>
1.1.1.4 ! root 56: #include <kern/timer.h>
1.1 root 57: #include <vm/vm_kern.h>
58: #include <sys/time.h>
59: #include <machine/mach_param.h> /* HZ */
60: #include <machine/machspl.h>
1.1.1.4 ! root 61: #include <machine/model_dep.h>
1.1 root 62:
63: #if MACH_PCSAMPLE
64: #include <kern/pc_sample.h>
65: #endif
66:
67: void softclock(); /* forward */
68:
69: int hz = HZ; /* number of ticks per second */
70: int tick = (1000000 / HZ); /* number of usec per tick */
71: time_value_t time = { 0, 0 }; /* time since bootup (uncorrected) */
72: unsigned long elapsed_ticks = 0; /* ticks elapsed since bootup */
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.
1.1.1.4 ! root 93: * We have to insert write fence operations.) FIXME
1.1 root 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; \
1.1.1.4 ! root 102: asm volatile("":::"memory"); \
1.1 root 103: mtime->microseconds = (time)->microseconds; \
1.1.1.4 ! root 104: asm volatile("":::"memory"); \
1.1 root 105: mtime->seconds = (time)->seconds; \
106: } \
107: MACRO_END
108:
109: decl_simple_lock_data(, timer_lock) /* lock for ... */
110: timer_elt_data_t timer_head; /* ordered list of timeouts */
111: /* (doubles as end-of-list) */
112:
113: /*
114: * Handle clock interrupts.
115: *
116: * The clock interrupt is assumed to be called at a (more or less)
117: * constant rate. The rate must be identical on all CPUS (XXX - fix).
118: *
119: * Usec is the number of microseconds that have elapsed since the
120: * last clock tick. It may be constant or computed, depending on
121: * the accuracy of the hardware clock.
122: *
123: */
124: void clock_interrupt(usec, usermode, basepri)
125: register int usec; /* microseconds per tick */
126: boolean_t usermode; /* executing user code */
127: boolean_t basepri; /* at base priority */
128: {
129: register int my_cpu = cpu_number();
130: register thread_t thread = current_thread();
131:
132: counter(c_clock_ticks++);
133: counter(c_threads_total += c_threads_current);
134: counter(c_stacks_total += c_stacks_current);
135:
136: #if STAT_TIME
137: /*
138: * Increment the thread time, if using
139: * statistical timing.
140: */
141: if (usermode) {
142: timer_bump(&thread->user_timer, usec);
143: }
144: else {
145: timer_bump(&thread->system_timer, usec);
146: }
1.1.1.3 root 147: #endif /* STAT_TIME */
1.1 root 148:
149: /*
150: * Increment the CPU time statistics.
151: */
152: {
153: extern void thread_quantum_update(); /* in priority.c */
154: register int state;
155:
156: if (usermode)
157: state = CPU_STATE_USER;
158: else if (!cpu_idle(my_cpu))
159: state = CPU_STATE_SYSTEM;
160: else
161: state = CPU_STATE_IDLE;
162:
163: machine_slot[my_cpu].cpu_ticks[state]++;
164:
165: /*
166: * Adjust the thread's priority and check for
167: * quantum expiration.
168: */
169:
170: thread_quantum_update(my_cpu, thread, 1, state);
171: }
172:
1.1.1.2 root 173: #if MACH_PCSAMPLE
1.1 root 174: /*
175: * Take a sample of pc for the user if required.
176: * This had better be MP safe. It might be interesting
177: * to keep track of cpu in the sample.
178: */
179: if (usermode) {
180: take_pc_sample_macro(thread, SAMPLED_PC_PERIODIC);
181: }
182: #endif /* MACH_PCSAMPLE */
183:
184: /*
185: * Time-of-day and time-out list are updated only
186: * on the master CPU.
187: */
188: if (my_cpu == master_cpu) {
189:
190: register spl_t s;
191: register timer_elt_t telt;
192: boolean_t needsoft = FALSE;
193:
194: #if TS_FORMAT == 1
195: /*
196: * Increment the tick count for the timestamping routine.
197: */
198: ts_tick_count++;
1.1.1.3 root 199: #endif /* TS_FORMAT == 1 */
1.1 root 200:
201: /*
202: * Update the tick count since bootup, and handle
203: * timeouts.
204: */
205:
206: s = splsched();
207: simple_lock(&timer_lock);
208:
209: elapsed_ticks++;
210:
211: telt = (timer_elt_t)queue_first(&timer_head.chain);
212: if (telt->ticks <= elapsed_ticks)
213: needsoft = TRUE;
214: simple_unlock(&timer_lock);
215: splx(s);
216:
217: /*
218: * Increment the time-of-day clock.
219: */
220: if (timedelta == 0) {
221: time_value_add_usec(&time, usec);
222: }
223: else {
224: register int delta;
225:
226: if (timedelta < 0) {
227: delta = usec - tickdelta;
228: timedelta += tickdelta;
229: }
230: else {
231: delta = usec + tickdelta;
232: timedelta -= tickdelta;
233: }
234: time_value_add_usec(&time, delta);
235: }
236: update_mapped_time(&time);
237:
238: /*
239: * Schedule soft-interupt for timeout if needed
240: */
241: if (needsoft) {
242: if (basepri) {
243: (void) splsoftclock();
244: softclock();
245: }
246: else {
247: setsoftclock();
248: }
249: }
250: }
251: }
252:
253: /*
254: * There is a nasty race between softclock and reset_timeout.
255: * For example, scheduling code looks at timer_set and calls
256: * reset_timeout, thinking the timer is set. However, softclock
257: * has already removed the timer but hasn't called thread_timeout
258: * yet.
259: *
260: * Interim solution: We initialize timers after pulling
261: * them out of the queue, so a race with reset_timeout won't
262: * hurt. The timeout functions (eg, thread_timeout,
263: * thread_depress_timeout) check timer_set/depress_priority
264: * to see if the timer has been cancelled and if so do nothing.
265: *
266: * This still isn't correct. For example, softclock pulls a
267: * timer off the queue, then thread_go resets timer_set (but
268: * reset_timeout does nothing), then thread_set_timeout puts the
269: * timer back on the queue and sets timer_set, then
270: * thread_timeout finally runs and clears timer_set, then
271: * thread_set_timeout tries to put the timer on the queue again
272: * and corrupts it.
273: */
274:
275: void softclock()
276: {
277: /*
278: * Handle timeouts.
279: */
280: spl_t s;
281: register timer_elt_t telt;
1.1.1.4 ! root 282: register void (*fcn)( void * param );
! 283: register void *param;
1.1 root 284:
285: while (TRUE) {
286: s = splsched();
287: simple_lock(&timer_lock);
288: telt = (timer_elt_t) queue_first(&timer_head.chain);
289: if (telt->ticks > elapsed_ticks) {
290: simple_unlock(&timer_lock);
291: splx(s);
292: break;
293: }
294: fcn = telt->fcn;
295: param = telt->param;
296:
297: remqueue(&timer_head.chain, (queue_entry_t)telt);
298: telt->set = TELT_UNSET;
299: simple_unlock(&timer_lock);
300: splx(s);
301:
302: assert(fcn != 0);
303: (*fcn)(param);
304: }
305: }
306:
307: /*
308: * Set timeout.
309: *
310: * Parameters:
311: * telt timer element. Function and param are already set.
312: * interval time-out interval, in hz.
313: */
314: void set_timeout(telt, interval)
315: register timer_elt_t telt; /* already loaded */
316: register unsigned int interval;
317: {
318: spl_t s;
319: register timer_elt_t next;
320:
321: s = splsched();
322: simple_lock(&timer_lock);
323:
324: interval += elapsed_ticks;
325:
326: for (next = (timer_elt_t)queue_first(&timer_head.chain);
327: ;
328: next = (timer_elt_t)queue_next((queue_entry_t)next)) {
329:
330: if (next->ticks > interval)
331: break;
332: }
333: telt->ticks = interval;
334: /*
335: * Insert new timer element before 'next'
336: * (after 'next'->prev)
337: */
338: insque((queue_entry_t) telt, ((queue_entry_t)next)->prev);
339: telt->set = TELT_SET;
340: simple_unlock(&timer_lock);
341: splx(s);
342: }
343:
344: boolean_t reset_timeout(telt)
345: register timer_elt_t telt;
346: {
347: spl_t s;
348:
349: s = splsched();
350: simple_lock(&timer_lock);
351: if (telt->set) {
352: remqueue(&timer_head.chain, (queue_entry_t)telt);
353: telt->set = TELT_UNSET;
354: simple_unlock(&timer_lock);
355: splx(s);
356: return TRUE;
357: }
358: else {
359: simple_unlock(&timer_lock);
360: splx(s);
361: return FALSE;
362: }
363: }
364:
365: void init_timeout()
366: {
367: simple_lock_init(&timer_lock);
368: queue_init(&timer_head.chain);
369: timer_head.ticks = ~0; /* MAXUINT - sentinel */
370:
371: elapsed_ticks = 0;
372: }
373:
374: /*
1.1.1.2 root 375: * Record a timestamp in STAMP.
376: */
377: void
378: record_time_stamp (time_value_t *stamp)
379: {
380: do {
381: stamp->seconds = mtime->seconds;
382: stamp->microseconds = mtime->microseconds;
383: } while (stamp->seconds != mtime->check_seconds);
384: }
385:
386:
387: /*
1.1 root 388: * Read the time.
389: */
390: kern_return_t
391: host_get_time(host, current_time)
392: host_t host;
393: time_value_t *current_time; /* OUT */
394: {
395: if (host == HOST_NULL)
396: return(KERN_INVALID_HOST);
397:
398: do {
399: current_time->seconds = mtime->seconds;
400: current_time->microseconds = mtime->microseconds;
401: } while (current_time->seconds != mtime->check_seconds);
402:
403: return (KERN_SUCCESS);
404: }
405:
406: /*
407: * Set the time. Only available to privileged users.
408: */
409: kern_return_t
410: host_set_time(host, new_time)
411: host_t host;
412: time_value_t new_time;
413: {
414: spl_t s;
415:
416: if (host == HOST_NULL)
417: return(KERN_INVALID_HOST);
418:
419: #if NCPUS > 1
420: /*
421: * Switch to the master CPU to synchronize correctly.
422: */
423: thread_bind(current_thread(), master_processor);
424: if (current_processor() != master_processor)
425: thread_block((void (*)) 0);
1.1.1.3 root 426: #endif /* NCPUS > 1 */
1.1 root 427:
428: s = splhigh();
429: time = new_time;
430: update_mapped_time(&time);
431: resettodr();
432: splx(s);
433:
434: #if NCPUS > 1
435: /*
436: * Switch off the master CPU.
437: */
438: thread_bind(current_thread(), PROCESSOR_NULL);
1.1.1.3 root 439: #endif /* NCPUS > 1 */
1.1 root 440:
441: return (KERN_SUCCESS);
442: }
443:
444: /*
445: * Adjust the time gradually.
446: */
447: kern_return_t
448: host_adjust_time(host, new_adjustment, old_adjustment)
449: host_t host;
450: time_value_t new_adjustment;
451: time_value_t *old_adjustment; /* OUT */
452: {
453: time_value_t oadj;
454: unsigned int ndelta;
455: spl_t s;
456:
457: if (host == HOST_NULL)
458: return (KERN_INVALID_HOST);
459:
460: ndelta = new_adjustment.seconds * 1000000
461: + new_adjustment.microseconds;
462:
463: #if NCPUS > 1
464: thread_bind(current_thread(), master_processor);
465: if (current_processor() != master_processor)
466: thread_block((void (*)) 0);
1.1.1.3 root 467: #endif /* NCPUS > 1 */
1.1 root 468:
469: s = splclock();
470:
471: oadj.seconds = timedelta / 1000000;
472: oadj.microseconds = timedelta % 1000000;
473:
474: if (timedelta == 0) {
475: if (ndelta > bigadj)
476: tickdelta = 10 * tickadj;
477: else
478: tickdelta = tickadj;
479: }
480: if (ndelta % tickdelta)
481: ndelta = ndelta / tickdelta * tickdelta;
482:
483: timedelta = ndelta;
484:
485: splx(s);
486: #if NCPUS > 1
487: thread_bind(current_thread(), PROCESSOR_NULL);
1.1.1.3 root 488: #endif /* NCPUS > 1 */
1.1 root 489:
490: *old_adjustment = oadj;
491:
492: return (KERN_SUCCESS);
493: }
494:
495: void mapable_time_init()
496: {
497: if (kmem_alloc_wired(kernel_map, (vm_offset_t *) &mtime, PAGE_SIZE)
498: != KERN_SUCCESS)
499: panic("mapable_time_init");
1.1.1.4 ! root 500: memset(mtime, 0, PAGE_SIZE);
1.1 root 501: update_mapped_time(&time);
502: }
503:
504: int timeopen()
505: {
506: return(0);
507: }
508: int timeclose()
509: {
510: return(0);
511: }
512:
513: /*
514: * Compatibility for device drivers.
515: * New code should use set_timeout/reset_timeout and private timers.
1.1.1.4 ! root 516: * These code can't use a cache to allocate timers, because
1.1 root 517: * it can be called from interrupt handlers.
518: */
519:
520: #define NTIMERS 20
521:
522: timer_elt_data_t timeout_timers[NTIMERS];
523:
524: /*
525: * Set timeout.
526: *
527: * fcn: function to call
528: * param: parameter to pass to function
529: * interval: timeout interval, in hz.
530: */
531: void timeout(fcn, param, interval)
1.1.1.4 ! root 532: void (*fcn)( void * param );
! 533: void * param;
1.1 root 534: int interval;
535: {
536: spl_t s;
537: register timer_elt_t elt;
538:
539: s = splsched();
540: simple_lock(&timer_lock);
541: for (elt = &timeout_timers[0]; elt < &timeout_timers[NTIMERS]; elt++)
542: if (elt->set == TELT_UNSET)
543: break;
544: if (elt == &timeout_timers[NTIMERS])
545: panic("timeout");
546: elt->fcn = fcn;
547: elt->param = param;
548: elt->set = TELT_ALLOC;
549: simple_unlock(&timer_lock);
550: splx(s);
551:
552: set_timeout(elt, (unsigned int)interval);
553: }
554:
555: /*
556: * Returns a boolean indicating whether the timeout element was found
557: * and removed.
558: */
559: boolean_t untimeout(fcn, param)
1.1.1.4 ! root 560: register void (*fcn)( void * param );
! 561: register void * param;
1.1 root 562: {
563: spl_t s;
564: register timer_elt_t elt;
565:
566: s = splsched();
567: simple_lock(&timer_lock);
568: queue_iterate(&timer_head.chain, elt, timer_elt_t, chain) {
569:
570: if ((fcn == elt->fcn) && (param == elt->param)) {
571: /*
572: * Found it.
573: */
574: remqueue(&timer_head.chain, (queue_entry_t)elt);
575: elt->set = TELT_UNSET;
576:
577: simple_unlock(&timer_lock);
578: splx(s);
579: return (TRUE);
580: }
581: }
582: simple_unlock(&timer_lock);
583: splx(s);
584: return (FALSE);
585: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.