diff options
author | Paul Menage <menage@google.com> | 2009-01-07 21:07:44 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-08 11:31:03 -0500 |
commit | a47295e6bc42ad35f9c15ac66f598aa24debd4e2 (patch) | |
tree | cb765e996ef35ae88e29d60796655d0d35e8cf5e | |
parent | e7b80bb695a5b64c92e314838e083b2f3bdf29b2 (diff) |
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: Paul Menage <menage@google.com>
Reviewed-by: Li Zefan <lizf@cn.fujitsu.com>
Tested-by: Li Zefan <lizf@cn.fujitsu.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | include/linux/cgroup.h | 5 | ||||
-rw-r--r-- | kernel/cgroup.c | 30 |
2 files changed, 25 insertions, 10 deletions
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index f68dfd8dd53a..73d1c730c3c4 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h | |||
@@ -116,7 +116,7 @@ struct cgroup { | |||
116 | struct list_head children; /* my children */ | 116 | struct list_head children; /* my children */ |
117 | 117 | ||
118 | struct cgroup *parent; /* my parent */ | 118 | struct cgroup *parent; /* my parent */ |
119 | struct dentry *dentry; /* cgroup fs entry */ | 119 | struct dentry *dentry; /* cgroup fs entry, RCU protected */ |
120 | 120 | ||
121 | /* Private pointers for each registered subsystem */ | 121 | /* Private pointers for each registered subsystem */ |
122 | struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT]; | 122 | struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT]; |
@@ -145,6 +145,9 @@ struct cgroup { | |||
145 | int pids_use_count; | 145 | int pids_use_count; |
146 | /* Length of the current tasks_pids array */ | 146 | /* Length of the current tasks_pids array */ |
147 | int pids_length; | 147 | int pids_length; |
148 | |||
149 | /* For RCU-protected deletion */ | ||
150 | struct rcu_head rcu_head; | ||
148 | }; | 151 | }; |
149 | 152 | ||
150 | /* A css_set is a structure holding pointers to a set of | 153 | /* A css_set is a structure holding pointers to a set of |
diff --git a/kernel/cgroup.c b/kernel/cgroup.c index cb7c72b91f46..83ea4f524be5 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c | |||
@@ -271,7 +271,7 @@ static void __put_css_set(struct css_set *cg, int taskexit) | |||
271 | 271 | ||
272 | rcu_read_lock(); | 272 | rcu_read_lock(); |
273 | for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { | 273 | for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { |
274 | struct cgroup *cgrp = cg->subsys[i]->cgroup; | 274 | struct cgroup *cgrp = rcu_dereference(cg->subsys[i]->cgroup); |
275 | if (atomic_dec_and_test(&cgrp->count) && | 275 | if (atomic_dec_and_test(&cgrp->count) && |
276 | notify_on_release(cgrp)) { | 276 | notify_on_release(cgrp)) { |
277 | if (taskexit) | 277 | if (taskexit) |
@@ -594,6 +594,13 @@ static void cgroup_call_pre_destroy(struct cgroup *cgrp) | |||
594 | return; | 594 | return; |
595 | } | 595 | } |
596 | 596 | ||
597 | static void free_cgroup_rcu(struct rcu_head *obj) | ||
598 | { | ||
599 | struct cgroup *cgrp = container_of(obj, struct cgroup, rcu_head); | ||
600 | |||
601 | kfree(cgrp); | ||
602 | } | ||
603 | |||
597 | static void cgroup_diput(struct dentry *dentry, struct inode *inode) | 604 | static void cgroup_diput(struct dentry *dentry, struct inode *inode) |
598 | { | 605 | { |
599 | /* is dentry a directory ? if so, kfree() associated cgroup */ | 606 | /* is dentry a directory ? if so, kfree() associated cgroup */ |
@@ -619,11 +626,13 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode) | |||
619 | cgrp->root->number_of_cgroups--; | 626 | cgrp->root->number_of_cgroups--; |
620 | mutex_unlock(&cgroup_mutex); | 627 | mutex_unlock(&cgroup_mutex); |
621 | 628 | ||
622 | /* Drop the active superblock reference that we took when we | 629 | /* |
623 | * created the cgroup */ | 630 | * Drop the active superblock reference that we took when we |
631 | * created the cgroup | ||
632 | */ | ||
624 | deactivate_super(cgrp->root->sb); | 633 | deactivate_super(cgrp->root->sb); |
625 | 634 | ||
626 | kfree(cgrp); | 635 | call_rcu(&cgrp->rcu_head, free_cgroup_rcu); |
627 | } | 636 | } |
628 | iput(inode); | 637 | iput(inode); |
629 | } | 638 | } |
@@ -1134,14 +1143,16 @@ static inline struct cftype *__d_cft(struct dentry *dentry) | |||
1134 | * @buf: the buffer to write the path into | 1143 | * @buf: the buffer to write the path into |
1135 | * @buflen: the length of the buffer | 1144 | * @buflen: the length of the buffer |
1136 | * | 1145 | * |
1137 | * Called with cgroup_mutex held. Writes path of cgroup into buf. | 1146 | * Called with cgroup_mutex held or else with an RCU-protected cgroup |
1138 | * Returns 0 on success, -errno on error. | 1147 | * reference. Writes path of cgroup into buf. Returns 0 on success, |
1148 | * -errno on error. | ||
1139 | */ | 1149 | */ |
1140 | int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen) | 1150 | int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen) |
1141 | { | 1151 | { |
1142 | char *start; | 1152 | char *start; |
1153 | struct dentry *dentry = rcu_dereference(cgrp->dentry); | ||
1143 | 1154 | ||
1144 | if (cgrp == dummytop) { | 1155 | if (!dentry || cgrp == dummytop) { |
1145 | /* | 1156 | /* |
1146 | * Inactive subsystems have no dentry for their root | 1157 | * Inactive subsystems have no dentry for their root |
1147 | * cgroup | 1158 | * cgroup |
@@ -1154,13 +1165,14 @@ int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen) | |||
1154 | 1165 | ||
1155 | *--start = '\0'; | 1166 | *--start = '\0'; |
1156 | for (;;) { | 1167 | for (;;) { |
1157 | int len = cgrp->dentry->d_name.len; | 1168 | int len = dentry->d_name.len; |
1158 | if ((start -= len) < buf) | 1169 | if ((start -= len) < buf) |
1159 | return -ENAMETOOLONG; | 1170 | return -ENAMETOOLONG; |
1160 | memcpy(start, cgrp->dentry->d_name.name, len); | 1171 | memcpy(start, cgrp->dentry->d_name.name, len); |
1161 | cgrp = cgrp->parent; | 1172 | cgrp = cgrp->parent; |
1162 | if (!cgrp) | 1173 | if (!cgrp) |
1163 | break; | 1174 | break; |
1175 | dentry = rcu_dereference(cgrp->dentry); | ||
1164 | if (!cgrp->parent) | 1176 | if (!cgrp->parent) |
1165 | continue; | 1177 | continue; |
1166 | if (--start < buf) | 1178 | if (--start < buf) |
@@ -1663,7 +1675,7 @@ static int cgroup_create_dir(struct cgroup *cgrp, struct dentry *dentry, | |||
1663 | if (!error) { | 1675 | if (!error) { |
1664 | dentry->d_fsdata = cgrp; | 1676 | dentry->d_fsdata = cgrp; |
1665 | inc_nlink(parent->d_inode); | 1677 | inc_nlink(parent->d_inode); |
1666 | cgrp->dentry = dentry; | 1678 | rcu_assign_pointer(cgrp->dentry, dentry); |
1667 | dget(dentry); | 1679 | dget(dentry); |
1668 | } | 1680 | } |
1669 | dput(dentry); | 1681 | dput(dentry); |