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