]>
Commit | Line | Data |
---|---|---|
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> |
37185b33 AV |
11 | #include <kern_util.h> |
12 | #include <os.h> | |
248b74c7 | 13 | #include "internal.h" |
cff65c4f | 14 | |
a2f018bf | 15 | int set_interval(void) |
cff65c4f | 16 | { |
1a805219 | 17 | int usec = UM_USEC_PER_SEC / UM_HZ; |
cff65c4f GS |
18 | struct itimerval interval = ((struct itimerval) { { 0, usec }, |
19 | { 0, usec } }); | |
20 | ||
a2f018bf | 21 | if (setitimer(ITIMER_VIRTUAL, &interval, NULL) == -1) |
537ae946 JD |
22 | return -errno; |
23 | ||
24 | return 0; | |
cff65c4f GS |
25 | } |
26 | ||
d2753a6d JD |
27 | int timer_one_shot(int ticks) |
28 | { | |
1a805219 JD |
29 | unsigned long usec = ticks * UM_USEC_PER_SEC / UM_HZ; |
30 | unsigned long sec = usec / UM_USEC_PER_SEC; | |
d2753a6d JD |
31 | struct itimerval interval; |
32 | ||
1a805219 | 33 | usec %= UM_USEC_PER_SEC; |
d2753a6d JD |
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 | ||
5f734614 JD |
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 | */ | |
52 | static 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 | ||
58 | long long disable_timer(void) | |
cff65c4f | 59 | { |
b160fb63 | 60 | struct itimerval time = ((struct itimerval) { { 0, 0 }, { 0, 0 } }); |
482db6df | 61 | long long remain, max = UM_NSEC_PER_SEC / UM_HZ; |
4c9e1385 | 62 | |
364e3a3d | 63 | if (setitimer(ITIMER_VIRTUAL, &time, &time) < 0) |
4c9e1385 JD |
64 | printk(UM_KERN_ERR "disable_timer - setitimer failed, " |
65 | "errno = %d\n", errno); | |
181bde80 | 66 | |
fe2cc53e JD |
67 | remain = timeval_to_ns(&time.it_value); |
68 | if (remain > max) | |
69 | remain = max; | |
70 | ||
71 | return remain; | |
cff65c4f | 72 | } |
1da177e4 | 73 | |
5f734614 | 74 | long long os_nsecs(void) |
1da177e4 LT |
75 | { |
76 | struct timeval tv; | |
77 | ||
78 | gettimeofday(&tv, NULL); | |
d2753a6d | 79 | return timeval_to_ns(&tv); |
1da177e4 LT |
80 | } |
81 | ||
364e3a3d JD |
82 | #ifdef UML_CONFIG_NO_HZ |
83 | static int after_sleep_interval(struct timespec *ts) | |
84 | { | |
0a765329 | 85 | return 0; |
364e3a3d | 86 | } |
fe2cc53e JD |
87 | |
88 | static void deliver_alarm(void) | |
89 | { | |
d3c1cfcd | 90 | alarm_handler(SIGVTALRM, NULL, NULL); |
fe2cc53e JD |
91 | } |
92 | ||
93 | static unsigned long long sleep_time(unsigned long long nsecs) | |
94 | { | |
95 | return nsecs; | |
96 | } | |
97 | ||
364e3a3d | 98 | #else |
fe2cc53e JD |
99 | unsigned long long last_tick; |
100 | unsigned long long skew; | |
101 | ||
102 | static void deliver_alarm(void) | |
103 | { | |
104 | unsigned long long this_tick = os_nsecs(); | |
105 | int one_tick = UM_NSEC_PER_SEC / UM_HZ; | |
106 | ||
06e1e4ff JD |
107 | /* Protection against the host's time going backwards */ |
108 | if ((last_tick != 0) && (this_tick < last_tick)) | |
109 | this_tick = last_tick; | |
110 | ||
fe2cc53e JD |
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) { | |
bc6c8364 | 117 | alarm_handler(SIGVTALRM, NULL, NULL); |
fe2cc53e JD |
118 | skew -= one_tick; |
119 | } | |
120 | ||
121 | last_tick = this_tick; | |
122 | } | |
123 | ||
124 | static unsigned long long sleep_time(unsigned long long nsecs) | |
125 | { | |
126 | return nsecs > skew ? nsecs - skew : 0; | |
127 | } | |
128 | ||
364e3a3d JD |
129 | static 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 | ||
135 | static 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; | |
fe2cc53e JD |
151 | |
152 | start_usecs -= skew / UM_NSEC_PER_USEC; | |
06e1e4ff JD |
153 | if (start_usecs < 0) |
154 | start_usecs = 0; | |
155 | ||
364e3a3d JD |
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 | ||
b160fb63 | 167 | void idle_sleep(unsigned long long nsecs) |
cff65c4f | 168 | { |
364e3a3d JD |
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; | |
fe2cc53e JD |
178 | |
179 | nsecs = sleep_time(nsecs); | |
364e3a3d JD |
180 | ts = ((struct timespec) { .tv_sec = nsecs / UM_NSEC_PER_SEC, |
181 | .tv_nsec = nsecs % UM_NSEC_PER_SEC }); | |
cff65c4f | 182 | |
b160fb63 | 183 | if (nanosleep(&ts, &ts) == 0) |
fe2cc53e | 184 | deliver_alarm(); |
364e3a3d | 185 | after_sleep_interval(&ts); |
cff65c4f | 186 | } |