diff options
author | Ingo Molnar <mingo@kernel.org> | 2013-04-21 05:04:42 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2013-04-21 05:04:42 -0400 |
commit | 2727872dfe5d273f313f8a0c0dd0fcc58e96cde7 (patch) | |
tree | 069ceb12d981f33d997afd214c2082a6685a2fd0 | |
parent | 65d798f0f9339ae2c4ebe9480e3260b33382a584 (diff) | |
parent | f98823ac758ba1aa77c6e3f8ad4ef3ad84ee0a7c (diff) |
Merge branch 'timers/nohz-reviewed' of git://git.kernel.org/pub/scm/linux/kernel/git/frederic/linux-dynticks into timers/nohz
Pull full dynticks timekeeping and RCU improvements from Frederic Weisbecker.
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | Documentation/kernel-parameters.txt | 6 | ||||
-rw-r--r-- | include/linux/rcupdate.h | 7 | ||||
-rw-r--r-- | include/linux/tick.h | 2 | ||||
-rw-r--r-- | init/main.c | 1 | ||||
-rw-r--r-- | kernel/rcutree.c | 6 | ||||
-rw-r--r-- | kernel/rcutree.h | 1 | ||||
-rw-r--r-- | kernel/rcutree_plugin.h | 13 | ||||
-rw-r--r-- | kernel/time/Kconfig | 10 | ||||
-rw-r--r-- | kernel/time/tick-sched.c | 77 |
9 files changed, 72 insertions, 51 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 82365dde00a8..4865e9bfd08d 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
@@ -1916,8 +1916,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. | |||
1916 | nohz_full= [KNL,BOOT] | 1916 | nohz_full= [KNL,BOOT] |
1917 | In kernels built with CONFIG_NO_HZ_FULL=y, set | 1917 | In kernels built with CONFIG_NO_HZ_FULL=y, set |
1918 | the specified list of CPUs whose tick will be stopped | 1918 | the specified list of CPUs whose tick will be stopped |
1919 | whenever possible. You need to keep at least one online | 1919 | whenever possible. The boot CPU will be forced outside |
1920 | CPU outside the range to maintain the timekeeping. | 1920 | the range to maintain the timekeeping. |
1921 | The CPUs in this range must also be included in the | ||
1922 | rcu_nocbs= set. | ||
1921 | 1923 | ||
1922 | noiotrap [SH] Disables trapped I/O port accesses. | 1924 | noiotrap [SH] Disables trapped I/O port accesses. |
1923 | 1925 | ||
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index b758ce17b309..8e0948c872fc 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h | |||
@@ -999,4 +999,11 @@ static inline notrace void rcu_read_unlock_sched_notrace(void) | |||
999 | #define kfree_rcu(ptr, rcu_head) \ | 999 | #define kfree_rcu(ptr, rcu_head) \ |
1000 | __kfree_rcu(&((ptr)->rcu_head), offsetof(typeof(*(ptr)), rcu_head)) | 1000 | __kfree_rcu(&((ptr)->rcu_head), offsetof(typeof(*(ptr)), rcu_head)) |
1001 | 1001 | ||
1002 | #ifdef CONFIG_RCU_NOCB_CPU | ||
1003 | extern bool rcu_is_nocb_cpu(int cpu); | ||
1004 | #else | ||
1005 | static inline bool rcu_is_nocb_cpu(int cpu) { return false; } | ||
1006 | #endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */ | ||
1007 | |||
1008 | |||
1002 | #endif /* __LINUX_RCUPDATE_H */ | 1009 | #endif /* __LINUX_RCUPDATE_H */ |
diff --git a/include/linux/tick.h b/include/linux/tick.h index b4e3b0c9639e..0b6873cbf512 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h | |||
@@ -158,8 +158,10 @@ static inline u64 get_cpu_iowait_time_us(int cpu, u64 *unused) { return -1; } | |||
158 | # endif /* !CONFIG_NO_HZ_COMMON */ | 158 | # endif /* !CONFIG_NO_HZ_COMMON */ |
159 | 159 | ||
160 | #ifdef CONFIG_NO_HZ_FULL | 160 | #ifdef CONFIG_NO_HZ_FULL |
161 | extern void tick_nohz_init(void); | ||
161 | extern int tick_nohz_full_cpu(int cpu); | 162 | extern int tick_nohz_full_cpu(int cpu); |
162 | #else | 163 | #else |
164 | static inline void tick_nohz_init(void) { } | ||
163 | static inline int tick_nohz_full_cpu(int cpu) { return 0; } | 165 | static inline int tick_nohz_full_cpu(int cpu) { return 0; } |
164 | #endif | 166 | #endif |
165 | 167 | ||
diff --git a/init/main.c b/init/main.c index 63534a141b4e..2acb5bbde99b 100644 --- a/init/main.c +++ b/init/main.c | |||
@@ -547,6 +547,7 @@ asmlinkage void __init start_kernel(void) | |||
547 | idr_init_cache(); | 547 | idr_init_cache(); |
548 | perf_event_init(); | 548 | perf_event_init(); |
549 | rcu_init(); | 549 | rcu_init(); |
550 | tick_nohz_init(); | ||
550 | radix_tree_init(); | 551 | radix_tree_init(); |
551 | /* init some links before init_ISA_irqs() */ | 552 | /* init some links before init_ISA_irqs() */ |
552 | early_irq_init(); | 553 | early_irq_init(); |
diff --git a/kernel/rcutree.c b/kernel/rcutree.c index f5ab50235cba..1d4ceff793a4 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c | |||
@@ -1695,7 +1695,7 @@ rcu_send_cbs_to_orphanage(int cpu, struct rcu_state *rsp, | |||
1695 | struct rcu_node *rnp, struct rcu_data *rdp) | 1695 | struct rcu_node *rnp, struct rcu_data *rdp) |
1696 | { | 1696 | { |
1697 | /* No-CBs CPUs do not have orphanable callbacks. */ | 1697 | /* No-CBs CPUs do not have orphanable callbacks. */ |
1698 | if (is_nocb_cpu(rdp->cpu)) | 1698 | if (rcu_is_nocb_cpu(rdp->cpu)) |
1699 | return; | 1699 | return; |
1700 | 1700 | ||
1701 | /* | 1701 | /* |
@@ -2757,10 +2757,10 @@ static void _rcu_barrier(struct rcu_state *rsp) | |||
2757 | * corresponding CPU's preceding callbacks have been invoked. | 2757 | * corresponding CPU's preceding callbacks have been invoked. |
2758 | */ | 2758 | */ |
2759 | for_each_possible_cpu(cpu) { | 2759 | for_each_possible_cpu(cpu) { |
2760 | if (!cpu_online(cpu) && !is_nocb_cpu(cpu)) | 2760 | if (!cpu_online(cpu) && !rcu_is_nocb_cpu(cpu)) |
2761 | continue; | 2761 | continue; |
2762 | rdp = per_cpu_ptr(rsp->rda, cpu); | 2762 | rdp = per_cpu_ptr(rsp->rda, cpu); |
2763 | if (is_nocb_cpu(cpu)) { | 2763 | if (rcu_is_nocb_cpu(cpu)) { |
2764 | _rcu_barrier_trace(rsp, "OnlineNoCB", cpu, | 2764 | _rcu_barrier_trace(rsp, "OnlineNoCB", cpu, |
2765 | rsp->n_barrier_done); | 2765 | rsp->n_barrier_done); |
2766 | atomic_inc(&rsp->barrier_cpu_count); | 2766 | atomic_inc(&rsp->barrier_cpu_count); |
diff --git a/kernel/rcutree.h b/kernel/rcutree.h index f993c0ac47db..38acc49da2c6 100644 --- a/kernel/rcutree.h +++ b/kernel/rcutree.h | |||
@@ -529,7 +529,6 @@ static void print_cpu_stall_info(struct rcu_state *rsp, int cpu); | |||
529 | static void print_cpu_stall_info_end(void); | 529 | static void print_cpu_stall_info_end(void); |
530 | static void zero_cpu_stall_ticks(struct rcu_data *rdp); | 530 | static void zero_cpu_stall_ticks(struct rcu_data *rdp); |
531 | static void increment_cpu_stall_ticks(void); | 531 | static void increment_cpu_stall_ticks(void); |
532 | static bool is_nocb_cpu(int cpu); | ||
533 | static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp, | 532 | static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp, |
534 | bool lazy); | 533 | bool lazy); |
535 | static bool rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp, | 534 | static bool rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp, |
diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index a5745e9b5d5a..0cd91cc18db4 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h | |||
@@ -2167,7 +2167,7 @@ static int __init parse_rcu_nocb_poll(char *arg) | |||
2167 | early_param("rcu_nocb_poll", parse_rcu_nocb_poll); | 2167 | early_param("rcu_nocb_poll", parse_rcu_nocb_poll); |
2168 | 2168 | ||
2169 | /* Is the specified CPU a no-CPUs CPU? */ | 2169 | /* Is the specified CPU a no-CPUs CPU? */ |
2170 | static bool is_nocb_cpu(int cpu) | 2170 | bool rcu_is_nocb_cpu(int cpu) |
2171 | { | 2171 | { |
2172 | if (have_rcu_nocb_mask) | 2172 | if (have_rcu_nocb_mask) |
2173 | return cpumask_test_cpu(cpu, rcu_nocb_mask); | 2173 | return cpumask_test_cpu(cpu, rcu_nocb_mask); |
@@ -2225,7 +2225,7 @@ static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp, | |||
2225 | bool lazy) | 2225 | bool lazy) |
2226 | { | 2226 | { |
2227 | 2227 | ||
2228 | if (!is_nocb_cpu(rdp->cpu)) | 2228 | if (!rcu_is_nocb_cpu(rdp->cpu)) |
2229 | return 0; | 2229 | return 0; |
2230 | __call_rcu_nocb_enqueue(rdp, rhp, &rhp->next, 1, lazy); | 2230 | __call_rcu_nocb_enqueue(rdp, rhp, &rhp->next, 1, lazy); |
2231 | return 1; | 2231 | return 1; |
@@ -2242,7 +2242,7 @@ static bool __maybe_unused rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp, | |||
2242 | long qll = rsp->qlen_lazy; | 2242 | long qll = rsp->qlen_lazy; |
2243 | 2243 | ||
2244 | /* If this is not a no-CBs CPU, tell the caller to do it the old way. */ | 2244 | /* If this is not a no-CBs CPU, tell the caller to do it the old way. */ |
2245 | if (!is_nocb_cpu(smp_processor_id())) | 2245 | if (!rcu_is_nocb_cpu(smp_processor_id())) |
2246 | return 0; | 2246 | return 0; |
2247 | rsp->qlen = 0; | 2247 | rsp->qlen = 0; |
2248 | rsp->qlen_lazy = 0; | 2248 | rsp->qlen_lazy = 0; |
@@ -2282,7 +2282,7 @@ static bool nocb_cpu_expendable(int cpu) | |||
2282 | * If there are no no-CB CPUs or if this CPU is not a no-CB CPU, | 2282 | * If there are no no-CB CPUs or if this CPU is not a no-CB CPU, |
2283 | * then offlining this CPU is harmless. Let it happen. | 2283 | * then offlining this CPU is harmless. Let it happen. |
2284 | */ | 2284 | */ |
2285 | if (!have_rcu_nocb_mask || is_nocb_cpu(cpu)) | 2285 | if (!have_rcu_nocb_mask || rcu_is_nocb_cpu(cpu)) |
2286 | return 1; | 2286 | return 1; |
2287 | 2287 | ||
2288 | /* If no memory, play it safe and keep the CPU around. */ | 2288 | /* If no memory, play it safe and keep the CPU around. */ |
@@ -2464,11 +2464,6 @@ static void __init rcu_init_nocb(void) | |||
2464 | 2464 | ||
2465 | #else /* #ifdef CONFIG_RCU_NOCB_CPU */ | 2465 | #else /* #ifdef CONFIG_RCU_NOCB_CPU */ |
2466 | 2466 | ||
2467 | static bool is_nocb_cpu(int cpu) | ||
2468 | { | ||
2469 | return false; | ||
2470 | } | ||
2471 | |||
2472 | static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp, | 2467 | static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp, |
2473 | bool lazy) | 2468 | bool lazy) |
2474 | { | 2469 | { |
diff --git a/kernel/time/Kconfig b/kernel/time/Kconfig index 358d601a4fec..99c3f13dd478 100644 --- a/kernel/time/Kconfig +++ b/kernel/time/Kconfig | |||
@@ -128,6 +128,16 @@ config NO_HZ_FULL | |||
128 | 128 | ||
129 | endchoice | 129 | endchoice |
130 | 130 | ||
131 | config NO_HZ_FULL_ALL | ||
132 | bool "Full dynticks system on all CPUs by default" | ||
133 | depends on NO_HZ_FULL | ||
134 | help | ||
135 | If the user doesn't pass the nohz_full boot option to | ||
136 | define the range of full dynticks CPUs, consider that all | ||
137 | CPUs in the system are full dynticks by default. | ||
138 | Note the boot CPU will still be kept outside the range to | ||
139 | handle the timekeeping duty. | ||
140 | |||
131 | config NO_HZ | 141 | config NO_HZ |
132 | bool "Old Idle dynticks config" | 142 | bool "Old Idle dynticks config" |
133 | depends on !ARCH_USES_GETTIMEOFFSET && GENERIC_CLOCKEVENTS | 143 | depends on !ARCH_USES_GETTIMEOFFSET && GENERIC_CLOCKEVENTS |
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 369b5769fc97..a76e09044f9f 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c | |||
@@ -158,11 +158,21 @@ int tick_nohz_full_cpu(int cpu) | |||
158 | /* Parse the boot-time nohz CPU list from the kernel parameters. */ | 158 | /* Parse the boot-time nohz CPU list from the kernel parameters. */ |
159 | static int __init tick_nohz_full_setup(char *str) | 159 | static int __init tick_nohz_full_setup(char *str) |
160 | { | 160 | { |
161 | int cpu; | ||
162 | |||
161 | alloc_bootmem_cpumask_var(&nohz_full_mask); | 163 | alloc_bootmem_cpumask_var(&nohz_full_mask); |
162 | if (cpulist_parse(str, nohz_full_mask) < 0) | 164 | if (cpulist_parse(str, nohz_full_mask) < 0) { |
163 | pr_warning("NOHZ: Incorrect nohz_full cpumask\n"); | 165 | pr_warning("NOHZ: Incorrect nohz_full cpumask\n"); |
164 | else | 166 | return 1; |
165 | have_nohz_full_mask = true; | 167 | } |
168 | |||
169 | cpu = smp_processor_id(); | ||
170 | if (cpumask_test_cpu(cpu, nohz_full_mask)) { | ||
171 | pr_warning("NO_HZ: Clearing %d from nohz_full range for timekeeping\n", cpu); | ||
172 | cpumask_clear_cpu(cpu, nohz_full_mask); | ||
173 | } | ||
174 | have_nohz_full_mask = true; | ||
175 | |||
166 | return 1; | 176 | return 1; |
167 | } | 177 | } |
168 | __setup("nohz_full=", tick_nohz_full_setup); | 178 | __setup("nohz_full=", tick_nohz_full_setup); |
@@ -193,51 +203,46 @@ static int __cpuinit tick_nohz_cpu_down_callback(struct notifier_block *nfb, | |||
193 | */ | 203 | */ |
194 | static char __initdata nohz_full_buf[NR_CPUS + 1]; | 204 | static char __initdata nohz_full_buf[NR_CPUS + 1]; |
195 | 205 | ||
196 | static int __init init_tick_nohz_full(void) | 206 | static int tick_nohz_init_all(void) |
197 | { | 207 | { |
198 | cpumask_var_t online_nohz; | 208 | int err = -1; |
199 | int cpu; | ||
200 | 209 | ||
201 | if (!have_nohz_full_mask) | 210 | #ifdef CONFIG_NO_HZ_FULL_ALL |
202 | return 0; | 211 | if (!alloc_cpumask_var(&nohz_full_mask, GFP_KERNEL)) { |
212 | pr_err("NO_HZ: Can't allocate full dynticks cpumask\n"); | ||
213 | return err; | ||
214 | } | ||
215 | err = 0; | ||
216 | cpumask_setall(nohz_full_mask); | ||
217 | cpumask_clear_cpu(smp_processor_id(), nohz_full_mask); | ||
218 | have_nohz_full_mask = true; | ||
219 | #endif | ||
220 | return err; | ||
221 | } | ||
203 | 222 | ||
204 | cpu_notifier(tick_nohz_cpu_down_callback, 0); | 223 | void __init tick_nohz_init(void) |
224 | { | ||
225 | int cpu; | ||
205 | 226 | ||
206 | if (!zalloc_cpumask_var(&online_nohz, GFP_KERNEL)) { | 227 | if (!have_nohz_full_mask) { |
207 | pr_warning("NO_HZ: Not enough memory to check full nohz mask\n"); | 228 | if (tick_nohz_init_all() < 0) |
208 | return -ENOMEM; | 229 | return; |
209 | } | 230 | } |
210 | 231 | ||
211 | /* | 232 | cpu_notifier(tick_nohz_cpu_down_callback, 0); |
212 | * CPUs can probably not be concurrently offlined on initcall time. | ||
213 | * But we are paranoid, aren't we? | ||
214 | */ | ||
215 | get_online_cpus(); | ||
216 | 233 | ||
217 | /* Ensure we keep a CPU outside the dynticks range for timekeeping */ | 234 | /* Make sure full dynticks CPU are also RCU nocbs */ |
218 | cpumask_and(online_nohz, cpu_online_mask, nohz_full_mask); | 235 | for_each_cpu(cpu, nohz_full_mask) { |
219 | if (cpumask_equal(online_nohz, cpu_online_mask)) { | 236 | if (!rcu_is_nocb_cpu(cpu)) { |
220 | pr_warning("NO_HZ: Must keep at least one online CPU " | 237 | pr_warning("NO_HZ: CPU %d is not RCU nocb: " |
221 | "out of nohz_full range\n"); | 238 | "cleared from nohz_full range", cpu); |
222 | /* | 239 | cpumask_clear_cpu(cpu, nohz_full_mask); |
223 | * We know the current CPU doesn't have its tick stopped. | 240 | } |
224 | * Let's use it for the timekeeping duty. | ||
225 | */ | ||
226 | preempt_disable(); | ||
227 | cpu = smp_processor_id(); | ||
228 | pr_warning("NO_HZ: Clearing %d from nohz_full range\n", cpu); | ||
229 | cpumask_clear_cpu(cpu, nohz_full_mask); | ||
230 | preempt_enable(); | ||
231 | } | 241 | } |
232 | put_online_cpus(); | ||
233 | free_cpumask_var(online_nohz); | ||
234 | 242 | ||
235 | cpulist_scnprintf(nohz_full_buf, sizeof(nohz_full_buf), nohz_full_mask); | 243 | cpulist_scnprintf(nohz_full_buf, sizeof(nohz_full_buf), nohz_full_mask); |
236 | pr_info("NO_HZ: Full dynticks CPUs: %s.\n", nohz_full_buf); | 244 | pr_info("NO_HZ: Full dynticks CPUs: %s.\n", nohz_full_buf); |
237 | |||
238 | return 0; | ||
239 | } | 245 | } |
240 | core_initcall(init_tick_nohz_full); | ||
241 | #else | 246 | #else |
242 | #define have_nohz_full_mask (0) | 247 | #define have_nohz_full_mask (0) |
243 | #endif | 248 | #endif |