Skip to contentSkip to conversation

cgroup: initialize cgrp->dentry before css_alloc()

PATCH In review Li Zefan 3 participants 14 messages Jan 24, 2013 – Jan 25, 2013

Subsystems (2)
lore
Share & tools
lore

Li Zefan

author
#0 lore raw

PATCH v2 1/6 cgroup: initialize cgrp->dentry before css_alloc()

With this change, we're guaranteed that cgroup_path() won't see NULL cgrp->dentry, and thus we can remove the NULL check in it.

(Well, it's not true, because dummptop.dentry is always NULL)

---
 kernel/cgroup.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
kernel/cgroup.c +4 -3
index ad3359f..b9a76e2 100644
@@ -1767,7 +1767,7 @@ int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen)
 	rcu_lockdep_assert(rcu_read_lock_held() || cgroup_lock_is_held(),
 			   "cgroup_path() called without proper locking");
 
-	if (!dentry || cgrp == dummytop) {
+	if (cgrp == dummytop) {
 		/*
 		 * Inactive subsystems have no dentry for their root
 		 * cgroup
@@ -4153,6 +4153,9 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
 
 	init_cgroup_housekeeping(cgrp);
 
+	dentry->d_fsdata = cgrp;
+	cgrp->dentry = dentry;
+
 	cgrp->parent = parent;
 	cgrp->root = parent->root;
 	cgrp->top_cgroup = parent->top_cgroup;
@@ -4190,8 +4193,6 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
 	lockdep_assert_held(&dentry->d_inode->i_mutex);
 
 	/* allocation complete, commit to creation */
-	dentry->d_fsdata = cgrp;
-	cgrp->dentry = dentry;
 	list_add_tail(&cgrp->allcg_node, &root->allcg_list);
 	list_add_tail_rcu(&cgrp->sibling, &cgrp->parent->children);
 	root->number_of_cgroups++;
Signature · 1 line
1.8.0.2
author
#1 lore raw

PATCH v2 2/6 sched: split out css_online/css_offline from tg creation/destruction

This is a preparaton for later patches.

- What do we gain from cpu_cgroup_css_online():

After ss->css_alloc() and before ss->css_online(), there's a small window that tg->css.cgroup is NULL. With this change, tg won't be seen before ss->css_online(), where it's added to the global list, so we're guaranteed we'll never see NULL tg->css.cgroup.

- What do we gain from cpu_cgroup_css_offline():

tg is freed via RCU, so is cgroup. Without this change, This is how synchronization works:

cgroup_rmdir()
no ss->css_offline()
diput()
syncornize_rcu()
ss->css_free() <-- unregister tg, and free it via call_rcu()
kfree_rcu(cgroup) <-- wait possible refs to cgroup, and free cgroup

We can't just kfree(cgroup), because tg might access tg->css.cgroup.

With this change:

cgroup_rmdir()
ss->css_offline() <-- unregister tg
diput()

  synchronize_rcu()    <-- wait possible refs to tg and cgroup
  ss->css_free()       <-- free tg
  kfree_rcu(cgroup)    <-- free cgroup

As you see, kfree_rcu() is redundant now.

---

v2: changelog rephrased

---
 include/linux/sched.h     |  3 +++
 kernel/sched/auto_group.c |  3 +++
 kernel/sched/core.c       | 49 +++++++++++++++++++++++++++++++++++++----------
 3 files changed, 45 insertions(+), 10 deletions(-)
include/linux/sched.h +3 -0
index 206bb08..577eb97 100644
@@ -2750,7 +2750,10 @@ extern void normalize_rt_tasks(void);
 extern struct task_group root_task_group;
 
 extern struct task_group *sched_create_group(struct task_group *parent);
+extern void sched_online_group(struct task_group *tg,
+			       struct task_group *parent);
 extern void sched_destroy_group(struct task_group *tg);
+extern void sched_offline_group(struct task_group *tg);
 extern void sched_move_task(struct task_struct *tsk);
 #ifdef CONFIG_FAIR_GROUP_SCHED
 extern int sched_group_set_shares(struct task_group *tg, unsigned long shares);
kernel/sched/auto_group.c +3 -0
index 0984a21..64de5f8 100644
@@ -35,6 +35,7 @@ static inline void autogroup_destroy(struct kref *kref)
 	ag->tg->rt_se = NULL;
 	ag->tg->rt_rq = NULL;
 #endif
+	sched_offline_group(ag->tg);
 	sched_destroy_group(ag->tg);
 }
 
@@ -76,6 +77,8 @@ static inline struct autogroup *autogroup_create(void)
 	if (IS_ERR(tg))
 		goto out_free;
 
+	sched_online_group(tg, &root_task_group);
+
 	kref_init(&ag->kref);
 	init_rwsem(&ag->lock);
 	ag->id = atomic_inc_return(&autogroup_seq_nr);
kernel/sched/core.c +39 -10

97 lines not on this page · raw message

Signature · 1 line
1.8.0.2
Acked-by on v2 2/6
#2 lore raw

Re: [PATCH v2 2/6] sched: split out css_online/css_offline from tg creation/destruction

Quoted 28 lines · Li Zefan

This is a preparaton for later patches.

- What do we gain from cpu_cgroup_css_online():

After ss->css_alloc() and before ss->css_online(), there's a small window that tg->css.cgroup is NULL. With this change, tg won't be seen before ss->css_online(), where it's added to the global list, so we're guaranteed we'll never see NULL tg->css.cgroup.

- What do we gain from cpu_cgroup_css_offline():

tg is freed via RCU, so is cgroup. Without this change, This is how synchronization works:

cgroup_rmdir()
no ss->css_offline()
diput()
syncornize_rcu()
ss->css_free() <-- unregister tg, and free it via call_rcu()
kfree_rcu(cgroup) <-- wait possible refs to cgroup, and free cgroup

We can't just kfree(cgroup), because tg might access tg->css.cgroup.

With this change:

cgroup_rmdir()
ss->css_offline() <-- unregister tg
diput()

Quoted 7 lines
  synchronize_rcu()    <-- wait possible refs to tg and cgroup
  ss->css_free()       <-- free tg
  kfree_rcu(cgroup)    <-- free cgroup

As you see, kfree_rcu() is redundant now.

Thanks,

Ingo

maintainer
#3 lore raw

Re: [PATCH v2 2/6] sched: split out css_online/css_offline from tg creation/destruction

Quoted 1 line · Ingo Molnar

Ingo, if you don't objecdt, I'll take these through cgroup/for-3.9 branch with other cgroup changes. Pleaes holler if you object.

Thanks.

Signature · 1 line
tejun
#4 lore raw

Re: [PATCH v2 2/6] sched: split out css_online/css_offline from tg creation/destruction

Quoted 5 lines · Tejun Heo
Quoted 1 line · Ingo Molnar

Ingo, if you don't objecdt, I'll take these through cgroup/for-3.9 branch with other cgroup changes. Pleaes holler if you object.

That's fine with me, it should not interact with anything pending in (or planned for) the scheduler tree for this cycle.

Thanks,

Ingo

#5 lore raw

Re: [PATCH v2 2/6] sched: split out css_online/css_offline from tg creation/destruction

In reply to Li Zefan

include/linux/sched.h extern void normalize_rt_tasks(void); · line 2756

Quoted 5 lines · Li Zefan

extern struct task_group *sched_create_group(struct task_group *parent);

include/linux/sched.h +3 -0
+extern void sched_online_group(struct task_group *tg,
+			       struct task_group *parent);
 extern void sched_destroy_group(struct task_group *tg);
+extern void sched_offline_group(struct task_group *tg);

Btw., a rename of these APIs might be in order, along the usual patterns:

sched_task_group_create()
sched_task_group_online()
sched_task_group_offline()
sched_task_group_destroy()

etc.

that way the naming is more hierarchical and there's no clash with the sched_group concept which is about something else.

(In a separate patch.)

Thanks,

Ingo

author
#6 lore raw

Re: [PATCH v2 2/6] sched: split out css_online/css_offline from tg creation/destruction

Quoted 17 lines · Ingo Molnar

include/linux/sched.h extern void normalize_rt_tasks(void); · line 2756

Quoted 5 lines · Li Zefan

extern struct task_group *sched_create_group(struct task_group *parent);

include/linux/sched.h +3 -0
+extern void sched_online_group(struct task_group *tg,
+			       struct task_group *parent);
 extern void sched_destroy_group(struct task_group *tg);
+extern void sched_offline_group(struct task_group *tg);

Btw., a rename of these APIs might be in order, along the usual patterns:

sched_task_group_create()
sched_task_group_online()
sched_task_group_offline()
sched_task_group_destroy()

Quoted 7 lines

etc.

that way the naming is more hierarchical and there's no clash with the sched_group concept which is about something else.

(In a separate patch.)

Agreed. I'll send a cleanup patch to you when those patches hit mainline. (That will be 3.9-rc1 or later)

author
#7 lore raw

PATCH v2 3/6 sched: remove redundant NULL cgroup check in task_group_path()

In reply to Li Zefan

A task_group won't be online (thus no one can see it) until cpu_cgroup_css_online(), and at that time tg->css.cgroup has been initialized, so this NULL check is redundant.

---
 kernel/sched/debug.c | 7 -------
 1 file changed, 7 deletions(-)
kernel/sched/debug.c +0 -7
index 2cd3c1b..38df0db 100644
@@ -110,13 +110,6 @@ static char *task_group_path(struct task_group *tg)
 	if (autogroup_path(tg, group_path, PATH_MAX))
 		return group_path;
 
-	/*
-	 * May be NULL if the underlying cgroup isn't fully-created yet
-	 */
-	if (!tg->css.cgroup) {
-		group_path[0] = '\0';
-		return group_path;
-	}
 	cgroup_path(tg->css.cgroup, group_path, PATH_MAX);
 	return group_path;
 }
Signature · 1 line
1.8.0.2
author
#8 lore raw

PATCH v2 4/6 cgroup: remove duplicate RCU free on struct cgroup

In reply to Li Zefan

When destroying a cgroup, though in cgroup_diput() we've called synchronize_rcu(), we then still have to free it via call_rcu().

The story is, long ago to fix a race between reading /proc/sched_debug and freeing cgroup, the code was changed to utilize call_rcu(). See commit a47295e6bc42ad35f9c15ac66f598aa24debd4e2 ("cgroups: make cgroup_path() RCU-safe")

As we've fixed cpu cgroup that cpu_cgroup_offline_css() is used to unregister a task_group so there won't be concurrent access to this task_group after synchronize_rcu() in diput(). Now we can just kfree(cgrp).

---
 kernel/cgroup.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/cgroup.c +1 -1
index b9a76e2..a39e2b0 100644
@@ -892,7 +892,7 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
 		simple_xattrs_free(&cgrp->xattrs);
 
 		ida_simple_remove(&cgrp->root->cgroup_ida, cgrp->id);
-		kfree_rcu(cgrp, rcu_head);
+		kfree(cgrp);
 	} else {
 		struct cfent *cfe = __d_cfe(dentry);
 		struct cgroup *cgrp = dentry->d_parent->d_fsdata;
Signature · 1 line
1.8.0.2
author
#9 lore raw

PATCH v2 5/6 cgroup: remove synchronize_rcu() from cgroup_diput()

In reply to Li Zefan

Free cgroup via call_rcu(). The actual work is done through workqueue.

---

v2: move INIT_WORK() from cgroup_create() to init_cgroup_housekeeping()

---
 include/linux/cgroup.h |  1 +
 kernel/cgroup.c        | 72 ++++++++++++++++++++++++++++++--------------------
 2 files changed, 44 insertions(+), 29 deletions(-)
include/linux/cgroup.h +1 -0
index 8118a31..900af59 100644
@@ -203,6 +203,7 @@ struct cgroup {
 
 	/* For RCU-protected deletion */
 	struct rcu_head rcu_head;
+	struct work_struct free_work;
 
 	/* List of events which userspace want to receive */
 	struct list_head event_list;
kernel/cgroup.c +43 -29

95 lines not on this page · raw message

Signature · 1 line
1.8.0.2
author
#10 lore raw

PATCH v2 6/6 cgroup: remove bogus comments in cgroup_diput()

In reply to Li Zefan

Since commit 48ddbe194623ae089cc0576e60363f2d2e85662a ("cgroup: make css->refcnt clearing on cgroup removal optional"), each css holds a ref on cgroup's dentry, so cgroup_diput() won't be called until all css' refs go down to 0, which invalids the comments.

---
 kernel/cgroup.c | 6 ------
 1 file changed, 6 deletions(-)
kernel/cgroup.c +0 -6
index f18b0d9..368cff1 100644
@@ -899,12 +899,6 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
 		struct cgroup *cgrp = dentry->d_fsdata;
 
 		BUG_ON(!(cgroup_is_removed(cgrp)));
-		/* It's possible for external users to be holding css
-		 * reference counts on a cgroup; css_put() needs to
-		 * be able to access the cgroup after decrementing
-		 * the reference count in order to know if it needs to
-		 * queue the cgroup to be handled by the release
-		 * agent */
 		call_rcu(&cgrp->rcu_head, cgroup_free_rcu);
 	} else {
 		struct cfent *cfe = __d_cfe(dentry);
Signature · 1 line
1.8.0.2
maintainer
#11 lore raw

Re: [PATCH v2 6/6] cgroup: remove bogus comments in cgroup_diput()

Hello, Li.

Quoted 15 lines · Li Zefan

Since commit 48ddbe194623ae089cc0576e60363f2d2e85662a ("cgroup: make css->refcnt clearing on cgroup removal optional"), each css holds a ref on cgroup's dentry, so cgroup_diput() won't be called until all css' refs go down to 0, which invalids the comments.

---
 kernel/cgroup.c | 6 ------
 1 file changed, 6 deletions(-)
kernel/cgroup.c +0 -0
index f18b0d9..368cff1 100644

kernel/cgroup.c static void cgroup_diput(struct dentry *dentry, struct inode *inode) · line 907

Quoted 9 lines
kernel/cgroup.c +0 -6
@@ -899,12 +899,6 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
 		struct cgroup *cgrp = dentry->d_fsdata;
 
 		BUG_ON(!(cgroup_is_removed(cgrp)));
-		/* It's possible for external users to be holding css
-		 * reference counts on a cgroup; css_put() needs to
-		 * be able to access the cgroup after decrementing
-		 * the reference count in order to know if it needs to
-		 * queue the cgroup to be handled by the release
-		 * agent */

The latter half is still true tho. I applied the patch anyway as the comment isn't all that useful without covering everything intended to be covered with RCU. Can you please write up another patch documenting what's covered by RCU (stuff that we intend to keep guaranteeing to be safe to access only w/ RCU)?

Thanks.

Signature · 1 line
tejun
author
#12 lore raw

Re: [PATCH v2 6/6] cgroup: remove bogus comments in cgroup_diput()

Quoted 18 lines · Li Zefan
Quoted 17 lines

Since commit 48ddbe194623ae089cc0576e60363f2d2e85662a ("cgroup: make css->refcnt clearing on cgroup removal optional"), each css holds a ref on cgroup's dentry, so cgroup_diput() won't be called until all css' refs go down to 0, which invalids the comments.

---
 kernel/cgroup.c | 6 ------
 1 file changed, 6 deletions(-)
kernel/cgroup.c +0 -0
index f18b0d9..368cff1 100644
@@ -899,12 +899,6 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
 		struct cgroup *cgrp = dentry->d_fsdata;
 
Quoted 14 lines

kernel/cgroup.c static void cgroup_diput(struct dentry *dentry, struct inode *inode) · line 907

Quoted 7 lines
kernel/cgroup.c +0 -6
 		BUG_ON(!(cgroup_is_removed(cgrp)));
-		/* It's possible for external users to be holding css
-		 * reference counts on a cgroup; css_put() needs to
-		 * be able to access the cgroup after decrementing
-		 * the reference count in order to know if it needs to
-		 * queue the cgroup to be handled by the release
-		 * agent */

The latter half is still true tho. I applied the patch anyway as the comment isn't all that useful without covering everything intended to be covered with RCU. Can you please write up another patch documenting what's covered by RCU (stuff that we intend to keep guaranteeing to be safe to access only w/ RCU)?

Sure. I'll try.

maintainer
#13 lore raw
In reply to Li Zefan
Quoted 6 lines · Li Zefan

With this change, we're guaranteed that cgroup_path() won't see NULL cgrp->dentry, and thus we can remove the NULL check in it.

(Well, it's not true, because dummptop.dentry is always NULL)

Appled 1-6 to cgroup/for-3.9.

Thanks.

Signature · 1 line
tejun