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