Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0ab02ca

Browse files
lizf-oshtejun
authored andcommitted
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}(). tj: Refreshed on top of b58c899 ("cgroup: fix error return from cgroup_create()"). Fixes: 4e96ee8 ("cgroup: convert cgroup_ida to cgroup_idr") Cc: <[email protected]> #3.12+ Reported-by: Michal Hocko <[email protected]> Signed-off-by: Li Zefan <[email protected]> Signed-off-by: Tejun Heo <[email protected]>
1 parent 48573a8 commit 0ab02ca

File tree

2 files changed

+20
-16
lines changed

2 files changed

+20
-16
lines changed

include/linux/cgroup.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ struct cgroup {
166166
*
167167
* The ID of the root cgroup is always 0, and a new cgroup
168168
* will be assigned with a smallest available ID.
169+
*
170+
* Allocating/Removing ID must be protected by cgroup_mutex.
169171
*/
170172
int id;
171173

kernel/cgroup.c

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -886,7 +886,9 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
886886
* per-subsystem and moved to css->id so that lookups are
887887
* successful until the target css is released.
888888
*/
889+
mutex_lock(&cgroup_mutex);
889890
idr_remove(&cgrp->root->cgroup_idr, cgrp->id);
891+
mutex_unlock(&cgroup_mutex);
890892
cgrp->id = -1;
891893

892894
call_rcu(&cgrp->rcu_head, cgroup_free_rcu);
@@ -4167,16 +4169,6 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
41674169
}
41684170
rcu_assign_pointer(cgrp->name, name);
41694171

4170-
/*
4171-
* Temporarily set the pointer to NULL, so idr_find() won't return
4172-
* a half-baked cgroup.
4173-
*/
4174-
cgrp->id = idr_alloc(&root->cgroup_idr, NULL, 1, 0, GFP_KERNEL);
4175-
if (cgrp->id < 0) {
4176-
err = -ENOMEM;
4177-
goto err_free_name;
4178-
}
4179-
41804172
/*
41814173
* Only live parents can have children. Note that the liveliness
41824174
* check isn't strictly necessary because cgroup_mkdir() and
@@ -4186,7 +4178,17 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
41864178
*/
41874179
if (!cgroup_lock_live_group(parent)) {
41884180
err = -ENODEV;
4189-
goto err_free_id;
4181+
goto err_free_name;
4182+
}
4183+
4184+
/*
4185+
* Temporarily set the pointer to NULL, so idr_find() won't return
4186+
* a half-baked cgroup.
4187+
*/
4188+
cgrp->id = idr_alloc(&root->cgroup_idr, NULL, 1, 0, GFP_KERNEL);
4189+
if (cgrp->id < 0) {
4190+
err = -ENOMEM;
4191+
goto err_unlock;
41904192
}
41914193

41924194
/* Grab a reference on the superblock so the hierarchy doesn't
@@ -4218,7 +4220,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
42184220
*/
42194221
err = cgroup_create_file(dentry, S_IFDIR | mode, sb);
42204222
if (err < 0)
4221-
goto err_unlock;
4223+
goto err_free_id;
42224224
lockdep_assert_held(&dentry->d_inode->i_mutex);
42234225

42244226
cgrp->serial_nr = cgroup_serial_nr_next++;
@@ -4254,12 +4256,12 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
42544256

42554257
return 0;
42564258

4257-
err_unlock:
4258-
mutex_unlock(&cgroup_mutex);
4259-
/* Release the reference count that we took on the superblock */
4260-
deactivate_super(sb);
42614259
err_free_id:
42624260
idr_remove(&root->cgroup_idr, cgrp->id);
4261+
/* Release the reference count that we took on the superblock */
4262+
deactivate_super(sb);
4263+
err_unlock:
4264+
mutex_unlock(&cgroup_mutex);
42634265
err_free_name:
42644266
kfree(rcu_dereference_raw(cgrp->name));
42654267
err_free_cgrp:

0 commit comments

Comments
 (0)