|
|
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.