[PATCH v2 3/3] cgroup/cpuset: Improve check for v1 task migration out of empty cpuset

Waiman Long posted 3 patches 3 days, 18 hours ago
There is a newer version of this series
[PATCH v2 3/3] cgroup/cpuset: Improve check for v1 task migration out of empty cpuset
Posted by Waiman Long 3 days, 18 hours ago
With the "cpuset_v2_mode" mount option, cpuset v1 will emulate v2 in
how it handles the management of CPUs. As a result, the effective_cpus
can differ from cpus_allowed and an empty cpus_allowed will cause
effective_cpus to inherit the value from its parent. Therefore task
migration out of a cpuset with empty "cpuset.cpus" should no longer
be needed in this case.

The current code doesn't handle this particular case. Update the code to
correctly detect that the cpuset has really no CPUs left by checking
effective_cpus instead of cpus_allowed.

Signed-off-by: Waiman Long <longman@redhat.com>
---
 kernel/cgroup/cpuset-v1.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/kernel/cgroup/cpuset-v1.c b/kernel/cgroup/cpuset-v1.c
index 0c818edd0a1d..9855de37d011 100644
--- a/kernel/cgroup/cpuset-v1.c
+++ b/kernel/cgroup/cpuset-v1.c
@@ -261,7 +261,7 @@ static void remove_tasks_in_empty_cpuset(struct cpuset *cs)
 	 * has online cpus, so can't be empty).
 	 */
 	parent = parent_cs(cs);
-	while (cpumask_empty(parent->cpus_allowed) ||
+	while (cpumask_empty(parent->effective_cpus) ||
 			nodes_empty(parent->mems_allowed))
 		parent = parent_cs(parent);
 
@@ -297,14 +297,16 @@ void cpuset1_hotplug_update_tasks(struct cpuset *cs,
 
 	/*
 	 * Don't call cpuset_update_tasks_cpumask() if the cpuset becomes empty,
-	 * as the tasks will be migrated to an ancestor.
+	 * as the tasks will be migrated to an ancestor. If cpuset_v2_mode mount
+	 * option is used, effective_cpus can differ from cpus_allowed. So
+	 * checking effective_cpus is more accurate for determining emptiness.
 	 */
-	if (cpus_updated && !cpumask_empty(cs->cpus_allowed))
+	if (cpus_updated && !cpumask_empty(cs->effective_cpus))
 		cpuset_update_tasks_cpumask(cs, new_cpus);
 	if (mems_updated && !nodes_empty(cs->mems_allowed))
 		cpuset_update_tasks_nodemask(cs);
 
-	is_empty = cpumask_empty(cs->cpus_allowed) ||
+	is_empty = cpumask_empty(cs->effective_cpus) ||
 		   nodes_empty(cs->mems_allowed);
 
 	/*
-- 
2.53.0
Re: [PATCH v2 3/3] cgroup/cpuset: Improve check for v1 task migration out of empty cpuset
Posted by Tejun Heo 2 days, 17 hours ago
On Sun, Mar 29, 2026 at 01:39:58PM -0400, Waiman Long wrote:
> With the "cpuset_v2_mode" mount option, cpuset v1 will emulate v2 in
> how it handles the management of CPUs. As a result, the effective_cpus
> can differ from cpus_allowed and an empty cpus_allowed will cause
> effective_cpus to inherit the value from its parent. Therefore task
> migration out of a cpuset with empty "cpuset.cpus" should no longer
> be needed in this case.
> 
> The current code doesn't handle this particular case. Update the code to
> correctly detect that the cpuset has really no CPUs left by checking
> effective_cpus instead of cpus_allowed.
> 
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
>  kernel/cgroup/cpuset-v1.c | 10 ++++++----
>  1 file changed, 6 insertions(+), 4 deletions(-)
> 
> diff --git a/kernel/cgroup/cpuset-v1.c b/kernel/cgroup/cpuset-v1.c
> index 0c818edd0a1d..9855de37d011 100644
> --- a/kernel/cgroup/cpuset-v1.c
> +++ b/kernel/cgroup/cpuset-v1.c
> @@ -261,7 +261,7 @@ static void remove_tasks_in_empty_cpuset(struct cpuset *cs)
>  	 * has online cpus, so can't be empty).
>  	 */
>  	parent = parent_cs(cs);
> -	while (cpumask_empty(parent->cpus_allowed) ||
> +	while (cpumask_empty(parent->effective_cpus) ||
>  			nodes_empty(parent->mems_allowed))
>  		parent = parent_cs(parent);
>  
> @@ -297,14 +297,16 @@ void cpuset1_hotplug_update_tasks(struct cpuset *cs,
>  
>  	/*
>  	 * Don't call cpuset_update_tasks_cpumask() if the cpuset becomes empty,
> -	 * as the tasks will be migrated to an ancestor.
> +	 * as the tasks will be migrated to an ancestor. If cpuset_v2_mode mount
> +	 * option is used, effective_cpus can differ from cpus_allowed. So
> +	 * checking effective_cpus is more accurate for determining emptiness.
>  	 */
> -	if (cpus_updated && !cpumask_empty(cs->cpus_allowed))
> +	if (cpus_updated && !cpumask_empty(cs->effective_cpus))
>  		cpuset_update_tasks_cpumask(cs, new_cpus);
>  	if (mems_updated && !nodes_empty(cs->mems_allowed))
>  		cpuset_update_tasks_nodemask(cs);
>  
> -	is_empty = cpumask_empty(cs->cpus_allowed) ||
> +	is_empty = cpumask_empty(cs->effective_cpus) ||
>  		   nodes_empty(cs->mems_allowed);

Are these meaningful changes? cpuset1_hotplug_update_tasks() is called by
cpuset_hotplug_update_tasks() when !is_in_v2_mode(). As is_in_v2_mode() says
yes on cpuset_v2_mode, the above change doesn't really change anything, no?

Thanks.

-- 
tejun
Re: [PATCH v2 3/3] cgroup/cpuset: Improve check for v1 task migration out of empty cpuset
Posted by Waiman Long 2 days, 11 hours ago
On 3/30/26 2:25 PM, Tejun Heo wrote:
> On Sun, Mar 29, 2026 at 01:39:58PM -0400, Waiman Long wrote:
>> With the "cpuset_v2_mode" mount option, cpuset v1 will emulate v2 in
>> how it handles the management of CPUs. As a result, the effective_cpus
>> can differ from cpus_allowed and an empty cpus_allowed will cause
>> effective_cpus to inherit the value from its parent. Therefore task
>> migration out of a cpuset with empty "cpuset.cpus" should no longer
>> be needed in this case.
>>
>> The current code doesn't handle this particular case. Update the code to
>> correctly detect that the cpuset has really no CPUs left by checking
>> effective_cpus instead of cpus_allowed.
>>
>> Signed-off-by: Waiman Long <longman@redhat.com>
>> ---
>>   kernel/cgroup/cpuset-v1.c | 10 ++++++----
>>   1 file changed, 6 insertions(+), 4 deletions(-)
>>
>> diff --git a/kernel/cgroup/cpuset-v1.c b/kernel/cgroup/cpuset-v1.c
>> index 0c818edd0a1d..9855de37d011 100644
>> --- a/kernel/cgroup/cpuset-v1.c
>> +++ b/kernel/cgroup/cpuset-v1.c
>> @@ -261,7 +261,7 @@ static void remove_tasks_in_empty_cpuset(struct cpuset *cs)
>>   	 * has online cpus, so can't be empty).
>>   	 */
>>   	parent = parent_cs(cs);
>> -	while (cpumask_empty(parent->cpus_allowed) ||
>> +	while (cpumask_empty(parent->effective_cpus) ||
>>   			nodes_empty(parent->mems_allowed))
>>   		parent = parent_cs(parent);
>>   
>> @@ -297,14 +297,16 @@ void cpuset1_hotplug_update_tasks(struct cpuset *cs,
>>   
>>   	/*
>>   	 * Don't call cpuset_update_tasks_cpumask() if the cpuset becomes empty,
>> -	 * as the tasks will be migrated to an ancestor.
>> +	 * as the tasks will be migrated to an ancestor. If cpuset_v2_mode mount
>> +	 * option is used, effective_cpus can differ from cpus_allowed. So
>> +	 * checking effective_cpus is more accurate for determining emptiness.
>>   	 */
>> -	if (cpus_updated && !cpumask_empty(cs->cpus_allowed))
>> +	if (cpus_updated && !cpumask_empty(cs->effective_cpus))
>>   		cpuset_update_tasks_cpumask(cs, new_cpus);
>>   	if (mems_updated && !nodes_empty(cs->mems_allowed))
>>   		cpuset_update_tasks_nodemask(cs);
>>   
>> -	is_empty = cpumask_empty(cs->cpus_allowed) ||
>> +	is_empty = cpumask_empty(cs->effective_cpus) ||
>>   		   nodes_empty(cs->mems_allowed);
> Are these meaningful changes? cpuset1_hotplug_update_tasks() is called by
> cpuset_hotplug_update_tasks() when !is_in_v2_mode(). As is_in_v2_mode() says
> yes on cpuset_v2_mode, the above change doesn't really change anything, no?

You are right. I missed that part. So it is really a no-op then. Will 
drop this patch.

Thanks!
Longman