Home | History | Annotate | Download | only in os
      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