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
clock_realtime_settime(timespec_t * ts)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
clock_realtime_gettime(timespec_t * ts)53 clock_realtime_gettime(timespec_t *ts)
54 {
55 gethrestime(ts);
56
57 return (0);
58 }
59
60 static int
clock_realtime_getres(timespec_t * ts)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
clock_realtime_fire(void * arg)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
clock_realtime_fire_first(void * arg)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
clock_realtime_timer_create(itimer_t * it,struct sigevent * ev)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
clock_realtime_timer_settime(itimer_t * it,int flags,const struct itimerspec * when)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
clock_realtime_timer_gettime(itimer_t * it,struct itimerspec * when)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
clock_realtime_timer_delete(itimer_t * it)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
clock_realtime_timer_lwpbind(itimer_t * it)300 clock_realtime_timer_lwpbind(itimer_t *it)
301 {
302 }
303
304 void
clock_realtime_init()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