diff mbox series

[v4,02/10] x86/cpu/topology: Add CPU type to struct cpuinfo_topology

Message ID 20240930-add-cpu-type-v4-2-104892b7ab5f@linux.intel.com
State New
Headers show
Series Add CPU-type to topology | expand

Commit Message

Pawan Gupta Sept. 30, 2024, 2:47 p.m. UTC
Sometimes it is required to take actions based on if a CPU is a performance
or efficiency core. As an example, intel_pstate driver uses the Intel
core-type to determine CPU scaling. Also, some CPU vulnerabilities only
affect a specific CPU type, like RFDS only affects Intel Atom. Hybrid
systems that have variants P+E, P-only(Core) and E-only(Atom), it is not
straightforward to identify which variant is affected by a type specific
vulnerability.

Such processors do have CPUID field that can uniquely identify them. Like,
P+E, P-only and E-only enumerates CPUID.1A.CORE_TYPE identification, while
P+E additionally enumerates CPUID.7.HYBRID. Based on this information, it
is possible for boot CPU to identify if a system has mixed CPU types.

Add a new field hw_cpu_type to struct cpuinfo_topology that stores the
hardware specific CPU type. This saves the overhead of IPIs to get the CPU
type of a different CPU. CPU type is populated early in the boot process,
before vulnerabilities are enumerated.

Signed-off-by: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
---
 arch/x86/include/asm/cpu.h            |  6 ++++++
 arch/x86/include/asm/processor.h      | 11 +++++++++++
 arch/x86/include/asm/topology.h       |  8 ++++++++
 arch/x86/kernel/cpu/debugfs.c         |  1 +
 arch/x86/kernel/cpu/intel.c           |  5 +++++
 arch/x86/kernel/cpu/topology_common.c | 11 +++++++++++
 6 files changed, 42 insertions(+)

Comments

Borislav Petkov Oct. 18, 2024, 4:19 p.m. UTC | #1
On Mon, Sep 30, 2024 at 07:47:24AM -0700, Pawan Gupta wrote:
> Subject: Re: [PATCH v4 02/10] x86/cpu/topology: Add CPU type to struct...

x86/cpu: ...

is enough.

> diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
> index 4a686f0e5dbf..61c8336bc99b 100644
> --- a/arch/x86/include/asm/processor.h
> +++ b/arch/x86/include/asm/processor.h
> @@ -105,6 +105,17 @@ struct cpuinfo_topology {
>  	// Cache level topology IDs
>  	u32			llc_id;
>  	u32			l2c_id;
> +
> +	// Hardware defined CPU-type
> +	union {
> +		u32		hw_cpu_type;
> +		struct {
> +			/* CPUID.1A.EAX[23-0] */

Might as well stick to only // comments as we do those in headers now.

> +			u32	intel_core_native_model_id:24;

wow, that needs a whole breath to speak: "intel_core_native_model_id".

"core" and "native" look like they wanna go. What is that field supposed to
mean even?

> +			/* CPUID.1A.EAX[31-24] */
> +			u32	intel_core_type:8;
> +		};
> +	};
>  };

...

> +enum x86_topology_hw_cpu_type topology_hw_cpu_type(struct cpuinfo_x86 *c)
> +{
> +	if (c->x86_vendor == X86_VENDOR_INTEL)
> +		return c->topo.intel_core_type;
> +
> +	return c->topo.hw_cpu_type;

Huh, the other vendors are not enabled. This should return
TOPO_HW_CPU_TYPE_UNKNOWN then.

I know, it does but make explicit pls.
Pawan Gupta Oct. 18, 2024, 9:11 p.m. UTC | #2
On Fri, Oct 18, 2024 at 11:28:31AM -0500, Mario Limonciello wrote:
> On 9/30/2024 09:47, Pawan Gupta wrote:
> > Sometimes it is required to take actions based on if a CPU is a performance
> > or efficiency core. As an example, intel_pstate driver uses the Intel
> > core-type to determine CPU scaling. Also, some CPU vulnerabilities only
> > affect a specific CPU type, like RFDS only affects Intel Atom. Hybrid
> > systems that have variants P+E, P-only(Core) and E-only(Atom), it is not
> > straightforward to identify which variant is affected by a type specific
> > vulnerability.
> > 
> > Such processors do have CPUID field that can uniquely identify them. Like,
> > P+E, P-only and E-only enumerates CPUID.1A.CORE_TYPE identification, while
> > P+E additionally enumerates CPUID.7.HYBRID. Based on this information, it
> > is possible for boot CPU to identify if a system has mixed CPU types.
> > 
> > Add a new field hw_cpu_type to struct cpuinfo_topology that stores the
> > hardware specific CPU type. This saves the overhead of IPIs to get the CPU
> > type of a different CPU. CPU type is populated early in the boot process,
> > before vulnerabilities are enumerated.
> > 
> > Signed-off-by: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
> > ---
> >   arch/x86/include/asm/cpu.h            |  6 ++++++
> >   arch/x86/include/asm/processor.h      | 11 +++++++++++
> >   arch/x86/include/asm/topology.h       |  8 ++++++++
> >   arch/x86/kernel/cpu/debugfs.c         |  1 +
> >   arch/x86/kernel/cpu/intel.c           |  5 +++++
> >   arch/x86/kernel/cpu/topology_common.c | 11 +++++++++++
> >   6 files changed, 42 insertions(+)
> > 
> > diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h
> > index aa30fd8cad7f..2244dd86066a 100644
> > --- a/arch/x86/include/asm/cpu.h
> > +++ b/arch/x86/include/asm/cpu.h
> > @@ -32,6 +32,7 @@ extern bool handle_user_split_lock(struct pt_regs *regs, long error_code);
> >   extern bool handle_guest_split_lock(unsigned long ip);
> >   extern void handle_bus_lock(struct pt_regs *regs);
> >   u8 get_this_hybrid_cpu_type(void);
> > +u32 intel_native_model_id(struct cpuinfo_x86 *c);
> >   #else
> >   static inline void __init sld_setup(struct cpuinfo_x86 *c) {}
> >   static inline bool handle_user_split_lock(struct pt_regs *regs, long error_code)
> > @@ -50,6 +51,11 @@ static inline u8 get_this_hybrid_cpu_type(void)
> >   {
> >   	return 0;
> >   }
> > +
> > +static u32 intel_native_model_id(struct cpuinfo_x86 *c)
> > +{
> > +	return 0;
> > +}
> >   #endif
> >   #ifdef CONFIG_IA32_FEAT_CTL
> >   void init_ia32_feat_ctl(struct cpuinfo_x86 *c);
> > diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
> > index 4a686f0e5dbf..61c8336bc99b 100644
> > --- a/arch/x86/include/asm/processor.h
> > +++ b/arch/x86/include/asm/processor.h
> > @@ -105,6 +105,17 @@ struct cpuinfo_topology {
> >   	// Cache level topology IDs
> >   	u32			llc_id;
> >   	u32			l2c_id;
> > +
> > +	// Hardware defined CPU-type
> > +	union {
> > +		u32		hw_cpu_type;
> > +		struct {
> > +			/* CPUID.1A.EAX[23-0] */
> > +			u32	intel_core_native_model_id:24;
> > +			/* CPUID.1A.EAX[31-24] */
> > +			u32	intel_core_type:8;
> > +		};
> > +	};
> >   };
> >   struct cpuinfo_x86 {
> > diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h
> > index aef70336d624..faf7cb7f7d7e 100644
> > --- a/arch/x86/include/asm/topology.h
> > +++ b/arch/x86/include/asm/topology.h
> > @@ -114,6 +114,12 @@ enum x86_topology_domains {
> >   	TOPO_MAX_DOMAIN,
> >   };
> > +enum x86_topology_hw_cpu_type {
> > +	TOPO_HW_CPU_TYPE_UNKNOWN	= 0,
> > +	TOPO_HW_CPU_TYPE_INTEL_ATOM	= 0x20,
> > +	TOPO_HW_CPU_TYPE_INTEL_CORE	= 0x40,
> > +};
> 
> This isn't exactly generic.  Unless you have a strong need to know "Atom"

The goal was not to have generic cpu_type here, but the actual CPU type
that hardware enumerates. I was asked to prepend "hw_" to cpu_type to make
is clear that this is hardware defined, and to leave scope for generic
cpu_type, if we add those in future.

> instead of "Efficient" or "Core" instead of "Performance" I think it would
> be better to do this as:
>
> enum x86_topology_hw_core_type {
> 	TOPO_HW_CORE_TYPE_UNKNOWN	= 0,
> 	TOPO_HW_CORE_TYPE_PERFORMANT,
> 	TOPO_HW_CORE_TYPE_EFFICIENT,
> };
>
> Then you can do the mapping of 0x20 = Efficient and 0x40 = performant in the
> Intel topology lookup function.

I can add a lookup function, but I wanted to understand the use case of
generic cpu_type. If we always have to lookup and map the cpu_type, then
why not have the actual cpu_type in the first place?

One case where generic cpu_type can be useful is when we expose them to
userspace, which I think is inevitable. Overall I am fine with adding generic
cpu type. It may also make sense to have separate accessors for generic and
and hardware defined cpu_type, and the generic ones when we actually have a
use case. Thoughts?

> After you land the series we can do something similar to move AMD code
> around and map it out to the right generic mapping.
Borislav Petkov Oct. 21, 2024, 1:38 p.m. UTC | #3
On October 18, 2024 10:30:53 PM GMT+02:00, Pawan Gupta <pawan.kumar.gupta@linux.intel.com> wrote:
>I will drop "core", but can we keep "native"? "native" is used in SDM to
>define this field. Also model_id could be confused with model number.
>
>  From Intel SDM Vol. 2A:
>
>  Bits 23-00: Native model ID of the core. The core-type and native model
>  ID can be used to uniquely identify the microarchitecture of the core.
>  This native model ID is not unique across core types, and not related to
>  the model ID reported in CPUID leaf 01H, and does not identify the SOC.

I'm still not clear on what "native" is supposed to mean here?

The core is born this way and then it changes... so this is its native model ID? Weird...

>Yes, topo.hw_cpu_type is initialized to TOPO_HW_CPU_TYPE_UNKNOWN. We should
>not ideally need the vendor check at all. As long as topo.hw_cpu_type has
>the core type, returning it should be enough here. For Intel hw_cpu_type
>also has the native_model_id, that is why we need the vendor check.
>
>If AMD or other vendors have similar use case, it makes sense to add the
>explicit vendor check. Please let me know if thats the likely case.

Yes, it either needs to be vendor-agnostic or you need to accommodate all vendors. Former sounds cleaner...
Pawan Gupta Oct. 21, 2024, 4:36 p.m. UTC | #4
On Mon, Oct 21, 2024 at 03:38:06PM +0200, Borislav Petkov wrote:
> On October 18, 2024 10:30:53 PM GMT+02:00, Pawan Gupta <pawan.kumar.gupta@linux.intel.com> wrote:
> >I will drop "core", but can we keep "native"? "native" is used in SDM to
> >define this field. Also model_id could be confused with model number.
> >
> >  From Intel SDM Vol. 2A:
> >
> >  Bits 23-00: Native model ID of the core. The core-type and native model
> >  ID can be used to uniquely identify the microarchitecture of the core.
> >  This native model ID is not unique across core types, and not related to
> >  the model ID reported in CPUID leaf 01H, and does not identify the SOC.
> 
> I'm still not clear on what "native" is supposed to mean here?
>
> The core is born this way and then it changes... so this is its native
> model ID? Weird...

In a hybrid system the model number reported by CPUID could represent
multiple core-types. As model number is same for all cores, it is
insufficient to uniquely identify the microarchitecture of a core. I
believe "native model ID" bridges that gap as it is specific to a core.

> >Yes, topo.hw_cpu_type is initialized to TOPO_HW_CPU_TYPE_UNKNOWN. We should
> >not ideally need the vendor check at all. As long as topo.hw_cpu_type has
> >the core type, returning it should be enough here. For Intel hw_cpu_type
> >also has the native_model_id, that is why we need the vendor check.
> >
> >If AMD or other vendors have similar use case, it makes sense to add the
> >explicit vendor check. Please let me know if thats the likely case.
> 
> Yes, it either needs to be vendor-agnostic or you need to accommodate all
> vendors. Former sounds cleaner...

Ok, I will add an explicit vendor check for AMD as well.
Luck, Tony Oct. 21, 2024, 5:13 p.m. UTC | #5
>> The core is born this way and then it changes... so this is its native
>> model ID? Weird...
>
> In a hybrid system the model number reported by CPUID could represent
> multiple core-types. As model number is same for all cores, it is
> insufficient to uniquely identify the microarchitecture of a core. I
> believe "native model ID" bridges that gap as it is specific to a core.

Example from <asm/intel-family.h>

#define INTEL_ALDERLAKE_L               IFM(6, 0x9A) /* Golden Cove / Gracemont */

#define INTEL_RAPTORLAKE                IFM(6, 0xB7) /* Raptor Cove / Enhanced Gracemont */

The native model number could be helpful to tell what each of your P-cores and E-cores
are based on. Could be useful when the same base core is used in more than one SoC
generation.

-Tony
Borislav Petkov Oct. 21, 2024, 5:45 p.m. UTC | #6
On Mon, Oct 21, 2024 at 05:13:41PM +0000, Luck, Tony wrote:
> Example from <asm/intel-family.h>
> 
> #define INTEL_ALDERLAKE_L               IFM(6, 0x9A) /* Golden Cove / Gracemont */
> 
> #define INTEL_RAPTORLAKE                IFM(6, 0xB7) /* Raptor Cove / Enhanced Gracemont */
> 
> The native model number could be helpful to tell what each of your P-cores and E-cores
> are based on. Could be useful when the same base core is used in more than one SoC
> generation.

How am I supposed to read this?

Gracemont is the "native", base core and from that they do a Golden Cove and
a Raptor Cove?

What does that have to do with the P- and E-cores? Are those above two
different types wrt performance?

Thx.
Luck, Tony Oct. 21, 2024, 5:59 p.m. UTC | #7
> > #define INTEL_ALDERLAKE_L               IFM(6, 0x9A) /* Golden Cove / Gracemont */
> >
> > #define INTEL_RAPTORLAKE                IFM(6, 0xB7) /* Raptor Cove / Enhanced Gracemont */
> >
> > The native model number could be helpful to tell what each of your P-cores and E-cores
> > are based on. Could be useful when the same base core is used in more than one SoC
> > generation.
>
> How am I supposed to read this?
>
> Gracemont is the "native", base core and from that they do a Golden Cove and
> a Raptor Cove?
>
> What does that have to do with the P- and E-cores? Are those above two
> different types wrt performance?

If you are running on an Alder Lake you'll see that CPUID says you are
family 6, model 9A. CPUID will also tell you that this is a hybrid part
with both P-cores and E-cores. CPUID will tell you which logical CPUs
are P-cores and which are E-cores.

But in some cases you might want to know *which* E-core you have
(likely cases are for performance counters). The native model number
will help with that.

We probably ought to publish a table of native model numbers ... but
I don't know if there are plans to do that. Likely that Wikipedia will get
to that before Intel does. 

-Tony
diff mbox series

Patch

diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h
index aa30fd8cad7f..2244dd86066a 100644
--- a/arch/x86/include/asm/cpu.h
+++ b/arch/x86/include/asm/cpu.h
@@ -32,6 +32,7 @@  extern bool handle_user_split_lock(struct pt_regs *regs, long error_code);
 extern bool handle_guest_split_lock(unsigned long ip);
 extern void handle_bus_lock(struct pt_regs *regs);
 u8 get_this_hybrid_cpu_type(void);
+u32 intel_native_model_id(struct cpuinfo_x86 *c);
 #else
 static inline void __init sld_setup(struct cpuinfo_x86 *c) {}
 static inline bool handle_user_split_lock(struct pt_regs *regs, long error_code)
@@ -50,6 +51,11 @@  static inline u8 get_this_hybrid_cpu_type(void)
 {
 	return 0;
 }
+
+static u32 intel_native_model_id(struct cpuinfo_x86 *c)
+{
+	return 0;
+}
 #endif
 #ifdef CONFIG_IA32_FEAT_CTL
 void init_ia32_feat_ctl(struct cpuinfo_x86 *c);
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 4a686f0e5dbf..61c8336bc99b 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -105,6 +105,17 @@  struct cpuinfo_topology {
 	// Cache level topology IDs
 	u32			llc_id;
 	u32			l2c_id;
+
+	// Hardware defined CPU-type
+	union {
+		u32		hw_cpu_type;
+		struct {
+			/* CPUID.1A.EAX[23-0] */
+			u32	intel_core_native_model_id:24;
+			/* CPUID.1A.EAX[31-24] */
+			u32	intel_core_type:8;
+		};
+	};
 };
 
 struct cpuinfo_x86 {
diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h
index aef70336d624..faf7cb7f7d7e 100644
--- a/arch/x86/include/asm/topology.h
+++ b/arch/x86/include/asm/topology.h
@@ -114,6 +114,12 @@  enum x86_topology_domains {
 	TOPO_MAX_DOMAIN,
 };
 
+enum x86_topology_hw_cpu_type {
+	TOPO_HW_CPU_TYPE_UNKNOWN	= 0,
+	TOPO_HW_CPU_TYPE_INTEL_ATOM	= 0x20,
+	TOPO_HW_CPU_TYPE_INTEL_CORE	= 0x40,
+};
+
 struct x86_topology_system {
 	unsigned int	dom_shifts[TOPO_MAX_DOMAIN];
 	unsigned int	dom_size[TOPO_MAX_DOMAIN];
@@ -149,6 +155,8 @@  extern unsigned int __max_threads_per_core;
 extern unsigned int __num_threads_per_package;
 extern unsigned int __num_cores_per_package;
 
+enum x86_topology_hw_cpu_type topology_hw_cpu_type(struct cpuinfo_x86 *c);
+
 static inline unsigned int topology_max_packages(void)
 {
 	return __max_logical_packages;
diff --git a/arch/x86/kernel/cpu/debugfs.c b/arch/x86/kernel/cpu/debugfs.c
index ca373b990c47..d1731e0e36b0 100644
--- a/arch/x86/kernel/cpu/debugfs.c
+++ b/arch/x86/kernel/cpu/debugfs.c
@@ -22,6 +22,7 @@  static int cpu_debug_show(struct seq_file *m, void *p)
 	seq_printf(m, "die_id:              %u\n", c->topo.die_id);
 	seq_printf(m, "cu_id:               %u\n", c->topo.cu_id);
 	seq_printf(m, "core_id:             %u\n", c->topo.core_id);
+	seq_printf(m, "hw_cpu_type:       0x%x\n", c->topo.hw_cpu_type);
 	seq_printf(m, "logical_pkg_id:      %u\n", c->topo.logical_pkg_id);
 	seq_printf(m, "logical_die_id:      %u\n", c->topo.logical_die_id);
 	seq_printf(m, "llc_id:              %u\n", c->topo.llc_id);
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index e7656cbef68d..e56401c5c050 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -1299,3 +1299,8 @@  u8 get_this_hybrid_cpu_type(void)
 
 	return cpuid_eax(0x0000001a) >> X86_HYBRID_CPU_TYPE_ID_SHIFT;
 }
+
+u32 intel_native_model_id(struct cpuinfo_x86 *c)
+{
+	return c->topo.intel_core_native_model_id;
+}
diff --git a/arch/x86/kernel/cpu/topology_common.c b/arch/x86/kernel/cpu/topology_common.c
index 9a6069e7133c..e4814cd3d8ae 100644
--- a/arch/x86/kernel/cpu/topology_common.c
+++ b/arch/x86/kernel/cpu/topology_common.c
@@ -27,6 +27,14 @@  void topology_set_dom(struct topo_scan *tscan, enum x86_topology_domains dom,
 	}
 }
 
+enum x86_topology_hw_cpu_type topology_hw_cpu_type(struct cpuinfo_x86 *c)
+{
+	if (c->x86_vendor == X86_VENDOR_INTEL)
+		return c->topo.intel_core_type;
+
+	return c->topo.hw_cpu_type;
+}
+
 static unsigned int __maybe_unused parse_num_cores_legacy(struct cpuinfo_x86 *c)
 {
 	struct {
@@ -87,6 +95,7 @@  static void parse_topology(struct topo_scan *tscan, bool early)
 		.cu_id			= 0xff,
 		.llc_id			= BAD_APICID,
 		.l2c_id			= BAD_APICID,
+		.hw_cpu_type		= TOPO_HW_CPU_TYPE_UNKNOWN,
 	};
 	struct cpuinfo_x86 *c = tscan->c;
 	struct {
@@ -132,6 +141,8 @@  static void parse_topology(struct topo_scan *tscan, bool early)
 	case X86_VENDOR_INTEL:
 		if (!IS_ENABLED(CONFIG_CPU_SUP_INTEL) || !cpu_parse_topology_ext(tscan))
 			parse_legacy(tscan);
+		if (c->cpuid_level >= 0x1a)
+			c->topo.hw_cpu_type = cpuid_eax(0x1a);
 		break;
 	case X86_VENDOR_HYGON:
 		if (IS_ENABLED(CONFIG_CPU_SUP_HYGON))