diff options
-rw-r--r-- | include/linux/tick.h | 2 | ||||
-rw-r--r-- | kernel/sched/idle.c | 11 | ||||
-rw-r--r-- | kernel/time/tick-sched.c | 61 | ||||
-rw-r--r-- | kernel/time/tick-sched.h | 2 |
4 files changed, 59 insertions, 17 deletions
diff --git a/include/linux/tick.h b/include/linux/tick.h index ef0717e5e526..e8e7ff16b929 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h | |||
@@ -116,6 +116,7 @@ extern bool tick_nohz_enabled; | |||
116 | extern bool tick_nohz_tick_stopped(void); | 116 | extern bool tick_nohz_tick_stopped(void); |
117 | extern bool tick_nohz_tick_stopped_cpu(int cpu); | 117 | extern bool tick_nohz_tick_stopped_cpu(int cpu); |
118 | extern void tick_nohz_idle_stop_tick(void); | 118 | extern void tick_nohz_idle_stop_tick(void); |
119 | extern void tick_nohz_idle_retain_tick(void); | ||
119 | extern void tick_nohz_idle_restart_tick(void); | 120 | extern void tick_nohz_idle_restart_tick(void); |
120 | extern void tick_nohz_idle_enter(void); | 121 | extern void tick_nohz_idle_enter(void); |
121 | extern void tick_nohz_idle_exit(void); | 122 | extern void tick_nohz_idle_exit(void); |
@@ -139,6 +140,7 @@ static inline void tick_nohz_idle_stop_tick_protected(void) | |||
139 | static inline int tick_nohz_tick_stopped(void) { return 0; } | 140 | static inline int tick_nohz_tick_stopped(void) { return 0; } |
140 | static inline int tick_nohz_tick_stopped_cpu(int cpu) { return 0; } | 141 | static inline int tick_nohz_tick_stopped_cpu(int cpu) { return 0; } |
141 | static inline void tick_nohz_idle_stop_tick(void) { } | 142 | static inline void tick_nohz_idle_stop_tick(void) { } |
143 | static inline void tick_nohz_idle_retain_tick(void) { } | ||
142 | static inline void tick_nohz_idle_restart_tick(void) { } | 144 | static inline void tick_nohz_idle_restart_tick(void) { } |
143 | static inline void tick_nohz_idle_enter(void) { } | 145 | static inline void tick_nohz_idle_enter(void) { } |
144 | static inline void tick_nohz_idle_exit(void) { } | 146 | static inline void tick_nohz_idle_exit(void) { } |
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index a966bd2a6fa0..1a3e9bddd17b 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c | |||
@@ -185,13 +185,18 @@ static void cpuidle_idle_call(void) | |||
185 | } else { | 185 | } else { |
186 | bool stop_tick = true; | 186 | bool stop_tick = true; |
187 | 187 | ||
188 | tick_nohz_idle_stop_tick(); | ||
189 | rcu_idle_enter(); | ||
190 | |||
191 | /* | 188 | /* |
192 | * Ask the cpuidle framework to choose a convenient idle state. | 189 | * Ask the cpuidle framework to choose a convenient idle state. |
193 | */ | 190 | */ |
194 | next_state = cpuidle_select(drv, dev, &stop_tick); | 191 | next_state = cpuidle_select(drv, dev, &stop_tick); |
192 | |||
193 | if (stop_tick) | ||
194 | tick_nohz_idle_stop_tick(); | ||
195 | else | ||
196 | tick_nohz_idle_retain_tick(); | ||
197 | |||
198 | rcu_idle_enter(); | ||
199 | |||
195 | entered_state = call_cpuidle(drv, dev, next_state); | 200 | entered_state = call_cpuidle(drv, dev, next_state); |
196 | /* | 201 | /* |
197 | * Give the governor an opportunity to reflect on the outcome | 202 | * Give the governor an opportunity to reflect on the outcome |
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index f56d2c695712..c57c98c7e953 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c | |||
@@ -913,16 +913,19 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts) | |||
913 | 913 | ||
914 | static void __tick_nohz_idle_stop_tick(struct tick_sched *ts) | 914 | static void __tick_nohz_idle_stop_tick(struct tick_sched *ts) |
915 | { | 915 | { |
916 | struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev); | ||
917 | ktime_t expires; | 916 | ktime_t expires; |
918 | int cpu = smp_processor_id(); | 917 | int cpu = smp_processor_id(); |
919 | 918 | ||
920 | WARN_ON_ONCE(ts->timer_expires_base); | 919 | /* |
921 | 920 | * If tick_nohz_get_sleep_length() ran tick_nohz_next_event(), the | |
922 | if (!can_stop_idle_tick(cpu, ts)) | 921 | * tick timer expiration time is known already. |
923 | goto out; | 922 | */ |
924 | 923 | if (ts->timer_expires_base) | |
925 | expires = tick_nohz_next_event(ts, cpu); | 924 | expires = ts->timer_expires; |
925 | else if (can_stop_idle_tick(cpu, ts)) | ||
926 | expires = tick_nohz_next_event(ts, cpu); | ||
927 | else | ||
928 | return; | ||
926 | 929 | ||
927 | ts->idle_calls++; | 930 | ts->idle_calls++; |
928 | 931 | ||
@@ -941,9 +944,6 @@ static void __tick_nohz_idle_stop_tick(struct tick_sched *ts) | |||
941 | } else { | 944 | } else { |
942 | tick_nohz_retain_tick(ts); | 945 | tick_nohz_retain_tick(ts); |
943 | } | 946 | } |
944 | |||
945 | out: | ||
946 | ts->sleep_length = ktime_sub(dev->next_event, ts->idle_entrytime); | ||
947 | } | 947 | } |
948 | 948 | ||
949 | /** | 949 | /** |
@@ -956,6 +956,16 @@ void tick_nohz_idle_stop_tick(void) | |||
956 | __tick_nohz_idle_stop_tick(this_cpu_ptr(&tick_cpu_sched)); | 956 | __tick_nohz_idle_stop_tick(this_cpu_ptr(&tick_cpu_sched)); |
957 | } | 957 | } |
958 | 958 | ||
959 | void tick_nohz_idle_retain_tick(void) | ||
960 | { | ||
961 | tick_nohz_retain_tick(this_cpu_ptr(&tick_cpu_sched)); | ||
962 | /* | ||
963 | * Undo the effect of get_next_timer_interrupt() called from | ||
964 | * tick_nohz_next_event(). | ||
965 | */ | ||
966 | timer_clear_idle(); | ||
967 | } | ||
968 | |||
959 | /** | 969 | /** |
960 | * tick_nohz_idle_enter - prepare for entering idle on the current CPU | 970 | * tick_nohz_idle_enter - prepare for entering idle on the current CPU |
961 | * | 971 | * |
@@ -1012,15 +1022,42 @@ bool tick_nohz_idle_got_tick(void) | |||
1012 | } | 1022 | } |
1013 | 1023 | ||
1014 | /** | 1024 | /** |
1015 | * tick_nohz_get_sleep_length - return the length of the current sleep | 1025 | * tick_nohz_get_sleep_length - return the expected length of the current sleep |
1016 | * | 1026 | * |
1017 | * Called from power state control code with interrupts disabled | 1027 | * Called from power state control code with interrupts disabled |
1018 | */ | 1028 | */ |
1019 | ktime_t tick_nohz_get_sleep_length(void) | 1029 | ktime_t tick_nohz_get_sleep_length(void) |
1020 | { | 1030 | { |
1031 | struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev); | ||
1021 | struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched); | 1032 | struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched); |
1033 | int cpu = smp_processor_id(); | ||
1034 | /* | ||
1035 | * The idle entry time is expected to be a sufficient approximation of | ||
1036 | * the current time at this point. | ||
1037 | */ | ||
1038 | ktime_t now = ts->idle_entrytime; | ||
1039 | ktime_t next_event; | ||
1040 | |||
1041 | WARN_ON_ONCE(!ts->inidle); | ||
1042 | |||
1043 | if (!can_stop_idle_tick(cpu, ts)) | ||
1044 | goto out_dev; | ||
1045 | |||
1046 | next_event = tick_nohz_next_event(ts, cpu); | ||
1047 | if (!next_event) | ||
1048 | goto out_dev; | ||
1049 | |||
1050 | /* | ||
1051 | * If the next highres timer to expire is earlier than next_event, the | ||
1052 | * idle governor needs to know that. | ||
1053 | */ | ||
1054 | next_event = min_t(u64, next_event, | ||
1055 | hrtimer_next_event_without(&ts->sched_timer)); | ||
1056 | |||
1057 | return ktime_sub(next_event, now); | ||
1022 | 1058 | ||
1023 | return ts->sleep_length; | 1059 | out_dev: |
1060 | return ktime_sub(dev->next_event, now); | ||
1024 | } | 1061 | } |
1025 | 1062 | ||
1026 | /** | 1063 | /** |
diff --git a/kernel/time/tick-sched.h b/kernel/time/tick-sched.h index 53e45a39bdbc..2b845f2c44b1 100644 --- a/kernel/time/tick-sched.h +++ b/kernel/time/tick-sched.h | |||
@@ -38,7 +38,6 @@ enum tick_nohz_mode { | |||
38 | * @idle_exittime: Time when the idle state was left | 38 | * @idle_exittime: Time when the idle state was left |
39 | * @idle_sleeptime: Sum of the time slept in idle with sched tick stopped | 39 | * @idle_sleeptime: Sum of the time slept in idle with sched tick stopped |
40 | * @iowait_sleeptime: Sum of the time slept in idle with sched tick stopped, with IO outstanding | 40 | * @iowait_sleeptime: Sum of the time slept in idle with sched tick stopped, with IO outstanding |
41 | * @sleep_length: Duration of the current idle sleep | ||
42 | * @timer_expires: Anticipated timer expiration time (in case sched tick is stopped) | 41 | * @timer_expires: Anticipated timer expiration time (in case sched tick is stopped) |
43 | * @timer_expires_base: Base time clock monotonic for @timer_expires | 42 | * @timer_expires_base: Base time clock monotonic for @timer_expires |
44 | * @do_timer_lst: CPU was the last one doing do_timer before going idle | 43 | * @do_timer_lst: CPU was the last one doing do_timer before going idle |
@@ -60,7 +59,6 @@ struct tick_sched { | |||
60 | ktime_t idle_exittime; | 59 | ktime_t idle_exittime; |
61 | ktime_t idle_sleeptime; | 60 | ktime_t idle_sleeptime; |
62 | ktime_t iowait_sleeptime; | 61 | ktime_t iowait_sleeptime; |
63 | ktime_t sleep_length; | ||
64 | unsigned long last_jiffies; | 62 | unsigned long last_jiffies; |
65 | u64 timer_expires; | 63 | u64 timer_expires; |
66 | u64 timer_expires_base; | 64 | u64 timer_expires_base; |