root/usr/src/uts/common/os/clock_realtime.c
/*
 * 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);
}