aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2012-04-01 15:09:56 -0400
committerTejun Heo <tj@kernel.org>2012-04-01 15:09:56 -0400
commit05ef1d7c4a5b6d09cadd2b8e9b3c395363d1a89c (patch)
treec63adfca7c1d6a6858cd0cecf6f4eb83775c35d8
parentf6ea93723d0049ae5fbbb5292cb10c51d7a80801 (diff)
cgroup: introduce struct cfent
This patch adds cfent (cgroup file entry) which is the association between a cgroup and a file. This is in-cgroup representation of files under a cgroup directory. This simplifies walking walking cgroup files and thus cgroup_clear_directory(), which is now implemented in two parts - cgroup_rm_file() and a loop around it. cgroup_rm_file() will be used to implement cftype removal and cfent is scheduled to serve cgroup specific per-file data (e.g. for sysfs-like "sever" semantics). v2: - cfe was freed from cgroup_rm_file() which led to use-after-free if the file had openers at the time of removal. Moved to cgroup_diput(). - cgroup_clear_directory() triggered WARN_ON_ONCE() if d_subdirs wasn't empty after removing all files. This triggered spuriously if some files were open during directory clearing. Removed. v3: - In cgroup_diput(), WARN_ONCE(!list_empty(&cfe->node)) could be spuriously triggered for root cgroups because they don't go through cgroup_clear_directory() on unmount. Don't trigger WARN for root cgroups. Signed-off-by: Tejun Heo <tj@kernel.org> Acked-by: Li Zefan <lizf@cn.fujitsu.com> Cc: Glauber Costa <glommer@parallels.com>
-rw-r--r--include/linux/cgroup.h1
-rw-r--r--kernel/cgroup.c113
2 files changed, 78 insertions, 36 deletions
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index 7a3d96755114..87b034ed0c31 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -175,6 +175,7 @@ struct cgroup {
175 */ 175 */
176 struct list_head sibling; /* my parent's children */ 176 struct list_head sibling; /* my parent's children */
177 struct list_head children; /* my children */ 177 struct list_head children; /* my children */
178 struct list_head files; /* my files */
178 179
179 struct cgroup *parent; /* my parent */ 180 struct cgroup *parent; /* my parent */
180 struct dentry __rcu *dentry; /* cgroup fs entry, RCU protected */ 181 struct dentry __rcu *dentry; /* cgroup fs entry, RCU protected */
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 2ab29eb381b7..7d5d1c927d9d 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -148,6 +148,15 @@ struct cgroupfs_root {
148static struct cgroupfs_root rootnode; 148static struct cgroupfs_root rootnode;
149 149
150/* 150/*
151 * cgroupfs file entry, pointed to from leaf dentry->d_fsdata.
152 */
153struct cfent {
154 struct list_head node;
155 struct dentry *dentry;
156 struct cftype *type;
157};
158
159/*
151 * CSS ID -- ID per subsys's Cgroup Subsys State(CSS). used only when 160 * CSS ID -- ID per subsys's Cgroup Subsys State(CSS). used only when
152 * cgroup_subsys->use_id != 0. 161 * cgroup_subsys->use_id != 0.
153 */ 162 */
@@ -287,11 +296,16 @@ static inline struct cgroup *__d_cgrp(struct dentry *dentry)
287 return dentry->d_fsdata; 296 return dentry->d_fsdata;
288} 297}
289 298
290static inline struct cftype *__d_cft(struct dentry *dentry) 299static inline struct cfent *__d_cfe(struct dentry *dentry)
291{ 300{
292 return dentry->d_fsdata; 301 return dentry->d_fsdata;
293} 302}
294 303
304static inline struct cftype *__d_cft(struct dentry *dentry)
305{
306 return __d_cfe(dentry)->type;
307}
308
295/* the list of cgroups eligible for automatic release. Protected by 309/* the list of cgroups eligible for automatic release. Protected by
296 * release_list_lock */ 310 * release_list_lock */
297static LIST_HEAD(release_list); 311static LIST_HEAD(release_list);
@@ -877,6 +891,14 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
877 BUG_ON(!list_empty(&cgrp->pidlists)); 891 BUG_ON(!list_empty(&cgrp->pidlists));
878 892
879 kfree_rcu(cgrp, rcu_head); 893 kfree_rcu(cgrp, rcu_head);
894 } else {
895 struct cfent *cfe = __d_cfe(dentry);
896 struct cgroup *cgrp = dentry->d_parent->d_fsdata;
897
898 WARN_ONCE(!list_empty(&cfe->node) &&
899 cgrp != &cgrp->root->top_cgroup,
900 "cfe still linked for %s\n", cfe->type->name);
901 kfree(cfe);
880 } 902 }
881 iput(inode); 903 iput(inode);
882} 904}
@@ -895,34 +917,36 @@ static void remove_dir(struct dentry *d)
895 dput(parent); 917 dput(parent);
896} 918}
897 919
898static void cgroup_clear_directory(struct dentry *dentry) 920static int cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft)
899{ 921{
900 struct list_head *node; 922 struct cfent *cfe;
901 923
902 BUG_ON(!mutex_is_locked(&dentry->d_inode->i_mutex)); 924 lockdep_assert_held(&cgrp->dentry->d_inode->i_mutex);
903 spin_lock(&dentry->d_lock); 925 lockdep_assert_held(&cgroup_mutex);
904 node = dentry->d_subdirs.next; 926
905 while (node != &dentry->d_subdirs) { 927 list_for_each_entry(cfe, &cgrp->files, node) {
906 struct dentry *d = list_entry(node, struct dentry, d_u.d_child); 928 struct dentry *d = cfe->dentry;
907 929
908 spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); 930 if (cft && cfe->type != cft)
909 list_del_init(node); 931 continue;
910 if (d->d_inode) { 932
911 /* This should never be called on a cgroup 933 dget(d);
912 * directory with child cgroups */ 934 d_delete(d);
913 BUG_ON(d->d_inode->i_mode & S_IFDIR); 935 simple_unlink(d->d_inode, d);
914 dget_dlock(d); 936 list_del_init(&cfe->node);
915 spin_unlock(&d->d_lock); 937 dput(d);
916 spin_unlock(&dentry->d_lock); 938
917 d_delete(d); 939 return 0;
918 simple_unlink(dentry->d_inode, d);
919 dput(d);
920 spin_lock(&dentry->d_lock);
921 } else
922 spin_unlock(&d->d_lock);
923 node = dentry->d_subdirs.next;
924 } 940 }
925 spin_unlock(&dentry->d_lock); 941 return -ENOENT;
942}
943
944static void cgroup_clear_directory(struct dentry *dir)
945{
946 struct cgroup *cgrp = __d_cgrp(dir);
947
948 while (!list_empty(&cgrp->files))
949 cgroup_rm_file(cgrp, NULL);
926} 950}
927 951
928/* 952/*
@@ -1352,6 +1376,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
1352{ 1376{
1353 INIT_LIST_HEAD(&cgrp->sibling); 1377 INIT_LIST_HEAD(&cgrp->sibling);
1354 INIT_LIST_HEAD(&cgrp->children); 1378 INIT_LIST_HEAD(&cgrp->children);
1379 INIT_LIST_HEAD(&cgrp->files);
1355 INIT_LIST_HEAD(&cgrp->css_sets); 1380 INIT_LIST_HEAD(&cgrp->css_sets);
1356 INIT_LIST_HEAD(&cgrp->release_list); 1381 INIT_LIST_HEAD(&cgrp->release_list);
1357 INIT_LIST_HEAD(&cgrp->pidlists); 1382 INIT_LIST_HEAD(&cgrp->pidlists);
@@ -2619,7 +2644,9 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys,
2619 const struct cftype *cft) 2644 const struct cftype *cft)
2620{ 2645{
2621 struct dentry *dir = cgrp->dentry; 2646 struct dentry *dir = cgrp->dentry;
2647 struct cgroup *parent = __d_cgrp(dir);
2622 struct dentry *dentry; 2648 struct dentry *dentry;
2649 struct cfent *cfe;
2623 int error; 2650 int error;
2624 umode_t mode; 2651 umode_t mode;
2625 char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 }; 2652 char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 };
@@ -2635,17 +2662,31 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys,
2635 strcat(name, "."); 2662 strcat(name, ".");
2636 } 2663 }
2637 strcat(name, cft->name); 2664 strcat(name, cft->name);
2665
2638 BUG_ON(!mutex_is_locked(&dir->d_inode->i_mutex)); 2666 BUG_ON(!mutex_is_locked(&dir->d_inode->i_mutex));
2667
2668 cfe = kzalloc(sizeof(*cfe), GFP_KERNEL);
2669 if (!cfe)
2670 return -ENOMEM;
2671
2639 dentry = lookup_one_len(name, dir, strlen(name)); 2672 dentry = lookup_one_len(name, dir, strlen(name));
2640 if (!IS_ERR(dentry)) { 2673 if (IS_ERR(dentry)) {
2641 mode = cgroup_file_mode(cft);
2642 error = cgroup_create_file(dentry, mode | S_IFREG,
2643 cgrp->root->sb);
2644 if (!error)
2645 dentry->d_fsdata = (void *)cft;
2646 dput(dentry);
2647 } else
2648 error = PTR_ERR(dentry); 2674 error = PTR_ERR(dentry);
2675 goto out;
2676 }
2677
2678 mode = cgroup_file_mode(cft);
2679 error = cgroup_create_file(dentry, mode | S_IFREG, cgrp->root->sb);
2680 if (!error) {
2681 cfe->type = (void *)cft;
2682 cfe->dentry = dentry;
2683 dentry->d_fsdata = cfe;
2684 list_add_tail(&cfe->node, &parent->files);
2685 cfe = NULL;
2686 }
2687 dput(dentry);
2688out:
2689 kfree(cfe);
2649 return error; 2690 return error;
2650} 2691}
2651 2692