diff options
Diffstat (limited to 'kernel/time/tick-sched.c')
-rw-r--r-- | kernel/time/tick-sched.c | 101 |
1 files changed, 72 insertions, 29 deletions
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index a547be11cf97..5bbb1044f847 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c | |||
@@ -155,7 +155,7 @@ void tick_nohz_update_jiffies(void) | |||
155 | touch_softlockup_watchdog(); | 155 | touch_softlockup_watchdog(); |
156 | } | 156 | } |
157 | 157 | ||
158 | void tick_nohz_stop_idle(int cpu) | 158 | static void tick_nohz_stop_idle(int cpu) |
159 | { | 159 | { |
160 | struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); | 160 | struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); |
161 | 161 | ||
@@ -377,6 +377,32 @@ ktime_t tick_nohz_get_sleep_length(void) | |||
377 | return ts->sleep_length; | 377 | return ts->sleep_length; |
378 | } | 378 | } |
379 | 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 | |||
380 | /** | 406 | /** |
381 | * 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 |
382 | * | 408 | * |
@@ -430,28 +456,9 @@ void tick_nohz_restart_sched_tick(void) | |||
430 | */ | 456 | */ |
431 | ts->tick_stopped = 0; | 457 | ts->tick_stopped = 0; |
432 | ts->idle_exittime = now; | 458 | ts->idle_exittime = now; |
433 | hrtimer_cancel(&ts->sched_timer); | ||
434 | hrtimer_set_expires(&ts->sched_timer, ts->idle_tick); | ||
435 | 459 | ||
436 | while (1) { | 460 | tick_nohz_restart(ts, now); |
437 | /* Forward the time to expire in the future */ | ||
438 | hrtimer_forward(&ts->sched_timer, now, tick_period); | ||
439 | 461 | ||
440 | if (ts->nohz_mode == NOHZ_MODE_HIGHRES) { | ||
441 | hrtimer_start_expires(&ts->sched_timer, | ||
442 | HRTIMER_MODE_ABS); | ||
443 | /* Check, if the timer was already in the past */ | ||
444 | if (hrtimer_active(&ts->sched_timer)) | ||
445 | break; | ||
446 | } else { | ||
447 | if (!tick_program_event( | ||
448 | hrtimer_get_expires(&ts->sched_timer), 0)) | ||
449 | break; | ||
450 | } | ||
451 | /* Update jiffies and reread time */ | ||
452 | tick_do_update_jiffies64(now); | ||
453 | now = ktime_get(); | ||
454 | } | ||
455 | local_irq_enable(); | 462 | local_irq_enable(); |
456 | } | 463 | } |
457 | 464 | ||
@@ -503,10 +510,6 @@ static void tick_nohz_handler(struct clock_event_device *dev) | |||
503 | update_process_times(user_mode(regs)); | 510 | update_process_times(user_mode(regs)); |
504 | profile_tick(CPU_PROFILING); | 511 | profile_tick(CPU_PROFILING); |
505 | 512 | ||
506 | /* Do not restart, when we are in the idle loop */ | ||
507 | if (ts->tick_stopped) | ||
508 | return; | ||
509 | |||
510 | while (tick_nohz_reprogram(ts, now)) { | 513 | while (tick_nohz_reprogram(ts, now)) { |
511 | now = ktime_get(); | 514 | now = ktime_get(); |
512 | tick_do_update_jiffies64(now); | 515 | tick_do_update_jiffies64(now); |
@@ -552,6 +555,37 @@ static void tick_nohz_switch_to_nohz(void) | |||
552 | smp_processor_id()); | 555 | smp_processor_id()); |
553 | } | 556 | } |
554 | 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 | struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); | ||
572 | ktime_t delta, now; | ||
573 | |||
574 | if (!ts->tick_stopped) | ||
575 | return; | ||
576 | |||
577 | /* | ||
578 | * Do not touch the tick device, when the next expiry is either | ||
579 | * already reached or less/equal than the tick period. | ||
580 | */ | ||
581 | now = ktime_get(); | ||
582 | delta = ktime_sub(hrtimer_get_expires(&ts->sched_timer), now); | ||
583 | if (delta.tv64 <= tick_period.tv64) | ||
584 | return; | ||
585 | |||
586 | tick_nohz_restart(ts, now); | ||
587 | } | ||
588 | |||
555 | #else | 589 | #else |
556 | 590 | ||
557 | static inline void tick_nohz_switch_to_nohz(void) { } | 591 | static inline void tick_nohz_switch_to_nohz(void) { } |
@@ -559,6 +593,19 @@ static inline void tick_nohz_switch_to_nohz(void) { } | |||
559 | #endif /* NO_HZ */ | 593 | #endif /* NO_HZ */ |
560 | 594 | ||
561 | /* | 595 | /* |
596 | * Called from irq_enter to notify about the possible interruption of idle() | ||
597 | */ | ||
598 | void tick_check_idle(int cpu) | ||
599 | { | ||
600 | tick_check_oneshot_broadcast(cpu); | ||
601 | #ifdef CONFIG_NO_HZ | ||
602 | tick_nohz_stop_idle(cpu); | ||
603 | tick_nohz_update_jiffies(); | ||
604 | tick_nohz_kick_tick(cpu); | ||
605 | #endif | ||
606 | } | ||
607 | |||
608 | /* | ||
562 | * High resolution timer specific code | 609 | * High resolution timer specific code |
563 | */ | 610 | */ |
564 | #ifdef CONFIG_HIGH_RES_TIMERS | 611 | #ifdef CONFIG_HIGH_RES_TIMERS |
@@ -611,10 +658,6 @@ static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer) | |||
611 | profile_tick(CPU_PROFILING); | 658 | profile_tick(CPU_PROFILING); |
612 | } | 659 | } |
613 | 660 | ||
614 | /* Do not restart, when we are in the idle loop */ | ||
615 | if (ts->tick_stopped) | ||
616 | return HRTIMER_NORESTART; | ||
617 | |||
618 | hrtimer_forward(timer, now, tick_period); | 661 | hrtimer_forward(timer, now, tick_period); |
619 | 662 | ||
620 | return HRTIMER_RESTART; | 663 | return HRTIMER_RESTART; |