xref: /freebsd/sys/kern/kern_time.c (revision b88ec951e15a7f82e499edd42304a1294d8ece4f)
19454b2d8SWarner Losh /*-
2df8bae1dSRodney W. Grimes  * Copyright (c) 1982, 1986, 1989, 1993
3df8bae1dSRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
4df8bae1dSRodney W. Grimes  *
5df8bae1dSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
6df8bae1dSRodney W. Grimes  * modification, are permitted provided that the following conditions
7df8bae1dSRodney W. Grimes  * are met:
8df8bae1dSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
9df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
10df8bae1dSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
11df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
12df8bae1dSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
13df8bae1dSRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
14df8bae1dSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
15df8bae1dSRodney W. Grimes  *    without specific prior written permission.
16df8bae1dSRodney W. Grimes  *
17df8bae1dSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18df8bae1dSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19df8bae1dSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20df8bae1dSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21df8bae1dSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22df8bae1dSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23df8bae1dSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24df8bae1dSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25df8bae1dSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26df8bae1dSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27df8bae1dSRodney W. Grimes  * SUCH DAMAGE.
28df8bae1dSRodney W. Grimes  *
29df8bae1dSRodney W. Grimes  *	@(#)kern_time.c	8.1 (Berkeley) 6/10/93
30df8bae1dSRodney W. Grimes  */
31df8bae1dSRodney W. Grimes 
32677b542eSDavid E. O'Brien #include <sys/cdefs.h>
33677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
34677b542eSDavid E. O'Brien 
354b8d5f2dSRobert Watson #include "opt_mac.h"
364b8d5f2dSRobert Watson 
37df8bae1dSRodney W. Grimes #include <sys/param.h>
38e96c1fdcSPeter Wemm #include <sys/systm.h>
39fb919e4dSMark Murray #include <sys/lock.h>
40fb919e4dSMark Murray #include <sys/mutex.h>
41d2d3e875SBruce Evans #include <sys/sysproto.h>
42df8bae1dSRodney W. Grimes #include <sys/resourcevar.h>
43797f2d22SPoul-Henning Kamp #include <sys/signalvar.h>
44df8bae1dSRodney W. Grimes #include <sys/kernel.h>
454b8d5f2dSRobert Watson #include <sys/mac.h>
46efa42cbcSPaul Saab #include <sys/syscallsubr.h>
4794c8fcd8SPeter Wemm #include <sys/sysent.h>
48df8bae1dSRodney W. Grimes #include <sys/proc.h>
49708e7684SPeter Wemm #include <sys/time.h>
5091266b96SPoul-Henning Kamp #include <sys/timetc.h>
51df8bae1dSRodney W. Grimes #include <sys/vnode.h>
52fb919e4dSMark Murray 
535b870b7bSPeter Wemm #include <vm/vm.h>
545b870b7bSPeter Wemm #include <vm/vm_extern.h>
55df8bae1dSRodney W. Grimes 
5691f1c2b3SPoul-Henning Kamp int tz_minuteswest;
5791f1c2b3SPoul-Henning Kamp int tz_dsttime;
58ac7e6123SDavid Greenman 
59df8bae1dSRodney W. Grimes /*
60df8bae1dSRodney W. Grimes  * Time of day and interval timer support.
61df8bae1dSRodney W. Grimes  *
62df8bae1dSRodney W. Grimes  * These routines provide the kernel entry points to get and set
63df8bae1dSRodney W. Grimes  * the time-of-day and per-process interval timers.  Subroutines
64df8bae1dSRodney W. Grimes  * here provide support for adding and subtracting timeval structures
65df8bae1dSRodney W. Grimes  * and decrementing interval timers, optionally reloading the interval
66df8bae1dSRodney W. Grimes  * timers when they expire.
67df8bae1dSRodney W. Grimes  */
68df8bae1dSRodney W. Grimes 
697edfb592SJohn Baldwin static int	settime(struct thread *, struct timeval *);
704d77a549SAlfred Perlstein static void	timevalfix(struct timeval *);
714d77a549SAlfred Perlstein static void	no_lease_updatetime(int);
7294c8fcd8SPeter Wemm 
731b09ae77SPoul-Henning Kamp static void
741b09ae77SPoul-Henning Kamp no_lease_updatetime(deltat)
751b09ae77SPoul-Henning Kamp 	int deltat;
761b09ae77SPoul-Henning Kamp {
771b09ae77SPoul-Henning Kamp }
781b09ae77SPoul-Henning Kamp 
794d77a549SAlfred Perlstein void (*lease_updatetime)(int)  = no_lease_updatetime;
801b09ae77SPoul-Henning Kamp 
8194c8fcd8SPeter Wemm static int
8291afe087SPoul-Henning Kamp settime(struct thread *td, struct timeval *tv)
8394c8fcd8SPeter Wemm {
84fcae3aa6SNick Sayer 	struct timeval delta, tv1, tv2;
85c0bd94a7SNick Sayer 	static struct timeval maxtime, laststep;
867ec73f64SPoul-Henning Kamp 	struct timespec ts;
8794c8fcd8SPeter Wemm 	int s;
8894c8fcd8SPeter Wemm 
89708e7684SPeter Wemm 	s = splclock();
909c8fff87SBruce Evans 	microtime(&tv1);
9100af9731SPoul-Henning Kamp 	delta = *tv;
9200af9731SPoul-Henning Kamp 	timevalsub(&delta, &tv1);
9394c8fcd8SPeter Wemm 
9494c8fcd8SPeter Wemm 	/*
959c8fff87SBruce Evans 	 * If the system is secure, we do not allow the time to be
96fcae3aa6SNick Sayer 	 * set to a value earlier than 1 second less than the highest
97fcae3aa6SNick Sayer 	 * time we have yet seen. The worst a miscreant can do in
98fcae3aa6SNick Sayer 	 * this circumstance is "freeze" time. He couldn't go
99fcae3aa6SNick Sayer 	 * back to the past.
100c0bd94a7SNick Sayer 	 *
101c0bd94a7SNick Sayer 	 * We similarly do not allow the clock to be stepped more
102c0bd94a7SNick Sayer 	 * than one second, nor more than once per second. This allows
103c0bd94a7SNick Sayer 	 * a miscreant to make the clock march double-time, but no worse.
10494c8fcd8SPeter Wemm 	 */
1057edfb592SJohn Baldwin 	if (securelevel_gt(td->td_ucred, 1) != 0) {
106fcae3aa6SNick Sayer 		if (delta.tv_sec < 0 || delta.tv_usec < 0) {
1073f92429aSMatt Jacob 			/*
108c0bd94a7SNick Sayer 			 * Update maxtime to latest time we've seen.
1093f92429aSMatt Jacob 			 */
110fcae3aa6SNick Sayer 			if (tv1.tv_sec > maxtime.tv_sec)
111fcae3aa6SNick Sayer 				maxtime = tv1;
112fcae3aa6SNick Sayer 			tv2 = *tv;
113fcae3aa6SNick Sayer 			timevalsub(&tv2, &maxtime);
114fcae3aa6SNick Sayer 			if (tv2.tv_sec < -1) {
1153f92429aSMatt Jacob 				tv->tv_sec = maxtime.tv_sec - 1;
116fcae3aa6SNick Sayer 				printf("Time adjustment clamped to -1 second\n");
117fcae3aa6SNick Sayer 			}
1183f92429aSMatt Jacob 		} else {
119c0bd94a7SNick Sayer 			if (tv1.tv_sec == laststep.tv_sec) {
120c0bd94a7SNick Sayer 				splx(s);
121c0bd94a7SNick Sayer 				return (EPERM);
122c0bd94a7SNick Sayer 			}
123c0bd94a7SNick Sayer 			if (delta.tv_sec > 1) {
124c0bd94a7SNick Sayer 				tv->tv_sec = tv1.tv_sec + 1;
125c0bd94a7SNick Sayer 				printf("Time adjustment clamped to +1 second\n");
126c0bd94a7SNick Sayer 			}
127c0bd94a7SNick Sayer 			laststep = *tv;
128fcae3aa6SNick Sayer 		}
1299c8fff87SBruce Evans 	}
1309c8fff87SBruce Evans 
1317ec73f64SPoul-Henning Kamp 	ts.tv_sec = tv->tv_sec;
1327ec73f64SPoul-Henning Kamp 	ts.tv_nsec = tv->tv_usec * 1000;
1337edfb592SJohn Baldwin 	mtx_lock(&Giant);
13491266b96SPoul-Henning Kamp 	tc_setclock(&ts);
13594c8fcd8SPeter Wemm 	(void) splsoftclock();
13694c8fcd8SPeter Wemm 	lease_updatetime(delta.tv_sec);
13794c8fcd8SPeter Wemm 	splx(s);
13894c8fcd8SPeter Wemm 	resettodr();
1397edfb592SJohn Baldwin 	mtx_unlock(&Giant);
14094c8fcd8SPeter Wemm 	return (0);
14194c8fcd8SPeter Wemm }
14294c8fcd8SPeter Wemm 
14394c8fcd8SPeter Wemm #ifndef _SYS_SYSPROTO_H_
14494c8fcd8SPeter Wemm struct clock_gettime_args {
14594c8fcd8SPeter Wemm 	clockid_t clock_id;
14694c8fcd8SPeter Wemm 	struct	timespec *tp;
14794c8fcd8SPeter Wemm };
14894c8fcd8SPeter Wemm #endif
149708e7684SPeter Wemm 
150fb99ab88SMatthew Dillon /*
151fb99ab88SMatthew Dillon  * MPSAFE
152fb99ab88SMatthew Dillon  */
15394c8fcd8SPeter Wemm /* ARGSUSED */
15494c8fcd8SPeter Wemm int
15591afe087SPoul-Henning Kamp clock_gettime(struct thread *td, struct clock_gettime_args *uap)
15694c8fcd8SPeter Wemm {
15794c8fcd8SPeter Wemm 	struct timespec ats;
158de0a9241SKelly Yancey 	struct timeval sys, user;
15978c85e8dSJohn Baldwin 	struct proc *p;
16094c8fcd8SPeter Wemm 
16178c85e8dSJohn Baldwin 	p = td->td_proc;
162b8817154SKelly Yancey 	switch (uap->clock_id) {
163b8817154SKelly Yancey 	case CLOCK_REALTIME:
1647ec73f64SPoul-Henning Kamp 		nanotime(&ats);
165b8817154SKelly Yancey 		break;
166b8817154SKelly Yancey 	case CLOCK_VIRTUAL:
16778c85e8dSJohn Baldwin 		PROC_LOCK(p);
16878c85e8dSJohn Baldwin 		calcru(p, &user, &sys);
16978c85e8dSJohn Baldwin 		PROC_UNLOCK(p);
170b8817154SKelly Yancey 		TIMEVAL_TO_TIMESPEC(&user, &ats);
171b8817154SKelly Yancey 		break;
172b8817154SKelly Yancey 	case CLOCK_PROF:
17378c85e8dSJohn Baldwin 		PROC_LOCK(p);
17478c85e8dSJohn Baldwin 		calcru(p, &user, &sys);
17578c85e8dSJohn Baldwin 		PROC_UNLOCK(p);
176de0a9241SKelly Yancey 		timevaladd(&user, &sys);
177de0a9241SKelly Yancey 		TIMEVAL_TO_TIMESPEC(&user, &ats);
178de0a9241SKelly Yancey 		break;
179de0a9241SKelly Yancey 	case CLOCK_MONOTONIC:
180de0a9241SKelly Yancey 		nanouptime(&ats);
181b8817154SKelly Yancey 		break;
182b8817154SKelly Yancey 	default:
1835cb3dc8fSPoul-Henning Kamp 		return (EINVAL);
184b8817154SKelly Yancey 	}
185d1e405c5SAlfred Perlstein 	return (copyout(&ats, uap->tp, sizeof(ats)));
18694c8fcd8SPeter Wemm }
18794c8fcd8SPeter Wemm 
18894c8fcd8SPeter Wemm #ifndef _SYS_SYSPROTO_H_
18994c8fcd8SPeter Wemm struct clock_settime_args {
19094c8fcd8SPeter Wemm 	clockid_t clock_id;
19194c8fcd8SPeter Wemm 	const struct	timespec *tp;
19294c8fcd8SPeter Wemm };
19394c8fcd8SPeter Wemm #endif
194708e7684SPeter Wemm 
195fb99ab88SMatthew Dillon /*
196fb99ab88SMatthew Dillon  * MPSAFE
197fb99ab88SMatthew Dillon  */
19894c8fcd8SPeter Wemm /* ARGSUSED */
19994c8fcd8SPeter Wemm int
20091afe087SPoul-Henning Kamp clock_settime(struct thread *td, struct clock_settime_args *uap)
20194c8fcd8SPeter Wemm {
20294c8fcd8SPeter Wemm 	struct timeval atv;
20394c8fcd8SPeter Wemm 	struct timespec ats;
20494c8fcd8SPeter Wemm 	int error;
20594c8fcd8SPeter Wemm 
2064b8d5f2dSRobert Watson #ifdef MAC
2074b8d5f2dSRobert Watson 	error = mac_check_system_settime(td->td_ucred);
2084b8d5f2dSRobert Watson 	if (error)
2094b8d5f2dSRobert Watson 		return (error);
2104b8d5f2dSRobert Watson #endif
21144731cabSJohn Baldwin 	if ((error = suser(td)) != 0)
2127edfb592SJohn Baldwin 		return (error);
213d1e405c5SAlfred Perlstein 	if (uap->clock_id != CLOCK_REALTIME)
2147edfb592SJohn Baldwin 		return (EINVAL);
215d1e405c5SAlfred Perlstein 	if ((error = copyin(uap->tp, &ats, sizeof(ats))) != 0)
2167edfb592SJohn Baldwin 		return (error);
2177edfb592SJohn Baldwin 	if (ats.tv_nsec < 0 || ats.tv_nsec >= 1000000000)
2187edfb592SJohn Baldwin 		return (EINVAL);
219a0502b19SPoul-Henning Kamp 	/* XXX Don't convert nsec->usec and back */
22094c8fcd8SPeter Wemm 	TIMESPEC_TO_TIMEVAL(&atv, &ats);
2217edfb592SJohn Baldwin 	error = settime(td, &atv);
22294c8fcd8SPeter Wemm 	return (error);
22394c8fcd8SPeter Wemm }
22494c8fcd8SPeter Wemm 
22594c8fcd8SPeter Wemm #ifndef _SYS_SYSPROTO_H_
22694c8fcd8SPeter Wemm struct clock_getres_args {
22794c8fcd8SPeter Wemm 	clockid_t clock_id;
22894c8fcd8SPeter Wemm 	struct	timespec *tp;
22994c8fcd8SPeter Wemm };
23094c8fcd8SPeter Wemm #endif
231708e7684SPeter Wemm 
23294c8fcd8SPeter Wemm int
23391afe087SPoul-Henning Kamp clock_getres(struct thread *td, struct clock_getres_args *uap)
23494c8fcd8SPeter Wemm {
23594c8fcd8SPeter Wemm 	struct timespec ts;
23694c8fcd8SPeter Wemm 
23794c8fcd8SPeter Wemm 	ts.tv_sec = 0;
238b8817154SKelly Yancey 	switch (uap->clock_id) {
239b8817154SKelly Yancey 	case CLOCK_REALTIME:
240b8817154SKelly Yancey 	case CLOCK_MONOTONIC:
241ac0653dcSBruce Evans 		/*
242ac0653dcSBruce Evans 		 * Round up the result of the division cheaply by adding 1.
243ac0653dcSBruce Evans 		 * Rounding up is especially important if rounding down
244ac0653dcSBruce Evans 		 * would give 0.  Perfect rounding is unimportant.
245ac0653dcSBruce Evans 		 */
246ac0653dcSBruce Evans 		ts.tv_nsec = 1000000000 / tc_getfrequency() + 1;
247b8817154SKelly Yancey 		break;
248b8817154SKelly Yancey 	case CLOCK_VIRTUAL:
249b8817154SKelly Yancey 	case CLOCK_PROF:
250b8817154SKelly Yancey 		/* Accurately round up here because we can do so cheaply. */
251b8817154SKelly Yancey 		ts.tv_nsec = (1000000000 + hz - 1) / hz;
252b8817154SKelly Yancey 		break;
253b8817154SKelly Yancey 	default:
254b8817154SKelly Yancey 		return (EINVAL);
25594c8fcd8SPeter Wemm 	}
256de0a9241SKelly Yancey 	if (uap->tp == NULL)
257de0a9241SKelly Yancey 		return (0);
258de0a9241SKelly Yancey 	return (copyout(&ts, uap->tp, sizeof(ts)));
25994c8fcd8SPeter Wemm }
26094c8fcd8SPeter Wemm 
26194c8fcd8SPeter Wemm static int nanowait;
26294c8fcd8SPeter Wemm 
2637fdf2c85SPaul Saab int
2647fdf2c85SPaul Saab kern_nanosleep(struct thread *td, struct timespec *rqt, struct timespec *rmt)
2655b870b7bSPeter Wemm {
2665704ba6aSPoul-Henning Kamp 	struct timespec ts, ts2, ts3;
26733841826SPoul-Henning Kamp 	struct timeval tv;
26833841826SPoul-Henning Kamp 	int error;
2695b870b7bSPeter Wemm 
2707d7fb492SBruce Evans 	if (rqt->tv_nsec < 0 || rqt->tv_nsec >= 1000000000)
271708e7684SPeter Wemm 		return (EINVAL);
272d254af07SMatthew Dillon 	if (rqt->tv_sec < 0 || (rqt->tv_sec == 0 && rqt->tv_nsec == 0))
2737d7fb492SBruce Evans 		return (0);
274c21410e1SPoul-Henning Kamp 	getnanouptime(&ts);
27500af9731SPoul-Henning Kamp 	timespecadd(&ts, rqt);
27633841826SPoul-Henning Kamp 	TIMESPEC_TO_TIMEVAL(&tv, rqt);
27733841826SPoul-Henning Kamp 	for (;;) {
27833841826SPoul-Henning Kamp 		error = tsleep(&nanowait, PWAIT | PCATCH, "nanslp",
27933841826SPoul-Henning Kamp 		    tvtohz(&tv));
280c21410e1SPoul-Henning Kamp 		getnanouptime(&ts2);
28133841826SPoul-Henning Kamp 		if (error != EWOULDBLOCK) {
28233841826SPoul-Henning Kamp 			if (error == ERESTART)
28394c8fcd8SPeter Wemm 				error = EINTR;
28433841826SPoul-Henning Kamp 			if (rmt != NULL) {
28533841826SPoul-Henning Kamp 				timespecsub(&ts, &ts2);
28633841826SPoul-Henning Kamp 				if (ts.tv_sec < 0)
28733841826SPoul-Henning Kamp 					timespecclear(&ts);
28800af9731SPoul-Henning Kamp 				*rmt = ts;
28900af9731SPoul-Henning Kamp 			}
29033841826SPoul-Henning Kamp 			return (error);
29133841826SPoul-Henning Kamp 		}
29233841826SPoul-Henning Kamp 		if (timespeccmp(&ts2, &ts, >=))
29333841826SPoul-Henning Kamp 			return (0);
2945704ba6aSPoul-Henning Kamp 		ts3 = ts;
2955704ba6aSPoul-Henning Kamp 		timespecsub(&ts3, &ts2);
2965704ba6aSPoul-Henning Kamp 		TIMESPEC_TO_TIMEVAL(&tv, &ts3);
29733841826SPoul-Henning Kamp 	}
2985b870b7bSPeter Wemm }
29994c8fcd8SPeter Wemm 
3005b870b7bSPeter Wemm #ifndef _SYS_SYSPROTO_H_
3015b870b7bSPeter Wemm struct nanosleep_args {
3025b870b7bSPeter Wemm 	struct	timespec *rqtp;
3035b870b7bSPeter Wemm 	struct	timespec *rmtp;
3045b870b7bSPeter Wemm };
3055b870b7bSPeter Wemm #endif
3065b870b7bSPeter Wemm 
307fb99ab88SMatthew Dillon /*
308fb99ab88SMatthew Dillon  * MPSAFE
309fb99ab88SMatthew Dillon  */
3105b870b7bSPeter Wemm /* ARGSUSED */
3115b870b7bSPeter Wemm int
31291afe087SPoul-Henning Kamp nanosleep(struct thread *td, struct nanosleep_args *uap)
3135b870b7bSPeter Wemm {
3145b870b7bSPeter Wemm 	struct timespec rmt, rqt;
315fb99ab88SMatthew Dillon 	int error;
3165b870b7bSPeter Wemm 
317d1e405c5SAlfred Perlstein 	error = copyin(uap->rqtp, &rqt, sizeof(rqt));
3185b870b7bSPeter Wemm 	if (error)
3195b870b7bSPeter Wemm 		return (error);
320fb99ab88SMatthew Dillon 
32131f3e2adSAlfred Perlstein 	if (uap->rmtp &&
32231f3e2adSAlfred Perlstein 	    !useracc((caddr_t)uap->rmtp, sizeof(rmt), VM_PROT_WRITE))
32331f3e2adSAlfred Perlstein 			return (EFAULT);
3247fdf2c85SPaul Saab 	error = kern_nanosleep(td, &rqt, &rmt);
325d1e405c5SAlfred Perlstein 	if (error && uap->rmtp) {
326fb99ab88SMatthew Dillon 		int error2;
327fb99ab88SMatthew Dillon 
328d1e405c5SAlfred Perlstein 		error2 = copyout(&rmt, uap->rmtp, sizeof(rmt));
32931f3e2adSAlfred Perlstein 		if (error2)
330fb99ab88SMatthew Dillon 			error = error2;
331708e7684SPeter Wemm 	}
332708e7684SPeter Wemm 	return (error);
33394c8fcd8SPeter Wemm }
33494c8fcd8SPeter Wemm 
3355b870b7bSPeter Wemm #ifndef _SYS_SYSPROTO_H_
336df8bae1dSRodney W. Grimes struct gettimeofday_args {
337df8bae1dSRodney W. Grimes 	struct	timeval *tp;
338df8bae1dSRodney W. Grimes 	struct	timezone *tzp;
339df8bae1dSRodney W. Grimes };
340d2d3e875SBruce Evans #endif
341fb99ab88SMatthew Dillon /*
342fb99ab88SMatthew Dillon  * MPSAFE
343fb99ab88SMatthew Dillon  */
344df8bae1dSRodney W. Grimes /* ARGSUSED */
34526f9a767SRodney W. Grimes int
34691afe087SPoul-Henning Kamp gettimeofday(struct thread *td, struct gettimeofday_args *uap)
347df8bae1dSRodney W. Grimes {
348df8bae1dSRodney W. Grimes 	struct timeval atv;
349411c25edSTim J. Robbins 	struct timezone rtz;
350df8bae1dSRodney W. Grimes 	int error = 0;
351df8bae1dSRodney W. Grimes 
352df8bae1dSRodney W. Grimes 	if (uap->tp) {
353df8bae1dSRodney W. Grimes 		microtime(&atv);
35401609114SAlfred Perlstein 		error = copyout(&atv, uap->tp, sizeof (atv));
355df8bae1dSRodney W. Grimes 	}
35621dcdb38SPoul-Henning Kamp 	if (error == 0 && uap->tzp != NULL) {
35791f1c2b3SPoul-Henning Kamp 		rtz.tz_minuteswest = tz_minuteswest;
35891f1c2b3SPoul-Henning Kamp 		rtz.tz_dsttime = tz_dsttime;
359411c25edSTim J. Robbins 		error = copyout(&rtz, uap->tzp, sizeof (rtz));
36021dcdb38SPoul-Henning Kamp 	}
361df8bae1dSRodney W. Grimes 	return (error);
362df8bae1dSRodney W. Grimes }
363df8bae1dSRodney W. Grimes 
364d2d3e875SBruce Evans #ifndef _SYS_SYSPROTO_H_
365df8bae1dSRodney W. Grimes struct settimeofday_args {
366df8bae1dSRodney W. Grimes 	struct	timeval *tv;
367df8bae1dSRodney W. Grimes 	struct	timezone *tzp;
368df8bae1dSRodney W. Grimes };
369d2d3e875SBruce Evans #endif
370fb99ab88SMatthew Dillon /*
371fb99ab88SMatthew Dillon  * MPSAFE
372fb99ab88SMatthew Dillon  */
373df8bae1dSRodney W. Grimes /* ARGSUSED */
37426f9a767SRodney W. Grimes int
37591afe087SPoul-Henning Kamp settimeofday(struct thread *td, struct settimeofday_args *uap)
376df8bae1dSRodney W. Grimes {
377b88ec951SJohn Baldwin 	struct timeval atv, *tvp;
378b88ec951SJohn Baldwin 	struct timezone atz, *tzp;
379b88ec951SJohn Baldwin 	int error;
380b88ec951SJohn Baldwin 
381b88ec951SJohn Baldwin 	if (uap->tv) {
382b88ec951SJohn Baldwin 		error = copyin(uap->tv, &atv, sizeof(atv));
383b88ec951SJohn Baldwin 		if (error)
384b88ec951SJohn Baldwin 			return (error);
385b88ec951SJohn Baldwin 		tvp = &atv;
386b88ec951SJohn Baldwin 	} else
387b88ec951SJohn Baldwin 		tvp = NULL;
388b88ec951SJohn Baldwin 	if (uap->tzp) {
389b88ec951SJohn Baldwin 		error = copyin(uap->tzp, &atz, sizeof(atz));
390b88ec951SJohn Baldwin 		if (error)
391b88ec951SJohn Baldwin 			return (error);
392b88ec951SJohn Baldwin 		tzp = &atz;
393b88ec951SJohn Baldwin 	} else
394b88ec951SJohn Baldwin 		tzp = NULL;
395b88ec951SJohn Baldwin 	return (kern_settimeofday(td, tvp, tzp));
396b88ec951SJohn Baldwin }
397b88ec951SJohn Baldwin 
398b88ec951SJohn Baldwin int
399b88ec951SJohn Baldwin kern_settimeofday(struct thread *td, struct timeval *tv, struct timezone *tzp)
400b88ec951SJohn Baldwin {
401b88ec951SJohn Baldwin 	int error;
402fb99ab88SMatthew Dillon 
4034b8d5f2dSRobert Watson #ifdef MAC
4044b8d5f2dSRobert Watson 	error = mac_check_system_settime(td->td_ucred);
4054b8d5f2dSRobert Watson 	if (error)
4064b8d5f2dSRobert Watson 		return (error);
4074b8d5f2dSRobert Watson #endif
408b88ec951SJohn Baldwin 	error = suser(td);
409b88ec951SJohn Baldwin 	if (error)
4107edfb592SJohn Baldwin 		return (error);
411df8bae1dSRodney W. Grimes 	/* Verify all parameters before changing time. */
412b88ec951SJohn Baldwin 	if (tv) {
413b88ec951SJohn Baldwin 		if (tv->tv_usec < 0 || tv->tv_usec >= 1000000)
4147edfb592SJohn Baldwin 			return (EINVAL);
415b88ec951SJohn Baldwin 		error = settime(td, tv);
416708e7684SPeter Wemm 	}
417b88ec951SJohn Baldwin 	if (tzp && error == 0) {
418b88ec951SJohn Baldwin 		tz_minuteswest = tzp->tz_minuteswest;
419b88ec951SJohn Baldwin 		tz_dsttime = tzp->tz_dsttime;
420b88ec951SJohn Baldwin 	}
4217edfb592SJohn Baldwin 	return (error);
422b88ec951SJohn Baldwin }
4237edfb592SJohn Baldwin 
424df8bae1dSRodney W. Grimes /*
425df8bae1dSRodney W. Grimes  * Get value of an interval timer.  The process virtual and
426df8bae1dSRodney W. Grimes  * profiling virtual time timers are kept in the p_stats area, since
427df8bae1dSRodney W. Grimes  * they can be swapped out.  These are kept internally in the
428df8bae1dSRodney W. Grimes  * way they are specified externally: in time until they expire.
429df8bae1dSRodney W. Grimes  *
430df8bae1dSRodney W. Grimes  * The real time interval timer is kept in the process table slot
431df8bae1dSRodney W. Grimes  * for the process, and its value (it_value) is kept as an
432df8bae1dSRodney W. Grimes  * absolute time rather than as a delta, so that it is easy to keep
433df8bae1dSRodney W. Grimes  * periodic real-time signals from drifting.
434df8bae1dSRodney W. Grimes  *
435df8bae1dSRodney W. Grimes  * Virtual time timers are processed in the hardclock() routine of
436df8bae1dSRodney W. Grimes  * kern_clock.c.  The real time timer is processed by a timeout
437df8bae1dSRodney W. Grimes  * routine, called from the softclock() routine.  Since a callout
438df8bae1dSRodney W. Grimes  * may be delayed in real time due to interrupt processing in the system,
439df8bae1dSRodney W. Grimes  * it is possible for the real time timeout routine (realitexpire, given below),
440df8bae1dSRodney W. Grimes  * to be delayed in real time past when it is supposed to occur.  It
441df8bae1dSRodney W. Grimes  * does not suffice, therefore, to reload the real timer .it_value from the
442df8bae1dSRodney W. Grimes  * real time timers .it_interval.  Rather, we compute the next time in
443df8bae1dSRodney W. Grimes  * absolute time the timer should go off.
444df8bae1dSRodney W. Grimes  */
445d2d3e875SBruce Evans #ifndef _SYS_SYSPROTO_H_
446df8bae1dSRodney W. Grimes struct getitimer_args {
447df8bae1dSRodney W. Grimes 	u_int	which;
448df8bae1dSRodney W. Grimes 	struct	itimerval *itv;
449df8bae1dSRodney W. Grimes };
450d2d3e875SBruce Evans #endif
451fb99ab88SMatthew Dillon /*
452fb99ab88SMatthew Dillon  * MPSAFE
453fb99ab88SMatthew Dillon  */
45426f9a767SRodney W. Grimes int
45591afe087SPoul-Henning Kamp getitimer(struct thread *td, struct getitimer_args *uap)
456df8bae1dSRodney W. Grimes {
457df8bae1dSRodney W. Grimes 	struct itimerval aitv;
458c90110d6SJohn Baldwin 	int error;
459df8bae1dSRodney W. Grimes 
460cfa0efe7SMaxim Sobolev 	error = kern_getitimer(td, uap->which, &aitv);
461cfa0efe7SMaxim Sobolev 	if (error != 0)
462cfa0efe7SMaxim Sobolev 		return (error);
463cfa0efe7SMaxim Sobolev 	return (copyout(&aitv, uap->itv, sizeof (struct itimerval)));
464cfa0efe7SMaxim Sobolev }
465cfa0efe7SMaxim Sobolev 
466cfa0efe7SMaxim Sobolev int
467cfa0efe7SMaxim Sobolev kern_getitimer(struct thread *td, u_int which, struct itimerval *aitv)
468cfa0efe7SMaxim Sobolev {
469cfa0efe7SMaxim Sobolev 	struct proc *p = td->td_proc;
470cfa0efe7SMaxim Sobolev 	struct timeval ctv;
471cfa0efe7SMaxim Sobolev 
472cfa0efe7SMaxim Sobolev 	if (which > ITIMER_PROF)
473df8bae1dSRodney W. Grimes 		return (EINVAL);
474fb99ab88SMatthew Dillon 
475cfa0efe7SMaxim Sobolev 	if (which == ITIMER_REAL) {
476df8bae1dSRodney W. Grimes 		/*
477ee002b68SBruce Evans 		 * Convert from absolute to relative time in .it_value
478df8bae1dSRodney W. Grimes 		 * part of real time timer.  If time for real time timer
479df8bae1dSRodney W. Grimes 		 * has passed return 0, else return difference between
480df8bae1dSRodney W. Grimes 		 * current time and time for the timer to go off.
481df8bae1dSRodney W. Grimes 		 */
48296d7f8efSTim J. Robbins 		PROC_LOCK(p);
483cfa0efe7SMaxim Sobolev 		*aitv = p->p_realtimer;
48496d7f8efSTim J. Robbins 		PROC_UNLOCK(p);
485cfa0efe7SMaxim Sobolev 		if (timevalisset(&aitv->it_value)) {
486c21410e1SPoul-Henning Kamp 			getmicrouptime(&ctv);
487cfa0efe7SMaxim Sobolev 			if (timevalcmp(&aitv->it_value, &ctv, <))
488cfa0efe7SMaxim Sobolev 				timevalclear(&aitv->it_value);
489df8bae1dSRodney W. Grimes 			else
490cfa0efe7SMaxim Sobolev 				timevalsub(&aitv->it_value, &ctv);
491227ee8a1SPoul-Henning Kamp 		}
492fb99ab88SMatthew Dillon 	} else {
49396d7f8efSTim J. Robbins 		mtx_lock_spin(&sched_lock);
494cfa0efe7SMaxim Sobolev 		*aitv = p->p_stats->p_timer[which];
49596d7f8efSTim J. Robbins 		mtx_unlock_spin(&sched_lock);
496fb99ab88SMatthew Dillon 	}
497cfa0efe7SMaxim Sobolev 	return (0);
498df8bae1dSRodney W. Grimes }
499df8bae1dSRodney W. Grimes 
500d2d3e875SBruce Evans #ifndef _SYS_SYSPROTO_H_
501df8bae1dSRodney W. Grimes struct setitimer_args {
502df8bae1dSRodney W. Grimes 	u_int	which;
503df8bae1dSRodney W. Grimes 	struct	itimerval *itv, *oitv;
504df8bae1dSRodney W. Grimes };
505d2d3e875SBruce Evans #endif
506cfa0efe7SMaxim Sobolev 
507fb99ab88SMatthew Dillon /*
508fb99ab88SMatthew Dillon  * MPSAFE
509fb99ab88SMatthew Dillon  */
51026f9a767SRodney W. Grimes int
51191afe087SPoul-Henning Kamp setitimer(struct thread *td, struct setitimer_args *uap)
512df8bae1dSRodney W. Grimes {
513cfa0efe7SMaxim Sobolev 	struct itimerval aitv, oitv;
514c90110d6SJohn Baldwin 	int error;
51596d7f8efSTim J. Robbins 
51696d7f8efSTim J. Robbins 	if (uap->itv == NULL) {
51796d7f8efSTim J. Robbins 		uap->itv = uap->oitv;
51896d7f8efSTim J. Robbins 		return (getitimer(td, (struct getitimer_args *)uap));
51996d7f8efSTim J. Robbins 	}
520df8bae1dSRodney W. Grimes 
52196d7f8efSTim J. Robbins 	if ((error = copyin(uap->itv, &aitv, sizeof(struct itimerval))))
522df8bae1dSRodney W. Grimes 		return (error);
523cfa0efe7SMaxim Sobolev 	error = kern_setitimer(td, uap->which, &aitv, &oitv);
524cfa0efe7SMaxim Sobolev 	if (error != 0 || uap->oitv == NULL)
525cfa0efe7SMaxim Sobolev 		return (error);
526cfa0efe7SMaxim Sobolev 	return (copyout(&oitv, uap->oitv, sizeof(struct itimerval)));
527cfa0efe7SMaxim Sobolev }
528cfa0efe7SMaxim Sobolev 
529cfa0efe7SMaxim Sobolev int
530c90110d6SJohn Baldwin kern_setitimer(struct thread *td, u_int which, struct itimerval *aitv,
531c90110d6SJohn Baldwin     struct itimerval *oitv)
532cfa0efe7SMaxim Sobolev {
533cfa0efe7SMaxim Sobolev 	struct proc *p = td->td_proc;
534cfa0efe7SMaxim Sobolev 	struct timeval ctv;
535cfa0efe7SMaxim Sobolev 
5365e85ac17SJohn Baldwin 	if (aitv == NULL)
5375e85ac17SJohn Baldwin 		return (kern_getitimer(td, which, oitv));
5385e85ac17SJohn Baldwin 
539cfa0efe7SMaxim Sobolev 	if (which > ITIMER_PROF)
54096d7f8efSTim J. Robbins 		return (EINVAL);
541cfa0efe7SMaxim Sobolev 	if (itimerfix(&aitv->it_value))
542cfa0efe7SMaxim Sobolev 		return (EINVAL);
543cfa0efe7SMaxim Sobolev 	if (!timevalisset(&aitv->it_value))
544cfa0efe7SMaxim Sobolev 		timevalclear(&aitv->it_interval);
545cfa0efe7SMaxim Sobolev 	else if (itimerfix(&aitv->it_interval))
54696d7f8efSTim J. Robbins 		return (EINVAL);
54796d7f8efSTim J. Robbins 
548cfa0efe7SMaxim Sobolev 	if (which == ITIMER_REAL) {
54996d7f8efSTim J. Robbins 		PROC_LOCK(p);
5504cf41af3SPoul-Henning Kamp 		if (timevalisset(&p->p_realtimer.it_value))
5514f559836SJake Burkholder 			callout_stop(&p->p_itcallout);
55225b4d3a8SJohn Baldwin 		getmicrouptime(&ctv);
553cfa0efe7SMaxim Sobolev 		if (timevalisset(&aitv->it_value)) {
554cfa0efe7SMaxim Sobolev 			callout_reset(&p->p_itcallout, tvtohz(&aitv->it_value),
5554f559836SJake Burkholder 			    realitexpire, p);
556cfa0efe7SMaxim Sobolev 			timevaladd(&aitv->it_value, &ctv);
55725b4d3a8SJohn Baldwin 		}
558cfa0efe7SMaxim Sobolev 		*oitv = p->p_realtimer;
559cfa0efe7SMaxim Sobolev 		p->p_realtimer = *aitv;
56096d7f8efSTim J. Robbins 		PROC_UNLOCK(p);
561cfa0efe7SMaxim Sobolev 		if (timevalisset(&oitv->it_value)) {
562cfa0efe7SMaxim Sobolev 			if (timevalcmp(&oitv->it_value, &ctv, <))
563cfa0efe7SMaxim Sobolev 				timevalclear(&oitv->it_value);
56496d7f8efSTim J. Robbins 			else
565cfa0efe7SMaxim Sobolev 				timevalsub(&oitv->it_value, &ctv);
566fb99ab88SMatthew Dillon 		}
56796d7f8efSTim J. Robbins 	} else {
56896d7f8efSTim J. Robbins 		mtx_lock_spin(&sched_lock);
569cfa0efe7SMaxim Sobolev 		*oitv = p->p_stats->p_timer[which];
570cfa0efe7SMaxim Sobolev 		p->p_stats->p_timer[which] = *aitv;
57196d7f8efSTim J. Robbins 		mtx_unlock_spin(&sched_lock);
57296d7f8efSTim J. Robbins 	}
57396d7f8efSTim J. Robbins 	return (0);
574df8bae1dSRodney W. Grimes }
575df8bae1dSRodney W. Grimes 
576df8bae1dSRodney W. Grimes /*
577df8bae1dSRodney W. Grimes  * Real interval timer expired:
578df8bae1dSRodney W. Grimes  * send process whose timer expired an alarm signal.
579df8bae1dSRodney W. Grimes  * If time is not set up to reload, then just return.
580df8bae1dSRodney W. Grimes  * Else compute next time timer should go off which is > current time.
581df8bae1dSRodney W. Grimes  * This is where delay in processing this timeout causes multiple
582df8bae1dSRodney W. Grimes  * SIGALRM calls to be compressed into one.
583c8b47828SBruce Evans  * tvtohz() always adds 1 to allow for the time until the next clock
5849207f00aSBruce Evans  * interrupt being strictly less than 1 clock tick, but we don't want
5859207f00aSBruce Evans  * that here since we want to appear to be in sync with the clock
5869207f00aSBruce Evans  * interrupt even when we're delayed.
587df8bae1dSRodney W. Grimes  */
588df8bae1dSRodney W. Grimes void
58991afe087SPoul-Henning Kamp realitexpire(void *arg)
590df8bae1dSRodney W. Grimes {
59191afe087SPoul-Henning Kamp 	struct proc *p;
592bfe6c9faSPoul-Henning Kamp 	struct timeval ctv, ntv;
593df8bae1dSRodney W. Grimes 
594df8bae1dSRodney W. Grimes 	p = (struct proc *)arg;
59537824023SJohn Baldwin 	PROC_LOCK(p);
596df8bae1dSRodney W. Grimes 	psignal(p, SIGALRM);
5974cf41af3SPoul-Henning Kamp 	if (!timevalisset(&p->p_realtimer.it_interval)) {
5984cf41af3SPoul-Henning Kamp 		timevalclear(&p->p_realtimer.it_value);
5995499ea01SJohn Baldwin 		if (p->p_flag & P_WEXIT)
6005499ea01SJohn Baldwin 			wakeup(&p->p_itcallout);
60137824023SJohn Baldwin 		PROC_UNLOCK(p);
602df8bae1dSRodney W. Grimes 		return;
603df8bae1dSRodney W. Grimes 	}
604df8bae1dSRodney W. Grimes 	for (;;) {
605df8bae1dSRodney W. Grimes 		timevaladd(&p->p_realtimer.it_value,
606df8bae1dSRodney W. Grimes 		    &p->p_realtimer.it_interval);
607c21410e1SPoul-Henning Kamp 		getmicrouptime(&ctv);
6084cf41af3SPoul-Henning Kamp 		if (timevalcmp(&p->p_realtimer.it_value, &ctv, >)) {
609bfe6c9faSPoul-Henning Kamp 			ntv = p->p_realtimer.it_value;
610bfe6c9faSPoul-Henning Kamp 			timevalsub(&ntv, &ctv);
6114f559836SJake Burkholder 			callout_reset(&p->p_itcallout, tvtohz(&ntv) - 1,
6124f559836SJake Burkholder 			    realitexpire, p);
61337824023SJohn Baldwin 			PROC_UNLOCK(p);
614df8bae1dSRodney W. Grimes 			return;
615df8bae1dSRodney W. Grimes 		}
616df8bae1dSRodney W. Grimes 	}
61737824023SJohn Baldwin 	/*NOTREACHED*/
618df8bae1dSRodney W. Grimes }
619df8bae1dSRodney W. Grimes 
620df8bae1dSRodney W. Grimes /*
621df8bae1dSRodney W. Grimes  * Check that a proposed value to load into the .it_value or
622df8bae1dSRodney W. Grimes  * .it_interval part of an interval timer is acceptable, and
623df8bae1dSRodney W. Grimes  * fix it to have at least minimal value (i.e. if it is less
624df8bae1dSRodney W. Grimes  * than the resolution of the clock, round it up.)
625df8bae1dSRodney W. Grimes  */
62626f9a767SRodney W. Grimes int
62791afe087SPoul-Henning Kamp itimerfix(struct timeval *tv)
628df8bae1dSRodney W. Grimes {
629df8bae1dSRodney W. Grimes 
630df8bae1dSRodney W. Grimes 	if (tv->tv_sec < 0 || tv->tv_sec > 100000000 ||
631df8bae1dSRodney W. Grimes 	    tv->tv_usec < 0 || tv->tv_usec >= 1000000)
632df8bae1dSRodney W. Grimes 		return (EINVAL);
633df8bae1dSRodney W. Grimes 	if (tv->tv_sec == 0 && tv->tv_usec != 0 && tv->tv_usec < tick)
634df8bae1dSRodney W. Grimes 		tv->tv_usec = tick;
635df8bae1dSRodney W. Grimes 	return (0);
636df8bae1dSRodney W. Grimes }
637df8bae1dSRodney W. Grimes 
638df8bae1dSRodney W. Grimes /*
639df8bae1dSRodney W. Grimes  * Decrement an interval timer by a specified number
640df8bae1dSRodney W. Grimes  * of microseconds, which must be less than a second,
641df8bae1dSRodney W. Grimes  * i.e. < 1000000.  If the timer expires, then reload
642df8bae1dSRodney W. Grimes  * it.  In this case, carry over (usec - old value) to
643df8bae1dSRodney W. Grimes  * reduce the value reloaded into the timer so that
644df8bae1dSRodney W. Grimes  * the timer does not drift.  This routine assumes
645df8bae1dSRodney W. Grimes  * that it is called in a context where the timers
646df8bae1dSRodney W. Grimes  * on which it is operating cannot change in value.
647df8bae1dSRodney W. Grimes  */
64826f9a767SRodney W. Grimes int
64991afe087SPoul-Henning Kamp itimerdecr(struct itimerval *itp, int usec)
650df8bae1dSRodney W. Grimes {
651df8bae1dSRodney W. Grimes 
652df8bae1dSRodney W. Grimes 	if (itp->it_value.tv_usec < usec) {
653df8bae1dSRodney W. Grimes 		if (itp->it_value.tv_sec == 0) {
654df8bae1dSRodney W. Grimes 			/* expired, and already in next interval */
655df8bae1dSRodney W. Grimes 			usec -= itp->it_value.tv_usec;
656df8bae1dSRodney W. Grimes 			goto expire;
657df8bae1dSRodney W. Grimes 		}
658df8bae1dSRodney W. Grimes 		itp->it_value.tv_usec += 1000000;
659df8bae1dSRodney W. Grimes 		itp->it_value.tv_sec--;
660df8bae1dSRodney W. Grimes 	}
661df8bae1dSRodney W. Grimes 	itp->it_value.tv_usec -= usec;
662df8bae1dSRodney W. Grimes 	usec = 0;
6634cf41af3SPoul-Henning Kamp 	if (timevalisset(&itp->it_value))
664df8bae1dSRodney W. Grimes 		return (1);
665df8bae1dSRodney W. Grimes 	/* expired, exactly at end of interval */
666df8bae1dSRodney W. Grimes expire:
6674cf41af3SPoul-Henning Kamp 	if (timevalisset(&itp->it_interval)) {
668df8bae1dSRodney W. Grimes 		itp->it_value = itp->it_interval;
669df8bae1dSRodney W. Grimes 		itp->it_value.tv_usec -= usec;
670df8bae1dSRodney W. Grimes 		if (itp->it_value.tv_usec < 0) {
671df8bae1dSRodney W. Grimes 			itp->it_value.tv_usec += 1000000;
672df8bae1dSRodney W. Grimes 			itp->it_value.tv_sec--;
673df8bae1dSRodney W. Grimes 		}
674df8bae1dSRodney W. Grimes 	} else
675df8bae1dSRodney W. Grimes 		itp->it_value.tv_usec = 0;		/* sec is already 0 */
676df8bae1dSRodney W. Grimes 	return (0);
677df8bae1dSRodney W. Grimes }
678df8bae1dSRodney W. Grimes 
679df8bae1dSRodney W. Grimes /*
680df8bae1dSRodney W. Grimes  * Add and subtract routines for timevals.
681df8bae1dSRodney W. Grimes  * N.B.: subtract routine doesn't deal with
682df8bae1dSRodney W. Grimes  * results which are before the beginning,
683df8bae1dSRodney W. Grimes  * it just gets very confused in this case.
684df8bae1dSRodney W. Grimes  * Caveat emptor.
685df8bae1dSRodney W. Grimes  */
68626f9a767SRodney W. Grimes void
6876ff7636eSAlfred Perlstein timevaladd(struct timeval *t1, const struct timeval *t2)
688df8bae1dSRodney W. Grimes {
689df8bae1dSRodney W. Grimes 
690df8bae1dSRodney W. Grimes 	t1->tv_sec += t2->tv_sec;
691df8bae1dSRodney W. Grimes 	t1->tv_usec += t2->tv_usec;
692df8bae1dSRodney W. Grimes 	timevalfix(t1);
693df8bae1dSRodney W. Grimes }
694df8bae1dSRodney W. Grimes 
69526f9a767SRodney W. Grimes void
6966ff7636eSAlfred Perlstein timevalsub(struct timeval *t1, const struct timeval *t2)
697df8bae1dSRodney W. Grimes {
698df8bae1dSRodney W. Grimes 
699df8bae1dSRodney W. Grimes 	t1->tv_sec -= t2->tv_sec;
700df8bae1dSRodney W. Grimes 	t1->tv_usec -= t2->tv_usec;
701df8bae1dSRodney W. Grimes 	timevalfix(t1);
702df8bae1dSRodney W. Grimes }
703df8bae1dSRodney W. Grimes 
70487b6de2bSPoul-Henning Kamp static void
70591afe087SPoul-Henning Kamp timevalfix(struct timeval *t1)
706df8bae1dSRodney W. Grimes {
707df8bae1dSRodney W. Grimes 
708df8bae1dSRodney W. Grimes 	if (t1->tv_usec < 0) {
709df8bae1dSRodney W. Grimes 		t1->tv_sec--;
710df8bae1dSRodney W. Grimes 		t1->tv_usec += 1000000;
711df8bae1dSRodney W. Grimes 	}
712df8bae1dSRodney W. Grimes 	if (t1->tv_usec >= 1000000) {
713df8bae1dSRodney W. Grimes 		t1->tv_sec++;
714df8bae1dSRodney W. Grimes 		t1->tv_usec -= 1000000;
715df8bae1dSRodney W. Grimes 	}
716df8bae1dSRodney W. Grimes }
71791974ce1SSam Leffler 
71891974ce1SSam Leffler /*
719addea9d4SSam Leffler  * ratecheck(): simple time-based rate-limit checking.
72091974ce1SSam Leffler  */
72191974ce1SSam Leffler int
72291974ce1SSam Leffler ratecheck(struct timeval *lasttime, const struct timeval *mininterval)
72391974ce1SSam Leffler {
72491974ce1SSam Leffler 	struct timeval tv, delta;
72591974ce1SSam Leffler 	int rv = 0;
72691974ce1SSam Leffler 
727addea9d4SSam Leffler 	getmicrouptime(&tv);		/* NB: 10ms precision */
728addea9d4SSam Leffler 	delta = tv;
729addea9d4SSam Leffler 	timevalsub(&delta, lasttime);
73091974ce1SSam Leffler 
73191974ce1SSam Leffler 	/*
73291974ce1SSam Leffler 	 * check for 0,0 is so that the message will be seen at least once,
73391974ce1SSam Leffler 	 * even if interval is huge.
73491974ce1SSam Leffler 	 */
73591974ce1SSam Leffler 	if (timevalcmp(&delta, mininterval, >=) ||
73691974ce1SSam Leffler 	    (lasttime->tv_sec == 0 && lasttime->tv_usec == 0)) {
73791974ce1SSam Leffler 		*lasttime = tv;
73891974ce1SSam Leffler 		rv = 1;
73991974ce1SSam Leffler 	}
74091974ce1SSam Leffler 
74191974ce1SSam Leffler 	return (rv);
74291974ce1SSam Leffler }
74391974ce1SSam Leffler 
74491974ce1SSam Leffler /*
74591974ce1SSam Leffler  * ppsratecheck(): packets (or events) per second limitation.
746addea9d4SSam Leffler  *
747addea9d4SSam Leffler  * Return 0 if the limit is to be enforced (e.g. the caller
748addea9d4SSam Leffler  * should drop a packet because of the rate limitation).
749addea9d4SSam Leffler  *
750893bec80SSam Leffler  * maxpps of 0 always causes zero to be returned.  maxpps of -1
751893bec80SSam Leffler  * always causes 1 to be returned; this effectively defeats rate
752893bec80SSam Leffler  * limiting.
753893bec80SSam Leffler  *
754addea9d4SSam Leffler  * Note that we maintain the struct timeval for compatibility
755addea9d4SSam Leffler  * with other bsd systems.  We reuse the storage and just monitor
756addea9d4SSam Leffler  * clock ticks for minimal overhead.
75791974ce1SSam Leffler  */
75891974ce1SSam Leffler int
75991974ce1SSam Leffler ppsratecheck(struct timeval *lasttime, int *curpps, int maxpps)
76091974ce1SSam Leffler {
761addea9d4SSam Leffler 	int now;
76291974ce1SSam Leffler 
76391974ce1SSam Leffler 	/*
764addea9d4SSam Leffler 	 * Reset the last time and counter if this is the first call
765addea9d4SSam Leffler 	 * or more than a second has passed since the last update of
766addea9d4SSam Leffler 	 * lasttime.
76791974ce1SSam Leffler 	 */
768addea9d4SSam Leffler 	now = ticks;
769addea9d4SSam Leffler 	if (lasttime->tv_sec == 0 || (u_int)(now - lasttime->tv_sec) >= hz) {
770addea9d4SSam Leffler 		lasttime->tv_sec = now;
771addea9d4SSam Leffler 		*curpps = 1;
772893bec80SSam Leffler 		return (maxpps != 0);
773addea9d4SSam Leffler 	} else {
774addea9d4SSam Leffler 		(*curpps)++;		/* NB: ignore potential overflow */
775addea9d4SSam Leffler 		return (maxpps < 0 || *curpps < maxpps);
776addea9d4SSam Leffler 	}
77791974ce1SSam Leffler }
778