|
|
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:
1.1.1.2 ! root 170: #if MACH_PCSAMPLE
1.1 root 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: /*
1.1.1.2 ! root 372: * Record a timestamp in STAMP.
! 373: */
! 374: void
! 375: record_time_stamp (time_value_t *stamp)
! 376: {
! 377: do {
! 378: stamp->seconds = mtime->seconds;
! 379: stamp->microseconds = mtime->microseconds;
! 380: } while (stamp->seconds != mtime->check_seconds);
! 381: }
! 382:
! 383:
! 384: /*
1.1 root 385: * Read the time.
386: */
387: kern_return_t
388: host_get_time(host, current_time)
389: host_t host;
390: time_value_t *current_time; /* OUT */
391: {
392: if (host == HOST_NULL)
393: return(KERN_INVALID_HOST);
394:
395: do {
396: current_time->seconds = mtime->seconds;
397: current_time->microseconds = mtime->microseconds;
398: } while (current_time->seconds != mtime->check_seconds);
399:
400: return (KERN_SUCCESS);
401: }
402:
403: /*
404: * Set the time. Only available to privileged users.
405: */
406: kern_return_t
407: host_set_time(host, new_time)
408: host_t host;
409: time_value_t new_time;
410: {
411: spl_t s;
412:
413: if (host == HOST_NULL)
414: return(KERN_INVALID_HOST);
415:
416: #if NCPUS > 1
417: /*
418: * Switch to the master CPU to synchronize correctly.
419: */
420: thread_bind(current_thread(), master_processor);
421: if (current_processor() != master_processor)
422: thread_block((void (*)) 0);
423: #endif NCPUS > 1
424:
425: s = splhigh();
426: time = new_time;
427: update_mapped_time(&time);
428: resettodr();
429: splx(s);
430:
431: #if NCPUS > 1
432: /*
433: * Switch off the master CPU.
434: */
435: thread_bind(current_thread(), PROCESSOR_NULL);
436: #endif NCPUS > 1
437:
438: return (KERN_SUCCESS);
439: }
440:
441: /*
442: * Adjust the time gradually.
443: */
444: kern_return_t
445: host_adjust_time(host, new_adjustment, old_adjustment)
446: host_t host;
447: time_value_t new_adjustment;
448: time_value_t *old_adjustment; /* OUT */
449: {
450: time_value_t oadj;
451: unsigned int ndelta;
452: spl_t s;
453:
454: if (host == HOST_NULL)
455: return (KERN_INVALID_HOST);
456:
457: ndelta = new_adjustment.seconds * 1000000
458: + new_adjustment.microseconds;
459:
460: #if NCPUS > 1
461: thread_bind(current_thread(), master_processor);
462: if (current_processor() != master_processor)
463: thread_block((void (*)) 0);
464: #endif NCPUS > 1
465:
466: s = splclock();
467:
468: oadj.seconds = timedelta / 1000000;
469: oadj.microseconds = timedelta % 1000000;
470:
471: if (timedelta == 0) {
472: if (ndelta > bigadj)
473: tickdelta = 10 * tickadj;
474: else
475: tickdelta = tickadj;
476: }
477: if (ndelta % tickdelta)
478: ndelta = ndelta / tickdelta * tickdelta;
479:
480: timedelta = ndelta;
481:
482: splx(s);
483: #if NCPUS > 1
484: thread_bind(current_thread(), PROCESSOR_NULL);
485: #endif NCPUS > 1
486:
487: *old_adjustment = oadj;
488:
489: return (KERN_SUCCESS);
490: }
491:
492: void mapable_time_init()
493: {
494: if (kmem_alloc_wired(kernel_map, (vm_offset_t *) &mtime, PAGE_SIZE)
495: != KERN_SUCCESS)
496: panic("mapable_time_init");
497: bzero((char *)mtime, PAGE_SIZE);
498: update_mapped_time(&time);
499: }
500:
501: int timeopen()
502: {
503: return(0);
504: }
505: int timeclose()
506: {
507: return(0);
508: }
509:
510: /*
511: * Compatibility for device drivers.
512: * New code should use set_timeout/reset_timeout and private timers.
513: * These code can't use a zone to allocate timers, because
514: * it can be called from interrupt handlers.
515: */
516:
517: #define NTIMERS 20
518:
519: timer_elt_data_t timeout_timers[NTIMERS];
520:
521: /*
522: * Set timeout.
523: *
524: * fcn: function to call
525: * param: parameter to pass to function
526: * interval: timeout interval, in hz.
527: */
528: void timeout(fcn, param, interval)
529: int (*fcn)(/* char * param */);
530: char * param;
531: int interval;
532: {
533: spl_t s;
534: register timer_elt_t elt;
535:
536: s = splsched();
537: simple_lock(&timer_lock);
538: for (elt = &timeout_timers[0]; elt < &timeout_timers[NTIMERS]; elt++)
539: if (elt->set == TELT_UNSET)
540: break;
541: if (elt == &timeout_timers[NTIMERS])
542: panic("timeout");
543: elt->fcn = fcn;
544: elt->param = param;
545: elt->set = TELT_ALLOC;
546: simple_unlock(&timer_lock);
547: splx(s);
548:
549: set_timeout(elt, (unsigned int)interval);
550: }
551:
552: /*
553: * Returns a boolean indicating whether the timeout element was found
554: * and removed.
555: */
556: boolean_t untimeout(fcn, param)
557: register int (*fcn)();
558: register char * param;
559: {
560: spl_t s;
561: register timer_elt_t elt;
562:
563: s = splsched();
564: simple_lock(&timer_lock);
565: queue_iterate(&timer_head.chain, elt, timer_elt_t, chain) {
566:
567: if ((fcn == elt->fcn) && (param == elt->param)) {
568: /*
569: * Found it.
570: */
571: remqueue(&timer_head.chain, (queue_entry_t)elt);
572: elt->set = TELT_UNSET;
573:
574: simple_unlock(&timer_lock);
575: splx(s);
576: return (TRUE);
577: }
578: }
579: simple_unlock(&timer_lock);
580: splx(s);
581: return (FALSE);
582: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.