|
@@ -149,6 +149,10 @@ struct mem_cgroup {
|
|
* reclaimed from. Protected by cgroup_lock()
|
|
* reclaimed from. Protected by cgroup_lock()
|
|
*/
|
|
*/
|
|
struct mem_cgroup *last_scanned_child;
|
|
struct mem_cgroup *last_scanned_child;
|
|
|
|
+ /*
|
|
|
|
+ * Should the accounting and control be hierarchical, per subtree?
|
|
|
|
+ */
|
|
|
|
+ bool use_hierarchy;
|
|
|
|
|
|
int obsolete;
|
|
int obsolete;
|
|
atomic_t refcnt;
|
|
atomic_t refcnt;
|
|
@@ -1543,6 +1547,44 @@ int mem_cgroup_force_empty_write(struct cgroup *cont, unsigned int event)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+static u64 mem_cgroup_hierarchy_read(struct cgroup *cont, struct cftype *cft)
|
|
|
|
+{
|
|
|
|
+ return mem_cgroup_from_cont(cont)->use_hierarchy;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int mem_cgroup_hierarchy_write(struct cgroup *cont, struct cftype *cft,
|
|
|
|
+ u64 val)
|
|
|
|
+{
|
|
|
|
+ int retval = 0;
|
|
|
|
+ struct mem_cgroup *mem = mem_cgroup_from_cont(cont);
|
|
|
|
+ struct cgroup *parent = cont->parent;
|
|
|
|
+ struct mem_cgroup *parent_mem = NULL;
|
|
|
|
+
|
|
|
|
+ if (parent)
|
|
|
|
+ parent_mem = mem_cgroup_from_cont(parent);
|
|
|
|
+
|
|
|
|
+ cgroup_lock();
|
|
|
|
+ /*
|
|
|
|
+ * If parent's use_hiearchy is set, we can't make any modifications
|
|
|
|
+ * in the child subtrees. If it is unset, then the change can
|
|
|
|
+ * occur, provided the current cgroup has no children.
|
|
|
|
+ *
|
|
|
|
+ * For the root cgroup, parent_mem is NULL, we allow value to be
|
|
|
|
+ * set if there are no children.
|
|
|
|
+ */
|
|
|
|
+ if ((!parent_mem || !parent_mem->use_hierarchy) &&
|
|
|
|
+ (val == 1 || val == 0)) {
|
|
|
|
+ if (list_empty(&cont->children))
|
|
|
|
+ mem->use_hierarchy = val;
|
|
|
|
+ else
|
|
|
|
+ retval = -EBUSY;
|
|
|
|
+ } else
|
|
|
|
+ retval = -EINVAL;
|
|
|
|
+ cgroup_unlock();
|
|
|
|
+
|
|
|
|
+ return retval;
|
|
|
|
+}
|
|
|
|
+
|
|
static u64 mem_cgroup_read(struct cgroup *cont, struct cftype *cft)
|
|
static u64 mem_cgroup_read(struct cgroup *cont, struct cftype *cft)
|
|
{
|
|
{
|
|
struct mem_cgroup *mem = mem_cgroup_from_cont(cont);
|
|
struct mem_cgroup *mem = mem_cgroup_from_cont(cont);
|
|
@@ -1706,6 +1748,11 @@ static struct cftype mem_cgroup_files[] = {
|
|
.name = "force_empty",
|
|
.name = "force_empty",
|
|
.trigger = mem_cgroup_force_empty_write,
|
|
.trigger = mem_cgroup_force_empty_write,
|
|
},
|
|
},
|
|
|
|
+ {
|
|
|
|
+ .name = "use_hierarchy",
|
|
|
|
+ .write_u64 = mem_cgroup_hierarchy_write,
|
|
|
|
+ .read_u64 = mem_cgroup_hierarchy_read,
|
|
|
|
+ },
|
|
};
|
|
};
|
|
|
|
|
|
#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
|
|
#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
|
|
@@ -1881,12 +1928,18 @@ mem_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cont)
|
|
if (cont->parent == NULL) {
|
|
if (cont->parent == NULL) {
|
|
enable_swap_cgroup();
|
|
enable_swap_cgroup();
|
|
parent = NULL;
|
|
parent = NULL;
|
|
- } else
|
|
|
|
|
|
+ } else {
|
|
parent = mem_cgroup_from_cont(cont->parent);
|
|
parent = mem_cgroup_from_cont(cont->parent);
|
|
|
|
+ mem->use_hierarchy = parent->use_hierarchy;
|
|
|
|
+ }
|
|
|
|
|
|
- res_counter_init(&mem->res, parent ? &parent->res : NULL);
|
|
|
|
- res_counter_init(&mem->memsw, parent ? &parent->memsw : NULL);
|
|
|
|
-
|
|
|
|
|
|
+ if (parent && parent->use_hierarchy) {
|
|
|
|
+ res_counter_init(&mem->res, &parent->res);
|
|
|
|
+ res_counter_init(&mem->memsw, &parent->memsw);
|
|
|
|
+ } else {
|
|
|
|
+ res_counter_init(&mem->res, NULL);
|
|
|
|
+ res_counter_init(&mem->memsw, NULL);
|
|
|
|
+ }
|
|
|
|
|
|
mem->last_scanned_child = NULL;
|
|
mem->last_scanned_child = NULL;
|
|
|
|
|