]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
e5568f75 A |
6 | * The contents of this file constitute Original Code as defined in and |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
1c79356b | 11 | * |
e5568f75 A |
12 | * This Original Code and all software distributed under the License are |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
1c79356b A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
e5568f75 A |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
1c79356b A |
19 | * |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * @OSF_COPYRIGHT@ | |
24 | */ | |
25 | /* | |
26 | * Mach Operating System | |
27 | * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University | |
28 | * All Rights Reserved. | |
29 | * | |
30 | * Permission to use, copy, modify and distribute this software and its | |
31 | * documentation is hereby granted, provided that both the copyright | |
32 | * notice and this permission notice appear in all copies of the | |
33 | * software, derivative works or modified versions, and any portions | |
34 | * thereof, and that both notices appear in supporting documentation. | |
35 | * | |
36 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" | |
37 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR | |
38 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | |
39 | * | |
40 | * Carnegie Mellon requests users of this software to return to | |
41 | * | |
42 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU | |
43 | * School of Computer Science | |
44 | * Carnegie Mellon University | |
45 | * Pittsburgh PA 15213-3890 | |
46 | * | |
47 | * any improvements or extensions that they make and grant Carnegie Mellon | |
48 | * the rights to redistribute these changes. | |
49 | */ | |
50 | /* | |
51 | */ | |
52 | ||
53 | #include <cpus.h> | |
54 | #include <stat_time.h> | |
55 | ||
56 | #include <mach/kern_return.h> | |
57 | #include <mach/port.h> | |
58 | #include <kern/queue.h> | |
59 | #include <kern/thread.h> | |
60 | #include <kern/sched_prim.h> | |
61 | #include <mach/time_value.h> | |
62 | #include <kern/timer.h> | |
63 | #include <kern/cpu_number.h> | |
64 | ||
65 | #include <kern/assert.h> | |
66 | #include <kern/macro_help.h> | |
67 | ||
68 | timer_t current_timer[NCPUS]; | |
69 | timer_data_t kernel_timer[NCPUS]; | |
70 | ||
71 | /* Forwards */ | |
72 | void timer_grab( | |
73 | timer_t timer, | |
74 | timer_save_t save); | |
75 | ||
76 | void db_timer_grab( | |
77 | timer_t timer, | |
78 | timer_save_t save); | |
79 | ||
80 | void db_thread_read_times( | |
81 | thread_t thread, | |
82 | time_value_t *user_time_p, | |
83 | time_value_t *system_time_p); | |
84 | ||
85 | /* | |
86 | * init_timers initializes all non-thread timers and puts the | |
87 | * service routine on the callout queue. All timers must be | |
88 | * serviced by the callout routine once an hour. | |
89 | */ | |
90 | void | |
91 | init_timers(void) | |
92 | { | |
93 | register int i; | |
94 | register timer_t this_timer; | |
95 | ||
96 | /* | |
97 | * Initialize all the kernel timers and start the one | |
98 | * for this cpu (master) slaves start theirs later. | |
99 | */ | |
100 | this_timer = &kernel_timer[0]; | |
101 | for ( i=0 ; i<NCPUS ; i++, this_timer++) { | |
102 | timer_init(this_timer); | |
103 | current_timer[i] = (timer_t) 0; | |
104 | } | |
105 | ||
106 | mp_disable_preemption(); | |
107 | start_timer(&kernel_timer[cpu_number()]); | |
108 | mp_enable_preemption(); | |
109 | } | |
110 | ||
111 | /* | |
112 | * timer_init initializes a single timer. | |
113 | */ | |
114 | void | |
115 | timer_init( | |
116 | register timer_t this_timer) | |
117 | { | |
118 | this_timer->low_bits = 0; | |
119 | this_timer->high_bits = 0; | |
120 | this_timer->tstamp = 0; | |
121 | this_timer->high_bits_check = 0; | |
122 | } | |
123 | ||
124 | #if STAT_TIME | |
125 | #else /* STAT_TIME */ | |
126 | ||
127 | #ifdef MACHINE_TIMER_ROUTINES | |
128 | ||
129 | /* | |
130 | * Machine-dependent code implements the timer routines. | |
131 | */ | |
132 | ||
133 | #else /* MACHINE_TIMER_ROUTINES */ | |
134 | ||
135 | /* | |
136 | * start_timer starts the given timer for this cpu. It is called | |
137 | * exactly once for each cpu during the boot sequence. | |
138 | */ | |
139 | void | |
140 | start_timer( | |
141 | register timer_t timer) | |
142 | { | |
143 | timer->tstamp = get_timestamp(); | |
144 | mp_disable_preemption(); | |
145 | current_timer[cpu_number()] = timer; | |
146 | mp_enable_preemption(); | |
147 | } | |
148 | ||
149 | /* | |
150 | * time_trap_uentry does trap entry timing. Caller must lock out | |
151 | * interrupts and take a timestamp. ts is a timestamp taken after | |
152 | * interrupts were locked out. Must only be called if trap was | |
153 | * from user mode. | |
154 | */ | |
155 | void | |
156 | time_trap_uentry( | |
157 | unsigned ts) | |
158 | { | |
159 | int elapsed; | |
160 | int mycpu; | |
161 | timer_t mytimer; | |
162 | ||
163 | mp_disable_preemption(); | |
164 | ||
165 | /* | |
166 | * Calculate elapsed time. | |
167 | */ | |
168 | mycpu = cpu_number(); | |
169 | mytimer = current_timer[mycpu]; | |
170 | elapsed = ts - mytimer->tstamp; | |
171 | #ifdef TIMER_MAX | |
172 | if (elapsed < 0) elapsed += TIMER_MAX; | |
173 | #endif /* TIMER_MAX */ | |
174 | ||
175 | /* | |
176 | * Update current timer. | |
177 | */ | |
178 | mytimer->low_bits += elapsed; | |
179 | mytimer->tstamp = 0; | |
180 | ||
181 | if (mytimer->low_bits & TIMER_LOW_FULL) { | |
182 | timer_normalize(mytimer); | |
183 | } | |
184 | ||
185 | /* | |
186 | * Record new timer. | |
187 | */ | |
188 | mytimer = &(current_thread()->system_timer); | |
189 | current_timer[mycpu] = mytimer; | |
190 | mytimer->tstamp = ts; | |
191 | ||
192 | mp_enable_preemption(); | |
193 | } | |
194 | ||
195 | /* | |
196 | * time_trap_uexit does trap exit timing. Caller must lock out | |
197 | * interrupts and take a timestamp. ts is a timestamp taken after | |
198 | * interrupts were locked out. Must only be called if returning to | |
199 | * user mode. | |
200 | */ | |
201 | void | |
202 | time_trap_uexit( | |
203 | unsigned ts) | |
204 | { | |
205 | int elapsed; | |
206 | int mycpu; | |
207 | timer_t mytimer; | |
208 | ||
209 | mp_disable_preemption(); | |
210 | ||
211 | /* | |
212 | * Calculate elapsed time. | |
213 | */ | |
214 | mycpu = cpu_number(); | |
215 | mytimer = current_timer[mycpu]; | |
216 | elapsed = ts - mytimer->tstamp; | |
217 | #ifdef TIMER_MAX | |
218 | if (elapsed < 0) elapsed += TIMER_MAX; | |
219 | #endif /* TIMER_MAX */ | |
220 | ||
221 | /* | |
222 | * Update current timer. | |
223 | */ | |
224 | mytimer->low_bits += elapsed; | |
225 | mytimer->tstamp = 0; | |
226 | ||
227 | if (mytimer->low_bits & TIMER_LOW_FULL) { | |
228 | timer_normalize(mytimer); /* SYSTEMMODE */ | |
229 | } | |
230 | ||
231 | mytimer = &(current_thread()->user_timer); | |
232 | ||
233 | /* | |
234 | * Record new timer. | |
235 | */ | |
236 | current_timer[mycpu] = mytimer; | |
237 | mytimer->tstamp = ts; | |
238 | ||
239 | mp_enable_preemption(); | |
240 | } | |
241 | ||
242 | /* | |
243 | * time_int_entry does interrupt entry timing. Caller must lock out | |
244 | * interrupts and take a timestamp. ts is a timestamp taken after | |
245 | * interrupts were locked out. new_timer is the new timer to | |
246 | * switch to. This routine returns the currently running timer, | |
247 | * which MUST be pushed onto the stack by the caller, or otherwise | |
248 | * saved for time_int_exit. | |
249 | */ | |
250 | timer_t | |
251 | time_int_entry( | |
252 | unsigned ts, | |
253 | timer_t new_timer) | |
254 | { | |
255 | int elapsed; | |
256 | int mycpu; | |
257 | timer_t mytimer; | |
258 | ||
259 | mp_disable_preemption(); | |
260 | ||
261 | /* | |
262 | * Calculate elapsed time. | |
263 | */ | |
264 | mycpu = cpu_number(); | |
265 | mytimer = current_timer[mycpu]; | |
266 | ||
267 | elapsed = ts - mytimer->tstamp; | |
268 | #ifdef TIMER_MAX | |
269 | if (elapsed < 0) elapsed += TIMER_MAX; | |
270 | #endif /* TIMER_MAX */ | |
271 | ||
272 | /* | |
273 | * Update current timer. | |
274 | */ | |
275 | mytimer->low_bits += elapsed; | |
276 | mytimer->tstamp = 0; | |
277 | ||
278 | /* | |
279 | * Switch to new timer, and save old one on stack. | |
280 | */ | |
281 | new_timer->tstamp = ts; | |
282 | current_timer[mycpu] = new_timer; | |
283 | ||
284 | mp_enable_preemption(); | |
285 | ||
286 | return(mytimer); | |
287 | } | |
288 | ||
289 | /* | |
290 | * time_int_exit does interrupt exit timing. Caller must lock out | |
291 | * interrupts and take a timestamp. ts is a timestamp taken after | |
292 | * interrupts were locked out. old_timer is the timer value pushed | |
293 | * onto the stack or otherwise saved after time_int_entry returned | |
294 | * it. | |
295 | */ | |
296 | void | |
297 | time_int_exit( | |
298 | unsigned ts, | |
299 | timer_t old_timer) | |
300 | { | |
301 | int elapsed; | |
302 | int mycpu; | |
303 | timer_t mytimer; | |
304 | ||
305 | mp_disable_preemption(); | |
306 | ||
307 | /* | |
308 | * Calculate elapsed time. | |
309 | */ | |
310 | mycpu = cpu_number(); | |
311 | mytimer = current_timer[mycpu]; | |
312 | elapsed = ts - mytimer->tstamp; | |
313 | #ifdef TIMER_MAX | |
314 | if (elapsed < 0) elapsed += TIMER_MAX; | |
315 | #endif /* TIMER_MAX */ | |
316 | ||
317 | /* | |
318 | * Update current timer. | |
319 | */ | |
320 | mytimer->low_bits += elapsed; | |
321 | mytimer->tstamp = 0; | |
322 | ||
323 | /* | |
324 | * If normalization requested, do it. | |
325 | */ | |
326 | if (mytimer->low_bits & TIMER_LOW_FULL) { | |
327 | timer_normalize(mytimer); | |
328 | } | |
329 | if (old_timer->low_bits & TIMER_LOW_FULL) { | |
330 | timer_normalize(old_timer); | |
331 | } | |
332 | ||
333 | /* | |
334 | * Start timer that was running before interrupt. | |
335 | */ | |
336 | old_timer->tstamp = ts; | |
337 | current_timer[mycpu] = old_timer; | |
338 | ||
339 | mp_enable_preemption(); | |
340 | } | |
341 | ||
342 | /* | |
343 | * timer_switch switches to a new timer. The machine | |
344 | * dependent routine/macro get_timestamp must return a timestamp. | |
345 | * Caller must lock out interrupts. | |
346 | */ | |
347 | void | |
348 | timer_switch( | |
349 | timer_t new_timer) | |
350 | { | |
351 | int elapsed; | |
352 | int mycpu; | |
353 | timer_t mytimer; | |
354 | unsigned ts; | |
355 | ||
356 | mp_disable_preemption(); | |
357 | ||
358 | /* | |
359 | * Calculate elapsed time. | |
360 | */ | |
361 | mycpu = cpu_number(); | |
362 | mytimer = current_timer[mycpu]; | |
363 | ts = get_timestamp(); | |
364 | elapsed = ts - mytimer->tstamp; | |
365 | #ifdef TIMER_MAX | |
366 | if (elapsed < 0) elapsed += TIMER_MAX; | |
367 | #endif /* TIMER_MAX */ | |
368 | ||
369 | /* | |
370 | * Update current timer. | |
371 | */ | |
372 | mytimer->low_bits += elapsed; | |
373 | mytimer->tstamp = 0; | |
374 | ||
375 | /* | |
376 | * Normalization check | |
377 | */ | |
378 | if (mytimer->low_bits & TIMER_LOW_FULL) { | |
379 | timer_normalize(mytimer); | |
380 | } | |
381 | ||
382 | /* | |
383 | * Record new timer. | |
384 | */ | |
385 | current_timer[mycpu] = new_timer; | |
386 | new_timer->tstamp = ts; | |
387 | ||
388 | mp_enable_preemption(); | |
389 | } | |
390 | ||
391 | #endif /* MACHINE_TIMER_ROUTINES */ | |
392 | #endif /* STAT_TIME */ | |
393 | ||
394 | /* | |
395 | * timer_normalize normalizes the value of a timer. It is | |
396 | * called only rarely, to make sure low_bits never overflows. | |
397 | */ | |
398 | ||
399 | void | |
400 | timer_normalize( | |
401 | register timer_t timer) | |
402 | { | |
403 | unsigned int high_increment; | |
404 | ||
405 | /* | |
406 | * Calculate high_increment, then write high check field first | |
407 | * followed by low and high. timer_grab() reads these fields in | |
408 | * reverse order so if high and high check match, we know | |
409 | * that the values read are ok. | |
410 | */ | |
411 | ||
412 | high_increment = timer->low_bits/TIMER_HIGH_UNIT; | |
413 | timer->high_bits_check += high_increment; | |
414 | timer->low_bits %= TIMER_HIGH_UNIT; | |
415 | timer->high_bits += high_increment; | |
416 | } | |
417 | ||
418 | /* | |
419 | * timer_grab() retrieves the value of a timer. | |
420 | * | |
421 | * Critical scheduling code uses TIMER_DELTA macro in timer.h | |
422 | * (called from thread_timer_delta in sched.h). | |
423 | * | |
424 | * Keep coherent with db_time_grab below. | |
425 | */ | |
426 | ||
427 | void | |
428 | timer_grab( | |
429 | timer_t timer, | |
430 | timer_save_t save) | |
431 | { | |
432 | #if MACH_ASSERT | |
433 | unsigned int passes=0; | |
434 | #endif | |
435 | do { | |
436 | (save)->high = (timer)->high_bits; | |
437 | (save)->low = (timer)->low_bits; | |
438 | /* | |
439 | * If the timer was normalized while we were doing this, | |
440 | * the high_bits value read above and the high_bits check | |
441 | * value will not match because high_bits_check is the first | |
442 | * field touched by the normalization procedure, and | |
443 | * high_bits is the last. | |
444 | * | |
445 | * Additions to timer only touch low bits and | |
446 | * are therefore atomic with respect to this. | |
447 | */ | |
448 | #if MACH_ASSERT | |
449 | passes++; | |
450 | assert(passes < 10000); | |
451 | #endif | |
452 | } while ( (save)->high != (timer)->high_bits_check); | |
453 | } | |
454 | ||
455 | /* | |
456 | * | |
457 | * Db_timer_grab(): used by db_thread_read_times. An nonblocking | |
458 | * version of db_thread_get_times. Keep coherent with timer_grab | |
459 | * above. | |
460 | * | |
461 | */ | |
462 | void | |
463 | db_timer_grab( | |
464 | timer_t timer, | |
465 | timer_save_t save) | |
466 | { | |
467 | /* Don't worry about coherency */ | |
468 | ||
469 | (save)->high = (timer)->high_bits; | |
470 | (save)->low = (timer)->low_bits; | |
471 | } | |
472 | ||
473 | ||
474 | /* | |
475 | * timer_read reads the value of a timer into a time_value_t. If the | |
476 | * timer was modified during the read, retry. The value returned | |
477 | * is accurate to the last update; time accumulated by a running | |
478 | * timer since its last timestamp is not included. | |
479 | */ | |
480 | ||
481 | void | |
482 | timer_read( | |
483 | timer_t timer, | |
484 | register time_value_t *tv) | |
485 | { | |
486 | timer_save_data_t temp; | |
487 | ||
488 | timer_grab(timer,&temp); | |
489 | /* | |
490 | * Normalize the result | |
491 | */ | |
492 | #ifdef TIMER_ADJUST | |
493 | TIMER_ADJUST(&temp); | |
494 | #endif /* TIMER_ADJUST */ | |
495 | tv->seconds = temp.high + temp.low/1000000; | |
496 | tv->microseconds = temp.low%1000000; | |
497 | } | |
498 | ||
499 | /* | |
500 | * thread_read_times reads the user and system times from a thread. | |
501 | * Time accumulated since last timestamp is not included. Should | |
502 | * be called at splsched() to avoid having user and system times | |
503 | * be out of step. Doesn't care if caller locked thread. | |
504 | * | |
505 | * Needs to be kept coherent with thread_read_times ahead. | |
506 | */ | |
507 | void | |
508 | thread_read_times( | |
509 | thread_t thread, | |
510 | time_value_t *user_time_p, | |
511 | time_value_t *system_time_p) | |
512 | { | |
513 | timer_save_data_t temp; | |
514 | register timer_t timer; | |
515 | ||
516 | timer = &thread->user_timer; | |
517 | timer_grab(timer, &temp); | |
518 | ||
519 | #ifdef TIMER_ADJUST | |
520 | TIMER_ADJUST(&temp); | |
521 | #endif /* TIMER_ADJUST */ | |
522 | user_time_p->seconds = temp.high + temp.low/1000000; | |
523 | user_time_p->microseconds = temp.low % 1000000; | |
524 | ||
525 | timer = &thread->system_timer; | |
526 | timer_grab(timer, &temp); | |
527 | ||
528 | #ifdef TIMER_ADJUST | |
529 | TIMER_ADJUST(&temp); | |
530 | #endif /* TIMER_ADJUST */ | |
531 | system_time_p->seconds = temp.high + temp.low/1000000; | |
532 | system_time_p->microseconds = temp.low % 1000000; | |
533 | } | |
534 | ||
535 | /* | |
536 | * Db_thread_read_times: A version of thread_read_times that | |
537 | * can be called by the debugger. This version does not call | |
538 | * timer_grab, which can block. Please keep it up to date with | |
539 | * thread_read_times above. | |
540 | * | |
541 | */ | |
542 | void | |
543 | db_thread_read_times( | |
544 | thread_t thread, | |
545 | time_value_t *user_time_p, | |
546 | time_value_t *system_time_p) | |
547 | { | |
548 | timer_save_data_t temp; | |
549 | register timer_t timer; | |
550 | ||
551 | timer = &thread->user_timer; | |
552 | db_timer_grab(timer, &temp); | |
553 | ||
554 | #ifdef TIMER_ADJUST | |
555 | TIMER_ADJUST(&temp); | |
556 | #endif /* TIMER_ADJUST */ | |
557 | user_time_p->seconds = temp.high + temp.low/1000000; | |
558 | user_time_p->microseconds = temp.low % 1000000; | |
559 | ||
560 | timer = &thread->system_timer; | |
561 | timer_grab(timer, &temp); | |
562 | ||
563 | #ifdef TIMER_ADJUST | |
564 | TIMER_ADJUST(&temp); | |
565 | #endif /* TIMER_ADJUST */ | |
566 | system_time_p->seconds = temp.high + temp.low/1000000; | |
567 | system_time_p->microseconds = temp.low % 1000000; | |
568 | } | |
569 | ||
570 | /* | |
571 | * timer_delta takes the difference of a saved timer value | |
572 | * and the current one, and updates the saved value to current. | |
573 | * The difference is returned as a function value. See | |
574 | * TIMER_DELTA macro (timer.h) for optimization to this. | |
575 | */ | |
576 | ||
577 | unsigned | |
578 | timer_delta( | |
579 | register timer_t timer, | |
580 | timer_save_t save) | |
581 | { | |
582 | timer_save_data_t new_save; | |
583 | register unsigned result; | |
584 | ||
585 | timer_grab(timer,&new_save); | |
586 | result = (new_save.high - save->high) * TIMER_HIGH_UNIT + | |
587 | new_save.low - save->low; | |
588 | save->high = new_save.high; | |
589 | save->low = new_save.low; | |
590 | return(result); | |
591 | } |