Skip to contentSkip to conversation

cpuset: Allow v2 behavior in v1 cgroup

PATCH Superseded Waiman Long 2 participants 5 messages Aug 17, 2017

Subsystems (2)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore

Waiman Long

author maintainer
#0 lore raw

v2 cover cpuset: Allow v2 behavior in v1 cgroup

v1->v2:
- Drop the kernel command line option and use cgroupfs mount option instead to enable v2 controller behavior in v1 cgroup.

v1: https://lkml.org/lkml/2017/8/15/570

This patchset adds a new cgroupfs mount option "v2_mode" that serves as a hint that a v1 controller can use v2 hehavior, if applicable. The cpuset controller is then modified to observe this mode.

Patch 1 adds the v1 cgroupfs mount option "v2_mode" which set a flag in the cgroup_root flags.

Patch 2 modifies the cpuset controller to check the cgroup_root flag and switch to v2 behavior even in a v1 cgroup.

Waiman Long (2):
cgroup: Add a hint to allow v1 controller to use v2 behavior
cpuset: Allow v2 behavior in v1 cgroup

 include/linux/cgroup-defs.h |  5 +++++
 kernel/cgroup/cgroup-v1.c   |  6 ++++++
 kernel/cgroup/cpuset.c      | 33 ++++++++++++++++++++-------------
 3 files changed, 31 insertions(+), 13 deletions(-)
Signature · 1 line
1.8.3.1
author maintainer
#1 lore raw

PATCH v2 1/2 cgroup: Add a hint to allow v1 controller to use v2 behavior

For some cgroup controllers, the behavior on cgroup v1 differs somewhat from cgroup v2. To indicate that a controller can use v2 behavior in cgroup v1, a new cgroupfs mount option "v2_mode" is added. This is essentially a hint, there is no obligation for a controller to comply with this semantics.

---
 include/linux/cgroup-defs.h | 5 +++++
 kernel/cgroup/cgroup-v1.c   | 6 ++++++
 2 files changed, 11 insertions(+)
include/linux/cgroup-defs.h +5 -0
index 09f4c7d..fae33ed 100644
@@ -74,6 +74,11 @@ enum {
 	 * aren't writeable from inside the namespace.
 	 */
 	CGRP_ROOT_NS_DELEGATE	= (1 << 3),
+
+	/*
+	 * A hint that v1 cgroup should use v2 behavior, if applicable.
+	 */
+	CGRP_ROOT_V2_MODE	= (1 << 4),
 };
 
 /* cftype->flags */
kernel/cgroup/cgroup-v1.c +6 -0
index 7bf4b15..77e22a4 100644
@@ -846,6 +846,8 @@ static int cgroup1_show_options(struct seq_file *seq, struct kernfs_root *kf_roo
 		seq_puts(seq, ",noprefix");
 	if (root->flags & CGRP_ROOT_XATTR)
 		seq_puts(seq, ",xattr");
+	if (root->flags & CGRP_ROOT_V2_MODE)
+		seq_puts(seq, ",v2_mode");
 
 	spin_lock(&release_agent_path_lock);
 	if (strlen(root->release_agent_path))
@@ -900,6 +902,10 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
 			opts->cpuset_clone_children = true;
 			continue;
 		}
+		if (!strcmp(token, "v2_mode")) {
+			opts->flags |= CGRP_ROOT_V2_MODE;
+			continue;
+		}
 		if (!strcmp(token, "xattr")) {
 			opts->flags |= CGRP_ROOT_XATTR;
 			continue;
Signature · 1 line
1.8.3.1
maintainer
#2 lore raw

Re: [PATCH v2 1/2] cgroup: Add a hint to allow v1 controller to use v2 behavior

Hello, Waiman.

Quoted 5 lines · Waiman Long

For some cgroup controllers, the behavior on cgroup v1 differs somewhat from cgroup v2. To indicate that a controller can use v2 behavior in cgroup v1, a new cgroupfs mount option "v2_mode" is added. This is essentially a hint, there is no obligation for a controller to comply with this semantics.

Heh, I don't think we can make it a generic flag. We can't have one kernel interpreting the option one way and later kernels doing differently. Let's just make it cpuset specific - something like cpuset_v2_mode.

Thanks.

Signature · 1 line
tejun
author maintainer
#3 lore raw

Re: [PATCH v2 1/2] cgroup: Add a hint to allow v1 controller to use v2 behavior

Quoted 8 lines · Tejun Heo

Hello, Waiman.

Quoted 5 lines · Waiman Long

For some cgroup controllers, the behavior on cgroup v1 differs somewhat from cgroup v2. To indicate that a controller can use v2 behavior in cgroup v1, a new cgroupfs mount option "v2_mode" is added. This is essentially a hint, there is no obligation for a controller to comply with this semantics.

Quoted 7 lines

Heh, I don't think we can make it a generic flag. We can't have one kernel interpreting the option one way and later kernels doing differently. Let's just make it cpuset specific - something like cpuset_v2_mode.

Thanks.

That is fine for me. I will update the patchset accordingly.

Thanks,
Longman

author maintainer
#4 lore raw

PATCH v2 2/2 cpuset: Allow v2 behavior in v1 cgroup

In reply to Waiman Long

Cpuset v2 has some useful behaviors that are not present in v1 because of backward compatibility concern. One of that is the restoration of the original cpu and memory node mask after a hot removal and addition event sequence.

This patch makes the cpuset controller to check the CGRP_ROOT_V2_MODE flag and use the v2 behavior if it is set.

---
 kernel/cgroup/cpuset.c | 33 ++++++++++++++++++++-------------
 1 file changed, 20 insertions(+), 13 deletions(-)
kernel/cgroup/cpuset.c +20 -13
index 9ed6a05..1c44df7 100644
@@ -300,6 +300,16 @@ static inline int is_spread_slab(const struct cpuset *cs)
 static DECLARE_WAIT_QUEUE_HEAD(cpuset_attach_wq);
 
 /*
+ * Cgroup v2 behavior is used when on default hierarchy or the
+ * cgroup_v2_mode flag is set.
+ */
+static inline bool is_in_v2_mode(void)
+{
+	return cgroup_subsys_on_dfl(cpuset_cgrp_subsys) ||
+	      (cpuset_cgrp_subsys.root->flags & CGRP_ROOT_V2_MODE);
+}
+
+/*
  * This is ugly, but preserves the userspace API for existing cpuset
  * users. If someone tries to mount the "cpuset" filesystem, we
  * silently switch it to mount "cgroup" instead
@@ -489,8 +499,7 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial)
 
 	/* On legacy hiearchy, we must be a subset of our parent cpuset. */
 	ret = -EACCES;
-	if (!cgroup_subsys_on_dfl(cpuset_cgrp_subsys) &&
-	    !is_cpuset_subset(trial, par))
+	if (!is_in_v2_mode() && !is_cpuset_subset(trial, par))
 		goto out;
 
 	/*
@@ -903,8 +912,7 @@ static void update_cpumasks_hier(struct cpuset *cs, struct cpumask *new_cpus)
 		 * If it becomes empty, inherit the effective mask of the
 		 * parent, which is guaranteed to have some CPUs.
 		 */
-		if (cgroup_subsys_on_dfl(cpuset_cgrp_subsys) &&
-		    cpumask_empty(new_cpus))
+		if (is_in_v2_mode() && cpumask_empty(new_cpus))
 			cpumask_copy(new_cpus, parent->effective_cpus);
 
 		/* Skip the whole subtree if the cpumask remains the same. */
@@ -921,7 +929,7 @@ static void update_cpumasks_hier(struct cpuset *cs, struct cpumask *new_cpus)
 		cpumask_copy(cp->effective_cpus, new_cpus);
 		spin_unlock_irq(&callback_lock);
 
-		WARN_ON(!cgroup_subsys_on_dfl(cpuset_cgrp_subsys) &&
+		WARN_ON(!is_in_v2_mode() &&
 			!cpumask_equal(cp->cpus_allowed, cp->effective_cpus));
 
 		update_tasks_cpumask(cp);
@@ -1157,8 +1165,7 @@ static void update_nodemasks_hier(struct cpuset *cs, nodemask_t *new_mems)
 		 * If it becomes empty, inherit the effective mask of the
 		 * parent, which is guaranteed to have some MEMs.
 		 */
-		if (cgroup_subsys_on_dfl(cpuset_cgrp_subsys) &&
-		    nodes_empty(*new_mems))
+		if (is_in_v2_mode() && nodes_empty(*new_mems))
 			*new_mems = parent->effective_mems;
 
 		/* Skip the whole subtree if the nodemask remains the same. */
@@ -1175,7 +1182,7 @@ static void update_nodemasks_hier(struct cpuset *cs, nodemask_t *new_mems)
 		cp->effective_mems = *new_mems;
 		spin_unlock_irq(&callback_lock);
 
-		WARN_ON(!cgroup_subsys_on_dfl(cpuset_cgrp_subsys) &&
+		WARN_ON(!is_in_v2_mode() &&
 			!nodes_equal(cp->mems_allowed, cp->effective_mems));
 
 		update_tasks_nodemask(cp);
@@ -1467,7 +1474,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
 
 	/* allow moving tasks into an empty cpuset if on default hierarchy */
 	ret = -ENOSPC;
-	if (!cgroup_subsys_on_dfl(cpuset_cgrp_subsys) &&
+	if (!is_in_v2_mode() &&
 	    (cpumask_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed)))
 		goto out_unlock;
 
@@ -1985,7 +1992,7 @@ static int cpuset_css_online(struct cgroup_subsys_state *css)
 	cpuset_inc();
 
 	spin_lock_irq(&callback_lock);
-	if (cgroup_subsys_on_dfl(cpuset_cgrp_subsys)) {
+	if (is_in_v2_mode()) {
 		cpumask_copy(cs->effective_cpus, parent->effective_cpus);
 		cs->effective_mems = parent->effective_mems;
 	}
@@ -2062,7 +2069,7 @@ static void cpuset_bind(struct cgroup_subsys_state *root_css)
 	mutex_lock(&cpuset_mutex);
 	spin_lock_irq(&callback_lock);
 
-	if (cgroup_subsys_on_dfl(cpuset_cgrp_subsys)) {
+	if (is_in_v2_mode()) {
 		cpumask_copy(top_cpuset.cpus_allowed, cpu_possible_mask);
 		top_cpuset.mems_allowed = node_possible_map;
 	} else {
@@ -2256,7 +2263,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs)
 	cpus_updated = !cpumask_equal(&new_cpus, cs->effective_cpus);
 	mems_updated = !nodes_equal(new_mems, cs->effective_mems);
 
-	if (cgroup_subsys_on_dfl(cpuset_cgrp_subsys))
+	if (is_in_v2_mode())
 		hotplug_update_tasks(cs, &new_cpus, &new_mems,
 				     cpus_updated, mems_updated);
 	else
@@ -2287,7 +2294,7 @@ static void cpuset_hotplug_workfn(struct work_struct *work)
 	static cpumask_t new_cpus;
 	static nodemask_t new_mems;
 	bool cpus_updated, mems_updated;
-	bool on_dfl = cgroup_subsys_on_dfl(cpuset_cgrp_subsys);
+	bool on_dfl = is_in_v2_mode();
 
 	mutex_lock(&cpuset_mutex);
 
Signature · 1 line
1.8.3.1