cgroup/for-next: WARNING: possible circular locking dependency detected in cpuset_write_resmask
Subsystems (6)
Hi,
We run CRIU tests for cgroup/for-next regularly, and today they detected this warning:
[ 380.301034] ======================================================
[ 380.301096] WARNING: possible circular locking dependency detected
[ 380.301159] 4.15.0-rc1+ #1 Not tainted
[ 380.301223] ------------------------------------------------------
[ 380.301284] criu/14277 is trying to acquire lock:
[ 380.301346] (cpuset_mutex){+.+.}, at: [<ffffffffb3160191>]
cpuset_write_resmask+0x61/0x1100
[ 380.301423]
[ 380.301423] but task is already holding lock:
[ 380.301489] (cpu_hotplug_lock.rw_sem){++++}, at:
[<ffffffffb3160183>] cpuset_write_resmask+0x53/0x1100
[ 380.301559]
[ 380.301559] which lock already depends on the new lock.
[ 380.301559]
[ 380.301632]
[ 380.301632] the existing dependency chain (in reverse order) is:
[ 380.301699]
[ 380.301699] -> #1 (cpu_hotplug_lock.rw_sem){++++}:
[ 380.301770] cpus_read_lock+0x3e/0x80
[ 380.301835] static_key_slow_inc+0xe/0xa0
[ 380.301897] cpuset_css_online+0x62/0x330
[ 380.301961] online_css+0x26/0x80
[ 380.302022] cgroup_apply_control_enable+0x266/0x3d0
[ 380.302084] cgroup_mkdir+0x37d/0x4f0
[ 380.302148] kernfs_iop_mkdir+0x53/0x80
[ 380.302213] vfs_mkdir+0x10e/0x1a0
[ 380.302275] SyS_mkdirat+0xb3/0xe0
[ 380.302338] entry_SYSCALL_64_fastpath+0x23/0x9a
[ 380.302399]
[ 380.302399] -> #0 (cpuset_mutex){+.+.}:
[ 380.302468] lock_acquire+0xec/0x1e0
[ 380.302531] __mutex_lock+0x89/0x920
[ 380.302593] cpuset_write_resmask+0x61/0x1100
[ 380.302655] cgroup_file_write+0x7b/0x200
[ 380.302716] kernfs_fop_write+0x112/0x1a0
[ 380.302779] __vfs_write+0x23/0x150
[ 380.302840] vfs_write+0xc8/0x1c0
[ 380.302901] SyS_write+0x45/0xa0
[ 380.302963] entry_SYSCALL_64_fastpath+0x23/0x9a
[ 380.303023]
[ 380.303023] other info that might help us debug this:
[ 380.303023]
[ 380.303095] Possible unsafe locking scenario:
[ 380.303095]
[ 380.303162] CPU0 CPU1
[ 380.303225] ---- ----
[ 380.303285] lock(cpu_hotplug_lock.rw_sem);
[ 380.303347] lock(cpuset_mutex);
[ 380.303408] lock(cpu_hotplug_lock.rw_sem);
[ 380.303470] lock(cpuset_mutex);
[ 380.303531]
[ 380.303531] *** DEADLOCK ***
[ 380.303531]
[ 380.303609] 3 locks held by criu/14277:
[ 380.303677] #0: (sb_writers#12){.+.+}, at: [<ffffffffb3284a70>]
vfs_write+0x1a0/0x1c0
[ 380.303746] #1: (&of->mutex){+.+.}, at: [<ffffffffb3317dc2>]
kernfs_fop_write+0xe2/0x1a0
[ 380.303816] #2: (cpu_hotplug_lock.rw_sem){++++}, at:
[<ffffffffb3160183>] cpuset_write_resmask+0x53/0x1100
[ 380.303886]
[ 380.303886] stack backtrace:
[ 380.303953] CPU: 0 PID: 14277 Comm: criu Not tainted 4.15.0-rc1+ #1
[ 380.304015] Hardware name: Google Google Compute Engine/Google
Compute Engine, BIOS Google 01/01/2011
[ 380.304081] Call Trace:
[ 380.304146] dump_stack+0x7c/0xbe
[ 380.304212] print_circular_bug+0x20d/0x320
[ 380.304276] ? copy_trace+0xb0/0xb0
[ 380.304337] check_prev_add+0x41d/0x770
[ 380.304400] ? __lock_acquire+0x1091/0x1130
[ 380.304461] __lock_acquire+0x1091/0x1130
[ 380.304523] lock_acquire+0xec/0x1e0
[ 380.304586] ? cpuset_write_resmask+0x61/0x1100
[ 380.304649] __mutex_lock+0x89/0x920
[ 380.304711] ? cpuset_write_resmask+0x61/0x1100
[ 380.304774] ? cpuset_write_resmask+0x61/0x1100
[ 380.304837] ? cpuset_write_resmask+0x27f/0x1100
[ 380.304900] ? cpuset_write_resmask+0x27f/0x1100
[ 380.304962] ? cpuset_write_resmask+0x61/0x1100
[ 380.305024] cpuset_write_resmask+0x61/0x1100
[ 380.305086] ? kernfs_fop_write+0xea/0x1a0
[ 380.305148] cgroup_file_write+0x7b/0x200
[ 380.305215] kernfs_fop_write+0x112/0x1a0
[ 380.305277] __vfs_write+0x23/0x150
[ 380.305341] ? rcu_read_lock_sched_held+0x3f/0x70
[ 380.305404] ? rcu_sync_lockdep_assert+0x2a/0x50
[ 380.305466] ? __sb_start_write+0xd2/0x1b0
[ 380.305527] ? vfs_write+0x1a0/0x1c0
[ 380.305588] vfs_write+0xc8/0x1c0
[ 380.305650] SyS_write+0x45/0xa0
[ 380.305712] entry_SYSCALL_64_fastpath+0x23/0x9a
[ 380.305774] RIP: 0033:0x145971dc4330
[ 380.305835] RSP: 002b:00007ffeaffb30b8 EFLAGS: 00000246 ORIG_RAX:
0000000000000001
[ 380.305903] RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 0000145971dc4330
[ 380.305964] RDX: 0000000000000003 RSI: 000000000137aac0 RDI: 0000000000000003
[ 380.306026] RBP: 0000000000000000 R08: 737570632e746573 R09: 6f72702070756f72
[ 380.306087] R10: 0000000000000000 R11: 0000000000000246 R12: 000000000137b440
[ 380.306149] R13: 0000000001373448 R14: 0000000001373450 R15: 000000000137a970[avagin@laptop linux]$ git describe cgroup/for-next v4.15-rc1-3-gddf7005f3221
All logs are here: https://travis-ci.org/avagin/linux/jobs/308154268
Thanks,
Andrei
PATCH 1/1 cgroup/cpuset: fix circular locking dependency
CPU1
cpus_read_lock+0x3e/0x80 static_key_slow_inc+0xe/0xa0 cpuset_css_online+0x62/0x330 online_css+0x26/0x80 cgroup_apply_control_enable+0x266/0x3d0 cgroup_mkdir+0x37d/0x4f0 kernfs_iop_mkdir+0x53/0x80 vfs_mkdir+0x10e/0x1a0 SyS_mkdirat+0xb3/0xe0 entry_SYSCALL_64_fastpath+0x23/0x9a
CPU0
lock_acquire+0xec/0x1e0 __mutex_lock+0x89/0x920 cpuset_write_resmask+0x61/0x1100 cgroup_file_write+0x7b/0x200 kernfs_fop_write+0x112/0x1a0 __vfs_write+0x23/0x150 vfs_write+0xc8/0x1c0 SyS_write+0x45/0xa0 entry_SYSCALL_64_fastpath+0x23/0x9a
CPU0 CPU1
---- ----
lock(cpu_hotplug_lock.rw_sem);
lock(cpuset_mutex);
lock(cpu_hotplug_lock.rw_sem);lock(cpuset_mutex);
Change locking order of cpu_hotplug_lock.rw_sem and cpuset_mutex in cpuset_css_online(). Use _cpuslocked() version for static_branch_inc/static_branch_dec in cpuset_inc()/cpuset_dec().
--- include/linux/cpuset.h | 8 ++++---- include/linux/jump_label.h | 10 ++++++++-- kernel/cgroup/cpuset.c | 4 ++-- kernel/jump_label.c | 13 +++++++++++++ 4 files changed, 27 insertions(+), 8 deletions(-)
include/linux/cpuset.h +4 -4
@@ -40,14 +40,14 @@ static inline bool cpusets_enabled(void)static inline void cpuset_inc (void ) { -static_branch_inc (&cpusets_pre_enable_key); -static_branch_inc (&cpusets_enabled_key); +static_branch_inc_cpuslocked (&cpusets_pre_enable_key); +static_branch_inc_cpuslocked (&cpusets_enabled_key); }static inline void cpuset_dec (void ) { -static_branch_dec (&cpusets_enabled_key); -static_branch_dec (&cpusets_pre_enable_key); +static_branch_dec_cpuslocked (&cpusets_enabled_key); +static_branch_dec_cpuslocked (&cpusets_pre_enable_key); }extern int cpuset_init (void );
include/linux/jump_label.h +8 -2
@@ -160,6 +160,8 @@ extern void arch_jump_label_transform_static(struct jump_entry *entry,extern int jump_label_text_reserved (void *start,void *end);extern void static_key_slow_inc (struct static_key *key);extern void static_key_slow_dec (struct static_key *key); +extern void static_key_slow_incr_cpuslocked (struct static_key *key); +extern void static_key_slow_decr_cpuslocked (struct static_key *key);extern void jump_label_apply_nops (struct module *mod);extern int static_key_count (struct static_key *key);extern void static_key_enable (struct static_key *key);
@@ -259,6 +261,8 @@ static inline void static_key_disable(struct static_key *key)#define static_key_enable_cpuslocked (k) static_key_enable((k))#define static_key_disable_cpuslocked (k) static_key_disable((k)) +#define static_key_slow_incr_cpuslocked (k) static_key_slow_inc((k)) +#define static_key_slow_decr_cpuslocked (k) static_key_slow_dec((k))#define STATIC_KEY_INIT_TRUE { .enabled = ATOMIC_INIT(1) }#define STATIC_KEY_INIT_FALSE { .enabled = ATOMIC_INIT(0) }
@@ -414,8 +418,10 @@ struct static_key_false {* Advanced usage; refcount, branch is enabled when: count != 0 */ -#define static_branch_inc (x) static_key_slow_inc(&(x)->key) -#define static_branch_dec (x) static_key_slow_dec(&(x)->key) +#define static_branch_inc (x) static_key_slow_inc(&(x)->key) +#define static_branch_dec (x) static_key_slow_dec(&(x)->key) +#define static_branch_inc_cpuslocked (x) static_key_slow_incr_cpuslocked(&(x)->key) +#define static_branch_dec_cpuslocked (x) static_key_slow_decr_cpuslocked(&(x)->key)/* * Normal usage; boolean enable/disable.
kernel/cgroup/cpuset.c +2 -2
@@ -1985,7 +1985,7 @@ static int cpuset_css_online(struct cgroup_subsys_state *css)if (!parent)return 0 ; -mutex_lock (&cpuset_mutex); +cpuset_sched_change_begin ();set_bit (CS_ONLINE , &cs->flags);if (is_spread_page (parent))
@@ -2034,7 +2034,7 @@ static int cpuset_css_online(struct cgroup_subsys_state *css)cpumask_copy (cs->effective_cpus, parent->cpus_allowed);spin_unlock_irq (&callback_lock); out_unlock: -mutex_unlock (&cpuset_mutex); +cpuset_sched_change_end ();return 0 ; }
kernel/jump_label.c +13 -0
@@ -126,6 +126,12 @@ void static_key_slow_inc(struct static_key *key) }EXPORT_SYMBOL_GPL (static_key_slow_inc); +void static_key_slow_incr_cpuslocked (struct static_key *key) +{ +static_key_slow_inc_cpuslocked (key); +} +EXPORT_SYMBOL_GPL (static_key_slow_incr_cpuslocked); +void static_key_enable_cpuslocked (struct static_key *key) {STATIC_KEY_CHECK_USE (key);
@@ -229,6 +235,13 @@ void static_key_slow_dec(struct static_key *key) }EXPORT_SYMBOL_GPL (static_key_slow_dec); +void static_key_slow_decr_cpuslocked (struct static_key *key) +{ +STATIC_KEY_CHECK_USE (key); +static_key_slow_dec_cpuslocked (key,0 ,NULL ); +} +EXPORT_SYMBOL_GPL (static_key_slow_decr_cpuslocked); +void static_key_slow_dec_deferred (struct static_key_deferred *key) {STATIC_KEY_CHECK_USE (key);
Signature · 2 lines
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 145 lines · Prateek Sood
CPU1
cpus_read_lock+0x3e/0x80 static_key_slow_inc+0xe/0xa0 cpuset_css_online+0x62/0x330 online_css+0x26/0x80 cgroup_apply_control_enable+0x266/0x3d0 cgroup_mkdir+0x37d/0x4f0 kernfs_iop_mkdir+0x53/0x80 vfs_mkdir+0x10e/0x1a0 SyS_mkdirat+0xb3/0xe0 entry_SYSCALL_64_fastpath+0x23/0x9aCPU0
lock_acquire+0xec/0x1e0 __mutex_lock+0x89/0x920 cpuset_write_resmask+0x61/0x1100 cgroup_file_write+0x7b/0x200 kernfs_fop_write+0x112/0x1a0 __vfs_write+0x23/0x150 vfs_write+0xc8/0x1c0 SyS_write+0x45/0xa0 entry_SYSCALL_64_fastpath+0x23/0x9aCPU0 CPU1
---- ----lock(cpu_hotplug_lock.rw_sem); lock(cpuset_mutex); lock(cpu_hotplug_lock.rw_sem);
lock(cpuset_mutex);Change locking order of cpu_hotplug_lock.rw_sem and
cpuset_mutexincpuset_css_online(). Use_cpuslocked()version for static_branch_inc/static_branch_dec in cpuset_inc()/cpuset_dec().Signed-off-by Prateek Sood <[email protected]>--- include/linux/cpuset.h | 8 ++++---- include/linux/jump_label.h | 10 ++++++++-- kernel/cgroup/cpuset.c | 4 ++-- kernel/jump_label.c | 13 +++++++++++++ 4 files changed, 27 insertions(+), 8 deletions(-)include/linux/cpuset.h +4
-4@@ -40,14 +40,14 @@ static inline bool cpusets_enabled(void)static inline void cpuset_inc (void ) { -static_branch_inc (&cpusets_pre_enable_key); -static_branch_inc (&cpusets_enabled_key); +static_branch_inc_cpuslocked (&cpusets_pre_enable_key); +static_branch_inc_cpuslocked (&cpusets_enabled_key); }static inline void cpuset_dec (void ) { -static_branch_dec (&cpusets_enabled_key); -static_branch_dec (&cpusets_pre_enable_key); +static_branch_dec_cpuslocked (&cpusets_enabled_key); +static_branch_dec_cpuslocked (&cpusets_pre_enable_key); }extern int cpuset_init (void );include/linux/jump_label.h +8
-2@@ -160,6 +160,8 @@ extern void arch_jump_label_transform_static(struct jump_entry *entry,extern int jump_label_text_reserved (void *start,void *end);extern void static_key_slow_inc (struct static_key *key);extern void static_key_slow_dec (struct static_key *key); +extern void static_key_slow_incr_cpuslocked (struct static_key *key); +extern void static_key_slow_decr_cpuslocked (struct static_key *key);extern void jump_label_apply_nops (struct module *mod);extern int static_key_count (struct static_key *key);extern void static_key_enable (struct static_key *key);@@ -259,6 +261,8 @@ static inline void static_key_disable(struct static_key *key)#define static_key_enable_cpuslocked (k) static_key_enable((k))#define static_key_disable_cpuslocked (k) static_key_disable((k)) +#define static_key_slow_incr_cpuslocked (k) static_key_slow_inc((k)) +#define static_key_slow_decr_cpuslocked (k) static_key_slow_dec((k))#define STATIC_KEY_INIT_TRUE { .enabled = ATOMIC_INIT(1) }#define STATIC_KEY_INIT_FALSE { .enabled = ATOMIC_INIT(0) }@@ -414,8 +418,10 @@ struct static_key_false {* Advanced usage; refcount, branch is enabled when: count != 0 */ -#define static_branch_inc (x) static_key_slow_inc(&(x)->key) -#define static_branch_dec (x) static_key_slow_dec(&(x)->key) +#define static_branch_inc (x) static_key_slow_inc(&(x)->key) +#define static_branch_dec (x) static_key_slow_dec(&(x)->key) +#define static_branch_inc_cpuslocked (x) static_key_slow_incr_cpuslocked(&(x)->key) +#define static_branch_dec_cpuslocked (x) static_key_slow_decr_cpuslocked(&(x)->key)/* * Normal usage; boolean enable/disable. kernel/cgroup/cpuset.c +2
-2@@ -1985,7 +1985,7 @@ static int cpuset_css_online(struct cgroup_subsys_state *css)if (!parent)return 0 ; -mutex_lock (&cpuset_mutex); +cpuset_sched_change_begin ();set_bit (CS_ONLINE , &cs->flags);if (is_spread_page (parent))@@ -2034,7 +2034,7 @@ static int cpuset_css_online(struct cgroup_subsys_state *css)cpumask_copy (cs->effective_cpus, parent->cpus_allowed);spin_unlock_irq (&callback_lock); out_unlock: -mutex_unlock (&cpuset_mutex); +cpuset_sched_change_end ();return 0 ; }kernel/jump_label.c +6
-0@@ -126,6 +126,12 @@ void static_key_slow_inc(struct static_key *key) }EXPORT_SYMBOL_GPL (static_key_slow_inc); +void static_key_slow_incr_cpuslocked (struct static_key *key) +{ +static_key_slow_inc_cpuslocked (key); +} +EXPORT_SYMBOL_GPL (static_key_slow_incr_cpuslocked); +void static_key_enable_cpuslocked (struct static_key *key) {STATIC_KEY_CHECK_USE (key);@@ -229,6 +235,13 @@ void static_key_slow_dec(struct static_key *key) }EXPORT_SYMBOL_GPL (static_key_slow_dec);
kernel/jump_label.c void static_key_slow_dec(struct static_key *key) · line 247
Quoted 10 lines
kernel/jump_label.c +7
-0+void static_key_slow_decr_cpuslocked (struct static_key *key) +{ +STATIC_KEY_CHECK_USE (key); +static_key_slow_dec_cpuslocked (key,0 ,NULL ); +} +EXPORT_SYMBOL_GPL (static_key_slow_decr_cpuslocked); +void static_key_slow_dec_deferred (struct static_key_deferred *key) {STATIC_KEY_CHECK_USE (key);
TJ,
Any feedback/suggestion for this patch?
Signature · 3 lines
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Tejun Heo
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
Hello,
Quoted 1 line · Prateek Sood
Any feedback/suggestion for this patch?
Sorry about the delay. I'm a bit worried because it feels like we're chasing a squirrel. I'll think through the recent changes and this one and get back to you.
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
Hello, again.
Quoted 8 lines · Tejun Heo
Hello,
Quoted 1 line · Prateek Sood
Any feedback/suggestion for this patch?
Sorry about the delay. I'm a bit worried because it feels like we're chasing a squirrel. I'll think through the recent changes and this one and get back to you.
Can you please take a look at the following pending commit?
AFAICS, this should remove the circular dependency you originally reported. I'll revert the two cpuset commits for now.
Thanks.
Signature · 1 line
tejun
Peter Zijlstra
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 12 lines · Tejun Heo
Hello, again.
Quoted 8 lines · Tejun Heo
Hello,
Quoted 1 line · Prateek Sood
Any feedback/suggestion for this patch?
Sorry about the delay. I'm a bit worried because it feels like we're chasing a squirrel. I'll think through the recent changes and this one and get back to you.
Quoted 6 lines
Can you please take a look at the following pending commit?
AFAICS, this should remove the circular dependency you originally reported. I'll revert the two cpuset commits for now.
So I liked his patches in that we would be able to go back to synchronous sched_domain building.
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 15 lines · Peter Zijlstra
Quoted 14 lines · Tejun Heo
Hello, again.
Quoted 8 lines · Tejun Heo
Hello,
Quoted 1 line · Prateek Sood
Any feedback/suggestion for this patch?
Sorry about the delay. I'm a bit worried because it feels like we're chasing a squirrel. I'll think through the recent changes and this one and get back to you.
Can you please take a look at the following pending commit?
Quoted 8 lines
Quoted 4 lines
AFAICS, this should remove the circular dependency you originally reported. I'll revert the two cpuset commits for now.
So I liked his patches in that we would be able to go back to synchronous
sched_domainbuilding.
This will fix the original circular locking dependency issue. I will let you both (Peter & TJ) to decide on which one to pick.
Signature · 3 lines
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 21 lines · Prateek Sood
Quoted 20 lines · Peter Zijlstra
Quoted 18 lines · Tejun Heo
Hello, again.
Quoted 8 lines · Tejun Heo
Hello,
Quoted 1 line · Prateek Sood
Any feedback/suggestion for this patch?
Sorry about the delay. I'm a bit worried because it feels like we're chasing a squirrel. I'll think through the recent changes and this one and get back to you.
Can you please take a look at the following pending commit?
AFAICS, this should remove the circular dependency you originally reported. I'll revert the two cpuset commits for now.
Quoted 11 lines
Quoted 3 lines
So I liked his patches in that we would be able to go back to synchronous
sched_domainbuilding.This will fix the original circular locking dependency issue. I will let you both (Peter & TJ) to decide on which one to pick.
TJ & Peter,
There is one deadlock issue during cgroup migration from cpu hotplug path when a task T is being moved from source to destination cgroup.
kworker/0:0cpuset_hotplug_workfn()
cpuset_hotplug_update_tasks()
hotplug_update_tasks_legacy()
remove_tasks_in_empty_cpuset()
cgroup_transfer_tasks() // stuck in iterator loop
cgroup_migrate()
cgroup_migrate_add_task()
In cgroup_migrate_add_task() it checks for PF_EXITING flag of task T. Task T will not migrate to destination cgroup. css_task_iter_start() will keep pointing to task T in loop waiting for task T cg_list node to be removed.
Task Tdo_exit()
exit_signals() // sets PF_EXITING
exit_task_namespaces()
switch_task_namespaces()
free_nsproxy()
put_mnt_ns()
drop_collected_mounts()
namespace_unlock()
synchronize_rcu()
_synchronize_rcu_expedited()
schedule_work() // on cpu0 low priority worker pool wait_event() // waiting for work item to execute
Task T inserted a work item in the worklist of cpu0 low priority worker pool. It is waiting for expedited grace period work item to execute. This work item will only be executed once kworker/0:0 complete execution of cpuset_hotplug_workfn().
kworker/0:0 ==> Task T ==>kworker/0:0
Following suggested patch might not be able to fix the above mentioned case:
https://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git/commit/?h=for-4.15-fixes&id=e8b3f8db7aad99fcc5234fc5b89984ff6620de3d
Combination of following patches fixes above mentioned scenario as well:
1) Inverting cpuset_mutex and cpu_hotplug_lock locking sequence
2) Making cpuset hotplug work synchronous
Signature · 3 lines
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Tejun Heo
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
Hello, Prateek.
Quoted 13 lines · Prateek Sood
There is one deadlock issue during cgroup migration from cpu hotplug path when a task T is being moved from source to destination cgroup.
kworker/0:0
cpuset_hotplug_workfn()
cpuset_hotplug_update_tasks()
hotplug_update_tasks_legacy()
remove_tasks_in_empty_cpuset()
cgroup_transfer_tasks()// stuck in iterator loop
cgroup_migrate()
cgroup_migrate_add_task()
Quoted 4 lines
In
cgroup_migrate_add_task()it checks forPF_EXITINGflag of task T. Task T will not migrate to destination cgroup.css_task_iter_start()will keep pointing to task T in loop waiting for task Tcg_listnode to be removed.
Heh, that's a bug in cgroup_transfer_tasks() which happened because I forgot to update when we changed how we handle exiting tasks. The right thing to do here is making cgroup_transfer_tasks() repeat iff there were a valid migration target which didn't get transferred.
Thanks.
Signature · 1 line
tejun
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 21 lines · Tejun Heo
Hello, Prateek.
Quoted 13 lines · Prateek Sood
There is one deadlock issue during cgroup migration from cpu hotplug path when a task T is being moved from source to destination cgroup.
kworker/0:0
cpuset_hotplug_workfn()
cpuset_hotplug_update_tasks()
hotplug_update_tasks_legacy()
remove_tasks_in_empty_cpuset()
cgroup_transfer_tasks()// stuck in iterator loop
cgroup_migrate()
cgroup_migrate_add_task()Quoted 4 lines
In
cgroup_migrate_add_task()it checks forPF_EXITINGflag of task T. Task T will not migrate to destination cgroup.css_task_iter_start()will keep pointing to task T in loop waiting for task Tcg_listnode to be removed.
Quoted 7 lines
Heh, that's a bug in
cgroup_transfer_tasks()which happened because I forgot to update when we changed how we handle exiting tasks. The right thing to do here is makingcgroup_transfer_tasks()repeat iff there were a valid migration target which didn't get transferred.Thanks.
Hi TJ,
Did you mean something like below. If not then could you please share a patch for this problem in cgroup_transfer_tasks().
include/linux/cgroup.h +2 -0
@@ -143,6 +143,8 @@ struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset,void css_task_iter_start (struct cgroup_subsys_state *css,unsigned int flags,struct css_task_iter *it); +void css_task_migrate_iter_start (struct cgroup_subsys_state *css, +unsigned int flags,struct css_task_iter *it);struct task_struct *css_task_iter_next (struct css_task_iter *it);void css_task_iter_end (struct css_task_iter *it);
kernel/cgroup/cgroup-v1.c +1 -1
@@ -122,7 +122,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)* ->can_attach() fails. */ do { - css_task_iter_start(&from->self, 0, &it); + css_task_migrate_iter_start(&from->self, 0, &it); task = css_task_iter_next(&it); if (task) get_task_struct(task);
kernel/cgroup/cgroup.c +36 -0
@@ -4189,6 +4189,42 @@ void css_task_iter_start(struct cgroup_subsys_state *css, unsigned int flags,spin_unlock_irq (&css_set_lock); } +void css_task_migrate_iter_start (struct cgroup_subsys_state *css, +unsigned int flags,struct css_task_iter *it) +{ +struct task_struct *task =NULL ; +/* no one should try to iterate before mounting cgroups */ +WARN_ON_ONCE (!use_task_css_set_links); + +memset (it,0 ,sizeof (*it)); + +spin_lock_irq (&css_set_lock); + + it->ss = css->ss; + it->flags = flags; + +if (it->ss) + it->cset_pos = &css->cgroup->e_csets[css->ss->id]; +else + it->cset_pos = &css->cgroup->cset_links; + + it->cset_head = it->cset_pos; + +css_task_iter_advance_css_set (it); + +while (it->task_pos) { + task = list_entry(it->task_pos, struct task_struct, + cg_list); + +if (likely (!(task->flags &PF_EXITING ))) +break ; + +css_task_iter_advance (it); + } + +spin_unlock_irq (&css_set_lock); +} +/** * css_task_iter_next - return the next task for the iterator * @it: the task iterator being iterated
Thanks
Signature · 3 lines
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Tejun Heo
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
Hello, Prateek.
Quoted 3 lines · Prateek Sood
Did you mean something like below. If not then could you please share a patch for this problem in
cgroup_transfer_tasks().
Oh we surely can add a new iterator but we can just count in cgroup_transfer_tasks() too, right?
Thanks.
Signature · 1 line
tejun
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
On 12/13/2017 09:10 PM, Tejun Heo wrote:
Hi TJ,
Quoted 9 lines
Hello, Prateek.
Quoted 3 lines · Prateek Sood
Did you mean something like below. If not then could you please share a patch for this problem in
cgroup_transfer_tasks().Oh we surely can add a new iterator but we can just count in
cgroup_transfer_tasks()too, right?
I did not get what you meant by this. Could you please share a patch for this.
Quoted 3 lines
Thanks.
Following are two ways to improve cgroup_transfer_tasks(). In both cases task in PF_EXITING state would be left in source cgroup. It would be removed from cgroup_exit() in exit path.
kernel/cgroup/cgroup-v1.c +4 -1
@@ -123,7 +123,10 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)*/ do { css_task_iter_start(&from->self, 0, &it); - task = css_task_iter_next(&it); + do { + task = css_task_iter_next(&it); + } while (task && (task & PF_EXITING)) + if (task) get_task_struct(task); css_task_iter_end(&it);
----------------------------8<---------------------------------
kernel/cgroup/cgroup-v1.c +2 -2
@@ -121,12 +121,11 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)* Migrate tasks one-by-one until @from is empty. This fails iff * ->can_attach() fails. */ +css_task_iter_start (&from->self,0 , &it); do { - css_task_iter_start(&from->self, 0, &it); task = css_task_iter_next(&it); if (task) get_task_struct(task); - css_task_iter_end(&it); if (task) { ret = cgroup_migrate(task, false, &mgctx);
@@ -135,6 +134,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)put_task_struct (task); } }while (task && !ret); +css_task_iter_end (&it); out_err:cgroup_migrate_finish (&mgctx);percpu_up_write (&cgroup_threadgroup_rwsem);
Thanks
Signature · 3 lines
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Tejun Heo
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
Hello, Prateek.
Quoted 9 lines · Prateek Sood
Following are two ways to improve
cgroup_transfer_tasks(). In both cases task inPF_EXITINGstate would be left in source cgroup. It would be removed fromcgroup_exit()in exit path.kernel/cgroup/cgroup-v1.c +0
-0
kernel/cgroup/cgroup-v1.c int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) · line 129
Quoted 8 lines
kernel/cgroup/cgroup-v1.c +4
-1@@ -123,7 +123,10 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)*/ do { css_task_iter_start(&from->self, 0, &it); - task = css_task_iter_next(&it); + do { + task = css_task_iter_next(&it); + } while (task && (task & PF_EXITING)) +
Yeah, this looks good to me. We can't just make a single pass as in the other one because we can race aginst fork. And PF_EXITING being left behind is what was happening previously too anyway. They can't be moved.
Thanks.
Signature · 1 line
tejun
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 26 lines · Tejun Heo
Hello, Prateek.
Quoted 9 lines · Prateek Sood
Following are two ways to improve
cgroup_transfer_tasks(). In both cases task inPF_EXITINGstate would be left in source cgroup. It would be removed fromcgroup_exit()in exit path.kernel/cgroup/cgroup-v1.c +0
-0kernel/cgroup/cgroup-v1.c int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) · line 129
Quoted 8 lines
kernel/cgroup/cgroup-v1.c +4
-1@@ -123,7 +123,10 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)*/ do { css_task_iter_start(&from->self, 0, &it); - task = css_task_iter_next(&it); + do { + task = css_task_iter_next(&it); + } while (task && (task & PF_EXITING)) +Yeah, this looks good to me. We can't just make a single pass as in the other one because we can race aginst fork. And
PF_EXITINGbeing left behind is what was happening previously too anyway. They can't be moved.
Quoted 2 lines
Thanks.
Thanks TJ for reviewing.
I will send a formal patch with the above approved approach.
Signature · 3 lines
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH 1/1 cgroup: Fix deadlock in cpu hotplug path
Deadlock during cgroup migration from cpu hotplug path when a task T is being moved from source to destination cgroup.
kworker/0:0cpuset_hotplug_workfn()
cpuset_hotplug_update_tasks()
hotplug_update_tasks_legacy()
remove_tasks_in_empty_cpuset()
cgroup_transfer_tasks() // stuck in iterator loop
cgroup_migrate()
cgroup_migrate_add_task()
In cgroup_migrate_add_task() it checks for PF_EXITING flag of task T. Task T will not migrate to destination cgroup. css_task_iter_start() will keep pointing to task T in loop waiting for task T cg_list node to be removed.
Task Tdo_exit()
exit_signals() // sets PF_EXITING
exit_task_namespaces()
switch_task_namespaces()
free_nsproxy()
put_mnt_ns()
drop_collected_mounts()
namespace_unlock()
synchronize_rcu()
_synchronize_rcu_expedited()
schedule_work() // on cpu0 low priority worker pool wait_event() // waiting for work item to execute
Task T inserted a work item in the worklist of cpu0 low priority worker pool. It is waiting for expedited grace period work item to execute. This work item will only be executed once kworker/0:0 complete execution of cpuset_hotplug_workfn().
kworker/0:0 ==> Task T ==>kworker/0:0
In case of PF_EXITING task being migrated from source to destination cgroup, migrate next available task in source cgroup.
--- kernel/cgroup/cgroup-v1.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
kernel/cgroup/cgroup-v1.c +5 -1
@@ -123,7 +123,11 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)*/ do { css_task_iter_start(&from->self, 0, &it); - task = css_task_iter_next(&it); + + do { + task = css_task_iter_next(&it); + } while (task && (task->flags & PF_EXITING)); + if (task) get_task_struct(task); css_task_iter_end(&it);
Signature · 2 lines
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH] cgroup: Fix deadlock in cpu hotplug path
Quoted 2 lines · Prateek Sood
Deadlock during cgroup migration from cpu hotplug path when a task T is being moved from source to destination cgroup.
...
Quoted 5 lines
Task T inserted a work item in the worklist of cpu0 low priority worker pool. It is waiting for expedited grace period work item to execute. This work item will only be executed once kworker/0:0 complete execution of
cpuset_hotplug_workfn().
Quoted 7 lines
kworker/0:0 ==> Task T ==>kworker/0:0
In case of
PF_EXITINGtask being migrated from source to destination cgroup, migrate next available task in source cgroup.Change-Id I8874fb04479c136cae4dabd5c168c7749df66664Signed-off-by Prateek Sood <[email protected]>
Applied to cgroup/for-4.15-fixes.
Thanks a lot, Prateek.
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
In reply to Peter ZijlstraHello, Peter.
Quoted 5 lines · Peter Zijlstra
Quoted 2 lines
AFAICS, this should remove the circular dependency you originally reported. I'll revert the two cpuset commits for now.
So I liked his patches in that we would be able to go back to synchronous
sched_domainbuilding.
Ah, yeah, that's a separate issue but didn't we intentionally make that asynchronous? IIRC, cpuset migration can take a really long time when the memory migration is turned on and doing that synchronously could mess up the system.
Thanks.
Signature · 1 line
tejun
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 9 lines · Tejun Heo
Hello, Peter.
Quoted 5 lines · Peter Zijlstra
Quoted 2 lines
AFAICS, this should remove the circular dependency you originally reported. I'll revert the two cpuset commits for now.
So I liked his patches in that we would be able to go back to synchronous
sched_domainbuilding.
Quoted 7 lines
Ah, yeah, that's a separate issue but didn't we intentionally make that asynchronous? IIRC, cpuset migration can take a really long time when the memory migration is turned on and doing that synchronously could mess up the system.
Thanks.
Hi TJ,
This change makes the usage of cpuset_hotplug_workfn() from cpu hotplug path synchronous. For memory hotplug it still remains asynchronous.
Memory migration happening from cpuset_hotplug_workfn() is already asynchronous by queuing cpuset_migrate_mm_workfn() in cpuset_migrate_mm_wq.
cpuset_hotplug_workfn()
cpuset_hotplug_workfn(()
cpuset_migrate_mm()
queue_work(cpuset_migrate_mm_wq)
It seems that memory migration latency might not have impact with this change.
Please let me know if you meant something else by cpuset migration taking time when memory migration is turned on.
Thanks
Signature · 3 lines
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Tejun Heo
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
Hello, Prateek.
Quoted 3 lines · Prateek Sood
This change makes the usage of
cpuset_hotplug_workfn()from cpu hotplug path synchronous. For memory hotplug it still remains asynchronous.
Ah, right.
Quoted 9 lines
Memory migration happening from
cpuset_hotplug_workfn()is already asynchronous by queuingcpuset_migrate_mm_workfn()incpuset_migrate_mm_wq.
cpuset_hotplug_workfn()
cpuset_hotplug_workfn(()
cpuset_migrate_mm()
queue_work(cpuset_migrate_mm_wq)
Quoted 5 lines
It seems that memory migration latency might not have impact with this change.
Please let me know if you meant something else by cpuset migration taking time when memory migration is turned on.
No, I didn't. I was just confused about which part became synchronous. So, I don't have anything against making the cpu part synchronous, but let's not do that as the fix to the deadlocks cuz, while we can avoid them by changing cpuset, I don't think cpuset is the root cause for them. If there are benefits to making cpuset cpu migration synchronous, let's do that for those benefits.
Thanks.
Signature · 1 line
tejun
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 24 lines · Tejun Heo
Hello, Prateek.
Quoted 3 lines · Prateek Sood
This change makes the usage of
cpuset_hotplug_workfn()from cpu hotplug path synchronous. For memory hotplug it still remains asynchronous.Ah, right.
Quoted 9 lines
Memory migration happening from
cpuset_hotplug_workfn()is already asynchronous by queuingcpuset_migrate_mm_workfn()incpuset_migrate_mm_wq.
cpuset_hotplug_workfn()
cpuset_hotplug_workfn(()
cpuset_migrate_mm()
queue_work(cpuset_migrate_mm_wq)Quoted 5 lines
It seems that memory migration latency might not have impact with this change.
Please let me know if you meant something else by cpuset migration taking time when memory migration is turned on.
Quoted 6 lines
No, I didn't. I was just confused about which part became synchronous. So, I don't have anything against making the cpu part synchronous, but let's not do that as the fix to the deadlocks cuz, while we can avoid them by changing cpuset, I don't think cpuset is the root cause for them. If there are benefits to making cpuset cpu migration synchronous, let's do that for those benefits.
Making CPU part synchronous can only be achieved if we retain the patch for inverting the locking order for cpuset_mutex and cpu_hotplug_lock.
Quoted 3 lines
Thanks.
Peter,
Do you suggest taking both the patches:
1) Inverting locking order of cpuset_mutex and cpu_hotplug_lock.
2) Make cpuset hotplug work synchronous.
or
I would leave it for you and TJ to decide on this.
Thanks
Signature · 3 lines
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
In reply to Tejun HeoQuoted 24 lines · Tejun Heo
Hello, Prateek.
Quoted 3 lines · Prateek Sood
This change makes the usage of
cpuset_hotplug_workfn()from cpu hotplug path synchronous. For memory hotplug it still remains asynchronous.Ah, right.
Quoted 9 lines
Memory migration happening from
cpuset_hotplug_workfn()is already asynchronous by queuingcpuset_migrate_mm_workfn()incpuset_migrate_mm_wq.
cpuset_hotplug_workfn()
cpuset_hotplug_workfn(()
cpuset_migrate_mm()
queue_work(cpuset_migrate_mm_wq)Quoted 5 lines
It seems that memory migration latency might not have impact with this change.
Please let me know if you meant something else by cpuset migration taking time when memory migration is turned on.
Quoted 9 lines
No, I didn't. I was just confused about which part became synchronous. So, I don't have anything against making the cpu part synchronous, but let's not do that as the fix to the deadlocks cuz, while we can avoid them by changing cpuset, I don't think cpuset is the root cause for them. If there are benefits to making cpuset cpu migration synchronous, let's do that for those benefits.
Thanks.
TJ,
One more deadlock scenario
Task: sh
[<ffffff874f917290>] wait_for_completion+0x14 [<ffffff874e8a82e0>] cpuhp_kick_ap_work+0x80 //waiting for cpuhp/2 [<ffffff874f913780>] _cpu_down+0xe0 [<ffffff874e8a9934>] cpu_down+0x38
[<ffffff874ef6e4ec>] cpu_subsys_offline+0x10
Task: cpuhp/2
[<ffffff874f91645c>] schedule+0x38 [<ffffff874e92c76c>] _synchronize_rcu_expedited+0x2ec [<ffffff874e92c874>] synchronize_sched_expedited+0x60 [<ffffff874e92c9f8>] synchronize_sched+0xb0 [<ffffff874e9104e4>] sugov_stop+0x58 [<ffffff874f36967c>] cpufreq_stop_governor+0x48 [<ffffff874f36a89c>] cpufreq_offline+0x84 [<ffffff874f36aa30>] cpuhp_cpufreq_offline+0xc [<ffffff874e8a797c>] cpuhp_invoke_callback+0xac [<ffffff874e8a89b4>] cpuhp_down_callbacks+0x58 [<ffffff874e8a95e8>] cpuhp_thread_fun+0xa8
_synchronize_rcu_expedited is waiting for execution of rcu expedited grace period work item wait_rcu_exp_gp()
Task: kworker/2:1
[<ffffff874f91645c>] schedule+0x38
[<ffffff874f916870>] schedule_preempt_disabled+0x20 [<ffffff874f918df8>] __mutex_lock_slowpath+0x158 [<ffffff874f919004>] mutex_lock+0x14
[<ffffff874e8a77b0>] get_online_cpus+0x34 //waiting for cpu_hotplug_lock [<ffffff874e96452c>] rebuild_sched_domains+0x30 [<ffffff874e964648>] cpuset_hotplug_workfn+0xb8 [<ffffff874e8c27b8>] process_one_work+0x168 [<ffffff874e8c2ff4>] worker_thread+0x140 [<ffffff874e8c95b8>] kthread+0xe0
cpu_hotplug_lock is acquired by task: sh
Task: kworker/2:3
[<ffffff874f91645c>] schedule+0x38
[<ffffff874f91a384>] schedule_timeout+0x1d8
[<ffffff874f9171d4>] wait_for_common+0xb4
[<ffffff874f917304>] wait_for_completion_killable+0x14 //waiting for kthreadd [<ffffff874e8c931c>] __kthread_create_on_node+0xec [<ffffff874e8c9448>] kthread_create_on_node+0x64 [<ffffff874e8c2d88>] create_worker+0xb4
[<ffffff874e8c3194>] worker_thread+0x2e0
[<ffffff874e8c95b8>] kthread+0xe0
Task: kthreadd
[<ffffff874e8858ec>] __switch_to+0x94 [<ffffff874f915ed8>] __schedule+0x2a8 [<ffffff874f91645c>] schedule+0x38 [<ffffff874f91a0ec>] rwsem_down_read_failed+0xe8 [<ffffff874e913dc4>] __percpu_down_read+0xfc [<ffffff874e8a4ab0>] copy_process.isra.72.part.73+0xf60 [<ffffff874e8a53b8>] _do_fork+0xc4 [<ffffff874e8a5720>] kernel_thread+0x34 [<ffffff874e8ca83c>] kthreadd+0x144
kthreadd is waiting for cgroup_threadgroup_rwsem acquired by task T
Task: T
[<ffffff874f91645c>] schedule+0x38 [<ffffff874f916870>] schedule_preempt_disabled+0x20 [<ffffff874f918df8>] __mutex_lock_slowpath+0x158 [<ffffff874f919004>] mutex_lock+0x14 [<ffffff874e962ff4>] cpuset_can_attach+0x58 [<ffffff874e95d640>] cgroup_taskset_migrate+0x8c [<ffffff874e95d9b4>] cgroup_migrate+0xa4 [<ffffff874e95daf0>] cgroup_attach_task+0x100 [<ffffff874e95df28>] __cgroup_procs_write.isra.35+0x228 [<ffffff874e95e00c>] cgroup_tasks_write+0x10 [<ffffff874e958294>] cgroup_file_write+0x44 [<ffffff874eaa4384>] kernfs_fop_write+0xc0
task T is waiting for cpuset_mutex acquired
by kworker/2:1
sh ==> cpuhp/2 ==> kworker/2:1 ==> sh
kworker/2:3 ==> kthreadd ==> Task T ==> kworker/2:1
It seems that my earlier patch set should fix this scenario:
1) Inverting locking order of cpuset_mutex and cpu_hotplug_lock.
2) Make cpuset hotplug work synchronous.
Could you please share your feedback.
Thanks
Signature · 3 lines
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Tejun Heo
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
Hello,
Quoted 5 lines · Prateek Sood
task T is waiting for
cpuset_mutexacquired
by kworker/2:1sh ==> cpuhp/2 ==> kworker/2:1 ==> sh
Quoted 7 lines
kworker/2:3 ==> kthreadd ==> Task T ==> kworker/2:1
It seems that my earlier patch set should fix this scenario:
1) Inverting locking order ofcpuset_mutexandcpu_hotplug_lock.
2) Make cpuset hotplug work synchronous.Could you please share your feedback.
Hmm... this can also be resolved by adding WQ_MEM_RECLAIM to the synchronize rcu workqueue, right? Given the wide-spread usages of synchronize_rcu and friends, maybe that's the right solution, or at least something we also need to do, for this particular deadlock?
Again, I don't have anything against making the domain rebuliding part of cpuset operations synchronous and these tricky deadlock scenarios do indicate that doing so would probably be beneficial. That said, tho, these scenarios seem more of manifestations of other problems exposed through kthreadd dependency than anything else.
Thanks.
Signature · 1 line
tejun
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 14 lines · Tejun Heo
Hello,
Quoted 11 lines · Prateek Sood
task T is waiting for
cpuset_mutexacquired
by kworker/2:1sh ==> cpuhp/2 ==> kworker/2:1 ==> sh
kworker/2:3 ==> kthreadd ==> Task T ==> kworker/2:1
It seems that my earlier patch set should fix this scenario:
1) Inverting locking order ofcpuset_mutexandcpu_hotplug_lock.
2) Make cpuset hotplug work synchronous.
Quoted 6 lines
Quoted 1 line
Could you please share your feedback.
Hmm... this can also be resolved by adding
WQ_MEM_RECLAIMto the synchronize rcu workqueue, right? Given the wide-spread usages ofsynchronize_rcuand friends, maybe that's the right solution, or at least something we also need to do, for this particular deadlock?
To make WQ_MEM_RECLAIM work, I need to dynamically allocate RCU's workqueues, correct? Or is there some way to mark a statically allocated workqueue as WQ_MEM_RECLAIM after the fact?
I can dynamically allocate them, but I need to carefully investigate boot-time use. So if it is possible to be lazy, I do want to take the easy way out. ;-)
Thanx, Paul
Quoted 11 lines
Again, I don't have anything against making the domain rebuliding part of cpuset operations synchronous and these tricky deadlock scenarios do indicate that doing so would probably be beneficial. That said, tho, these scenarios seem more of manifestations of other problems exposed through kthreadd dependency than anything else.
Thanks.
Signature · 1 line
tejun
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 22 lines · Paul E. McKenney
Quoted 14 lines · Tejun Heo
Hello,
Quoted 11 lines · Prateek Sood
task T is waiting for
cpuset_mutexacquired
by kworker/2:1sh ==> cpuhp/2 ==> kworker/2:1 ==> sh
kworker/2:3 ==> kthreadd ==> Task T ==> kworker/2:1
It seems that my earlier patch set should fix this scenario:
1) Inverting locking order ofcpuset_mutexandcpu_hotplug_lock.
2) Make cpuset hotplug work synchronous.Quoted 6 lines
Quoted 1 line
Could you please share your feedback.
Hmm... this can also be resolved by adding
WQ_MEM_RECLAIMto the synchronize rcu workqueue, right? Given the wide-spread usages ofsynchronize_rcuand friends, maybe that's the right solution, or at least something we also need to do, for this particular deadlock?
Quoted 7 lines
To make
WQ_MEM_RECLAIMwork, I need to dynamically allocate RCU's workqueues, correct? Or is there some way to mark a statically allocated workqueue asWQ_MEM_RECLAIMafter the fact?I can dynamically allocate them, but I need to carefully investigate boot-time use. So if it is possible to be lazy, I do want to take the easy way out. ;-)
Actually, after taking a quick look, could you please supply me with a way of mark a statically allocated workqueue as WQ_MEM_RECLAIM after the fact? Otherwise, I end up having to check for the workqueue having been allocated pretty much each time I use it, which is going to be an open invitation for bugs. Plus it looks like there are ways that RCU's workqueue wakeups can be executed during very early boot, which can be handled, but again in a rather messy fashion.
In contrast, given a way of mark a statically allocated workqueue as WQ_MEM_RECLAIM after the fact, I simply continue initializing the workqueue at early boot, and then add the WQ_MEM_RECLAIM marking some arbitrarily chosen time after the scheduler has been initialized.
The required change to workqueues looks easy, just move the body of the "if (flags & WQ_MEM_RECLAIM) {" statement in __alloc_workqueue_key() to a separate function, right?
Thanx, Paul
Tejun Heo
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
Hello, Paul.
Sorry about the delay. Travel followed by cold. :(
Quoted 3 lines · Paul E. McKenney
Actually, after taking a quick look, could you please supply me with a way of mark a statically allocated workqueue as
WQ_MEM_RECLAIMafter the fact? Otherwise, I end up having to check for the workqueue having
Hmmm... there is no statically allocated workqueue tho. If you're referring to the system-wide workqueues (system*_wq), they're just created dynamically early during boot.
Quoted 5 lines
been allocated pretty much each time I use it, which is going to be an open invitation for bugs. Plus it looks like there are ways that RCU's workqueue wakeups can be executed during very early boot, which can be handled, but again in a rather messy fashion.
Quoted 8 lines
In contrast, given a way of mark a statically allocated workqueue as
WQ_MEM_RECLAIMafter the fact, I simply continue initializing the workqueue at early boot, and then add theWQ_MEM_RECLAIMmarking some arbitrarily chosen time after the scheduler has been initialized.The required change to workqueues looks easy, just move the body of the "if (flags &
WQ_MEM_RECLAIM) {" statement in__alloc_workqueue_key()to a separate function, right?
Ah, okay, yes, currently, workqueue init is kinda silly in that while it allows init of non-mem-reclaiming workqueues way before workqueue is actually online, it doesn't allow the same for mem-reclaiming ones. As you pointed out, it's just an oversight on my part as the init path split was done initially to accomodate early init of system workqueues.
I'll update the code so that rescuers can be added later too; however, please note that while the work items may be queued, they won't be executed until workqueue_init() is run (the same as now) as there can't be worker threads anyway before that point.
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerPATCH 1/2 workqueue: separate out init_rescuer()
Hello, Paul.
Applied the following two patches to allow early init of WQ_MEM_RECLAIM workqueues to the following git branch.
git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git for-4.16
The branch won't be rebased and currently only contains these two patches. Please feel free to pull and put further RCU changes on top.
Thanks.
------ 8< ------
From 983c751532738b83c4c5b51192ebc6fbfe9330f7 Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Mon, 8 Jan 2018 05:38:32 -0800
Separate out init_rescuer() from __alloc_workqueue_key() to prepare for early init support for WQ_MEM_RECLAIM. This patch doesn't introduce any functional changes.
--- kernel/workqueue.c | 56 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 23 deletions(-)
kernel/workqueue.c +33 -23
@@ -3939,6 +3939,37 @@ static int wq_clamp_max_active(int max_active, unsigned int flags,return clamp_val (max_active,1 , lim); } +/* +* Workqueues which may be used during memory reclaim should have a rescuer +* to guarantee forward progress. +*/ +static int init_rescuer (struct workqueue_struct *wq) +{ +struct worker *rescuer; +int ret; + +if (!(wq->flags &WQ_MEM_RECLAIM )) +return 0 ; + + rescuer =alloc_worker (NUMA_NO_NODE ); +if (!rescuer) +return -ENOMEM ; + + rescuer->rescue_wq = wq; + rescuer->task =kthread_create (rescuer_thread, rescuer,"%s" , wq->name); + ret =PTR_ERR_OR_ZERO (rescuer->task); +if (ret) { +kfree (rescuer); +return ret; + } + + wq->rescuer = rescuer; +kthread_bind_mask (rescuer->task, cpu_possible_mask); +wake_up_process (rescuer->task); + +return 0 ; +} + struct workqueue_struct *__alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,
@@ -4001,29 +4032,8 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt,if (alloc_and_link_pwqs (wq) <0 ) goto err_free_wq; -/* -* Workqueues which may be used during memory reclaim should -* have a rescuer to guarantee forward progress. -*/ -if (flags &WQ_MEM_RECLAIM ) { -struct worker *rescuer; - - rescuer =alloc_worker (NUMA_NO_NODE ); -if (!rescuer) - goto err_destroy; - - rescuer->rescue_wq = wq; - rescuer->task =kthread_create (rescuer_thread, rescuer,"%s" , - wq->name); -if (IS_ERR (rescuer->task)) { -kfree (rescuer); - goto err_destroy; - } - - wq->rescuer = rescuer; -kthread_bind_mask (rescuer->task, cpu_possible_mask); -wake_up_process (rescuer->task); - } +if (init_rescuer (wq) <0 ) + goto err_destroy;if ((wq->flags &WQ_SYSFS ) &&workqueue_sysfs_register (wq)) goto err_destroy;
Signature · 1 line
2.9.5
Tejun Heo
maintainerPATCH 2/2 workqueue: allow WQ_MEM_RECLAIM on early init workqueues
From 40c17f75dfa9ec163478efd3f7443fd6af9992c9 Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Mon, 8 Jan 2018 05:38:37 -0800
Workqueues can be created early during boot before workqueue subsystem in fully online - work items are queued waiting for later full initialization. However, early init wasn't supported for WQ_MEM_RECLAIM workqueues causing unnecessary annoyances for a subset of users. Expand early init support to include WQ_MEM_RECLAIM workqueues.
--- kernel/workqueue.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
kernel/workqueue.c +8 -2
@@ -4032,7 +4032,7 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt,if (alloc_and_link_pwqs (wq) <0 ) goto err_free_wq; -if (init_rescuer (wq) <0 ) +if (wq_online &&init_rescuer (wq) <0 ) goto err_destroy;if ((wq->flags &WQ_SYSFS ) &&workqueue_sysfs_register (wq))
@@ -5639,6 +5639,8 @@ int __init workqueue_init(void)* archs such as power and arm64. As per-cpu pools created * previously could be missing node hint and unbound pools NUMA * affinity, fix them up. +* +* Also, while iterating workqueues, create rescuers if requested. */ wq_numa_init ();
@@ -5650,8 +5652,12 @@ int __init workqueue_init(void) } } -list_for_each_entry (wq, &workqueues, list) +list_for_each_entry (wq, &workqueues, list) {wq_update_unbound_numa (wq,smp_processor_id (), true); +WARN (init_rescuer (wq), +"workqueue: failed to create early rescuer for %s" , + wq->name); + }mutex_unlock (&wq_pool_mutex);
Signature · 1 line
2.9.5
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
In reply to Tejun HeoQuoted 4 lines · Tejun Heo
Hello, Paul.
Sorry about the delay. Travel followed by cold. :(
Quoted 8 lines
Quoted 3 lines · Paul E. McKenney
Actually, after taking a quick look, could you please supply me with a way of mark a statically allocated workqueue as
WQ_MEM_RECLAIMafter the fact? Otherwise, I end up having to check for the workqueue havingHmmm... there is no statically allocated workqueue tho. If you're referring to the system-wide workqueues (system*_wq), they're just created dynamically early during boot.
Good point, I was confused. But yes, they are conveniently allocated just before the call to rcu_init(), which does work out well. ;-)
Quoted 21 lines
Quoted 5 lines
been allocated pretty much each time I use it, which is going to be an open invitation for bugs. Plus it looks like there are ways that RCU's workqueue wakeups can be executed during very early boot, which can be handled, but again in a rather messy fashion.
Quoted 8 lines
In contrast, given a way of mark a statically allocated workqueue as
WQ_MEM_RECLAIMafter the fact, I simply continue initializing the workqueue at early boot, and then add theWQ_MEM_RECLAIMmarking some arbitrarily chosen time after the scheduler has been initialized.The required change to workqueues looks easy, just move the body of the "if (flags &
WQ_MEM_RECLAIM) {" statement in__alloc_workqueue_key()to a separate function, right?Ah, okay, yes, currently, workqueue init is kinda silly in that while it allows init of non-mem-reclaiming workqueues way before workqueue is actually online, it doesn't allow the same for mem-reclaiming ones. As you pointed out, it's just an oversight on my part as the init path split was done initially to accomodate early init of system workqueues.
Quoted 4 lines
I'll update the code so that rescuers can be added later too; however, please note that while the work items may be queued, they won't be executed until
workqueue_init()is run (the same as now) as there can't be worker threads anyway before that point.
Thank you! I added the following patch to allow RCU access to the init_rescuer() function. Does that work for you, or did you have some other arrangement in mind?
Thanx, Paul
------------------------------------------------------------------------
commit 66683a07503d71e5d5cceac72caf772e6e59c787
Author: Paul E. McKenney <[email protected]> Date: Mon Jan 8 14:27:46 2018 -0800
workqueue: Allow init_rescuer() to be invoked from other files
This commit exports init_rescuer() so that RCU can invoke it.
include/linux/workqueue.h +2 -0
@@ -433,6 +433,8 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,#define create_singlethread_workqueue (name) \ alloc_ordered_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, name) +int init_rescuer (struct workqueue_struct *wq); +extern void destroy_workqueue (struct workqueue_struct *wq);struct workqueue_attrs *alloc_workqueue_attrs (gfp_t gfp_mask);
kernel/workqueue.c +1 -1
@@ -3943,7 +3943,7 @@ static int wq_clamp_max_active(int max_active, unsigned int flags,* Workqueues which may be used during memory reclaim should have a rescuer * to guarantee forward progress. */ -static int init_rescuer (struct workqueue_struct *wq) +int init_rescuer (struct workqueue_struct *wq) {struct worker *rescuer;int ret;
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 38 lines · Paul E. McKenney
Quoted 4 lines · Tejun Heo
Hello, Paul.
Sorry about the delay. Travel followed by cold. :(
Quoted 8 lines
Quoted 3 lines · Paul E. McKenney
Actually, after taking a quick look, could you please supply me with a way of mark a statically allocated workqueue as
WQ_MEM_RECLAIMafter the fact? Otherwise, I end up having to check for the workqueue havingHmmm... there is no statically allocated workqueue tho. If you're referring to the system-wide workqueues (system*_wq), they're just created dynamically early during boot.
Good point, I was confused. But yes, they are conveniently allocated just before the call to
rcu_init(), which does work out well. ;-)Quoted 21 lines
Quoted 5 lines
been allocated pretty much each time I use it, which is going to be an open invitation for bugs. Plus it looks like there are ways that RCU's workqueue wakeups can be executed during very early boot, which can be handled, but again in a rather messy fashion.
Quoted 8 lines
In contrast, given a way of mark a statically allocated workqueue as
WQ_MEM_RECLAIMafter the fact, I simply continue initializing the workqueue at early boot, and then add theWQ_MEM_RECLAIMmarking some arbitrarily chosen time after the scheduler has been initialized.The required change to workqueues looks easy, just move the body of the "if (flags &
WQ_MEM_RECLAIM) {" statement in__alloc_workqueue_key()to a separate function, right?Ah, okay, yes, currently, workqueue init is kinda silly in that while it allows init of non-mem-reclaiming workqueues way before workqueue is actually online, it doesn't allow the same for mem-reclaiming ones. As you pointed out, it's just an oversight on my part as the init path split was done initially to accomodate early init of system workqueues.
Quoted 8 lines
Quoted 4 lines
I'll update the code so that rescuers can be added later too; however, please note that while the work items may be queued, they won't be executed until
workqueue_init()is run (the same as now) as there can't be worker threads anyway before that point.Thank you! I added the following patch to allow RCU access to the
init_rescuer()function. Does that work for you, or did you have some other arrangement in mind?
And here are the corresponding changes to RCU, which pass light rcutorture testing.
Thanx, Paul
------------------------------------------------------------------------
commit d0d6626927faf3421df6a1db875ad7099f7d49cd
Author: Paul E. McKenney <[email protected]> Date: Mon Jan 8 14:35:52 2018 -0800
rcu: Create RCU-specific workqueues with rescuers
RCU's expedited grace periods can participate in out-of-memory deadlocks due to all available system_wq kthreads being blocked and there not being memory available to create more. This commit prevents such deadlocks by allocating an RCU-specific workqueue_struct at early boot time, and providing it with a rescuer to ensure forward progress. This uses the shiny new init_rescuer() function provided by Tejun.
This commit also causes SRCU to use this new RCU-specific workqueue_struct. Note that SRCU's use of workqueues never blocks them waiting for readers, so this should be safe from a forward-progress viewpoint.
kernel/rcu/rcu.h +1 -0
@@ -493,6 +493,7 @@ void show_rcu_gp_kthreads(void);void rcu_force_quiescent_state (void );void rcu_bh_force_quiescent_state (void );void rcu_sched_force_quiescent_state (void ); +extern struct workqueue_struct *rcu_gp_workqueue;#endif /* #else #ifdef CONFIG_TINY_RCU */ #ifdef CONFIG_RCU_NOCB_CPU
kernel/rcu/srcutree.c +3 -3
@@ -465,7 +465,7 @@ static bool srcu_queue_delayed_work_on(int cpu, struct workqueue_struct *wq,*/ static void srcu_schedule_cbs_sdp (struct srcu_data *sdp,unsigned long delay) { -srcu_queue_delayed_work_on (sdp->cpu, system_power_efficient_wq, +srcu_queue_delayed_work_on (sdp->cpu, rcu_gp_workqueue, &sdp->work, delay); }
@@ -664,7 +664,7 @@ static void srcu_funnel_gp_start(struct srcu_struct *sp, struct srcu_data *sdp, rcu_seq_state(sp->srcu_gp_seq) == SRCU_STATE_IDLE) {WARN_ON_ONCE (ULONG_CMP_GE (sp->srcu_gp_seq, sp->srcu_gp_seq_needed));srcu_gp_start (sp); -queue_delayed_work (system_power_efficient_wq, &sp->work, +queue_delayed_work (rcu_gp_workqueue, &sp->work,srcu_get_delay (sp)); }raw_spin_unlock_irqrestore_rcu_node (sp, flags);
@@ -1198,7 +1198,7 @@ static void srcu_reschedule(struct srcu_struct *sp, unsigned long delay)raw_spin_unlock_irq_rcu_node (sp);if (pushgp) -queue_delayed_work (system_power_efficient_wq, &sp->work, delay); +queue_delayed_work (rcu_gp_workqueue, &sp->work, delay); }/*
kernel/rcu/tree.c +13 -0
@@ -4272,6 +4272,15 @@ static void __init rcu_dump_rcu_node_tree(struct rcu_state *rsp)pr_cont ("\n" ); } +struct workqueue_struct *rcu_gp_workqueue; + +static int __init rcu_init_wq_rescuer (void ) +{ +WARN_ON (init_rescuer (rcu_gp_workqueue)); +return 0 ; +} +core_initcall (rcu_init_wq_rescuer); +void __init rcu_init (void ) {int cpu;
@@ -4298,6 +4307,10 @@ void __init rcu_init(void)rcu_cpu_starting (cpu);rcutree_online_cpu (cpu); } + +/* Create workqueue for expedited GPs and for Tree SRCU. */ + rcu_gp_workqueue =alloc_workqueue ("rcu_gp" ,0 ,0 ); +WARN_ON (!rcu_gp_workqueue); }#include "tree_exp.h"
kernel/rcu/tree_exp.h +1 -1
@@ -606,7 +606,7 @@ static void _synchronize_rcu_expedited(struct rcu_state *rsp, rew.rew_rsp = rsp; rew.rew_s = s;INIT_WORK_ONSTACK (&rew.rew_work, wait_rcu_exp_gp); -schedule_work (&rew.rew_work); +queue_work (rcu_gp_workqueue, &rew.rew_work); }/* Wait for expedited grace period to complete. */
Tejun Heo
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
Hello, Paul.
kernel/rcu/tree.c static void __init rcu_dump_rcu_node_tree(struct rcu_state *rsp) · line 4282
Quoted 6 lines · Paul E. McKenney
kernel/rcu/tree.c +6
-0+static int __init rcu_init_wq_rescuer (void ) +{ +WARN_ON (init_rescuer (rcu_gp_workqueue)); +return 0 ; +} +core_initcall (rcu_init_wq_rescuer);
So, what I don't get is why RCU needs to call this explicitly. core_initcall() is after workqueue_init() anyway. Why am I missing?
Thanks.
Signature · 1 line
tejun
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 12 lines · Tejun Heo
Hello, Paul.
Review
Quoted 6 lines · Paul E. McKenney
(quoted diff) +6
-0+static int __init rcu_init_wq_rescuer(void) +{ + WARN_ON(init_rescuer(rcu_gp_workqueue)); + return 0; +} +core_initcall(rcu_init_wq_rescuer);So, what I don't get is why RCU needs to call this explicitly.
core_initcall()is afterworkqueue_init()anyway. Why am I missing?
Me being stupid, I guess.
OK, so I can put WQ_MEM_RECLAIM on the early boot creation of RCU's workqueue_struct as shown below, right?
Thanx, Paul
------------------------------------------------------------------------
commit 9884a945a65837cda6de2ff621d47c59a6ca3e28
Author: Paul E. McKenney <[email protected]> Date: Mon Jan 8 14:35:52 2018 -0800
rcu: Create RCU-specific workqueues with rescuers
RCU's expedited grace periods can participate in out-of-memory deadlocks due to all available system_wq kthreads being blocked and there not being memory available to create more. This commit prevents such deadlocks by allocating an RCU-specific workqueue_struct at early boot time, and providing it with a rescuer to ensure forward progress. This uses the shiny new init_rescuer() function provided by Tejun (but indirectly).
This commit also causes SRCU to use this new RCU-specific workqueue_struct. Note that SRCU's use of workqueues never blocks them waiting for readers, so this should be safe from a forward-progress viewpoint.
kernel/rcu/rcu.h +1 -0
@@ -493,6 +493,7 @@ void show_rcu_gp_kthreads(void);void rcu_force_quiescent_state (void );void rcu_bh_force_quiescent_state (void );void rcu_sched_force_quiescent_state (void ); +extern struct workqueue_struct *rcu_gp_workqueue;#endif /* #else #ifdef CONFIG_TINY_RCU */ #ifdef CONFIG_RCU_NOCB_CPU
kernel/rcu/srcutree.c +3 -3
@@ -465,7 +465,7 @@ static bool srcu_queue_delayed_work_on(int cpu, struct workqueue_struct *wq,*/ static void srcu_schedule_cbs_sdp (struct srcu_data *sdp,unsigned long delay) { -srcu_queue_delayed_work_on (sdp->cpu, system_power_efficient_wq, +srcu_queue_delayed_work_on (sdp->cpu, rcu_gp_workqueue, &sdp->work, delay); }
@@ -664,7 +664,7 @@ static void srcu_funnel_gp_start(struct srcu_struct *sp, struct srcu_data *sdp, rcu_seq_state(sp->srcu_gp_seq) == SRCU_STATE_IDLE) {WARN_ON_ONCE (ULONG_CMP_GE (sp->srcu_gp_seq, sp->srcu_gp_seq_needed));srcu_gp_start (sp); -queue_delayed_work (system_power_efficient_wq, &sp->work, +queue_delayed_work (rcu_gp_workqueue, &sp->work,srcu_get_delay (sp)); }raw_spin_unlock_irqrestore_rcu_node (sp, flags);
@@ -1198,7 +1198,7 @@ static void srcu_reschedule(struct srcu_struct *sp, unsigned long delay)raw_spin_unlock_irq_rcu_node (sp);if (pushgp) -queue_delayed_work (system_power_efficient_wq, &sp->work, delay); +queue_delayed_work (rcu_gp_workqueue, &sp->work, delay); }/*
kernel/rcu/tree.c +6 -0
@@ -4272,6 +4272,8 @@ static void __init rcu_dump_rcu_node_tree(struct rcu_state *rsp)pr_cont ("\n" ); } +struct workqueue_struct *rcu_gp_workqueue; +void __init rcu_init (void ) {int cpu;
@@ -4298,6 +4300,10 @@ void __init rcu_init(void)rcu_cpu_starting (cpu);rcutree_online_cpu (cpu); } + +/* Create workqueue for expedited GPs and for Tree SRCU. */ + rcu_gp_workqueue =alloc_workqueue ("rcu_gp" ,WQ_MEM_RECLAIM ,0 ); +WARN_ON (!rcu_gp_workqueue); }#include "tree_exp.h"
kernel/rcu/tree_exp.h +1 -1
@@ -606,7 +606,7 @@ static void _synchronize_rcu_expedited(struct rcu_state *rsp, rew.rew_rsp = rsp; rew.rew_s = s;INIT_WORK_ONSTACK (&rew.rew_work, wait_rcu_exp_gp); -schedule_work (&rew.rew_work); +queue_work (rcu_gp_workqueue, &rew.rew_work); }/* Wait for expedited grace period to complete. */
Tejun Heo
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
Hello, Paul.
Quoted 2 lines · Paul E. McKenney
OK, so I can put
WQ_MEM_RECLAIMon the early boot creation of RCU'sworkqueue_structas shown below, right?
Yes, this looks good to me. Just one question.
kernel/rcu/tree.c void __init rcu_init(void) · line 4306
Quoted 13 lines
kernel/rcu/tree.c +6
-0+struct workqueue_struct *rcu_gp_workqueue; +void __init rcu_init (void ) {int cpu;@@ -4298,6 +4300,10 @@ void __init rcu_init(void)rcu_cpu_starting (cpu);rcutree_online_cpu (cpu); } + +/* Create workqueue for expedited GPs and for Tree SRCU. */ + rcu_gp_workqueue =alloc_workqueue ("rcu_gp" ,WQ_MEM_RECLAIM ,0 ); +WARN_ON (!rcu_gp_workqueue);
The code was previously using both system_power_efficient_wq and system_workqueue (for the expedited path). I guess the options were either using two workqueues or dropping POWER_EFFICIENT. I have no idea how big an impact this will make or whether it'd even be noticeable but maybe it'd be worthwhile to mention that in the description?
Thanks.
Signature · 1 line
tejun
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 20 lines · Tejun Heo
Hello, Paul.
Quoted 2 lines · Paul E. McKenney
OK, so I can put
WQ_MEM_RECLAIMon the early boot creation of RCU'sworkqueue_structas shown below, right?Yes, this looks good to me. Just one question.
Quoted 12 lines
kernel/rcu/tree.c +5
-0+struct workqueue_struct *rcu_gp_workqueue; +void __init rcu_init (void ) {int cpu;@@ -4298,6 +4300,10 @@ void __init rcu_init(void)rcu_cpu_starting (cpu);rcutree_online_cpu (cpu); } + +/* Create workqueue for expedited GPs and for Tree SRCU. */ + rcu_gp_workqueue =alloc_workqueue ("rcu_gp" ,WQ_MEM_RECLAIM ,0 );
Quoted 8 lines
kernel/rcu/tree.c void __init rcu_init(void) · line 4306
Quoted 1 line
kernel/rcu/tree.c +1
-0+WARN_ON (!rcu_gp_workqueue);The code was previously using both
system_power_efficient_wqandsystem_workqueue(for the expedited path). I guess the options were either using two workqueues or droppingPOWER_EFFICIENT. I have no idea how big an impact this will make or whether it'd even be noticeable but maybe it'd be worthwhile to mention that in the description?
Good point! How about if I change the last paragraph of the commit log to read as follows?
Thanx, Paul
------------------------------------------------------------------------
This commit also causes SRCU to use this new RCU-specific workqueue_struct. Note that SRCU's use of workqueues never blocks them waiting for readers, so this should be safe from a forward-progress viewpoint. Note that this moves SRCU from system_power_efficient_wq to a normal workqueue. In the unlikely event that this results in measurable degradation, a separate power-efficient workqueue will be creates for SRCU.
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Hello, Paul.
Quoted 14 lines · Paul E. McKenney
Quoted 6 lines
The code was previously using both
system_power_efficient_wqandsystem_workqueue(for the expedited path). I guess the options were either using two workqueues or droppingPOWER_EFFICIENT. I have no idea how big an impact this will make or whether it'd even be noticeable but maybe it'd be worthwhile to mention that in the description?Good point! How about if I change the last paragraph of the commit log to read as follows?
Thanx, Paul
------------------------------------------------------------------------
Quoted 7 lines
This commit also causes SRCU to use this new RCU-specific
workqueue_struct. Note that SRCU's use of workqueues never blocks them waiting for readers, so this should be safe from a forward-progress viewpoint. Note that this moves SRCU fromsystem_power_efficient_wqto a normal workqueue. In the unlikely event that this results in measurable degradation, a separate power-efficient workqueue will be creates for SRCU.
Sounds good. Please feel free to add
Thanks.
Signature · 1 line
tejun
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 25 lines · Tejun Heo
Hello, Paul.
Quoted 14 lines · Paul E. McKenney
Quoted 6 lines
The code was previously using both
system_power_efficient_wqandsystem_workqueue(for the expedited path). I guess the options were either using two workqueues or droppingPOWER_EFFICIENT. I have no idea how big an impact this will make or whether it'd even be noticeable but maybe it'd be worthwhile to mention that in the description?Good point! How about if I change the last paragraph of the commit log to read as follows?
Thanx, Paul
------------------------------------------------------------------------
Quoted 7 lines
This commit also causes SRCU to use this new RCU-specific
workqueue_struct. Note that SRCU's use of workqueues never blocks them waiting for readers, so this should be safe from a forward-progress viewpoint. Note that this moves SRCU fromsystem_power_efficient_wqto a normal workqueue. In the unlikely event that this results in measurable degradation, a separate power-efficient workqueue will be creates for SRCU.
Quoted 3 lines
Sounds good. Please feel free to add
Acked-by Tejun Heo <[email protected]>
Done, thank you!
Thanx, Paul
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 26 lines · Paul E. McKenney
Quoted 25 lines · Tejun Heo
Hello, Paul.
Quoted 21 lines · Paul E. McKenney
Quoted 6 lines
The code was previously using both
system_power_efficient_wqandsystem_workqueue(for the expedited path). I guess the options were either using two workqueues or droppingPOWER_EFFICIENT. I have no idea how big an impact this will make or whether it'd even be noticeable but maybe it'd be worthwhile to mention that in the description?Good point! How about if I change the last paragraph of the commit log to read as follows?
Thanx, Paul
------------------------------------------------------------------------
This commit also causes SRCU to use this new RCU-specific
workqueue_struct. Note that SRCU's use of workqueues never blocks them waiting for readers, so this should be safe from a forward-progress viewpoint. Note that this moves SRCU fromsystem_power_efficient_wqto a normal workqueue. In the unlikely event that this results in measurable degradation, a separate power-efficient workqueue will be creates for SRCU.
Quoted 5 lines
Quoted 3 lines
Sounds good. Please feel free to add
Acked-by Tejun Heo <[email protected]>Done, thank you!
And one additional question... How are we pushing this upstream? By default, I would push things starting this late into the merge window following the next one (v4.17), but would be more than willing to make an exception given that this fixes a valid real-world complaint.
For that matter, if you would rather push my commit along with your pair of commits, that works for me! Either way, please just let me know.
Thanx, Paul
Tejun Heo
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
Hello, Paul.
Quoted 4 lines · Paul E. McKenney
And one additional question... How are we pushing this upstream? By default, I would push things starting this late into the merge window following the next one (v4.17), but would be more than willing to make an exception given that this fixes a valid real-world complaint.
I put the workqueue part in the fixes branch. If there's gonna be another rc, I'm gonna push it to Linus. If not, I'll push when the next window opens. I think either way should be fine.
Quoted 2 lines
For that matter, if you would rather push my commit along with your pair of commits, that works for me! Either way, please just let me know.
Oh, let's push these through their respective trees. Please take the RCU one through the usual RCU tree.
Thank you very much.
Signature · 1 line
tejun
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
Quoted 12 lines · Tejun Heo
Hello, Paul.
Quoted 4 lines · Paul E. McKenney
And one additional question... How are we pushing this upstream? By default, I would push things starting this late into the merge window following the next one (v4.17), but would be more than willing to make an exception given that this fixes a valid real-world complaint.
I put the workqueue part in the fixes branch. If there's gonna be another rc, I'm gonna push it to Linus. If not, I'll push when the next window opens. I think either way should be fine.
Quoted 5 lines
Quoted 2 lines
For that matter, if you would rather push my commit along with your pair of commits, that works for me! Either way, please just let me know.
Oh, let's push these through their respective trees. Please take the RCU one through the usual RCU tree.
Works for me!
Quoted 1 line
Thank you very much.
And to you!
Thanx, Paul
Re: [PATCH] cgroup/cpuset: fix circular locking dependency
In reply to Tejun HeoQuoted 21 lines · Tejun Heo
Hello,
Quoted 5 lines · Prateek Sood
task T is waiting for
cpuset_mutexacquired
by kworker/2:1sh ==> cpuhp/2 ==> kworker/2:1 ==> sh
Quoted 7 lines
kworker/2:3 ==> kthreadd ==> Task T ==> kworker/2:1
It seems that my earlier patch set should fix this scenario:
1) Inverting locking order ofcpuset_mutexandcpu_hotplug_lock.
2) Make cpuset hotplug work synchronous.Could you please share your feedback.
Hmm... this can also be resolved by adding
WQ_MEM_RECLAIMto the synchronize rcu workqueue, right? Given the wide-spread usages ofsynchronize_rcuand friends, maybe that's the right solution, or at least something we also need to do, for this particular deadlock?
Quoted 8 lines
Again, I don't have anything against making the domain rebuliding part of cpuset operations synchronous and these tricky deadlock scenarios do indicate that doing so would probably be beneficial. That said, tho, these scenarios seem more of manifestations of other problems exposed through kthreadd dependency than anything else.
Thanks.
Hi TJ,
Thanks for suggesting WQ_MEM_RECLAIM solution.
My understanding of WQ_MEM_RECLAIM was that it needs to be used for cases where memory pressure could cause deadlocks.
In this case it does not seem to be a memory pressure issue. Overloading WQ_MEM_RECLAIM usage for solution to another problem is the correct approach?
This scenario can be resolved by using WQ_MEM_RECLAIM and a separate workqueue for rcu. But there seems to be a possibility in future if any cpu hotplug callbacks use other predefined workqueues which do not have WQ_MEM_RECLAIM option.
Please let me know your feedback on this.
Thanks
Signature · 3 lines
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Tejun Heo
maintainerRe: [PATCH] cgroup/cpuset: fix circular locking dependency
Hello, Prateek.
Quoted 2 lines · Prateek Sood
My understanding of
WQ_MEM_RECLAIMwas that it needs to be used for cases where memory pressure could cause deadlocks.
Yes, that is the primary role; however, there are a couple places where we need it to isolate a low level subsystem's forward progress from the dependencies of the dynanic worker pool management.
Quoted 3 lines
In this case it does not seem to be a memory pressure issue. Overloading
WQ_MEM_RECLAIMusage for solution to another problem is the correct approach?
In general, we don't face this issue but for things like RCU, I do believe this is the right approach. The name of the flag is a bit of misnomer for those cases as it only describes one of the dependencies of worker pool management. I don't think it's an actual problem given that vast majority of the usages are protecting against memory dependency.
Quoted 4 lines
This scenario can be resolved by using
WQ_MEM_RECLAIMand a separate workqueue for rcu. But there seems to be a possibility in future if any cpu hotplug callbacks use other predefined workqueues which do not haveWQ_MEM_RECLAIMoption.
We want to isolate from this sort of dependencies anyway, especially how widely and deeply we depend on RCU these days. Maybe we will develop a justified case in the future but at least up until now every case around this issue seems to be something to be fixed on their own.
Thanks.
Signature · 1 line
tejun