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]);
        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",
                .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 */
 };
 
        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;
        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);