|
@@ -1827,6 +1827,8 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
|
|
|
cgrp->self.cgroup = cgrp;
|
|
|
cgrp->self.flags |= CSS_ONLINE;
|
|
|
cgrp->dom_cgrp = cgrp;
|
|
|
+ cgrp->max_descendants = INT_MAX;
|
|
|
+ cgrp->max_depth = INT_MAX;
|
|
|
|
|
|
for_each_subsys(ss, ssid)
|
|
|
INIT_LIST_HEAD(&cgrp->e_csets[ssid]);
|
|
@@ -3209,6 +3211,92 @@ static ssize_t cgroup_type_write(struct kernfs_open_file *of, char *buf,
|
|
|
return ret ?: nbytes;
|
|
|
}
|
|
|
|
|
|
+static int cgroup_max_descendants_show(struct seq_file *seq, void *v)
|
|
|
+{
|
|
|
+ struct cgroup *cgrp = seq_css(seq)->cgroup;
|
|
|
+ int descendants = READ_ONCE(cgrp->max_descendants);
|
|
|
+
|
|
|
+ if (descendants == INT_MAX)
|
|
|
+ seq_puts(seq, "max\n");
|
|
|
+ else
|
|
|
+ seq_printf(seq, "%d\n", descendants);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t cgroup_max_descendants_write(struct kernfs_open_file *of,
|
|
|
+ char *buf, size_t nbytes, loff_t off)
|
|
|
+{
|
|
|
+ struct cgroup *cgrp;
|
|
|
+ int descendants;
|
|
|
+ ssize_t ret;
|
|
|
+
|
|
|
+ buf = strstrip(buf);
|
|
|
+ if (!strcmp(buf, "max")) {
|
|
|
+ descendants = INT_MAX;
|
|
|
+ } else {
|
|
|
+ ret = kstrtoint(buf, 0, &descendants);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (descendants < 0 || descendants > INT_MAX)
|
|
|
+ return -ERANGE;
|
|
|
+
|
|
|
+ cgrp = cgroup_kn_lock_live(of->kn, false);
|
|
|
+ if (!cgrp)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ cgrp->max_descendants = descendants;
|
|
|
+
|
|
|
+ cgroup_kn_unlock(of->kn);
|
|
|
+
|
|
|
+ return nbytes;
|
|
|
+}
|
|
|
+
|
|
|
+static int cgroup_max_depth_show(struct seq_file *seq, void *v)
|
|
|
+{
|
|
|
+ struct cgroup *cgrp = seq_css(seq)->cgroup;
|
|
|
+ int depth = READ_ONCE(cgrp->max_depth);
|
|
|
+
|
|
|
+ if (depth == INT_MAX)
|
|
|
+ seq_puts(seq, "max\n");
|
|
|
+ else
|
|
|
+ seq_printf(seq, "%d\n", depth);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t cgroup_max_depth_write(struct kernfs_open_file *of,
|
|
|
+ char *buf, size_t nbytes, loff_t off)
|
|
|
+{
|
|
|
+ struct cgroup *cgrp;
|
|
|
+ ssize_t ret;
|
|
|
+ int depth;
|
|
|
+
|
|
|
+ buf = strstrip(buf);
|
|
|
+ if (!strcmp(buf, "max")) {
|
|
|
+ depth = INT_MAX;
|
|
|
+ } else {
|
|
|
+ ret = kstrtoint(buf, 0, &depth);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (depth < 0 || depth > INT_MAX)
|
|
|
+ return -ERANGE;
|
|
|
+
|
|
|
+ cgrp = cgroup_kn_lock_live(of->kn, false);
|
|
|
+ if (!cgrp)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ cgrp->max_depth = depth;
|
|
|
+
|
|
|
+ cgroup_kn_unlock(of->kn);
|
|
|
+
|
|
|
+ return nbytes;
|
|
|
+}
|
|
|
+
|
|
|
static int cgroup_events_show(struct seq_file *seq, void *v)
|
|
|
{
|
|
|
seq_printf(seq, "populated %d\n",
|
|
@@ -4309,6 +4397,16 @@ static struct cftype cgroup_base_files[] = {
|
|
|
.file_offset = offsetof(struct cgroup, events_file),
|
|
|
.seq_show = cgroup_events_show,
|
|
|
},
|
|
|
+ {
|
|
|
+ .name = "cgroup.max.descendants",
|
|
|
+ .seq_show = cgroup_max_descendants_show,
|
|
|
+ .write = cgroup_max_descendants_write,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .name = "cgroup.max.depth",
|
|
|
+ .seq_show = cgroup_max_depth_show,
|
|
|
+ .write = cgroup_max_depth_write,
|
|
|
+ },
|
|
|
{ } /* terminate */
|
|
|
};
|
|
|
|
|
@@ -4662,6 +4760,29 @@ out_free_cgrp:
|
|
|
return ERR_PTR(ret);
|
|
|
}
|
|
|
|
|
|
+static bool cgroup_check_hierarchy_limits(struct cgroup *parent)
|
|
|
+{
|
|
|
+ struct cgroup *cgroup;
|
|
|
+ int ret = false;
|
|
|
+ int level = 1;
|
|
|
+
|
|
|
+ lockdep_assert_held(&cgroup_mutex);
|
|
|
+
|
|
|
+ for (cgroup = parent; cgroup; cgroup = cgroup_parent(cgroup)) {
|
|
|
+ if (cgroup->nr_descendants >= cgroup->max_descendants)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ if (level > cgroup->max_depth)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ level++;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = true;
|
|
|
+fail:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode)
|
|
|
{
|
|
|
struct cgroup *parent, *cgrp;
|
|
@@ -4676,6 +4797,11 @@ int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode)
|
|
|
if (!parent)
|
|
|
return -ENODEV;
|
|
|
|
|
|
+ if (!cgroup_check_hierarchy_limits(parent)) {
|
|
|
+ ret = -EAGAIN;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
cgrp = cgroup_create(parent);
|
|
|
if (IS_ERR(cgrp)) {
|
|
|
ret = PTR_ERR(cgrp);
|