Setup cgroupfs like this:
# mount -t cgroup -o cpuacct xxx /cgroup
# mkdir /cgroup/sub1
# mkdir /cgroup/sub2
Then run these two commands:
# for ((; ;)) { mkdir /cgroup/sub1/tmp && rmdir /mnt/sub1/tmp; } &
# for ((; ;)) { mkdir /cgroup/sub2/tmp && rmdir /mnt/sub2/tmp; } &
After seconds you may see this warning:
------------[ cut here ]------------
WARNING: CPU: 1 PID: 25243 at lib/idr.c:527 sub_remove+0x87/0x1b0()
idr_remove called for id=6 which is not allocated.
...
Call Trace:
[<ffffffff8156063c>] dump_stack+0x7a/0x96
[<ffffffff810591ac>] warn_slowpath_common+0x8c/0xc0
[<ffffffff81059296>] warn_slowpath_fmt+0x46/0x50
[<ffffffff81300aa7>] sub_remove+0x87/0x1b0
[<ffffffff810f3f02>] ? css_killed_work_fn+0x32/0x1b0
[<ffffffff81300bf5>] idr_remove+0x25/0xd0
[<ffffffff810f2bab>] cgroup_destroy_css_killed+0x5b/0xc0
[<ffffffff810f4000>] css_killed_work_fn+0x130/0x1b0
[<ffffffff8107cdbc>] process_one_work+0x26c/0x550
[<ffffffff8107eefe>] worker_thread+0x12e/0x3b0
[<ffffffff81085f96>] kthread+0xe6/0xf0
[<ffffffff81570bac>] ret_from_fork+0x7c/0xb0
---[ end trace 2d1577ec10cf80d0 ]---
It's because allocating/removing cgroup ID is not properly synchronized.
The bug was introduced when we converted cgroup_ida to cgroup_idr.
While synchronization is already done inside ida_simple_{get,remove}(),
users are responsible for concurrent calls to idr_{alloc,remove}().
Fixes: 4e96ee8e981b ("cgroup: convert cgroup_ida to cgroup_idr")
Cc: <redacted> #3.12+
Reported-by: Michal Hocko <redacted>
Signed-off-by: Li Zefan <redacted>
---
include/linux/cgroup.h | 2 ++
kernel/cgroup.c | 30 ++++++++++++++++--------------
2 files changed, 18 insertions(+), 14 deletions(-)
@@ -4189,9 +4183,17 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,*/if(!cgroup_lock_live_group(parent)){err=-ENODEV;-gotoerr_free_id;+gotoerr_free_name;}+/*+*TemporarilysetthepointertoNULL,soidr_find()won'treturn+*ahalf-bakedcgroup.+*/+cgrp->id=idr_alloc(&root->cgroup_idr,NULL,1,0,GFP_KERNEL);+if(cgrp->id<0)+gotoerr_unlock;+/* Grab a reference on the superblock so the hierarchy doesn't*getdeletedonunmountiftherearechildcgroups.This*canbedoneoutsidecgroup_mutex,sincethesbcan't
@@ -4257,12 +4259,12 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,return0;-err_unlock:-mutex_unlock(&cgroup_mutex);-/* Release the reference count that we took on the superblock */-deactivate_super(sb);err_free_id:idr_remove(&root->cgroup_idr,cgrp->id);+/* Release the reference count that we took on the superblock */+deactivate_super(sb);+err_unlock:+mutex_unlock(&cgroup_mutex);err_free_name:kfree(rcu_dereference_raw(cgrp->name));err_free_cgrp:
From: Michal Hocko <hidden> Date: 2014-02-11 10:20:42
Hi Li,
good work in reproducing the issue so quickly!
I have tried to backport this patch to 3.12 kernel but the code has
changed since then.
The only two instances of idr_remove which are called outside of
cgroup_mutex seem to be:
- cgroup_create calling it from err_free_id: path
- css_free_work_fn
mem_cgroup_css_free
__mem_cgroup_free
free_css_id
The second one takes ss->id_lock spinlock which should be sufficient
to exclude get_new_cssid but cgroup_mount and cgroup_create don't use
id_lock. They do hold cgroup_mutex though. So I guess I need something
like the following (I will have it tested):
---
Date: Tue, 11 Feb 2014 16:05:46 +0800
From: Li Zefan <redacted>
Subject: [PATCH - for 3.12] cgroup: protect modifications to cgroup_idr with cgroup_mutex
Setup cgroupfs like this:
# mount -t cgroup -o cpuacct xxx /cgroup
# mkdir /cgroup/sub1
# mkdir /cgroup/sub2
Then run these two commands:
# for ((; ;)) { mkdir /cgroup/sub1/tmp && rmdir /mnt/sub1/tmp; } &
# for ((; ;)) { mkdir /cgroup/sub2/tmp && rmdir /mnt/sub2/tmp; } &
After seconds you may see this warning:
------------[ cut here ]------------
WARNING: CPU: 1 PID: 25243 at lib/idr.c:527 sub_remove+0x87/0x1b0()
idr_remove called for id=6 which is not allocated.
...
Call Trace:
[<ffffffff8156063c>] dump_stack+0x7a/0x96
[<ffffffff810591ac>] warn_slowpath_common+0x8c/0xc0
[<ffffffff81059296>] warn_slowpath_fmt+0x46/0x50
[<ffffffff81300aa7>] sub_remove+0x87/0x1b0
[<ffffffff810f3f02>] ? css_killed_work_fn+0x32/0x1b0
[<ffffffff81300bf5>] idr_remove+0x25/0xd0
[<ffffffff810f2bab>] cgroup_destroy_css_killed+0x5b/0xc0
[<ffffffff810f4000>] css_killed_work_fn+0x130/0x1b0
[<ffffffff8107cdbc>] process_one_work+0x26c/0x550
[<ffffffff8107eefe>] worker_thread+0x12e/0x3b0
[<ffffffff81085f96>] kthread+0xe6/0xf0
[<ffffffff81570bac>] ret_from_fork+0x7c/0xb0
---[ end trace 2d1577ec10cf80d0 ]---
It's because allocating/removing cgroup ID is not properly synchronized.
The bug was introduced when we converted cgroup_ida to cgroup_idr.
While synchronization is already done inside ida_simple_{get,remove}(),
users are responsible for concurrent calls to idr_{alloc,remove}().
[mhocko-AlSwsSmVLrQ@public.gmane.org: ported to 3.12]
Fixes: 4e96ee8e981b ("cgroup: convert cgroup_ida to cgroup_idr")
Cc: <redacted> #3.12+
Reported-by: Michal Hocko <redacted>
Signed-off-by: Li Zefan <redacted>
---
include/linux/cgroup.h | 2 ++
kernel/cgroup.c | 25 ++++++++++++++-----------
2 files changed, 16 insertions(+), 11 deletions(-)
@@ -4410,14 +4410,6 @@ static long cgroup_create(struct cgrouprcu_assign_pointer(cgrp->name,name);/*-*TemporarilysetthepointertoNULL,soidr_find()won'treturn-*ahalf-bakedcgroup.-*/-cgrp->id=idr_alloc(&root->cgroup_idr,NULL,1,0,GFP_KERNEL);-if(cgrp->id<0)-gotoerr_free_name;--/**Onlyliveparentscanhavechildren.Notethattheliveliness*checkisn'tstrictlynecessarybecausecgroup_mkdir()and*cgroup_rmdir()arefullysynchronizedbyi_mutex;however,doit
@@ -4426,9 +4418,17 @@ static long cgroup_create(struct cgroup*/if(!cgroup_lock_live_group(parent)){err=-ENODEV;-gotoerr_free_id;+gotoerr_free_name;}+/*+*TemporarilysetthepointertoNULL,soidr_find()won'treturn+*ahalf-bakedcgroup.+*/+cgrp->id=idr_alloc(&root->cgroup_idr,NULL,1,0,GFP_KERNEL);+if(cgrp->id<0)+gotoerr_unlock;+/* Grab a reference on the superblock so the hierarchy doesn't*getdeletedonunmountiftherearechildcgroups.This*canbedoneoutsidecgroup_mutex,sincethesbcan't
@@ -4542,11 +4542,11 @@ err_free_all:ss->css_free(css);}}+idr_remove(&root->cgroup_idr,cgrp->id);+err_unlock:mutex_unlock(&cgroup_mutex);/* Release the reference count that we took on the superblock */deactivate_super(sb);-err_free_id:-idr_remove(&root->cgroup_idr,cgrp->id);err_free_name:kfree(rcu_dereference_raw(cgrp->name));err_free_cgrp:
On Tue, Feb 11, 2014 at 04:05:46PM +0800, Li Zefan wrote:
Setup cgroupfs like this:
# mount -t cgroup -o cpuacct xxx /cgroup
# mkdir /cgroup/sub1
# mkdir /cgroup/sub2
Then run these two commands:
# for ((; ;)) { mkdir /cgroup/sub1/tmp && rmdir /mnt/sub1/tmp; } &
# for ((; ;)) { mkdir /cgroup/sub2/tmp && rmdir /mnt/sub2/tmp; } &
After seconds you may see this warning:
------------[ cut here ]------------
WARNING: CPU: 1 PID: 25243 at lib/idr.c:527 sub_remove+0x87/0x1b0()
idr_remove called for id=6 which is not allocated.
...
Call Trace:
[<ffffffff8156063c>] dump_stack+0x7a/0x96
[<ffffffff810591ac>] warn_slowpath_common+0x8c/0xc0
[<ffffffff81059296>] warn_slowpath_fmt+0x46/0x50
[<ffffffff81300aa7>] sub_remove+0x87/0x1b0
[<ffffffff810f3f02>] ? css_killed_work_fn+0x32/0x1b0
[<ffffffff81300bf5>] idr_remove+0x25/0xd0
[<ffffffff810f2bab>] cgroup_destroy_css_killed+0x5b/0xc0
[<ffffffff810f4000>] css_killed_work_fn+0x130/0x1b0
[<ffffffff8107cdbc>] process_one_work+0x26c/0x550
[<ffffffff8107eefe>] worker_thread+0x12e/0x3b0
[<ffffffff81085f96>] kthread+0xe6/0xf0
[<ffffffff81570bac>] ret_from_fork+0x7c/0xb0
---[ end trace 2d1577ec10cf80d0 ]---
It's because allocating/removing cgroup ID is not properly synchronized.
The bug was introduced when we converted cgroup_ida to cgroup_idr.
While synchronization is already done inside ida_simple_{get,remove}(),
users are responsible for concurrent calls to idr_{alloc,remove}().
Fixes: 4e96ee8e981b ("cgroup: convert cgroup_ida to cgroup_idr")
Cc: <redacted> #3.12+
Reported-by: Michal Hocko <redacted>
Signed-off-by: Li Zefan <redacted>
Jesus, you're right. I missed that completely. It conflicts with the
return value fix in cgroup/for-3.14-fixes but easy to fix up.
Applying to cgroup/for-3.14-fixes.
Thanks!
--
tejun
Hello,
This is the slightly modified patch that I applied to
cgroup/for-3.14-fixes.
Thanks a lot!
-------- 8< --------
From 0ab02ca8f887908152d1a96db5130fc661d36a1e Mon Sep 17 00:00:00 2001
From: Li Zefan <redacted>
Date: Tue, 11 Feb 2014 16:05:46 +0800
Setup cgroupfs like this:
# mount -t cgroup -o cpuacct xxx /cgroup
# mkdir /cgroup/sub1
# mkdir /cgroup/sub2
Then run these two commands:
# for ((; ;)) { mkdir /cgroup/sub1/tmp && rmdir /mnt/sub1/tmp; } &
# for ((; ;)) { mkdir /cgroup/sub2/tmp && rmdir /mnt/sub2/tmp; } &
After seconds you may see this warning:
------------[ cut here ]------------
WARNING: CPU: 1 PID: 25243 at lib/idr.c:527 sub_remove+0x87/0x1b0()
idr_remove called for id=6 which is not allocated.
...
Call Trace:
[<ffffffff8156063c>] dump_stack+0x7a/0x96
[<ffffffff810591ac>] warn_slowpath_common+0x8c/0xc0
[<ffffffff81059296>] warn_slowpath_fmt+0x46/0x50
[<ffffffff81300aa7>] sub_remove+0x87/0x1b0
[<ffffffff810f3f02>] ? css_killed_work_fn+0x32/0x1b0
[<ffffffff81300bf5>] idr_remove+0x25/0xd0
[<ffffffff810f2bab>] cgroup_destroy_css_killed+0x5b/0xc0
[<ffffffff810f4000>] css_killed_work_fn+0x130/0x1b0
[<ffffffff8107cdbc>] process_one_work+0x26c/0x550
[<ffffffff8107eefe>] worker_thread+0x12e/0x3b0
[<ffffffff81085f96>] kthread+0xe6/0xf0
[<ffffffff81570bac>] ret_from_fork+0x7c/0xb0
---[ end trace 2d1577ec10cf80d0 ]---
It's because allocating/removing cgroup ID is not properly synchronized.
The bug was introduced when we converted cgroup_ida to cgroup_idr.
While synchronization is already done inside ida_simple_{get,remove}(),
users are responsible for concurrent calls to idr_{alloc,remove}().
tj: Refreshed on top of b58c89986a77 ("cgroup: fix error return from
cgroup_create()").
Fixes: 4e96ee8e981b ("cgroup: convert cgroup_ida to cgroup_idr")
Cc: <redacted> #3.12+
Reported-by: Michal Hocko <redacted>
Signed-off-by: Li Zefan <redacted>
Signed-off-by: Tejun Heo <redacted>
---
include/linux/cgroup.h | 2 ++
kernel/cgroup.c | 34 ++++++++++++++++++----------------
2 files changed, 20 insertions(+), 16 deletions(-)
@@ -4186,7 +4178,17 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,*/if(!cgroup_lock_live_group(parent)){err=-ENODEV;-gotoerr_free_id;+gotoerr_free_name;+}++/*+*TemporarilysetthepointertoNULL,soidr_find()won'treturn+*ahalf-bakedcgroup.+*/+cgrp->id=idr_alloc(&root->cgroup_idr,NULL,1,0,GFP_KERNEL);+if(cgrp->id<0){+err=-ENOMEM;+gotoerr_unlock;}/* Grab a reference on the superblock so the hierarchy doesn't
@@ -4254,12 +4256,12 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,return0;-err_unlock:-mutex_unlock(&cgroup_mutex);-/* Release the reference count that we took on the superblock */-deactivate_super(sb);err_free_id:idr_remove(&root->cgroup_idr,cgrp->id);+/* Release the reference count that we took on the superblock */+deactivate_super(sb);+err_unlock:+mutex_unlock(&cgroup_mutex);err_free_name:kfree(rcu_dereference_raw(cgrp->name));err_free_cgrp:
From: Michal Hocko <hidden> Date: 2014-02-11 16:35:04
On Tue 11-02-14 10:41:05, Tejun Heo wrote:
[...]
quoted hunk
@@ -4254,12 +4256,12 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, return 0;-err_unlock:- mutex_unlock(&cgroup_mutex);- /* Release the reference count that we took on the superblock */- deactivate_super(sb); err_free_id: idr_remove(&root->cgroup_idr, cgrp->id);+ /* Release the reference count that we took on the superblock */+ deactivate_super(sb);+err_unlock:+ mutex_unlock(&cgroup_mutex); err_free_name: kfree(rcu_dereference_raw(cgrp->name)); err_free_cgrp:
Do I have to change deactivate_super vs. mutex_unlock ordering in my
backport for 3.12 as well?
--
Michal Hocko
SUSE Labs
Hi Li,
good work in reproducing the issue so quickly!
I have tried to backport this patch to 3.12 kernel but the code has
changed since then.
The only two instances of idr_remove which are called outside of
cgroup_mutex seem to be:
- cgroup_create calling it from err_free_id: path
- css_free_work_fn
mem_cgroup_css_free
__mem_cgroup_free
free_css_id
The second one takes ss->id_lock spinlock which should be sufficient
to exclude get_new_cssid but cgroup_mount and cgroup_create don't use
id_lock. They do hold cgroup_mutex though. So I guess I need something
like the following (I will have it tested):
I don't think you need to do anything with ss->idr.
cgroup_create() calls alloc_css_id() -> get_new_cssid(), and get_new_cssid()
uses id_lock.
cgroup_mount() won't touch ss->idr, because the css_id for root cgroup is
always 0.
@@ -4254,12 +4256,12 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, return 0;-err_unlock:- mutex_unlock(&cgroup_mutex);- /* Release the reference count that we took on the superblock */- deactivate_super(sb); err_free_id: idr_remove(&root->cgroup_idr, cgrp->id);+ /* Release the reference count that we took on the superblock */+ deactivate_super(sb);+err_unlock:+ mutex_unlock(&cgroup_mutex); err_free_name: kfree(rcu_dereference_raw(cgrp->name)); err_free_cgrp:
Do I have to change deactivate_super vs. mutex_unlock ordering in my
backport for 3.12 as well?
Your change is wrong that you shouldn't drop sb refcnt in err_unlock path.
But you made me think if it's OK to hold cgroup_mutex while calling deactivate_super(),
and the answer is NO! deactive_super() may call cgroup_kill_sb() which will
acquire cgroup_mutex.
I'll update the patch.
Thank Tejun we won't be entangled with vfs internal anymore after coverting
to kernfs.
@@ -4254,12 +4256,12 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, return 0;-err_unlock:- mutex_unlock(&cgroup_mutex);- /* Release the reference count that we took on the superblock */- deactivate_super(sb); err_free_id: idr_remove(&root->cgroup_idr, cgrp->id);+ /* Release the reference count that we took on the superblock */+ deactivate_super(sb);+err_unlock:+ mutex_unlock(&cgroup_mutex); err_free_name: kfree(rcu_dereference_raw(cgrp->name)); err_free_cgrp:
Do I have to change deactivate_super vs. mutex_unlock ordering in my
backport for 3.12 as well?
Your change is wrong that you shouldn't drop sb refcnt in err_unlock path.
But you made me think if it's OK to hold cgroup_mutex while calling deactivate_super(),
and the answer is NO! deactive_super() may call cgroup_kill_sb() which will
acquire cgroup_mutex.
I'll update the patch.
Thank Tejun we won't be entangled with vfs internal anymore after coverting
to kernfs.
On second thought, it should be safe to call deactivate_super() before
releasing cgroup_mutex, as cgroup_create() is called through vfs, so vfs
should guanrantee the superblock won't disapear, so this deactivate_super()
won't drop sb refcnt to 0.
Still this is just my guess without diving into vfs code, and we'd better
not depend on it even my guess is correct.
From: Michal Hocko <hidden> Date: 2014-02-12 09:12:48
Li has pointed out that my previous backport was not correct because
err_unlock label releases a reference to supperblock which was not taken
before idr_alloc. I've also removed cgroup_mutex from free_css_id as per
Li.
Fixed in this version.
---
Date: Tue, 11 Feb 2014 16:05:46 +0800
From: Li Zefan <redacted>
Subject: [PATCH - for 3.12] cgroup: protect modifications to cgroup_idr with cgroup_mutex
Setup cgroupfs like this:
# mount -t cgroup -o cpuacct xxx /cgroup
# mkdir /cgroup/sub1
# mkdir /cgroup/sub2
Then run these two commands:
# for ((; ;)) { mkdir /cgroup/sub1/tmp && rmdir /mnt/sub1/tmp; } &
# for ((; ;)) { mkdir /cgroup/sub2/tmp && rmdir /mnt/sub2/tmp; } &
After seconds you may see this warning:
------------[ cut here ]------------
WARNING: CPU: 1 PID: 25243 at lib/idr.c:527 sub_remove+0x87/0x1b0()
idr_remove called for id=6 which is not allocated.
...
Call Trace:
[<ffffffff8156063c>] dump_stack+0x7a/0x96
[<ffffffff810591ac>] warn_slowpath_common+0x8c/0xc0
[<ffffffff81059296>] warn_slowpath_fmt+0x46/0x50
[<ffffffff81300aa7>] sub_remove+0x87/0x1b0
[<ffffffff810f3f02>] ? css_killed_work_fn+0x32/0x1b0
[<ffffffff81300bf5>] idr_remove+0x25/0xd0
[<ffffffff810f2bab>] cgroup_destroy_css_killed+0x5b/0xc0
[<ffffffff810f4000>] css_killed_work_fn+0x130/0x1b0
[<ffffffff8107cdbc>] process_one_work+0x26c/0x550
[<ffffffff8107eefe>] worker_thread+0x12e/0x3b0
[<ffffffff81085f96>] kthread+0xe6/0xf0
[<ffffffff81570bac>] ret_from_fork+0x7c/0xb0
---[ end trace 2d1577ec10cf80d0 ]---
It's because allocating/removing cgroup ID is not properly synchronized.
The bug was introduced when we converted cgroup_ida to cgroup_idr.
While synchronization is already done inside ida_simple_{get,remove}(),
users are responsible for concurrent calls to idr_{alloc,remove}().
[mhocko@suse.cz: ported to 3.12]
Fixes: 4e96ee8e981b ("cgroup: convert cgroup_ida to cgroup_idr")
Cc: <redacted> #3.12+
Reported-by: Michal Hocko <redacted>
Signed-off-by: Li Zefan <redacted>
Signed-off-by: Michal Hocko <redacted>
---
include/linux/cgroup.h | 2 ++
kernel/cgroup.c | 23 ++++++++++++-----------
2 files changed, 14 insertions(+), 11 deletions(-)
@@ -4410,14 +4410,6 @@ static long cgroup_create(struct cgrouprcu_assign_pointer(cgrp->name,name);/*-*TemporarilysetthepointertoNULL,soidr_find()won'treturn-*ahalf-bakedcgroup.-*/-cgrp->id=idr_alloc(&root->cgroup_idr,NULL,1,0,GFP_KERNEL);-if(cgrp->id<0)-gotoerr_free_name;--/**Onlyliveparentscanhavechildren.Notethattheliveliness*checkisn'tstrictlynecessarybecausecgroup_mkdir()and*cgroup_rmdir()arefullysynchronizedbyi_mutex;however,doit
@@ -4426,7 +4418,7 @@ static long cgroup_create(struct cgroup*/if(!cgroup_lock_live_group(parent)){err=-ENODEV;-gotoerr_free_id;+gotoerr_free_name;}/* Grab a reference on the superblock so the hierarchy doesn't
@@ -4436,6 +4428,14 @@ static long cgroup_create(struct cgroup*fs*/atomic_inc(&sb->s_active);+/*+*TemporarilysetthepointertoNULL,soidr_find()won'treturn+*ahalf-bakedcgroup.+*/+cgrp->id=idr_alloc(&root->cgroup_idr,NULL,1,0,GFP_KERNEL);+if(cgrp->id<0)+gotoerr_unlock;+init_cgroup_housekeeping(cgrp);dentry->d_fsdata=cgrp;
@@ -4542,11 +4542,11 @@ err_free_all:ss->css_free(css);}}+idr_remove(&root->cgroup_idr,cgrp->id);+err_unlock:mutex_unlock(&cgroup_mutex);/* Release the reference count that we took on the superblock */deactivate_super(sb);-err_free_id:-idr_remove(&root->cgroup_idr,cgrp->id);err_free_name:kfree(rcu_dereference_raw(cgrp->name));err_free_cgrp:
Li has pointed out that my previous backport was not correct because
err_unlock label releases a reference to supperblock which was not taken
before idr_alloc. I've also removed cgroup_mutex from free_css_id as per
Li.
Fixed in this version.
From: Michal Hocko <hidden> Date: 2014-02-12 09:38:36
On Wed 12-02-14 17:26:40, Li Zefan wrote:
On 2014/2/12 17:12, Michal Hocko wrote:
quoted
Li has pointed out that my previous backport was not correct because
err_unlock label releases a reference to supperblock which was not taken
before idr_alloc. I've also removed cgroup_mutex from free_css_id as per
Li.
Fixed in this version.
Looks good to me!
Thanks for double checking.
Are you going to send it to stable mailing list after the fix hits mainline
or use it internally?
I plan to have it tested internally and then send it later on when
stable FAILED to apply mail arrives. Unless you want to send it yourself
of course.