Commit 9ccece80 authored by Tejun Heo's avatar Tejun Heo

cgroup: fix cgroup_add_cftypes() error handling

cgroup_add_cftypes() uses cgroup_cfts_commit() to actually create the
files; however, both functions ignore actual file creation errors and
just assume success.  This can lead to, for example, blkio hierarchy
with some of the cgroups with only subset of interface files populated
after cfq-iosched is loaded under heavy memory pressure, which is
nasty.

This patch updates cgroup_cfts_commit() and cgroup_add_cftypes() to
guarantee that all files are created on success and no file is created
on failure.
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Acked-by: default avatarLi Zefan <lizefan@huawei.com>
parent b1f28d31
...@@ -2836,7 +2836,7 @@ static void cgroup_cfts_prepare(void) ...@@ -2836,7 +2836,7 @@ static void cgroup_cfts_prepare(void)
mutex_lock(&cgroup_mutex); mutex_lock(&cgroup_mutex);
} }
static void cgroup_cfts_commit(struct cgroup_subsys *ss, static int cgroup_cfts_commit(struct cgroup_subsys *ss,
struct cftype *cfts, bool is_add) struct cftype *cfts, bool is_add)
__releases(&cgroup_mutex) __releases(&cgroup_mutex)
{ {
...@@ -2846,12 +2846,13 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss, ...@@ -2846,12 +2846,13 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss,
struct dentry *prev = NULL; struct dentry *prev = NULL;
struct inode *inode; struct inode *inode;
u64 update_before; u64 update_before;
int ret = 0;
/* %NULL @cfts indicates abort and don't bother if @ss isn't attached */ /* %NULL @cfts indicates abort and don't bother if @ss isn't attached */
if (!cfts || ss->root == &cgroup_dummy_root || if (!cfts || ss->root == &cgroup_dummy_root ||
!atomic_inc_not_zero(&sb->s_active)) { !atomic_inc_not_zero(&sb->s_active)) {
mutex_unlock(&cgroup_mutex); mutex_unlock(&cgroup_mutex);
return; return 0;
} }
/* /*
...@@ -2867,10 +2868,13 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss, ...@@ -2867,10 +2868,13 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss,
inode = root->dentry->d_inode; inode = root->dentry->d_inode;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
mutex_lock(&cgroup_mutex); mutex_lock(&cgroup_mutex);
cgroup_addrm_files(root, ss, cfts, is_add); ret = cgroup_addrm_files(root, ss, cfts, is_add);
mutex_unlock(&cgroup_mutex); mutex_unlock(&cgroup_mutex);
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
if (ret)
goto out_deact;
/* add/rm files for all cgroups created before */ /* add/rm files for all cgroups created before */
rcu_read_lock(); rcu_read_lock();
cgroup_for_each_descendant_pre(cgrp, root) { cgroup_for_each_descendant_pre(cgrp, root) {
...@@ -2887,15 +2891,19 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss, ...@@ -2887,15 +2891,19 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss,
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
mutex_lock(&cgroup_mutex); mutex_lock(&cgroup_mutex);
if (cgrp->serial_nr < update_before && !cgroup_is_dead(cgrp)) if (cgrp->serial_nr < update_before && !cgroup_is_dead(cgrp))
cgroup_addrm_files(cgrp, ss, cfts, is_add); ret = cgroup_addrm_files(cgrp, ss, cfts, is_add);
mutex_unlock(&cgroup_mutex); mutex_unlock(&cgroup_mutex);
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
rcu_read_lock(); rcu_read_lock();
if (ret)
break;
} }
rcu_read_unlock(); rcu_read_unlock();
dput(prev); dput(prev);
out_deact:
deactivate_super(sb); deactivate_super(sb);
return ret;
} }
/** /**
...@@ -2915,6 +2923,7 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss, ...@@ -2915,6 +2923,7 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss,
int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
{ {
struct cftype_set *set; struct cftype_set *set;
int ret;
set = kzalloc(sizeof(*set), GFP_KERNEL); set = kzalloc(sizeof(*set), GFP_KERNEL);
if (!set) if (!set)
...@@ -2923,9 +2932,10 @@ int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) ...@@ -2923,9 +2932,10 @@ int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
cgroup_cfts_prepare(); cgroup_cfts_prepare();
set->cfts = cfts; set->cfts = cfts;
list_add_tail(&set->node, &ss->cftsets); list_add_tail(&set->node, &ss->cftsets);
cgroup_cfts_commit(ss, cfts, true); ret = cgroup_cfts_commit(ss, cfts, true);
if (ret)
return 0; cgroup_rm_cftypes(ss, cfts);
return ret;
} }
EXPORT_SYMBOL_GPL(cgroup_add_cftypes); EXPORT_SYMBOL_GPL(cgroup_add_cftypes);
......
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