mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Luigi Rizzo <lrizzo@google.com>
To: Luigi Rizzo <rizzo.unipi@gmail.com>,
	Ingo Molnar <mingo@redhat.com>,
	 Peter Zijlstra <peterz@infradead.org>,
	Juri Lelli <juri.lelli@redhat.com>,
	 Vincent Guittot <vincent.guittot@linaro.org>
Cc: 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>,
	 linux-kernel@vger.kernel.org, Luigi Rizzo <lrizzo@google.com>
Subject: [PATCH] sched/cpupri: Remove count field from struct cpupri_vec
Date: Wed, 19 Aug 2026 09:51:28 +0000	[thread overview]
Message-ID: <20260819095129.4056035-1-lrizzo@google.com> (raw)

Under heavy I/O workloads (such as fio using threaded IRQs), irq_thread
runs as SCHED_FIFO 50 and undergoes frequent enqueue/dequeue cycles.
Every dequeue invokes cpupri_set() to transition the CPU priority back
to CPUPRI_NORMAL.

The "count" field in struct cpupri_vec was designed as an early-exit
heuristic for __cpupri_find(). However, maintaining count in sync with
mask requires atomics and expensive barriers (e.g. "dmb ish" on ARM),
which on large multicore systems become extremely expensive:

Experiment on a dual socket ARM with 220 CPUs:
- runtime for cpupri_set(), nanoseconds:

  with count:  p10:  700    p50: 1915  p90: 6619  p98 11150  p99: 13200
  without:     p10:  290    p50:  425  p90:  567  p98:  685  p99:   821

- cpupri_set() usage on softirq CPU, measured with perf top

  with count:  ~ 25%
  without:     <  2%

Remove the count field from struct cpupri_vec and code manipulating it.
This eliminates the cache-line contention and pipeline stalls while
preserving scheduler correctness.

Signed-off-by: Luigi Rizzo <lrizzo@google.com>
---
 kernel/sched/cpupri.c | 61 ++-----------------------------------------
 kernel/sched/cpupri.h |  2 --
 2 files changed, 2 insertions(+), 61 deletions(-)

diff --git a/kernel/sched/cpupri.c b/kernel/sched/cpupri.c
index 8f2237e8b484f..e9025f47880d9 100644
--- a/kernel/sched/cpupri.c
+++ b/kernel/sched/cpupri.c
@@ -69,33 +69,6 @@ static inline int __cpupri_find(struct cpupri *cp, struct task_struct *p,
 				struct cpumask *lowest_mask, int idx)
 {
 	struct cpupri_vec *vec  = &cp->pri_to_cpu[idx];
-	int skip = 0;
-
-	if (!atomic_read(&(vec)->count))
-		skip = 1;
-	/*
-	 * When looking at the vector, we need to read the counter,
-	 * do a memory barrier, then read the mask.
-	 *
-	 * Note: This is still all racy, but we can deal with it.
-	 *  Ideally, we only want to look at masks that are set.
-	 *
-	 *  If a mask is not set, then the only thing wrong is that we
-	 *  did a little more work than necessary.
-	 *
-	 *  If we read a zero count but the mask is set, because of the
-	 *  memory barriers, that can only happen when the highest prio
-	 *  task for a run queue has left the run queue, in which case,
-	 *  it will be followed by a pull. If the task we are processing
-	 *  fails to find a proper place to go, that pull request will
-	 *  pull this task if the run queue is running at a lower
-	 *  priority.
-	 */
-	smp_rmb();
-
-	/* Need to do the rmb for every iteration */
-	if (skip)
-		return 0;
 
 	if (cpumask_any_and(&p->cpus_mask, vec->mask) >= nr_cpu_ids)
 		return 0;
@@ -212,7 +185,6 @@ void cpupri_set(struct cpupri *cp, int cpu, int newpri)
 {
 	int *currpri = &cp->cpu_to_pri[cpu];
 	int oldpri = *currpri;
-	int do_mb = 0;
 
 	newpri = convert_prio(newpri);
 
@@ -231,39 +203,11 @@ void cpupri_set(struct cpupri *cp, int cpu, int newpri)
 		struct cpupri_vec *vec = &cp->pri_to_cpu[newpri];
 
 		cpumask_set_cpu(cpu, vec->mask);
-		/*
-		 * When adding a new vector, we update the mask first,
-		 * do a write memory barrier, and then update the count, to
-		 * make sure the vector is visible when count is set.
-		 */
-		smp_mb__before_atomic();
-		atomic_inc(&(vec)->count);
-		do_mb = 1;
 	}
-	if (likely(oldpri != CPUPRI_INVALID)) {
-		struct cpupri_vec *vec  = &cp->pri_to_cpu[oldpri];
 
-		/*
-		 * Because the order of modification of the vec->count
-		 * is important, we must make sure that the update
-		 * of the new prio is seen before we decrement the
-		 * old prio. This makes sure that the loop sees
-		 * one or the other when we raise the priority of
-		 * the run queue. We don't care about when we lower the
-		 * priority, as that will trigger an rt pull anyway.
-		 *
-		 * We only need to do a memory barrier if we updated
-		 * the new priority vec.
-		 */
-		if (do_mb)
-			smp_mb__after_atomic();
+	if (likely(oldpri != CPUPRI_INVALID)) {
+		struct cpupri_vec *vec = &cp->pri_to_cpu[oldpri];
 
-		/*
-		 * When removing from the vector, we decrement the counter first
-		 * do a memory barrier and then clear the mask.
-		 */
-		atomic_dec(&(vec)->count);
-		smp_mb__after_atomic();
 		cpumask_clear_cpu(cpu, vec->mask);
 	}
 
@@ -283,7 +227,6 @@ int cpupri_init(struct cpupri *cp)
 	for (i = 0; i < CPUPRI_NR_PRIORITIES; i++) {
 		struct cpupri_vec *vec = &cp->pri_to_cpu[i];
 
-		atomic_set(&vec->count, 0);
 		if (!zalloc_cpumask_var(&vec->mask, GFP_KERNEL))
 			goto cleanup;
 	}
diff --git a/kernel/sched/cpupri.h b/kernel/sched/cpupri.h
index 6f562088c0565..78516c8428761 100644
--- a/kernel/sched/cpupri.h
+++ b/kernel/sched/cpupri.h
@@ -1,5 +1,4 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-#include <linux/atomic.h>
 #include <linux/cpumask.h>
 #include <linux/sched/rt.h>
 
@@ -11,7 +10,6 @@
 #define CPUPRI_HIGHER		100
 
 struct cpupri_vec {
-	atomic_t		count;
 	cpumask_var_t		mask;
 };
 
-- 
2.55.0.737.g08866a6d13-goog


             reply	other threads:[~2026-08-19  9:51 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-19  9:51 Luigi Rizzo [this message]
2026-08-20 10:35 ` [PATCH v2] " Luigi Rizzo

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=20260819095129.4056035-1-lrizzo@google.com \
    --to=lrizzo@google.com \
    --cc=bsegall@google.com \
    --cc=dietmar.eggemann@arm.com \
    --cc=juri.lelli@redhat.com \
    --cc=kprateek.nayak@amd.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mgorman@suse.de \
    --cc=mingo@redhat.com \
    --cc=peterz@infradead.org \
    --cc=rizzo.unipi@gmail.com \
    --cc=rostedt@goodmis.org \
    --cc=vincent.guittot@linaro.org \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®