|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.