Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/roland...
[deliverable/linux.git] / arch / um / os-Linux / time.c
CommitLineData
cff65c4f 1/*
4c9e1385 2 * Copyright (C) 2000 - 2007 Jeff Dike (jdike{addtoit,linux.intel}.com)
cff65c4f
GS
3 * Licensed under the GPL
4 */
5
4c9e1385
JD
6#include <stddef.h>
7#include <errno.h>
8#include <signal.h>
cff65c4f 9#include <time.h>
1da177e4 10#include <sys/time.h>
cff65c4f 11#include "kern_constants.h"
fe2cc53e 12#include "kern_util.h"
cff65c4f 13#include "os.h"
fe2cc53e 14#include "process.h"
4c9e1385 15#include "user.h"
cff65c4f 16
a2f018bf 17int set_interval(void)
cff65c4f 18{
1a805219 19 int usec = UM_USEC_PER_SEC / UM_HZ;
cff65c4f
GS
20 struct itimerval interval = ((struct itimerval) { { 0, usec },
21 { 0, usec } });
22
a2f018bf 23 if (setitimer(ITIMER_VIRTUAL, &interval, NULL) == -1)
537ae946
JD
24 return -errno;
25
26 return 0;
cff65c4f
GS
27}
28
d2753a6d
JD
29int timer_one_shot(int ticks)
30{
1a805219
JD
31 unsigned long usec = ticks * UM_USEC_PER_SEC / UM_HZ;
32 unsigned long sec = usec / UM_USEC_PER_SEC;
d2753a6d
JD
33 struct itimerval interval;
34
1a805219 35 usec %= UM_USEC_PER_SEC;
d2753a6d
JD
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
5f734614
JD
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)
cff65c4f 61{
b160fb63 62 struct itimerval time = ((struct itimerval) { { 0, 0 }, { 0, 0 } });
fe2cc53e 63 int remain, max = UM_NSEC_PER_SEC / UM_HZ;
4c9e1385 64
364e3a3d 65 if (setitimer(ITIMER_VIRTUAL, &time, &time) < 0)
4c9e1385
JD
66 printk(UM_KERN_ERR "disable_timer - setitimer failed, "
67 "errno = %d\n", errno);
181bde80 68
fe2cc53e
JD
69 remain = timeval_to_ns(&time.it_value);
70 if (remain > max)
71 remain = max;
72
73 return remain;
cff65c4f 74}
1da177e4 75
5f734614 76long long os_nsecs(void)
1da177e4
LT
77{
78 struct timeval tv;
79
80 gettimeofday(&tv, NULL);
d2753a6d 81 return timeval_to_ns(&tv);
1da177e4
LT
82}
83
364e3a3d
JD
84#ifdef UML_CONFIG_NO_HZ
85static int after_sleep_interval(struct timespec *ts)
86{
0a765329 87 return 0;
364e3a3d 88}
fe2cc53e
JD
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
364e3a3d 100#else
fe2cc53e
JD
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
06e1e4ff
JD
109 /* Protection against the host's time going backwards */
110 if ((last_tick != 0) && (this_tick < last_tick))
111 this_tick = last_tick;
112
fe2cc53e
JD
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
364e3a3d
JD
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;
fe2cc53e
JD
153
154 start_usecs -= skew / UM_NSEC_PER_USEC;
06e1e4ff
JD
155 if (start_usecs < 0)
156 start_usecs = 0;
157
364e3a3d
JD
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
b160fb63 169void idle_sleep(unsigned long long nsecs)
cff65c4f 170{
364e3a3d
JD
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;
fe2cc53e
JD
180
181 nsecs = sleep_time(nsecs);
364e3a3d
JD
182 ts = ((struct timespec) { .tv_sec = nsecs / UM_NSEC_PER_SEC,
183 .tv_nsec = nsecs % UM_NSEC_PER_SEC });
cff65c4f 184
b160fb63 185 if (nanosleep(&ts, &ts) == 0)
fe2cc53e 186 deliver_alarm();
364e3a3d 187 after_sleep_interval(&ts);
cff65c4f 188}
This page took 0.452582 seconds and 5 git commands to generate.