Skip to content

Commit d9977c4

Browse files
Kan LiangPeter Zijlstra
authored andcommitted
perf/x86: Register hybrid PMUs
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. All the hybrid PMUs are registered at boot time. Before the registration, add intel_pmu_check_hybrid_pmus() to check and update the counters information, the event constraints, the extra registers and the unique capabilities for each hybrid PMUs. Postpone the display of the PMU information and HW check to CPU_STARTING, because the boot CPU is the only online CPU in the init_hw_perf_events(). Perf doesn't know the availability of the other PMUs. Perf should display the PMU information only if the counters of the PMU are available. One type of CPUs may be all offline. For this case, users can still observe the PMU in /sys/devices, but its CPU mask is 0. 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. Perf may not retrieve a valid core type from get_this_hybrid_cpu_type(). For example, ADL may have an alternative configuration. With that configuration, Perf cannot retrieve the core type from the CPUID leaf 0x1a. Add a platform specific get_hybrid_cpu_type(). If the generic way fails, invoke the platform specific get_hybrid_cpu_type(). Suggested-by: Peter Zijlstra (Intel) <[email protected]> Signed-off-by: Kan Liang <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent e11c1a7 commit d9977c4

File tree

3 files changed

+223
-21
lines changed

3 files changed

+223
-21
lines changed

arch/x86/events/core.c

Lines changed: 118 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ int x86_setup_perfctr(struct perf_event *event)
478478
local64_set(&hwc->period_left, hwc->sample_period);
479479
}
480480

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

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

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

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

743743
static inline int is_x86_event(struct perf_event *event)
744744
{
745-
return event->pmu == &pmu;
745+
int i;
746+
747+
if (!is_hybrid())
748+
return event->pmu == &pmu;
749+
750+
for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
751+
if (event->pmu == &x86_pmu.hybrid_pmu[i].pmu)
752+
return true;
753+
}
754+
755+
return false;
746756
}
747757

748758
struct pmu *x86_get_pmu(unsigned int cpu)
@@ -1990,6 +2000,23 @@ void x86_pmu_show_pmu_cap(int num_counters, int num_counters_fixed,
19902000
pr_info("... event mask: %016Lx\n", intel_ctrl);
19912001
}
19922002

2003+
/*
2004+
* The generic code is not hybrid friendly. The hybrid_pmu->pmu
2005+
* of the first registered PMU is unconditionally assigned to
2006+
* each possible cpuctx->ctx.pmu.
2007+
* Update the correct hybrid PMU to the cpuctx->ctx.pmu.
2008+
*/
2009+
void x86_pmu_update_cpu_context(struct pmu *pmu, int cpu)
2010+
{
2011+
struct perf_cpu_context *cpuctx;
2012+
2013+
if (!pmu->pmu_cpu_context)
2014+
return;
2015+
2016+
cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu);
2017+
cpuctx->ctx.pmu = pmu;
2018+
}
2019+
19932020
static int __init init_hw_perf_events(void)
19942021
{
19952022
struct x86_pmu_quirk *quirk;
@@ -2050,8 +2077,11 @@ static int __init init_hw_perf_events(void)
20502077

20512078
pmu.attr_update = x86_pmu.attr_update;
20522079

2053-
x86_pmu_show_pmu_cap(x86_pmu.num_counters, x86_pmu.num_counters_fixed,
2054-
x86_pmu.intel_ctrl);
2080+
if (!is_hybrid()) {
2081+
x86_pmu_show_pmu_cap(x86_pmu.num_counters,
2082+
x86_pmu.num_counters_fixed,
2083+
x86_pmu.intel_ctrl);
2084+
}
20552085

20562086
if (!x86_pmu.read)
20572087
x86_pmu.read = _x86_pmu_read;
@@ -2081,9 +2111,45 @@ static int __init init_hw_perf_events(void)
20812111
if (err)
20822112
goto out1;
20832113

2084-
err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
2085-
if (err)
2086-
goto out2;
2114+
if (!is_hybrid()) {
2115+
err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
2116+
if (err)
2117+
goto out2;
2118+
} else {
2119+
u8 cpu_type = get_this_hybrid_cpu_type();
2120+
struct x86_hybrid_pmu *hybrid_pmu;
2121+
int i, j;
2122+
2123+
if (!cpu_type && x86_pmu.get_hybrid_cpu_type)
2124+
cpu_type = x86_pmu.get_hybrid_cpu_type();
2125+
2126+
for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
2127+
hybrid_pmu = &x86_pmu.hybrid_pmu[i];
2128+
2129+
hybrid_pmu->pmu = pmu;
2130+
hybrid_pmu->pmu.type = -1;
2131+
hybrid_pmu->pmu.attr_update = x86_pmu.attr_update;
2132+
hybrid_pmu->pmu.capabilities |= PERF_PMU_CAP_HETEROGENEOUS_CPUS;
2133+
2134+
err = perf_pmu_register(&hybrid_pmu->pmu, hybrid_pmu->name,
2135+
(hybrid_pmu->cpu_type == hybrid_big) ? PERF_TYPE_RAW : -1);
2136+
if (err)
2137+
break;
2138+
2139+
if (cpu_type == hybrid_pmu->cpu_type)
2140+
x86_pmu_update_cpu_context(&hybrid_pmu->pmu, raw_smp_processor_id());
2141+
}
2142+
2143+
if (i < x86_pmu.num_hybrid_pmus) {
2144+
for (j = 0; j < i; j++)
2145+
perf_pmu_unregister(&x86_pmu.hybrid_pmu[j].pmu);
2146+
pr_warn("Failed to register hybrid PMUs\n");
2147+
kfree(x86_pmu.hybrid_pmu);
2148+
x86_pmu.hybrid_pmu = NULL;
2149+
x86_pmu.num_hybrid_pmus = 0;
2150+
goto out2;
2151+
}
2152+
}
20872153

20882154
return 0;
20892155

@@ -2208,16 +2274,27 @@ static void free_fake_cpuc(struct cpu_hw_events *cpuc)
22082274
kfree(cpuc);
22092275
}
22102276

2211-
static struct cpu_hw_events *allocate_fake_cpuc(void)
2277+
static struct cpu_hw_events *allocate_fake_cpuc(struct pmu *event_pmu)
22122278
{
22132279
struct cpu_hw_events *cpuc;
2214-
int cpu = raw_smp_processor_id();
2280+
int cpu;
22152281

22162282
cpuc = kzalloc(sizeof(*cpuc), GFP_KERNEL);
22172283
if (!cpuc)
22182284
return ERR_PTR(-ENOMEM);
22192285
cpuc->is_fake = 1;
22202286

2287+
if (is_hybrid()) {
2288+
struct x86_hybrid_pmu *h_pmu;
2289+
2290+
h_pmu = hybrid_pmu(event_pmu);
2291+
if (cpumask_empty(&h_pmu->supported_cpus))
2292+
goto error;
2293+
cpu = cpumask_first(&h_pmu->supported_cpus);
2294+
} else
2295+
cpu = raw_smp_processor_id();
2296+
cpuc->pmu = event_pmu;
2297+
22212298
if (intel_cpuc_prepare(cpuc, cpu))
22222299
goto error;
22232300

@@ -2236,7 +2313,7 @@ static int validate_event(struct perf_event *event)
22362313
struct event_constraint *c;
22372314
int ret = 0;
22382315

2239-
fake_cpuc = allocate_fake_cpuc();
2316+
fake_cpuc = allocate_fake_cpuc(event->pmu);
22402317
if (IS_ERR(fake_cpuc))
22412318
return PTR_ERR(fake_cpuc);
22422319

@@ -2270,7 +2347,27 @@ static int validate_group(struct perf_event *event)
22702347
struct cpu_hw_events *fake_cpuc;
22712348
int ret = -EINVAL, n;
22722349

2273-
fake_cpuc = allocate_fake_cpuc();
2350+
/*
2351+
* Reject events from different hybrid PMUs.
2352+
*/
2353+
if (is_hybrid()) {
2354+
struct perf_event *sibling;
2355+
struct pmu *pmu = NULL;
2356+
2357+
if (is_x86_event(leader))
2358+
pmu = leader->pmu;
2359+
2360+
for_each_sibling_event(sibling, leader) {
2361+
if (!is_x86_event(sibling))
2362+
continue;
2363+
if (!pmu)
2364+
pmu = sibling->pmu;
2365+
else if (pmu != sibling->pmu)
2366+
return ret;
2367+
}
2368+
}
2369+
2370+
fake_cpuc = allocate_fake_cpuc(event->pmu);
22742371
if (IS_ERR(fake_cpuc))
22752372
return PTR_ERR(fake_cpuc);
22762373
/*
@@ -2298,16 +2395,18 @@ static int validate_group(struct perf_event *event)
22982395

22992396
static int x86_pmu_event_init(struct perf_event *event)
23002397
{
2398+
struct x86_hybrid_pmu *pmu = NULL;
23012399
int err;
23022400

2303-
switch (event->attr.type) {
2304-
case PERF_TYPE_RAW:
2305-
case PERF_TYPE_HARDWARE:
2306-
case PERF_TYPE_HW_CACHE:
2307-
break;
2308-
2309-
default:
2401+
if ((event->attr.type != event->pmu->type) &&
2402+
(event->attr.type != PERF_TYPE_HARDWARE) &&
2403+
(event->attr.type != PERF_TYPE_HW_CACHE))
23102404
return -ENOENT;
2405+
2406+
if (is_hybrid() && (event->cpu != -1)) {
2407+
pmu = hybrid_pmu(event->pmu);
2408+
if (!cpumask_test_cpu(event->cpu, &pmu->supported_cpus))
2409+
return -ENOENT;
23112410
}
23122411

23132412
err = __x86_pmu_event_init(event);

arch/x86/events/intel/core.c

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3714,7 +3714,8 @@ static int intel_pmu_hw_config(struct perf_event *event)
37143714
event->hw.flags |= PERF_X86_EVENT_PEBS_VIA_PT;
37153715
}
37163716

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

37203721
/*
@@ -4212,12 +4213,62 @@ static void flip_smm_bit(void *data)
42124213
}
42134214
}
42144215

4216+
static bool init_hybrid_pmu(int cpu)
4217+
{
4218+
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
4219+
u8 cpu_type = get_this_hybrid_cpu_type();
4220+
struct x86_hybrid_pmu *pmu = NULL;
4221+
int i;
4222+
4223+
if (!cpu_type && x86_pmu.get_hybrid_cpu_type)
4224+
cpu_type = x86_pmu.get_hybrid_cpu_type();
4225+
4226+
for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
4227+
if (x86_pmu.hybrid_pmu[i].cpu_type == cpu_type) {
4228+
pmu = &x86_pmu.hybrid_pmu[i];
4229+
break;
4230+
}
4231+
}
4232+
if (WARN_ON_ONCE(!pmu || (pmu->pmu.type == -1))) {
4233+
cpuc->pmu = NULL;
4234+
return false;
4235+
}
4236+
4237+
/* Only check and dump the PMU information for the first CPU */
4238+
if (!cpumask_empty(&pmu->supported_cpus))
4239+
goto end;
4240+
4241+
if (!check_hw_exists(&pmu->pmu, pmu->num_counters, pmu->num_counters_fixed))
4242+
return false;
4243+
4244+
pr_info("%s PMU driver: ", pmu->name);
4245+
4246+
if (pmu->intel_cap.pebs_output_pt_available)
4247+
pr_cont("PEBS-via-PT ");
4248+
4249+
pr_cont("\n");
4250+
4251+
x86_pmu_show_pmu_cap(pmu->num_counters, pmu->num_counters_fixed,
4252+
pmu->intel_ctrl);
4253+
4254+
end:
4255+
cpumask_set_cpu(cpu, &pmu->supported_cpus);
4256+
cpuc->pmu = &pmu->pmu;
4257+
4258+
x86_pmu_update_cpu_context(&pmu->pmu, cpu);
4259+
4260+
return true;
4261+
}
4262+
42154263
static void intel_pmu_cpu_starting(int cpu)
42164264
{
42174265
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
42184266
int core_id = topology_core_id(cpu);
42194267
int i;
42204268

4269+
if (is_hybrid() && !init_hybrid_pmu(cpu))
4270+
return;
4271+
42214272
init_debug_store_on_cpu(cpu);
42224273
/*
42234274
* Deal with CPUs that don't clear their LBRs on power-up.
@@ -4331,7 +4382,12 @@ void intel_cpuc_finish(struct cpu_hw_events *cpuc)
43314382

43324383
static void intel_pmu_cpu_dead(int cpu)
43334384
{
4334-
intel_cpuc_finish(&per_cpu(cpu_hw_events, cpu));
4385+
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
4386+
4387+
intel_cpuc_finish(cpuc);
4388+
4389+
if (is_hybrid() && cpuc->pmu)
4390+
cpumask_clear_cpu(cpu, &hybrid_pmu(cpuc->pmu)->supported_cpus);
43354391
}
43364392

43374393
static void intel_pmu_sched_task(struct perf_event_context *ctx,
@@ -5147,6 +5203,36 @@ static void intel_pmu_check_extra_regs(struct extra_reg *extra_regs)
51475203
}
51485204
}
51495205

5206+
static void intel_pmu_check_hybrid_pmus(u64 fixed_mask)
5207+
{
5208+
struct x86_hybrid_pmu *pmu;
5209+
int i;
5210+
5211+
for (i = 0; i < x86_pmu.num_hybrid_pmus; i++) {
5212+
pmu = &x86_pmu.hybrid_pmu[i];
5213+
5214+
intel_pmu_check_num_counters(&pmu->num_counters,
5215+
&pmu->num_counters_fixed,
5216+
&pmu->intel_ctrl,
5217+
fixed_mask);
5218+
5219+
if (pmu->intel_cap.perf_metrics) {
5220+
pmu->intel_ctrl |= 1ULL << GLOBAL_CTRL_EN_PERF_METRICS;
5221+
pmu->intel_ctrl |= INTEL_PMC_MSK_FIXED_SLOTS;
5222+
}
5223+
5224+
if (pmu->intel_cap.pebs_output_pt_available)
5225+
pmu->pmu.capabilities |= PERF_PMU_CAP_AUX_OUTPUT;
5226+
5227+
intel_pmu_check_event_constraints(pmu->event_constraints,
5228+
pmu->num_counters,
5229+
pmu->num_counters_fixed,
5230+
pmu->intel_ctrl);
5231+
5232+
intel_pmu_check_extra_regs(pmu->extra_regs);
5233+
}
5234+
}
5235+
51505236
__init int intel_pmu_init(void)
51515237
{
51525238
struct attribute **extra_skl_attr = &empty_attrs;
@@ -5826,6 +5912,9 @@ __init int intel_pmu_init(void)
58265912
if (!is_hybrid() && x86_pmu.intel_cap.perf_metrics)
58275913
x86_pmu.intel_ctrl |= 1ULL << GLOBAL_CTRL_EN_PERF_METRICS;
58285914

5915+
if (is_hybrid())
5916+
intel_pmu_check_hybrid_pmus((u64)fixed_mask);
5917+
58295918
return 0;
58305919
}
58315920

0 commit comments

Comments
 (0)