Skip to content

Commit bd24f9b

Browse files
Kan LiangPeter Zijlstra
authored andcommitted
perf/x86/intel: Add a check for dynamic constraints
The current event scheduler has a limit. If the counter constraint of an event is not a subset of any other counter constraint with an equal or higher weight. The counters may not be fully utilized. To workaround it, the commit bc1738f ("perf, x86: Fix event scheduler for constraints with overlapping counters") introduced an overlap flag, which is hardcoded to the event constraint that may trigger the limit. It only works for static constraints. Many features on and after Intel PMON v6 require dynamic constraints. An event constraint is decided by both static and dynamic constraints at runtime. See commit 4dfe323 ("perf/x86: Add dynamic constraint"). The dynamic constraints are from CPUID enumeration. It's impossible to hardcode it in advance. It's not practical to set the overlap flag to all events. It's harmful to the scheduler. For the existing Intel platforms, the dynamic constraints don't trigger the limit. A real fix is not required. However, for virtualization, VMM may give a weird CPUID enumeration to a guest. It's impossible to indicate what the weird enumeration is. A check is introduced, which can list the possible breaks if a weird enumeration is used. Check the dynamic constraints enumerated for normal, branch counters logging, and auto-counter reload. Check both PEBS and non-PEBS constratins. Closes: https://lore.kernel.org/lkml/20250416195610.GC38216@noisy.programming.kicks-ass.net/ Signed-off-by: Kan Liang <kan.liang@linux.intel.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://patch.msgid.link/20250512175542.2000708-1-kan.liang@linux.intel.com
1 parent bb5f13d commit bd24f9b

1 file changed

Lines changed: 148 additions & 8 deletions

File tree

arch/x86/events/intel/core.c

Lines changed: 148 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5420,6 +5420,151 @@ static void intel_pmu_check_event_constraints(struct event_constraint *event_con
54205420
u64 fixed_cntr_mask,
54215421
u64 intel_ctrl);
54225422

5423+
enum dyn_constr_type {
5424+
DYN_CONSTR_NONE,
5425+
DYN_CONSTR_BR_CNTR,
5426+
DYN_CONSTR_ACR_CNTR,
5427+
DYN_CONSTR_ACR_CAUSE,
5428+
5429+
DYN_CONSTR_MAX,
5430+
};
5431+
5432+
static const char * const dyn_constr_type_name[] = {
5433+
[DYN_CONSTR_NONE] = "a normal event",
5434+
[DYN_CONSTR_BR_CNTR] = "a branch counter logging event",
5435+
[DYN_CONSTR_ACR_CNTR] = "an auto-counter reload event",
5436+
[DYN_CONSTR_ACR_CAUSE] = "an auto-counter reload cause event",
5437+
};
5438+
5439+
static void __intel_pmu_check_dyn_constr(struct event_constraint *constr,
5440+
enum dyn_constr_type type, u64 mask)
5441+
{
5442+
struct event_constraint *c1, *c2;
5443+
int new_weight, check_weight;
5444+
u64 new_mask, check_mask;
5445+
5446+
for_each_event_constraint(c1, constr) {
5447+
new_mask = c1->idxmsk64 & mask;
5448+
new_weight = hweight64(new_mask);
5449+
5450+
/* ignore topdown perf metrics event */
5451+
if (c1->idxmsk64 & INTEL_PMC_MSK_TOPDOWN)
5452+
continue;
5453+
5454+
if (!new_weight && fls64(c1->idxmsk64) < INTEL_PMC_IDX_FIXED) {
5455+
pr_info("The event 0x%llx is not supported as %s.\n",
5456+
c1->code, dyn_constr_type_name[type]);
5457+
}
5458+
5459+
if (new_weight <= 1)
5460+
continue;
5461+
5462+
for_each_event_constraint(c2, c1 + 1) {
5463+
bool check_fail = false;
5464+
5465+
check_mask = c2->idxmsk64 & mask;
5466+
check_weight = hweight64(check_mask);
5467+
5468+
if (c2->idxmsk64 & INTEL_PMC_MSK_TOPDOWN ||
5469+
!check_weight)
5470+
continue;
5471+
5472+
/* The same constraints or no overlap */
5473+
if (new_mask == check_mask ||
5474+
(new_mask ^ check_mask) == (new_mask | check_mask))
5475+
continue;
5476+
5477+
/*
5478+
* A scheduler issue may be triggered in the following cases.
5479+
* - Two overlap constraints have the same weight.
5480+
* E.g., A constraints: 0x3, B constraints: 0x6
5481+
* event counter failure case
5482+
* B PMC[2:1] 1
5483+
* A PMC[1:0] 0
5484+
* A PMC[1:0] FAIL
5485+
* - Two overlap constraints have different weight.
5486+
* The constraint has a low weight, but has high last bit.
5487+
* E.g., A constraints: 0x7, B constraints: 0xC
5488+
* event counter failure case
5489+
* B PMC[3:2] 2
5490+
* A PMC[2:0] 0
5491+
* A PMC[2:0] 1
5492+
* A PMC[2:0] FAIL
5493+
*/
5494+
if (new_weight == check_weight) {
5495+
check_fail = true;
5496+
} else if (new_weight < check_weight) {
5497+
if ((new_mask | check_mask) != check_mask &&
5498+
fls64(new_mask) > fls64(check_mask))
5499+
check_fail = true;
5500+
} else {
5501+
if ((new_mask | check_mask) != new_mask &&
5502+
fls64(new_mask) < fls64(check_mask))
5503+
check_fail = true;
5504+
}
5505+
5506+
if (check_fail) {
5507+
pr_info("The two events 0x%llx and 0x%llx may not be "
5508+
"fully scheduled under some circumstances as "
5509+
"%s.\n",
5510+
c1->code, c2->code, dyn_constr_type_name[type]);
5511+
}
5512+
}
5513+
}
5514+
}
5515+
5516+
static void intel_pmu_check_dyn_constr(struct pmu *pmu,
5517+
struct event_constraint *constr,
5518+
u64 cntr_mask)
5519+
{
5520+
enum dyn_constr_type i;
5521+
u64 mask;
5522+
5523+
for (i = DYN_CONSTR_NONE; i < DYN_CONSTR_MAX; i++) {
5524+
mask = 0;
5525+
switch (i) {
5526+
case DYN_CONSTR_NONE:
5527+
mask = cntr_mask;
5528+
break;
5529+
case DYN_CONSTR_BR_CNTR:
5530+
if (x86_pmu.flags & PMU_FL_BR_CNTR)
5531+
mask = x86_pmu.lbr_counters;
5532+
break;
5533+
case DYN_CONSTR_ACR_CNTR:
5534+
mask = hybrid(pmu, acr_cntr_mask64) & GENMASK_ULL(INTEL_PMC_MAX_GENERIC - 1, 0);
5535+
break;
5536+
case DYN_CONSTR_ACR_CAUSE:
5537+
if (hybrid(pmu, acr_cntr_mask64) == hybrid(pmu, acr_cause_mask64))
5538+
continue;
5539+
mask = hybrid(pmu, acr_cause_mask64) & GENMASK_ULL(INTEL_PMC_MAX_GENERIC - 1, 0);
5540+
break;
5541+
default:
5542+
pr_warn("Unsupported dynamic constraint type %d\n", i);
5543+
}
5544+
5545+
if (mask)
5546+
__intel_pmu_check_dyn_constr(constr, i, mask);
5547+
}
5548+
}
5549+
5550+
static void intel_pmu_check_event_constraints_all(struct pmu *pmu)
5551+
{
5552+
struct event_constraint *event_constraints = hybrid(pmu, event_constraints);
5553+
struct event_constraint *pebs_constraints = hybrid(pmu, pebs_constraints);
5554+
u64 cntr_mask = hybrid(pmu, cntr_mask64);
5555+
u64 fixed_cntr_mask = hybrid(pmu, fixed_cntr_mask64);
5556+
u64 intel_ctrl = hybrid(pmu, intel_ctrl);
5557+
5558+
intel_pmu_check_event_constraints(event_constraints, cntr_mask,
5559+
fixed_cntr_mask, intel_ctrl);
5560+
5561+
if (event_constraints)
5562+
intel_pmu_check_dyn_constr(pmu, event_constraints, cntr_mask);
5563+
5564+
if (pebs_constraints)
5565+
intel_pmu_check_dyn_constr(pmu, pebs_constraints, cntr_mask);
5566+
}
5567+
54235568
static void intel_pmu_check_extra_regs(struct extra_reg *extra_regs);
54245569

54255570
static inline bool intel_pmu_broken_perf_cap(void)
@@ -5537,10 +5682,7 @@ static void intel_pmu_check_hybrid_pmus(struct x86_hybrid_pmu *pmu)
55375682
else
55385683
pmu->intel_ctrl &= ~GLOBAL_CTRL_EN_PERF_METRICS;
55395684

5540-
intel_pmu_check_event_constraints(pmu->event_constraints,
5541-
pmu->cntr_mask64,
5542-
pmu->fixed_cntr_mask64,
5543-
pmu->intel_ctrl);
5685+
intel_pmu_check_event_constraints_all(&pmu->pmu);
55445686

55455687
intel_pmu_check_extra_regs(pmu->extra_regs);
55465688
}
@@ -7963,10 +8105,8 @@ __init int intel_pmu_init(void)
79638105
if (x86_pmu.intel_cap.anythread_deprecated)
79648106
x86_pmu.format_attrs = intel_arch_formats_attr;
79658107

7966-
intel_pmu_check_event_constraints(x86_pmu.event_constraints,
7967-
x86_pmu.cntr_mask64,
7968-
x86_pmu.fixed_cntr_mask64,
7969-
x86_pmu.intel_ctrl);
8108+
intel_pmu_check_event_constraints_all(NULL);
8109+
79708110
/*
79718111
* Access LBR MSR may cause #GP under certain circumstances.
79728112
* Check all LBR MSR here.

0 commit comments

Comments
 (0)