aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/time/tick-broadcast.c3
-rw-r--r--kernel/time/tick-common.c5
-rw-r--r--kernel/time/tick-sched.c47
3 files changed, 51 insertions, 4 deletions
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index 2fb8cb88df8d..8a6875cc1879 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -573,7 +573,8 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc)
573 bc->event_handler = tick_handle_oneshot_broadcast; 573 bc->event_handler = tick_handle_oneshot_broadcast;
574 574
575 /* Take the do_timer update */ 575 /* Take the do_timer update */
576 tick_do_timer_cpu = cpu; 576 if (!tick_nohz_extended_cpu(cpu))
577 tick_do_timer_cpu = cpu;
577 578
578 /* 579 /*
579 * We must be careful here. There might be other CPUs 580 * We must be careful here. There might be other CPUs
diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c
index b1600a6973f4..b7dc0cbdb59b 100644
--- a/kernel/time/tick-common.c
+++ b/kernel/time/tick-common.c
@@ -163,7 +163,10 @@ static void tick_setup_device(struct tick_device *td,
163 * this cpu: 163 * this cpu:
164 */ 164 */
165 if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) { 165 if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) {
166 tick_do_timer_cpu = cpu; 166 if (!tick_nohz_extended_cpu(cpu))
167 tick_do_timer_cpu = cpu;
168 else
169 tick_do_timer_cpu = TICK_DO_TIMER_NONE;
167 tick_next_period = ktime_get(); 170 tick_next_period = ktime_get();
168 tick_period = ktime_set(0, NSEC_PER_SEC / HZ); 171 tick_period = ktime_set(0, NSEC_PER_SEC / HZ);
169 } 172 }
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 79c275f08b7d..57bb3fe5aaa3 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -112,7 +112,8 @@ static void tick_sched_do_timer(ktime_t now)
112 * this duty, then the jiffies update is still serialized by 112 * this duty, then the jiffies update is still serialized by
113 * jiffies_lock. 113 * jiffies_lock.
114 */ 114 */
115 if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE)) 115 if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE)
116 && !tick_nohz_extended_cpu(cpu))
116 tick_do_timer_cpu = cpu; 117 tick_do_timer_cpu = cpu;
117#endif 118#endif
118 119
@@ -166,6 +167,25 @@ static int __init tick_nohz_extended_setup(char *str)
166} 167}
167__setup("nohz_extended=", tick_nohz_extended_setup); 168__setup("nohz_extended=", tick_nohz_extended_setup);
168 169
170static int __cpuinit tick_nohz_cpu_down_callback(struct notifier_block *nfb,
171 unsigned long action,
172 void *hcpu)
173{
174 unsigned int cpu = (unsigned long)hcpu;
175
176 switch (action & ~CPU_TASKS_FROZEN) {
177 case CPU_DOWN_PREPARE:
178 /*
179 * If we handle the timekeeping duty for full dynticks CPUs,
180 * we can't safely shutdown that CPU.
181 */
182 if (have_nohz_extended_mask && tick_do_timer_cpu == cpu)
183 return -EINVAL;
184 break;
185 }
186 return NOTIFY_OK;
187}
188
169static int __init init_tick_nohz_extended(void) 189static int __init init_tick_nohz_extended(void)
170{ 190{
171 cpumask_var_t online_nohz; 191 cpumask_var_t online_nohz;
@@ -174,6 +194,8 @@ static int __init init_tick_nohz_extended(void)
174 if (!have_nohz_extended_mask) 194 if (!have_nohz_extended_mask)
175 return 0; 195 return 0;
176 196
197 cpu_notifier(tick_nohz_cpu_down_callback, 0);
198
177 if (!zalloc_cpumask_var(&online_nohz, GFP_KERNEL)) { 199 if (!zalloc_cpumask_var(&online_nohz, GFP_KERNEL)) {
178 pr_warning("NO_HZ: Not enough memory to check extended nohz mask\n"); 200 pr_warning("NO_HZ: Not enough memory to check extended nohz mask\n");
179 return -ENOMEM; 201 return -ENOMEM;
@@ -188,11 +210,17 @@ static int __init init_tick_nohz_extended(void)
188 /* Ensure we keep a CPU outside the dynticks range for timekeeping */ 210 /* Ensure we keep a CPU outside the dynticks range for timekeeping */
189 cpumask_and(online_nohz, cpu_online_mask, nohz_extended_mask); 211 cpumask_and(online_nohz, cpu_online_mask, nohz_extended_mask);
190 if (cpumask_equal(online_nohz, cpu_online_mask)) { 212 if (cpumask_equal(online_nohz, cpu_online_mask)) {
191 cpu = cpumask_any(cpu_online_mask);
192 pr_warning("NO_HZ: Must keep at least one online CPU " 213 pr_warning("NO_HZ: Must keep at least one online CPU "
193 "out of nohz_extended range\n"); 214 "out of nohz_extended range\n");
215 /*
216 * We know the current CPU doesn't have its tick stopped.
217 * Let's use it for the timekeeping duty.
218 */
219 preempt_disable();
220 cpu = smp_processor_id();
194 pr_warning("NO_HZ: Clearing %d from nohz_extended range\n", cpu); 221 pr_warning("NO_HZ: Clearing %d from nohz_extended range\n", cpu);
195 cpumask_clear_cpu(cpu, nohz_extended_mask); 222 cpumask_clear_cpu(cpu, nohz_extended_mask);
223 preempt_enable();
196 } 224 }
197 put_online_cpus(); 225 put_online_cpus();
198 free_cpumask_var(online_nohz); 226 free_cpumask_var(online_nohz);
@@ -551,6 +579,21 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts)
551 return false; 579 return false;
552 } 580 }
553 581
582 if (have_nohz_extended_mask) {
583 /*
584 * Keep the tick alive to guarantee timekeeping progression
585 * if there are full dynticks CPUs around
586 */
587 if (tick_do_timer_cpu == cpu)
588 return false;
589 /*
590 * Boot safety: make sure the timekeeping duty has been
591 * assigned before entering dyntick-idle mode,
592 */
593 if (tick_do_timer_cpu == TICK_DO_TIMER_NONE)
594 return false;
595 }
596
554 return true; 597 return true;
555} 598}
556 599