]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_clock.c
3c56bf012160b5498c9ee7011286404044080c9d
[apple/xnu.git] / bsd / kern / kern_clock.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
31 /*-
32 * Copyright (c) 1982, 1986, 1991, 1993
33 * The Regents of the University of California. All rights reserved.
34 * (c) UNIX System Laboratories, Inc.
35 * All or some portions of this file are derived from material licensed
36 * to the University of California by American Telephone and Telegraph
37 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
38 * the permission of UNIX System Laboratories, Inc.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * 3. All advertising materials mentioning features or use of this software
49 * must display the following acknowledgement:
50 * This product includes software developed by the University of
51 * California, Berkeley and its contributors.
52 * 4. Neither the name of the University nor the names of its contributors
53 * may be used to endorse or promote products derived from this software
54 * without specific prior written permission.
55 *
56 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
57 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
58 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
59 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
60 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
61 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
62 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
63 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
64 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
65 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
66 * SUCH DAMAGE.
67 *
68 * @(#)kern_clock.c 8.5 (Berkeley) 1/21/94
69 */
70 /*
71 * HISTORY
72 */
73
74 #include <machine/spl.h>
75
76 #include <sys/param.h>
77 #include <sys/systm.h>
78 #include <sys/time.h>
79 #include <sys/resourcevar.h>
80 #include <sys/kernel.h>
81 #include <sys/resource.h>
82 #include <sys/proc_internal.h>
83 #include <sys/vm.h>
84 #include <sys/sysctl.h>
85
86 #ifdef GPROF
87 #include <sys/gmon.h>
88 #endif
89
90 #include <kern/thread.h>
91 #include <kern/ast.h>
92 #include <kern/assert.h>
93 #include <mach/boolean.h>
94
95 #include <kern/thread_call.h>
96
97 void bsd_uprofil(struct time_value *syst, user_addr_t pc);
98 void get_procrustime(time_value_t *tv);
99 int sysctl_clockrate(user_addr_t where, size_t *sizep);
100 int tvtohz(struct timeval *tv);
101 extern void psignal_sigprof(struct proc *);
102 extern void psignal_vtalarm(struct proc *);
103 extern void psignal_xcpu(struct proc *);
104
105 /*
106 * Clock handling routines.
107 *
108 * This code is written to operate with two timers which run
109 * independently of each other. The main clock, running at hz
110 * times per second, is used to do scheduling and timeout calculations.
111 * The second timer does resource utilization estimation statistically
112 * based on the state of the machine phz times a second. Both functions
113 * can be performed by a single clock (ie hz == phz), however the
114 * statistics will be much more prone to errors. Ideally a machine
115 * would have separate clocks measuring time spent in user state, system
116 * state, interrupt state, and idle state. These clocks would allow a non-
117 * approximate measure of resource utilization.
118 */
119
120 /*
121 * The hz hardware interval timer.
122 * We update the events relating to real time.
123 * If this timer is also being used to gather statistics,
124 * we run through the statistics gathering routine as well.
125 */
126
127 int hz = 100; /* GET RID OF THIS !!! */
128 int tick = (1000000 / 100); /* GET RID OF THIS !!! */
129
130 int bsd_hardclockinit = 0;
131 /*ARGSUSED*/
132 void
133 bsd_hardclock(
134 boolean_t usermode,
135 #ifdef GPROF
136 caddr_t pc,
137 #else
138 __unused caddr_t pc,
139 #endif
140 int numticks
141 )
142 {
143 register struct proc *p;
144 register thread_t thread;
145 int nusecs = numticks * tick;
146 struct timeval tv;
147
148 if (!bsd_hardclockinit)
149 return;
150
151 if (bsd_hardclockinit < 0) {
152 return;
153 }
154
155 thread = current_thread();
156 /*
157 * Charge the time out based on the mode the cpu is in.
158 * Here again we fudge for the lack of proper interval timers
159 * assuming that the current state has been around at least
160 * one tick.
161 */
162 p = (struct proc *)current_proc();
163 if (p && ((p->p_flag & P_WEXIT) == 0)) {
164 if (usermode) {
165 if (p->p_stats && p->p_stats->p_prof.pr_scale) {
166 p->p_flag |= P_OWEUPC;
167 astbsd_on();
168 }
169
170 /*
171 * CPU was in user state. Increment
172 * user time counter, and process process-virtual time
173 * interval timer.
174 */
175 if (p->p_stats &&
176 timerisset(&p->p_stats->p_timer[ITIMER_VIRTUAL].it_value) &&
177 !itimerdecr(&p->p_stats->p_timer[ITIMER_VIRTUAL], nusecs)) {
178
179 /* does psignal(p, SIGVTALRM) in a thread context */
180 thread_call_func((thread_call_func_t)psignal_vtalarm, p, FALSE);
181 }
182 }
183
184 /*
185 * If the cpu is currently scheduled to a process, then
186 * charge it with resource utilization for a tick, updating
187 * statistics which run in (user+system) virtual time,
188 * such as the cpu time limit and profiling timers.
189 * This assumes that the current process has been running
190 * the entire last tick.
191 */
192 if (!is_thread_idle(thread)) {
193 if (p->p_limit &&
194 p->p_limit->pl_rlimit[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) {
195 time_value_t sys_time, user_time;
196
197 thread_read_times(thread, &user_time, &sys_time);
198 if ((sys_time.seconds + user_time.seconds + 1) >
199 p->p_limit->pl_rlimit[RLIMIT_CPU].rlim_cur) {
200
201 /* does psignal(p, SIGXCPU) in a thread context */
202 thread_call_func((thread_call_func_t)psignal_xcpu, p, FALSE);
203
204 if (p->p_limit->pl_rlimit[RLIMIT_CPU].rlim_cur <
205 p->p_limit->pl_rlimit[RLIMIT_CPU].rlim_max)
206 p->p_limit->pl_rlimit[RLIMIT_CPU].rlim_cur += 5;
207 }
208 }
209 if (timerisset(&p->p_stats->p_timer[ITIMER_PROF].it_value) &&
210 !itimerdecr(&p->p_stats->p_timer[ITIMER_PROF], nusecs)) {
211
212 /* does psignal(p, SIGPROF) in a thread context */
213 thread_call_func((thread_call_func_t)psignal_sigprof, p, FALSE);
214 }
215 }
216 }
217
218 #ifdef GPROF
219 /*
220 * Gather some statistics.
221 */
222 gatherstats(usermode, pc);
223 #endif
224 }
225
226 /*
227 * Gather some statistics.
228 */
229 /*ARGSUSED*/
230 void
231 gatherstats(
232 #ifdef GPROF
233 boolean_t usermode,
234 caddr_t pc
235 #else
236 __unused boolean_t usermode,
237 __unused caddr_t pc
238 #endif
239 )
240
241 {
242 #ifdef GPROF
243 if (!usermode) {
244 struct gmonparam *p = &_gmonparam;
245
246 if (p->state == GMON_PROF_ON) {
247 register int s;
248
249 s = pc - p->lowpc;
250 if (s < p->textsize) {
251 s /= (HISTFRACTION * sizeof(*p->kcount));
252 p->kcount[s]++;
253 }
254 }
255 }
256 #endif
257 }
258
259
260 /*
261 * Kernel timeout services.
262 */
263
264 /*
265 * Set a timeout.
266 *
267 * fcn: function to call
268 * param: parameter to pass to function
269 * interval: timeout interval, in hz.
270 */
271 void
272 timeout(
273 timeout_fcn_t fcn,
274 void *param,
275 int interval)
276 {
277 uint64_t deadline;
278
279 clock_interval_to_deadline(interval, NSEC_PER_SEC / hz, &deadline);
280 thread_call_func_delayed((thread_call_func_t)fcn, param, deadline);
281 }
282
283 /*
284 * Cancel a timeout.
285 */
286 void
287 untimeout(
288 register timeout_fcn_t fcn,
289 register void *param)
290 {
291 thread_call_func_cancel((thread_call_func_t)fcn, param, FALSE);
292 }
293
294
295 /*
296 * Set a timeout.
297 *
298 * fcn: function to call
299 * param: parameter to pass to function
300 * ts: timeout interval, in timespec
301 */
302 void
303 bsd_timeout(
304 timeout_fcn_t fcn,
305 void *param,
306 struct timespec *ts)
307 {
308 uint64_t deadline = 0;
309
310 if (ts && (ts->tv_sec || ts->tv_nsec)) {
311 nanoseconds_to_absolutetime((uint64_t)ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec, &deadline );
312 clock_absolutetime_interval_to_deadline( deadline, &deadline );
313 }
314 thread_call_func_delayed((thread_call_func_t)fcn, param, deadline);
315 }
316
317 /*
318 * Cancel a timeout.
319 */
320 void
321 bsd_untimeout(
322 register timeout_fcn_t fcn,
323 register void *param)
324 {
325 thread_call_func_cancel((thread_call_func_t)fcn, param, FALSE);
326 }
327
328
329 /*
330 * Compute number of hz until specified time.
331 * Used to compute third argument to timeout() from an
332 * absolute time.
333 */
334 int
335 hzto(tv)
336 struct timeval *tv;
337 {
338 struct timeval now;
339 register long ticks;
340 register long sec;
341
342 microtime(&now);
343 /*
344 * If number of milliseconds will fit in 32 bit arithmetic,
345 * then compute number of milliseconds to time and scale to
346 * ticks. Otherwise just compute number of hz in time, rounding
347 * times greater than representible to maximum value.
348 *
349 * Delta times less than 25 days can be computed ``exactly''.
350 * Maximum value for any timeout in 10ms ticks is 250 days.
351 */
352 sec = tv->tv_sec - now.tv_sec;
353 if (sec <= 0x7fffffff / 1000 - 1000)
354 ticks = ((tv->tv_sec - now.tv_sec) * 1000 +
355 (tv->tv_usec - now.tv_usec) / 1000)
356 / (tick / 1000);
357 else if (sec <= 0x7fffffff / hz)
358 ticks = sec * hz;
359 else
360 ticks = 0x7fffffff;
361
362 return (ticks);
363 }
364
365 /*
366 * Return information about system clocks.
367 */
368 int
369 sysctl_clockrate(user_addr_t where, size_t *sizep)
370 {
371 struct clockinfo clkinfo;
372
373 /*
374 * Construct clockinfo structure.
375 */
376 clkinfo.hz = hz;
377 clkinfo.tick = tick;
378 clkinfo.profhz = hz;
379 clkinfo.stathz = hz;
380 return sysctl_rdstruct(where, sizep, USER_ADDR_NULL, &clkinfo, sizeof(clkinfo));
381 }
382
383
384 /*
385 * Compute number of ticks in the specified amount of time.
386 */
387 int
388 tvtohz(struct timeval *tv)
389 {
390 register unsigned long ticks;
391 register long sec, usec;
392
393 /*
394 * If the number of usecs in the whole seconds part of the time
395 * difference fits in a long, then the total number of usecs will
396 * fit in an unsigned long. Compute the total and convert it to
397 * ticks, rounding up and adding 1 to allow for the current tick
398 * to expire. Rounding also depends on unsigned long arithmetic
399 * to avoid overflow.
400 *
401 * Otherwise, if the number of ticks in the whole seconds part of
402 * the time difference fits in a long, then convert the parts to
403 * ticks separately and add, using similar rounding methods and
404 * overflow avoidance. This method would work in the previous
405 * case but it is slightly slower and assumes that hz is integral.
406 *
407 * Otherwise, round the time difference down to the maximum
408 * representable value.
409 *
410 * If ints have 32 bits, then the maximum value for any timeout in
411 * 10ms ticks is 248 days.
412 */
413 sec = tv->tv_sec;
414 usec = tv->tv_usec;
415 if (usec < 0) {
416 sec--;
417 usec += 1000000;
418 }
419 if (sec < 0) {
420 #ifdef DIAGNOSTIC
421 if (usec > 0) {
422 sec++;
423 usec -= 1000000;
424 }
425 printf("tvotohz: negative time difference %ld sec %ld usec\n",
426 sec, usec);
427 #endif
428 ticks = 1;
429 } else if (sec <= LONG_MAX / 1000000)
430 ticks = (sec * 1000000 + (unsigned long)usec + (tick - 1))
431 / tick + 1;
432 else if (sec <= LONG_MAX / hz)
433 ticks = sec * hz
434 + ((unsigned long)usec + (tick - 1)) / tick + 1;
435 else
436 ticks = LONG_MAX;
437 if (ticks > INT_MAX)
438 ticks = INT_MAX;
439 return ((int)ticks);
440 }
441
442
443 /*
444 * Start profiling on a process.
445 *
446 * Kernel profiling passes kernel_proc which never exits and hence
447 * keeps the profile clock running constantly.
448 */
449 void
450 startprofclock(p)
451 register struct proc *p;
452 {
453 if ((p->p_flag & P_PROFIL) == 0)
454 p->p_flag |= P_PROFIL;
455 }
456
457 /*
458 * Stop profiling on a process.
459 */
460 void
461 stopprofclock(p)
462 register struct proc *p;
463 {
464 if (p->p_flag & P_PROFIL)
465 p->p_flag &= ~P_PROFIL;
466 }
467
468 void
469 bsd_uprofil(struct time_value *syst, user_addr_t pc)
470 {
471 struct proc *p = current_proc();
472 int ticks;
473 struct timeval *tv;
474 struct timeval st;
475
476 if (p == NULL)
477 return;
478 if ( !(p->p_flag & P_PROFIL))
479 return;
480
481 st.tv_sec = syst->seconds;
482 st.tv_usec = syst->microseconds;
483
484 tv = &(p->p_stats->p_ru.ru_stime);
485
486 ticks = ((tv->tv_sec - st.tv_sec) * 1000 +
487 (tv->tv_usec - st.tv_usec) / 1000) /
488 (tick / 1000);
489 if (ticks)
490 addupc_task(p, pc, ticks);
491 }
492
493 void
494 get_procrustime(time_value_t *tv)
495 {
496 struct proc *p = current_proc();
497 struct timeval st;
498
499 if (p == NULL)
500 return;
501 if ( !(p->p_flag & P_PROFIL))
502 return;
503
504 st = p->p_stats->p_ru.ru_stime;
505
506 tv->seconds = st.tv_sec;
507 tv->microseconds = st.tv_usec;
508 }