Offering: HULK hulk inclusion category: bugfix bugzilla: https://atomgit.com/openeuler/kernel/issues/10338 -------------------------------- Commit 7fd4da9c1584 ("cgroup/cpuset: Optimize cpuset_attach() on v2") skips the whole task iteration in cpuset_attach() when the destination cpuset has the same effective cpus/mems as the source one. The fix for prefer_cpus ineffectiveness (5ce00cbd095c) extended that condition with prefer_cpus_updated(), which compares the *destination* cpuset's prefer_cpus with the *source* cpuset's. That is not a valid proxy for "the tasks do not need updating", because a task's prefer_cpus is not guaranteed to match the prefer_cpus of the cpuset it belongs to: - /proc/<pid>/preferred_cpuset sets the per-task mask directly, bypassing the cgroup; - update_tasks_prefer_cpumask() pushes cs->prefer_cpus only when the cgroup file is written, and the push is dropped silently when __set_prefer_cpus_ptr() rejects it, e.g. because the task is pinned to a narrower cpumask (new_mask not a subset of p->cpus_ptr), or because the task is not on fair_sched_class; - sched_setaffinity() only rewrites p->cpus_mask, it does not re-synchronize p->prefer_cpus. Consequently, when a task with a stale/empty per-task mask is moved between two cpusets that are configured with the same prefer_cpus, the fast path skips cpuset_attach_task() and the stale value survives forever. The dynamic affinity feature then keeps selecting p->cpus_ptr in set_task_select_cpus() because prefer_cpus_valid() fails, so the task never runs on the preferred CPUs even though the destination cgroup is configured correctly. Fix this by checking the per-task state instead: skip the attach only when applying cs->prefer_cpus to every task in the migration taskset is really a no-op. A task whose prefer_cpus is NULL does not support the feature and is ignored. The per-task masks are inert when neither dynamic affinity nor PARAL is in use, so keep the fast path in that case. This also makes migrating a task into a cpuset whose prefer_cpus is empty clear the stale per-task mask, as expected from the "empty prefer_cpus disables the feature" semantics documented in update_prefer_cpumask(). Note that the old prefer_cpus_updated() helper becomes unused and is removed. Fixes: 5ce00cbd095c ("cpuset: Fix prefer_cpus ineffectiveness by tracking prefer_cpus") Signed-off-by: Yao Yiqi <yaoyiqi3@huawei.com> --- kernel/cgroup/cpuset.c | 56 +++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index be932fb6cc79..55f695285a1f 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -1040,16 +1040,6 @@ static int update_prefer_cpumask(struct cpuset *cs, struct cpuset *trialcs, return 0; } - -static inline bool prefer_cpus_updated(struct cpuset *cs, struct cpuset *oldcs) -{ - return !cpumask_equal(cs->prefer_cpus, oldcs->prefer_cpus); -} -#else -static inline bool prefer_cpus_updated(struct cpuset *cs, struct cpuset *oldcs) -{ - return false; -} #endif #ifdef CONFIG_SMP @@ -3613,6 +3603,49 @@ static void cpuset_attach_task(struct cpuset *cs, struct task_struct *task) cpuset_update_task_spread_flags(cs, task); } +#ifdef CONFIG_QOS_SCHED_DYNAMIC_AFFINITY +/* + * Check if pushing @cs->prefer_cpus to all the tasks of @tset is a no-op. + * + * The per-task value has to be used instead of comparing @cs against the old + * cpuset: a task's prefer_cpus may legitimately differ from the cpuset's + * configuration, e.g. it has been overridden via + * /proc/<pid>/preferred_cpuset, or a previous push was rejected because the + * task was pinned to a narrower cpumask. Comparing cpusets would skip the + * attach in that case and leave the task with a stale mask. + * + * A task with a NULL prefer_cpus does not support this feature and is + * ignored. + */ +static bool tasks_prefer_cpus_unchanged(struct cgroup_taskset *tset, + struct cpuset *cs) +{ + struct cgroup_subsys_state *css; + struct task_struct *task; + + /* + * The per-task masks are inert when both features are off, so there is + * nothing to synchronize. Keep this in sync with prefer_cpus_valid(). + */ + if (likely(!dynamic_affinity_enabled() && !sched_paral_used())) + return true; + + cgroup_taskset_for_each(task, css, tset) { + if (task->prefer_cpus && + !cpumask_equal(task->prefer_cpus, cs->prefer_cpus)) + return false; + } + + return true; +} +#else +static inline bool tasks_prefer_cpus_unchanged(struct cgroup_taskset *tset, + struct cpuset *cs) +{ + return true; +} +#endif + static void cpuset_attach(struct cgroup_taskset *tset) { struct task_struct *task; @@ -3638,7 +3671,8 @@ static void cpuset_attach(struct cgroup_taskset *tset) * by skipping the task iteration and update. */ if (cgroup_subsys_on_dfl(cpuset_cgrp_subsys) && - !cpus_updated && !mems_updated && !prefer_cpus_updated(cs, oldcs)) { + !cpus_updated && !mems_updated && + tasks_prefer_cpus_unchanged(tset, cs)) { cpuset_attach_nodemask_to = cs->effective_mems; goto out; } -- 2.34.1