diff options
author | Jeff Dike <jdike@addtoit.com> | 2007-10-16 04:27:25 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-16 12:43:08 -0400 |
commit | d2753a6d199791a6abc75d9f657e3457fe61705f (patch) | |
tree | c4cda30f216d6c045ca2b83bee1fa6bc4dfc3de3 /arch/um | |
parent | 791a644a8d73a9b95515f074afbb3caa0a9964fa (diff) |
uml: tickless support
Enable tickless support.
CONFIG_TICK_ONESHOT and CONFIG_NO_HZ are enabled.
itimer_clockevent gets CLOCK_EVT_FEAT_ONESHOT and an implementation of
.set_next_event.
CONFIG_UML_REAL_TIME_CLOCK goes away because it only makes sense when there is
a clock ticking away all the time. timer_handler now just calls do_IRQ once
without trying to figure out how many ticks to emulate.
The idle loop now needs to turn ticking on and off.
Userspace ticks keep happening as usual. However, the userspace loop keep
track of when the next wakeup should happen and suppresses process ticks until
that happens.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/um')
-rw-r--r-- | arch/um/Kconfig | 12 | ||||
-rw-r--r-- | arch/um/defconfig | 5 | ||||
-rw-r--r-- | arch/um/include/os.h | 1 | ||||
-rw-r--r-- | arch/um/kernel/process.c | 3 | ||||
-rw-r--r-- | arch/um/kernel/time.c | 45 | ||||
-rw-r--r-- | arch/um/os-Linux/skas/process.c | 27 | ||||
-rw-r--r-- | arch/um/os-Linux/time.c | 17 |
7 files changed, 55 insertions, 55 deletions
diff --git a/arch/um/Kconfig b/arch/um/Kconfig index 64fe8d5c067a..740d8a922e48 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig | |||
@@ -250,18 +250,6 @@ config KERNEL_STACK_ORDER | |||
250 | be 1 << order pages. The default is OK unless you're running Valgrind | 250 | be 1 << order pages. The default is OK unless you're running Valgrind |
251 | on UML, in which case, set this to 3. | 251 | on UML, in which case, set this to 3. |
252 | 252 | ||
253 | config UML_REAL_TIME_CLOCK | ||
254 | bool "Real-time Clock" | ||
255 | default y | ||
256 | help | ||
257 | This option makes UML time deltas match wall clock deltas. This | ||
258 | should normally be enabled. The exception would be if you are | ||
259 | debugging with UML and spend long times with UML stopped at a | ||
260 | breakpoint. In this case, when UML is restarted, it will call the | ||
261 | timer enough times to make up for the time spent at the breakpoint. | ||
262 | This could result in a noticeable lag. If this is a problem, then | ||
263 | disable this option. | ||
264 | |||
265 | endmenu | 253 | endmenu |
266 | 254 | ||
267 | source "init/Kconfig" | 255 | source "init/Kconfig" |
diff --git a/arch/um/defconfig b/arch/um/defconfig index 9f105c87fcc4..1cbbe980f106 100644 --- a/arch/um/defconfig +++ b/arch/um/defconfig | |||
@@ -73,8 +73,8 @@ CONFIG_FLATMEM=y | |||
73 | CONFIG_FLAT_NODE_MEM_MAP=y | 73 | CONFIG_FLAT_NODE_MEM_MAP=y |
74 | # CONFIG_SPARSEMEM_STATIC is not set | 74 | # CONFIG_SPARSEMEM_STATIC is not set |
75 | CONFIG_SPLIT_PTLOCK_CPUS=4 | 75 | CONFIG_SPLIT_PTLOCK_CPUS=4 |
76 | # CONFIG_TICK_ONESHOT is not set | 76 | CONFIG_TICK_ONESHOT=y |
77 | # CONFIG_NO_HZ is not set | 77 | CONFIG_NO_HZ=y |
78 | CONFIG_HIGH_RES_TIMERS=y | 78 | CONFIG_HIGH_RES_TIMERS=y |
79 | CONFIG_LD_SCRIPT_DYN=y | 79 | CONFIG_LD_SCRIPT_DYN=y |
80 | CONFIG_NET=y | 80 | CONFIG_NET=y |
@@ -87,7 +87,6 @@ CONFIG_MAGIC_SYSRQ=y | |||
87 | CONFIG_NEST_LEVEL=0 | 87 | CONFIG_NEST_LEVEL=0 |
88 | # CONFIG_HIGHMEM is not set | 88 | # CONFIG_HIGHMEM is not set |
89 | CONFIG_KERNEL_STACK_ORDER=0 | 89 | CONFIG_KERNEL_STACK_ORDER=0 |
90 | CONFIG_UML_REAL_TIME_CLOCK=y | ||
91 | 90 | ||
92 | # | 91 | # |
93 | # Code maturity level options | 92 | # Code maturity level options |
diff --git a/arch/um/include/os.h b/arch/um/include/os.h index a6d80721d3c4..e4f2fe11ba50 100644 --- a/arch/um/include/os.h +++ b/arch/um/include/os.h | |||
@@ -254,6 +254,7 @@ extern void os_dump_core(void); | |||
254 | extern int switch_timers(int to_real); | 254 | extern int switch_timers(int to_real); |
255 | extern void idle_sleep(int secs); | 255 | extern void idle_sleep(int secs); |
256 | extern int set_interval(void); | 256 | extern int set_interval(void); |
257 | extern int timer_one_shot(int ticks); | ||
257 | extern void disable_timer(void); | 258 | extern void disable_timer(void); |
258 | extern void uml_idle_timer(void); | 259 | extern void uml_idle_timer(void); |
259 | extern unsigned long long os_nsecs(void); | 260 | extern unsigned long long os_nsecs(void); |
diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c index 56d75afedbf7..aef494b6b81a 100644 --- a/arch/um/kernel/process.c +++ b/arch/um/kernel/process.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include "linux/ptrace.h" | 13 | #include "linux/ptrace.h" |
14 | #include "linux/random.h" | 14 | #include "linux/random.h" |
15 | #include "linux/sched.h" | 15 | #include "linux/sched.h" |
16 | #include "linux/tick.h" | ||
16 | #include "linux/threads.h" | 17 | #include "linux/threads.h" |
17 | #include "asm/pgtable.h" | 18 | #include "asm/pgtable.h" |
18 | #include "asm/uaccess.h" | 19 | #include "asm/uaccess.h" |
@@ -244,9 +245,11 @@ void default_idle(void) | |||
244 | if (need_resched()) | 245 | if (need_resched()) |
245 | schedule(); | 246 | schedule(); |
246 | 247 | ||
248 | tick_nohz_stop_sched_tick(); | ||
247 | switch_timers(1); | 249 | switch_timers(1); |
248 | idle_sleep(10); | 250 | idle_sleep(10); |
249 | switch_timers(0); | 251 | switch_timers(0); |
252 | tick_nohz_restart_sched_tick(); | ||
250 | } | 253 | } |
251 | } | 254 | } |
252 | 255 | ||
diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c index 3cb7135e5c47..2acdc7efb2ac 100644 --- a/arch/um/kernel/time.c +++ b/arch/um/kernel/time.c | |||
@@ -20,41 +20,12 @@ unsigned long long sched_clock(void) | |||
20 | return (unsigned long long)jiffies_64 * (1000000000 / HZ); | 20 | return (unsigned long long)jiffies_64 * (1000000000 / HZ); |
21 | } | 21 | } |
22 | 22 | ||
23 | #ifdef CONFIG_UML_REAL_TIME_CLOCK | ||
24 | static unsigned long long prev_nsecs[NR_CPUS]; | ||
25 | static long long delta[NR_CPUS]; /* Deviation per interval */ | ||
26 | #endif | ||
27 | |||
28 | void timer_handler(int sig, struct uml_pt_regs *regs) | 23 | void timer_handler(int sig, struct uml_pt_regs *regs) |
29 | { | 24 | { |
30 | unsigned long long ticks = 0; | ||
31 | unsigned long flags; | 25 | unsigned long flags; |
32 | #ifdef CONFIG_UML_REAL_TIME_CLOCK | ||
33 | int c = cpu(); | ||
34 | if (prev_nsecs[c]) { | ||
35 | /* We've had 1 tick */ | ||
36 | unsigned long long nsecs = os_nsecs(); | ||
37 | |||
38 | delta[c] += nsecs - prev_nsecs[c]; | ||
39 | prev_nsecs[c] = nsecs; | ||
40 | |||
41 | /* Protect against the host clock being set backwards */ | ||
42 | if (delta[c] < 0) | ||
43 | delta[c] = 0; | ||
44 | |||
45 | ticks += (delta[c] * HZ) / BILLION; | ||
46 | delta[c] -= (ticks * BILLION) / HZ; | ||
47 | } | ||
48 | else prev_nsecs[c] = os_nsecs(); | ||
49 | #else | ||
50 | ticks = 1; | ||
51 | #endif | ||
52 | 26 | ||
53 | local_irq_save(flags); | 27 | local_irq_save(flags); |
54 | while (ticks > 0) { | 28 | do_IRQ(TIMER_IRQ, regs); |
55 | do_IRQ(TIMER_IRQ, regs); | ||
56 | ticks--; | ||
57 | } | ||
58 | local_irq_restore(flags); | 29 | local_irq_restore(flags); |
59 | } | 30 | } |
60 | 31 | ||
@@ -68,10 +39,8 @@ static void itimer_set_mode(enum clock_event_mode mode, | |||
68 | 39 | ||
69 | case CLOCK_EVT_MODE_SHUTDOWN: | 40 | case CLOCK_EVT_MODE_SHUTDOWN: |
70 | case CLOCK_EVT_MODE_UNUSED: | 41 | case CLOCK_EVT_MODE_UNUSED: |
71 | disable_timer(); | ||
72 | break; | ||
73 | case CLOCK_EVT_MODE_ONESHOT: | 42 | case CLOCK_EVT_MODE_ONESHOT: |
74 | BUG(); | 43 | disable_timer(); |
75 | break; | 44 | break; |
76 | 45 | ||
77 | case CLOCK_EVT_MODE_RESUME: | 46 | case CLOCK_EVT_MODE_RESUME: |
@@ -79,13 +48,19 @@ static void itimer_set_mode(enum clock_event_mode mode, | |||
79 | } | 48 | } |
80 | } | 49 | } |
81 | 50 | ||
51 | static int itimer_next_event(unsigned long delta, | ||
52 | struct clock_event_device *evt) | ||
53 | { | ||
54 | return timer_one_shot(delta + 1); | ||
55 | } | ||
56 | |||
82 | static struct clock_event_device itimer_clockevent = { | 57 | static struct clock_event_device itimer_clockevent = { |
83 | .name = "itimer", | 58 | .name = "itimer", |
84 | .rating = 250, | 59 | .rating = 250, |
85 | .cpumask = CPU_MASK_ALL, | 60 | .cpumask = CPU_MASK_ALL, |
86 | .features = CLOCK_EVT_FEAT_PERIODIC, | 61 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, |
87 | .set_mode = itimer_set_mode, | 62 | .set_mode = itimer_set_mode, |
88 | .set_next_event = NULL, | 63 | .set_next_event = itimer_next_event, |
89 | .shift = 32, | 64 | .shift = 32, |
90 | .irq = 0, | 65 | .irq = 0, |
91 | }; | 66 | }; |
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index 0036164bb0fb..3e64814e888e 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c | |||
@@ -287,10 +287,18 @@ int start_userspace(unsigned long stub_stack) | |||
287 | 287 | ||
288 | void userspace(struct uml_pt_regs *regs) | 288 | void userspace(struct uml_pt_regs *regs) |
289 | { | 289 | { |
290 | struct itimerval timer; | ||
291 | unsigned long long nsecs, now; | ||
290 | int err, status, op, pid = userspace_pid[0]; | 292 | int err, status, op, pid = userspace_pid[0]; |
291 | /* To prevent races if using_sysemu changes under us.*/ | 293 | /* To prevent races if using_sysemu changes under us.*/ |
292 | int local_using_sysemu; | 294 | int local_using_sysemu; |
293 | 295 | ||
296 | if (getitimer(ITIMER_VIRTUAL, &timer)) | ||
297 | printk("Failed to get itimer, errno = %d\n", errno); | ||
298 | nsecs = timer.it_value.tv_sec * BILLION + | ||
299 | timer.it_value.tv_usec * 1000; | ||
300 | nsecs += os_nsecs(); | ||
301 | |||
294 | while (1) { | 302 | while (1) { |
295 | restore_registers(pid, regs); | 303 | restore_registers(pid, regs); |
296 | 304 | ||
@@ -333,8 +341,18 @@ void userspace(struct uml_pt_regs *regs) | |||
333 | case SIGTRAP: | 341 | case SIGTRAP: |
334 | relay_signal(SIGTRAP, regs); | 342 | relay_signal(SIGTRAP, regs); |
335 | break; | 343 | break; |
336 | case SIGIO: | ||
337 | case SIGVTALRM: | 344 | case SIGVTALRM: |
345 | now = os_nsecs(); | ||
346 | if(now < nsecs) | ||
347 | break; | ||
348 | block_signals(); | ||
349 | (*sig_info[sig])(sig, regs); | ||
350 | unblock_signals(); | ||
351 | nsecs = timer.it_value.tv_sec * BILLION + | ||
352 | timer.it_value.tv_usec * 1000; | ||
353 | nsecs += os_nsecs(); | ||
354 | break; | ||
355 | case SIGIO: | ||
338 | case SIGILL: | 356 | case SIGILL: |
339 | case SIGBUS: | 357 | case SIGBUS: |
340 | case SIGFPE: | 358 | case SIGFPE: |
@@ -378,6 +396,7 @@ __initcall(init_thread_regs); | |||
378 | 396 | ||
379 | int copy_context_skas0(unsigned long new_stack, int pid) | 397 | int copy_context_skas0(unsigned long new_stack, int pid) |
380 | { | 398 | { |
399 | struct timeval tv = { .tv_sec = 0, .tv_usec = 1000000 / UM_HZ }; | ||
381 | int err; | 400 | int err; |
382 | unsigned long current_stack = current_stub_stack(); | 401 | unsigned long current_stack = current_stub_stack(); |
383 | struct stub_data *data = (struct stub_data *) current_stack; | 402 | struct stub_data *data = (struct stub_data *) current_stack; |
@@ -392,9 +411,9 @@ int copy_context_skas0(unsigned long new_stack, int pid) | |||
392 | *data = ((struct stub_data) { .offset = MMAP_OFFSET(new_offset), | 411 | *data = ((struct stub_data) { .offset = MMAP_OFFSET(new_offset), |
393 | .fd = new_fd, | 412 | .fd = new_fd, |
394 | .timer = ((struct itimerval) | 413 | .timer = ((struct itimerval) |
395 | { { 0, 1000000 / UM_HZ }, | 414 | { .it_value = tv, |
396 | { 0, 1000000 / UM_HZ }}) | 415 | .it_interval = tv }) }); |
397 | }); | 416 | |
398 | err = ptrace_setregs(pid, thread_regs); | 417 | err = ptrace_setregs(pid, thread_regs); |
399 | if (err < 0) | 418 | if (err < 0) |
400 | panic("copy_context_skas0 : PTRACE_SETREGS failed, " | 419 | panic("copy_context_skas0 : PTRACE_SETREGS failed, " |
diff --git a/arch/um/os-Linux/time.c b/arch/um/os-Linux/time.c index 6ff3d98281ba..9ffc61ac8ed6 100644 --- a/arch/um/os-Linux/time.c +++ b/arch/um/os-Linux/time.c | |||
@@ -26,6 +26,21 @@ int set_interval(void) | |||
26 | return 0; | 26 | return 0; |
27 | } | 27 | } |
28 | 28 | ||
29 | int timer_one_shot(int ticks) | ||
30 | { | ||
31 | unsigned long usec = ticks * 1000000 / UM_HZ; | ||
32 | unsigned long sec = usec / 1000000; | ||
33 | struct itimerval interval; | ||
34 | |||
35 | usec %= 1000000; | ||
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 | |||
29 | void disable_timer(void) | 44 | void disable_timer(void) |
30 | { | 45 | { |
31 | struct itimerval disable = ((struct itimerval) { { 0, 0 }, { 0, 0 }}); | 46 | struct itimerval disable = ((struct itimerval) { { 0, 0 }, { 0, 0 }}); |
@@ -74,7 +89,7 @@ unsigned long long os_nsecs(void) | |||
74 | struct timeval tv; | 89 | struct timeval tv; |
75 | 90 | ||
76 | gettimeofday(&tv, NULL); | 91 | gettimeofday(&tv, NULL); |
77 | return (unsigned long long) tv.tv_sec * BILLION + tv.tv_usec * 1000; | 92 | return timeval_to_ns(&tv); |
78 | } | 93 | } |
79 | 94 | ||
80 | void idle_sleep(int secs) | 95 | void idle_sleep(int secs) |