mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Chang S. Bae" <chang.seok.bae@intel.com>
To: linux-kernel@vger.kernel.org
Cc: x86@kernel.org, tglx@kernel.org, mingo@redhat.com, bp@alien8.de,
	dave.hansen@linux.intel.com, kevin.shu@intel.com,
	chang.seok.bae@intel.com
Subject: [PATCH RFC v1 3/8] x86/cpu/topology: Introduce primary core mask
Date: Sat, 12 Sep 2026 00:08:09 +0000	[thread overview]
Message-ID: <20260912000815.997720-4-chang.seok.bae@intel.com> (raw)
In-Reply-To: <20260912000815.997720-1-chang.seok.bae@intel.com>

The microcode loading distinguishes primary SMT threads from sibling
threads to avoid races during updates. Because early loading relies on
this distinction, parallel CPU bringup splits APs into two groups and
brings them up in phases.

Upcoming Intel microcode loading feature expands the loading scope beyond
per-core updates to package-wide or system-wide updates. While CPU0 is
the obvious primary for the system-wide scope, there is currently no
facility to identify per-package primary CPUs for the parallel bringup.

This matters because the parallel bringup logic must determine primary
CPUs before any AP is ever brought up. But topology_logical_package_id()
is not usable at that stage, as logical package IDs are established
during AP bringup itself, as described in:

  7af541cee1e0e ("x86/topology: Don't evaluate logical IDs during early boot")

Unlike cpu_primary_thread_mask, there is also no relevant mask that
tracks one primary CPU per package. Thus, introduce cpu_primary_core_mask
to fill this gap.

The mask intentionally includes all threads of the selected core. This
preserves core-domain granularity and avoids coupling with the lower-
level topology details such as SMT primary selection.

Use the mask for the microcode staging path to simplify staging CPU
selection at the moment. Following changes will address the microcode
loading use.

Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
Note:
The staging path itself could have continued using topology_logical_package_id()
as the late loading is only relevant. IOW, the cpu_primary_core_mask
introduction was not required for the staging enablement back then.
---
 arch/x86/include/asm/topology.h       |  3 +++
 arch/x86/kernel/cpu/microcode/intel.c | 14 ++++----------
 arch/x86/kernel/cpu/topology.c        | 23 ++++++++++++++++++++---
 arch/x86/kernel/cpu/topology_common.c |  9 +++++++++
 4 files changed, 36 insertions(+), 13 deletions(-)

diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h
index 1825691af941..02ad56cfc6f4 100644
--- a/arch/x86/include/asm/topology.h
+++ b/arch/x86/include/asm/topology.h
@@ -234,6 +234,9 @@ static inline unsigned int topology_amd_nodes_per_pkg(void) { return 1; }
 extern struct cpumask __cpu_primary_thread_mask;
 #define cpu_primary_thread_mask ((const struct cpumask *)&__cpu_primary_thread_mask)
 
+extern struct cpumask __cpu_primary_core_mask;
+#define cpu_primary_core_mask ((const struct cpumask *)&__cpu_primary_core_mask)
+
 /**
  * topology_is_primary_thread - Check whether CPU is the primary SMT thread
  * @cpu:	CPU to check
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index 1142183c950c..4b5feee14712 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -605,7 +605,6 @@ static int do_stage(u64 mmio_pa)
 
 static void stage_microcode(void)
 {
-	unsigned int pkg_id = UINT_MAX;
 	int cpu, err;
 	u64 mmio_pa;
 
@@ -619,15 +618,10 @@ static void stage_microcode(void)
 
 	/*
 	 * The MMIO address is unique per package, and all the SMT
-	 * primary threads are online here. Find each MMIO space by
-	 * their package IDs to avoid duplicate staging.
+	 * primary threads are online here. Find unique MMIO space to
+	 * avoid duplicate staging.
 	 */
-	for_each_cpu(cpu, cpu_primary_thread_mask) {
-		if (topology_logical_package_id(cpu) == pkg_id)
-			continue;
-
-		pkg_id = topology_logical_package_id(cpu);
-
+	for_each_cpu_and(cpu, cpu_primary_core_mask, cpu_primary_thread_mask) {
 		err = rdmsrq_on_cpu(cpu, MSR_IA32_MCU_STAGING_MBOX_ADDR, &mmio_pa);
 		if (WARN_ON_ONCE(err))
 			return;
@@ -635,7 +629,7 @@ static void stage_microcode(void)
 		err = do_stage(mmio_pa);
 		if (err) {
 			pr_err("Error: staging failed (%d) for CPU%d at package %u.\n",
-			       err, cpu, pkg_id);
+			       err, cpu, topology_logical_package_id(cpu));
 			return;
 		}
 	}
diff --git a/arch/x86/kernel/cpu/topology.c b/arch/x86/kernel/cpu/topology.c
index 4913b64ec592..be6f5a3bd54f 100644
--- a/arch/x86/kernel/cpu/topology.c
+++ b/arch/x86/kernel/cpu/topology.c
@@ -75,12 +75,25 @@ bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
 	return phys_id == (u64)cpuid_to_apicid[cpu];
 }
 
+static unsigned int __max_cores_per_package __ro_after_init = 1;
+
 static void cpu_mark_primary_thread(unsigned int cpu, unsigned int apicid)
 {
 	if (!(apicid & (__max_threads_per_core - 1)))
 		cpumask_set_cpu(cpu, &__cpu_primary_thread_mask);
 }
 
+/*
+ * Select core 0 of every package and all SMT threads of that core so that the
+ * mask stays at core granularity.
+ */
+static void cpu_mark_primary_core(unsigned int cpu, unsigned int apicid)
+{
+	if (!((apicid >> get_count_order(__max_threads_per_core)) &
+	      (__max_cores_per_package - 1)))
+		cpumask_set_cpu(cpu, &__cpu_primary_core_mask);
+}
+
 /*
  * Convert the APIC ID to a domain level ID by masking out the low bits
  * below the domain level @dom.
@@ -505,13 +518,16 @@ void __init topology_init_possible_cpus(void)
 	pr_info("Max. logical dies:     %3u\n", cntb);
 	pr_info("Max. dies per package: %3u\n", __max_dies_per_package);
 
-	cnta = domain_weight(TOPO_CORE_DOMAIN);
-	cntb = domain_weight(TOPO_SMT_DOMAIN);
+	cntb = domain_weight(TOPO_CORE_DOMAIN);
+	__max_cores_per_package = 1U << (get_count_order(cntb) - get_count_order(cnta));
+	pr_info("Max. cores per package:%3u\n", __max_cores_per_package);
+
+	cnta = domain_weight(TOPO_SMT_DOMAIN);
 	/*
 	 * Can't use order delta here as order(cnta) can be equal
 	 * order(cntb) even if cnta != cntb.
 	 */
-	__max_threads_per_core = DIV_ROUND_UP(cntb, cnta);
+	__max_threads_per_core = DIV_ROUND_UP(cnta, cntb);
 	pr_info("Max. threads per core: %3u\n", __max_threads_per_core);
 
 	firstid = find_first_bit(apic_maps[TOPO_SMT_DOMAIN].map, MAX_LOCAL_APIC);
@@ -545,6 +561,7 @@ void __init topology_init_possible_cpus(void)
 			continue;
 
 		cpu_mark_primary_thread(cpu, apicid);
+		cpu_mark_primary_core(cpu, apicid);
 		set_cpu_present(cpu, test_bit(apicid, phys_cpu_present_map));
 	}
 }
diff --git a/arch/x86/kernel/cpu/topology_common.c b/arch/x86/kernel/cpu/topology_common.c
index 6845e3c63fbb..dc1d96be8ba5 100644
--- a/arch/x86/kernel/cpu/topology_common.c
+++ b/arch/x86/kernel/cpu/topology_common.c
@@ -20,6 +20,15 @@ EXPORT_SYMBOL_GPL(__amd_nodes_per_pkg);
 /* CPUs which are the primary SMT threads */
 struct cpumask __cpu_primary_thread_mask __read_mostly;
 
+/*
+ * CPUs belonging to the primary core of each package
+ *
+ * To preserve core-domain granularity and to avoid the next-level primary
+ * details that cpu_primary_thread_mask can present, the mask includes all
+ * threads of each primary core.
+ */
+struct cpumask __cpu_primary_core_mask __read_mostly;
+
 void topology_set_dom(struct topo_scan *tscan, enum x86_topology_domains dom,
 		      unsigned int shift, unsigned int ncpus)
 {
-- 
2.53.0


  parent reply	other threads:[~2026-09-12  0:35 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-12  0:08 [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Chang S. Bae
2026-09-12  0:08 ` [PATCH RFC v1 1/8] cpu/hotplug: Allow architecture-specific primary CPU bringup Chang S. Bae
2026-09-12  0:08 ` [PATCH RFC v1 2/8] x86/hotplug: Implement SMT-primary selection for parallel bringup Chang S. Bae
2026-09-12  0:08 ` Chang S. Bae [this message]
2026-09-12  0:08 ` [PATCH RFC v1 4/8] x86/microcode: Extend struct microcode_ops for uniform loading Chang S. Bae
2026-09-12  0:08 ` [PATCH RFC v1 5/8] x86/microcode: Clarify online enforcement with " Chang S. Bae
2026-09-12  0:08 ` [PATCH RFC v1 6/8] x86/microcode: Support uniform scope for late loading Chang S. Bae
2026-09-12  0:08 ` [PATCH RFC v1 7/8] x86/microcode/intel: Support uniform scope for early loading Chang S. Bae
2026-09-12  0:08 ` [PATCH RFC v1 8/8] x86/microcode/intel: Enable uniform loading Chang S. Bae
2026-09-16  0:45 ` [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Borislav Petkov

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=20260912000815.997720-4-chang.seok.bae@intel.com \
    --to=chang.seok.bae@intel.com \
    --cc=bp@alien8.de \
    --cc=dave.hansen@linux.intel.com \
    --cc=kevin.shu@intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=tglx@kernel.org \
    --cc=x86@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

all inboxes | Powered by JetHome®