From: Chen Yu <yu.c.chen@intel.com>
To: Vincent Guittot <vincent.guittot@linaro.org>
Cc: <mingo@redhat.com>, <peterz@infradead.org>,
<juri.lelli@redhat.com>, <dietmar.eggemann@arm.com>,
<rostedt@goodmis.org>, <bsegall@google.com>, <mgorman@suse.de>,
<vschneid@redhat.com>, <kprateek.nayak@amd.com>,
<linux-kernel@vger.kernel.org>, <lukasz.luba@arm.com>,
<rafael@kernel.org>, <linux-pm@vger.kernel.org>, <tj@kernel.org>,
<void@manifault.com>, <arighi@nvidia.com>, <changwoo@igalia.com>,
<sched-ext@lists.linux.dev>, <qyousef@layalina.io>,
<christian.loehle@arm.com>, <pierre.gondois@arm.com>,
<sshegde@linux.ibm.com>, <chen.yu@linux.dev>
Subject: Re: [PATCH 07/18] sched/fair: Add push task mechanism for fair
Date: Wed, 7 Oct 2026 10:45:58 +0800 [thread overview]
Message-ID: <asWyZtW7zPVo3k-A@chenyu-dev> (raw)
In-Reply-To: <20261002154415.2270586-8-vincent.guittot@linaro.org>
On Fri, Oct 02, 2026 at 05:44:04PM +0200, Vincent Guittot wrote:
> EAS is based on wakeup events to efficiently place tasks on the system, but
> there are cases where a task doesn't have wakeup events anymore or at a far
> too low pace. For such situation, we can take advantage of the task being
> put back in the enqueued list to check if it should be pushed on another
> CPU.
>
> Add a push task mechanism that enables fair scheduler to push runnable
> tasks. EAS will be one user but other feature like filling idle CPUs or
> short slice tasks can also take advantage of it.
>
A hackbench was launched on a Xeon server with 192 cores, using the default
settings to see if there was any regression. According to the test results,
there is no obvious difference, so I think it is good now. Later, I'll also
test with a mix of different time slice workloads:
=========================================
Hackbench comparison
BASE: baseline
TEST: push
=========================================
MODE G FD | BASE(s) | TEST(s) | DIFF% | RESULT
------- -- ----+-----------------+-----------------+---------+----------
process 16 20 | 76.438/0.2% | 76.310/0.1% | 0.17% | IMPROVED
process 1 20 | 52.532/2.3% | 51.217/1.1% | 2.50% | IMPROVED
process 4 20 | 61.754/0.6% | 61.758/1.8% | -0.01% | REGRESSED
process 8 20 | 76.817/0.5% | 79.049/0.2% | -2.91% | REGRESSED
threads 16 20 | 66.290/1.5% | 67.120/3.5% | -1.25% | REGRESSED
threads 1 20 | 62.311/2.3% | 61.518/1.7% | 1.27% | IMPROVED
threads 4 20 | 56.376/0.8% | 57.455/0.8% | -1.91% | REGRESSED
threads 8 20 | 63.871/3.8% | 68.611/0.3% | -7.42% | REGRESSED
Besides, as suggested by Qais, cache-aware scheduling could also be a user
of the push mechanism. This is an evaluation patch that uses task push for
cache-aware scheduling by allowing the cache-aware sensitive task to leverage
the push path to be pushed to its preferred LLC.
In my opinion, task pushing is triggered less frequently than task wakeup,
because it has a high bar to be launched(preempted runnable task)
This could reduce the risk of contention between the task pushing and the
cache-aware load balancer, and achieve faster task aggregation on its
preferred LLC.
We have launched a simple migration test based on the following change, and
will update later to see what the result is.
---
kernel/sched/fair.c | 88 ++++++++++++++++++++++++++++++++++++++++----
kernel/sched/sched.h | 1 +
2 files changed, 82 insertions(+), 7 deletions(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 7eea0414fd46..d5c6bbf04b47 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -1391,7 +1391,8 @@ static bool update_deadline(struct cfs_rq *cfs_rq, struct sched_entity *se)
#include "pelt.h"
-static int select_idle_sibling(struct task_struct *p, int prev_cpu, int cpu);
+static int select_idle_sibling(struct task_struct *p, int prev_cpu, int cpu,
+ int push_cpu);
static unsigned long task_h_load(struct task_struct *p);
static unsigned long capacity_of(int cpu);
@@ -9325,7 +9326,8 @@ static inline bool asym_fits_cpu(unsigned long util,
/*
* Try and locate an idle core/thread in the LLC cache domain.
*/
-static int select_idle_sibling(struct task_struct *p, int prev, int target)
+static int select_idle_sibling(struct task_struct *p, int prev, int target,
+ int push_cpu)
{
bool has_idle_core = false;
struct sched_domain *sd;
@@ -9348,6 +9350,19 @@ static int select_idle_sibling(struct task_struct *p, int prev, int target)
*/
lockdep_assert_irqs_disabled();
+ /*
+ * push_cpu must be checked before the target: when
+ * a task is pushed from the tick, it is rq->curr, so
+ * idle_cpu_without() would consider the target as
+ * idle and keep the task on its current (non-preferred) LLC.
+ */
+ if (push_cpu != -1 && push_cpu != target && !cpus_share_cache(push_cpu, target)) {
+ target = push_cpu;
+ if (choose_idle_cpu(push_cpu, p) &&
+ asym_fits_cpu(task_util, util_min, util_max, push_cpu))
+ goto select_smt_priority;
+ }
+
if (choose_idle_cpu(target, p) &&
asym_fits_cpu(task_util, util_min, util_max, target))
goto select_smt_priority;
@@ -10156,6 +10171,15 @@ static bool check_pushable_short_task(struct rq *rq, struct task_struct *p)
return false;
}
+#ifdef CONFIG_SCHED_CACHE
+static int check_pushable_cache_task(int this_cpu, struct task_struct *p);
+#else
+static inline int check_pushable_cache_task(int this_cpu, struct task_struct *p)
+{
+ return -1;
+}
+#endif
+
static bool fair_check_pushable_task(struct rq *rq, struct task_struct *p, struct task_struct *next)
{
if (!__check_pushable_fair_task(rq, p))
@@ -10167,6 +10191,9 @@ static bool fair_check_pushable_task(struct rq *rq, struct task_struct *p, struc
if (check_pushable_short_task(rq, p))
return true;
+ if (check_pushable_cache_task(rq->cpu, p) != -1)
+ return true;
+
return false;
}
@@ -10231,7 +10258,7 @@ static bool fair_push_task(struct rq *rq)
if (!raw_spin_trylock(&next_task->pi_lock))
return true;
- new_cpu = select_task_rq_fair(next_task, prev_cpu, 0);
+ new_cpu = select_task_rq_fair(next_task, prev_cpu, WF_RQ_PUSH);
/* Task doesn't need to migrate */
if (new_cpu == prev_cpu)
@@ -10335,7 +10362,7 @@ static inline bool tick_pushable_task(struct task_struct *p, struct rq *rq, stru
if (!raw_spin_trylock(&p->pi_lock))
return false;
- new_cpu = select_task_rq_fair(p, cpu, 0);
+ new_cpu = select_task_rq_fair(p, cpu, WF_RQ_PUSH);
raw_spin_unlock(&p->pi_lock);
@@ -10382,7 +10409,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int select_flags)
{
int sync = (select_flags & WF_SYNC) && !(current->flags & PF_EXITING);
int want_sibling = !(select_flags & (WF_EXEC | WF_FORK));
- int new_cpu, cpu = smp_processor_id();
+ int new_cpu, cpu = smp_processor_id(), push_cpu;
struct sched_domain *tmp, *sd = NULL;
/* SD_flags and WF_flags share the first nibble */
int sd_flag = select_flags & 0xF;
@@ -10414,6 +10441,15 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int select_flags)
if (select_flags & WF_TTWU)
want_affine = !wake_wide(p) && cpumask_test_cpu(cpu, p->cpus_ptr);
+ /*
+ * Only a push triggered by cache aware scheduling will push the
+ * task towards its preferred LLC. Pushes for other reasons, like short
+ * slice task, uses the default push strategy.
+ */
+ push_cpu = -1;
+ if (select_flags & WF_RQ_PUSH)
+ push_cpu = check_pushable_cache_task(prev_cpu, p);
+
new_cpu = prev_cpu;
for_each_domain(cpu, tmp) {
@@ -10441,7 +10477,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int select_flags)
break;
}
- /* Slow path */
+ /* Slow path, push task will not go inside due to flags = 0 */
if (unlikely(sd)) {
new_cpu = sched_balance_find_dst_cpu(sd, p, cpu, prev_cpu, sd_flag);
return select_idle_smt_cpu(p, new_cpu);
@@ -10449,7 +10485,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int select_flags)
/* Fast path */
if (want_sibling)
- new_cpu = select_idle_sibling(p, prev_cpu, new_cpu);
+ new_cpu = select_idle_sibling(p, prev_cpu, new_cpu, push_cpu);
return new_cpu;
}
@@ -11572,6 +11608,44 @@ static bool migrate_degrades_llc(struct task_struct *p, struct lb_env *env)
return true;
}
+/*
+ * Return the preferred CPU that task p running on this_cpu should be
+ * pushed to for better LLC locality, or -1 if no such push is wanted.
+ */
+static int check_pushable_cache_task(int this_cpu, struct task_struct *p)
+{
+ struct sched_cache_group *grp;
+ int pref_cpu;
+
+ if (!sched_cache_enabled())
+ return -1;
+
+ /* preemption already disabled */
+ grp = rcu_dereference_all(p->sched_cache_grp);
+ if (!grp)
+ return -1;
+
+ pref_cpu = READ_ONCE(grp->cpu);
+ /* If the task's pref_cpu is on the wrong(non-preferred) LLC, move it there */
+ if (pref_cpu == -1 || cpus_share_cache(pref_cpu, this_cpu))
+ return -1;
+
+ if (!cpumask_test_cpu(pref_cpu, p->cpus_ptr) ||
+ !cpumask_test_cpu(pref_cpu, cpu_active_mask))
+ return -1;
+
+#ifdef CONFIG_NUMA_BALANCING
+ if (static_branch_likely(&sched_numa_balancing) &&
+ p->numa_preferred_nid != NUMA_NO_NODE &&
+ p->numa_preferred_nid != cpu_to_node(pref_cpu))
+ return -1;
+#endif
+ if (can_migrate_llc(this_cpu, pref_cpu, task_util(p), true) == mig_forbid)
+ return -1;
+
+ return pref_cpu;
+}
+
#else
static inline bool get_llc_stats(int cpu, unsigned long *util,
unsigned long *cap)
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 95a18c0d21c6..85527034572d 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2559,6 +2559,7 @@ static inline int task_on_rq_migrating(struct task_struct *p)
#define WF_CURRENT_CPU 0x40 /* Prefer to move the wakee to the current CPU. */
#define WF_RQ_SELECTED 0x80 /* ->select_task_rq() was called */
#define WF_TTWU_RQ 0x100 /* Wakeup completed through ttwu_runnable() */
+#define WF_RQ_PUSH 0x200 /* Push the task */
static_assert(WF_EXEC == SD_BALANCE_EXEC);
static_assert(WF_FORK == SD_BALANCE_FORK);
--
2.43.0
next prev parent reply other threads:[~2026-10-07 2:59 UTC|newest]
Thread overview: 50+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-02 15:43 [PATCH 00/18 v2] Improving latency of short slice tasks Vincent Guittot
2026-10-02 15:43 ` [PATCH 01/18 v2] sched/eevdf: Decay positive lag of sleeping entities Vincent Guittot
2026-10-02 15:43 ` [PATCH 02/18] sched/eevdf: Reset lag when waking up on idle cpu Vincent Guittot
2026-10-04 17:30 ` Kayra Cizmeci
2026-10-09 13:35 ` Vincent Guittot
2026-10-02 15:44 ` [PATCH 03/18 v2] sched/eevdf: Add per cpu cached min_slice Vincent Guittot
2026-10-02 15:44 ` [PATCH 04/18 v2] sched/eevdf: Compare min slice during wake_affine Vincent Guittot
2026-10-05 15:58 ` Kayra Cizmeci
2026-10-02 15:44 ` [PATCH 05/18 v2] sched/eevdf: Add min slice check when selecting CPU Vincent Guittot
2026-10-04 19:18 ` Kayra Cizmeci
2026-10-02 15:44 ` [PATCH 06/18 v2] sched/fair: Prepare select_task_rq_fair() to be called for new cases Vincent Guittot
2026-10-06 22:25 ` Tim Chen
2026-10-09 13:36 ` Vincent Guittot
2026-10-02 15:44 ` [PATCH 07/18] sched/fair: Add push task mechanism for fair Vincent Guittot
2026-10-07 2:45 ` Chen Yu [this message]
2026-10-09 13:38 ` Vincent Guittot
2026-10-09 14:31 ` Vincent Guittot
2026-10-10 15:42 ` Chen Yu
2026-10-12 9:18 ` Vincent Guittot
2026-10-13 5:17 ` Chen Yu
2026-10-12 14:06 ` Shrikanth Hegde
2026-10-02 15:44 ` [PATCH 08/18] sched/fair: Optimize " Vincent Guittot
2026-10-02 15:44 ` [PATCH 09/18 v2] sched/core: Add rq flag to tick parameters Vincent Guittot
2026-10-02 15:44 ` [PATCH 10/18 v2] sched/fair: Add force push task mechanism for fair Vincent Guittot
2026-10-06 19:24 ` Kayra Cizmeci
2026-10-09 14:01 ` Vincent Guittot
2026-10-09 5:53 ` Kayra Cizmeci
2026-10-09 14:04 ` Vincent Guittot
2026-10-09 14:47 ` Vincent Guittot
2026-10-11 17:40 ` Kayra Cizmeci
2026-10-12 8:18 ` Vincent Guittot
2026-10-12 13:40 ` Kayra Cizmeci
2026-10-02 15:44 ` [PATCH 11/18 v2] sched/fair: Support not wakeup case in select_idle_sibling Vincent Guittot
2026-10-07 17:55 ` Tim Chen
2026-10-09 14:06 ` Vincent Guittot
2026-10-02 15:44 ` [PATCH 12/18 v2] sched/eevdf: Try to push short slice task on a better CPU Vincent Guittot
2026-10-02 15:44 ` [PATCH 13/18 v2] sched/eevdf: Push short slice task that are not picked Vincent Guittot
2026-10-02 15:44 ` [PATCH 14/18 v2] sched/fair: Enable push task for preempt short Vincent Guittot
2026-10-09 11:16 ` Kayra Cizmeci
2026-10-09 14:07 ` Vincent Guittot
2026-10-02 15:44 ` [PATCH 15/18 v2] energy model: Add a get previous state function Vincent Guittot
2026-10-02 15:44 ` [PATCH 16/18 v2] sched/fair: Rework feec() to use cost instead of spare capacity Vincent Guittot
2026-10-02 15:44 ` [PATCH 17/18 v2] energy model: Remove unused em_cpu_energy() Vincent Guittot
2026-10-02 15:44 ` [PATCH 18/18 v2] sched/fair: Take into account slice in EAS Vincent Guittot
2026-10-07 14:36 ` Kayra Cizmeci
2026-10-08 22:53 ` Tim Chen
2026-10-09 6:31 ` Kayra Cizmeci
2026-10-09 14:17 ` Vincent Guittot
2026-10-08 19:12 ` [PATCH 00/18 v2] Improving latency of short slice tasks Kayra Cizmeci
2026-10-09 14:56 ` Vincent Guittot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=asWyZtW7zPVo3k-A@chenyu-dev \
--to=yu.c.chen@intel.com \
--cc=arighi@nvidia.com \
--cc=bsegall@google.com \
--cc=changwoo@igalia.com \
--cc=chen.yu@linux.dev \
--cc=christian.loehle@arm.com \
--cc=dietmar.eggemann@arm.com \
--cc=juri.lelli@redhat.com \
--cc=kprateek.nayak@amd.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=lukasz.luba@arm.com \
--cc=mgorman@suse.de \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=pierre.gondois@arm.com \
--cc=qyousef@layalina.io \
--cc=rafael@kernel.org \
--cc=rostedt@goodmis.org \
--cc=sched-ext@lists.linux.dev \
--cc=sshegde@linux.ibm.com \
--cc=tj@kernel.org \
--cc=vincent.guittot@linaro.org \
--cc=void@manifault.com \
--cc=vschneid@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.