Commit a47295e6 authored by Paul Menage's avatar Paul Menage Committed by Linus Torvalds

cgroups: make cgroup_path() RCU-safe

Fix races between /proc/sched_debug by freeing cgroup objects via an RCU
callback.  Thus any cgroup reference obtained from an RCU-safe source will
remain valid during the RCU section.  Since dentries are also RCU-safe,
this allows us to traverse up the tree safely.

Additionally, make cgroup_path() check for a NULL cgrp->dentry to avoid
trying to report a path for a partially-created cgroup.

[lizf@cn.fujitsu.com: call deactive_super() in cgroup_diput()]
Signed-off-by: default avatarPaul Menage <menage@google.com>
Reviewed-by: default avatarLi Zefan <lizf@cn.fujitsu.com>
Tested-by: default avatarLi Zefan <lizf@cn.fujitsu.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: default avatarLi Zefan <lizf@cn.fujitsu.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent e7b80bb6
...@@ -116,7 +116,7 @@ struct cgroup { ...@@ -116,7 +116,7 @@ struct cgroup {
struct list_head children; /* my children */ struct list_head children; /* my children */
struct cgroup *parent; /* my parent */ struct cgroup *parent; /* my parent */
struct dentry *dentry; /* cgroup fs entry */ struct dentry *dentry; /* cgroup fs entry, RCU protected */
/* Private pointers for each registered subsystem */ /* Private pointers for each registered subsystem */
struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT]; struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];
...@@ -145,6 +145,9 @@ struct cgroup { ...@@ -145,6 +145,9 @@ struct cgroup {
int pids_use_count; int pids_use_count;
/* Length of the current tasks_pids array */ /* Length of the current tasks_pids array */
int pids_length; int pids_length;
/* For RCU-protected deletion */
struct rcu_head rcu_head;
}; };
/* A css_set is a structure holding pointers to a set of /* A css_set is a structure holding pointers to a set of
......
...@@ -271,7 +271,7 @@ static void __put_css_set(struct css_set *cg, int taskexit) ...@@ -271,7 +271,7 @@ static void __put_css_set(struct css_set *cg, int taskexit)
rcu_read_lock(); rcu_read_lock();
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
struct cgroup *cgrp = cg->subsys[i]->cgroup; struct cgroup *cgrp = rcu_dereference(cg->subsys[i]->cgroup);
if (atomic_dec_and_test(&cgrp->count) && if (atomic_dec_and_test(&cgrp->count) &&
notify_on_release(cgrp)) { notify_on_release(cgrp)) {
if (taskexit) if (taskexit)
...@@ -594,6 +594,13 @@ static void cgroup_call_pre_destroy(struct cgroup *cgrp) ...@@ -594,6 +594,13 @@ static void cgroup_call_pre_destroy(struct cgroup *cgrp)
return; return;
} }
static void free_cgroup_rcu(struct rcu_head *obj)
{
struct cgroup *cgrp = container_of(obj, struct cgroup, rcu_head);
kfree(cgrp);
}
static void cgroup_diput(struct dentry *dentry, struct inode *inode) static void cgroup_diput(struct dentry *dentry, struct inode *inode)
{ {
/* is dentry a directory ? if so, kfree() associated cgroup */ /* is dentry a directory ? if so, kfree() associated cgroup */
...@@ -619,11 +626,13 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode) ...@@ -619,11 +626,13 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
cgrp->root->number_of_cgroups--; cgrp->root->number_of_cgroups--;
mutex_unlock(&cgroup_mutex); mutex_unlock(&cgroup_mutex);
/* Drop the active superblock reference that we took when we /*
* created the cgroup */ * Drop the active superblock reference that we took when we
* created the cgroup
*/
deactivate_super(cgrp->root->sb); deactivate_super(cgrp->root->sb);
kfree(cgrp); call_rcu(&cgrp->rcu_head, free_cgroup_rcu);
} }
iput(inode); iput(inode);
} }
...@@ -1134,14 +1143,16 @@ static inline struct cftype *__d_cft(struct dentry *dentry) ...@@ -1134,14 +1143,16 @@ static inline struct cftype *__d_cft(struct dentry *dentry)
* @buf: the buffer to write the path into * @buf: the buffer to write the path into
* @buflen: the length of the buffer * @buflen: the length of the buffer
* *
* Called with cgroup_mutex held. Writes path of cgroup into buf. * Called with cgroup_mutex held or else with an RCU-protected cgroup
* Returns 0 on success, -errno on error. * reference. Writes path of cgroup into buf. Returns 0 on success,
* -errno on error.
*/ */
int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen) int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen)
{ {
char *start; char *start;
struct dentry *dentry = rcu_dereference(cgrp->dentry);
if (cgrp == dummytop) { if (!dentry || cgrp == dummytop) {
/* /*
* Inactive subsystems have no dentry for their root * Inactive subsystems have no dentry for their root
* cgroup * cgroup
...@@ -1154,13 +1165,14 @@ int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen) ...@@ -1154,13 +1165,14 @@ int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen)
*--start = '\0'; *--start = '\0';
for (;;) { for (;;) {
int len = cgrp->dentry->d_name.len; int len = dentry->d_name.len;
if ((start -= len) < buf) if ((start -= len) < buf)
return -ENAMETOOLONG; return -ENAMETOOLONG;
memcpy(start, cgrp->dentry->d_name.name, len); memcpy(start, cgrp->dentry->d_name.name, len);
cgrp = cgrp->parent; cgrp = cgrp->parent;
if (!cgrp) if (!cgrp)
break; break;
dentry = rcu_dereference(cgrp->dentry);
if (!cgrp->parent) if (!cgrp->parent)
continue; continue;
if (--start < buf) if (--start < buf)
...@@ -1663,7 +1675,7 @@ static int cgroup_create_dir(struct cgroup *cgrp, struct dentry *dentry, ...@@ -1663,7 +1675,7 @@ static int cgroup_create_dir(struct cgroup *cgrp, struct dentry *dentry,
if (!error) { if (!error) {
dentry->d_fsdata = cgrp; dentry->d_fsdata = cgrp;
inc_nlink(parent->d_inode); inc_nlink(parent->d_inode);
cgrp->dentry = dentry; rcu_assign_pointer(cgrp->dentry, dentry);
dget(dentry); dget(dentry);
} }
dput(dentry); dput(dentry);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment