2021-03-10 16:45:28

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 00/25] Add Alder Lake support for perf (kernel)

From: Kan Liang <[email protected]>

Changes since V1:
- Drop all user space patches, which will be reviewed later separately.
- Don't save the CPU type in struct cpuinfo_x86. Instead, provide helper
functions to get parameters of hybrid CPUs. (Boris)
- Rework the perf kernel patches according to Peter's suggestion. The
key changes include,
- Code style changes. Drop all the macro which names in capital
letters.
- Drop the hybrid PMU index, track the pointer of the hybrid PMU in
the per-CPU struct cpu_hw_events.
- Fix the x86_get_pmu() support
- Fix the allocate_fake_cpuc() support
- Fix validate_group() support
- Dynamically allocate the *hybrid_pmu for each hybrid PMU

Alder Lake uses a hybrid architecture utilizing Golden Cove cores
and Gracemont cores. On such architectures, all CPUs support the same,
homogeneous and symmetric, instruction set. Also, CPUID enumerate
the same features for all CPUs. There may be model-specific differences,
such as those addressed in this patchset.

The first two patches enumerate the hybrid CPU feature bit and provide
helper functions to get parameters of hybrid CPUs. (The initial idea [1]
was to save the CPU type in a new field x86_cpu_type in struct cpuinfo_x86.
Since the only user of the new field is perf, querying the
X86_FEATURE_HYBRID_CPU at the call site is a simpler alternative.[2])
Compared with the initial submission, the below two concerns[3][4] are
also addressed,
- Provide a good use case, PMU.
- Clarify what Intel Hybrid Technology is and is not.

The PMU capabilities for Golden Cove core and Gracemont core are not the
same. The key differences include the number of counters, events, perf
metrics feature, and PEBS-via-PT feature. A dedicated hybrid PMU has to
be registered for each of them. However, the current perf X86 assumes
that there is only one CPU PMU. To handle the hybrid PMUs, the patchset
- Introduce a new struct x86_hybrid_pmu to save the unique capabilities
from different PMUs. It's part of the global x86_pmu. The architecture
capabilities, which are available for all PMUs, are still saved in
the global x86_pmu. To save the space, the x86_hybrid_pmu is
dynamically allocated.
- The hybrid PMU registration has been moved to the cpu_starting(),
because only boot CPU is available when invoking the
init_hw_perf_events().
- Hybrid PMUs have different events and formats. Add new structures and
helpers for events attribute and format attribute which take the PMU
type into account.
- Add a PMU aware version PERF_TYPE_HARDWARE_PMU and
PERF_TYPE_HW_CACHE_PMU to facilitate user space tools

The uncore, MSR and cstate are the same between hybrid CPUs.
Don't need to register hybrid PMUs for them.

The generic code kernel/events/core.c is not hybrid friendly either,
especially for the per-task monitoring. Peter once proposed a
patchset[5], but it hasn't been merged. This patchset doesn't intend to
improve the generic code (which can be improved later separately). It
still uses the capability PERF_PMU_CAP_HETEROGENEOUS_CPUS for each
hybrid PMUs. For per-task and system-wide monitoring, user space tools
have to create events on all available hybrid PMUs. The events which are
from different hybrid PMUs cannot be included in the same group.

[1]. https://lore.kernel.org/lkml/[email protected]/
[2]. https://lore.kernel.org/lkml/[email protected]/
[3]. https://lore.kernel.org/lkml/[email protected]/
[4]. https://lore.kernel.org/lkml/[email protected]/
[5]. https://lkml.kernel.org/r/[email protected]/

Kan Liang (22):
perf/x86: Track pmu in per-CPU cpu_hw_events
perf/x86/intel: Hybrid PMU support for perf capabilities
perf/x86: Hybrid PMU support for intel_ctrl
perf/x86: Hybrid PMU support for counters
perf/x86: Hybrid PMU support for unconstrained
perf/x86: Hybrid PMU support for hardware cache event
perf/x86: Hybrid PMU support for event constraints
perf/x86: Hybrid PMU support for extra_regs
perf/x86/intel: Factor out intel_pmu_check_num_counters
perf/x86/intel: Factor out intel_pmu_check_event_constraints
perf/x86/intel: Factor out intel_pmu_check_extra_regs
perf/x86: Remove temporary pmu assignment in event_init
perf/x86: Factor out x86_pmu_show_pmu_cap
perf/x86: Register hybrid PMUs
perf/x86: Add structures for the attributes of Hybrid PMUs
perf/x86/intel: Add attr_update for Hybrid PMUs
perf/x86: Support filter_match callback
perf/x86/intel: Add Alder Lake Hybrid support
perf: Introduce PERF_TYPE_HARDWARE_PMU and PERF_TYPE_HW_CACHE_PMU
perf/x86/intel/uncore: Add Alder Lake support
perf/x86/msr: Add Alder Lake CPU support
perf/x86/cstate: Add Alder Lake CPU support

Ricardo Neri (2):
x86/cpufeatures: Enumerate Intel Hybrid Technology feature bit
x86/cpu: Add helper functions to get parameters of hybrid CPUs

Zhang Rui (1):
perf/x86/rapl: Add support for Intel Alder Lake

arch/x86/events/core.c | 314 ++++++++++++----
arch/x86/events/intel/core.c | 717 +++++++++++++++++++++++++++++++-----
arch/x86/events/intel/cstate.c | 39 +-
arch/x86/events/intel/ds.c | 32 +-
arch/x86/events/intel/lbr.c | 9 +-
arch/x86/events/intel/uncore.c | 7 +
arch/x86/events/intel/uncore.h | 1 +
arch/x86/events/intel/uncore_snb.c | 131 +++++++
arch/x86/events/msr.c | 2 +
arch/x86/events/perf_event.h | 108 +++++-
arch/x86/events/rapl.c | 2 +
arch/x86/include/asm/cpu.h | 6 +
arch/x86/include/asm/cpufeatures.h | 1 +
arch/x86/include/asm/intel-family.h | 4 +
arch/x86/include/asm/msr-index.h | 3 +
arch/x86/include/asm/processor.h | 3 +
arch/x86/kernel/cpu/intel.c | 44 +++
include/linux/perf_event.h | 12 +
include/uapi/linux/perf_event.h | 26 ++
kernel/events/core.c | 14 +-
20 files changed, 1281 insertions(+), 194 deletions(-)

--
2.7.4


2021-03-10 16:45:29

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 2/25] x86/cpu: Add helper functions to get parameters of hybrid CPUs

From: Ricardo Neri <[email protected]>

On processors with Intel Hybrid Technology (i.e., one having more than one
type of CPU in the same package), all CPUs support the same instruction
set and enumerate the same features on CPUID. Thus, all software can run
on any CPU without restrictions. However, there may be model-specific
differences among types of CPUs. For instance, each type of CPU may support
a different number of performance counters. Also, machine check error banks
may be wired differently. Even though most software will not care about
these differences, kernel subsystems dealing with these differences must
know. Add a new member to cpuinfo_x86 that subsystems can query to know
the type of CPU.

Hybrid processors also have a native model ID to uniquely identify the
micro-architecture of each CPU. Please note that the native model ID is not
related with the existing x86_model_id read from CPUID leaf 0x1.

Add helper functions and definitions to get both the CPU type and the
combined CPU type and native model ID. Only expose get_hybrid_cpu_type().
The sole known use case (i.e., perf for hybrid processors) only wants to
know the type but not the model ID. get_hybrid_params() can be exposed in
the future if new use cases arise.

The Intel Software Developer's Manual defines the CPU type and the CPU
native model ID as 8-bit and 24-bit identifiers, respectively.

Cc: Andi Kleen <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Dave Hansen <[email protected]>
Cc: Kan Liang <[email protected]>
Cc: "Peter Zijlstra (Intel)" <[email protected]>
Cc: "Rafael J. Wysocki" <[email protected]>
Cc: "Ravi V. Shankar" <[email protected]>
Cc: Srinivas Pandruvada <[email protected]>
Cc: [email protected]
Reviewed-by: Len Brown <[email protected]>
Reviewed-by: Tony Luck <[email protected]>
Signed-off-by: Ricardo Neri <[email protected]>
---
Changes since v1 (as part of patchset for perf change for Alderlake)
* Removed cpuinfo_x86.x86_cpu_type. It can be added later if needed.
Instead, implement helper functions that subsystems can use.(Boris)
* Add definitions for Atom and Core CPU types. (Kan)

Changes since v1 (in a separate posting)
* Simplify code by using cpuid_eax(). (Boris)
* Reworded the commit message to clarify the concept of Intel Hybrid
Technology. Stress that all CPUs can run the same instruction set
and support the same features.
---
arch/x86/include/asm/cpu.h | 6 ++++
arch/x86/include/asm/intel-family.h | 4 +++
arch/x86/include/asm/processor.h | 3 ++
arch/x86/kernel/cpu/intel.c | 44 +++++++++++++++++++++++++++++
4 files changed, 57 insertions(+)

diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h
index da78ccbd493b..845c478dfcd4 100644
--- a/arch/x86/include/asm/cpu.h
+++ b/arch/x86/include/asm/cpu.h
@@ -45,6 +45,7 @@ extern void __init cpu_set_core_cap_bits(struct cpuinfo_x86 *c);
extern void switch_to_sld(unsigned long tifn);
extern bool handle_user_split_lock(struct pt_regs *regs, long error_code);
extern bool handle_guest_split_lock(unsigned long ip);
+u8 get_hybrid_cpu_type(int cpu);
#else
static inline void __init cpu_set_core_cap_bits(struct cpuinfo_x86 *c) {}
static inline void switch_to_sld(unsigned long tifn) {}
@@ -57,6 +58,11 @@ static inline bool handle_guest_split_lock(unsigned long ip)
{
return false;
}
+
+static inline u8 get_hybrid_cpu_type(int cpu)
+{
+ return 0;
+}
#endif
#ifdef CONFIG_IA32_FEAT_CTL
void init_ia32_feat_ctl(struct cpuinfo_x86 *c);
diff --git a/arch/x86/include/asm/intel-family.h b/arch/x86/include/asm/intel-family.h
index 9abe842dbd84..b5fb475bdf10 100644
--- a/arch/x86/include/asm/intel-family.h
+++ b/arch/x86/include/asm/intel-family.h
@@ -134,4 +134,8 @@
/* Family 5 */
#define INTEL_FAM5_QUARK_X1000 0x09 /* Quark X1000 SoC */

+/* Types of CPUs in hybrid parts. */
+#define INTEL_HYBRID_TYPE_ATOM 0x20
+#define INTEL_HYBRID_TYPE_CORE 0x40
+
#endif /* _ASM_X86_INTEL_FAMILY_H */
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index dc6d149bf851..d43d7a11afba 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -166,6 +166,9 @@ enum cpuid_regs_idx {

#define X86_VENDOR_UNKNOWN 0xff

+#define X86_HYBRID_CPU_TYPE_ID_SHIFT 24
+#define X86_HYBRID_CPU_NATIVE_MODEL_ID_MASK 0xffffff
+
/*
* capabilities of CPUs
*/
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index 0e422a544835..aa0dac287c1f 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -1195,3 +1195,47 @@ void __init cpu_set_core_cap_bits(struct cpuinfo_x86 *c)
cpu_model_supports_sld = true;
split_lock_setup();
}
+
+static void read_hybrid_params(void *data)
+{
+ u32 *eax = data;
+
+ *eax = cpuid_eax(0x0000001a);
+}
+
+/**
+ * get_hybrid_params() - Get type and model ID of a hybrid CPU
+ * @cpu: CPU of which hybrid parameters are queried.
+ *
+ * Returns the combined CPU type [31:24] and the native model ID [23:0] of a
+ * CPU in a hybrid processor. It must be called with preemption disabled.
+ * If @cpu is invalid or the processor is not hybrid, returns 0;
+ */
+static u32 get_hybrid_params(int cpu)
+{
+ u32 param;
+
+ if (!cpu_feature_enabled(X86_FEATURE_HYBRID_CPU))
+ return 0;
+
+ if (cpu == smp_processor_id())
+ return cpuid_eax(0x0000001a);
+
+ if (cpu < 0 || cpu >= nr_cpu_ids)
+ return 0;
+
+ smp_call_function_single(cpu, read_hybrid_params, &param, true);
+
+ return param;
+}
+
+/**
+ * get_hybrid_cpu_type() - Get the type of a hybrid CPU
+ *
+ * Returns the type (i.e., Atom or Core) of a CPU in a hybrid processor. It
+ * must be called with preemption disabled.
+ */
+u8 get_hybrid_cpu_type(int cpu)
+{
+ return get_hybrid_params(cpu) >> X86_HYBRID_CPU_TYPE_ID_SHIFT;
+}
--
2.17.1

2021-03-10 16:45:29

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 03/25] perf/x86: Track pmu in per-CPU cpu_hw_events

From: Kan Liang <[email protected]>

Some platforms, e.g. Alder Lake, have hybrid architecture. In the same
package, there may be more than one type of CPU. The PMU capabilities
are different among different types of CPU. Perf will register a
dedicated PMU for each type of CPU.

Add a 'pmu' variable in the struct cpu_hw_events to track the dedicated
PMU of the current CPU.

Current x86_get_pmu() use the global 'pmu', which will be broken on a
hybrid platform. Modify it to apply the 'pmu' of the specific CPU.

Initialize the per-CPU 'pmu' variable with the global 'pmu'. There is
nothing changed for the non-hybrid platforms.

The is_x86_event() will be updated in the later patch ("perf/x86:
Register hybrid PMUs") for hybrid platforms. For the non-hybrid
platforms, nothing is changed here.

Suggested-by: Peter Zijlstra (Intel) <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/core.c | 17 +++++++++++++----
arch/x86/events/intel/core.c | 2 +-
arch/x86/events/intel/ds.c | 4 ++--
arch/x86/events/intel/lbr.c | 9 +++++----
arch/x86/events/perf_event.h | 4 +++-
5 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 6ddeed3..4873e40 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -45,9 +45,11 @@
#include "perf_event.h"

struct x86_pmu x86_pmu __read_mostly;
+static struct pmu pmu;

DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
.enabled = 1,
+ .pmu = &pmu,
};

DEFINE_STATIC_KEY_FALSE(rdpmc_never_available_key);
@@ -720,16 +722,23 @@ void x86_pmu_enable_all(int added)
}
}

-static struct pmu pmu;
-
static inline int is_x86_event(struct perf_event *event)
{
return event->pmu == &pmu;
}

-struct pmu *x86_get_pmu(void)
+struct pmu *x86_get_pmu(unsigned int cpu)
{
- return &pmu;
+ struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
+
+ /*
+ * All CPUs of the hybrid type have been offline.
+ * The x86_get_pmu() should not be invoked.
+ */
+ if (WARN_ON_ONCE(!cpuc->pmu))
+ return &pmu;
+
+ return cpuc->pmu;
}
/*
* Event scheduler state:
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 5bac48d..6a4d6b2 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -4873,7 +4873,7 @@ static void update_tfa_sched(void *ignored)
* and if so force schedule out for all event types all contexts
*/
if (test_bit(3, cpuc->active_mask))
- perf_pmu_resched(x86_get_pmu());
+ perf_pmu_resched(x86_get_pmu(smp_processor_id()));
}

static ssize_t show_sysctl_tfa(struct device *cdev,
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index 7ebae18..1bfea8c 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -2192,7 +2192,7 @@ void __init intel_ds_init(void)
PERF_SAMPLE_TIME;
x86_pmu.flags |= PMU_FL_PEBS_ALL;
pebs_qual = "-baseline";
- x86_get_pmu()->capabilities |= PERF_PMU_CAP_EXTENDED_REGS;
+ x86_get_pmu(smp_processor_id())->capabilities |= PERF_PMU_CAP_EXTENDED_REGS;
} else {
/* Only basic record supported */
x86_pmu.large_pebs_flags &=
@@ -2207,7 +2207,7 @@ void __init intel_ds_init(void)

if (x86_pmu.intel_cap.pebs_output_pt_available) {
pr_cont("PEBS-via-PT, ");
- x86_get_pmu()->capabilities |= PERF_PMU_CAP_AUX_OUTPUT;
+ x86_get_pmu(smp_processor_id())->capabilities |= PERF_PMU_CAP_AUX_OUTPUT;
}

break;
diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
index 21890da..bb4486c 100644
--- a/arch/x86/events/intel/lbr.c
+++ b/arch/x86/events/intel/lbr.c
@@ -705,7 +705,7 @@ void intel_pmu_lbr_add(struct perf_event *event)

void release_lbr_buffers(void)
{
- struct kmem_cache *kmem_cache = x86_get_pmu()->task_ctx_cache;
+ struct kmem_cache *kmem_cache;
struct cpu_hw_events *cpuc;
int cpu;

@@ -714,6 +714,7 @@ void release_lbr_buffers(void)

for_each_possible_cpu(cpu) {
cpuc = per_cpu_ptr(&cpu_hw_events, cpu);
+ kmem_cache = x86_get_pmu(cpu)->task_ctx_cache;
if (kmem_cache && cpuc->lbr_xsave) {
kmem_cache_free(kmem_cache, cpuc->lbr_xsave);
cpuc->lbr_xsave = NULL;
@@ -1609,7 +1610,7 @@ void intel_pmu_lbr_init_hsw(void)
x86_pmu.lbr_sel_mask = LBR_SEL_MASK;
x86_pmu.lbr_sel_map = hsw_lbr_sel_map;

- x86_get_pmu()->task_ctx_cache = create_lbr_kmem_cache(size, 0);
+ x86_get_pmu(smp_processor_id())->task_ctx_cache = create_lbr_kmem_cache(size, 0);

if (lbr_from_signext_quirk_needed())
static_branch_enable(&lbr_from_quirk_key);
@@ -1629,7 +1630,7 @@ __init void intel_pmu_lbr_init_skl(void)
x86_pmu.lbr_sel_mask = LBR_SEL_MASK;
x86_pmu.lbr_sel_map = hsw_lbr_sel_map;

- x86_get_pmu()->task_ctx_cache = create_lbr_kmem_cache(size, 0);
+ x86_get_pmu(smp_processor_id())->task_ctx_cache = create_lbr_kmem_cache(size, 0);

/*
* SW branch filter usage:
@@ -1726,7 +1727,7 @@ static bool is_arch_lbr_xsave_available(void)

void __init intel_pmu_arch_lbr_init(void)
{
- struct pmu *pmu = x86_get_pmu();
+ struct pmu *pmu = x86_get_pmu(smp_processor_id());
union cpuid28_eax eax;
union cpuid28_ebx ebx;
union cpuid28_ecx ecx;
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 53b2b5f..a4c2f65 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -327,6 +327,8 @@ struct cpu_hw_events {
int n_pair; /* Large increment events */

void *kfree_on_online[X86_PERF_KFREE_MAX];
+
+ struct pmu *pmu;
};

#define __EVENT_CONSTRAINT_RANGE(c, e, n, m, w, o, f) { \
@@ -905,7 +907,7 @@ static struct perf_pmu_events_ht_attr event_attr_##v = { \
.event_str_ht = ht, \
}

-struct pmu *x86_get_pmu(void);
+struct pmu *x86_get_pmu(unsigned int cpu);
extern struct x86_pmu x86_pmu __read_mostly;

static __always_inline struct x86_perf_task_context_opt *task_context_opt(void *ctx)
--
2.7.4

2021-03-10 16:45:29

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 05/25] perf/x86: Hybrid PMU support for intel_ctrl

From: Kan Liang <[email protected]>

The intel_ctrl is the counter mask of a PMU. The PMU counter information
may be different among hybrid PMUs, each hybrid PMU should use its own
intel_ctrl to check and access the counters.

When handling a certain hybrid PMU, apply the intel_ctrl from the
corresponding hybrid PMU.

When checking the HW existence, apply the PMU and number of counters
from the corresponding hybrid PMU as well. Perf will check the HW
existence for each Hybrid PMU before registration. Expose the
check_hw_exists() for a later patch.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/core.c | 14 +++++++-------
arch/x86/events/intel/core.c | 14 +++++++++-----
arch/x86/events/perf_event.h | 10 ++++++++--
3 files changed, 24 insertions(+), 14 deletions(-)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 316efb1..90eb82e 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -226,7 +226,7 @@ static void release_pmc_hardware(void) {}

#endif

-static bool check_hw_exists(void)
+bool check_hw_exists(struct pmu *pmu, int num_counters, int num_counters_fixed)
{
u64 val, val_fail = -1, val_new= ~0;
int i, reg, reg_fail = -1, ret = 0;
@@ -237,7 +237,7 @@ static bool check_hw_exists(void)
* Check to see if the BIOS enabled any of the counters, if so
* complain and bail.
*/
- for (i = 0; i < x86_pmu.num_counters; i++) {
+ for (i = 0; i < num_counters; i++) {
reg = x86_pmu_config_addr(i);
ret = rdmsrl_safe(reg, &val);
if (ret)
@@ -251,13 +251,13 @@ static bool check_hw_exists(void)
}
}

- if (x86_pmu.num_counters_fixed) {
+ if (num_counters_fixed) {
reg = MSR_ARCH_PERFMON_FIXED_CTR_CTRL;
ret = rdmsrl_safe(reg, &val);
if (ret)
goto msr_fail;
- for (i = 0; i < x86_pmu.num_counters_fixed; i++) {
- if (fixed_counter_disabled(i))
+ for (i = 0; i < num_counters_fixed; i++) {
+ if (fixed_counter_disabled(i, pmu))
continue;
if (val & (0x03 << i*4)) {
bios_fail = 1;
@@ -1543,7 +1543,7 @@ void perf_event_print_debug(void)
cpu, idx, prev_left);
}
for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) {
- if (fixed_counter_disabled(idx))
+ if (fixed_counter_disabled(idx, cpuc->pmu))
continue;
rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, pmc_count);

@@ -1995,7 +1995,7 @@ static int __init init_hw_perf_events(void)
pmu_check_apic();

/* sanity check that the hardware exists or is emulated */
- if (!check_hw_exists())
+ if (!check_hw_exists(&pmu, x86_pmu.num_counters, x86_pmu.num_counters_fixed))
return 0;

pr_cont("%s PMU driver.\n", x86_pmu.name);
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index e466a49..1f1c003 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -2153,10 +2153,11 @@ static void intel_pmu_disable_all(void)
static void __intel_pmu_enable_all(int added, bool pmi)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ u64 intel_ctrl = hybrid(cpuc->pmu, intel_ctrl);

intel_pmu_lbr_enable_all(pmi);
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL,
- x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_guest_mask);
+ intel_ctrl & ~cpuc->intel_ctrl_guest_mask);

if (test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask)) {
struct perf_event *event =
@@ -2709,6 +2710,7 @@ int intel_pmu_save_and_restart(struct perf_event *event)
static void intel_pmu_reset(void)
{
struct debug_store *ds = __this_cpu_read(cpu_hw_events.ds);
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
unsigned long flags;
int idx;

@@ -2724,7 +2726,7 @@ static void intel_pmu_reset(void)
wrmsrl_safe(x86_pmu_event_addr(idx), 0ull);
}
for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) {
- if (fixed_counter_disabled(idx))
+ if (fixed_counter_disabled(idx, cpuc->pmu))
continue;
wrmsrl_safe(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull);
}
@@ -2753,6 +2755,7 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status)
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
int bit;
int handled = 0;
+ u64 intel_ctrl = hybrid(cpuc->pmu, intel_ctrl);

inc_irq_stat(apic_perf_irqs);

@@ -2798,7 +2801,7 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status)

handled++;
x86_pmu.drain_pebs(regs, &data);
- status &= x86_pmu.intel_ctrl | GLOBAL_STATUS_TRACE_TOPAPMI;
+ status &= intel_ctrl | GLOBAL_STATUS_TRACE_TOPAPMI;

/*
* PMI throttle may be triggered, which stops the PEBS event.
@@ -3804,10 +3807,11 @@ static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
struct perf_guest_switch_msr *arr = cpuc->guest_switch_msrs;
+ u64 intel_ctrl = hybrid(cpuc->pmu, intel_ctrl);

arr[0].msr = MSR_CORE_PERF_GLOBAL_CTRL;
- arr[0].host = x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_guest_mask;
- arr[0].guest = x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_host_mask;
+ arr[0].host = intel_ctrl & ~cpuc->intel_ctrl_guest_mask;
+ arr[0].guest = intel_ctrl & ~cpuc->intel_ctrl_host_mask;
if (x86_pmu.flags & PMU_FL_PEBS_ALL)
arr[0].guest &= ~cpuc->pebs_enabled;
else
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index aa7cc88..b499abd6 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -635,6 +635,7 @@ enum {
struct x86_hybrid_pmu {
struct pmu pmu;
union perf_capabilities intel_cap;
+ u64 intel_ctrl;
};

static __always_inline bool is_hybrid(void)
@@ -1001,6 +1002,9 @@ static inline int x86_pmu_rdpmc_index(int index)
return x86_pmu.rdpmc_index ? x86_pmu.rdpmc_index(index) : index;
}

+bool check_hw_exists(struct pmu *pmu, int num_counters,
+ int num_counters_fixed);
+
int x86_add_exclusive(unsigned int what);

void x86_del_exclusive(unsigned int what);
@@ -1105,9 +1109,11 @@ ssize_t events_sysfs_show(struct device *dev, struct device_attribute *attr,
ssize_t events_ht_sysfs_show(struct device *dev, struct device_attribute *attr,
char *page);

-static inline bool fixed_counter_disabled(int i)
+static inline bool fixed_counter_disabled(int i, struct pmu *pmu)
{
- return !(x86_pmu.intel_ctrl >> (i + INTEL_PMC_IDX_FIXED));
+ u64 intel_ctrl = hybrid(pmu, intel_ctrl);
+
+ return !(intel_ctrl >> (i + INTEL_PMC_IDX_FIXED));
}

#ifdef CONFIG_CPU_SUP_AMD
--
2.7.4

2021-03-10 16:45:32

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 04/25] perf/x86/intel: Hybrid PMU support for perf capabilities

From: Kan Liang <[email protected]>

Some platforms, e.g. Alder Lake, have hybrid architecture. Although most
PMU capabilities are the same, there are still some unique PMU
capabilities for different hybrid PMUs. Perf should register a dedicated
pmu for each hybrid PMU.

Add a new struct x86_hybrid_pmu, which saves the dedicated pmu and
capabilities for each hybrid PMU.

The architecture MSR, MSR_IA32_PERF_CAPABILITIES, only indicates the
architecture features which are available on all hybrid PMUs. The
architecture features are stored in the global x86_pmu.intel_cap.

For Alder Lake, the model-specific features are perf metrics and
PEBS-via-PT. The corresponding bits of the global x86_pmu.intel_cap
should be 0 for these two features. Perf should not use the global
intel_cap to check the features on a hybrid system.
Add a dedicated intel_cap in the x86_hybrid_pmu to store the
model-specific capabilities. Use the dedicated intel_cap to replace
the global intel_cap for thse two features. The dedicated intel_cap
will be set in the following "Add Alder Lake Hybrid support" patch.

Suggested-by: Peter Zijlstra (Intel) <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/core.c | 6 ++++--
arch/x86/events/intel/core.c | 27 ++++++++++++++++++++++-----
arch/x86/events/intel/ds.c | 2 +-
arch/x86/events/perf_event.h | 35 +++++++++++++++++++++++++++++++++++
arch/x86/include/asm/msr-index.h | 3 +++
5 files changed, 65 insertions(+), 8 deletions(-)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 4873e40..316efb1 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -1101,8 +1101,9 @@ static void del_nr_metric_event(struct cpu_hw_events *cpuc,
static int collect_event(struct cpu_hw_events *cpuc, struct perf_event *event,
int max_count, int n)
{
+ union perf_capabilities intel_cap = hybrid(cpuc->pmu, intel_cap);

- if (x86_pmu.intel_cap.perf_metrics && add_nr_metric_event(cpuc, event))
+ if (intel_cap.perf_metrics && add_nr_metric_event(cpuc, event))
return -EINVAL;

if (n >= max_count + cpuc->n_metric)
@@ -1578,6 +1579,7 @@ void x86_pmu_stop(struct perf_event *event, int flags)
static void x86_pmu_del(struct perf_event *event, int flags)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ union perf_capabilities intel_cap = hybrid(cpuc->pmu, intel_cap);
int i;

/*
@@ -1617,7 +1619,7 @@ static void x86_pmu_del(struct perf_event *event, int flags)
}
cpuc->event_constraint[i-1] = NULL;
--cpuc->n_events;
- if (x86_pmu.intel_cap.perf_metrics)
+ if (intel_cap.perf_metrics)
del_nr_metric_event(cpuc, event);

perf_event_update_userpage(event);
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 6a4d6b2..e466a49 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3646,6 +3646,15 @@ static inline bool is_mem_loads_aux_event(struct perf_event *event)
return (event->attr.config & INTEL_ARCH_EVENT_MASK) == X86_CONFIG(.event=0x03, .umask=0x82);
}

+static inline bool intel_pmu_has_cap(struct perf_event *event, int idx)
+{
+ union perf_capabilities *intel_cap;
+
+ intel_cap = is_hybrid() ? &hybrid_pmu(event->pmu)->intel_cap :
+ &x86_pmu.intel_cap;
+
+ return test_bit(idx, (unsigned long *)&intel_cap->capabilities);
+}

static int intel_pmu_hw_config(struct perf_event *event)
{
@@ -3709,7 +3718,7 @@ static int intel_pmu_hw_config(struct perf_event *event)
* with a slots event as group leader. When the slots event
* is used in a metrics group, it too cannot support sampling.
*/
- if (x86_pmu.intel_cap.perf_metrics && is_topdown_event(event)) {
+ if (intel_pmu_has_cap(event, PERF_CAP_METRICS_IDX) && is_topdown_event(event)) {
if (event->attr.config1 || event->attr.config2)
return -EINVAL;

@@ -4216,8 +4225,16 @@ static void intel_pmu_cpu_starting(int cpu)
if (x86_pmu.version > 1)
flip_smm_bit(&x86_pmu.attr_freeze_on_smi);

- /* Disable perf metrics if any added CPU doesn't support it. */
- if (x86_pmu.intel_cap.perf_metrics) {
+ /*
+ * Disable perf metrics if any added CPU doesn't support it.
+ *
+ * Turn off the check for a hybrid architecture, because the
+ * architecture MSR, MSR_IA32_PERF_CAPABILITIES, only indicate
+ * the architecture features. The perf metrics is a model-specific
+ * feature for now. The corresponding bit should always be 0 on
+ * a hybrid platform, e.g., Alder Lake.
+ */
+ if (!is_hybrid() && x86_pmu.intel_cap.perf_metrics) {
union perf_capabilities perf_cap;

rdmsrl(MSR_IA32_PERF_CAPABILITIES, perf_cap.capabilities);
@@ -4327,7 +4344,7 @@ static int intel_pmu_check_period(struct perf_event *event, u64 value)

static int intel_pmu_aux_output_match(struct perf_event *event)
{
- if (!x86_pmu.intel_cap.pebs_output_pt_available)
+ if (!intel_pmu_has_cap(event, PERF_CAP_PT_IDX))
return 0;

return is_intel_pt_event(event);
@@ -5767,7 +5784,7 @@ __init int intel_pmu_init(void)
pr_cont("full-width counters, ");
}

- if (x86_pmu.intel_cap.perf_metrics)
+ if (!is_hybrid() && x86_pmu.intel_cap.perf_metrics)
x86_pmu.intel_ctrl |= 1ULL << GLOBAL_CTRL_EN_PERF_METRICS;

return 0;
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index 1bfea8c..9328aa1 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -2205,7 +2205,7 @@ void __init intel_ds_init(void)
}
pr_cont("PEBS fmt4%c%s, ", pebs_type, pebs_qual);

- if (x86_pmu.intel_cap.pebs_output_pt_available) {
+ if (!is_hybrid() && x86_pmu.intel_cap.pebs_output_pt_available) {
pr_cont("PEBS-via-PT, ");
x86_get_pmu(smp_processor_id())->capabilities |= PERF_PMU_CAP_AUX_OUTPUT;
}
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index a4c2f65..aa7cc88 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -632,6 +632,31 @@ enum {
x86_lbr_exclusive_max,
};

+struct x86_hybrid_pmu {
+ struct pmu pmu;
+ union perf_capabilities intel_cap;
+};
+
+static __always_inline bool is_hybrid(void)
+{
+ return unlikely(cpu_feature_enabled(X86_FEATURE_HYBRID_CPU));
+}
+
+static __always_inline struct x86_hybrid_pmu *hybrid_pmu(struct pmu *pmu)
+{
+ return container_of(pmu, struct x86_hybrid_pmu, pmu);
+}
+
+#define hybrid(_pmu, _field) \
+({ \
+ typeof(x86_pmu._field) __F = x86_pmu._field; \
+ \
+ if (is_hybrid() && (_pmu)) \
+ __F = hybrid_pmu(_pmu)->_field; \
+ \
+ __F; \
+})
+
/*
* struct x86_pmu - generic x86 pmu
*/
@@ -818,6 +843,16 @@ struct x86_pmu {
int (*check_period) (struct perf_event *event, u64 period);

int (*aux_output_match) (struct perf_event *event);
+
+ /*
+ * Hybrid support
+ *
+ * Most PMU capabilities are the same among different hybrid PMUs.
+ * The global x86_pmu saves the architecture capabilities, which
+ * are available for all PMUs. The hybrid_pmu only includes the
+ * unique capabilities.
+ */
+ struct x86_hybrid_pmu *hybrid_pmu;
};

struct x86_perf_task_context_opt {
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 546d6ec..163f5d2 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -185,6 +185,9 @@
#define MSR_PEBS_DATA_CFG 0x000003f2
#define MSR_IA32_DS_AREA 0x00000600
#define MSR_IA32_PERF_CAPABILITIES 0x00000345
+#define PERF_CAP_METRICS_IDX 15
+#define PERF_CAP_PT_IDX 16
+
#define MSR_PEBS_LD_LAT_THRESHOLD 0x000003f6

#define MSR_IA32_RTIT_CTL 0x00000570
--
2.7.4

2021-03-10 16:45:32

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 1/25] x86/cpufeatures: Enumerate Intel Hybrid Technology feature bit

From: Ricardo Neri <[email protected]>

Add feature enumeration to identify a processor with Intel Hybrid
Technology: one in which CPUs of more than one type are the same package.
On a hybrid processor, all CPUs support the same homogeneous (i.e.,
symmetric) instruction set. All CPUs enumerate the same features in CPUID.
Thus, software (user space and kernel) can run and migrate to any CPU in
the system as well as utilize any of the enumerated features without any
change or special provisions. The main difference among CPUs in a hybrid
processor are power and performance properties.

Cc: Andi Kleen <[email protected]>
Cc: Kan Liang <[email protected]>
Cc: "Peter Zijlstra (Intel)" <[email protected]>
Cc: "Rafael J. Wysocki" <[email protected]>
Cc: "Ravi V. Shankar" <[email protected]>
Cc: Srinivas Pandruvada <[email protected]>
Cc: [email protected]
Reviewed-by: Len Brown <[email protected]>
Reviewed-by: Tony Luck <[email protected]>
Signed-off-by: Ricardo Neri <[email protected]>
---
Changes since v1 (as part of patchset for perf change for Alderlake)
* None

Changes since v1 (in a separate posting):
* Reworded commit message to clearly state what is Intel Hybrid
Technology. Stress that all CPUs can run the same instruction
set and support the same features.
---
arch/x86/include/asm/cpufeatures.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index cc96e26d69f7..e7cfc9eedf8d 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -374,6 +374,7 @@
#define X86_FEATURE_MD_CLEAR (18*32+10) /* VERW clears CPU buffers */
#define X86_FEATURE_TSX_FORCE_ABORT (18*32+13) /* "" TSX_FORCE_ABORT */
#define X86_FEATURE_SERIALIZE (18*32+14) /* SERIALIZE instruction */
+#define X86_FEATURE_HYBRID_CPU (18*32+15) /* This part has CPUs of more than one type */
#define X86_FEATURE_TSXLDTRK (18*32+16) /* TSX Suspend Load Address Tracking */
#define X86_FEATURE_PCONFIG (18*32+18) /* Intel PCONFIG */
#define X86_FEATURE_ARCH_LBR (18*32+19) /* Intel ARCH LBR */
--
2.17.1

2021-03-10 16:45:43

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 06/25] perf/x86: Hybrid PMU support for counters

From: Kan Liang <[email protected]>

The number of GP and fixed counters are different among hybrid PMUs.
Each hybrid PMU should use its own counter related information.

When handling a certain hybrid PMU, apply the number of counters from
the corresponding hybrid PMU.

When reserving the counters in the initialization of a new event,
reserve all possible counters. Add a num_hybrid_pmus to indicate the
number of the hybrid PMUs.

The number of counter recored in the global x86_pmu is for the
architecture counters which are available for all hybrid PMUs. KVM
doesn't support the hybrid PMU yet. Return the number of the
architecture counters for now.

For the functions only available for the old platforms, e.g.,
intel_pmu_drain_pebs_nhm(), nothing is changed.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/core.c | 55 ++++++++++++++++++++++++++++++--------------
arch/x86/events/intel/core.c | 8 ++++---
arch/x86/events/intel/ds.c | 14 +++++++----
arch/x86/events/perf_event.h | 4 ++++
4 files changed, 56 insertions(+), 25 deletions(-)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 90eb82e..039a851 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -180,16 +180,29 @@ static DEFINE_MUTEX(pmc_reserve_mutex);

#ifdef CONFIG_X86_LOCAL_APIC

+static inline int get_possible_num_counters(void)
+{
+ int i, num_counters = x86_pmu.num_counters;
+
+ if (!is_hybrid())
+ return num_counters;
+
+ for (i = 0; i < x86_pmu.num_hybrid_pmus; i++)
+ num_counters = max_t(int, num_counters, x86_pmu.hybrid_pmu[i].num_counters);
+
+ return num_counters;
+}
+
static bool reserve_pmc_hardware(void)
{
- int i;
+ int i, num_counters = get_possible_num_counters();

- for (i = 0; i < x86_pmu.num_counters; i++) {
+ for (i = 0; i < num_counters; i++) {
if (!reserve_perfctr_nmi(x86_pmu_event_addr(i)))
goto perfctr_fail;
}

- for (i = 0; i < x86_pmu.num_counters; i++) {
+ for (i = 0; i < num_counters; i++) {
if (!reserve_evntsel_nmi(x86_pmu_config_addr(i)))
goto eventsel_fail;
}
@@ -200,7 +213,7 @@ static bool reserve_pmc_hardware(void)
for (i--; i >= 0; i--)
release_evntsel_nmi(x86_pmu_config_addr(i));

- i = x86_pmu.num_counters;
+ i = num_counters;

perfctr_fail:
for (i--; i >= 0; i--)
@@ -211,9 +224,9 @@ static bool reserve_pmc_hardware(void)

static void release_pmc_hardware(void)
{
- int i;
+ int i, num_counters = get_possible_num_counters();

- for (i = 0; i < x86_pmu.num_counters; i++) {
+ for (i = 0; i < num_counters; i++) {
release_perfctr_nmi(x86_pmu_event_addr(i));
release_evntsel_nmi(x86_pmu_config_addr(i));
}
@@ -941,6 +954,7 @@ EXPORT_SYMBOL_GPL(perf_assign_events);

int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
{
+ int num_counters = hybrid(cpuc->pmu, num_counters);
struct event_constraint *c;
struct perf_event *e;
int n0, i, wmin, wmax, unsched = 0;
@@ -1016,7 +1030,7 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)

/* slow path */
if (i != n) {
- int gpmax = x86_pmu.num_counters;
+ int gpmax = num_counters;

/*
* Do not allow scheduling of more than half the available
@@ -1037,7 +1051,7 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
* the extra Merge events needed by large increment events.
*/
if (x86_pmu.flags & PMU_FL_PAIR) {
- gpmax = x86_pmu.num_counters - cpuc->n_pair;
+ gpmax = num_counters - cpuc->n_pair;
WARN_ON(gpmax <= 0);
}

@@ -1124,10 +1138,12 @@ static int collect_event(struct cpu_hw_events *cpuc, struct perf_event *event,
*/
static int collect_events(struct cpu_hw_events *cpuc, struct perf_event *leader, bool dogrp)
{
+ int num_counters = hybrid(cpuc->pmu, num_counters);
+ int num_counters_fixed = hybrid(cpuc->pmu, num_counters_fixed);
struct perf_event *event;
int n, max_count;

- max_count = x86_pmu.num_counters + x86_pmu.num_counters_fixed;
+ max_count = num_counters + num_counters_fixed;

/* current number of events already accepted */
n = cpuc->n_events;
@@ -1495,18 +1511,18 @@ void perf_event_print_debug(void)
{
u64 ctrl, status, overflow, pmc_ctrl, pmc_count, prev_left, fixed;
u64 pebs, debugctl;
- struct cpu_hw_events *cpuc;
+ int cpu = smp_processor_id();
+ struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
+ int num_counters = hybrid(cpuc->pmu, num_counters);
+ int num_counters_fixed = hybrid(cpuc->pmu, num_counters_fixed);
unsigned long flags;
- int cpu, idx;
+ int idx;

- if (!x86_pmu.num_counters)
+ if (!num_counters)
return;

local_irq_save(flags);

- cpu = smp_processor_id();
- cpuc = &per_cpu(cpu_hw_events, cpu);
-
if (x86_pmu.version >= 2) {
rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl);
rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status);
@@ -1529,7 +1545,7 @@ void perf_event_print_debug(void)
}
pr_info("CPU#%d: active: %016llx\n", cpu, *(u64 *)cpuc->active_mask);

- for (idx = 0; idx < x86_pmu.num_counters; idx++) {
+ for (idx = 0; idx < num_counters; idx++) {
rdmsrl(x86_pmu_config_addr(idx), pmc_ctrl);
rdmsrl(x86_pmu_event_addr(idx), pmc_count);

@@ -1542,7 +1558,7 @@ void perf_event_print_debug(void)
pr_info("CPU#%d: gen-PMC%d left: %016llx\n",
cpu, idx, prev_left);
}
- for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) {
+ for (idx = 0; idx < num_counters_fixed; idx++) {
if (fixed_counter_disabled(idx, cpuc->pmu))
continue;
rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, pmc_count);
@@ -2784,6 +2800,11 @@ unsigned long perf_misc_flags(struct pt_regs *regs)
void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
{
cap->version = x86_pmu.version;
+ /*
+ * KVM doesn't support the hybrid PMU yet.
+ * Return the common value in global x86_pmu,
+ * which available for all cores.
+ */
cap->num_counters_gp = x86_pmu.num_counters;
cap->num_counters_fixed = x86_pmu.num_counters_fixed;
cap->bit_width_gp = x86_pmu.cntval_bits;
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 1f1c003..e187cf5 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -2711,21 +2711,23 @@ static void intel_pmu_reset(void)
{
struct debug_store *ds = __this_cpu_read(cpu_hw_events.ds);
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ int num_counters_fixed = hybrid(cpuc->pmu, num_counters_fixed);
+ int num_counters = hybrid(cpuc->pmu, num_counters);
unsigned long flags;
int idx;

- if (!x86_pmu.num_counters)
+ if (!num_counters)
return;

local_irq_save(flags);

pr_info("clearing PMU state on CPU#%d\n", smp_processor_id());

- for (idx = 0; idx < x86_pmu.num_counters; idx++) {
+ for (idx = 0; idx < num_counters; idx++) {
wrmsrl_safe(x86_pmu_config_addr(idx), 0ull);
wrmsrl_safe(x86_pmu_event_addr(idx), 0ull);
}
- for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) {
+ for (idx = 0; idx < num_counters_fixed; idx++) {
if (fixed_counter_disabled(idx, cpuc->pmu))
continue;
wrmsrl_safe(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull);
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index 9328aa1..312bf3b 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -1007,6 +1007,8 @@ void intel_pmu_pebs_sched_task(struct perf_event_context *ctx, bool sched_in)
static inline void pebs_update_threshold(struct cpu_hw_events *cpuc)
{
struct debug_store *ds = cpuc->ds;
+ int max_pebs_events = hybrid(cpuc->pmu, max_pebs_events);
+ int num_counters_fixed = hybrid(cpuc->pmu, num_counters_fixed);
u64 threshold;
int reserved;

@@ -1014,9 +1016,9 @@ static inline void pebs_update_threshold(struct cpu_hw_events *cpuc)
return;

if (x86_pmu.flags & PMU_FL_PEBS_ALL)
- reserved = x86_pmu.max_pebs_events + x86_pmu.num_counters_fixed;
+ reserved = max_pebs_events + num_counters_fixed;
else
- reserved = x86_pmu.max_pebs_events;
+ reserved = max_pebs_events;

if (cpuc->n_pebs == cpuc->n_large_pebs) {
threshold = ds->pebs_absolute_maximum -
@@ -2072,6 +2074,8 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d
{
short counts[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS] = {};
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ int max_pebs_events = hybrid(cpuc->pmu, max_pebs_events);
+ int num_counters_fixed = hybrid(cpuc->pmu, num_counters_fixed);
struct debug_store *ds = cpuc->ds;
struct perf_event *event;
void *base, *at, *top;
@@ -2086,9 +2090,9 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d

ds->pebs_index = ds->pebs_buffer_base;

- mask = ((1ULL << x86_pmu.max_pebs_events) - 1) |
- (((1ULL << x86_pmu.num_counters_fixed) - 1) << INTEL_PMC_IDX_FIXED);
- size = INTEL_PMC_IDX_FIXED + x86_pmu.num_counters_fixed;
+ mask = ((1ULL << max_pebs_events) - 1) |
+ (((1ULL << num_counters_fixed) - 1) << INTEL_PMC_IDX_FIXED);
+ size = INTEL_PMC_IDX_FIXED + num_counters_fixed;

if (unlikely(base >= top)) {
intel_pmu_pebs_event_update_no_drain(cpuc, size);
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index b499abd6..d2f97bd 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -636,6 +636,9 @@ struct x86_hybrid_pmu {
struct pmu pmu;
union perf_capabilities intel_cap;
u64 intel_ctrl;
+ int max_pebs_events;
+ int num_counters;
+ int num_counters_fixed;
};

static __always_inline bool is_hybrid(void)
@@ -853,6 +856,7 @@ struct x86_pmu {
* are available for all PMUs. The hybrid_pmu only includes the
* unique capabilities.
*/
+ int num_hybrid_pmus;
struct x86_hybrid_pmu *hybrid_pmu;
};

--
2.7.4

2021-03-10 16:45:59

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 11/25] perf/x86/intel: Factor out intel_pmu_check_num_counters

From: Kan Liang <[email protected]>

Each Hybrid PMU has to check its own number of counters and mask fixed
counters before registration.

The intel_pmu_check_num_counters will be reused later when registering a
dedicated hybrid PMU.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/intel/core.c | 38 ++++++++++++++++++++++++--------------
1 file changed, 24 insertions(+), 14 deletions(-)

diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 1030fa9..2da6ae2 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -4215,6 +4215,26 @@ static void flip_smm_bit(void *data)
}
}

+static void intel_pmu_check_num_counters(int *num_counters,
+ int *num_counters_fixed,
+ u64 *intel_ctrl, u64 fixed_mask)
+{
+ if (*num_counters > INTEL_PMC_MAX_GENERIC) {
+ WARN(1, KERN_ERR "hw perf events %d > max(%d), clipping!",
+ *num_counters, INTEL_PMC_MAX_GENERIC);
+ *num_counters = INTEL_PMC_MAX_GENERIC;
+ }
+ *intel_ctrl = (1ULL << *num_counters) - 1;
+
+ if (*num_counters_fixed > INTEL_PMC_MAX_FIXED) {
+ WARN(1, KERN_ERR "hw perf events fixed %d > max(%d), clipping!",
+ *num_counters_fixed, INTEL_PMC_MAX_FIXED);
+ *num_counters_fixed = INTEL_PMC_MAX_FIXED;
+ }
+
+ *intel_ctrl |= fixed_mask << INTEL_PMC_IDX_FIXED;
+}
+
static void intel_pmu_cpu_starting(int cpu)
{
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
@@ -5706,20 +5726,10 @@ __init int intel_pmu_init(void)

x86_pmu.attr_update = attr_update;

- if (x86_pmu.num_counters > INTEL_PMC_MAX_GENERIC) {
- WARN(1, KERN_ERR "hw perf events %d > max(%d), clipping!",
- x86_pmu.num_counters, INTEL_PMC_MAX_GENERIC);
- x86_pmu.num_counters = INTEL_PMC_MAX_GENERIC;
- }
- x86_pmu.intel_ctrl = (1ULL << x86_pmu.num_counters) - 1;
-
- if (x86_pmu.num_counters_fixed > INTEL_PMC_MAX_FIXED) {
- WARN(1, KERN_ERR "hw perf events fixed %d > max(%d), clipping!",
- x86_pmu.num_counters_fixed, INTEL_PMC_MAX_FIXED);
- x86_pmu.num_counters_fixed = INTEL_PMC_MAX_FIXED;
- }
-
- x86_pmu.intel_ctrl |= (u64)fixed_mask << INTEL_PMC_IDX_FIXED;
+ intel_pmu_check_num_counters(&x86_pmu.num_counters,
+ &x86_pmu.num_counters_fixed,
+ &x86_pmu.intel_ctrl,
+ (u64)fixed_mask);

/* AnyThread may be deprecated on arch perfmon v5 or later */
if (x86_pmu.intel_cap.anythread_deprecated)
--
2.7.4

2021-03-10 16:46:00

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 12/25] perf/x86/intel: Factor out intel_pmu_check_event_constraints

From: Kan Liang <[email protected]>

Each Hybrid PMU has to check and update its own event constraints before
registration.

The intel_pmu_check_event_constraints will be reused later when
registering a dedicated hybrid PMU.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/intel/core.c | 82 +++++++++++++++++++++++++-------------------
1 file changed, 47 insertions(+), 35 deletions(-)

diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 2da6ae2..6df8edd7 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -4235,6 +4235,49 @@ static void intel_pmu_check_num_counters(int *num_counters,
*intel_ctrl |= fixed_mask << INTEL_PMC_IDX_FIXED;
}

+static void intel_pmu_check_event_constraints(struct event_constraint *event_constraints,
+ int num_counters,
+ int num_counters_fixed,
+ u64 intel_ctrl)
+{
+ struct event_constraint *c;
+
+ if (!event_constraints)
+ return;
+
+ /*
+ * event on fixed counter2 (REF_CYCLES) only works on this
+ * counter, so do not extend mask to generic counters
+ */
+ for_each_event_constraint(c, event_constraints) {
+ /*
+ * Don't extend the topdown slots and metrics
+ * events to the generic counters.
+ */
+ if (c->idxmsk64 & INTEL_PMC_MSK_TOPDOWN) {
+ /*
+ * Disable topdown slots and metrics events,
+ * if slots event is not in CPUID.
+ */
+ if (!(INTEL_PMC_MSK_FIXED_SLOTS & intel_ctrl))
+ c->idxmsk64 = 0;
+ c->weight = hweight64(c->idxmsk64);
+ continue;
+ }
+
+ if (c->cmask == FIXED_EVENT_FLAGS) {
+ /* Disabled fixed counters which are not in CPUID */
+ c->idxmsk64 &= intel_ctrl;
+
+ if (c->idxmsk64 != INTEL_PMC_MSK_FIXED_REF_CYCLES)
+ c->idxmsk64 |= (1ULL << num_counters) - 1;
+ }
+ c->idxmsk64 &=
+ ~(~0ULL << (INTEL_PMC_IDX_FIXED + num_counters_fixed));
+ c->weight = hweight64(c->idxmsk64);
+ }
+}
+
static void intel_pmu_cpu_starting(int cpu)
{
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
@@ -5097,7 +5140,6 @@ __init int intel_pmu_init(void)
union cpuid10_edx edx;
union cpuid10_eax eax;
union cpuid10_ebx ebx;
- struct event_constraint *c;
unsigned int fixed_mask;
struct extra_reg *er;
bool pmem = false;
@@ -5735,40 +5777,10 @@ __init int intel_pmu_init(void)
if (x86_pmu.intel_cap.anythread_deprecated)
x86_pmu.format_attrs = intel_arch_formats_attr;

- if (x86_pmu.event_constraints) {
- /*
- * event on fixed counter2 (REF_CYCLES) only works on this
- * counter, so do not extend mask to generic counters
- */
- for_each_event_constraint(c, x86_pmu.event_constraints) {
- /*
- * Don't extend the topdown slots and metrics
- * events to the generic counters.
- */
- if (c->idxmsk64 & INTEL_PMC_MSK_TOPDOWN) {
- /*
- * Disable topdown slots and metrics events,
- * if slots event is not in CPUID.
- */
- if (!(INTEL_PMC_MSK_FIXED_SLOTS & x86_pmu.intel_ctrl))
- c->idxmsk64 = 0;
- c->weight = hweight64(c->idxmsk64);
- continue;
- }
-
- if (c->cmask == FIXED_EVENT_FLAGS) {
- /* Disabled fixed counters which are not in CPUID */
- c->idxmsk64 &= x86_pmu.intel_ctrl;
-
- if (c->idxmsk64 != INTEL_PMC_MSK_FIXED_REF_CYCLES)
- c->idxmsk64 |= (1ULL << x86_pmu.num_counters) - 1;
- }
- c->idxmsk64 &=
- ~(~0ULL << (INTEL_PMC_IDX_FIXED + x86_pmu.num_counters_fixed));
- c->weight = hweight64(c->idxmsk64);
- }
- }
-
+ intel_pmu_check_event_constraints(x86_pmu.event_constraints,
+ x86_pmu.num_counters,
+ x86_pmu.num_counters_fixed,
+ x86_pmu.intel_ctrl);
/*
* Access LBR MSR may cause #GP under certain circumstances.
* E.g. KVM doesn't support LBR MSR
--
2.7.4

2021-03-10 16:46:00

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 08/25] perf/x86: Hybrid PMU support for hardware cache event

From: Kan Liang <[email protected]>

The hardware cache events are different among hybrid PMUs. Each hybrid
PMU should have its own hw cache event table.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/core.c | 11 +++++++++--
arch/x86/events/perf_event.h | 9 +++++++++
2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 039a851..1db4a67 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -352,6 +352,7 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
{
struct perf_event_attr *attr = &event->attr;
unsigned int cache_type, cache_op, cache_result;
+ struct x86_hybrid_pmu *pmu = is_hybrid() ? hybrid_pmu(event->pmu) : NULL;
u64 config, val;

config = attr->config;
@@ -371,7 +372,10 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
return -EINVAL;
cache_result = array_index_nospec(cache_result, PERF_COUNT_HW_CACHE_RESULT_MAX);

- val = hw_cache_event_ids[cache_type][cache_op][cache_result];
+ if (pmu)
+ val = pmu->hw_cache_event_ids[cache_type][cache_op][cache_result];
+ else
+ val = hw_cache_event_ids[cache_type][cache_op][cache_result];

if (val == 0)
return -ENOENT;
@@ -380,7 +384,10 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
return -EINVAL;

hwc->config |= val;
- attr->config1 = hw_cache_extra_regs[cache_type][cache_op][cache_result];
+ if (pmu)
+ attr->config1 = pmu->hw_cache_extra_regs[cache_type][cache_op][cache_result];
+ else
+ attr->config1 = hw_cache_extra_regs[cache_type][cache_op][cache_result];
return x86_pmu_extra_regs(val, event);
}

diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 7bce193..f464bda 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -640,6 +640,15 @@ struct x86_hybrid_pmu {
int num_counters;
int num_counters_fixed;
struct event_constraint unconstrained;
+
+ u64 hw_cache_event_ids
+ [PERF_COUNT_HW_CACHE_MAX]
+ [PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX];
+ u64 hw_cache_extra_regs
+ [PERF_COUNT_HW_CACHE_MAX]
+ [PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX];
};

static __always_inline bool is_hybrid(void)
--
2.7.4

2021-03-10 16:46:00

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 10/25] perf/x86: Hybrid PMU support for extra_regs

From: Kan Liang <[email protected]>

Different hybrid PMU may have different extra registers, e.g. Core PMU
may have offcore registers, frontend register and ldlat register. Atom
core may only have offcore registers and ldlat register. Each hybrid PMU
should use its own extra_regs.

An Intel Hybrid system should always have extra registers.
Unconditionally allocate shared_regs for Intel Hybrid system.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/core.c | 5 +++--
arch/x86/events/intel/core.c | 15 +++++++++------
arch/x86/events/perf_event.h | 1 +
3 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 38c271a..32e8fed 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -149,15 +149,16 @@ u64 x86_perf_event_update(struct perf_event *event)
*/
static int x86_pmu_extra_regs(u64 config, struct perf_event *event)
{
+ struct extra_reg *extra_regs = hybrid(event->pmu, extra_regs);
struct hw_perf_event_extra *reg;
struct extra_reg *er;

reg = &event->hw.extra_reg;

- if (!x86_pmu.extra_regs)
+ if (!extra_regs)
return 0;

- for (er = x86_pmu.extra_regs; er->msr; er++) {
+ for (er = extra_regs; er->msr; er++) {
if (er->event != (config & er->config_mask))
continue;
if (event->attr.config1 & ~er->valid_mask)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 572509f..1030fa9 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -2966,8 +2966,10 @@ intel_vlbr_constraints(struct perf_event *event)
return NULL;
}

-static int intel_alt_er(int idx, u64 config)
+static int intel_alt_er(struct cpu_hw_events *cpuc,
+ int idx, u64 config)
{
+ struct extra_reg *extra_regs = hybrid(cpuc->pmu, extra_regs);
int alt_idx = idx;

if (!(x86_pmu.flags & PMU_FL_HAS_RSP_1))
@@ -2979,7 +2981,7 @@ static int intel_alt_er(int idx, u64 config)
if (idx == EXTRA_REG_RSP_1)
alt_idx = EXTRA_REG_RSP_0;

- if (config & ~x86_pmu.extra_regs[alt_idx].valid_mask)
+ if (config & ~extra_regs[alt_idx].valid_mask)
return idx;

return alt_idx;
@@ -2987,15 +2989,16 @@ static int intel_alt_er(int idx, u64 config)

static void intel_fixup_er(struct perf_event *event, int idx)
{
+ struct extra_reg *extra_regs = hybrid(event->pmu, extra_regs);
event->hw.extra_reg.idx = idx;

if (idx == EXTRA_REG_RSP_0) {
event->hw.config &= ~INTEL_ARCH_EVENT_MASK;
- event->hw.config |= x86_pmu.extra_regs[EXTRA_REG_RSP_0].event;
+ event->hw.config |= extra_regs[EXTRA_REG_RSP_0].event;
event->hw.extra_reg.reg = MSR_OFFCORE_RSP_0;
} else if (idx == EXTRA_REG_RSP_1) {
event->hw.config &= ~INTEL_ARCH_EVENT_MASK;
- event->hw.config |= x86_pmu.extra_regs[EXTRA_REG_RSP_1].event;
+ event->hw.config |= extra_regs[EXTRA_REG_RSP_1].event;
event->hw.extra_reg.reg = MSR_OFFCORE_RSP_1;
}
}
@@ -3071,7 +3074,7 @@ __intel_shared_reg_get_constraints(struct cpu_hw_events *cpuc,
*/
c = NULL;
} else {
- idx = intel_alt_er(idx, reg->config);
+ idx = intel_alt_er(cpuc, idx, reg->config);
if (idx != reg->idx) {
raw_spin_unlock_irqrestore(&era->lock, flags);
goto again;
@@ -4158,7 +4161,7 @@ int intel_cpuc_prepare(struct cpu_hw_events *cpuc, int cpu)
{
cpuc->pebs_record_size = x86_pmu.pebs_record_size;

- if (x86_pmu.extra_regs || x86_pmu.lbr_sel_map) {
+ if (is_hybrid() || x86_pmu.extra_regs || x86_pmu.lbr_sel_map) {
cpuc->shared_regs = allocate_shared_regs(cpu);
if (!cpuc->shared_regs)
goto err;
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 9316fec..e65a477 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -651,6 +651,7 @@ struct x86_hybrid_pmu {
[PERF_COUNT_HW_CACHE_RESULT_MAX];
struct event_constraint *event_constraints;
struct event_constraint *pebs_constraints;
+ struct extra_reg *extra_regs;
};

static __always_inline bool is_hybrid(void)
--
2.7.4

2021-03-10 16:46:01

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 09/25] perf/x86: Hybrid PMU support for event constraints

From: Kan Liang <[email protected]>

The events are different among hybrid PMUs. Each hybrid PMU should use
its own event constraints.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/core.c | 3 ++-
arch/x86/events/intel/core.c | 5 +++--
arch/x86/events/intel/ds.c | 5 +++--
arch/x86/events/perf_event.h | 2 ++
4 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 1db4a67..38c271a 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -1522,6 +1522,7 @@ void perf_event_print_debug(void)
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
int num_counters = hybrid(cpuc->pmu, num_counters);
int num_counters_fixed = hybrid(cpuc->pmu, num_counters_fixed);
+ struct event_constraint *pebs_constraints = hybrid(cpuc->pmu, pebs_constraints);
unsigned long flags;
int idx;

@@ -1541,7 +1542,7 @@ void perf_event_print_debug(void)
pr_info("CPU#%d: status: %016llx\n", cpu, status);
pr_info("CPU#%d: overflow: %016llx\n", cpu, overflow);
pr_info("CPU#%d: fixed: %016llx\n", cpu, fixed);
- if (x86_pmu.pebs_constraints) {
+ if (pebs_constraints) {
rdmsrl(MSR_IA32_PEBS_ENABLE, pebs);
pr_info("CPU#%d: pebs: %016llx\n", cpu, pebs);
}
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 540dba2..572509f 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3136,10 +3136,11 @@ struct event_constraint *
x86_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
struct perf_event *event)
{
+ struct event_constraint *event_constraints = hybrid(cpuc->pmu, event_constraints);
struct event_constraint *c;

- if (x86_pmu.event_constraints) {
- for_each_event_constraint(c, x86_pmu.event_constraints) {
+ if (event_constraints) {
+ for_each_event_constraint(c, event_constraints) {
if (constraint_match(c, event->hw.config)) {
event->hw.flags |= c->flags;
return c;
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index 312bf3b..f1402bc 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -959,13 +959,14 @@ struct event_constraint intel_spr_pebs_event_constraints[] = {

struct event_constraint *intel_pebs_constraints(struct perf_event *event)
{
+ struct event_constraint *pebs_constraints = hybrid(event->pmu, pebs_constraints);
struct event_constraint *c;

if (!event->attr.precise_ip)
return NULL;

- if (x86_pmu.pebs_constraints) {
- for_each_event_constraint(c, x86_pmu.pebs_constraints) {
+ if (pebs_constraints) {
+ for_each_event_constraint(c, pebs_constraints) {
if (constraint_match(c, event->hw.config)) {
event->hw.flags |= c->flags;
return c;
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index f464bda..9316fec 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -649,6 +649,8 @@ struct x86_hybrid_pmu {
[PERF_COUNT_HW_CACHE_MAX]
[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_COUNT_HW_CACHE_RESULT_MAX];
+ struct event_constraint *event_constraints;
+ struct event_constraint *pebs_constraints;
};

static __always_inline bool is_hybrid(void)
--
2.7.4

2021-03-10 16:46:01

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 13/25] perf/x86/intel: Factor out intel_pmu_check_extra_regs

From: Kan Liang <[email protected]>

Each Hybrid PMU has to check and update its own extra registers before
registration.

The intel_pmu_check_extra_regs will be reused later when registering a
dedicated hybrid PMU.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/intel/core.c | 37 +++++++++++++++++++++++--------------
1 file changed, 23 insertions(+), 14 deletions(-)

diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 6df8edd7..cdfbab3 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -4278,6 +4278,28 @@ static void intel_pmu_check_event_constraints(struct event_constraint *event_con
}
}

+static bool check_msr(unsigned long msr, u64 mask);
+
+static void intel_pmu_check_extra_regs(struct extra_reg *extra_regs)
+{
+ struct extra_reg *er;
+
+ /*
+ * Access extra MSR may cause #GP under certain circumstances.
+ * E.g. KVM doesn't support offcore event
+ * Check all extra_regs here.
+ */
+ if (!extra_regs)
+ return;
+
+ for (er = extra_regs; er->msr; er++) {
+ er->extra_msr_access = check_msr(er->msr, 0x11UL);
+ /* Disable LBR select mapping */
+ if ((er->idx == EXTRA_REG_LBR) && !er->extra_msr_access)
+ x86_pmu.lbr_sel_map = NULL;
+ }
+}
+
static void intel_pmu_cpu_starting(int cpu)
{
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
@@ -5141,7 +5163,6 @@ __init int intel_pmu_init(void)
union cpuid10_eax eax;
union cpuid10_ebx ebx;
unsigned int fixed_mask;
- struct extra_reg *er;
bool pmem = false;
int version, i;
char *name;
@@ -5798,19 +5819,7 @@ __init int intel_pmu_init(void)
if (x86_pmu.lbr_nr)
pr_cont("%d-deep LBR, ", x86_pmu.lbr_nr);

- /*
- * Access extra MSR may cause #GP under certain circumstances.
- * E.g. KVM doesn't support offcore event
- * Check all extra_regs here.
- */
- if (x86_pmu.extra_regs) {
- for (er = x86_pmu.extra_regs; er->msr; er++) {
- er->extra_msr_access = check_msr(er->msr, 0x11UL);
- /* Disable LBR select mapping */
- if ((er->idx == EXTRA_REG_LBR) && !er->extra_msr_access)
- x86_pmu.lbr_sel_map = NULL;
- }
- }
+ intel_pmu_check_extra_regs(x86_pmu.extra_regs);

/* Support full width counters using alternative MSR range */
if (x86_pmu.intel_cap.full_width_write) {
--
2.7.4

2021-03-10 16:46:10

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 14/25] perf/x86: Remove temporary pmu assignment in event_init

From: Kan Liang <[email protected]>

The temporary pmu assignment in event_init is unnecessary.

The assignment was introduced by commit 8113070d6639 ("perf_events:
Add fast-path to the rescheduling code"). At that time, event->pmu is
not assigned yet when initializing an event. The assignment is required.
However, from commit 7e5b2a01d2ca ("perf: provide PMU when initing
events"), the event->pmu is provided before event_init is invoked.
The temporary pmu assignment in event_init should be removed.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/core.c | 11 -----------
1 file changed, 11 deletions(-)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 32e8fed..96b4a72 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -2302,7 +2302,6 @@ static int validate_group(struct perf_event *event)

static int x86_pmu_event_init(struct perf_event *event)
{
- struct pmu *tmp;
int err;

switch (event->attr.type) {
@@ -2317,20 +2316,10 @@ static int x86_pmu_event_init(struct perf_event *event)

err = __x86_pmu_event_init(event);
if (!err) {
- /*
- * we temporarily connect event to its pmu
- * such that validate_group() can classify
- * it as an x86 event using is_x86_event()
- */
- tmp = event->pmu;
- event->pmu = &pmu;
-
if (event->group_leader != event)
err = validate_group(event);
else
err = validate_event(event);
-
- event->pmu = tmp;
}
if (err) {
if (event->destroy)
--
2.7.4

2021-03-10 16:46:32

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 16/25] perf/x86: Register hybrid PMUs

From: Kan Liang <[email protected]>

Different hybrid PMUs have different PMU capabilities and events. Perf
should registers a dedicated PMU for each of them.

To check the X86 event, perf has to go through all possible hybrid pmus.

Only the PMU for the boot CPU is registered in init_hw_perf_events()
because the boot CPU is the only online CPU at that moment.
The init_hybrid_pmu() is introduced to register and initialize the other
type of PMUs when the new type of CPU is online.

All hybrid PMUs have capability PERF_PMU_CAP_HETEROGENEOUS_CPUS.
The PMU name for hybrid PMUs will be "cpu_XXX", which will be assigned
later in a separated patch.

The PMU type id for the core PMU is still PERF_TYPE_RAW. For the other
hybrid PMUs, the PMU type id is not hard code.

The event->cpu must be compatitable with the supported CPUs of the PMU.
Add a check in the x86_pmu_event_init().

The events in a group must be from the same type of hybrid PMU.
The fake cpuc used in the validation must be from the supported CPU of
the event->pmu.

Suggested-by: Peter Zijlstra (Intel) <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/core.c | 110 +++++++++++++++++++++++++++++++++-------
arch/x86/events/intel/core.c | 116 ++++++++++++++++++++++++++++++++++++++++++-
arch/x86/events/perf_event.h | 12 +++++
3 files changed, 218 insertions(+), 20 deletions(-)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 15d9325..7dc1430 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -481,7 +481,7 @@ int x86_setup_perfctr(struct perf_event *event)
local64_set(&hwc->period_left, hwc->sample_period);
}

- if (attr->type == PERF_TYPE_RAW)
+ if (attr->type == event->pmu->type)
return x86_pmu_extra_regs(event->attr.config, event);

if (attr->type == PERF_TYPE_HW_CACHE)
@@ -616,7 +616,7 @@ int x86_pmu_hw_config(struct perf_event *event)
if (!event->attr.exclude_kernel)
event->hw.config |= ARCH_PERFMON_EVENTSEL_OS;

- if (event->attr.type == PERF_TYPE_RAW)
+ if (event->attr.type == event->pmu->type)
event->hw.config |= event->attr.config & X86_RAW_EVENT_MASK;

if (event->attr.sample_period && x86_pmu.limit_period) {
@@ -745,7 +745,17 @@ void x86_pmu_enable_all(int added)

static inline int is_x86_event(struct perf_event *event)
{
- return event->pmu == &pmu;
+ int i;
+
+ if (!is_hybrid())
+ return event->pmu == &pmu;
+
+ for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
+ if (event->pmu == &x86_pmu.hybrid_pmu[i].pmu)
+ return true;
+ }
+
+ return false;
}

struct pmu *x86_get_pmu(unsigned int cpu)
@@ -2061,8 +2071,11 @@ static int __init init_hw_perf_events(void)

pmu.attr_update = x86_pmu.attr_update;

- x86_pmu_show_pmu_cap(x86_pmu.num_counters, x86_pmu.num_counters_fixed,
- x86_pmu.intel_ctrl);
+ if (!is_hybrid()) {
+ x86_pmu_show_pmu_cap(x86_pmu.num_counters,
+ x86_pmu.num_counters_fixed,
+ x86_pmu.intel_ctrl);
+ }

if (!x86_pmu.read)
x86_pmu.read = _x86_pmu_read;
@@ -2092,9 +2105,37 @@ static int __init init_hw_perf_events(void)
if (err)
goto out1;

- err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
- if (err)
- goto out2;
+ if (!is_hybrid()) {
+ err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
+ if (err)
+ goto out2;
+ } else {
+ u8 cpu_type = get_hybrid_cpu_type(smp_processor_id());
+ struct x86_hybrid_pmu *hybrid_pmu;
+ int i;
+
+ for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
+ hybrid_pmu = &x86_pmu.hybrid_pmu[i];
+
+ hybrid_pmu->pmu = pmu;
+ hybrid_pmu->pmu.type = -1;
+ hybrid_pmu->pmu.attr_update = x86_pmu.attr_update;
+ hybrid_pmu->pmu.capabilities |= PERF_PMU_CAP_HETEROGENEOUS_CPUS;
+
+ /* Only register the PMU for the boot CPU */
+ if (hybrid_pmu->cpu_type != cpu_type)
+ continue;
+
+ err = perf_pmu_register(&hybrid_pmu->pmu, hybrid_pmu->name,
+ x86_get_hybrid_pmu_type(cpu_type));
+ if (err) {
+ pr_warn("Failed to register a PMU, err %d\n", err);
+ kfree(x86_pmu.hybrid_pmu);
+ x86_pmu.hybrid_pmu = NULL;
+ goto out2;
+ }
+ }
+ }

return 0;

@@ -2219,16 +2260,25 @@ static void free_fake_cpuc(struct cpu_hw_events *cpuc)
kfree(cpuc);
}

-static struct cpu_hw_events *allocate_fake_cpuc(void)
+static struct cpu_hw_events *allocate_fake_cpuc(struct pmu *event_pmu)
{
struct cpu_hw_events *cpuc;
- int cpu = raw_smp_processor_id();
+ int cpu;

cpuc = kzalloc(sizeof(*cpuc), GFP_KERNEL);
if (!cpuc)
return ERR_PTR(-ENOMEM);
cpuc->is_fake = 1;

+ if (is_hybrid()) {
+ struct x86_hybrid_pmu *h_pmu;
+
+ h_pmu = hybrid_pmu(event_pmu);
+ cpu = cpumask_first(&h_pmu->supported_cpus);
+ } else
+ cpu = raw_smp_processor_id();
+ cpuc->pmu = event_pmu;
+
if (intel_cpuc_prepare(cpuc, cpu))
goto error;

@@ -2247,7 +2297,7 @@ static int validate_event(struct perf_event *event)
struct event_constraint *c;
int ret = 0;

- fake_cpuc = allocate_fake_cpuc();
+ fake_cpuc = allocate_fake_cpuc(event->pmu);
if (IS_ERR(fake_cpuc))
return PTR_ERR(fake_cpuc);

@@ -2281,7 +2331,27 @@ static int validate_group(struct perf_event *event)
struct cpu_hw_events *fake_cpuc;
int ret = -EINVAL, n;

- fake_cpuc = allocate_fake_cpuc();
+ /*
+ * Reject events from different hybrid PMUs.
+ */
+ if (is_hybrid()) {
+ struct perf_event *sibling;
+ struct pmu *pmu = NULL;
+
+ if (is_x86_event(leader))
+ pmu = leader->pmu;
+
+ for_each_sibling_event(sibling, leader) {
+ if (!is_x86_event(sibling))
+ continue;
+ if (!pmu)
+ pmu = sibling->pmu;
+ else if (pmu != sibling->pmu)
+ return ret;
+ }
+ }
+
+ fake_cpuc = allocate_fake_cpuc(event->pmu);
if (IS_ERR(fake_cpuc))
return PTR_ERR(fake_cpuc);
/*
@@ -2309,16 +2379,18 @@ static int validate_group(struct perf_event *event)

static int x86_pmu_event_init(struct perf_event *event)
{
+ struct x86_hybrid_pmu *pmu = NULL;
int err;

- switch (event->attr.type) {
- case PERF_TYPE_RAW:
- case PERF_TYPE_HARDWARE:
- case PERF_TYPE_HW_CACHE:
- break;
-
- default:
+ if ((event->attr.type != event->pmu->type) &&
+ (event->attr.type != PERF_TYPE_HARDWARE) &&
+ (event->attr.type != PERF_TYPE_HW_CACHE))
return -ENOENT;
+
+ if (is_hybrid() && (event->cpu != -1)) {
+ pmu = hybrid_pmu(event->pmu);
+ if (!cpumask_test_cpu(event->cpu, &pmu->supported_cpus))
+ return -ENOENT;
}

err = __x86_pmu_event_init(event);
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index a257e39..6e8d4ac 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3717,7 +3717,8 @@ static int intel_pmu_hw_config(struct perf_event *event)
event->hw.flags |= PERF_X86_EVENT_PEBS_VIA_PT;
}

- if (event->attr.type != PERF_TYPE_RAW)
+ if ((event->attr.type == PERF_TYPE_HARDWARE) ||
+ (event->attr.type == PERF_TYPE_HW_CACHE))
return 0;

/*
@@ -4300,12 +4301,104 @@ static void intel_pmu_check_extra_regs(struct extra_reg *extra_regs)
}
}

+int x86_get_hybrid_pmu_type(u8 cpu_type)
+{
+ if (cpu_type == INTEL_HYBRID_TYPE_CORE)
+ return PERF_TYPE_RAW;
+ return -1;
+}
+
+static void init_hybrid_pmu(int cpu)
+{
+ unsigned int fixed_mask, unused_eax, unused_ebx, unused_edx;
+ struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
+ u8 cpu_type = get_hybrid_cpu_type(cpu);
+ struct x86_hybrid_pmu *pmu = NULL;
+ struct perf_cpu_context *cpuctx;
+ int i;
+
+ for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
+ if (x86_pmu.hybrid_pmu[i].cpu_type == cpu_type) {
+ pmu = &x86_pmu.hybrid_pmu[i];
+ break;
+ }
+ }
+ if (WARN_ON_ONCE(!pmu))
+ return;
+
+ cpuc->pmu = &pmu->pmu;
+
+ /* Only register PMU for the first CPU */
+ if (!cpumask_empty(&pmu->supported_cpus)) {
+ cpumask_set_cpu(cpu, &pmu->supported_cpus);
+ goto end;
+ }
+
+ if (!check_hw_exists(&pmu->pmu, pmu->num_counters, pmu->num_counters_fixed))
+ return;
+
+ if ((pmu->pmu.type == -1) &&
+ perf_pmu_register(&pmu->pmu, pmu->name, x86_get_hybrid_pmu_type(pmu->cpu_type)))
+ return;
+
+ /*
+ * Except for ECX, other fields have been stored in the x86 struct
+ * at boot time.
+ */
+ cpuid(10, &unused_eax, &unused_ebx, &fixed_mask, &unused_edx);
+
+ intel_pmu_check_num_counters(&pmu->num_counters,
+ &pmu->num_counters_fixed,
+ &pmu->intel_ctrl,
+ (u64)fixed_mask);
+
+ pr_info("%s PMU driver: ", pmu->name);
+
+ if (pmu->intel_cap.perf_metrics) {
+ pmu->intel_ctrl |= 1ULL << GLOBAL_CTRL_EN_PERF_METRICS;
+ pmu->intel_ctrl |= INTEL_PMC_MSK_FIXED_SLOTS;
+ }
+
+ if (pmu->intel_cap.pebs_output_pt_available) {
+ pmu->pmu.capabilities |= PERF_PMU_CAP_AUX_OUTPUT;
+ pr_cont("PEBS-via-PT ");
+ }
+
+ intel_pmu_check_event_constraints(pmu->event_constraints,
+ pmu->num_counters,
+ pmu->num_counters_fixed,
+ pmu->intel_ctrl);
+
+ intel_pmu_check_extra_regs(pmu->extra_regs);
+
+ pr_cont("\n");
+
+ x86_pmu_show_pmu_cap(pmu->num_counters, pmu->num_counters_fixed,
+ pmu->intel_ctrl);
+
+ cpumask_set_cpu(cpu, &pmu->supported_cpus);
+end:
+ /*
+ * The cpuctx of all CPUs are allocated when registering the
+ * boot CPU's PMU. At that time, the PMU for other hybrid CPUs
+ * is not registered yet. The boot CPU's PMU was
+ * unconditionally assigned to each cpuctx->ctx.pmu.
+ * Update the cpuctx->ctx.pmu when the PMU for other hybrid
+ * CPUs is known.
+ */
+ cpuctx = per_cpu_ptr(pmu->pmu.pmu_cpu_context, cpu);
+ cpuctx->ctx.pmu = &pmu->pmu;
+}
+
static void intel_pmu_cpu_starting(int cpu)
{
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
int core_id = topology_core_id(cpu);
int i;

+ if (is_hybrid())
+ init_hybrid_pmu(cpu);
+
init_debug_store_on_cpu(cpu);
/*
* Deal with CPUs that don't clear their LBRs on power-up.
@@ -4420,6 +4513,27 @@ void intel_cpuc_finish(struct cpu_hw_events *cpuc)
static void intel_pmu_cpu_dead(int cpu)
{
intel_cpuc_finish(&per_cpu(cpu_hw_events, cpu));
+
+ if (is_hybrid()) {
+ struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
+ struct x86_hybrid_pmu *hybrid_pmu = NULL;
+ int i;
+
+ for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
+ if (cpumask_test_and_clear_cpu(cpu, &x86_pmu.hybrid_pmu[i].supported_cpus)) {
+ hybrid_pmu = &x86_pmu.hybrid_pmu[i];
+ break;
+ }
+ }
+ if (WARN_ON_ONCE(!hybrid_pmu))
+ return;
+
+ cpuc->pmu = NULL;
+ if (cpumask_empty(&hybrid_pmu->supported_cpus)) {
+ perf_pmu_unregister(&hybrid_pmu->pmu);
+ hybrid_pmu->pmu.type = -1;
+ }
+ }
}

static void intel_pmu_sched_task(struct perf_event_context *ctx,
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 1d0de9e..334d991 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -15,6 +15,7 @@
#include <linux/perf_event.h>

#include <asm/intel_ds.h>
+#include <asm/cpu.h>

/* To enable MSR tracing please use the generic trace points. */

@@ -634,6 +635,9 @@ enum {

struct x86_hybrid_pmu {
struct pmu pmu;
+ const char *name;
+ u8 cpu_type;
+ cpumask_t supported_cpus;
union perf_capabilities intel_cap;
u64 intel_ctrl;
int max_pebs_events;
@@ -1331,6 +1335,8 @@ static inline int is_ht_workaround_enabled(void)
return !!(x86_pmu.flags & PMU_FL_EXCL_ENABLED);
}

+int x86_get_hybrid_pmu_type(u8 cpu_type);
+
#else /* CONFIG_CPU_SUP_INTEL */

static inline void reserve_ds_buffers(void)
@@ -1363,6 +1369,12 @@ static inline int is_ht_workaround_enabled(void)
{
return 0;
}
+
+static inline int x86_get_hybrid_pmu_type(u8 cpu_type)
+{
+ return -1;
+}
+
#endif /* CONFIG_CPU_SUP_INTEL */

#if ((defined CONFIG_CPU_SUP_CENTAUR) || (defined CONFIG_CPU_SUP_ZHAOXIN))
--
2.7.4

2021-03-10 16:46:58

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 19/25] perf/x86: Support filter_match callback

From: Kan Liang <[email protected]>

Implement filter_match callback for X86, which check whether an event is
schedulable on the current CPU.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/core.c | 10 ++++++++++
arch/x86/events/perf_event.h | 1 +
2 files changed, 11 insertions(+)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 43f5529..b675283 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -2625,6 +2625,14 @@ static int x86_pmu_aux_output_match(struct perf_event *event)
return 0;
}

+static int x86_pmu_filter_match(struct perf_event *event)
+{
+ if (x86_pmu.filter_match)
+ return x86_pmu.filter_match(event);
+
+ return 1;
+}
+
static struct pmu pmu = {
.pmu_enable = x86_pmu_enable,
.pmu_disable = x86_pmu_disable,
@@ -2652,6 +2660,8 @@ static struct pmu pmu = {
.check_period = x86_pmu_check_period,

.aux_output_match = x86_pmu_aux_output_match,
+
+ .filter_match = x86_pmu_filter_match,
};

void arch_perf_update_userpage(struct perf_event *event,
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 36a08709..74bdcfe 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -865,6 +865,7 @@ struct x86_pmu {

int (*aux_output_match) (struct perf_event *event);

+ int (*filter_match)(struct perf_event *event);
/*
* Hybrid support
*
--
2.7.4

2021-03-10 16:47:15

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support

From: Kan Liang <[email protected]>

Alder Lake Hybrid system has two different types of core, Golden Cove
core and Gracemont core. The Golden Cove core is registered to
"cpu_core" PMU. The Gracemont core is registered to "cpu_atom" PMU.

The difference between the two PMUs include:
- Number of GP and fixed counters
- Events
- The "cpu_core" PMU supports Topdown metrics.
The "cpu_atom" PMU supports PEBS-via-PT.

The "cpu_core" PMU is similar to the Sapphire Rapids PMU, but without
PMEM.
The "cpu_atom" PMU is similar to Tremont, but with different
event_constraints, extra_regs and number of counters.

The mem-loads AUX event workaround only applies to the Golden Cove core.

Users may disable all CPUs of the same CPU type on the command line or
in the BIOS. For this case, perf still initializes a PMU for the CPU
type, but will not register the PMU. The PMU will only be registered
when any corresponding CPU is online.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/intel/core.c | 248 ++++++++++++++++++++++++++++++++++++++++++-
arch/x86/events/intel/ds.c | 7 ++
arch/x86/events/perf_event.h | 7 ++
3 files changed, 261 insertions(+), 1 deletion(-)

diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 8ac948a..568e100 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -2076,6 +2076,14 @@ static struct extra_reg intel_tnt_extra_regs[] __read_mostly = {
EVENT_EXTRA_END
};

+static struct extra_reg intel_grt_extra_regs[] __read_mostly = {
+ /* must define OFFCORE_RSP_X first, see intel_fixup_er() */
+ INTEL_UEVENT_EXTRA_REG(0x01b7, MSR_OFFCORE_RSP_0, 0x3fffffffffull, RSP_0),
+ INTEL_UEVENT_EXTRA_REG(0x02b7, MSR_OFFCORE_RSP_1, 0x3fffffffffull, RSP_1),
+ INTEL_UEVENT_PEBS_LDLAT_EXTRA_REG(0x5d0),
+ EVENT_EXTRA_END
+};
+
#define KNL_OT_L2_HITE BIT_ULL(19) /* Other Tile L2 Hit */
#define KNL_OT_L2_HITF BIT_ULL(20) /* Other Tile L2 Hit */
#define KNL_MCDRAM_LOCAL BIT_ULL(21)
@@ -2430,6 +2438,16 @@ static int icl_set_topdown_event_period(struct perf_event *event)
return 0;
}

+static int adl_set_topdown_event_period(struct perf_event *event)
+{
+ struct x86_hybrid_pmu *pmu = hybrid_pmu(event->pmu);
+
+ if (pmu->cpu_type != INTEL_HYBRID_TYPE_CORE)
+ return 0;
+
+ return icl_set_topdown_event_period(event);
+}
+
static inline u64 icl_get_metrics_event_value(u64 metric, u64 slots, int idx)
{
u32 val;
@@ -2570,6 +2588,17 @@ static u64 icl_update_topdown_event(struct perf_event *event)
x86_pmu.num_topdown_events - 1);
}

+static u64 adl_update_topdown_event(struct perf_event *event)
+{
+ struct x86_hybrid_pmu *pmu = hybrid_pmu(event->pmu);
+
+ if (pmu->cpu_type != INTEL_HYBRID_TYPE_CORE)
+ return 0;
+
+ return icl_update_topdown_event(event);
+}
+
+
static void intel_pmu_read_topdown_event(struct perf_event *event)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
@@ -3658,6 +3687,17 @@ static inline bool is_mem_loads_aux_event(struct perf_event *event)
return (event->attr.config & INTEL_ARCH_EVENT_MASK) == X86_CONFIG(.event=0x03, .umask=0x82);
}

+static inline bool require_mem_loads_aux_event(struct perf_event *event)
+{
+ if (!(x86_pmu.flags & PMU_FL_MEM_LOADS_AUX))
+ return false;
+
+ if (is_hybrid())
+ return hybrid_pmu(event->pmu)->cpu_type == INTEL_HYBRID_TYPE_CORE;
+
+ return true;
+}
+
static inline bool intel_pmu_has_cap(struct perf_event *event, int idx)
{
union perf_capabilities *intel_cap;
@@ -3782,7 +3822,7 @@ static int intel_pmu_hw_config(struct perf_event *event)
* event. The rule is to simplify the implementation of the check.
* That's because perf cannot have a complete group at the moment.
*/
- if (x86_pmu.flags & PMU_FL_MEM_LOADS_AUX &&
+ if (require_mem_loads_aux_event(event) &&
(event->attr.sample_type & PERF_SAMPLE_DATA_SRC) &&
is_mem_loads_event(event)) {
struct perf_event *leader = event->group_leader;
@@ -4059,6 +4099,34 @@ tfa_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
return c;
}

+static struct event_constraint *
+adl_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
+ struct perf_event *event)
+{
+ struct x86_hybrid_pmu *pmu = hybrid_pmu(event->pmu);
+
+ if (pmu->cpu_type == INTEL_HYBRID_TYPE_CORE)
+ return spr_get_event_constraints(cpuc, idx, event);
+ else if (pmu->cpu_type == INTEL_HYBRID_TYPE_ATOM)
+ return tnt_get_event_constraints(cpuc, idx, event);
+
+ WARN_ON(1);
+ return &emptyconstraint;
+}
+
+static int adl_hw_config(struct perf_event *event)
+{
+ struct x86_hybrid_pmu *pmu = hybrid_pmu(event->pmu);
+
+ if (pmu->cpu_type == INTEL_HYBRID_TYPE_CORE)
+ return hsw_hw_config(event);
+ else if (pmu->cpu_type == INTEL_HYBRID_TYPE_ATOM)
+ return intel_pmu_hw_config(event);
+
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+}
+
/*
* Broadwell:
*
@@ -4564,6 +4632,14 @@ static int intel_pmu_aux_output_match(struct perf_event *event)
return is_intel_pt_event(event);
}

+static int intel_pmu_filter_match(struct perf_event *event)
+{
+ struct x86_hybrid_pmu *pmu = hybrid_pmu(event->pmu);
+ unsigned int cpu = smp_processor_id();
+
+ return cpumask_test_cpu(cpu, &pmu->supported_cpus);
+}
+
PMU_FORMAT_ATTR(offcore_rsp, "config1:0-63");

PMU_FORMAT_ATTR(ldlat, "config1:0-15");
@@ -5266,6 +5342,84 @@ static const struct attribute_group *attr_update[] = {
NULL,
};

+EVENT_ATTR_STR_HYBRID(slots, slots_hybrid, "event=0x00,umask=0x4", INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(topdown-retiring, td_retiring_hybrid, "event=0xc2,umask=0x0;event=0x00,umask=0x80", INTEL_HYBRID_TYPE_ATOM | INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(topdown-bad-spec, td_bad_spec_hybrid, "event=0x73,umask=0x0;event=0x00,umask=0x81", INTEL_HYBRID_TYPE_ATOM | INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(topdown-fe-bound, td_fe_bound_hybrid, "event=0x71,umask=0x0;event=0x00,umask=0x82", INTEL_HYBRID_TYPE_ATOM | INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(topdown-be-bound, td_be_bound_hybrid, "event=0x74,umask=0x0;event=0x00,umask=0x83", INTEL_HYBRID_TYPE_ATOM | INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(topdown-heavy-ops, td_heavy_ops_hybrid, "event=0x00,umask=0x84", INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(topdown-br-mispredict, td_br_mispredict_hybrid, "event=0x00,umask=0x85", INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(topdown-fetch-lat, td_fetch_lat_hybrid, "event=0x00,umask=0x86", INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(topdown-mem-bound, td_mem_bound_hybrid, "event=0x00,umask=0x87", INTEL_HYBRID_TYPE_CORE);
+
+static struct attribute *adl_hybrid_events_attrs[] = {
+ EVENT_PTR(slots_hybrid),
+ EVENT_PTR(td_retiring_hybrid),
+ EVENT_PTR(td_bad_spec_hybrid),
+ EVENT_PTR(td_fe_bound_hybrid),
+ EVENT_PTR(td_be_bound_hybrid),
+ EVENT_PTR(td_heavy_ops_hybrid),
+ EVENT_PTR(td_br_mispredict_hybrid),
+ EVENT_PTR(td_fetch_lat_hybrid),
+ EVENT_PTR(td_mem_bound_hybrid),
+ NULL,
+};
+
+/* Must be in IDX order */
+EVENT_ATTR_STR_HYBRID(mem-loads, mem_ld_adl_hybrid, "event=0xd0,umask=0x5,ldlat=3;event=0xcd,umask=0x1,ldlat=3", INTEL_HYBRID_TYPE_ATOM | INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(mem-stores, mem_st_adl_hybrid, "event=0xd0,umask=0x6;event=0xcd,umask=0x2", INTEL_HYBRID_TYPE_ATOM | INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(mem-loads-aux, mem_ld_aux_hybrid, "event=0x03,umask=0x82", INTEL_HYBRID_TYPE_CORE);
+
+static struct attribute *adl_hybrid_mem_attrs[] = {
+ EVENT_PTR(mem_ld_adl_hybrid),
+ EVENT_PTR(mem_st_adl_hybrid),
+ EVENT_PTR(mem_ld_aux_hybrid),
+ NULL,
+};
+
+EVENT_ATTR_STR_HYBRID(tx-start, tx_start_adl_hybrid, "event=0xc9,umask=0x1", INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(tx-commit, tx_commit_adl_hybrid, "event=0xc9,umask=0x2", INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(tx-abort, tx_abort_adl_hybrid, "event=0xc9,umask=0x4", INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(tx-conflict, tx_conflict_adl_hybrid, "event=0x54,umask=0x1", INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(cycles-t, cycles_t_adl_hybrid, "event=0x3c,in_tx=1", INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(cycles-ct, cycles_ct_adl_hybrid, "event=0x3c,in_tx=1,in_tx_cp=1", INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(tx-capacity-read, tx_capacity_read_adl_hybrid, "event=0x54,umask=0x80", INTEL_HYBRID_TYPE_CORE);
+EVENT_ATTR_STR_HYBRID(tx-capacity-write, tx_capacity_write_adl_hybrid, "event=0x54,umask=0x2", INTEL_HYBRID_TYPE_CORE);
+
+static struct attribute *adl_hybrid_tsx_attrs[] = {
+ EVENT_PTR(tx_start_adl_hybrid),
+ EVENT_PTR(tx_abort_adl_hybrid),
+ EVENT_PTR(tx_commit_adl_hybrid),
+ EVENT_PTR(tx_capacity_read_adl_hybrid),
+ EVENT_PTR(tx_capacity_write_adl_hybrid),
+ EVENT_PTR(tx_conflict_adl_hybrid),
+ EVENT_PTR(cycles_t_adl_hybrid),
+ EVENT_PTR(cycles_ct_adl_hybrid),
+ NULL,
+};
+
+FORMAT_ATTR_HYBRID(in_tx, INTEL_HYBRID_TYPE_CORE);
+FORMAT_ATTR_HYBRID(in_tx_cp, INTEL_HYBRID_TYPE_CORE);
+FORMAT_ATTR_HYBRID(offcore_rsp, INTEL_HYBRID_TYPE_CORE | INTEL_HYBRID_TYPE_ATOM);
+FORMAT_ATTR_HYBRID(ldlat, INTEL_HYBRID_TYPE_CORE | INTEL_HYBRID_TYPE_ATOM);
+FORMAT_ATTR_HYBRID(frontend, INTEL_HYBRID_TYPE_CORE);
+
+static struct attribute *adl_hybrid_extra_attr_rtm[] = {
+ FORMAT_HYBRID_PTR(in_tx),
+ FORMAT_HYBRID_PTR(in_tx_cp),
+ FORMAT_HYBRID_PTR(offcore_rsp),
+ FORMAT_HYBRID_PTR(ldlat),
+ FORMAT_HYBRID_PTR(frontend),
+ NULL,
+};
+
+static struct attribute *adl_hybrid_extra_attr[] = {
+ FORMAT_HYBRID_PTR(offcore_rsp),
+ FORMAT_HYBRID_PTR(ldlat),
+ FORMAT_HYBRID_PTR(frontend),
+ NULL,
+};
+
static bool is_attr_for_this_pmu(struct kobject *kobj, struct attribute *attr)
{
struct device *dev = kobj_to_dev(kobj);
@@ -5382,6 +5536,7 @@ __init int intel_pmu_init(void)
bool pmem = false;
int version, i;
char *name;
+ struct x86_hybrid_pmu *pmu;

if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) {
switch (boot_cpu_data.x86) {
@@ -5976,6 +6131,97 @@ __init int intel_pmu_init(void)
name = "sapphire_rapids";
break;

+ case INTEL_FAM6_ALDERLAKE:
+ case INTEL_FAM6_ALDERLAKE_L:
+ /*
+ * Alder Lake has 2 types of CPU, core and atom.
+ *
+ * Initialize the common PerfMon capabilities here.
+ */
+ x86_pmu.hybrid_pmu = kcalloc(X86_HYBRID_NUM_PMUS,
+ sizeof(struct x86_hybrid_pmu),
+ GFP_KERNEL);
+ if (!x86_pmu.hybrid_pmu)
+ return -ENOMEM;
+ x86_pmu.num_hybrid_pmus = X86_HYBRID_NUM_PMUS;
+
+ x86_pmu.late_ack = true;
+ x86_pmu.pebs_aliases = NULL;
+ x86_pmu.pebs_prec_dist = true;
+ x86_pmu.pebs_block = true;
+ x86_pmu.flags |= PMU_FL_HAS_RSP_1;
+ x86_pmu.flags |= PMU_FL_NO_HT_SHARING;
+ x86_pmu.flags |= PMU_FL_PEBS_ALL;
+ x86_pmu.flags |= PMU_FL_INSTR_LATENCY;
+ x86_pmu.flags |= PMU_FL_MEM_LOADS_AUX;
+ x86_pmu.lbr_pt_coexist = true;
+ intel_pmu_pebs_data_source_skl(false);
+ x86_pmu.num_topdown_events = 8;
+ x86_pmu.update_topdown_event = adl_update_topdown_event;
+ x86_pmu.set_topdown_event_period = adl_set_topdown_event_period;
+
+ x86_pmu.filter_match = intel_pmu_filter_match;
+ x86_pmu.get_event_constraints = adl_get_event_constraints;
+ x86_pmu.hw_config = adl_hw_config;
+ x86_pmu.limit_period = spr_limit_period;
+ /*
+ * The rtm_abort_event is used to check whether to enable GPRs
+ * for the RTM abort event. Atom doesn't have the RTM abort
+ * event. There is no harmful to set it in the common
+ * x86_pmu.rtm_abort_event.
+ */
+ x86_pmu.rtm_abort_event = X86_CONFIG(.event=0xc9, .umask=0x04);
+
+ td_attr = adl_hybrid_events_attrs;
+ mem_attr = adl_hybrid_mem_attrs;
+ tsx_attr = adl_hybrid_tsx_attrs;
+ extra_attr = boot_cpu_has(X86_FEATURE_RTM) ?
+ adl_hybrid_extra_attr_rtm : adl_hybrid_extra_attr;
+
+ /* Initialize big core specific PerfMon capabilities.*/
+ pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];
+ pmu->name = "cpu_core";
+ pmu->cpu_type = INTEL_HYBRID_TYPE_CORE;
+ pmu->num_counters = x86_pmu.num_counters + 2;
+ pmu->num_counters_fixed = x86_pmu.num_counters_fixed + 1;
+ pmu->max_pebs_events = min_t(unsigned, MAX_PEBS_EVENTS, pmu->num_counters);
+ pmu->unconstrained = (struct event_constraint)
+ __EVENT_CONSTRAINT(0, (1ULL << pmu->num_counters) - 1,
+ 0, pmu->num_counters, 0, 0);
+ pmu->intel_cap.capabilities = x86_pmu.intel_cap.capabilities;
+ pmu->intel_cap.perf_metrics = 1;
+ pmu->intel_cap.pebs_output_pt_available = 0;
+
+ memcpy(pmu->hw_cache_event_ids, spr_hw_cache_event_ids, sizeof(pmu->hw_cache_event_ids));
+ memcpy(pmu->hw_cache_extra_regs, spr_hw_cache_extra_regs, sizeof(pmu->hw_cache_extra_regs));
+ pmu->event_constraints = intel_spr_event_constraints;
+ pmu->pebs_constraints = intel_spr_pebs_event_constraints;
+ pmu->extra_regs = intel_spr_extra_regs;
+
+ /* Initialize Atom core specific PerfMon capabilities.*/
+ pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];
+ pmu->name = "cpu_atom";
+ pmu->cpu_type = INTEL_HYBRID_TYPE_ATOM;
+ pmu->num_counters = x86_pmu.num_counters;
+ pmu->num_counters_fixed = x86_pmu.num_counters_fixed;
+ pmu->max_pebs_events = x86_pmu.max_pebs_events;
+ pmu->unconstrained = (struct event_constraint)
+ __EVENT_CONSTRAINT(0, (1ULL << pmu->num_counters) - 1,
+ 0, pmu->num_counters, 0, 0);
+ pmu->intel_cap.capabilities = x86_pmu.intel_cap.capabilities;
+ pmu->intel_cap.perf_metrics = 0;
+ pmu->intel_cap.pebs_output_pt_available = 1;
+
+ memcpy(pmu->hw_cache_event_ids, glp_hw_cache_event_ids, sizeof(pmu->hw_cache_event_ids));
+ memcpy(pmu->hw_cache_extra_regs, tnt_hw_cache_extra_regs, sizeof(pmu->hw_cache_extra_regs));
+ pmu->hw_cache_event_ids[C(ITLB)][C(OP_READ)][C(RESULT_ACCESS)] = -1;
+ pmu->event_constraints = intel_slm_event_constraints;
+ pmu->pebs_constraints = intel_grt_pebs_event_constraints;
+ pmu->extra_regs = intel_grt_extra_regs;
+ pr_cont("Alderlake Hybrid events, ");
+ name = "alderlake_hybrid";
+ break;
+
default:
switch (x86_pmu.version) {
case 1:
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index f1402bc..2780cb5 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -779,6 +779,13 @@ struct event_constraint intel_glm_pebs_event_constraints[] = {
EVENT_CONSTRAINT_END
};

+struct event_constraint intel_grt_pebs_event_constraints[] = {
+ /* Allow all events as PEBS with no flags */
+ INTEL_PLD_CONSTRAINT(0x5d0, 0xf),
+ INTEL_PSD_CONSTRAINT(0x6d0, 0xf),
+ EVENT_CONSTRAINT_END
+};
+
struct event_constraint intel_nehalem_pebs_event_constraints[] = {
INTEL_PLD_CONSTRAINT(0x100b, 0xf), /* MEM_INST_RETIRED.* */
INTEL_FLAGS_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 74bdcfe..0c28f61 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -678,6 +678,11 @@ static __always_inline struct x86_hybrid_pmu *hybrid_pmu(struct pmu *pmu)
__F; \
})

+#define X86_HYBRID_PMU_ATOM_IDX 0
+#define X86_HYBRID_PMU_CORE_IDX 1
+
+#define X86_HYBRID_NUM_PMUS 2
+
/*
* struct x86_pmu - generic x86 pmu
*/
@@ -1241,6 +1246,8 @@ extern struct event_constraint intel_glm_pebs_event_constraints[];

extern struct event_constraint intel_glp_pebs_event_constraints[];

+extern struct event_constraint intel_grt_pebs_event_constraints[];
+
extern struct event_constraint intel_nehalem_pebs_event_constraints[];

extern struct event_constraint intel_westmere_pebs_event_constraints[];
--
2.7.4

2021-03-10 16:47:27

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 23/25] perf/x86/msr: Add Alder Lake CPU support

From: Kan Liang <[email protected]>

PPERF and SMI_COUNT MSRs are also supported on Alder Lake.

The External Design Specification (EDS) is not published yet. It comes
from an authoritative internal source.

The patch has been tested on real hardware.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/msr.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/arch/x86/events/msr.c b/arch/x86/events/msr.c
index 680404c..c853b28 100644
--- a/arch/x86/events/msr.c
+++ b/arch/x86/events/msr.c
@@ -100,6 +100,8 @@ static bool test_intel(int idx, void *data)
case INTEL_FAM6_TIGERLAKE_L:
case INTEL_FAM6_TIGERLAKE:
case INTEL_FAM6_ROCKETLAKE:
+ case INTEL_FAM6_ALDERLAKE:
+ case INTEL_FAM6_ALDERLAKE_L:
if (idx == PERF_MSR_SMI || idx == PERF_MSR_PPERF)
return true;
break;
--
2.7.4

2021-03-10 16:47:30

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 24/25] perf/x86/cstate: Add Alder Lake CPU support

From: Kan Liang <[email protected]>

Compared with the Rocket Lake, the CORE C1 Residency Counter is added
for Alder Lake, but the CORE C3 Residency Counter is removed. Other
counters are the same.

Create a new adl_cstates for Alder Lake. Update the comments
accordingly.

The External Design Specification (EDS) is not published yet. It comes
from an authoritative internal source.

The patch has been tested on real hardware.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/intel/cstate.c | 39 +++++++++++++++++++++++++++++----------
1 file changed, 29 insertions(+), 10 deletions(-)

diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c
index 407eee5..4333990 100644
--- a/arch/x86/events/intel/cstate.c
+++ b/arch/x86/events/intel/cstate.c
@@ -40,7 +40,7 @@
* Model specific counters:
* MSR_CORE_C1_RES: CORE C1 Residency Counter
* perf code: 0x00
- * Available model: SLM,AMT,GLM,CNL,TNT
+ * Available model: SLM,AMT,GLM,CNL,TNT,ADL
* Scope: Core (each processor core has a MSR)
* MSR_CORE_C3_RESIDENCY: CORE C3 Residency Counter
* perf code: 0x01
@@ -51,46 +51,49 @@
* perf code: 0x02
* Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW,
* SKL,KNL,GLM,CNL,KBL,CML,ICL,TGL,
- * TNT,RKL
+ * TNT,RKL,ADL
* Scope: Core
* MSR_CORE_C7_RESIDENCY: CORE C7 Residency Counter
* perf code: 0x03
* Available model: SNB,IVB,HSW,BDW,SKL,CNL,KBL,CML,
- * ICL,TGL,RKL
+ * ICL,TGL,RKL,ADL
* Scope: Core
* MSR_PKG_C2_RESIDENCY: Package C2 Residency Counter.
* perf code: 0x00
* Available model: SNB,IVB,HSW,BDW,SKL,KNL,GLM,CNL,
- * KBL,CML,ICL,TGL,TNT,RKL
+ * KBL,CML,ICL,TGL,TNT,RKL,ADL
* Scope: Package (physical package)
* MSR_PKG_C3_RESIDENCY: Package C3 Residency Counter.
* perf code: 0x01
* Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL,KNL,
- * GLM,CNL,KBL,CML,ICL,TGL,TNT,RKL
+ * GLM,CNL,KBL,CML,ICL,TGL,TNT,RKL,
+ * ADL
* Scope: Package (physical package)
* MSR_PKG_C6_RESIDENCY: Package C6 Residency Counter.
* perf code: 0x02
* Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW,
* SKL,KNL,GLM,CNL,KBL,CML,ICL,TGL,
- * TNT,RKL
+ * TNT,RKL,ADL
* Scope: Package (physical package)
* MSR_PKG_C7_RESIDENCY: Package C7 Residency Counter.
* perf code: 0x03
* Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL,CNL,
- * KBL,CML,ICL,TGL,RKL
+ * KBL,CML,ICL,TGL,RKL,ADL
* Scope: Package (physical package)
* MSR_PKG_C8_RESIDENCY: Package C8 Residency Counter.
* perf code: 0x04
- * Available model: HSW ULT,KBL,CNL,CML,ICL,TGL,RKL
+ * Available model: HSW ULT,KBL,CNL,CML,ICL,TGL,RKL,
+ * ADL
* Scope: Package (physical package)
* MSR_PKG_C9_RESIDENCY: Package C9 Residency Counter.
* perf code: 0x05
- * Available model: HSW ULT,KBL,CNL,CML,ICL,TGL,RKL
+ * Available model: HSW ULT,KBL,CNL,CML,ICL,TGL,RKL,
+ * ADL
* Scope: Package (physical package)
* MSR_PKG_C10_RESIDENCY: Package C10 Residency Counter.
* perf code: 0x06
* Available model: HSW ULT,KBL,GLM,CNL,CML,ICL,TGL,
- * TNT,RKL
+ * TNT,RKL,ADL
* Scope: Package (physical package)
*
*/
@@ -563,6 +566,20 @@ static const struct cstate_model icl_cstates __initconst = {
BIT(PERF_CSTATE_PKG_C10_RES),
};

+static const struct cstate_model adl_cstates __initconst = {
+ .core_events = BIT(PERF_CSTATE_CORE_C1_RES) |
+ BIT(PERF_CSTATE_CORE_C6_RES) |
+ BIT(PERF_CSTATE_CORE_C7_RES),
+
+ .pkg_events = BIT(PERF_CSTATE_PKG_C2_RES) |
+ BIT(PERF_CSTATE_PKG_C3_RES) |
+ BIT(PERF_CSTATE_PKG_C6_RES) |
+ BIT(PERF_CSTATE_PKG_C7_RES) |
+ BIT(PERF_CSTATE_PKG_C8_RES) |
+ BIT(PERF_CSTATE_PKG_C9_RES) |
+ BIT(PERF_CSTATE_PKG_C10_RES),
+};
+
static const struct cstate_model slm_cstates __initconst = {
.core_events = BIT(PERF_CSTATE_CORE_C1_RES) |
BIT(PERF_CSTATE_CORE_C6_RES),
@@ -650,6 +667,8 @@ static const struct x86_cpu_id intel_cstates_match[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, &icl_cstates),
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, &icl_cstates),
X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE, &icl_cstates),
+ X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, &adl_cstates),
+ X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, &adl_cstates),
{ },
};
MODULE_DEVICE_TABLE(x86cpu, intel_cstates_match);
--
2.7.4

2021-03-10 16:48:00

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 07/25] perf/x86: Hybrid PMU support for unconstrained

From: Kan Liang <[email protected]>

The unconstrained value depends on the number of GP and fixed counters.
Each hybrid PMU should use its own unconstrained.

Suggested-by: Peter Zijlstra (Intel) <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/intel/core.c | 5 ++++-
arch/x86/events/perf_event.h | 1 +
2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index e187cf5..540dba2 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3147,7 +3147,10 @@ x86_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
}
}

- return &unconstrained;
+ if (!is_hybrid() || !cpuc->pmu)
+ return &unconstrained;
+
+ return &hybrid_pmu(cpuc->pmu)->unconstrained;
}

static struct event_constraint *
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index d2f97bd..7bce193 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -639,6 +639,7 @@ struct x86_hybrid_pmu {
int max_pebs_events;
int num_counters;
int num_counters_fixed;
+ struct event_constraint unconstrained;
};

static __always_inline bool is_hybrid(void)
--
2.7.4

2021-03-10 16:48:11

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 15/25] perf/x86: Factor out x86_pmu_show_pmu_cap

From: Kan Liang <[email protected]>

The PMU capabilities are different among hybrid PMUs. Perf should dump
the PMU capabilities information for each hybrid PMU.

Factor out x86_pmu_show_pmu_cap() which shows the PMU capabilities
information. The function will be reused later when registering a
dedicated hybrid PMU.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/core.c | 25 ++++++++++++++++---------
arch/x86/events/perf_event.h | 3 +++
2 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 96b4a72..15d9325 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -1987,6 +1987,20 @@ perf_guest_get_msrs_nop(int *nr)
return NULL;
}

+void x86_pmu_show_pmu_cap(int num_counters, int num_counters_fixed,
+ u64 intel_ctrl)
+{
+ pr_info("... version: %d\n", x86_pmu.version);
+ pr_info("... bit width: %d\n", x86_pmu.cntval_bits);
+ pr_info("... generic registers: %d\n", num_counters);
+ pr_info("... value mask: %016Lx\n", x86_pmu.cntval_mask);
+ pr_info("... max period: %016Lx\n", x86_pmu.max_period);
+ pr_info("... fixed-purpose events: %lu\n",
+ hweight64((((1ULL << num_counters_fixed) - 1)
+ << INTEL_PMC_IDX_FIXED) & intel_ctrl));
+ pr_info("... event mask: %016Lx\n", intel_ctrl);
+}
+
static int __init init_hw_perf_events(void)
{
struct x86_pmu_quirk *quirk;
@@ -2047,15 +2061,8 @@ static int __init init_hw_perf_events(void)

pmu.attr_update = x86_pmu.attr_update;

- pr_info("... version: %d\n", x86_pmu.version);
- pr_info("... bit width: %d\n", x86_pmu.cntval_bits);
- pr_info("... generic registers: %d\n", x86_pmu.num_counters);
- pr_info("... value mask: %016Lx\n", x86_pmu.cntval_mask);
- pr_info("... max period: %016Lx\n", x86_pmu.max_period);
- pr_info("... fixed-purpose events: %lu\n",
- hweight64((((1ULL << x86_pmu.num_counters_fixed) - 1)
- << INTEL_PMC_IDX_FIXED) & x86_pmu.intel_ctrl));
- pr_info("... event mask: %016Lx\n", x86_pmu.intel_ctrl);
+ x86_pmu_show_pmu_cap(x86_pmu.num_counters, x86_pmu.num_counters_fixed,
+ x86_pmu.intel_ctrl);

if (!x86_pmu.read)
x86_pmu.read = _x86_pmu_read;
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index e65a477..3ec5914 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -1085,6 +1085,9 @@ void x86_pmu_enable_event(struct perf_event *event);

int x86_pmu_handle_irq(struct pt_regs *regs);

+void x86_pmu_show_pmu_cap(int num_counters, int num_counters_fixed,
+ u64 intel_ctrl);
+
extern struct event_constraint emptyconstraint;

extern struct event_constraint unconstrained;
--
2.7.4

2021-03-10 16:48:52

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 18/25] perf/x86/intel: Add attr_update for Hybrid PMUs

From: Kan Liang <[email protected]>

The attribute_group for Hybrid PMUs should be different from the previous
cpu PMU. For example, cpumask is required for a Hybrid PMU. The PMU type
should be included in the event and format attribute.

Add hybrid_attr_update for the Hybrid PMU.
Check the PMU type in is_visible() function. Only display the event or
format for the matched Hybrid PMU.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/intel/core.c | 120 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 114 insertions(+), 6 deletions(-)

diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 0026a54..8ac948a 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -5266,6 +5266,106 @@ static const struct attribute_group *attr_update[] = {
NULL,
};

+static bool is_attr_for_this_pmu(struct kobject *kobj, struct attribute *attr)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct x86_hybrid_pmu *pmu =
+ container_of(dev_get_drvdata(dev), struct x86_hybrid_pmu, pmu);
+ struct perf_pmu_events_hybrid_attr *pmu_attr =
+ container_of(attr, struct perf_pmu_events_hybrid_attr, attr.attr);
+
+ return pmu->cpu_type & pmu_attr->pmu_type;
+}
+
+static umode_t hybrid_events_is_visible(struct kobject *kobj,
+ struct attribute *attr, int i)
+{
+ return is_attr_for_this_pmu(kobj, attr) ? attr->mode : 0;
+}
+
+static inline int hybrid_find_supported_cpu(struct x86_hybrid_pmu *pmu)
+{
+ int cpu = cpumask_first(&pmu->supported_cpus);
+
+ return (cpu >= nr_cpu_ids) ? -1 : cpu;
+}
+
+static umode_t hybrid_tsx_is_visible(struct kobject *kobj,
+ struct attribute *attr, int i)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct x86_hybrid_pmu *pmu =
+ container_of(dev_get_drvdata(dev), struct x86_hybrid_pmu, pmu);
+ int cpu = hybrid_find_supported_cpu(pmu);
+
+ return (cpu >= 0) && is_attr_for_this_pmu(kobj, attr) && cpu_has(&cpu_data(cpu), X86_FEATURE_RTM) ? attr->mode : 0;
+}
+
+static umode_t hybrid_format_is_visible(struct kobject *kobj,
+ struct attribute *attr, int i)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct x86_hybrid_pmu *pmu =
+ container_of(dev_get_drvdata(dev), struct x86_hybrid_pmu, pmu);
+ struct perf_pmu_format_hybrid_attr *pmu_attr =
+ container_of(attr, struct perf_pmu_format_hybrid_attr, attr.attr);
+ int cpu = hybrid_find_supported_cpu(pmu);
+
+ return (cpu >= 0) && (pmu->cpu_type & pmu_attr->pmu_type) ? attr->mode : 0;
+}
+
+static struct attribute_group hybrid_group_events_td = {
+ .name = "events",
+ .is_visible = hybrid_events_is_visible,
+};
+
+static struct attribute_group hybrid_group_events_mem = {
+ .name = "events",
+ .is_visible = hybrid_events_is_visible,
+};
+
+static struct attribute_group hybrid_group_events_tsx = {
+ .name = "events",
+ .is_visible = hybrid_tsx_is_visible,
+};
+
+static struct attribute_group hybrid_group_format_extra = {
+ .name = "format",
+ .is_visible = hybrid_format_is_visible,
+};
+
+static ssize_t intel_hybrid_get_attr_cpus(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct x86_hybrid_pmu *pmu =
+ container_of(dev_get_drvdata(dev), struct x86_hybrid_pmu, pmu);
+
+ return cpumap_print_to_pagebuf(true, buf, &pmu->supported_cpus);
+}
+
+static DEVICE_ATTR(cpus, S_IRUGO, intel_hybrid_get_attr_cpus, NULL);
+static struct attribute *intel_hybrid_cpus_attrs[] = {
+ &dev_attr_cpus.attr,
+ NULL,
+};
+
+static struct attribute_group hybrid_group_cpus = {
+ .attrs = intel_hybrid_cpus_attrs,
+};
+
+static const struct attribute_group *hybrid_attr_update[] = {
+ &hybrid_group_events_td,
+ &hybrid_group_events_mem,
+ &hybrid_group_events_tsx,
+ &group_caps_gen,
+ &group_caps_lbr,
+ &hybrid_group_format_extra,
+ &group_default,
+ &hybrid_group_cpus,
+ NULL,
+};
+
static struct attribute *empty_attrs;

__init int intel_pmu_init(void)
@@ -5896,14 +5996,22 @@ __init int intel_pmu_init(void)

snprintf(pmu_name_str, sizeof(pmu_name_str), "%s", name);

+ if (!is_hybrid()) {
+ group_events_td.attrs = td_attr;
+ group_events_mem.attrs = mem_attr;
+ group_events_tsx.attrs = tsx_attr;
+ group_format_extra.attrs = extra_attr;
+ group_format_extra_skl.attrs = extra_skl_attr;

- group_events_td.attrs = td_attr;
- group_events_mem.attrs = mem_attr;
- group_events_tsx.attrs = tsx_attr;
- group_format_extra.attrs = extra_attr;
- group_format_extra_skl.attrs = extra_skl_attr;
+ x86_pmu.attr_update = attr_update;
+ } else {
+ hybrid_group_events_td.attrs = td_attr;
+ hybrid_group_events_mem.attrs = mem_attr;
+ hybrid_group_events_tsx.attrs = tsx_attr;
+ hybrid_group_format_extra.attrs = extra_attr;

- x86_pmu.attr_update = attr_update;
+ x86_pmu.attr_update = hybrid_attr_update;
+ }

intel_pmu_check_num_counters(&x86_pmu.num_counters,
&x86_pmu.num_counters_fixed,
--
2.7.4

2021-03-10 16:48:52

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 17/25] perf/x86: Add structures for the attributes of Hybrid PMUs

From: Kan Liang <[email protected]>

Hybrid PMUs have different events and formats. In theory, Hybrid PMU
specific attributes should be maintained in the dedicated struct
x86_hybrid_pmu, but it wastes space because the events and formats are
similar among Hybrid PMUs.

To reduce duplication, all hybrid PMUs will share a group of attributes
in the following patch. To distinguish an attribute from different
Hybrid PMUs, a PMU aware attribute structure is introduced. A PMU type
is required for the attribute structure. The type is internal usage. It
is not visible in the sysfs API.

Hybrid PMUs may support the same event name, but with different event
encoding, e.g., the mem-loads event on an Atom PMU has different event
encoding from a Core PMU. It brings issue if two attributes are
created for them. Current sysfs_update_group finds an attribute by
searching the attr name (aka event name). If two attributes have the
same event name, the first attribute will be replaced.
To address the issue, only one attribute is created for the event. The
event_str is extended and stores event encodings from all Hybrid PMUs.
Each event encoding is divided by ";". The order of the event encodings
must follow the order of the hybrid PMU index. The event_str is internal
usage as well. When a user wants to show the attribute of a Hybrid PMU,
only the corresponding part of the string is displayed.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/core.c | 43 +++++++++++++++++++++++++++++++++++++++++++
arch/x86/events/perf_event.h | 19 +++++++++++++++++++
include/linux/perf_event.h | 12 ++++++++++++
3 files changed, 74 insertions(+)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 7dc1430..43f5529 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -1864,6 +1864,49 @@ ssize_t events_ht_sysfs_show(struct device *dev, struct device_attribute *attr,
pmu_attr->event_str_noht);
}

+ssize_t events_hybrid_sysfs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *page)
+{
+ struct perf_pmu_events_hybrid_attr *pmu_attr =
+ container_of(attr, struct perf_pmu_events_hybrid_attr, attr);
+ struct x86_hybrid_pmu *pmu;
+ const char *str, *next_str;
+ int i;
+
+ if (hweight64(pmu_attr->pmu_type) == 1)
+ return sprintf(page, "%s", pmu_attr->event_str);
+
+ /*
+ * Hybrid PMUs may support the same event name, but with different
+ * event encoding, e.g., the mem-loads event on an Atom PMU has
+ * different event encoding from a Core PMU.
+ *
+ * The event_str includes all event encodings. Each event encoding
+ * is divided by ";". The order of the event encodings must follow
+ * the order of the hybrid PMU index.
+ */
+ pmu = container_of(dev_get_drvdata(dev), struct x86_hybrid_pmu, pmu);
+
+ str = pmu_attr->event_str;
+ for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
+ if (!(x86_pmu.hybrid_pmu[i].cpu_type & pmu_attr->pmu_type))
+ continue;
+ if (x86_pmu.hybrid_pmu[i].cpu_type & pmu->cpu_type) {
+ next_str = strchr(str, ';');
+ if (next_str)
+ return snprintf(page, next_str - str + 1, "%s", str);
+ else
+ return sprintf(page, "%s", str);
+ }
+ str = strchr(str, ';');
+ str++;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(events_hybrid_sysfs_show);
+
EVENT_ATTR(cpu-cycles, CPU_CYCLES );
EVENT_ATTR(instructions, INSTRUCTIONS );
EVENT_ATTR(cache-references, CACHE_REFERENCES );
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 809c036..36a08709 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -964,6 +964,22 @@ static struct perf_pmu_events_ht_attr event_attr_##v = { \
.event_str_ht = ht, \
}

+#define EVENT_ATTR_STR_HYBRID(_name, v, str, _pmu) \
+static struct perf_pmu_events_hybrid_attr event_attr_##v = { \
+ .attr = __ATTR(_name, 0444, events_hybrid_sysfs_show, NULL),\
+ .id = 0, \
+ .event_str = str, \
+ .pmu_type = _pmu, \
+}
+
+#define FORMAT_HYBRID_PTR(_id) (&format_attr_hybrid_##_id.attr.attr)
+
+#define FORMAT_ATTR_HYBRID(_name, _pmu) \
+static struct perf_pmu_format_hybrid_attr format_attr_hybrid_##_name = {\
+ .attr = __ATTR_RO(_name), \
+ .pmu_type = _pmu, \
+}
+
struct pmu *x86_get_pmu(unsigned int cpu);
extern struct x86_pmu x86_pmu __read_mostly;

@@ -1132,6 +1148,9 @@ ssize_t events_sysfs_show(struct device *dev, struct device_attribute *attr,
char *page);
ssize_t events_ht_sysfs_show(struct device *dev, struct device_attribute *attr,
char *page);
+ssize_t events_hybrid_sysfs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *page);

static inline bool fixed_counter_disabled(int i, struct pmu *pmu)
{
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index fab42cf..21ab3f5 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1546,6 +1546,18 @@ struct perf_pmu_events_ht_attr {
const char *event_str_noht;
};

+struct perf_pmu_events_hybrid_attr {
+ struct device_attribute attr;
+ u64 id;
+ const char *event_str;
+ u64 pmu_type;
+};
+
+struct perf_pmu_format_hybrid_attr {
+ struct device_attribute attr;
+ u64 pmu_type;
+};
+
ssize_t perf_event_sysfs_show(struct device *dev, struct device_attribute *attr,
char *page);

--
2.7.4

2021-03-10 16:49:20

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 21/25] perf: Introduce PERF_TYPE_HARDWARE_PMU and PERF_TYPE_HW_CACHE_PMU

From: Kan Liang <[email protected]>

Current Hardware events and Hardware cache events have special perf
types, PERF_TYPE_HARDWARE and PERF_TYPE_HW_CACHE. The two types don't
pass the PMU type in the user interface. For a hybrid system, the perf
subsystem doesn't know which PMU the events belong to. The first capable
PMU will always be assigned to the events. The events never get a chance
to run on the other capable PMUs.

Add a PMU aware version PERF_TYPE_HARDWARE_PMU and
PERF_TYPE_HW_CACHE_PMU. The PMU type ID is stored at attr.config[40:32].
Support the new types for X86.

Suggested-by: Andi Kleen <[email protected]>
Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/core.c | 10 ++++++++--
include/uapi/linux/perf_event.h | 26 ++++++++++++++++++++++++++
kernel/events/core.c | 14 +++++++++++++-
3 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index b675283..f031d00 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -484,7 +484,7 @@ int x86_setup_perfctr(struct perf_event *event)
if (attr->type == event->pmu->type)
return x86_pmu_extra_regs(event->attr.config, event);

- if (attr->type == PERF_TYPE_HW_CACHE)
+ if ((attr->type == PERF_TYPE_HW_CACHE) || (attr->type == PERF_TYPE_HW_CACHE_PMU))
return set_ext_hw_attr(hwc, event);

if (attr->config >= x86_pmu.max_events)
@@ -2427,9 +2427,15 @@ static int x86_pmu_event_init(struct perf_event *event)

if ((event->attr.type != event->pmu->type) &&
(event->attr.type != PERF_TYPE_HARDWARE) &&
- (event->attr.type != PERF_TYPE_HW_CACHE))
+ (event->attr.type != PERF_TYPE_HW_CACHE) &&
+ (event->attr.type != PERF_TYPE_HARDWARE_PMU) &&
+ (event->attr.type != PERF_TYPE_HW_CACHE_PMU))
return -ENOENT;

+ if ((event->attr.type == PERF_TYPE_HARDWARE_PMU) ||
+ (event->attr.type == PERF_TYPE_HW_CACHE_PMU))
+ event->attr.config &= PERF_HW_CACHE_EVENT_MASK;
+
if (is_hybrid() && (event->cpu != -1)) {
pmu = hybrid_pmu(event->pmu);
if (!cpumask_test_cpu(event->cpu, &pmu->supported_cpus))
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
index ad15e40..c0a511e 100644
--- a/include/uapi/linux/perf_event.h
+++ b/include/uapi/linux/perf_event.h
@@ -33,6 +33,8 @@ enum perf_type_id {
PERF_TYPE_HW_CACHE = 3,
PERF_TYPE_RAW = 4,
PERF_TYPE_BREAKPOINT = 5,
+ PERF_TYPE_HARDWARE_PMU = 6,
+ PERF_TYPE_HW_CACHE_PMU = 7,

PERF_TYPE_MAX, /* non-ABI */
};
@@ -95,6 +97,30 @@ enum perf_hw_cache_op_result_id {
};

/*
+ * attr.config layout for type PERF_TYPE_HARDWARE* and PERF_TYPE_HW_CACHE*
+ * PERF_TYPE_HARDWARE: 0xAA
+ * AA: hardware event ID
+ * PERF_TYPE_HW_CACHE: 0xCCBBAA
+ * AA: hardware cache ID
+ * BB: hardware cache op ID
+ * CC: hardware cache op result ID
+ * PERF_TYPE_HARDWARE_PMU: 0xDD000000AA
+ * AA: hardware event ID
+ * DD: PMU type ID
+ * PERF_TYPE_HW_CACHE_PMU: 0xDD00CCBBAA
+ * AA: hardware cache ID
+ * BB: hardware cache op ID
+ * CC: hardware cache op result ID
+ * DD: PMU type ID
+ */
+#define PERF_HW_CACHE_ID_SHIFT 0
+#define PERF_HW_CACHE_OP_ID_SHIFT 8
+#define PERF_HW_CACHE_OP_RESULT_ID_SHIFT 16
+#define PERF_HW_CACHE_EVENT_MASK 0xffffff
+
+#define PERF_PMU_TYPE_SHIFT 32
+
+/*
* Special "software" events provided by the kernel, even if the hardware
* does not support performance events. These events measure various
* physical and sw events of the kernel (and allow the profiling of them as
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 0aeca5f..6d7524e 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -11059,6 +11059,14 @@ static int perf_try_init_event(struct pmu *pmu, struct perf_event *event)
return ret;
}

+static bool perf_event_is_hw_pmu_type(struct perf_event *event)
+{
+ int type = event->attr.type;
+
+ return type == PERF_TYPE_HARDWARE_PMU ||
+ type == PERF_TYPE_HW_CACHE_PMU;
+}
+
static struct pmu *perf_init_event(struct perf_event *event)
{
int idx, type, ret;
@@ -11082,13 +11090,17 @@ static struct pmu *perf_init_event(struct perf_event *event)
if (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE)
type = PERF_TYPE_RAW;

+ if (perf_event_is_hw_pmu_type(event))
+ type = event->attr.config >> PERF_PMU_TYPE_SHIFT;
+
again:
rcu_read_lock();
pmu = idr_find(&pmu_idr, type);
rcu_read_unlock();
if (pmu) {
ret = perf_try_init_event(pmu, event);
- if (ret == -ENOENT && event->attr.type != type) {
+ if (ret == -ENOENT && event->attr.type != type &&
+ !perf_event_is_hw_pmu_type(event)) {
type = event->attr.type;
goto again;
}
--
2.7.4

2021-03-10 16:49:42

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 22/25] perf/x86/intel/uncore: Add Alder Lake support

From: Kan Liang <[email protected]>

The uncore subsystem for Alder Lake is similar to the previous Tiger
Lake.

The difference includes:
- New MSR addresses for global control, fixed counters, CBOX and ARB.
Add a new adl_uncore_msr_ops for uncore operations.
- Add a new threshold field for CBOX.
- New PCIIDs for IMC devices.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Kan Liang <[email protected]>
---
arch/x86/events/intel/uncore.c | 7 ++
arch/x86/events/intel/uncore.h | 1 +
arch/x86/events/intel/uncore_snb.c | 131 +++++++++++++++++++++++++++++++++++++
3 files changed, 139 insertions(+)

diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c
index 33c8180..3ad5df2 100644
--- a/arch/x86/events/intel/uncore.c
+++ b/arch/x86/events/intel/uncore.c
@@ -1625,6 +1625,11 @@ static const struct intel_uncore_init_fun rkl_uncore_init __initconst = {
.pci_init = skl_uncore_pci_init,
};

+static const struct intel_uncore_init_fun adl_uncore_init __initconst = {
+ .cpu_init = adl_uncore_cpu_init,
+ .mmio_init = tgl_uncore_mmio_init,
+};
+
static const struct intel_uncore_init_fun icx_uncore_init __initconst = {
.cpu_init = icx_uncore_cpu_init,
.pci_init = icx_uncore_pci_init,
@@ -1673,6 +1678,8 @@ static const struct x86_cpu_id intel_uncore_match[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, &tgl_l_uncore_init),
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, &tgl_uncore_init),
X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE, &rkl_uncore_init),
+ X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, &adl_uncore_init),
+ X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, &adl_uncore_init),
X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_D, &snr_uncore_init),
{},
};
diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h
index a3c6e16..30e6557 100644
--- a/arch/x86/events/intel/uncore.h
+++ b/arch/x86/events/intel/uncore.h
@@ -567,6 +567,7 @@ void snb_uncore_cpu_init(void);
void nhm_uncore_cpu_init(void);
void skl_uncore_cpu_init(void);
void icl_uncore_cpu_init(void);
+void adl_uncore_cpu_init(void);
void tgl_uncore_cpu_init(void);
void tgl_uncore_mmio_init(void);
void tgl_l_uncore_mmio_init(void);
diff --git a/arch/x86/events/intel/uncore_snb.c b/arch/x86/events/intel/uncore_snb.c
index 5127128..0f63706 100644
--- a/arch/x86/events/intel/uncore_snb.c
+++ b/arch/x86/events/intel/uncore_snb.c
@@ -62,6 +62,8 @@
#define PCI_DEVICE_ID_INTEL_TGL_H_IMC 0x9a36
#define PCI_DEVICE_ID_INTEL_RKL_1_IMC 0x4c43
#define PCI_DEVICE_ID_INTEL_RKL_2_IMC 0x4c53
+#define PCI_DEVICE_ID_INTEL_ADL_1_IMC 0x4660
+#define PCI_DEVICE_ID_INTEL_ADL_2_IMC 0x4641

/* SNB event control */
#define SNB_UNC_CTL_EV_SEL_MASK 0x000000ff
@@ -131,12 +133,33 @@
#define ICL_UNC_ARB_PER_CTR 0x3b1
#define ICL_UNC_ARB_PERFEVTSEL 0x3b3

+/* ADL uncore global control */
+#define ADL_UNC_PERF_GLOBAL_CTL 0x2ff0
+#define ADL_UNC_FIXED_CTR_CTRL 0x2fde
+#define ADL_UNC_FIXED_CTR 0x2fdf
+
+/* ADL Cbo register */
+#define ADL_UNC_CBO_0_PER_CTR0 0x2002
+#define ADL_UNC_CBO_0_PERFEVTSEL0 0x2000
+#define ADL_UNC_CTL_THRESHOLD 0x3f000000
+#define ADL_UNC_RAW_EVENT_MASK (SNB_UNC_CTL_EV_SEL_MASK | \
+ SNB_UNC_CTL_UMASK_MASK | \
+ SNB_UNC_CTL_EDGE_DET | \
+ SNB_UNC_CTL_INVERT | \
+ ADL_UNC_CTL_THRESHOLD)
+
+/* ADL ARB register */
+#define ADL_UNC_ARB_PER_CTR0 0x2FD2
+#define ADL_UNC_ARB_PERFEVTSEL0 0x2FD0
+#define ADL_UNC_ARB_MSR_OFFSET 0x8
+
DEFINE_UNCORE_FORMAT_ATTR(event, event, "config:0-7");
DEFINE_UNCORE_FORMAT_ATTR(umask, umask, "config:8-15");
DEFINE_UNCORE_FORMAT_ATTR(edge, edge, "config:18");
DEFINE_UNCORE_FORMAT_ATTR(inv, inv, "config:23");
DEFINE_UNCORE_FORMAT_ATTR(cmask5, cmask, "config:24-28");
DEFINE_UNCORE_FORMAT_ATTR(cmask8, cmask, "config:24-31");
+DEFINE_UNCORE_FORMAT_ATTR(threshold, threshold, "config:24-29");

/* Sandy Bridge uncore support */
static void snb_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
@@ -422,6 +445,106 @@ void tgl_uncore_cpu_init(void)
skl_uncore_msr_ops.init_box = rkl_uncore_msr_init_box;
}

+static void adl_uncore_msr_init_box(struct intel_uncore_box *box)
+{
+ if (box->pmu->pmu_idx == 0)
+ wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
+}
+
+static void adl_uncore_msr_enable_box(struct intel_uncore_box *box)
+{
+ wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
+}
+
+static void adl_uncore_msr_disable_box(struct intel_uncore_box *box)
+{
+ if (box->pmu->pmu_idx == 0)
+ wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, 0);
+}
+
+static void adl_uncore_msr_exit_box(struct intel_uncore_box *box)
+{
+ if (box->pmu->pmu_idx == 0)
+ wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, 0);
+}
+
+static struct intel_uncore_ops adl_uncore_msr_ops = {
+ .init_box = adl_uncore_msr_init_box,
+ .enable_box = adl_uncore_msr_enable_box,
+ .disable_box = adl_uncore_msr_disable_box,
+ .exit_box = adl_uncore_msr_exit_box,
+ .disable_event = snb_uncore_msr_disable_event,
+ .enable_event = snb_uncore_msr_enable_event,
+ .read_counter = uncore_msr_read_counter,
+};
+
+static struct attribute *adl_uncore_formats_attr[] = {
+ &format_attr_event.attr,
+ &format_attr_umask.attr,
+ &format_attr_edge.attr,
+ &format_attr_inv.attr,
+ &format_attr_threshold.attr,
+ NULL,
+};
+
+static const struct attribute_group adl_uncore_format_group = {
+ .name = "format",
+ .attrs = adl_uncore_formats_attr,
+};
+
+static struct intel_uncore_type adl_uncore_cbox = {
+ .name = "cbox",
+ .num_counters = 2,
+ .perf_ctr_bits = 44,
+ .perf_ctr = ADL_UNC_CBO_0_PER_CTR0,
+ .event_ctl = ADL_UNC_CBO_0_PERFEVTSEL0,
+ .event_mask = ADL_UNC_RAW_EVENT_MASK,
+ .msr_offset = ICL_UNC_CBO_MSR_OFFSET,
+ .ops = &adl_uncore_msr_ops,
+ .format_group = &adl_uncore_format_group,
+};
+
+static struct intel_uncore_type adl_uncore_arb = {
+ .name = "arb",
+ .num_counters = 2,
+ .num_boxes = 2,
+ .perf_ctr_bits = 44,
+ .perf_ctr = ADL_UNC_ARB_PER_CTR0,
+ .event_ctl = ADL_UNC_ARB_PERFEVTSEL0,
+ .event_mask = SNB_UNC_RAW_EVENT_MASK,
+ .msr_offset = ADL_UNC_ARB_MSR_OFFSET,
+ .constraints = snb_uncore_arb_constraints,
+ .ops = &adl_uncore_msr_ops,
+ .format_group = &snb_uncore_format_group,
+};
+
+static struct intel_uncore_type adl_uncore_clockbox = {
+ .name = "clock",
+ .num_counters = 1,
+ .num_boxes = 1,
+ .fixed_ctr_bits = 48,
+ .fixed_ctr = ADL_UNC_FIXED_CTR,
+ .fixed_ctl = ADL_UNC_FIXED_CTR_CTRL,
+ .single_fixed = 1,
+ .event_mask = SNB_UNC_CTL_EV_SEL_MASK,
+ .format_group = &icl_uncore_clock_format_group,
+ .ops = &adl_uncore_msr_ops,
+ .event_descs = icl_uncore_events,
+};
+
+static struct intel_uncore_type *adl_msr_uncores[] = {
+ &adl_uncore_cbox,
+ &adl_uncore_arb,
+ &adl_uncore_clockbox,
+ NULL,
+};
+
+void adl_uncore_cpu_init(void)
+{
+ adl_uncore_cbox.num_boxes = icl_get_cbox_num();
+ uncore_msr_uncores = adl_msr_uncores;
+}
+
enum {
SNB_PCI_UNCORE_IMC,
};
@@ -1203,6 +1326,14 @@ static const struct pci_device_id tgl_uncore_pci_ids[] = {
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TGL_H_IMC),
.driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
},
+ { /* IMC */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ADL_1_IMC),
+ .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+ },
+ { /* IMC */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ADL_2_IMC),
+ .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+ },
{ /* end: all zeroes */ }
};

--
2.7.4

2021-03-10 16:49:47

by Liang, Kan

[permalink] [raw]
Subject: [PATCH V2 25/25] perf/x86/rapl: Add support for Intel Alder Lake

From: Zhang Rui <[email protected]>

Alder Lake RAPL support is the same as previous Sky Lake.
Add Alder Lake model for RAPL.

Reviewed-by: Andi Kleen <[email protected]>
Signed-off-by: Zhang Rui <[email protected]>
---
arch/x86/events/rapl.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/arch/x86/events/rapl.c b/arch/x86/events/rapl.c
index f42a704..84a1042 100644
--- a/arch/x86/events/rapl.c
+++ b/arch/x86/events/rapl.c
@@ -800,6 +800,8 @@ static const struct x86_cpu_id rapl_model_match[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X, &model_hsx),
X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE_L, &model_skl),
X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, &model_skl),
+ X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, &model_skl),
+ X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, &model_skl),
X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, &model_spr),
X86_MATCH_VENDOR_FAM(AMD, 0x17, &model_amd_fam17h),
X86_MATCH_VENDOR_FAM(HYGON, 0x18, &model_amd_fam17h),
--
2.7.4

2021-03-10 16:52:02

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH V2 16/25] perf/x86: Register hybrid PMUs

On 3/10/21 8:37 AM, [email protected] wrote:
> - err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
> - if (err)
> - goto out2;
> + if (!is_hybrid()) {
> + err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
> + if (err)
> + goto out2;
> + } else {
> + u8 cpu_type = get_hybrid_cpu_type(smp_processor_id());
> + struct x86_hybrid_pmu *hybrid_pmu;
> + int i;

Where's the preempt_disable()?

> +static void init_hybrid_pmu(int cpu)
> +{
> + unsigned int fixed_mask, unused_eax, unused_ebx, unused_edx;
> + struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
> + u8 cpu_type = get_hybrid_cpu_type(cpu);
> + struct x86_hybrid_pmu *pmu = NULL;
> + struct perf_cpu_context *cpuctx;
> + int i;

Ditto.

Are we really sure the IPIs are worth the trouble? Why don't we just
cache the leaf when we bring the CPU up like just about every other
thing we read from CPUID?

2021-03-10 16:57:56

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH V2 1/25] x86/cpufeatures: Enumerate Intel Hybrid Technology feature bit

On Wed, Mar 10, 2021 at 08:37:37AM -0800, [email protected] wrote:
> From: Ricardo Neri <[email protected]>
>
> Add feature enumeration to identify a processor with Intel Hybrid
> Technology: one in which CPUs of more than one type are the same package.
> On a hybrid processor, all CPUs support the same homogeneous (i.e.,
> symmetric) instruction set. All CPUs enumerate the same features in CPUID.
> Thus, software (user space and kernel) can run and migrate to any CPU in
> the system as well as utilize any of the enumerated features without any
> change or special provisions. The main difference among CPUs in a hybrid
> processor are power and performance properties.
>
> Cc: Andi Kleen <[email protected]>
> Cc: Kan Liang <[email protected]>
> Cc: "Peter Zijlstra (Intel)" <[email protected]>
> Cc: "Rafael J. Wysocki" <[email protected]>
> Cc: "Ravi V. Shankar" <[email protected]>
> Cc: Srinivas Pandruvada <[email protected]>
> Cc: [email protected]
> Reviewed-by: Len Brown <[email protected]>
> Reviewed-by: Tony Luck <[email protected]>
> Signed-off-by: Ricardo Neri <[email protected]>
> ---
> Changes since v1 (as part of patchset for perf change for Alderlake)
> * None
>
> Changes since v1 (in a separate posting):
> * Reworded commit message to clearly state what is Intel Hybrid
> Technology. Stress that all CPUs can run the same instruction
> set and support the same features.
> ---
> arch/x86/include/asm/cpufeatures.h | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
> index cc96e26d69f7..e7cfc9eedf8d 100644
> --- a/arch/x86/include/asm/cpufeatures.h
> +++ b/arch/x86/include/asm/cpufeatures.h
> @@ -374,6 +374,7 @@
> #define X86_FEATURE_MD_CLEAR (18*32+10) /* VERW clears CPU buffers */
> #define X86_FEATURE_TSX_FORCE_ABORT (18*32+13) /* "" TSX_FORCE_ABORT */
> #define X86_FEATURE_SERIALIZE (18*32+14) /* SERIALIZE instruction */
> +#define X86_FEATURE_HYBRID_CPU (18*32+15) /* This part has CPUs of more than one type */

/* "" This ...

unless you have a valid use case for "hybrid_cpu" being present there.

Thx.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2021-03-10 17:41:38

by Liang, Kan

[permalink] [raw]
Subject: Re: [PATCH V2 16/25] perf/x86: Register hybrid PMUs



On 3/10/2021 11:50 AM, Dave Hansen wrote:
> On 3/10/21 8:37 AM, [email protected] wrote:
>> - err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
>> - if (err)
>> - goto out2;
>> + if (!is_hybrid()) {
>> + err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
>> + if (err)
>> + goto out2;
>> + } else {
>> + u8 cpu_type = get_hybrid_cpu_type(smp_processor_id());
>> + struct x86_hybrid_pmu *hybrid_pmu;
>> + int i;
>
> Where's the preempt_disable()?
>
>> +static void init_hybrid_pmu(int cpu)
>> +{
>> + unsigned int fixed_mask, unused_eax, unused_ebx, unused_edx;
>> + struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
>> + u8 cpu_type = get_hybrid_cpu_type(cpu);
>> + struct x86_hybrid_pmu *pmu = NULL;
>> + struct perf_cpu_context *cpuctx;
>> + int i;
>
> Ditto.
>
> Are we really sure the IPIs are worth the trouble? Why don't we just
> cache the leaf when we bring the CPU up like just about every other
> thing we read from CPUID?

Simple answer: You are right. We don't need it. A simple
get_this_hybrid_cpu_type() should be fine for perf.

Here is the complete story.
I need the CPU type of the dead CPU in the cpu_dead. In the previous
patch set, we can read it from the cached CPU type in the struct
cpuinfo_x86.

In the V2 patch, I tried to do a similar thing (but I have to read it at
runtime). Since the CPU is offlined, I asked Ricardo to provide the
function get_hybrid_cpu_type() which can read the CPU type of a specific
CPU. But I'm wrong. We cannot retrieve the valid CPU type from an
offlined CPU. So I dropped the method and used another method to
retrieve the information, but I didn't let Ricardo update the function.
My bad.

Now, we only need to read the CPU type of the current CPU. A
get_this_hybrid_cpu_type() function is enough for me.

I think we can get rid of the IPIs trouble with the new
get_this_hybrid_cpu_type() in the next version. We shouldn't need the
preempt_disable() either.

Thanks for pointing that out.

Kan

2021-03-10 19:36:01

by srinivas pandruvada

[permalink] [raw]
Subject: Re: [PATCH V2 1/25] x86/cpufeatures: Enumerate Intel Hybrid Technology feature bit

On Wed, 2021-03-10 at 17:53 +0100, Borislav Petkov wrote:
> On Wed, Mar 10, 2021 at 08:37:37AM -0800, [email protected]
> wrote:
> > From: Ricardo Neri <[email protected]>
> >
> > Add feature enumeration to identify a processor with Intel Hybrid
> > Technology: one in which CPUs of more than one type are the same
> > package.
> > On a hybrid processor, all CPUs support the same homogeneous (i.e.,
> > symmetric) instruction set. All CPUs enumerate the same features in
> > CPUID.
> > Thus, software (user space and kernel) can run and migrate to any
> > CPU in
> > the system as well as utilize any of the enumerated features
> > without any
> > change or special provisions. The main difference among CPUs in a
> > hybrid
> > processor are power and performance properties.
> >
> > Cc: Andi Kleen <[email protected]>
> > Cc: Kan Liang <[email protected]>
> > Cc: "Peter Zijlstra (Intel)" <[email protected]>
> > Cc: "Rafael J. Wysocki" <[email protected]>
> > Cc: "Ravi V. Shankar" <[email protected]>
> > Cc: Srinivas Pandruvada <[email protected]>
> > Cc: [email protected]
> > Reviewed-by: Len Brown <[email protected]>
> > Reviewed-by: Tony Luck <[email protected]>
> > Signed-off-by: Ricardo Neri <[email protected]>
> > ---
> > Changes since v1 (as part of patchset for perf change for
> > Alderlake)
> > * None
> >
> > Changes since v1 (in a separate posting):
> > * Reworded commit message to clearly state what is Intel Hybrid
> > Technology. Stress that all CPUs can run the same instruction
> > set and support the same features.
> > ---
> > arch/x86/include/asm/cpufeatures.h | 1 +
> > 1 file changed, 1 insertion(+)
> >
> > diff --git a/arch/x86/include/asm/cpufeatures.h
> > b/arch/x86/include/asm/cpufeatures.h
> > index cc96e26d69f7..e7cfc9eedf8d 100644
> > --- a/arch/x86/include/asm/cpufeatures.h
> > +++ b/arch/x86/include/asm/cpufeatures.h
> > @@ -374,6 +374,7 @@
> > #define X86_FEATURE_MD_CLEAR (18*32+10) /* VERW
> > clears CPU buffers */
> > #define X86_FEATURE_TSX_FORCE_ABORT (18*32+13) /* ""
> > TSX_FORCE_ABORT */
> > #define X86_FEATURE_SERIALIZE (18*32+14) /* SERIALIZE
> > instruction */
> > +#define X86_FEATURE_HYBRID_CPU (18*32+15) /* This part
> > has CPUs of more than one type */
>
> /* "" This
> ...
>
> unless you have a valid use case for "hybrid_cpu" being present
> there.
We are working on changes to P-State driver for hybrid CPUs using this
define. They are still work in progress.
But this patch can be submitted later with our set of changes.

Thanks,
Srinivas


>
> Thx.
>

2021-03-10 19:48:58

by Ricardo Neri

[permalink] [raw]
Subject: Re: [PATCH V2 1/25] x86/cpufeatures: Enumerate Intel Hybrid Technology feature bit

On Wed, Mar 10, 2021 at 05:53:58PM +0100, Borislav Petkov wrote:
> On Wed, Mar 10, 2021 at 08:37:37AM -0800, [email protected] wrote:
> > From: Ricardo Neri <[email protected]>
> >
> > Add feature enumeration to identify a processor with Intel Hybrid
> > Technology: one in which CPUs of more than one type are the same package.
> > On a hybrid processor, all CPUs support the same homogeneous (i.e.,
> > symmetric) instruction set. All CPUs enumerate the same features in CPUID.
> > Thus, software (user space and kernel) can run and migrate to any CPU in
> > the system as well as utilize any of the enumerated features without any
> > change or special provisions. The main difference among CPUs in a hybrid
> > processor are power and performance properties.
> >
> > Cc: Andi Kleen <[email protected]>
> > Cc: Kan Liang <[email protected]>
> > Cc: "Peter Zijlstra (Intel)" <[email protected]>
> > Cc: "Rafael J. Wysocki" <[email protected]>
> > Cc: "Ravi V. Shankar" <[email protected]>
> > Cc: Srinivas Pandruvada <[email protected]>
> > Cc: [email protected]
> > Reviewed-by: Len Brown <[email protected]>
> > Reviewed-by: Tony Luck <[email protected]>
> > Signed-off-by: Ricardo Neri <[email protected]>
> > ---
> > Changes since v1 (as part of patchset for perf change for Alderlake)
> > * None
> >
> > Changes since v1 (in a separate posting):
> > * Reworded commit message to clearly state what is Intel Hybrid
> > Technology. Stress that all CPUs can run the same instruction
> > set and support the same features.
> > ---
> > arch/x86/include/asm/cpufeatures.h | 1 +
> > 1 file changed, 1 insertion(+)
> >
> > diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
> > index cc96e26d69f7..e7cfc9eedf8d 100644
> > --- a/arch/x86/include/asm/cpufeatures.h
> > +++ b/arch/x86/include/asm/cpufeatures.h
> > @@ -374,6 +374,7 @@
> > #define X86_FEATURE_MD_CLEAR (18*32+10) /* VERW clears CPU buffers */
> > #define X86_FEATURE_TSX_FORCE_ABORT (18*32+13) /* "" TSX_FORCE_ABORT */
> > #define X86_FEATURE_SERIALIZE (18*32+14) /* SERIALIZE instruction */
> > +#define X86_FEATURE_HYBRID_CPU (18*32+15) /* This part has CPUs of more than one type */
>
> /* "" This ...
>
> unless you have a valid use case for "hybrid_cpu" being present there.

But this series provides the use case, right? Kan's patches handle PMU counters
that may differ cross types of CPUs. In patch 2, get_hybrid_params()
needs to check first if X86_FEATURE_HYBRID_CPU is enabled before
querying the hybrid parameters. Otherwise, we would need to rely on the
maximum level of CPUID, which may not be reliable.

Thanks and BR,
Ricardo

2021-03-10 20:04:01

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH V2 1/25] x86/cpufeatures: Enumerate Intel Hybrid Technology feature bit

On Wed, Mar 10, 2021 at 11:46:44AM -0800, Ricardo Neri wrote:
> But this series provides the use case, right? Kan's patches handle PMU counters
> that may differ cross types of CPUs. In patch 2, get_hybrid_params()
> needs to check first if X86_FEATURE_HYBRID_CPU is enabled before
> querying the hybrid parameters. Otherwise, we would need to rely on the
> maximum level of CPUID, which may not be reliable.

On Wed, Mar 10, 2021 at 11:33:54AM -0800, Srinivas Pandruvada wrote:
> We are working on changes to P-State driver for hybrid CPUs using this
> define. They are still work in progress.
> But this patch can be submitted later with our set of changes.

Answering to both with a single mail:

I don't have a problem with X86_FEATURE_HYBRID_CPU - I simply don't want
to show "hybrid_cpu" in /proc/cpuinfo unless there's a valid use case
for userspace to know that it is running on a hybrid CPU.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2021-03-10 22:29:33

by Ricardo Neri

[permalink] [raw]
Subject: Re: [PATCH V2 1/25] x86/cpufeatures: Enumerate Intel Hybrid Technology feature bit

On Wed, Mar 10, 2021 at 09:01:47PM +0100, Borislav Petkov wrote:
> On Wed, Mar 10, 2021 at 11:46:44AM -0800, Ricardo Neri wrote:
> > But this series provides the use case, right? Kan's patches handle PMU counters
> > that may differ cross types of CPUs. In patch 2, get_hybrid_params()
> > needs to check first if X86_FEATURE_HYBRID_CPU is enabled before
> > querying the hybrid parameters. Otherwise, we would need to rely on the
> > maximum level of CPUID, which may not be reliable.
>
> On Wed, Mar 10, 2021 at 11:33:54AM -0800, Srinivas Pandruvada wrote:
> > We are working on changes to P-State driver for hybrid CPUs using this
> > define. They are still work in progress.
> > But this patch can be submitted later with our set of changes.
>
> Answering to both with a single mail:
>
> I don't have a problem with X86_FEATURE_HYBRID_CPU - I simply don't want
> to show "hybrid_cpu" in /proc/cpuinfo unless there's a valid use case
> for userspace to know that it is running on a hybrid CPU.

Ah, I get your point now. You would like to see

#define X86_FEATURE_HYBRID_CPU (18*32+15) /* "" This part has CPUs of more than one type */

Right? Now your first comment makes sense.

Srinivas, Kan, I don't think we need to expose "hybrid_cpu" in
/proc/cpuinfo, do we?

Thanks and BR,
Ricardo

2021-03-10 22:34:48

by Liang, Kan

[permalink] [raw]
Subject: Re: [PATCH V2 1/25] x86/cpufeatures: Enumerate Intel Hybrid Technology feature bit



On 3/10/2021 5:25 PM, Ricardo Neri wrote:
> On Wed, Mar 10, 2021 at 09:01:47PM +0100, Borislav Petkov wrote:
>> On Wed, Mar 10, 2021 at 11:46:44AM -0800, Ricardo Neri wrote:
>>> But this series provides the use case, right? Kan's patches handle PMU counters
>>> that may differ cross types of CPUs. In patch 2, get_hybrid_params()
>>> needs to check first if X86_FEATURE_HYBRID_CPU is enabled before
>>> querying the hybrid parameters. Otherwise, we would need to rely on the
>>> maximum level of CPUID, which may not be reliable.
>>
>> On Wed, Mar 10, 2021 at 11:33:54AM -0800, Srinivas Pandruvada wrote:
>>> We are working on changes to P-State driver for hybrid CPUs using this
>>> define. They are still work in progress.
>>> But this patch can be submitted later with our set of changes.
>>
>> Answering to both with a single mail:
>>
>> I don't have a problem with X86_FEATURE_HYBRID_CPU - I simply don't want
>> to show "hybrid_cpu" in /proc/cpuinfo unless there's a valid use case
>> for userspace to know that it is running on a hybrid CPU.
>
> Ah, I get your point now. You would like to see
>
> #define X86_FEATURE_HYBRID_CPU (18*32+15) /* "" This part has CPUs of more than one type */
>
> Right? Now your first comment makes sense.
>
> Srinivas, Kan, I don't think we need to expose "hybrid_cpu" in
> /proc/cpuinfo, do we?
>

Right, Perf doesn't use the "hybrid_cpu" in /proc/cpuinfo.

Thanks,
Kan

2021-03-10 22:44:39

by srinivas pandruvada

[permalink] [raw]
Subject: Re: [PATCH V2 1/25] x86/cpufeatures: Enumerate Intel Hybrid Technology feature bit

On Wed, 2021-03-10 at 14:25 -0800, Ricardo Neri wrote:
> On Wed, Mar 10, 2021 at 09:01:47PM +0100, Borislav Petkov wrote:
> > On Wed, Mar 10, 2021 at 11:46:44AM -0800, Ricardo Neri wrote:
> > > But this series provides the use case, right? Kan's patches
> > > handle PMU counters
> > > that may differ cross types of CPUs. In patch 2,
> > > get_hybrid_params()
> > > needs to check first if X86_FEATURE_HYBRID_CPU is enabled before
> > > querying the hybrid parameters. Otherwise, we would need to rely
> > > on the
> > > maximum level of CPUID, which may not be reliable.
> >
> > On Wed, Mar 10, 2021 at 11:33:54AM -0800, Srinivas Pandruvada
> > wrote:
> > > We are working on changes to P-State driver for hybrid CPUs using
> > > this
> > > define. They are still work in progress.
> > > But this patch can be submitted later with our set of changes.
> >
> > Answering to both with a single mail:
> >
> > I don't have a problem with X86_FEATURE_HYBRID_CPU - I simply don't
> > want
> > to show "hybrid_cpu" in /proc/cpuinfo unless there's a valid use
> > case
> > for userspace to know that it is running on a hybrid CPU.
>
> Ah, I get your point now. You would like to see
>
> #define X86_FEATURE_HYBRID_CPU (18*32+15) /* "" This
> part has CPUs of more than one type */
>
> Right? Now your first comment makes sense.
>
> Srinivas, Kan, I don't think we need to expose "hybrid_cpu" in
> /proc/cpuinfo, do we?
I don't see any need.

Thanks,
Srinivas


>
> Thanks and BR,
> Ricardo

2021-03-11 11:09:31

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 08/25] perf/x86: Hybrid PMU support for hardware cache event

On Wed, Mar 10, 2021 at 08:37:44AM -0800, [email protected] wrote:
> From: Kan Liang <[email protected]>
>
> The hardware cache events are different among hybrid PMUs. Each hybrid
> PMU should have its own hw cache event table.
>
> Reviewed-by: Andi Kleen <[email protected]>
> Signed-off-by: Kan Liang <[email protected]>
> ---
> arch/x86/events/core.c | 11 +++++++++--
> arch/x86/events/perf_event.h | 9 +++++++++
> 2 files changed, 18 insertions(+), 2 deletions(-)
>
> diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
> index 039a851..1db4a67 100644
> --- a/arch/x86/events/core.c
> +++ b/arch/x86/events/core.c
> @@ -352,6 +352,7 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
> {
> struct perf_event_attr *attr = &event->attr;
> unsigned int cache_type, cache_op, cache_result;
> + struct x86_hybrid_pmu *pmu = is_hybrid() ? hybrid_pmu(event->pmu) : NULL;
> u64 config, val;
>
> config = attr->config;
> @@ -371,7 +372,10 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
> return -EINVAL;
> cache_result = array_index_nospec(cache_result, PERF_COUNT_HW_CACHE_RESULT_MAX);
>
> - val = hw_cache_event_ids[cache_type][cache_op][cache_result];
> + if (pmu)
> + val = pmu->hw_cache_event_ids[cache_type][cache_op][cache_result];
> + else
> + val = hw_cache_event_ids[cache_type][cache_op][cache_result];
>
> if (val == 0)
> return -ENOENT;
> @@ -380,7 +384,10 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
> return -EINVAL;
>
> hwc->config |= val;
> - attr->config1 = hw_cache_extra_regs[cache_type][cache_op][cache_result];
> + if (pmu)
> + attr->config1 = pmu->hw_cache_extra_regs[cache_type][cache_op][cache_result];
> + else
> + attr->config1 = hw_cache_extra_regs[cache_type][cache_op][cache_result];

Why not:

attr->config1 = hybrid(event->pmu, hw_cache_extra_regs)[cache_type][cache_op][cache_result];

?

2021-03-11 11:52:30

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH V2 2/25] x86/cpu: Add helper functions to get parameters of hybrid CPUs

On Wed, Mar 10, 2021 at 08:37:38AM -0800, [email protected] wrote:
> From: Ricardo Neri <[email protected]>
>
> On processors with Intel Hybrid Technology (i.e., one having more than one
> type of CPU in the same package), all CPUs support the same instruction
> set and enumerate the same features on CPUID. Thus, all software can run
> on any CPU without restrictions. However, there may be model-specific
> differences among types of CPUs. For instance, each type of CPU may support
> a different number of performance counters. Also, machine check error banks
> may be wired differently. Even though most software will not care about
> these differences, kernel subsystems dealing with these differences must
> know. Add a new member to cpuinfo_x86 that subsystems can query to know
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

That is not the case anymore.

And it looks like the IPIing is going away too, which is good.

Thx.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2021-03-11 11:59:20

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 16/25] perf/x86: Register hybrid PMUs

On Wed, Mar 10, 2021 at 08:37:52AM -0800, [email protected] wrote:
> +static void init_hybrid_pmu(int cpu)
> +{
> + unsigned int fixed_mask, unused_eax, unused_ebx, unused_edx;
> + struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
> + u8 cpu_type = get_hybrid_cpu_type(cpu);
> + struct x86_hybrid_pmu *pmu = NULL;
> + struct perf_cpu_context *cpuctx;
> + int i;
> +
> + for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
> + if (x86_pmu.hybrid_pmu[i].cpu_type == cpu_type) {
> + pmu = &x86_pmu.hybrid_pmu[i];
> + break;
> + }
> + }
> + if (WARN_ON_ONCE(!pmu))
> + return;
> +
> + cpuc->pmu = &pmu->pmu;
> +
> + /* Only register PMU for the first CPU */
> + if (!cpumask_empty(&pmu->supported_cpus)) {
> + cpumask_set_cpu(cpu, &pmu->supported_cpus);
> + goto end;
> + }
> +
> + if (!check_hw_exists(&pmu->pmu, pmu->num_counters, pmu->num_counters_fixed))
> + return;
> +
> + if ((pmu->pmu.type == -1) &&
> + perf_pmu_register(&pmu->pmu, pmu->name, x86_get_hybrid_pmu_type(pmu->cpu_type)))
> + return;
> +
> + /*
> + * Except for ECX, other fields have been stored in the x86 struct
> + * at boot time.
> + */
> + cpuid(10, &unused_eax, &unused_ebx, &fixed_mask, &unused_edx);
> +
> + intel_pmu_check_num_counters(&pmu->num_counters,
> + &pmu->num_counters_fixed,
> + &pmu->intel_ctrl,
> + (u64)fixed_mask);
> +
> + pr_info("%s PMU driver: ", pmu->name);
> +
> + if (pmu->intel_cap.perf_metrics) {
> + pmu->intel_ctrl |= 1ULL << GLOBAL_CTRL_EN_PERF_METRICS;
> + pmu->intel_ctrl |= INTEL_PMC_MSK_FIXED_SLOTS;
> + }
> +
> + if (pmu->intel_cap.pebs_output_pt_available) {
> + pmu->pmu.capabilities |= PERF_PMU_CAP_AUX_OUTPUT;
> + pr_cont("PEBS-via-PT ");
> + }
> +
> + intel_pmu_check_event_constraints(pmu->event_constraints,
> + pmu->num_counters,
> + pmu->num_counters_fixed,
> + pmu->intel_ctrl);
> +
> + intel_pmu_check_extra_regs(pmu->extra_regs);
> +
> + pr_cont("\n");
> +
> + x86_pmu_show_pmu_cap(pmu->num_counters, pmu->num_counters_fixed,
> + pmu->intel_ctrl);
> +
> + cpumask_set_cpu(cpu, &pmu->supported_cpus);
> +end:
> + /*
> + * The cpuctx of all CPUs are allocated when registering the
> + * boot CPU's PMU. At that time, the PMU for other hybrid CPUs
> + * is not registered yet. The boot CPU's PMU was
> + * unconditionally assigned to each cpuctx->ctx.pmu.
> + * Update the cpuctx->ctx.pmu when the PMU for other hybrid
> + * CPUs is known.
> + */
> + cpuctx = per_cpu_ptr(pmu->pmu.pmu_cpu_context, cpu);
> + cpuctx->ctx.pmu = &pmu->pmu;
> +}
> +
> static void intel_pmu_cpu_starting(int cpu)
> {
> struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
> int core_id = topology_core_id(cpu);
> int i;
>
> + if (is_hybrid())
> + init_hybrid_pmu(cpu);
> +
> init_debug_store_on_cpu(cpu);
> /*
> * Deal with CPUs that don't clear their LBRs on power-up.

This is buggered. CPU_STARTING is the initial IRQs disabled part of
hotplug, but you're calling perf_pmu_register() which does mutex_lock().

2021-03-11 12:20:03

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 16/25] perf/x86: Register hybrid PMUs

On Wed, Mar 10, 2021 at 08:37:52AM -0800, [email protected] wrote:
> @@ -2092,9 +2105,37 @@ static int __init init_hw_perf_events(void)
> if (err)
> goto out1;
>
> - err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
> - if (err)
> - goto out2;
> + if (!is_hybrid()) {
> + err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
> + if (err)
> + goto out2;
> + } else {
> + u8 cpu_type = get_hybrid_cpu_type(smp_processor_id());
> + struct x86_hybrid_pmu *hybrid_pmu;
> + int i;
> +
> + for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
> + hybrid_pmu = &x86_pmu.hybrid_pmu[i];

I can't find where you actually allocated x86_pmu.hybrid_pmu.

> + hybrid_pmu->pmu = pmu;
> + hybrid_pmu->pmu.type = -1;
> + hybrid_pmu->pmu.attr_update = x86_pmu.attr_update;
> + hybrid_pmu->pmu.capabilities |= PERF_PMU_CAP_HETEROGENEOUS_CPUS;
> +
> + /* Only register the PMU for the boot CPU */
> + if (hybrid_pmu->cpu_type != cpu_type)
> + continue;
> +
> + err = perf_pmu_register(&hybrid_pmu->pmu, hybrid_pmu->name,
> + x86_get_hybrid_pmu_type(cpu_type));
> + if (err) {
> + pr_warn("Failed to register a PMU, err %d\n", err);
> + kfree(x86_pmu.hybrid_pmu);
> + x86_pmu.hybrid_pmu = NULL;
> + goto out2;
> + }
> + }
> + }
>
> return 0;

2021-03-11 12:21:14

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 16/25] perf/x86: Register hybrid PMUs

On Wed, Mar 10, 2021 at 08:37:52AM -0800, [email protected] wrote:
> @@ -4420,6 +4513,27 @@ void intel_cpuc_finish(struct cpu_hw_events *cpuc)
> static void intel_pmu_cpu_dead(int cpu)
> {
> intel_cpuc_finish(&per_cpu(cpu_hw_events, cpu));
> +
> + if (is_hybrid()) {
> + struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
> + struct x86_hybrid_pmu *hybrid_pmu = NULL;
> + int i;
> +
> + for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
> + if (cpumask_test_and_clear_cpu(cpu, &x86_pmu.hybrid_pmu[i].supported_cpus)) {
> + hybrid_pmu = &x86_pmu.hybrid_pmu[i];
> + break;
> + }
> + }
> + if (WARN_ON_ONCE(!hybrid_pmu))
> + return;
> +
> + cpuc->pmu = NULL;
> + if (cpumask_empty(&hybrid_pmu->supported_cpus)) {
> + perf_pmu_unregister(&hybrid_pmu->pmu);
> + hybrid_pmu->pmu.type = -1;
> + }
> + }
> }

CPU_DEAD is the part of hotplug after we've disabled IRQs for the last
time and before we kill ourselves, and you're trying to use mutexes
again!

2021-03-11 12:35:11

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 16/25] perf/x86: Register hybrid PMUs

On Thu, Mar 11, 2021 at 01:17:15PM +0100, Peter Zijlstra wrote:
> On Wed, Mar 10, 2021 at 08:37:52AM -0800, [email protected] wrote:
> > @@ -2092,9 +2105,37 @@ static int __init init_hw_perf_events(void)
> > if (err)
> > goto out1;
> >
> > - err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
> > - if (err)
> > - goto out2;
> > + if (!is_hybrid()) {
> > + err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
> > + if (err)
> > + goto out2;
> > + } else {
> > + u8 cpu_type = get_hybrid_cpu_type(smp_processor_id());
> > + struct x86_hybrid_pmu *hybrid_pmu;
> > + int i;
> > +
> > + for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
> > + hybrid_pmu = &x86_pmu.hybrid_pmu[i];
>
> I can't find where you actually allocated x86_pmu.hybrid_pmu.

Ah, patch 20 has that.

> > + hybrid_pmu->pmu = pmu;
> > + hybrid_pmu->pmu.type = -1;
> > + hybrid_pmu->pmu.attr_update = x86_pmu.attr_update;
> > + hybrid_pmu->pmu.capabilities |= PERF_PMU_CAP_HETEROGENEOUS_CPUS;
> > +
> > + /* Only register the PMU for the boot CPU */
> > + if (hybrid_pmu->cpu_type != cpu_type)
> > + continue;
> > +
> > + err = perf_pmu_register(&hybrid_pmu->pmu, hybrid_pmu->name,
> > + x86_get_hybrid_pmu_type(cpu_type));
> > + if (err) {
> > + pr_warn("Failed to register a PMU, err %d\n", err);
> > + kfree(x86_pmu.hybrid_pmu);
> > + x86_pmu.hybrid_pmu = NULL;
> > + goto out2;
> > + }
> > + }
> > + }
> >
> > return 0;

2021-03-11 12:36:41

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 16/25] perf/x86: Register hybrid PMUs

On Wed, Mar 10, 2021 at 08:37:52AM -0800, [email protected] wrote:
> @@ -2092,9 +2105,37 @@ static int __init init_hw_perf_events(void)
> if (err)
> goto out1;
>
> - err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
> - if (err)
> - goto out2;
> + if (!is_hybrid()) {
> + err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
> + if (err)
> + goto out2;
> + } else {
> + u8 cpu_type = get_hybrid_cpu_type(smp_processor_id());
> + struct x86_hybrid_pmu *hybrid_pmu;
> + int i;
> +
> + for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
> + hybrid_pmu = &x86_pmu.hybrid_pmu[i];
> +
> + hybrid_pmu->pmu = pmu;
> + hybrid_pmu->pmu.type = -1;
> + hybrid_pmu->pmu.attr_update = x86_pmu.attr_update;
> + hybrid_pmu->pmu.capabilities |= PERF_PMU_CAP_HETEROGENEOUS_CPUS;
> +
> + /* Only register the PMU for the boot CPU */

Why ?!

AFAICT we could register them all here. That instantly fixes that
CPU_STARTING / CPU_DEAD fail elsewhere in this patch.

> + if (hybrid_pmu->cpu_type != cpu_type)
> + continue;
> +
> + err = perf_pmu_register(&hybrid_pmu->pmu, hybrid_pmu->name,
> + x86_get_hybrid_pmu_type(cpu_type));
> + if (err) {
> + pr_warn("Failed to register a PMU, err %d\n", err);
> + kfree(x86_pmu.hybrid_pmu);
> + x86_pmu.hybrid_pmu = NULL;
> + goto out2;
> + }
> + }
> + }
>
> return 0;
>

2021-03-11 12:53:12

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support

On Wed, Mar 10, 2021 at 08:37:56AM -0800, [email protected] wrote:
> @@ -4059,6 +4099,34 @@ tfa_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
> return c;
> }
>
> +static struct event_constraint *
> +adl_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
> + struct perf_event *event)
> +{
> + struct x86_hybrid_pmu *pmu = hybrid_pmu(event->pmu);
> +
> + if (pmu->cpu_type == INTEL_HYBRID_TYPE_CORE)
> + return spr_get_event_constraints(cpuc, idx, event);
> + else if (pmu->cpu_type == INTEL_HYBRID_TYPE_ATOM)
> + return tnt_get_event_constraints(cpuc, idx, event);
> +
> + WARN_ON(1);
> + return &emptyconstraint;
> +}
> +
> +static int adl_hw_config(struct perf_event *event)
> +{
> + struct x86_hybrid_pmu *pmu = hybrid_pmu(event->pmu);
> +
> + if (pmu->cpu_type == INTEL_HYBRID_TYPE_CORE)
> + return hsw_hw_config(event);
> + else if (pmu->cpu_type == INTEL_HYBRID_TYPE_ATOM)
> + return intel_pmu_hw_config(event);
> +
> + WARN_ON(1);
> + return -EOPNOTSUPP;
> +}
> +
> /*
> * Broadwell:
> *

> @@ -5266,6 +5342,84 @@ static const struct attribute_group *attr_update[] = {
> NULL,
> };
>
> +EVENT_ATTR_STR_HYBRID(slots, slots_hybrid, "event=0x00,umask=0x4", INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(topdown-retiring, td_retiring_hybrid, "event=0xc2,umask=0x0;event=0x00,umask=0x80", INTEL_HYBRID_TYPE_ATOM | INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(topdown-bad-spec, td_bad_spec_hybrid, "event=0x73,umask=0x0;event=0x00,umask=0x81", INTEL_HYBRID_TYPE_ATOM | INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(topdown-fe-bound, td_fe_bound_hybrid, "event=0x71,umask=0x0;event=0x00,umask=0x82", INTEL_HYBRID_TYPE_ATOM | INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(topdown-be-bound, td_be_bound_hybrid, "event=0x74,umask=0x0;event=0x00,umask=0x83", INTEL_HYBRID_TYPE_ATOM | INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(topdown-heavy-ops, td_heavy_ops_hybrid, "event=0x00,umask=0x84", INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(topdown-br-mispredict, td_br_mispredict_hybrid, "event=0x00,umask=0x85", INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(topdown-fetch-lat, td_fetch_lat_hybrid, "event=0x00,umask=0x86", INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(topdown-mem-bound, td_mem_bound_hybrid, "event=0x00,umask=0x87", INTEL_HYBRID_TYPE_CORE);
> +
> +static struct attribute *adl_hybrid_events_attrs[] = {
> + EVENT_PTR(slots_hybrid),
> + EVENT_PTR(td_retiring_hybrid),
> + EVENT_PTR(td_bad_spec_hybrid),
> + EVENT_PTR(td_fe_bound_hybrid),
> + EVENT_PTR(td_be_bound_hybrid),
> + EVENT_PTR(td_heavy_ops_hybrid),
> + EVENT_PTR(td_br_mispredict_hybrid),
> + EVENT_PTR(td_fetch_lat_hybrid),
> + EVENT_PTR(td_mem_bound_hybrid),
> + NULL,
> +};
> +
> +/* Must be in IDX order */
> +EVENT_ATTR_STR_HYBRID(mem-loads, mem_ld_adl_hybrid, "event=0xd0,umask=0x5,ldlat=3;event=0xcd,umask=0x1,ldlat=3", INTEL_HYBRID_TYPE_ATOM | INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(mem-stores, mem_st_adl_hybrid, "event=0xd0,umask=0x6;event=0xcd,umask=0x2", INTEL_HYBRID_TYPE_ATOM | INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(mem-loads-aux, mem_ld_aux_hybrid, "event=0x03,umask=0x82", INTEL_HYBRID_TYPE_CORE);
> +
> +static struct attribute *adl_hybrid_mem_attrs[] = {
> + EVENT_PTR(mem_ld_adl_hybrid),
> + EVENT_PTR(mem_st_adl_hybrid),
> + EVENT_PTR(mem_ld_aux_hybrid),
> + NULL,
> +};
> +
> +EVENT_ATTR_STR_HYBRID(tx-start, tx_start_adl_hybrid, "event=0xc9,umask=0x1", INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(tx-commit, tx_commit_adl_hybrid, "event=0xc9,umask=0x2", INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(tx-abort, tx_abort_adl_hybrid, "event=0xc9,umask=0x4", INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(tx-conflict, tx_conflict_adl_hybrid, "event=0x54,umask=0x1", INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(cycles-t, cycles_t_adl_hybrid, "event=0x3c,in_tx=1", INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(cycles-ct, cycles_ct_adl_hybrid, "event=0x3c,in_tx=1,in_tx_cp=1", INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(tx-capacity-read, tx_capacity_read_adl_hybrid, "event=0x54,umask=0x80", INTEL_HYBRID_TYPE_CORE);
> +EVENT_ATTR_STR_HYBRID(tx-capacity-write, tx_capacity_write_adl_hybrid, "event=0x54,umask=0x2", INTEL_HYBRID_TYPE_CORE);
> +
> +static struct attribute *adl_hybrid_tsx_attrs[] = {
> + EVENT_PTR(tx_start_adl_hybrid),
> + EVENT_PTR(tx_abort_adl_hybrid),
> + EVENT_PTR(tx_commit_adl_hybrid),
> + EVENT_PTR(tx_capacity_read_adl_hybrid),
> + EVENT_PTR(tx_capacity_write_adl_hybrid),
> + EVENT_PTR(tx_conflict_adl_hybrid),
> + EVENT_PTR(cycles_t_adl_hybrid),
> + EVENT_PTR(cycles_ct_adl_hybrid),
> + NULL,
> +};
> +
> +FORMAT_ATTR_HYBRID(in_tx, INTEL_HYBRID_TYPE_CORE);
> +FORMAT_ATTR_HYBRID(in_tx_cp, INTEL_HYBRID_TYPE_CORE);
> +FORMAT_ATTR_HYBRID(offcore_rsp, INTEL_HYBRID_TYPE_CORE | INTEL_HYBRID_TYPE_ATOM);
> +FORMAT_ATTR_HYBRID(ldlat, INTEL_HYBRID_TYPE_CORE | INTEL_HYBRID_TYPE_ATOM);
> +FORMAT_ATTR_HYBRID(frontend, INTEL_HYBRID_TYPE_CORE);


This really could do with something like:

enum {
BIGGIE = 0x40,
smalls = 0x20,
B_I_G = BIGGIE | smalls,
};

s/INTEL_HYBRID_TYPE_CORE/BIGGIE/g
s/INTEL_HYBRID_TYPE_ATOM/smalls/g
s/INTEL_HYBRID_TYPE_ATOM | INTEL_HYBRID_TYPE_CORE/B_I_G/g

Also, many of thaot ATTR thingies can do with some horizontal alignment,
they're unreadable gibberish.

2021-03-11 15:13:21

by Liang, Kan

[permalink] [raw]
Subject: Re: [PATCH V2 08/25] perf/x86: Hybrid PMU support for hardware cache event



On 3/11/2021 6:07 AM, Peter Zijlstra wrote:
> On Wed, Mar 10, 2021 at 08:37:44AM -0800, [email protected] wrote:
>> From: Kan Liang <[email protected]>
>>
>> The hardware cache events are different among hybrid PMUs. Each hybrid
>> PMU should have its own hw cache event table.
>>
>> Reviewed-by: Andi Kleen <[email protected]>
>> Signed-off-by: Kan Liang <[email protected]>
>> ---
>> arch/x86/events/core.c | 11 +++++++++--
>> arch/x86/events/perf_event.h | 9 +++++++++
>> 2 files changed, 18 insertions(+), 2 deletions(-)
>>
>> diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
>> index 039a851..1db4a67 100644
>> --- a/arch/x86/events/core.c
>> +++ b/arch/x86/events/core.c
>> @@ -352,6 +352,7 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
>> {
>> struct perf_event_attr *attr = &event->attr;
>> unsigned int cache_type, cache_op, cache_result;
>> + struct x86_hybrid_pmu *pmu = is_hybrid() ? hybrid_pmu(event->pmu) : NULL;
>> u64 config, val;
>>
>> config = attr->config;
>> @@ -371,7 +372,10 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
>> return -EINVAL;
>> cache_result = array_index_nospec(cache_result, PERF_COUNT_HW_CACHE_RESULT_MAX);
>>
>> - val = hw_cache_event_ids[cache_type][cache_op][cache_result];
>> + if (pmu)
>> + val = pmu->hw_cache_event_ids[cache_type][cache_op][cache_result];
>> + else
>> + val = hw_cache_event_ids[cache_type][cache_op][cache_result];
>>
>> if (val == 0)
>> return -ENOENT;
>> @@ -380,7 +384,10 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
>> return -EINVAL;
>>
>> hwc->config |= val;
>> - attr->config1 = hw_cache_extra_regs[cache_type][cache_op][cache_result];
>> + if (pmu)
>> + attr->config1 = pmu->hw_cache_extra_regs[cache_type][cache_op][cache_result];
>> + else
>> + attr->config1 = hw_cache_extra_regs[cache_type][cache_op][cache_result];
>
> Why not:
>
> attr->config1 = hybrid(event->pmu, hw_cache_extra_regs)[cache_type][cache_op][cache_result];
>
> ?

Because hw_cache_extra_regs is not part of the struct x86_pmu.


Thanks,
Kan

2021-03-11 15:42:43

by Liang, Kan

[permalink] [raw]
Subject: Re: [PATCH V2 16/25] perf/x86: Register hybrid PMUs



On 3/11/2021 7:34 AM, Peter Zijlstra wrote:
> On Wed, Mar 10, 2021 at 08:37:52AM -0800, [email protected] wrote:
>> @@ -2092,9 +2105,37 @@ static int __init init_hw_perf_events(void)
>> if (err)
>> goto out1;
>>
>> - err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
>> - if (err)
>> - goto out2;
>> + if (!is_hybrid()) {
>> + err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
>> + if (err)
>> + goto out2;
>> + } else {
>> + u8 cpu_type = get_hybrid_cpu_type(smp_processor_id());
>> + struct x86_hybrid_pmu *hybrid_pmu;
>> + int i;
>> +
>> + for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
>> + hybrid_pmu = &x86_pmu.hybrid_pmu[i];
>> +
>> + hybrid_pmu->pmu = pmu;
>> + hybrid_pmu->pmu.type = -1;
>> + hybrid_pmu->pmu.attr_update = x86_pmu.attr_update;
>> + hybrid_pmu->pmu.capabilities |= PERF_PMU_CAP_HETEROGENEOUS_CPUS;
>> +
>> + /* Only register the PMU for the boot CPU */
>
> Why ?!
> > AFAICT we could register them all here. That instantly fixes that
> CPU_STARTING / CPU_DEAD fail elsewhere in this patch.

It's possible that all CPUs of a certain type all offline, but I cannot
know the information here, because the boot CPU is the only online CPU.
I don't know the status of the other CPUs.

If we unconditionally register all PMUs, users may see a PMU in
/sys/devices, but they cannot use it, because there is no available CPU.
Is it acceptable that registering an empty PMU?


Thanks,
Kan

2021-03-11 16:13:28

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support

On Wed, Mar 10, 2021 at 08:37:56AM -0800, [email protected] wrote:
> From: Kan Liang <[email protected]>
>
> Alder Lake Hybrid system has two different types of core, Golden Cove
> core and Gracemont core. The Golden Cove core is registered to
> "cpu_core" PMU. The Gracemont core is registered to "cpu_atom" PMU.
>
> The difference between the two PMUs include:
> - Number of GP and fixed counters
> - Events
> - The "cpu_core" PMU supports Topdown metrics.
> The "cpu_atom" PMU supports PEBS-via-PT.
>
> The "cpu_core" PMU is similar to the Sapphire Rapids PMU, but without
> PMEM.
> The "cpu_atom" PMU is similar to Tremont, but with different
> event_constraints, extra_regs and number of counters.
>

> + /* Initialize big core specific PerfMon capabilities.*/
> + pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];
> + pmu->name = "cpu_core";

> + /* Initialize Atom core specific PerfMon capabilities.*/
> + pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];
> + pmu->name = "cpu_atom";

So do these things use the same event lists as SPR and TNT? Is there any
way to discover that, because AFAICT /proc/cpuinfo will say every CPU
is 'Alderlake', and the above also doesn't give any clue.

FWIW, ARM big.LITTLE does discriminate in its /proc/cpuinfo, but I'm not
entirely sure it's really useful. Mark said perf userspace uses
somethink akin to our CPUID, except exposed through sysfs, to find the
event lists.

My desktop has: cpu/caps/pmu_name and that gives "skylake", do we want
the above to have cpu_core/caps/pmu_name give "sapphire_rapids" etc.. ?

2021-03-11 16:16:01

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 16/25] perf/x86: Register hybrid PMUs

On Thu, Mar 11, 2021 at 10:41:00AM -0500, Liang, Kan wrote:
>
>
> On 3/11/2021 7:34 AM, Peter Zijlstra wrote:
> > On Wed, Mar 10, 2021 at 08:37:52AM -0800, [email protected] wrote:
> > > @@ -2092,9 +2105,37 @@ static int __init init_hw_perf_events(void)
> > > if (err)
> > > goto out1;
> > > - err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
> > > - if (err)
> > > - goto out2;
> > > + if (!is_hybrid()) {
> > > + err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
> > > + if (err)
> > > + goto out2;
> > > + } else {
> > > + u8 cpu_type = get_hybrid_cpu_type(smp_processor_id());
> > > + struct x86_hybrid_pmu *hybrid_pmu;
> > > + int i;
> > > +
> > > + for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
> > > + hybrid_pmu = &x86_pmu.hybrid_pmu[i];
> > > +
> > > + hybrid_pmu->pmu = pmu;
> > > + hybrid_pmu->pmu.type = -1;
> > > + hybrid_pmu->pmu.attr_update = x86_pmu.attr_update;
> > > + hybrid_pmu->pmu.capabilities |= PERF_PMU_CAP_HETEROGENEOUS_CPUS;
> > > +
> > > + /* Only register the PMU for the boot CPU */
> >
> > Why ?!
> > > AFAICT we could register them all here. That instantly fixes that
> > CPU_STARTING / CPU_DEAD fail elsewhere in this patch.
>
> It's possible that all CPUs of a certain type all offline, but I cannot know
> the information here, because the boot CPU is the only online CPU. I don't
> know the status of the other CPUs.
>
> If we unconditionally register all PMUs, users may see a PMU in
> /sys/devices, but they cannot use it, because there is no available CPU.
> Is it acceptable that registering an empty PMU?

Sure. That's what it has a cpumask for after all.

ISTR that being the case with some dodgy RAPL thing on SPR as well.

2021-03-11 16:35:07

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support

On Thu, Mar 11, 2021 at 05:09:25PM +0100, Peter Zijlstra wrote:
> On Wed, Mar 10, 2021 at 08:37:56AM -0800, [email protected] wrote:
> > From: Kan Liang <[email protected]>
> >
> > Alder Lake Hybrid system has two different types of core, Golden Cove
> > core and Gracemont core. The Golden Cove core is registered to
> > "cpu_core" PMU. The Gracemont core is registered to "cpu_atom" PMU.
> >
> > The difference between the two PMUs include:
> > - Number of GP and fixed counters
> > - Events
> > - The "cpu_core" PMU supports Topdown metrics.
> > The "cpu_atom" PMU supports PEBS-via-PT.
> >
> > The "cpu_core" PMU is similar to the Sapphire Rapids PMU, but without
> > PMEM.
> > The "cpu_atom" PMU is similar to Tremont, but with different
> > event_constraints, extra_regs and number of counters.
> >
>
> > + /* Initialize big core specific PerfMon capabilities.*/
> > + pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];
> > + pmu->name = "cpu_core";
>
> > + /* Initialize Atom core specific PerfMon capabilities.*/
> > + pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];
> > + pmu->name = "cpu_atom";
>
> So do these things use the same event lists as SPR and TNT? Is there any
> way to discover that, because AFAICT /proc/cpuinfo will say every CPU
> is 'Alderlake', and the above also doesn't give any clue.
>
> FWIW, ARM big.LITTLE does discriminate in its /proc/cpuinfo, but I'm not
> entirely sure it's really useful. Mark said perf userspace uses
> somethink akin to our CPUID, except exposed through sysfs, to find the
> event lists.
>
> My desktop has: cpu/caps/pmu_name and that gives "skylake", do we want
> the above to have cpu_core/caps/pmu_name give "sapphire_rapids" etc.. ?

FWIW, "Tremont" is the only pmu_name with a capital :-( I don't suppose
we can still fix that?

2021-03-11 16:55:32

by Liang, Kan

[permalink] [raw]
Subject: Re: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support



On 3/11/2021 11:09 AM, Peter Zijlstra wrote:
> On Wed, Mar 10, 2021 at 08:37:56AM -0800, [email protected] wrote:
>> From: Kan Liang <[email protected]>
>>
>> Alder Lake Hybrid system has two different types of core, Golden Cove
>> core and Gracemont core. The Golden Cove core is registered to
>> "cpu_core" PMU. The Gracemont core is registered to "cpu_atom" PMU.
>>
>> The difference between the two PMUs include:
>> - Number of GP and fixed counters
>> - Events
>> - The "cpu_core" PMU supports Topdown metrics.
>> The "cpu_atom" PMU supports PEBS-via-PT.
>>
>> The "cpu_core" PMU is similar to the Sapphire Rapids PMU, but without
>> PMEM.
>> The "cpu_atom" PMU is similar to Tremont, but with different
>> event_constraints, extra_regs and number of counters.
>>
>
>> + /* Initialize big core specific PerfMon capabilities.*/
>> + pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];
>> + pmu->name = "cpu_core";
>
>> + /* Initialize Atom core specific PerfMon capabilities.*/
>> + pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];
>> + pmu->name = "cpu_atom";
>
> So do these things use the same event lists as SPR and TNT?

No, there will be two new event lists on ADL. One is for Atom core, and
the other is for big core. They are different to SPR and TNT.

> Is there any
> way to discover that, because AFAICT /proc/cpuinfo will say every CPU
> is 'Alderlake', and the above also doesn't give any clue.
>

Ricardo once submitted a patch to expose the CPU type under
/sys/devices/system/cpu, but I don't know the latest status.
https://lore.kernel.org/lkml/[email protected]/



> FWIW, ARM big.LITTLE does discriminate in its /proc/cpuinfo, but I'm not
> entirely sure it's really useful. Mark said perf userspace uses
> somethink akin to our CPUID, except exposed through sysfs, to find the
> event lists.
>

Perf tool can use the pmu name.
For perf stat -e cpu_atom/EVENT_NAME/, perf will apply the event list
for atom.
For perf stat -e cpu_core/EVENT_NAME/, perf will apply the event list
for the big core.
For perf stat -e EVENT_NAME, perf tool will check if the EVENT_NAME
exists. If it's available on both event list, perf will automatically
create two events, perf stat -e cpu_atom/EVENT_NAME/,cpu_core/EVENT_NAME/.
If the event name is only available on a certain type, e.g., atom. The
perf tool will only apply the corresponding event, e.g., perf stat -e
cpu_atom/EVENT_NAME/

> My desktop has: cpu/caps/pmu_name and that gives "skylake", do we want
> the above to have cpu_core/caps/pmu_name give "sapphire_rapids" etc.. ?
>

I think current implementation should be good enough.

$ cat /sys/devices/cpu_atom/caps/pmu_name
alderlake_hybrid

"alderlake_hybrid" tells the perf tool that it's Alder Lake Hybrid system.
"cpu_atom" tells the perf tool that it's for Atom core.


Thanks,
Kan

2021-03-11 17:27:11

by Liang, Kan

[permalink] [raw]
Subject: Re: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support



On 3/11/2021 11:53 AM, Liang, Kan wrote:
>
>
> On 3/11/2021 11:09 AM, Peter Zijlstra wrote:
>> On Wed, Mar 10, 2021 at 08:37:56AM -0800, [email protected]
>> wrote:
>>> From: Kan Liang <[email protected]>
>>>
>>> Alder Lake Hybrid system has two different types of core, Golden Cove
>>> core and Gracemont core. The Golden Cove core is registered to
>>> "cpu_core" PMU. The Gracemont core is registered to "cpu_atom" PMU.
>>>
>>> The difference between the two PMUs include:
>>> - Number of GP and fixed counters
>>> - Events
>>> - The "cpu_core" PMU supports Topdown metrics.
>>>    The "cpu_atom" PMU supports PEBS-via-PT.
>>>
>>> The "cpu_core" PMU is similar to the Sapphire Rapids PMU, but without
>>> PMEM.
>>> The "cpu_atom" PMU is similar to Tremont, but with different
>>> event_constraints, extra_regs and number of counters.
>>>
>>
>>> +        /* Initialize big core specific PerfMon capabilities.*/
>>> +        pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];
>>> +        pmu->name = "cpu_core";
>>
>>> +        /* Initialize Atom core specific PerfMon capabilities.*/
>>> +        pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];
>>> +        pmu->name = "cpu_atom";
>>
>> So do these things use the same event lists as SPR and TNT?
>
> No, there will be two new event lists on ADL. One is for Atom core, and
> the other is for big core. They are different to SPR and TNT.
>
>> Is there any
>> way to discover that, because AFAICT /proc/cpuinfo will say every CPU
>> is 'Alderlake', and the above also doesn't give any clue.
>>
>
> Ricardo once submitted a patch to expose the CPU type under
> /sys/devices/system/cpu, but I don't know the latest status.
> https://lore.kernel.org/lkml/[email protected]/
>
>
>
>
>> FWIW, ARM big.LITTLE does discriminate in its /proc/cpuinfo, but I'm not
>> entirely sure it's really useful. Mark said perf userspace uses
>> somethink akin to our CPUID, except exposed through sysfs, to find the
>> event lists.
>>

Ah, I guess I misunderstood the concern. Let me try again.

Here is how perf tool find a event name via event list.

To get the correct event list file, yes, perf tool relies on the CPUID.
It will search a CPUID table in the
tools/perf/pmu-events/arch/x86/mapfile.csv.
GenuineIntel-6-97,v1,alderlake,core
Now perf tool knows the event list file "alderlake" is for the CPUID 97.

In the event list file for the Alder Lake (CPUID 0x97), we add a new
field "Unit" to distinguish the type of PMU.
"Unit": "cpu_core"
"Unit": "cpu_atom"

So perf can search the event name for a certain type of PMU via PMU name
"cpu_core" or "cpu_atom".

Perf tool doesn't use the "cpu_core/caps/pmu_name" for the event list.

Thanks,
Kan

2021-03-11 17:34:52

by Liang, Kan

[permalink] [raw]
Subject: Re: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support



On 3/11/2021 11:32 AM, Peter Zijlstra wrote:
> On Thu, Mar 11, 2021 at 05:09:25PM +0100, Peter Zijlstra wrote:
>> On Wed, Mar 10, 2021 at 08:37:56AM -0800, [email protected] wrote:
>>> From: Kan Liang <[email protected]>
>>>
>>> Alder Lake Hybrid system has two different types of core, Golden Cove
>>> core and Gracemont core. The Golden Cove core is registered to
>>> "cpu_core" PMU. The Gracemont core is registered to "cpu_atom" PMU.
>>>
>>> The difference between the two PMUs include:
>>> - Number of GP and fixed counters
>>> - Events
>>> - The "cpu_core" PMU supports Topdown metrics.
>>> The "cpu_atom" PMU supports PEBS-via-PT.
>>>
>>> The "cpu_core" PMU is similar to the Sapphire Rapids PMU, but without
>>> PMEM.
>>> The "cpu_atom" PMU is similar to Tremont, but with different
>>> event_constraints, extra_regs and number of counters.
>>>
>>
>>> + /* Initialize big core specific PerfMon capabilities.*/
>>> + pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];
>>> + pmu->name = "cpu_core";
>>
>>> + /* Initialize Atom core specific PerfMon capabilities.*/
>>> + pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];
>>> + pmu->name = "cpu_atom";
>>
>> So do these things use the same event lists as SPR and TNT? Is there any
>> way to discover that, because AFAICT /proc/cpuinfo will say every CPU
>> is 'Alderlake', and the above also doesn't give any clue.
>>
>> FWIW, ARM big.LITTLE does discriminate in its /proc/cpuinfo, but I'm not
>> entirely sure it's really useful. Mark said perf userspace uses
>> somethink akin to our CPUID, except exposed through sysfs, to find the
>> event lists.
>>
>> My desktop has: cpu/caps/pmu_name and that gives "skylake", do we want
>> the above to have cpu_core/caps/pmu_name give "sapphire_rapids" etc.. ?
>
> FWIW, "Tremont" is the only pmu_name with a capital :-( I don't suppose
> we can still fix that?
>

The cpu/caps/pmu_name is not used for the event list.

I don't think perf tool checks the "Tremont". I think it should be OK to
fix it. Let me post a patch.

Thanks,
Kan

2021-03-11 17:56:14

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH V2 16/25] perf/x86: Register hybrid PMUs

> AFAICT we could register them all here. That instantly fixes that
> CPU_STARTING / CPU_DEAD fail elsewhere in this patch.

This would mean a system that only has Atoms or only has big cores
would still show the other CPU's PMU. We expect those to exist.

-Andi

2021-03-11 19:57:41

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 16/25] perf/x86: Register hybrid PMUs

On Thu, Mar 11, 2021 at 09:53:58AM -0800, Andi Kleen wrote:
> > AFAICT we could register them all here. That instantly fixes that
> > CPU_STARTING / CPU_DEAD fail elsewhere in this patch.
>
> This would mean a system that only has Atoms or only has big cores
> would still show the other CPU's PMU. We expect those to exist.

Well, barring enumeration in ACPI/SRAT or thereabout, there's simply no
way to know.

Also, what's the point of making an alderlake with only big cores? Isn't
that what we're supposed to call saphire rappids? Same with only Atoms,
we're supposed to call that a temont.

Anyway, if people want to do weird things and not enumerate it sanely,
they'll get whatever we can make of it.

2021-03-11 20:03:02

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support

On Thu, Mar 11, 2021 at 11:53:35AM -0500, Liang, Kan wrote:

> > > The "cpu_core" PMU is similar to the Sapphire Rapids PMU, but without
> > > PMEM.
> > > The "cpu_atom" PMU is similar to Tremont, but with different
> > > event_constraints, extra_regs and number of counters.

> > So do these things use the same event lists as SPR and TNT?
>
> No, there will be two new event lists on ADL. One is for Atom core, and the
> other is for big core. They are different to SPR and TNT.

*sigh* how different?

> > Is there any
> > way to discover that, because AFAICT /proc/cpuinfo will say every CPU
> > is 'Alderlake', and the above also doesn't give any clue.
> >
>
> Ricardo once submitted a patch to expose the CPU type under
> /sys/devices/system/cpu, but I don't know the latest status.
> https://lore.kernel.org/lkml/[email protected]/

Yeah, but that was useless, it doesn't list the Cores as
FAM6_SAPPHIRERAPIDS nor the Atom as FAM6_ATOM_TREMONT.

> > My desktop has: cpu/caps/pmu_name and that gives "skylake", do we want
> > the above to have cpu_core/caps/pmu_name give "sapphire_rapids" etc.. ?
> >
>
> I think current implementation should be good enough.
>
> $ cat /sys/devices/cpu_atom/caps/pmu_name
> alderlake_hybrid
>
> "alderlake_hybrid" tells the perf tool that it's Alder Lake Hybrid system.
> "cpu_atom" tells the perf tool that it's for Atom core.

Yeah, but then I have to ask Google wth those atoms and cores actually
are. Why not tell me upfront?

Since we're now working on it, we all know, but in 6 months time nobody
will remember and then we'll constantly have to look it up and curse
ourselves for not doing better.

2021-03-11 20:34:33

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support

On Thu, Mar 11, 2021 at 08:58:32PM +0100, Peter Zijlstra wrote:
> On Thu, Mar 11, 2021 at 11:53:35AM -0500, Liang, Kan wrote:
>
> > > > The "cpu_core" PMU is similar to the Sapphire Rapids PMU, but without
> > > > PMEM.
> > > > The "cpu_atom" PMU is similar to Tremont, but with different
> > > > event_constraints, extra_regs and number of counters.
>
> > > So do these things use the same event lists as SPR and TNT?
> >
> > No, there will be two new event lists on ADL. One is for Atom core, and the
> > other is for big core. They are different to SPR and TNT.
>
> *sigh* how different?

Atom and Big core event list have significant differences.

Often event lists of the same core in different SOCs are different too
because some events indicate stuff outside the core (e.g. Offcore Response
and others)

>
> > > Is there any
> > > way to discover that, because AFAICT /proc/cpuinfo will say every CPU
> > > is 'Alderlake', and the above also doesn't give any clue.
> > >
> >
> > Ricardo once submitted a patch to expose the CPU type under
> > /sys/devices/system/cpu, but I don't know the latest status.
> > https://lore.kernel.org/lkml/[email protected]/
>
> Yeah, but that was useless, it doesn't list the Cores as
> FAM6_SAPPHIRERAPIDS nor the Atom as FAM6_ATOM_TREMONT.

It's Gracemont, not Tremont.

But what would you do with the information that the core is related
to some other core.

The event lists are tied to the Alderlake model number. You cannot
just use event lists from some other part because there are
differences to other Golden Cove or Gracemont implementations.

For non event list usages, the model numbers also indicate a lot of things
in the SOC, so even you knew it was a somewhat similar core as Sapphire
Rapids, it wouldn't tell the complete story. Even on the cores there are
differences.

In the end you need to know that Alderlake is Alderlake.

-Andi

2021-03-11 20:38:31

by Liang, Kan

[permalink] [raw]
Subject: Re: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support



On 3/11/2021 2:58 PM, Peter Zijlstra wrote:
> On Thu, Mar 11, 2021 at 11:53:35AM -0500, Liang, Kan wrote:
>
>>>> The "cpu_core" PMU is similar to the Sapphire Rapids PMU, but without
>>>> PMEM.
>>>> The "cpu_atom" PMU is similar to Tremont, but with different
>>>> event_constraints, extra_regs and number of counters.
>
>>> So do these things use the same event lists as SPR and TNT?
>>
>> No, there will be two new event lists on ADL. One is for Atom core, and the
>> other is for big core. They are different to SPR and TNT.
>
> *sigh* how different?

The core PMU event list should be similar between SPR and the big core
of ADL, because they both utilize the Golden Cove core. But the uncore
PMU event list is totally different for client and server.

The Atom core of ADL utilizes the Gracemont core, which is a successor
to Tremont. It introduces many new events. We cannot use the Tremont
event list instead.


>
>>> My desktop has: cpu/caps/pmu_name and that gives "skylake", do we want
>>> the above to have cpu_core/caps/pmu_name give "sapphire_rapids" etc.. ?
>>>
>>
>> I think current implementation should be good enough.
>>
>> $ cat /sys/devices/cpu_atom/caps/pmu_name
>> alderlake_hybrid
>>
>> "alderlake_hybrid" tells the perf tool that it's Alder Lake Hybrid system.
>> "cpu_atom" tells the perf tool that it's for Atom core.
>
> Yeah, but then I have to ask Google wth those atoms and cores actually
> are. Why not tell me upfront?
>
> Since we're now working on it, we all know, but in 6 months time nobody
> will remember and then we'll constantly have to look it up and curse
> ourselves for not doing better.

I think the "sapphire_rapids" is the code name for the server platform.
Maybe we should use the code name of core?

$ cat /sys/devices/cpu_atom/caps/pmu_name
gracemont
$ cat /sys/devices/cpu_core/caps/pmu_name
golden_cove


Thanks,
Kan

2021-03-11 20:43:13

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support

On Thu, Mar 11, 2021 at 12:30:53PM -0800, Andi Kleen wrote:

> But what would you do with the information that the core is related
> to some other core.

Reduce mental clutter I suppose... there are simply too many variations
of all this about :-(

> In the end you need to know that Alderlake is Alderlake.

*sigh*, okay.

2021-03-11 20:50:04

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support

On Thu, Mar 11, 2021 at 03:32:44PM -0500, Liang, Kan wrote:
> I think the "sapphire_rapids" is the code name for the server platform.

If that's really the case, then:

#define INTEL_FAM6_SAPPHIRERAPIDS_X 0x8F

is wrong, those things should be uarch name, not platform name. Tony?

2021-03-11 21:17:37

by Luck, Tony

[permalink] [raw]
Subject: RE: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support

>> I think the "sapphire_rapids" is the code name for the server platform.
>
> If that's really the case, then:
>
> #define INTEL_FAM6_SAPPHIRERAPIDS_X 0x8F
>
> is wrong, those things should be uarch name, not platform name. Tony?

0x8F is the model number of the CPU that is named Sapphire Rapids that
goes into the Eagle Stream platform

If you want a uarch name, that might be the name of the core
(.... something-cove ... I can't keep track of the names of all the
coves). But that would likely lead to different confusion later if that
*cove core is used in some other CPU model number that doesn't
neatly fit into our _X, _L etc. suffix scheme)

-Tony

2021-03-11 21:45:33

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support

On Thu, Mar 11, 2021 at 09:09:57PM +0000, Luck, Tony wrote:
> >> I think the "sapphire_rapids" is the code name for the server platform.
> >
> > If that's really the case, then:
> >
> > #define INTEL_FAM6_SAPPHIRERAPIDS_X 0x8F
> >
> > is wrong, those things should be uarch name, not platform name. Tony?
>
> 0x8F is the model number of the CPU that is named Sapphire Rapids that
> goes into the Eagle Stream platform
>
> If you want a uarch name, that might be the name of the core
> (.... something-cove ... I can't keep track of the names of all the
> coves).

uarch names used to be different from that. But looking at wikipedia,
things have gone completely apeshit after skylake :-/

We used to have one microarch and then a laptop,desktop and server sku,
but now each of those has a separately named microarch, so where we had
one new name each year, we now have at least 3. No wonder I've no
sodding clue anymore.

/me stmps off to get a beer to forget all I've seen ...

2021-03-12 00:05:59

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH V2 20/25] perf/x86/intel: Add Alder Lake Hybrid support

> If you want a uarch name, that might be the name of the core
> (.... something-cove ... I can't keep track of the names of all the

GoldenCove for Sapphire Rapids/AlderLake.

But keep in mind that they're still different. Kind of like a different
distro with different patches and configuration from the same base kernel
release.

-Andi