diff options
Diffstat (limited to 'kernel/time/tick-sched.c')
-rw-r--r-- | kernel/time/tick-sched.c | 131 |
1 files changed, 93 insertions, 38 deletions
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index cb02324bdb88..342fc9ccab46 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/profile.h> | 20 | #include <linux/profile.h> |
21 | #include <linux/sched.h> | 21 | #include <linux/sched.h> |
22 | #include <linux/tick.h> | 22 | #include <linux/tick.h> |
23 | #include <linux/module.h> | ||
23 | 24 | ||
24 | #include <asm/irq_regs.h> | 25 | #include <asm/irq_regs.h> |
25 | 26 | ||
@@ -154,7 +155,7 @@ void tick_nohz_update_jiffies(void) | |||
154 | touch_softlockup_watchdog(); | 155 | touch_softlockup_watchdog(); |
155 | } | 156 | } |
156 | 157 | ||
157 | void tick_nohz_stop_idle(int cpu) | 158 | static void tick_nohz_stop_idle(int cpu) |
158 | { | 159 | { |
159 | struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); | 160 | struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); |
160 | 161 | ||
@@ -190,9 +191,17 @@ u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time) | |||
190 | { | 191 | { |
191 | struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); | 192 | struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); |
192 | 193 | ||
193 | *last_update_time = ktime_to_us(ts->idle_lastupdate); | 194 | if (!tick_nohz_enabled) |
195 | return -1; | ||
196 | |||
197 | if (ts->idle_active) | ||
198 | *last_update_time = ktime_to_us(ts->idle_lastupdate); | ||
199 | else | ||
200 | *last_update_time = ktime_to_us(ktime_get()); | ||
201 | |||
194 | return ktime_to_us(ts->idle_sleeptime); | 202 | return ktime_to_us(ts->idle_sleeptime); |
195 | } | 203 | } |
204 | EXPORT_SYMBOL_GPL(get_cpu_idle_time_us); | ||
196 | 205 | ||
197 | /** | 206 | /** |
198 | * tick_nohz_stop_sched_tick - stop the idle tick from the idle task | 207 | * tick_nohz_stop_sched_tick - stop the idle tick from the idle task |
@@ -261,7 +270,7 @@ void tick_nohz_stop_sched_tick(int inidle) | |||
261 | next_jiffies = get_next_timer_interrupt(last_jiffies); | 270 | next_jiffies = get_next_timer_interrupt(last_jiffies); |
262 | delta_jiffies = next_jiffies - last_jiffies; | 271 | delta_jiffies = next_jiffies - last_jiffies; |
263 | 272 | ||
264 | if (rcu_needs_cpu(cpu)) | 273 | if (rcu_needs_cpu(cpu) || printk_needs_cpu(cpu)) |
265 | delta_jiffies = 1; | 274 | delta_jiffies = 1; |
266 | /* | 275 | /* |
267 | * Do not stop the tick, if we are only one off | 276 | * Do not stop the tick, if we are only one off |
@@ -291,7 +300,7 @@ void tick_nohz_stop_sched_tick(int inidle) | |||
291 | goto out; | 300 | goto out; |
292 | } | 301 | } |
293 | 302 | ||
294 | ts->idle_tick = ts->sched_timer.expires; | 303 | ts->idle_tick = hrtimer_get_expires(&ts->sched_timer); |
295 | ts->tick_stopped = 1; | 304 | ts->tick_stopped = 1; |
296 | ts->idle_jiffies = last_jiffies; | 305 | ts->idle_jiffies = last_jiffies; |
297 | rcu_enter_nohz(); | 306 | rcu_enter_nohz(); |
@@ -368,6 +377,32 @@ ktime_t tick_nohz_get_sleep_length(void) | |||
368 | return ts->sleep_length; | 377 | return ts->sleep_length; |
369 | } | 378 | } |
370 | 379 | ||
380 | static void tick_nohz_restart(struct tick_sched *ts, ktime_t now) | ||
381 | { | ||
382 | hrtimer_cancel(&ts->sched_timer); | ||
383 | hrtimer_set_expires(&ts->sched_timer, ts->idle_tick); | ||
384 | |||
385 | while (1) { | ||
386 | /* Forward the time to expire in the future */ | ||
387 | hrtimer_forward(&ts->sched_timer, now, tick_period); | ||
388 | |||
389 | if (ts->nohz_mode == NOHZ_MODE_HIGHRES) { | ||
390 | hrtimer_start_expires(&ts->sched_timer, | ||
391 | HRTIMER_MODE_ABS); | ||
392 | /* Check, if the timer was already in the past */ | ||
393 | if (hrtimer_active(&ts->sched_timer)) | ||
394 | break; | ||
395 | } else { | ||
396 | if (!tick_program_event( | ||
397 | hrtimer_get_expires(&ts->sched_timer), 0)) | ||
398 | break; | ||
399 | } | ||
400 | /* Update jiffies and reread time */ | ||
401 | tick_do_update_jiffies64(now); | ||
402 | now = ktime_get(); | ||
403 | } | ||
404 | } | ||
405 | |||
371 | /** | 406 | /** |
372 | * tick_nohz_restart_sched_tick - restart the idle tick from the idle task | 407 | * tick_nohz_restart_sched_tick - restart the idle tick from the idle task |
373 | * | 408 | * |
@@ -421,35 +456,16 @@ void tick_nohz_restart_sched_tick(void) | |||
421 | */ | 456 | */ |
422 | ts->tick_stopped = 0; | 457 | ts->tick_stopped = 0; |
423 | ts->idle_exittime = now; | 458 | ts->idle_exittime = now; |
424 | hrtimer_cancel(&ts->sched_timer); | ||
425 | ts->sched_timer.expires = ts->idle_tick; | ||
426 | 459 | ||
427 | while (1) { | 460 | tick_nohz_restart(ts, now); |
428 | /* Forward the time to expire in the future */ | ||
429 | hrtimer_forward(&ts->sched_timer, now, tick_period); | ||
430 | 461 | ||
431 | if (ts->nohz_mode == NOHZ_MODE_HIGHRES) { | ||
432 | hrtimer_start(&ts->sched_timer, | ||
433 | ts->sched_timer.expires, | ||
434 | HRTIMER_MODE_ABS); | ||
435 | /* Check, if the timer was already in the past */ | ||
436 | if (hrtimer_active(&ts->sched_timer)) | ||
437 | break; | ||
438 | } else { | ||
439 | if (!tick_program_event(ts->sched_timer.expires, 0)) | ||
440 | break; | ||
441 | } | ||
442 | /* Update jiffies and reread time */ | ||
443 | tick_do_update_jiffies64(now); | ||
444 | now = ktime_get(); | ||
445 | } | ||
446 | local_irq_enable(); | 462 | local_irq_enable(); |
447 | } | 463 | } |
448 | 464 | ||
449 | static int tick_nohz_reprogram(struct tick_sched *ts, ktime_t now) | 465 | static int tick_nohz_reprogram(struct tick_sched *ts, ktime_t now) |
450 | { | 466 | { |
451 | hrtimer_forward(&ts->sched_timer, now, tick_period); | 467 | hrtimer_forward(&ts->sched_timer, now, tick_period); |
452 | return tick_program_event(ts->sched_timer.expires, 0); | 468 | return tick_program_event(hrtimer_get_expires(&ts->sched_timer), 0); |
453 | } | 469 | } |
454 | 470 | ||
455 | /* | 471 | /* |
@@ -494,10 +510,6 @@ static void tick_nohz_handler(struct clock_event_device *dev) | |||
494 | update_process_times(user_mode(regs)); | 510 | update_process_times(user_mode(regs)); |
495 | profile_tick(CPU_PROFILING); | 511 | profile_tick(CPU_PROFILING); |
496 | 512 | ||
497 | /* Do not restart, when we are in the idle loop */ | ||
498 | if (ts->tick_stopped) | ||
499 | return; | ||
500 | |||
501 | while (tick_nohz_reprogram(ts, now)) { | 513 | while (tick_nohz_reprogram(ts, now)) { |
502 | now = ktime_get(); | 514 | now = ktime_get(); |
503 | tick_do_update_jiffies64(now); | 515 | tick_do_update_jiffies64(now); |
@@ -532,7 +544,7 @@ static void tick_nohz_switch_to_nohz(void) | |||
532 | next = tick_init_jiffy_update(); | 544 | next = tick_init_jiffy_update(); |
533 | 545 | ||
534 | for (;;) { | 546 | for (;;) { |
535 | ts->sched_timer.expires = next; | 547 | hrtimer_set_expires(&ts->sched_timer, next); |
536 | if (!tick_program_event(next, 0)) | 548 | if (!tick_program_event(next, 0)) |
537 | break; | 549 | break; |
538 | next = ktime_add(next, tick_period); | 550 | next = ktime_add(next, tick_period); |
@@ -543,6 +555,41 @@ static void tick_nohz_switch_to_nohz(void) | |||
543 | smp_processor_id()); | 555 | smp_processor_id()); |
544 | } | 556 | } |
545 | 557 | ||
558 | /* | ||
559 | * When NOHZ is enabled and the tick is stopped, we need to kick the | ||
560 | * tick timer from irq_enter() so that the jiffies update is kept | ||
561 | * alive during long running softirqs. That's ugly as hell, but | ||
562 | * correctness is key even if we need to fix the offending softirq in | ||
563 | * the first place. | ||
564 | * | ||
565 | * Note, this is different to tick_nohz_restart. We just kick the | ||
566 | * timer and do not touch the other magic bits which need to be done | ||
567 | * when idle is left. | ||
568 | */ | ||
569 | static void tick_nohz_kick_tick(int cpu) | ||
570 | { | ||
571 | #if 0 | ||
572 | /* Switch back to 2.6.27 behaviour */ | ||
573 | |||
574 | struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); | ||
575 | ktime_t delta, now; | ||
576 | |||
577 | if (!ts->tick_stopped) | ||
578 | return; | ||
579 | |||
580 | /* | ||
581 | * Do not touch the tick device, when the next expiry is either | ||
582 | * already reached or less/equal than the tick period. | ||
583 | */ | ||
584 | now = ktime_get(); | ||
585 | delta = ktime_sub(hrtimer_get_expires(&ts->sched_timer), now); | ||
586 | if (delta.tv64 <= tick_period.tv64) | ||
587 | return; | ||
588 | |||
589 | tick_nohz_restart(ts, now); | ||
590 | #endif | ||
591 | } | ||
592 | |||
546 | #else | 593 | #else |
547 | 594 | ||
548 | static inline void tick_nohz_switch_to_nohz(void) { } | 595 | static inline void tick_nohz_switch_to_nohz(void) { } |
@@ -550,6 +597,19 @@ static inline void tick_nohz_switch_to_nohz(void) { } | |||
550 | #endif /* NO_HZ */ | 597 | #endif /* NO_HZ */ |
551 | 598 | ||
552 | /* | 599 | /* |
600 | * Called from irq_enter to notify about the possible interruption of idle() | ||
601 | */ | ||
602 | void tick_check_idle(int cpu) | ||
603 | { | ||
604 | tick_check_oneshot_broadcast(cpu); | ||
605 | #ifdef CONFIG_NO_HZ | ||
606 | tick_nohz_stop_idle(cpu); | ||
607 | tick_nohz_update_jiffies(); | ||
608 | tick_nohz_kick_tick(cpu); | ||
609 | #endif | ||
610 | } | ||
611 | |||
612 | /* | ||
553 | * High resolution timer specific code | 613 | * High resolution timer specific code |
554 | */ | 614 | */ |
555 | #ifdef CONFIG_HIGH_RES_TIMERS | 615 | #ifdef CONFIG_HIGH_RES_TIMERS |
@@ -602,10 +662,6 @@ static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer) | |||
602 | profile_tick(CPU_PROFILING); | 662 | profile_tick(CPU_PROFILING); |
603 | } | 663 | } |
604 | 664 | ||
605 | /* Do not restart, when we are in the idle loop */ | ||
606 | if (ts->tick_stopped) | ||
607 | return HRTIMER_NORESTART; | ||
608 | |||
609 | hrtimer_forward(timer, now, tick_period); | 665 | hrtimer_forward(timer, now, tick_period); |
610 | 666 | ||
611 | return HRTIMER_RESTART; | 667 | return HRTIMER_RESTART; |
@@ -628,16 +684,15 @@ void tick_setup_sched_timer(void) | |||
628 | ts->sched_timer.cb_mode = HRTIMER_CB_IRQSAFE_PERCPU; | 684 | ts->sched_timer.cb_mode = HRTIMER_CB_IRQSAFE_PERCPU; |
629 | 685 | ||
630 | /* Get the next period (per cpu) */ | 686 | /* Get the next period (per cpu) */ |
631 | ts->sched_timer.expires = tick_init_jiffy_update(); | 687 | hrtimer_set_expires(&ts->sched_timer, tick_init_jiffy_update()); |
632 | offset = ktime_to_ns(tick_period) >> 1; | 688 | offset = ktime_to_ns(tick_period) >> 1; |
633 | do_div(offset, num_possible_cpus()); | 689 | do_div(offset, num_possible_cpus()); |
634 | offset *= smp_processor_id(); | 690 | offset *= smp_processor_id(); |
635 | ts->sched_timer.expires = ktime_add_ns(ts->sched_timer.expires, offset); | 691 | hrtimer_add_expires_ns(&ts->sched_timer, offset); |
636 | 692 | ||
637 | for (;;) { | 693 | for (;;) { |
638 | hrtimer_forward(&ts->sched_timer, now, tick_period); | 694 | hrtimer_forward(&ts->sched_timer, now, tick_period); |
639 | hrtimer_start(&ts->sched_timer, ts->sched_timer.expires, | 695 | hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS); |
640 | HRTIMER_MODE_ABS); | ||
641 | /* Check, if the timer was already in the past */ | 696 | /* Check, if the timer was already in the past */ |
642 | if (hrtimer_active(&ts->sched_timer)) | 697 | if (hrtimer_active(&ts->sched_timer)) |
643 | break; | 698 | break; |