Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Andrea Righi <arighi@nvidia.com>
To: Christian Loehle <christian.loehle@arm.com>
Cc: Ingo Molnar <mingo@redhat.com>,
	Peter Zijlstra <peterz@infradead.org>,
	Juri Lelli <juri.lelli@redhat.com>,
	Vincent Guittot <vincent.guittot@linaro.org>,
	Catalin Marinas <catalin.marinas@arm.com>,
	Will Deacon <will@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>,
	Mark Rutland <mark.rutland@arm.com>,
	Shrikanth Hegde <sshegde@linux.ibm.com>,
	Phil Auld <pauld@redhat.com>, Breno Leitao <leitao@debian.org>,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH 1/2] arm64: topology: Prefer PE0 on NVIDIA Olympus SMT cores
Date: Tue, 1 Sep 2026 08:05:31 +0200	[thread overview]
Message-ID: <apZrKweoy6uej0Q-@gpd4> (raw)
In-Reply-To: <apX1j6srdTcFLZOd@gpd4>

Hi Christian,

On Mon, Aug 31, 2026 at 11:43:54PM +0200, Andrea Righi wrote:
...
> Combining the priorities is a valid experiment, but I'm not sure if it
> completely solves the problem by itself, I'll give it a try and share the
> results.

I did some tests comparing this asym-capacity+asym-packing approach vs the
combined-asym-packing approach (tested patch for the combined-asym-packing
approach is at the end - patch 2 is the same).

Policies tested
---------------

combined-asym-packing:

- combined capacity/PE priority
- SD_ASYM_PACKING at all topology levels
- SD_ASYM_CPUCAPACITY disabled

asym-capacity+smt-asym-packing:

- capacity-aware physical-core selection retained
- PE0 priority applied only in the SMT domain

[ Both policies include the idle-selection fix - patch 2 ]

SGEMM throughput
----------------

The primary result is ten repetitions of the exact 88-thread command provided.

Metric                    combined-asym-packing    asym-capacity+smt-asym-packing    Difference
Average throughput        9.864 +/- 0.180 TFLOP/s  10.094 +/- 0.065 TFLOP/s          +2.34%
Best throughput           10.194 +/- 0.193 TFLOP/s 10.374 +/- 0.065 TFLOP/s          +1.77%
Minimum average run       9.575 TFLOP/s            9.968 TFLOP/s                    +4.10%
Maximum average run       10.057 TFLOP/s           10.204 TFLOP/s                   +1.46%

asym-capacity+smt-asym-packing averages 10.095 TFLOP/s versus 9.920 TFLOP/s, a
1.76% advantage. More importantly, the run-to-run standard deviation drops from
180 to 65 GFLOP/s. combined-asym-packing can reach a good peak, but it does not
sustain it as reliably.

Thread scaling
--------------

Three runs per point, average throughput:

Threads  combined-asym-packing    asym-capacity+smt-asym-packing    Difference
22       3.065 +/- 0.002 TFLOP/s  3.068 +/- 0.002 TFLOP/s          +0.08%
44       5.849 +/- 0.064 TFLOP/s  5.888 +/- 0.022 TFLOP/s          +0.66%
88       9.975 +/- 0.128 TFLOP/s  10.062 +/- 0.107 TFLOP/s         +0.86%
176      10.695 +/- 0.004 TFLOP/s 10.704 +/- 0.012 TFLOP/s         +0.09%

The difference is specifically most visible around the intended
one-thread-per-core operating point. At 176 threads, where both SMT PEs are
used, the policies are effectively tied (as expected).

Cyclic wake-up latency
----------------------

Values are averages of three runs. Percentiles are the mean of each run's
reported percentile.

Condition  Metric  combined-asym-packing  asym-capacity+smt-asym-packing
Idle       median  6.433 us               6.398 us
Idle       p99     12.753 us              12.494 us
Idle       p99.9   18.324 us              19.025 us
Loaded     median  5.114 us               4.500 us
Loaded     p99     12.398 us              12.759 us
Loaded     p99.9   24.240 us              20.954 us

Idle latency is essentially tied. Under concurrent 88-thread SGEMM,
asym-capacity+smt-asym-packing improves median latency by 12% and p99.9 by
13.6%.

The worst observed loaded sample was 579.5 us with combined-asym-packing and
74.5 us with asym-capacity+smt-asym-packing. That is only one outlier and should
not be generalized without longer runs, but it favors
asym-capacity+smt-asym-packing.

Concurrent SGEMM throughput was tied: 9.902 versus 9.899 TFLOP/s.

Scheduler microbenchmarks
-------------------------

Lower is better for these results.

Test                       combined-asym-packing  asym-capacity+smt-asym-packing  Difference
sched pipe, processes      4.478 us/op            4.446 us/op                     -0.7%
sched pipe, threads        3.618 us/op            3.625 us/op                     +0.2%
SMT pair, CPU 0/176        1.815 us/op            1.813 us/op                     tied
Separate cores, CPU 0/1    4.388 us/op            4.311 us/op                     -1.8%
Unrestricted node 0        4.284 us/op            4.385 us/op                     +2.4%

These simple ping-pong results are effectively tied.

combined-asym-packing did materially better in the broader 160-task sched
messaging socket tests:

Test            combined-asym-packing  asym-capacity+smt-asym-packing
Process/socket  0.669 s                1.039 s
Thread/socket   0.653 s                0.989 s
Process/pipe    0.296 s                0.337 s

This suggests that combined asym-packing can help some highly communicating,
oversubscribed workloads by changing how runnable tasks are packed.

Futex
-----

Test             combined-asym-packing  asym-capacity+smt-asym-packing  Difference
Wake one         0.1695 ms              0.1800 ms                       +6.2%
Wake all         0.1734 ms              0.1693 ms                       -2.4%
Parallel wake    0.0430 ms              0.0332 ms                       -22.6%
Hash throughput  4.052 Mops/s           4.064 Mops/s                    +0.3%

Futex hashing is tied. Wake results are mixed, with
asym-capacity+smt-asym-packing notably better in the parallel-waker case.

Conclusion
----------

The combined priority is technically workable, but combined-asym-packing does
more than express the PE0 preference:

- it replaces SD_ASYM_CPUCAPACITY with asym-packing across the Olympus topology.
- It changes placement policy for unrelated multi-core and communication-heavy
  workloads
- It still requires the idle-selection scheduler change
- It produces lower and substantially more variable throughput

The asym-capacity+smt-asym-packing seems to solve the specific spatial-SMT issue
on Vera, retains the existing capacity-aware semantics, provides the best
target-workload result and has no general latency regression in this dataset.

combined-asym-packing patch
---------------------------

The following is the combined-asym-packing policy patch tested in this report.
The idle-selection patch was present in both test kernels and is therefore not
included in this policy delta.

---
 arch/arm64/include/asm/topology.h |  1 +
 arch/arm64/kernel/smp.c           |  1 +
 arch/arm64/kernel/topology.c      | 90 +++++++++++++++++++++++++++++++
 include/linux/sched/topology.h    |  2 +
 kernel/sched/topology.c           |  8 +++
 5 files changed, 102 insertions(+)

diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
index b9eaf4ad70850..edc1c59b3448d 100644
--- a/arch/arm64/include/asm/topology.h
+++ b/arch/arm64/include/asm/topology.h
@@ -18,6 +18,7 @@ int pcibus_to_node(struct pci_bus *bus);
 #include <linux/arch_topology.h>
 
 void update_freq_counters_refs(void);
+void arm64_init_sched_topology(void);
 
 /* Replace task scheduler's default frequency-invariant accounting */
 #define arch_scale_freq_tick topology_scale_freq_tick
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index a61dc3016a117..0135ac4eea8bd 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -443,6 +443,7 @@ void __init smp_cpus_done(unsigned int max_cpus)
 	hyp_mode_check();
 	setup_system_features();
 	setup_user_features();
+	arm64_init_sched_topology();
 	mark_linear_text_alias_ro();
 }
 
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index d28438f8b83f1..42a5c0d4f5d2e 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -19,6 +19,8 @@
 #include <linux/init.h>
 #include <linux/percpu.h>
 #include <linux/sched/isolation.h>
+#include <linux/sched/topology.h>
+#include <linux/smp.h>
 #include <linux/xarray.h>
 
 #include <asm/cpu.h>
@@ -44,6 +46,94 @@
 static DEFINE_PER_CPU_READ_MOSTLY(unsigned long, arch_max_freq_scale) =  1UL << (2 * SCHED_CAPACITY_SHIFT);
 static cpumask_var_t amu_fie_cpus;
 
+/*
+ * Switching the active PE on an NVIDIA Olympus SMT core can keep the core in
+ * two-thread active mode, with resources partitioned between the PEs.
+ *
+ * Prefer PE0 so PE1 can remain idle and the core can stay in full-resource
+ * mode. Combine that preference with the normalized maximum CPU capacity so
+ * asym-packing also orders physical cores by performance. Firmware does not
+ * currently describe the PE preference, so detect Olympus by MIDR until a
+ * firmware interface is available.
+ */
+static bool olympus_prefer_pe0 __ro_after_init;
+
+static int arm64_asym_packing_flags(void)
+{
+	return olympus_prefer_pe0 ? SD_ASYM_PACKING : 0;
+}
+
+#ifdef CONFIG_SCHED_SMT
+static int arm64_smt_flags(void)
+{
+	return cpu_smt_flags() | arm64_asym_packing_flags();
+}
+#endif
+
+#ifdef CONFIG_SCHED_CLUSTER
+static int arm64_cluster_flags(void)
+{
+	return cpu_cluster_flags() | arm64_asym_packing_flags();
+}
+#endif
+
+#ifdef CONFIG_SCHED_MC
+static int arm64_core_flags(void)
+{
+	return cpu_core_flags() | arm64_asym_packing_flags();
+}
+#endif
+
+static struct sched_domain_topology_level arm64_asym_smt_topology[] = {
+#ifdef CONFIG_SCHED_SMT
+	SDTL_INIT(tl_smt_mask, arm64_smt_flags, SMT),
+#endif
+#ifdef CONFIG_SCHED_CLUSTER
+	SDTL_INIT(tl_cls_mask, arm64_cluster_flags, CLS),
+#endif
+#ifdef CONFIG_SCHED_MC
+	SDTL_INIT(tl_mc_mask, arm64_core_flags, MC),
+#endif
+	SDTL_INIT(tl_pkg_mask, arm64_asym_packing_flags, PKG),
+	{ NULL, },
+};
+
+void __init arm64_init_sched_topology(void)
+{
+	if (!IS_ENABLED(CONFIG_SCHED_SMT))
+		return;
+
+	if ((read_cpuid_id() & MIDR_CPU_MODEL_MASK) != MIDR_NVIDIA_OLYMPUS)
+		return;
+
+	if (!topology_core_has_smt(smp_processor_id()))
+		return;
+
+	olympus_prefer_pe0 = true;
+	set_sched_topology(arm64_asym_smt_topology);
+	pr_info("Enabling capacity and PE asym-packing for NVIDIA Olympus\n");
+}
+
+int arch_asym_cpu_priority(int cpu)
+{
+	int priority;
+
+	if (!olympus_prefer_pe0)
+		return 0;
+
+	/* cpu_scale preserves the ordering provided by CPPC highest_perf. */
+	priority = topology_get_cpu_scale(cpu);
+	if (MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0) == 0)
+		priority *= 2;
+
+	return priority;
+}
+
+bool arch_asym_cpu_capacity_enabled(void)
+{
+	return !olympus_prefer_pe0;
+}
+
 struct amu_cntr_sample {
 	u64		arch_const_cycles_prev;
 	u64		arch_core_cycles_prev;
diff --git a/include/linux/sched/topology.h b/include/linux/sched/topology.h
index b5d9d7c2b8add..dd40b8f466ca0 100644
--- a/include/linux/sched/topology.h
+++ b/include/linux/sched/topology.h
@@ -50,6 +50,8 @@ extern const struct cpumask *tl_mc_mask(struct sched_domain_topology_level *tl,
 extern const struct cpumask *tl_pkg_mask(struct sched_domain_topology_level *tl, int cpu);
 
 extern int arch_asym_cpu_priority(int cpu);
+/* Return false when the architecture represents capacity through packing. */
+bool arch_asym_cpu_capacity_enabled(void);
 
 struct sched_domain_attr {
 	int relax_domain_level;
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index 0248227d983a7..8ccc734efd748 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -1682,6 +1682,9 @@ asym_cpu_capacity_classify(const struct cpumask *sd_span,
 	struct asym_cap_data *entry;
 	int count = 0, miss = 0;
 
+	if (!arch_asym_cpu_capacity_enabled())
+		return 0;
+
 	/*
 	 * Count how many unique CPU capacities this domain spans across
 	 * (compare sched_domain CPUs mask with ones representing  available
@@ -1709,6 +1712,11 @@ asym_cpu_capacity_classify(const struct cpumask *sd_span,
 
 }
 
+bool __weak arch_asym_cpu_capacity_enabled(void)
+{
+	return true;
+}
+
 static void free_asym_cap_entry(struct rcu_head *head)
 {
 	struct asym_cap_data *entry = container_of(head, struct asym_cap_data, rcu);

Thanks,
-Andrea


  reply	other threads:[~2026-09-01  6:06 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-31 18:10 [PATCH 0/2] sched: Enable preferred SMT siblings on NVIDIA Olympus Andrea Righi
2026-08-31 18:10 ` [PATCH 1/2] arm64: topology: Prefer PE0 on NVIDIA Olympus SMT cores Andrea Righi
2026-08-31 21:13   ` Christian Loehle
2026-08-31 21:43     ` Andrea Righi
2026-09-01  6:05       ` Andrea Righi [this message]
2026-09-01  8:32       ` Christian Loehle
2026-09-01 19:38         ` Andrea Righi
2026-08-31 18:10 ` [PATCH 2/2] sched/fair: Honor asymmetric SMT priority in idle selection Andrea Righi

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=apZrKweoy6uej0Q-@gpd4 \
    --to=arighi@nvidia.com \
    --cc=bsegall@google.com \
    --cc=catalin.marinas@arm.com \
    --cc=christian.loehle@arm.com \
    --cc=dietmar.eggemann@arm.com \
    --cc=juri.lelli@redhat.com \
    --cc=kprateek.nayak@amd.com \
    --cc=leitao@debian.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=mgorman@suse.de \
    --cc=mingo@redhat.com \
    --cc=pauld@redhat.com \
    --cc=peterz@infradead.org \
    --cc=rostedt@goodmis.org \
    --cc=sshegde@linux.ibm.com \
    --cc=vincent.guittot@linaro.org \
    --cc=vschneid@redhat.com \
    --cc=will@kernel.org \
    /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