aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Dike <jdike@addtoit.com>2007-11-28 19:21:51 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-11-29 12:24:53 -0500
commit364e3a3d8a26aae058cf1c257457ad1f6b1cfe4c (patch)
tree1e5c04d671c5fcc8c2e25c37b08378e95a7e4e11
parent9fc89c2dea7ca7915e6606e49167cdca2f3c4e30 (diff)
uml: fix !NO_HZ busy-loop
With NO_HZ disabled, the UML idle loop effectively becomes a busy loop, as it will sleep for no time. The cause was forgetting to restart the tick after waking up from sleep. It was disabled before sleeping, and the remaining time used as the interval to sleep. So, the tick needs to be restarted when nanosleep finishes. This is done by introducing after_sleep_interval, which is empty in the NO_HZ case, but which sets the tick starting in the !NO_HZ case. Signed-off-by: Jeff Dike <jdike@linux.intel.com> Cc: Miklos Szeredi <miklos@szeredi.hu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/um/os-Linux/time.c54
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
78static int after_sleep_interval(struct timespec *ts)
79{
80}
81#else
82static 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
88static 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
77extern void alarm_handler(int sig, struct sigcontext *sc); 115extern void alarm_handler(int sig, struct sigcontext *sc);
78 116
79void idle_sleep(unsigned long long nsecs) 117void 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}