From: Dapeng Mi dapeng1.mi@linux.intel.com
[ Upstream commit 2676dbf9f4fb7f6739d1207c0f1deaf63124642a ]
ICL_FIXED_0_ADAPTIVE is missed to be added into INTEL_FIXED_BITS_MASK, add it.
With help of this new INTEL_FIXED_BITS_MASK, intel_pmu_enable_fixed() can be optimized. The old fixed counter control bits can be unconditionally cleared with INTEL_FIXED_BITS_MASK and then set new control bits base on new configuration.
Signed-off-by: Dapeng Mi dapeng1.mi@linux.intel.com Signed-off-by: Peter Zijlstra (Intel) peterz@infradead.org Reviewed-by: Kan Liang kan.liang@linux.intel.com Tested-by: Yi Lai yi1.lai@intel.com Link: https://lore.kernel.org/r/20250820023032.17128-7-dapeng1.mi@linux.intel.com Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
**YES**
This commit should definitely be backported to stable kernel trees.
## Extensive Analysis
### Nature of the Bug
This commit fixes a **real and significant bug** in the Intel PMU (Performance Monitoring Unit) fixed counter handling. The bug has existed since kernel v6.5 when `INTEL_FIXED_BITS_MASK` was introduced in commit 10d95a317ec12 (May 2023).
### Technical Details of the Bug
**In arch/x86/include/asm/perf_event.h:18-35:**
The original `INTEL_FIXED_BITS_MASK` was defined as `0xFULL` (binary 1111), covering only bits 0-3: ```c -#define INTEL_FIXED_BITS_MASK 0xFULL ```
However, the mask was missing `ICL_FIXED_0_ADAPTIVE` (bit 32), which has existed since 2019 for Ice Lake adaptive PEBS v4 support (commit c22497f5838c2). The fix correctly includes all relevant bits: ```c +#define INTEL_FIXED_BITS_MASK \ + (INTEL_FIXED_0_KERNEL | INTEL_FIXED_0_USER | \ + INTEL_FIXED_0_ANYTHREAD | INTEL_FIXED_0_ENABLE_PMI | \ + ICL_FIXED_0_ADAPTIVE) ```
**In arch/x86/events/intel/core.c:2844-2896:**
The bug manifests in `intel_pmu_enable_fixed()` at lines 2888-2895. When reconfiguring a fixed counter:
**Before the fix:** - Line 2888 creates `mask` with only bits 0-3 - Lines 2890-2893 conditionally add `ICL_FIXED_0_ADAPTIVE` to both `bits` and `mask` only if PEBS is enabled - Line 2895 clears bits using the incomplete mask - **Problem:** If a counter previously had `ICL_FIXED_0_ADAPTIVE` set but the new configuration doesn't need it, the bit won't be cleared because it's not in the mask
**After the fix:** - The mask always includes `ICL_FIXED_0_ADAPTIVE` - Line 2890 unconditionally clears all relevant bits (including `ICL_FIXED_0_ADAPTIVE`) - Lines 2890-2891 set `ICL_FIXED_0_ADAPTIVE` only when needed - The code is cleaner and bug-free
### Impact Analysis
1. **Affected Hardware:** Intel Ice Lake (ICL) and newer processors with adaptive PEBS support
2. **Symptom:** The `ICL_FIXED_0_ADAPTIVE` bit can remain incorrectly set after reconfiguring performance counters, causing: - Incorrect PMU behavior - Adaptive PEBS being enabled when it should be disabled - Performance monitoring data corruption
3. **Severity:** This bug was explicitly identified as **"Bug #3"** in KVM commit 9e985cbf2942a (March 2024), which stated:
"Bug #3 is in perf. intel_pmu_disable_fixed() doesn't clear the
upper bits either, i.e. leaves ICL_FIXED_0_ADAPTIVE set, and intel_pmu_enable_fixed() effectively doesn't clear ICL_FIXED_0_ADAPTIVE either. I.e. perf _always_ enables ADAPTIVE counters, regardless of what KVM requests."
4. **Security Context:** KVM had to **completely disable adaptive PEBS support** (with a Cc: stable tag) as a workaround for multiple bugs, including this one. The KVM commit mentioned potential security implications including LBR leaks.
### Why This Should Be Backported
1. ✅ **Fixes an important bug** affecting Intel processors since 2019 (Ice Lake) 2. ✅ **Small, contained change** - only modifies a constant definition and simplifies existing code 3. ✅ **Low regression risk** - the change makes the mask complete and correct 4. ✅ **Well-reviewed and tested** - Reviewed-by: Kan Liang, Tested-by: Yi Lai (both from Intel) 5. ✅ **Addresses known issue** - this was explicitly identified in a previous security-related commit 6. ✅ **Affects both enable and disable paths** - also fixes `intel_pmu_disable_fixed()` at line 2562 which uses the same mask 7. ✅ **No architectural changes** - pure bug fix 8. ✅ **Stable since v6.5** - applies cleanly to all kernels since the mask was introduced
### Dependencies
This fix requires that `INTEL_FIXED_BITS_MASK` exists, which was introduced in kernel v6.5. The fix should be backported to all stable trees from **v6.5 onwards**.
### Conclusion
This is a textbook example of a commit suitable for stable backporting: it fixes a real bug with clear symptoms, is small and low-risk, and has been properly reviewed and tested. The fact that it addresses an issue severe enough to warrant disabling an entire feature in KVM further underscores its importance.
arch/x86/events/intel/core.c | 10 +++------- arch/x86/include/asm/perf_event.h | 6 +++++- arch/x86/kvm/pmu.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index c2fb729c270ec..af47d266f8064 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -2845,8 +2845,8 @@ static void intel_pmu_enable_fixed(struct perf_event *event) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; - u64 mask, bits = 0; int idx = hwc->idx; + u64 bits = 0;
if (is_topdown_idx(idx)) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); @@ -2885,14 +2885,10 @@ static void intel_pmu_enable_fixed(struct perf_event *event)
idx -= INTEL_PMC_IDX_FIXED; bits = intel_fixed_bits_by_idx(idx, bits); - mask = intel_fixed_bits_by_idx(idx, INTEL_FIXED_BITS_MASK); - - if (x86_pmu.intel_cap.pebs_baseline && event->attr.precise_ip) { + if (x86_pmu.intel_cap.pebs_baseline && event->attr.precise_ip) bits |= intel_fixed_bits_by_idx(idx, ICL_FIXED_0_ADAPTIVE); - mask |= intel_fixed_bits_by_idx(idx, ICL_FIXED_0_ADAPTIVE); - }
- cpuc->fixed_ctrl_val &= ~mask; + cpuc->fixed_ctrl_val &= ~intel_fixed_bits_by_idx(idx, INTEL_FIXED_BITS_MASK); cpuc->fixed_ctrl_val |= bits; }
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index 70d1d94aca7e6..ee943bd1595af 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -35,7 +35,6 @@ #define ARCH_PERFMON_EVENTSEL_EQ (1ULL << 36) #define ARCH_PERFMON_EVENTSEL_UMASK2 (0xFFULL << 40)
-#define INTEL_FIXED_BITS_MASK 0xFULL #define INTEL_FIXED_BITS_STRIDE 4 #define INTEL_FIXED_0_KERNEL (1ULL << 0) #define INTEL_FIXED_0_USER (1ULL << 1) @@ -48,6 +47,11 @@ #define ICL_EVENTSEL_ADAPTIVE (1ULL << 34) #define ICL_FIXED_0_ADAPTIVE (1ULL << 32)
+#define INTEL_FIXED_BITS_MASK \ + (INTEL_FIXED_0_KERNEL | INTEL_FIXED_0_USER | \ + INTEL_FIXED_0_ANYTHREAD | INTEL_FIXED_0_ENABLE_PMI | \ + ICL_FIXED_0_ADAPTIVE) + #define intel_fixed_bits_by_idx(_idx, _bits) \ ((_bits) << ((_idx) * INTEL_FIXED_BITS_STRIDE))
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h index ad89d0bd60058..103604c4b33b5 100644 --- a/arch/x86/kvm/pmu.h +++ b/arch/x86/kvm/pmu.h @@ -13,7 +13,7 @@ #define MSR_IA32_MISC_ENABLE_PMU_RO_MASK (MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL | \ MSR_IA32_MISC_ENABLE_BTS_UNAVAIL)
-/* retrieve the 4 bits for EN and PMI out of IA32_FIXED_CTR_CTRL */ +/* retrieve a fixed counter bits out of IA32_FIXED_CTR_CTRL */ #define fixed_ctrl_field(ctrl_reg, idx) \ (((ctrl_reg) >> ((idx) * INTEL_FIXED_BITS_STRIDE)) & INTEL_FIXED_BITS_MASK)