diff options
-rw-r--r-- | include/linux/tick.h | 6 | ||||
-rw-r--r-- | kernel/softirq.c | 7 | ||||
-rw-r--r-- | kernel/time/tick-sched.c | 70 |
3 files changed, 60 insertions, 23 deletions
diff --git a/include/linux/tick.h b/include/linux/tick.h index f4a1395e05ff..0fadf95debe1 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h | |||
@@ -51,8 +51,10 @@ struct tick_sched { | |||
51 | unsigned long idle_jiffies; | 51 | unsigned long idle_jiffies; |
52 | unsigned long idle_calls; | 52 | unsigned long idle_calls; |
53 | unsigned long idle_sleeps; | 53 | unsigned long idle_sleeps; |
54 | int idle_active; | ||
54 | ktime_t idle_entrytime; | 55 | ktime_t idle_entrytime; |
55 | ktime_t idle_sleeptime; | 56 | ktime_t idle_sleeptime; |
57 | ktime_t idle_lastupdate; | ||
56 | ktime_t sleep_length; | 58 | ktime_t sleep_length; |
57 | unsigned long last_jiffies; | 59 | unsigned long last_jiffies; |
58 | unsigned long next_jiffies; | 60 | unsigned long next_jiffies; |
@@ -103,6 +105,8 @@ extern void tick_nohz_stop_sched_tick(void); | |||
103 | extern void tick_nohz_restart_sched_tick(void); | 105 | extern void tick_nohz_restart_sched_tick(void); |
104 | extern void tick_nohz_update_jiffies(void); | 106 | extern void tick_nohz_update_jiffies(void); |
105 | extern ktime_t tick_nohz_get_sleep_length(void); | 107 | extern ktime_t tick_nohz_get_sleep_length(void); |
108 | extern void tick_nohz_stop_idle(int cpu); | ||
109 | extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time); | ||
106 | # else | 110 | # else |
107 | static inline void tick_nohz_stop_sched_tick(void) { } | 111 | static inline void tick_nohz_stop_sched_tick(void) { } |
108 | static inline void tick_nohz_restart_sched_tick(void) { } | 112 | static inline void tick_nohz_restart_sched_tick(void) { } |
@@ -113,6 +117,8 @@ static inline ktime_t tick_nohz_get_sleep_length(void) | |||
113 | 117 | ||
114 | return len; | 118 | return len; |
115 | } | 119 | } |
120 | static inline void tick_nohz_stop_idle(int cpu) { } | ||
121 | static inline u64 get_cpu_idle_time_us(int cpu, u64 *unused) { return 0; } | ||
116 | # endif /* !NO_HZ */ | 122 | # endif /* !NO_HZ */ |
117 | 123 | ||
118 | #endif | 124 | #endif |
diff --git a/kernel/softirq.c b/kernel/softirq.c index 8fe1ff40102d..d7837d45419e 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c | |||
@@ -280,9 +280,14 @@ asmlinkage void do_softirq(void) | |||
280 | */ | 280 | */ |
281 | void irq_enter(void) | 281 | void irq_enter(void) |
282 | { | 282 | { |
283 | #ifdef CONFIG_NO_HZ | ||
284 | int cpu = smp_processor_id(); | ||
285 | if (idle_cpu(cpu) && !in_interrupt()) | ||
286 | tick_nohz_stop_idle(cpu); | ||
287 | #endif | ||
283 | __irq_enter(); | 288 | __irq_enter(); |
284 | #ifdef CONFIG_NO_HZ | 289 | #ifdef CONFIG_NO_HZ |
285 | if (idle_cpu(smp_processor_id())) | 290 | if (idle_cpu(cpu)) |
286 | tick_nohz_update_jiffies(); | 291 | tick_nohz_update_jiffies(); |
287 | #endif | 292 | #endif |
288 | } | 293 | } |
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 49e12f6a4bab..63f24b550695 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c | |||
@@ -143,6 +143,44 @@ void tick_nohz_update_jiffies(void) | |||
143 | local_irq_restore(flags); | 143 | local_irq_restore(flags); |
144 | } | 144 | } |
145 | 145 | ||
146 | void tick_nohz_stop_idle(int cpu) | ||
147 | { | ||
148 | struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); | ||
149 | |||
150 | if (ts->idle_active) { | ||
151 | ktime_t now, delta; | ||
152 | now = ktime_get(); | ||
153 | delta = ktime_sub(now, ts->idle_entrytime); | ||
154 | ts->idle_lastupdate = now; | ||
155 | ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta); | ||
156 | ts->idle_active = 0; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | static ktime_t tick_nohz_start_idle(int cpu) | ||
161 | { | ||
162 | struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); | ||
163 | ktime_t now, delta; | ||
164 | |||
165 | now = ktime_get(); | ||
166 | if (ts->idle_active) { | ||
167 | delta = ktime_sub(now, ts->idle_entrytime); | ||
168 | ts->idle_lastupdate = now; | ||
169 | ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta); | ||
170 | } | ||
171 | ts->idle_entrytime = now; | ||
172 | ts->idle_active = 1; | ||
173 | return now; | ||
174 | } | ||
175 | |||
176 | u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time) | ||
177 | { | ||
178 | struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); | ||
179 | |||
180 | *last_update_time = ktime_to_us(ts->idle_lastupdate); | ||
181 | return ktime_to_us(ts->idle_sleeptime); | ||
182 | } | ||
183 | |||
146 | /** | 184 | /** |
147 | * tick_nohz_stop_sched_tick - stop the idle tick from the idle task | 185 | * tick_nohz_stop_sched_tick - stop the idle tick from the idle task |
148 | * | 186 | * |
@@ -155,13 +193,14 @@ void tick_nohz_stop_sched_tick(void) | |||
155 | unsigned long seq, last_jiffies, next_jiffies, delta_jiffies, flags; | 193 | unsigned long seq, last_jiffies, next_jiffies, delta_jiffies, flags; |
156 | unsigned long rt_jiffies; | 194 | unsigned long rt_jiffies; |
157 | struct tick_sched *ts; | 195 | struct tick_sched *ts; |
158 | ktime_t last_update, expires, now, delta; | 196 | ktime_t last_update, expires, now; |
159 | struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev; | 197 | struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev; |
160 | int cpu; | 198 | int cpu; |
161 | 199 | ||
162 | local_irq_save(flags); | 200 | local_irq_save(flags); |
163 | 201 | ||
164 | cpu = smp_processor_id(); | 202 | cpu = smp_processor_id(); |
203 | now = tick_nohz_start_idle(cpu); | ||
165 | ts = &per_cpu(tick_cpu_sched, cpu); | 204 | ts = &per_cpu(tick_cpu_sched, cpu); |
166 | 205 | ||
167 | /* | 206 | /* |
@@ -193,19 +232,7 @@ void tick_nohz_stop_sched_tick(void) | |||
193 | } | 232 | } |
194 | } | 233 | } |
195 | 234 | ||
196 | now = ktime_get(); | ||
197 | /* | ||
198 | * When called from irq_exit we need to account the idle sleep time | ||
199 | * correctly. | ||
200 | */ | ||
201 | if (ts->tick_stopped) { | ||
202 | delta = ktime_sub(now, ts->idle_entrytime); | ||
203 | ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta); | ||
204 | } | ||
205 | |||
206 | ts->idle_entrytime = now; | ||
207 | ts->idle_calls++; | 235 | ts->idle_calls++; |
208 | |||
209 | /* Read jiffies and the time when jiffies were updated last */ | 236 | /* Read jiffies and the time when jiffies were updated last */ |
210 | do { | 237 | do { |
211 | seq = read_seqbegin(&xtime_lock); | 238 | seq = read_seqbegin(&xtime_lock); |
@@ -337,23 +364,22 @@ void tick_nohz_restart_sched_tick(void) | |||
337 | int cpu = smp_processor_id(); | 364 | int cpu = smp_processor_id(); |
338 | struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); | 365 | struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu); |
339 | unsigned long ticks; | 366 | unsigned long ticks; |
340 | ktime_t now, delta; | 367 | ktime_t now; |
341 | 368 | ||
342 | if (!ts->tick_stopped) | 369 | local_irq_disable(); |
370 | tick_nohz_stop_idle(cpu); | ||
371 | |||
372 | if (!ts->tick_stopped) { | ||
373 | local_irq_enable(); | ||
343 | return; | 374 | return; |
375 | } | ||
344 | 376 | ||
345 | /* Update jiffies first */ | 377 | /* Update jiffies first */ |
346 | now = ktime_get(); | ||
347 | |||
348 | local_irq_disable(); | ||
349 | select_nohz_load_balancer(0); | 378 | select_nohz_load_balancer(0); |
379 | now = ktime_get(); | ||
350 | tick_do_update_jiffies64(now); | 380 | tick_do_update_jiffies64(now); |
351 | cpu_clear(cpu, nohz_cpu_mask); | 381 | cpu_clear(cpu, nohz_cpu_mask); |
352 | 382 | ||
353 | /* Account the idle time */ | ||
354 | delta = ktime_sub(now, ts->idle_entrytime); | ||
355 | ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta); | ||
356 | |||
357 | /* | 383 | /* |
358 | * We stopped the tick in idle. Update process times would miss the | 384 | * We stopped the tick in idle. Update process times would miss the |
359 | * time we slept as update_process_times does only a 1 tick | 385 | * time we slept as update_process_times does only a 1 tick |