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().
Showing the changes to kernel (1 of 1 file). Show all
Unified · Split
| @@ -22,6 +22,7 @@ · 21 unmodified lines | ||
| 22 | 22 | #include <linux/freezer.h> |
| 23 | 23 | #include <linux/seq_file.h> |
| 24 | 24 | #include <linux/mutex.h> |
| 25 | +#include <linux/cpu.h> | |
| 25 | 26 | |
| 26 | 27 | /* |
| 27 | 28 | * 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 | 352 | if (freeze) { |
| 352 | 353 | if (!(freezer->state & CGROUP_FREEZING)) |
| 353 | − static_branch_inc(&freezer_active); | |
| 354 | + static_branch_inc_cpuslocked(&freezer_active); | |
| 354 | 355 | freezer->state |= state; |
| 355 | 356 | freeze_cgroup(freezer); |
| 356 | 357 | } else { |
| @@ -361,7 +362,7 @@ static void freezer_apply_state(struct freezer *freezer, bool freeze, · 4 unmodified lines | ||
| 361 | 362 | if (!(freezer->state & CGROUP_FREEZING)) { |
| 362 | 363 | freezer->state &= ~CGROUP_FROZEN; |
| 363 | 364 | if (was_freezing) |
| 364 | − static_branch_dec(&freezer_active); | |
| 365 | + static_branch_dec_cpuslocked(&freezer_active); | |
| 365 | 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 | 381 | struct cgroup_subsys_state *pos; |
| 381 | 382 | |
| 383 | + cpus_read_lock(); | |
| 382 | 384 | /* |
| 383 | 385 | * Update all its descendants in pre-order traversal. Each |
| 384 | 386 | * 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 | 410 | rcu_read_unlock(); |
| 409 | 411 | mutex_unlock(&freezer_mutex); |
| 412 | + cpus_read_unlock(); | |
| 410 | 413 | } |
| 411 | 414 | |
| 412 | 415 | static ssize_t freezer_write(struct kernfs_open_file *of, |