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