xref: /illumos-gate/usr/src/uts/common/os/clock_realtime.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/timer.h>
30 #include <sys/systm.h>
31 #include <sys/param.h>
32 #include <sys/kmem.h>
33 #include <sys/debug.h>
34 
35 static clock_backend_t clock_realtime;
36 
37 static int
38 clock_realtime_settime(timespec_t *ts)
39 {
40 	mutex_enter(&tod_lock);
41 	tod_set(*ts);
42 	set_hrestime(ts);
43 	mutex_exit(&tod_lock);
44 
45 	return (0);
46 }
47 
48 /*
49  * We normally won't execute this path; libc will see CLOCK_REALTIME and
50  * fast trap directly into gethrestime().
51  */
52 static int
53 clock_realtime_gettime(timespec_t *ts)
54 {
55 	gethrestime(ts);
56 
57 	return (0);
58 }
59 
60 static int
61 clock_realtime_getres(timespec_t *ts)
62 {
63 	ts->tv_sec = 0;
64 	ts->tv_nsec = nsec_per_tick;
65 
66 	return (0);
67 }
68 
69 static void
70 clock_realtime_fire(void *arg)
71 {
72 	int cnt2nth;
73 	itimer_t *it = (itimer_t *)arg;
74 	timeout_id_t *tidp = it->it_arg;
75 	timespec_t now, interval2nth;
76 	timespec_t *val, *interval;
77 	proc_t *p = it->it_proc;
78 	clock_t ticks;
79 
80 	/*
81 	 * First call into the timer subsystem to get the signal going.
82 	 */
83 	timer_fire(it);
84 
85 	val = &it->it_itime.it_value;
86 	interval = &it->it_itime.it_interval;
87 
88 	mutex_enter(&p->p_lock);
89 
90 	if (!timerspecisset(interval)) {
91 		timerspecclear(val);
92 		*tidp = 0;
93 	} else {
94 		/*
95 		 * If this is an interval timer, we need to determine a time
96 		 * at which to go off in the future.  In the event that the
97 		 * clock has been adjusted, we want to find our new interval
98 		 * relatively quickly (and we don't want to simply take the
99 		 * current time and add the interval; it would lead to
100 		 * unnecessary jitter in the timer).  We therefore take steps
101 		 * from the time we expected to go off into the future;
102 		 * if the resulting time is still in the past, then we double
103 		 * our step size and continue.  Once the resulting time is
104 		 * in the future, we subtract our last step, change our step
105 		 * size back to the original interval, and repeat until we
106 		 * can get to a valid, future timeout in one step.  This
107 		 * assures that we will get the minimum, valid timeout
108 		 * value in a reasonable amount of wall time.
109 		 */
110 		for (;;) {
111 			interval2nth = *interval;
112 
113 			/*
114 			 * We put a floor on interval2nth at nsec_per_tick.
115 			 * If we don't do this, and the interval is shorter
116 			 * than the time required to run through this logic,
117 			 * we'll never catch up to the current time (which
118 			 * is a moving target).
119 			 */
120 			if (interval2nth.tv_sec == 0 &&
121 			    interval2nth.tv_nsec < nsec_per_tick)
122 				interval2nth.tv_nsec = nsec_per_tick;
123 
124 			for (cnt2nth = 0; ; cnt2nth++) {
125 				timespecadd(val, &interval2nth);
126 				gethrestime(&now);
127 				if (timerspeccmp(val, &now) > 0)
128 					break;
129 				timespecadd(&interval2nth, &interval2nth);
130 			}
131 			if (cnt2nth == 0)
132 				break;
133 			timespecsub(val, &interval2nth);
134 		}
135 
136 		ticks = timespectohz(val, now);
137 		*tidp = realtime_timeout(clock_realtime_fire, it, ticks);
138 	}
139 	mutex_exit(&p->p_lock);
140 }
141 
142 /*
143  * See the block comment in clock_realtime_timer_settime(), below.
144  */
145 static void
146 clock_realtime_fire_first(void *arg)
147 {
148 	itimer_t *it = (itimer_t *)arg;
149 	timespec_t now;
150 	timespec_t *val = &it->it_itime.it_value;
151 	timeout_id_t *tidp = it->it_arg;
152 	proc_t *p = it->it_proc;
153 
154 	gethrestime(&now);
155 
156 	if ((val->tv_sec > now.tv_sec) ||
157 	    (val->tv_sec == now.tv_sec && val->tv_nsec > now.tv_nsec)) {
158 		/*
159 		 * We went off too early.  We'll go to bed for one more tick,
160 		 * regardless of the actual difference; if the difference
161 		 * is greater than one tick, then we must have seen an adjtime.
162 		 */
163 		mutex_enter(&p->p_lock);
164 		*tidp = realtime_timeout(clock_realtime_fire, it, 1);
165 		mutex_exit(&p->p_lock);
166 		return;
167 	}
168 
169 	clock_realtime_fire(arg);
170 }
171 
172 /*ARGSUSED*/
173 static int
174 clock_realtime_timer_create(itimer_t *it, struct sigevent *ev)
175 {
176 	it->it_arg = kmem_zalloc(sizeof (timeout_id_t), KM_SLEEP);
177 
178 	return (0);
179 }
180 
181 static int
182 clock_realtime_timer_settime(itimer_t *it, int flags,
183 	const struct itimerspec *when)
184 {
185 	timeout_id_t tid, *tidp = it->it_arg;
186 	timespec_t now;
187 	proc_t *p = curproc;
188 	clock_t ticks;
189 
190 	gethrestime(&now);
191 
192 	mutex_enter(&p->p_lock);
193 
194 	while ((tid = *tidp) != 0) {
195 		*tidp = 0;
196 		mutex_exit(&p->p_lock);
197 		(void) untimeout(tid);
198 		mutex_enter(&p->p_lock);
199 	}
200 
201 	/*
202 	 * The timeout has been removed; it is safe to update it_itime.
203 	 */
204 	it->it_itime = *when;
205 
206 	if (timerspecisset(&it->it_itime.it_value)) {
207 		if (!(flags & TIMER_ABSTIME))
208 			timespecadd(&it->it_itime.it_value, &now);
209 
210 		ticks = timespectohz(&it->it_itime.it_value, now);
211 
212 		/*
213 		 * gethrestime() works by reading hres_last_tick, and
214 		 * adding in the current time delta (that is, the amount of
215 		 * time which has passed since the last tick of the clock).
216 		 * As a result, the time returned in "now", above, represents
217 		 * an hrestime sometime after lbolt was last bumped.
218 		 * The "ticks" we've been returned from timespectohz(), then,
219 		 * reflects the number of times the clock will tick between
220 		 * "now" and our desired execution time.
221 		 *
222 		 * However, when we call into realtime_timeout(), below,
223 		 * "ticks" will be interpreted against lbolt.  That is,
224 		 * if we specify 1 tick, we will be registering a callout
225 		 * for the next tick of the clock -- which may occur in
226 		 * less than (1 / hz) seconds.  More generally, we are
227 		 * registering a callout for "ticks" of the clock, which
228 		 * may be less than ("ticks" / hz) seconds (but not more than
229 		 * (1 / hz) seconds less).  In other words, we may go off
230 		 * early.
231 		 *
232 		 * This is only a problem for the initial firing of the
233 		 * timer, so we have the initial firing go through a
234 		 * different handler which implements a nanosleep-esque
235 		 * algorithm.
236 		 */
237 		*tidp = realtime_timeout(clock_realtime_fire_first, it, ticks);
238 	}
239 
240 	mutex_exit(&p->p_lock);
241 
242 	return (0);
243 }
244 
245 static int
246 clock_realtime_timer_gettime(itimer_t *it, struct itimerspec *when)
247 {
248 	timespec_t now;
249 	proc_t *p = curproc;
250 
251 	/*
252 	 * We always keep it_itime up to date, so we just need to snapshot
253 	 * the time under p_lock, and clean it up.
254 	 */
255 	mutex_enter(&p->p_lock);
256 	gethrestime(&now);
257 	*when = it->it_itime;
258 	mutex_exit(&p->p_lock);
259 
260 	if (!timerspecisset(&when->it_value))
261 		return (0);
262 
263 	if (timerspeccmp(&when->it_value, &now) < 0) {
264 		/*
265 		 * If this timer should have already gone off, set it_value
266 		 * to 0.
267 		 */
268 		timerspecclear(&when->it_value);
269 	} else {
270 		timespecsub(&when->it_value, &now);
271 	}
272 
273 	return (0);
274 }
275 
276 static int
277 clock_realtime_timer_delete(itimer_t *it)
278 {
279 	proc_t *p = curproc;
280 	timeout_id_t tid, *tidp = it->it_arg;
281 
282 	mutex_enter(&p->p_lock);
283 
284 	while ((tid = *tidp) != 0) {
285 		*tidp = 0;
286 		mutex_exit(&p->p_lock);
287 		(void) untimeout(tid);
288 		mutex_enter(&p->p_lock);
289 	}
290 
291 	mutex_exit(&p->p_lock);
292 
293 	kmem_free(tidp, sizeof (timeout_id_t));
294 
295 	return (0);
296 }
297 
298 /*ARGSUSED*/
299 void
300 clock_realtime_timer_lwpbind(itimer_t *it)
301 {
302 }
303 
304 void
305 clock_realtime_init()
306 {
307 	clock_backend_t *be = &clock_realtime;
308 	struct sigevent *ev = &be->clk_default;
309 
310 	ev->sigev_signo = SIGALRM;
311 	ev->sigev_notify = SIGEV_SIGNAL;
312 	ev->sigev_value.sival_ptr = NULL;
313 
314 	be->clk_clock_settime = clock_realtime_settime;
315 	be->clk_clock_gettime = clock_realtime_gettime;
316 	be->clk_clock_getres = clock_realtime_getres;
317 	be->clk_timer_gettime = clock_realtime_timer_gettime;
318 	be->clk_timer_settime = clock_realtime_timer_settime;
319 	be->clk_timer_delete = clock_realtime_timer_delete;
320 	be->clk_timer_lwpbind = clock_realtime_timer_lwpbind;
321 	be->clk_timer_create = clock_realtime_timer_create;
322 	clock_add_backend(CLOCK_REALTIME, &clock_realtime);
323 	/*
324 	 * For binary compatibility with old statically linked
325 	 * applications, we make the behavior of __CLOCK_REALTIME0
326 	 * the same as CLOCK_REALTIME.
327 	 */
328 	clock_add_backend(__CLOCK_REALTIME0, &clock_realtime);
329 }
330