From: K Prateek Nayak <kprateek.nayak@amd.com>
To: Ingo Molnar <mingo@redhat.com>,
Peter Zijlstra <peterz@infradead.org>,
Juri Lelli <juri.lelli@redhat.com>,
Vincent Guittot <vincent.guittot@linaro.org>,
Anna-Maria Behnsen <anna-maria@linutronix.de>,
Frederic Weisbecker <frederic@kernel.org>,
Thomas Gleixner <tglx@linutronix.de>
Cc: <linux-kernel@vger.kernel.org>,
Dietmar Eggemann <dietmar.eggemann@arm.com>,
Steven Rostedt <rostedt@goodmis.org>,
Ben Segall <bsegall@google.com>, Mel Gorman <mgorman@suse.de>,
Valentin Schneider <vschneid@redhat.com>,
K Prateek Nayak <kprateek.nayak@amd.com>,
"Gautham R. Shenoy" <gautham.shenoy@amd.com>,
Swapnil Sapkal <swapnil.sapkal@amd.com>,
Shrikanth Hegde <sshegde@linux.ibm.com>,
Chen Yu <yu.c.chen@intel.com>
Subject: [RESEND RFC PATCH v2 29/29] [EXPERIMENTAL] sched/fair: Faster alternate for intra-NUMA newidle balance
Date: Mon, 8 Dec 2025 09:27:15 +0000 [thread overview]
Message-ID: <20251208092744.32737-29-kprateek.nayak@amd.com> (raw)
In-Reply-To: <20251208083602.31898-1-kprateek.nayak@amd.com>
Kernels that enable CONFIG_PREEMPTION only pull a single task during
newidle balance to keep the latency low. In standard newidle balance
path, the computation of busiest group, busiest rq, and then pulling
a single task from the busy rq adds a lot of overhead.
During the discussions at OSPM around overheads of load balancing, Peter
suggested trying out a different strategy for inter-NUMA newidle balance
with the goal of pulling a task as quickly as possible.
Try out an alternative strategy of newidle balance of directry
traversing the CPUs in the sched domain to pull runnable tasks.
Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
kernel/sched/fair.c | 119 ++++++++++++++++++++++++++++++++++----------
1 file changed, 93 insertions(+), 26 deletions(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 46d33ab63336..aa2821a9b800 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -11701,6 +11701,11 @@ static int need_active_balance(struct lb_env *env)
static int active_load_balance_cpu_stop(void *data);
+static inline bool sched_newidle_stop_balance(struct rq *rq)
+{
+ return (rq->nr_running > 0 || rq->ttwu_pending);
+}
+
static int should_we_balance(struct lb_env *env)
{
struct cpumask *swb_cpus = this_cpu_cpumask_var_ptr(should_we_balance_tmpmask);
@@ -11722,7 +11727,7 @@ static int should_we_balance(struct lb_env *env)
* to optimize wakeup latency.
*/
if (env->idle == CPU_NEWLY_IDLE) {
- if (env->dst_rq->nr_running > 0 || env->dst_rq->ttwu_pending)
+ if (sched_newidle_stop_balance(env->dst_rq))
return 0;
return 1;
}
@@ -13256,6 +13261,7 @@ static inline void fair_queue_pushable_tasks(struct rq *rq) { }
*/
static int sched_balance_newidle(struct rq *this_rq, struct rq_flags *rf)
{
+ struct cpumask *cpus = this_cpu_cpumask_var_ptr(load_balance_mask);
unsigned long next_balance = jiffies + HZ;
int this_cpu = this_rq->cpu;
int continue_balancing = 1;
@@ -13315,8 +13321,11 @@ static int sched_balance_newidle(struct rq *this_rq, struct rq_flags *rf)
t0 = sched_clock_cpu(this_cpu);
sched_balance_update_blocked_averages(this_cpu);
+ cpumask_clear(cpus);
+
rcu_read_lock();
for_each_domain(this_cpu, sd) {
+ unsigned int weight = 1;
u64 domain_cost;
update_next_balance(sd, &next_balance);
@@ -13324,40 +13333,98 @@ static int sched_balance_newidle(struct rq *this_rq, struct rq_flags *rf)
if (this_rq->avg_idle < curr_cost + sd->max_newidle_lb_cost)
break;
- if (sd->flags & SD_BALANCE_NEWIDLE) {
- unsigned int weight = 1;
+ if (!(sd->flags & SD_BALANCE_NEWIDLE))
+ continue;
- if (sched_feat(NI_RANDOM)) {
- /*
- * Throw a 1k sided dice; and only run
- * newidle_balance according to the success
- * rate.
- */
- u32 d1k = sched_rng() % 1024;
- weight = 1 + sd->newidle_ratio;
- if (d1k > weight) {
- update_newidle_stats(sd, 0);
- continue;
- }
- weight = (1024 + weight/2) / weight;
+ if (sched_feat(NI_RANDOM)) {
+ /*
+ * Throw a 1k sided dice; and only run
+ * newidle_balance according to the success
+ * rate.
+ */
+ u32 d1k = sched_rng() % 1024;
+ weight = 1 + sd->newidle_ratio;
+ if (d1k > weight) {
+ update_newidle_stats(sd, 0);
+ continue;
}
+ weight = (1024 + weight/2) / weight;
+ }
- pulled_task = sched_balance_rq(this_cpu, this_rq,
- sd, CPU_NEWLY_IDLE,
- &continue_balancing);
- t1 = sched_clock_cpu(this_cpu);
- domain_cost = t1 - t0;
- curr_cost += domain_cost;
- t0 = t1;
+ /*
+ * Non-preemptible kernels can pull more than one task during
+ * newidle balance and NUMA domains may need special
+ * consideration to preserve tasks on preferred NUMA node.
+ *
+ * Only take fast-path on preemptible kernels for intra NUMA
+ * domains.
+ */
+ if (!IS_ENABLED(CONFIG_PREEMPTION) || (sd->flags & SD_NUMA)) {
+ pulled_task = sched_balance_rq(this_cpu, this_rq,
+ sd, CPU_NEWLY_IDLE,
+ &continue_balancing);
+ } else {
+ struct lb_env env = {
+ .sd = sd,
+ .dst_cpu = this_cpu,
+ .dst_rq = this_rq,
+ .idle = CPU_NEWLY_IDLE,
+ };
+ int cpu;
/*
- * Track max cost of a domain to make sure to not delay the
- * next wakeup on the CPU.
+ * Clear the CPUs of child domain. They have already
+ * been visited during last balance. !NUMA domains do
+ * not overlap so simply excluding the previous
+ * domain's span should be enough.
*/
- update_newidle_cost(sd, domain_cost, weight * !!pulled_task);
+ cpumask_andnot(cpus, sched_domain_span(sd), cpus);
+
+ /* Commit to searching the sd if we are idle at start. */
+ continue_balancing = sched_newidle_stop_balance(this_rq);
+ if (!continue_balancing)
+ break;
+
+ for_each_cpu_wrap(cpu, cpus, this_cpu + 1) {
+ struct task_struct *p = NULL;
+ struct rq *rq = cpu_rq(cpu);
+
+ /* Not overloaded with runnable tasks. */
+ if (rq->cfs.h_nr_runnable <= 1)
+ continue;
+
+ scoped_guard(rq_lock, rq) {
+ /* Check again with rq lock held. */
+ if (rq->cfs.h_nr_runnable <= 1)
+ break;
+
+ env.src_cpu = cpu;
+ env.src_rq = rq;
+
+ update_rq_clock(rq);
+ p = detach_one_task(&env);
+ }
+
+ if (p) {
+ attach_one_task(this_rq, p);
+ pulled_task = 1;
+ break;
+ }
+ }
}
+ t1 = sched_clock_cpu(this_cpu);
+ domain_cost = t1 - t0;
+ curr_cost += domain_cost;
+ t0 = t1;
+
+ /*
+ * Track max cost of a domain to make sure to not delay the
+ * next wakeup on the CPU.
+ */
+ update_newidle_cost(sd, domain_cost, weight * !!pulled_task);
+
/*
* Stop searching for tasks to pull if there are
* now runnable tasks on this rq.
--
2.43.0
next prev parent reply other threads:[~2025-12-08 9:36 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-08 9:26 [RESEND RFC PATCH v2 00/29] sched/fair: Push-based load balancing K Prateek Nayak
2025-12-08 9:26 ` [RESEND RFC PATCH v2 01/29] sched/fair: Simplify set_cpu_sd_state_*() with guards K Prateek Nayak
2025-12-08 9:26 ` [RESEND RFC PATCH v2 02/29] sched/fair: Use rq->nohz_tick_stopped in update_nohz_stats() K Prateek Nayak
2025-12-08 9:26 ` [RESEND RFC PATCH v2 03/29] sched/topology: Optimize sd->shared allocation and assignment K Prateek Nayak
2025-12-08 9:26 ` [RESEND RFC PATCH v2 04/29] sched/fair: Simplify the entry condition for update_idle_cpu_scan() K Prateek Nayak
2025-12-08 9:26 ` [RESEND RFC PATCH v2 05/29] sched/fair: Simplity SIS_UTIL handling in select_idle_cpu() K Prateek Nayak
2025-12-08 9:26 ` [RESEND RFC PATCH v2 06/29] cpumask: Introduce for_each_cpu_and_wrap() and bitfield helpers K Prateek Nayak
2025-12-12 21:03 ` Yury Norov
2025-12-08 9:26 ` [RESEND RFC PATCH v2 07/29] sched/fair: Use for_each_cpu_and_wrap() in select_idle_capacity() K Prateek Nayak
2025-12-08 9:26 ` [RESEND RFC PATCH v2 08/29] sched/fair: Use for_each_cpu_and_wrap() in select_idle_cpu() K Prateek Nayak
2025-12-08 9:26 ` [RESEND RFC PATCH v2 09/29] sched/fair: Rotate the CPU resposible for busy load balancing K Prateek Nayak
2025-12-08 9:26 ` [RESEND RFC PATCH v2 10/29] sched/fair: Use xchg() to set sd->nohz_idle state K Prateek Nayak
2025-12-08 9:26 ` [RESEND RFC PATCH v2 11/29] sched/topology: Attach new hierarchy in rq_attach_root() K Prateek Nayak
2025-12-08 9:26 ` [RESEND RFC PATCH v2 12/29] sched/fair: Fixup sd->nohz_idle state during hotplug / cpuset K Prateek Nayak
2025-12-08 9:26 ` [RESEND RFC PATCH v2 13/29] sched/fair: Account idle cpus instead of busy cpus in sd->shared K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 14/29] sched/topology: Introduce fallback sd->shared assignment K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 15/29] sched/topology: Introduce percpu sd_nohz for nohz state tracking K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 16/29] sched/topology: Introduce "nohz_idle_cpus_mask" in sd->shared K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 17/29] sched/topology: Introduce "nohz_shared_list" to keep track of sd->shared K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 18/29] sched/fair: Reorder the barrier in nohz_balance_enter_idle() K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 19/29] sched/fair: Extract the main _nohz_idle_balance() loop into a helper K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 20/29] sched/fair: Convert find_new_ilb() to use nohz_shared_list K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 21/29] sched/fair: Introduce sched_asym_prefer_idle() for ILB kick K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 22/29] sched/fair: Convert sched_balance_nohz_idle() to use nohz_shared_list K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 23/29] sched/fair: Remove "nohz.idle_cpus_mask" K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 24/29] sched/fair: Optimize global "nohz.nr_cpus" tracking K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 25/29] sched/topology: Add basic debug information for "nohz_shared_list" K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 26/29] [EXPERIMENTAL] sched/fair: Add push task framework K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 27/29] [EXPERIMENTAL] sched/fair: Proactive idle balance using push mechanism K Prateek Nayak
2025-12-08 9:27 ` [RESEND RFC PATCH v2 28/29] [EXPERIMENTAL] sched/fair: Add a local counter to rate limit task push K Prateek Nayak
2025-12-08 12:33 ` Christian Loehle
2025-12-08 17:35 ` K Prateek Nayak
2025-12-08 9:27 ` K Prateek Nayak [this message]
2025-12-08 14:04 ` [RESEND RFC PATCH v2 00/29] sched/fair: Push-based load balancing Shrikanth Hegde
2025-12-08 17:36 ` K Prateek Nayak
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=20251208092744.32737-29-kprateek.nayak@amd.com \
--to=kprateek.nayak@amd.com \
--cc=anna-maria@linutronix.de \
--cc=bsegall@google.com \
--cc=dietmar.eggemann@arm.com \
--cc=frederic@kernel.org \
--cc=gautham.shenoy@amd.com \
--cc=juri.lelli@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mgorman@suse.de \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=rostedt@goodmis.org \
--cc=sshegde@linux.ibm.com \
--cc=swapnil.sapkal@amd.com \
--cc=tglx@linutronix.de \
--cc=vincent.guittot@linaro.org \
--cc=vschneid@redhat.com \
--cc=yu.c.chen@intel.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox