Re: [PATCH-next 2/6] cgroup/cpuset: Properly compute effective xcpus in update_parent_effective_cpumask()

From: Guopeng Zhang

Date: Thu Oct 22 2026 - 21:41:43 EST




在 2026/10/22 22:47, Waiman Long 写道:
> On 10/22/26 9:45 AM, Guopeng Zhang wrote:
>> Hi Longman,
>>
>> 在 2026/10/22 11:16, Waiman Long 写道:
>>> Since the introduction of cpuset.cpus.exclusive in commit
>>> e2ffe502ba45 ("cgroup/cpuset: Add cpuset.cpus.exclusive for v2"), a
>>> compute_effective_exclusive_cpumask() helper which is later renamed to
>>> compute_excpus() is added to compute the effective xcpus of a partition
>>> root. However update_parent_effective_cpumask() is not updated in all
>>> proper places to use the new helper to check if a given cpuset should
>>> remain a valid partition root. This can cause a cpuset to incorrectly
>>> remain a valid partition root even if no effective xcpus is left.
>>>
>>> Fix that problem by using compute_excpus() to compute the effective xcpus
>>> into delmask in all cases except partcmd_disable and partcmd_invalidate
>>> and use it for tasks_nocpu_error() check as well as checking its
>>> emptiness. This enables us to catch invalid partition root more
>>> accurately in update_parent_effective_cpumask().
>>>
>> I re-tested the repro from the link below on top of this series
>> (applied on cgroup/for-7.4-fixes), and it still fails:
>>
>>    https://lore.kernel.org/all/20261010075947.333549-3-guopeng.zhang@xxxxxxxxx/
>>
>>    cd /sys/fs/cgroup
>>    echo +cpuset > cgroup.subtree_control
>>    mkdir A
>>    echo 0-7 > A/cpuset.cpus
>>    echo 0-7 > A/cpuset.cpus.exclusive
>>    echo root > A/cpuset.cpus.partition
>>    cat A/cpuset.cpus.partition
>>    echo 1 > A/cpuset.cpus.exclusive
>>    cat A/cpuset.cpus.partition
>>
>> The last command still shows "root invalid (Parent unable to distribute
>> cpu downstream)".
>>
>> In this case compute_excpus() returns the stale user mask unchanged.
>> user_xcpus() is gone from update_parent_effective_cpumask(), but the
>> dependency just moved one level down: the new code calls
>> compute_excpus(cs, tmp->delmask), and its first line is
>>
>>    cpumask_and(excpus, user_xcpus(cs), parent->effective_xcpus)
>>
>> Here user_xcpus(cs) is cs->exclusive_cpus, which still holds the old
>> 0-7 when compute_excpus() runs: update_exclusive_cpumask() publishes
>> cs->exclusive_cpus only after partition_cpus_change(). Nothing corrects
>> it either - the sibling trim is skipped as exclusive_cpus is non-empty,
>> and for a direct child of the root cpuset the intersection with
>> parent->effective_xcpus is a no-op. So the stale 0-7 mask still reaches
>> tasks_nocpu_error() and PERR_NOCPUS is set again. Please point it out
>> if I got the analysis wrong.
>>
>> I have rebased my fix on top of this series and the partition recovers
>> correctly with it applied. Should the fix be folded into this patch,
>> or is there a better way to fix it? Happy to hear your thoughts.
> OK, I will look into that again.
>>
>> Also, is the test case below worth adding here as well? Note that it
>> fails until the fix is in:
>>
>>      # An invalid local partition that took all the CPUs from its
>>      # populated parent should recover after its cpuset.cpus.exclusive
>>      # list is changed to a valid one.
>>      "C0-7:X0-7:P1  .      .      .     X1      .      .      .     0 A1:1|XA1:1 A1:P1 ."
>
> This test case assumes that there are only 8 CPUs in the test system which may not be true in many cases. We need test case that will work no matter the system it is running on.
>
You are right, I wasn't thinking clearly and missed that rule.

Thanks,
Guopeng