diff options
-rw-r--r-- | arch/um/os-Linux/time.c | 54 |
1 files changed, 51 insertions, 3 deletions
diff --git a/arch/um/os-Linux/time.c b/arch/um/os-Linux/time.c index e34e1effe0f5..ef02d941c2ad 100644 --- a/arch/um/os-Linux/time.c +++ b/arch/um/os-Linux/time.c | |||
@@ -59,7 +59,7 @@ long long disable_timer(void) | |||
59 | { | 59 | { |
60 | struct itimerval time = ((struct itimerval) { { 0, 0 }, { 0, 0 } }); | 60 | struct itimerval time = ((struct itimerval) { { 0, 0 }, { 0, 0 } }); |
61 | 61 | ||
62 | if(setitimer(ITIMER_VIRTUAL, &time, &time) < 0) | 62 | if (setitimer(ITIMER_VIRTUAL, &time, &time) < 0) |
63 | printk(UM_KERN_ERR "disable_timer - setitimer failed, " | 63 | printk(UM_KERN_ERR "disable_timer - setitimer failed, " |
64 | "errno = %d\n", errno); | 64 | "errno = %d\n", errno); |
65 | 65 | ||
@@ -74,13 +74,61 @@ long long os_nsecs(void) | |||
74 | return timeval_to_ns(&tv); | 74 | return timeval_to_ns(&tv); |
75 | } | 75 | } |
76 | 76 | ||
77 | #ifdef UML_CONFIG_NO_HZ | ||
78 | static int after_sleep_interval(struct timespec *ts) | ||
79 | { | ||
80 | } | ||
81 | #else | ||
82 | static inline long long timespec_to_us(const struct timespec *ts) | ||
83 | { | ||
84 | return ((long long) ts->tv_sec * UM_USEC_PER_SEC) + | ||
85 | ts->tv_nsec / UM_NSEC_PER_USEC; | ||
86 | } | ||
87 | |||
88 | static int after_sleep_interval(struct timespec *ts) | ||
89 | { | ||
90 | int usec = UM_USEC_PER_SEC / UM_HZ; | ||
91 | long long start_usecs = timespec_to_us(ts); | ||
92 | struct timeval tv; | ||
93 | struct itimerval interval; | ||
94 | |||
95 | /* | ||
96 | * It seems that rounding can increase the value returned from | ||
97 | * setitimer to larger than the one passed in. Over time, | ||
98 | * this will cause the remaining time to be greater than the | ||
99 | * tick interval. If this happens, then just reduce the first | ||
100 | * tick to the interval value. | ||
101 | */ | ||
102 | if (start_usecs > usec) | ||
103 | start_usecs = usec; | ||
104 | tv = ((struct timeval) { .tv_sec = start_usecs / UM_USEC_PER_SEC, | ||
105 | .tv_usec = start_usecs % UM_USEC_PER_SEC }); | ||
106 | interval = ((struct itimerval) { { 0, usec }, tv }); | ||
107 | |||
108 | if (setitimer(ITIMER_VIRTUAL, &interval, NULL) == -1) | ||
109 | return -errno; | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | #endif | ||
114 | |||
77 | extern void alarm_handler(int sig, struct sigcontext *sc); | 115 | extern void alarm_handler(int sig, struct sigcontext *sc); |
78 | 116 | ||
79 | void idle_sleep(unsigned long long nsecs) | 117 | void idle_sleep(unsigned long long nsecs) |
80 | { | 118 | { |
81 | struct timespec ts = { .tv_sec = nsecs / UM_NSEC_PER_SEC, | 119 | struct timespec ts; |
82 | .tv_nsec = nsecs % UM_NSEC_PER_SEC }; | 120 | |
121 | /* | ||
122 | * nsecs can come in as zero, in which case, this starts a | ||
123 | * busy loop. To prevent this, reset nsecs to the tick | ||
124 | * interval if it is zero. | ||
125 | */ | ||
126 | if (nsecs == 0) | ||
127 | nsecs = UM_NSEC_PER_SEC / UM_HZ; | ||
128 | ts = ((struct timespec) { .tv_sec = nsecs / UM_NSEC_PER_SEC, | ||
129 | .tv_nsec = nsecs % UM_NSEC_PER_SEC }); | ||
83 | 130 | ||
84 | if (nanosleep(&ts, &ts) == 0) | 131 | if (nanosleep(&ts, &ts) == 0) |
85 | alarm_handler(SIGVTALRM, NULL); | 132 | alarm_handler(SIGVTALRM, NULL); |
133 | after_sleep_interval(&ts); | ||
86 | } | 134 | } |