linux/arch/um/os-Linux/time.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2000 - 2007 Jeff Dike (jdike{addtoit,linux.intel}.com)
   3 * Licensed under the GPL
   4 */
   5
   6#include <stddef.h>
   7#include <errno.h>
   8#include <signal.h>
   9#include <time.h>
  10#include <sys/time.h>
  11#include "kern_util.h"
  12#include "os.h"
  13#include "internal.h"
  14
  15int set_interval(void)
  16{
  17        int usec = UM_USEC_PER_SEC / UM_HZ;
  18        struct itimerval interval = ((struct itimerval) { { 0, usec },
  19                                                          { 0, usec } });
  20
  21        if (setitimer(ITIMER_VIRTUAL, &interval, NULL) == -1)
  22                return -errno;
  23
  24        return 0;
  25}
  26
  27int timer_one_shot(int ticks)
  28{
  29        unsigned long usec = ticks * UM_USEC_PER_SEC / UM_HZ;
  30        unsigned long sec = usec / UM_USEC_PER_SEC;
  31        struct itimerval interval;
  32
  33        usec %= UM_USEC_PER_SEC;
  34        interval = ((struct itimerval) { { 0, 0 }, { sec, usec } });
  35
  36        if (setitimer(ITIMER_VIRTUAL, &interval, NULL) == -1)
  37                return -errno;
  38
  39        return 0;
  40}
  41
  42/**
  43 * timeval_to_ns - Convert timeval to nanoseconds
  44 * @ts:         pointer to the timeval variable to be converted
  45 *
  46 * Returns the scalar nanosecond representation of the timeval
  47 * parameter.
  48 *
  49 * Ripped from linux/time.h because it's a kernel header, and thus
  50 * unusable from here.
  51 */
  52static inline long long timeval_to_ns(const struct timeval *tv)
  53{
  54        return ((long long) tv->tv_sec * UM_NSEC_PER_SEC) +
  55                tv->tv_usec * UM_NSEC_PER_USEC;
  56}
  57
  58long long disable_timer(void)
  59{
  60        struct itimerval time = ((struct itimerval) { { 0, 0 }, { 0, 0 } });
  61        long long remain, max = UM_NSEC_PER_SEC / UM_HZ;
  62
  63        if (setitimer(ITIMER_VIRTUAL, &time, &time) < 0)
  64                printk(UM_KERN_ERR "disable_timer - setitimer failed, "
  65                       "errno = %d\n", errno);
  66
  67        remain = timeval_to_ns(&time.it_value);
  68        if (remain > max)
  69                remain = max;
  70
  71        return remain;
  72}
  73
  74long long os_nsecs(void)
  75{
  76        struct timeval tv;
  77
  78        gettimeofday(&tv, NULL);
  79        return timeval_to_ns(&tv);
  80}
  81
  82#ifdef UML_CONFIG_NO_HZ
  83static int after_sleep_interval(struct timespec *ts)
  84{
  85        return 0;
  86}
  87
  88static void deliver_alarm(void)
  89{
  90        alarm_handler(SIGVTALRM, NULL);
  91}
  92
  93static unsigned long long sleep_time(unsigned long long nsecs)
  94{
  95        return nsecs;
  96}
  97
  98#else
  99unsigned long long last_tick;
 100unsigned long long skew;
 101
 102static void deliver_alarm(void)
 103{
 104        unsigned long long this_tick = os_nsecs();
 105        int one_tick = UM_NSEC_PER_SEC / UM_HZ;
 106
 107        /* Protection against the host's time going backwards */
 108        if ((last_tick != 0) && (this_tick < last_tick))
 109                this_tick = last_tick;
 110
 111        if (last_tick == 0)
 112                last_tick = this_tick - one_tick;
 113
 114        skew += this_tick - last_tick;
 115
 116        while (skew >= one_tick) {
 117                alarm_handler(SIGVTALRM, NULL);
 118                skew -= one_tick;
 119        }
 120
 121        last_tick = this_tick;
 122}
 123
 124static unsigned long long sleep_time(unsigned long long nsecs)
 125{
 126        return nsecs > skew ? nsecs - skew : 0;
 127}
 128
 129static inline long long timespec_to_us(const struct timespec *ts)
 130{
 131        return ((long long) ts->tv_sec * UM_USEC_PER_SEC) +
 132                ts->tv_nsec / UM_NSEC_PER_USEC;
 133}
 134
 135static int after_sleep_interval(struct timespec *ts)
 136{
 137        int usec = UM_USEC_PER_SEC / UM_HZ;
 138        long long start_usecs = timespec_to_us(ts);
 139        struct timeval tv;
 140        struct itimerval interval;
 141
 142        /*
 143         * It seems that rounding can increase the value returned from
 144         * setitimer to larger than the one passed in.  Over time,
 145         * this will cause the remaining time to be greater than the
 146         * tick interval.  If this happens, then just reduce the first
 147         * tick to the interval value.
 148         */
 149        if (start_usecs > usec)
 150                start_usecs = usec;
 151
 152        start_usecs -= skew / UM_NSEC_PER_USEC;
 153        if (start_usecs < 0)
 154                start_usecs = 0;
 155
 156        tv = ((struct timeval) { .tv_sec  = start_usecs / UM_USEC_PER_SEC,
 157                                 .tv_usec = start_usecs % UM_USEC_PER_SEC });
 158        interval = ((struct itimerval) { { 0, usec }, tv });
 159
 160        if (setitimer(ITIMER_VIRTUAL, &interval, NULL) == -1)
 161                return -errno;
 162
 163        return 0;
 164}
 165#endif
 166
 167void idle_sleep(unsigned long long nsecs)
 168{
 169        struct timespec ts;
 170
 171        /*
 172         * nsecs can come in as zero, in which case, this starts a
 173         * busy loop.  To prevent this, reset nsecs to the tick
 174         * interval if it is zero.
 175         */
 176        if (nsecs == 0)
 177                nsecs = UM_NSEC_PER_SEC / UM_HZ;
 178
 179        nsecs = sleep_time(nsecs);
 180        ts = ((struct timespec) { .tv_sec       = nsecs / UM_NSEC_PER_SEC,
 181                                  .tv_nsec      = nsecs % UM_NSEC_PER_SEC });
 182
 183        if (nanosleep(&ts, &ts) == 0)
 184                deliver_alarm();
 185        after_sleep_interval(&ts);
 186}
 187
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.