Commit e5d981df9a1d3f13dfff083ab0578daedbcd9e8a

Authored by Peter Zijlstra
Committed by Greg Kroah-Hartman
1 parent 72e5a2bdf7

sched/core: Fix cpu.max vs. cpuhotplug deadlock

commit ce48c146495a1a50e48cdbfbfaba3e708be7c07c upstream

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 3 changed files with 18 additions and 5 deletions Side-by-side Diff

include/linux/jump_label.h
... ... @@ -160,6 +160,8 @@
160 160 extern int jump_label_text_reserved(void *start, void *end);
161 161 extern void static_key_slow_inc(struct static_key *key);
162 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 165 extern void jump_label_apply_nops(struct module *mod);
164 166 extern int static_key_count(struct static_key *key);
165 167 extern void static_key_enable(struct static_key *key);
... ... @@ -222,6 +224,9 @@
222 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 230 static inline int jump_label_text_reserved(void *start, void *end)
226 231 {
227 232 return 0;
... ... @@ -416,6 +421,8 @@
416 421  
417 422 #define static_branch_inc(x) static_key_slow_inc(&(x)->key)
418 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 428 * Normal usage; boolean enable/disable.
... ... @@ -79,7 +79,7 @@
79 79 }
80 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 84 int v, v1;
85 85  
... ... @@ -180,7 +180,7 @@
180 180 }
181 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 184 unsigned long rate_limit,
185 185 struct delayed_work *work)
186 186 {
... ... @@ -211,7 +211,7 @@
211 211 struct delayed_work *work)
212 212 {
213 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 215 cpus_read_unlock();
216 216 }
217 217  
... ... @@ -228,6 +228,12 @@
228 228 __static_key_slow_dec(key, 0, NULL);
229 229 }
230 230 EXPORT_SYMBOL_GPL(static_key_slow_dec);
  231 +
  232 +void static_key_slow_dec_cpuslocked(struct static_key *key)
  233 +{
  234 + STATIC_KEY_CHECK_USE();
  235 + __static_key_slow_dec_cpuslocked(key, 0, NULL);
  236 +}
231 237  
232 238 void static_key_slow_dec_deferred(struct static_key_deferred *key)
233 239 {
... ... @@ -4040,12 +4040,12 @@
4040 4040  
4041 4041 void cfs_bandwidth_usage_inc(void)
4042 4042 {
4043   - static_key_slow_inc(&__cfs_bandwidth_used);
  4043 + static_key_slow_inc_cpuslocked(&__cfs_bandwidth_used);
4044 4044 }
4045 4045  
4046 4046 void cfs_bandwidth_usage_dec(void)
4047 4047 {
4048   - static_key_slow_dec(&__cfs_bandwidth_used);
  4048 + static_key_slow_dec_cpuslocked(&__cfs_bandwidth_used);
4049 4049 }
4050 4050 #else /* HAVE_JUMP_LABEL */
4051 4051 static bool cfs_bandwidth_used(void)