diff options
author | Peter Zijlstra <peterz@infradead.org> | 2018-01-22 16:53:28 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2018-01-24 04:03:44 -0500 |
commit | ce48c146495a1a50e48cdbfbfaba3e708be7c07c (patch) | |
tree | 6aabd7d32c16454175afdb2a20352056d9beb71a | |
parent | 1f07476ec143bbed7bf0b641749783b1094b4c4f (diff) |
sched/core: Fix cpu.max vs. cpuhotplug deadlock
Tejun reported the following cpu-hotplug lock (percpu-rwsem) read recursion:
tg_set_cfs_bandwidth()
get_online_cpus()
cpus_read_lock()
cfs_bandwidth_usage_inc()
static_key_slow_inc()
cpus_read_lock()
Reported-by: Tejun Heo <tj@kernel.org>
Tested-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/20180122215328.GP3397@worktop
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | include/linux/jump_label.h | 7 | ||||
-rw-r--r-- | kernel/jump_label.c | 12 | ||||
-rw-r--r-- | kernel/sched/fair.c | 4 |
3 files changed, 18 insertions, 5 deletions
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index c7b368c734af..e0340ca08d98 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h | |||
@@ -160,6 +160,8 @@ extern void arch_jump_label_transform_static(struct jump_entry *entry, | |||
160 | extern int jump_label_text_reserved(void *start, void *end); | 160 | extern int jump_label_text_reserved(void *start, void *end); |
161 | extern void static_key_slow_inc(struct static_key *key); | 161 | extern void static_key_slow_inc(struct static_key *key); |
162 | extern void static_key_slow_dec(struct static_key *key); | 162 | extern void static_key_slow_dec(struct static_key *key); |
163 | extern void static_key_slow_inc_cpuslocked(struct static_key *key); | ||
164 | extern void static_key_slow_dec_cpuslocked(struct static_key *key); | ||
163 | extern void jump_label_apply_nops(struct module *mod); | 165 | extern void jump_label_apply_nops(struct module *mod); |
164 | extern int static_key_count(struct static_key *key); | 166 | extern int static_key_count(struct static_key *key); |
165 | extern void static_key_enable(struct static_key *key); | 167 | extern void static_key_enable(struct static_key *key); |
@@ -222,6 +224,9 @@ static inline void static_key_slow_dec(struct static_key *key) | |||
222 | atomic_dec(&key->enabled); | 224 | atomic_dec(&key->enabled); |
223 | } | 225 | } |
224 | 226 | ||
227 | #define static_key_slow_inc_cpuslocked(key) static_key_slow_inc(key) | ||
228 | #define static_key_slow_dec_cpuslocked(key) static_key_slow_dec(key) | ||
229 | |||
225 | static inline int jump_label_text_reserved(void *start, void *end) | 230 | static inline int jump_label_text_reserved(void *start, void *end) |
226 | { | 231 | { |
227 | return 0; | 232 | return 0; |
@@ -416,6 +421,8 @@ extern bool ____wrong_branch_error(void); | |||
416 | 421 | ||
417 | #define static_branch_inc(x) static_key_slow_inc(&(x)->key) | 422 | #define static_branch_inc(x) static_key_slow_inc(&(x)->key) |
418 | #define static_branch_dec(x) static_key_slow_dec(&(x)->key) | 423 | #define static_branch_dec(x) static_key_slow_dec(&(x)->key) |
424 | #define static_branch_inc_cpuslocked(x) static_key_slow_inc_cpuslocked(&(x)->key) | ||
425 | #define static_branch_dec_cpuslocked(x) static_key_slow_dec_cpuslocked(&(x)->key) | ||
419 | 426 | ||
420 | /* | 427 | /* |
421 | * Normal usage; boolean enable/disable. | 428 | * Normal usage; boolean enable/disable. |
diff --git a/kernel/jump_label.c b/kernel/jump_label.c index 8594d24e4adc..b4517095db6a 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c | |||
@@ -79,7 +79,7 @@ int static_key_count(struct static_key *key) | |||
79 | } | 79 | } |
80 | EXPORT_SYMBOL_GPL(static_key_count); | 80 | EXPORT_SYMBOL_GPL(static_key_count); |
81 | 81 | ||
82 | static void static_key_slow_inc_cpuslocked(struct static_key *key) | 82 | void static_key_slow_inc_cpuslocked(struct static_key *key) |
83 | { | 83 | { |
84 | int v, v1; | 84 | int v, v1; |
85 | 85 | ||
@@ -180,7 +180,7 @@ void static_key_disable(struct static_key *key) | |||
180 | } | 180 | } |
181 | EXPORT_SYMBOL_GPL(static_key_disable); | 181 | EXPORT_SYMBOL_GPL(static_key_disable); |
182 | 182 | ||
183 | static void static_key_slow_dec_cpuslocked(struct static_key *key, | 183 | static void __static_key_slow_dec_cpuslocked(struct static_key *key, |
184 | unsigned long rate_limit, | 184 | unsigned long rate_limit, |
185 | struct delayed_work *work) | 185 | struct delayed_work *work) |
186 | { | 186 | { |
@@ -211,7 +211,7 @@ static void __static_key_slow_dec(struct static_key *key, | |||
211 | struct delayed_work *work) | 211 | struct delayed_work *work) |
212 | { | 212 | { |
213 | cpus_read_lock(); | 213 | cpus_read_lock(); |
214 | static_key_slow_dec_cpuslocked(key, rate_limit, work); | 214 | __static_key_slow_dec_cpuslocked(key, rate_limit, work); |
215 | cpus_read_unlock(); | 215 | cpus_read_unlock(); |
216 | } | 216 | } |
217 | 217 | ||
@@ -229,6 +229,12 @@ void static_key_slow_dec(struct static_key *key) | |||
229 | } | 229 | } |
230 | EXPORT_SYMBOL_GPL(static_key_slow_dec); | 230 | EXPORT_SYMBOL_GPL(static_key_slow_dec); |
231 | 231 | ||
232 | void static_key_slow_dec_cpuslocked(struct static_key *key) | ||
233 | { | ||
234 | STATIC_KEY_CHECK_USE(key); | ||
235 | __static_key_slow_dec_cpuslocked(key, 0, NULL); | ||
236 | } | ||
237 | |||
232 | void static_key_slow_dec_deferred(struct static_key_deferred *key) | 238 | void static_key_slow_dec_deferred(struct static_key_deferred *key) |
233 | { | 239 | { |
234 | STATIC_KEY_CHECK_USE(key); | 240 | STATIC_KEY_CHECK_USE(key); |
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 2fe3aa853e4d..26a71ebcd3c2 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c | |||
@@ -4365,12 +4365,12 @@ static inline bool cfs_bandwidth_used(void) | |||
4365 | 4365 | ||
4366 | void cfs_bandwidth_usage_inc(void) | 4366 | void cfs_bandwidth_usage_inc(void) |
4367 | { | 4367 | { |
4368 | static_key_slow_inc(&__cfs_bandwidth_used); | 4368 | static_key_slow_inc_cpuslocked(&__cfs_bandwidth_used); |
4369 | } | 4369 | } |
4370 | 4370 | ||
4371 | void cfs_bandwidth_usage_dec(void) | 4371 | void cfs_bandwidth_usage_dec(void) |
4372 | { | 4372 | { |
4373 | static_key_slow_dec(&__cfs_bandwidth_used); | 4373 | static_key_slow_dec_cpuslocked(&__cfs_bandwidth_used); |
4374 | } | 4374 | } |
4375 | #else /* HAVE_JUMP_LABEL */ | 4375 | #else /* HAVE_JUMP_LABEL */ |
4376 | static bool cfs_bandwidth_used(void) | 4376 | static bool cfs_bandwidth_used(void) |