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