]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_time.c
xnu-123.5.tar.gz
[apple/xnu.git] / bsd / kern / kern_time.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
23/*
24 * Copyright (c) 1982, 1986, 1989, 1993
25 * The Regents of the University of California. All rights reserved.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 * 3. All advertising materials mentioning features or use of this software
36 * must display the following acknowledgement:
37 * This product includes software developed by the University of
38 * California, Berkeley and its contributors.
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53 * SUCH DAMAGE.
54 *
55 * @(#)kern_time.c 8.4 (Berkeley) 5/26/95
56 */
57
58#include <sys/param.h>
59#include <sys/resourcevar.h>
60#include <sys/kernel.h>
61#include <sys/systm.h>
62#include <sys/proc.h>
63#include <sys/vnode.h>
64
65#include <sys/mount.h>
66
67#include <kern/cpu_number.h>
68
69#include <kern/clock.h>
70
71#define HZ 100 /* XXX */
72
73struct timeval time;
74
75/*
76 * Time of day and interval timer support.
77 *
78 * These routines provide the kernel entry points to get and set
79 * the time-of-day and per-process interval timers. Subroutines
80 * here provide support for adding and subtracting timeval structures
81 * and decrementing interval timers, optionally reloading the interval
82 * timers when they expire.
83 */
84struct gettimeofday_args{
85 struct timeval *tp;
86 struct timezone *tzp;
87};
88/* ARGSUSED */
89int
90gettimeofday(p, uap, retval)
91 struct proc *p;
92 register struct gettimeofday_args *uap;
93 register_t *retval;
94{
95 struct timeval atv;
96 int error = 0;
97
98 if (uap->tp) {
99 microtime(&atv);
100 if (error = copyout((caddr_t)&atv, (caddr_t)uap->tp,
101 sizeof (atv)))
102 return(error);
103 }
104
105 if (uap->tzp)
106 error = copyout((caddr_t)&tz, (caddr_t)uap->tzp,
107 sizeof (tz));
108
109 return(error);
110}
111
112struct settimeofday_args {
113 struct timeval *tv;
114 struct timezone *tzp;
115};
116/* ARGSUSED */
117int
118settimeofday(p, uap, retval)
119 struct proc *p;
120 struct settimeofday_args *uap;
121 register_t *retval;
122{
123 struct timeval atv;
124 struct timezone atz;
125 int error, s;
126
127 if (error = suser(p->p_ucred, &p->p_acflag))
128 return (error);
129 /* Verify all parameters before changing time. */
130 if (uap->tv && (error = copyin((caddr_t)uap->tv,
131 (caddr_t)&atv, sizeof(atv))))
132 return (error);
133 if (uap->tzp && (error = copyin((caddr_t)uap->tzp,
134 (caddr_t)&atz, sizeof(atz))))
135 return (error);
136 if (uap->tv)
137 setthetime(&atv);
138 if (uap->tzp)
139 tz = atz;
140 return (0);
141}
142
143setthetime(tv)
144 struct timeval *tv;
145{
146 mach_timespec_t now;
147 long delta;
148 int s;
149
150 now.tv_sec = tv->tv_sec;
151 now.tv_nsec = tv->tv_usec * NSEC_PER_USEC;
152
153 clock_set_calendar_value(now);
154 delta = tv->tv_sec - time.tv_sec;
155 boottime.tv_sec += delta;
156#if NFSCLIENT || NFSSERVER
157 lease_updatetime(delta);
158#endif
159 s = splhigh();
160 microtime(&time);
161 splx(s);
162}
163
164int tickadj = 240000 / (60 * HZ); /* "standard" clock skew, us./tick */
165int tickdelta; /* current clock skew, us. per tick */
166long timedelta; /* unapplied time correction, us. */
167long bigadj = 1000000; /* use 10x skew above bigadj us. */
168
169struct adjtime_args {
170 struct timeval *delta;
171 struct timeval *olddelta;
172};
173/* ARGSUSED */
174int
175adjtime(p, uap, retval)
176 struct proc *p;
177 register struct adjtime_args *uap;
178 register_t *retval;
179{
180 struct timeval atv, oatv;
181 register long ndelta;
182 int s, error;
183
184 if (error = suser(p->p_ucred, &p->p_acflag))
185 return (error);
186 if(error = copyin((caddr_t)uap->delta, (caddr_t)&atv,
187 sizeof (struct timeval)))
188 return(error);
189
190 ndelta = atv.tv_sec * 1000000 + atv.tv_usec;
191 if (timedelta == 0)
192 if (ndelta > bigadj)
193 tickdelta = 10 * tickadj;
194 else
195 tickdelta = tickadj;
196 if (ndelta % tickdelta)
197 ndelta = ndelta / tickdelta * tickdelta;
198
199 s = splclock();
200 if (uap->olddelta) {
201 oatv.tv_sec = timedelta / 1000000;
202 oatv.tv_usec = timedelta % 1000000;
203 }
204 timedelta = ndelta;
205 splx(s);
206
207 if (uap->olddelta)
208 (void) copyout((caddr_t)&oatv, (caddr_t)uap->olddelta,
209 sizeof (struct timeval));
210 return(0);
211}
212
213#define SECDAY ((unsigned)(24*60*60)) /* seconds per day */
214#define SECYR ((unsigned)(365*SECDAY)) /* per common year */
215#define YRREF 70 /* UNIX time referenced to 1970 */
216
217/*
218 * Initialze the time of day register.
219 * Trust the RTC except for the case where it is set before
220 * the UNIX epoch. In that case use the the UNIX epoch.
221 * The argument passed in is ignored.
222 */
223void
224inittodr(base)
225 time_t base;
226{
227 /*
228 * Initialize the calendar by
229 * reading the BBC, if not already set.
230 */
231 clock_initialize_calendar();
232
233 /*
234 * The value returned by microtime()
235 * is gotten from the calendar.
236 */
237 microtime(&time);
238
239 /*
240 * This variable still exists to keep
241 * 'w' happy. It should only be considered
242 * an approximation.
243 */
244 boottime.tv_sec = time.tv_sec;
245 boottime.tv_usec = 0;
246
247 /*
248 * If the RTC does not have acceptable value, i.e. time before
249 * the UNIX epoch, set it to the UNIX epoch
250 */
251 if (time.tv_sec < 0) {
252 printf ("WARNING: preposterous time in Real Time Clock");
253 time.tv_sec = 0; /* the UNIX epoch */
254 time.tv_usec = 0;
255 setthetime(&time);
256 boottime = time;
257 printf(" -- CHECK AND RESET THE DATE!\n");
258 }
259
260 return;
261}
262
263/*
264 * Get value of an interval timer. The process virtual and
265 * profiling virtual time timers are kept in the u. area, since
266 * they can be swapped out. These are kept internally in the
267 * way they are specified externally: in time until they expire.
268 *
269 * The real time interval timer is kept in the process table slot
270 * for the process, and its value (it_value) is kept as an
271 * absolute time rather than as a delta, so that it is easy to keep
272 * periodic real-time signals from drifting.
273 *
274 * Virtual time timers are processed in the hardclock() routine of
275 * kern_clock.c. The real time timer is processed by a timeout
276 * routine, called from the softclock() routine. Since a callout
277 * may be delayed in real time due to interrupt processing in the system,
278 * it is possible for the real time timeout routine (realitexpire, given below),
279 * to be delayed in real time past when it is supposed to occur. It
280 * does not suffice, therefore, to reload the real timer .it_value from the
281 * real time timers .it_interval. Rather, we compute the next time in
282 * absolute time the timer should go off.
283 */
284
285struct getitimer_args {
286 u_int which;
287 struct itimerval *itv;
288};
289/* ARGSUSED */
290int
291getitimer(p, uap, retval)
292 struct proc *p;
293 register struct getitimer_args *uap;
294 register_t *retval;
295{
296 struct itimerval aitv;
297 int s;
298
299 if (uap->which > ITIMER_PROF)
300 return(EINVAL);
301
302 s = splclock();
303 if (uap->which == ITIMER_REAL) {
304 /*
305 * Convert from absoulte to relative time in .it_value
306 * part of real time timer. If time for real time timer
307 * has passed return 0, else return difference between
308 * current time and time for the timer to go off.
309 */
310 aitv = p->p_realtimer;
311 if (timerisset(&aitv.it_value))
312 if (timercmp(&aitv.it_value, &time, <))
313 timerclear(&aitv.it_value);
314 else
315 timevalsub(&aitv.it_value, &time);
316 } else
317 aitv =p->p_stats->p_timer[uap->which];
318 splx(s);
319 return(copyout((caddr_t)&aitv, (caddr_t)uap->itv,
320 sizeof (struct itimerval)));
321}
322
323struct setitimer_args {
324 u_int which;
325 struct itimerval *itv;
326 struct itimerval *oitv;
327};
328/* ARGSUSED */
329int
330setitimer(p, uap, retval)
331 struct proc *p;
332 register struct setitimer_args *uap;
333 register_t *retval;
334{
335 struct itimerval aitv;
336 register struct itimerval *itvp;
337 int s, error;
338
339 if (uap->which > ITIMER_PROF)
340 return(EINVAL);
341 itvp = uap->itv;
342 if (itvp && (error = copyin((caddr_t)itvp, (caddr_t)&aitv,
343 sizeof(struct itimerval))))
344 return (error);
345 if ((uap->itv = uap->oitv) &&
346 (error = getitimer(p, uap, retval)))
347 return (error);
348 if (itvp == 0)
349 return (0);
350 if (itimerfix(&aitv.it_value) || itimerfix(&aitv.it_interval))
351 return (EINVAL);
352 s = splclock();
353 if (uap->which == ITIMER_REAL) {
354 untimeout(realitexpire, (caddr_t)p);
355 if (timerisset(&aitv.it_value)) {
356 timevaladd(&aitv.it_value, &time);
357 timeout(realitexpire, (caddr_t)p, hzto(&aitv.it_value));
358 }
359 p->p_realtimer = aitv;
360 } else
361 p->p_stats->p_timer[uap->which] = aitv;
362 splx(s);
363 return(0); /* To insure good return value on success */
364}
365
366/*
367 * Real interval timer expired:
368 * send process whose timer expired an alarm signal.
369 * If time is not set up to reload, then just return.
370 * Else compute next time timer should go off which is > current time.
371 * This is where delay in processing this timeout causes multiple
372 * SIGALRM calls to be compressed into one.
373 */
374void
375realitexpire(arg)
376 void *arg;
377{
378 register struct proc *p;
379 int s;
380 boolean_t funnel_state;
381
382 funnel_state = thread_funnel_set(kernel_flock,TRUE);
383
384 p = (struct proc *)arg;
385 psignal(p, SIGALRM);
386 if (!timerisset(&p->p_realtimer.it_interval)) {
387 timerclear(&p->p_realtimer.it_value);
388 (void) thread_funnel_set(kernel_flock, FALSE);
389 return;
390 }
391
392 /*
393 * If the time's way off, don't try to compensate by getting
394 * there incrementally.
395 */
396 s = splclock();
397 if (p->p_realtimer.it_value.tv_sec < time.tv_sec - 10) {
398 p->p_realtimer.it_value = time;
399 timeout(realitexpire, (caddr_t)p,
400 hzto(&p->p_realtimer.it_value));
401 splx(s);
402 (void) thread_funnel_set(kernel_flock, FALSE);
403 return;
404
405 }
406 splx(s);
407
408 for (;;) {
409 s = splclock();
410 timevaladd(&p->p_realtimer.it_value,
411 &p->p_realtimer.it_interval);
412 if (timercmp(&p->p_realtimer.it_value, &time, >)) {
413 timeout(realitexpire, (caddr_t)p,
414 hzto(&p->p_realtimer.it_value));
415 splx(s);
416 (void) thread_funnel_set(kernel_flock, FALSE);
417 return;
418 }
419 splx(s);
420 }
421
422 (void) thread_funnel_set(kernel_flock, FALSE);
423}
424
425/*
426 * Check that a proposed value to load into the .it_value or
427 * .it_interval part of an interval timer is acceptable, and
428 * fix it to have at least minimal value (i.e. if it is less
429 * than the resolution of the clock, round it up.)
430 */
431int
432itimerfix(tv)
433 struct timeval *tv;
434{
435
436 if (tv->tv_sec < 0 || tv->tv_sec > 100000000 ||
437 tv->tv_usec < 0 || tv->tv_usec >= 1000000)
438 return (EINVAL);
439 if (tv->tv_sec == 0 && tv->tv_usec != 0 && tv->tv_usec < tick)
440 tv->tv_usec = tick;
441 return (0);
442}
443
444/*
445 * Decrement an interval timer by a specified number
446 * of microseconds, which must be less than a second,
447 * i.e. < 1000000. If the timer expires, then reload
448 * it. In this case, carry over (usec - old value) to
449 * reducint the value reloaded into the timer so that
450 * the timer does not drift. This routine assumes
451 * that it is called in a context where the timers
452 * on which it is operating cannot change in value.
453 */
454int
455itimerdecr(itp, usec)
456 register struct itimerval *itp;
457 int usec;
458{
459
460 if (itp->it_value.tv_usec < usec) {
461 if (itp->it_value.tv_sec == 0) {
462 /* expired, and already in next interval */
463 usec -= itp->it_value.tv_usec;
464 goto expire;
465 }
466 itp->it_value.tv_usec += 1000000;
467 itp->it_value.tv_sec--;
468 }
469 itp->it_value.tv_usec -= usec;
470 usec = 0;
471 if (timerisset(&itp->it_value))
472 return (1);
473 /* expired, exactly at end of interval */
474expire:
475 if (timerisset(&itp->it_interval)) {
476 itp->it_value = itp->it_interval;
477 itp->it_value.tv_usec -= usec;
478 if (itp->it_value.tv_usec < 0) {
479 itp->it_value.tv_usec += 1000000;
480 itp->it_value.tv_sec--;
481 }
482 } else
483 itp->it_value.tv_usec = 0; /* sec is already 0 */
484 return (0);
485}
486
487/*
488 * Add and subtract routines for timevals.
489 * N.B.: subtract routine doesn't deal with
490 * results which are before the beginning,
491 * it just gets very confused in this case.
492 * Caveat emptor.
493 */
494void
495timevaladd(t1, t2)
496 struct timeval *t1, *t2;
497{
498
499 t1->tv_sec += t2->tv_sec;
500 t1->tv_usec += t2->tv_usec;
501 timevalfix(t1);
502}
503void
504timevalsub(t1, t2)
505 struct timeval *t1, *t2;
506{
507
508 t1->tv_sec -= t2->tv_sec;
509 t1->tv_usec -= t2->tv_usec;
510 timevalfix(t1);
511}
512void
513timevalfix(t1)
514 struct timeval *t1;
515{
516
517 if (t1->tv_usec < 0) {
518 t1->tv_sec--;
519 t1->tv_usec += 1000000;
520 }
521 if (t1->tv_usec >= 1000000) {
522 t1->tv_sec++;
523 t1->tv_usec -= 1000000;
524 }
525}
526
527/*
528 * Return the best possible estimate of the time in the timeval
529 * to which tvp points.
530 */
531void
532microtime(struct timeval * tvp)
533{
534 mach_timespec_t now = clock_get_calendar_value();
535
536 tvp->tv_sec = now.tv_sec;
537 tvp->tv_usec = now.tv_nsec / NSEC_PER_USEC;
538}