History

cgroup,freezer: hold cpu_hotplug_lock before freezer_mutex

syzbot is reporting circular locking dependency between cpu_hotplug_lock and freezer_mutex, for commit f5d39b020809 ("freezer,sched: Rewrite core freezer logic") replaced atomic_inc() in freezer_apply_state() with static_branch_inc() which holds cpu_hotplug_lock. cpu_hotplug_lock => cgroup_threadgroup_rwsem => freezer_mutex cgroup_file_write() { cgroup_procs_write() { __cgroup_procs_write() { cgroup_procs_write_start() { cgroup_attach_lock() { cpus_read_lock() { percpu_down_read(&cpu_hotplug_lock); } percpu_down_write(&cgroup_threadgroup_rwsem); } } cgroup_attach_task() { cgroup_migrate() { cgroup_migrate_execute() { freezer_attach() { mutex_lock(&freezer_mutex); (...snipped...) } } } } (...snipped...) } } } freezer_mutex => cpu_hotplug_lock cgroup_file_write() { freezer_write() { freezer_change_state() { mutex_lock(&freezer_mutex); freezer_apply_state() { static_branch_inc(&freezer_active) { static_key_slow_inc() { cpus_read_lock(); static_key_slow_inc_cpuslocked(); cpus_read_unlock(); } } } mutex_unlock(&freezer_mutex); } } } Swap locking order by moving cpus_read_lock() in freezer_apply_state() to before mutex_lock(&freezer_mutex) in freezer_change_state().

Tetsuo Handa committed 3 years ago

Commit 57dcd641 parent 292fd841 file · +5 −2Browse files

Showing the changes to kernel (1 of 1 file). Show all

Unified · Split

kernel/cgroup/legacy_freezer.c+5−2
@@ -22,6 +22,7 @@ · 21 unmodified lines
22#include <linux/freezer.h>22#include <linux/freezer.h>
23#include <linux/seq_file.h>23#include <linux/seq_file.h>
24#include <linux/mutex.h>24#include <linux/mutex.h>
25#include <linux/cpu.h>
25 26
26/*27/*
27 * A cgroup is freezing if any FREEZING flags are set. FREEZING_SELF is28 * A cgroup is freezing if any FREEZING flags are set. FREEZING_SELF is
@@ -350,7 +351,7 @@ static void freezer_apply_state(struct freezer *freezer, bool freeze, · 322 unmodified lines
350 351
351 if (freeze) {352 if (freeze) {
352 if (!(freezer->state & CGROUP_FREEZING))353 if (!(freezer->state & CGROUP_FREEZING))
353 static_branch_inc(&freezer_active);354 static_branch_inc_cpuslocked(&freezer_active);
354 freezer->state |= state;355 freezer->state |= state;
355 freeze_cgroup(freezer);356 freeze_cgroup(freezer);
356 } else {357 } else {
@@ -361,7 +362,7 @@ static void freezer_apply_state(struct freezer *freezer, bool freeze, · 4 unmodified lines
361 if (!(freezer->state & CGROUP_FREEZING)) {362 if (!(freezer->state & CGROUP_FREEZING)) {
362 freezer->state &= ~CGROUP_FROZEN;363 freezer->state &= ~CGROUP_FROZEN;
363 if (was_freezing)364 if (was_freezing)
364 static_branch_dec(&freezer_active);365 static_branch_dec_cpuslocked(&freezer_active);
365 unfreeze_cgroup(freezer);366 unfreeze_cgroup(freezer);
366 }367 }
367 }368 }
@@ -379,6 +380,7 @@ static void freezer_change_state(struct freezer *freezer, bool freeze) · 11 unmodified lines
379{380{
380 struct cgroup_subsys_state *pos;381 struct cgroup_subsys_state *pos;
381 382
383 cpus_read_lock();
382 /*384 /*
383 * Update all its descendants in pre-order traversal. Each385 * Update all its descendants in pre-order traversal. Each
384 * descendant will try to inherit its parent's FREEZING state as386 * descendant will try to inherit its parent's FREEZING state as
@@ -407,6 +409,7 @@ static void freezer_change_state(struct freezer *freezer, bool freeze) · 22 unmodified lines
407 }409 }
408 rcu_read_unlock();410 rcu_read_unlock();
409 mutex_unlock(&freezer_mutex);411 mutex_unlock(&freezer_mutex);
412 cpus_read_unlock();
410}413}
411 414
412static ssize_t freezer_write(struct kernfs_open_file *of,415static ssize_t freezer_write(struct kernfs_open_file *of,