lib/sbi_pmu: Don't fallback to fixed counters when sscofpmf && !smcntrpmf

Currently when searching for a hardware counter for an event, if no
programmable counter is available, the code falls back to using a fixed
counter (mcycle/minstret) if one matches the event.

However the fallback is incorrect when sscofpmf is present but
smcntrpmf is not. That's because with sscofpmf, programmable counters
support mode filtering, but the fixed counters do not (without
smcntrpmf). Even if the caller didn't configure mode filtering, by
default programmable counters don't count M mode when sscofpmf is
present, whereas mcycle/minstret do.

Fix the logic to not fallback to a fixed counter if sscofpmf is present
but smcntrpmf is not.

Fixes: 0c304b6619 ("lib: sbi: Allow programmable counters to monitor cycle/instret events")
Signed-off-by: Michael Ellerman <mpe@kernel.org>
Reviewed-by: Anup Patel <anup@brainfault.org>
Link: https://lore.kernel.org/r/20260324-mcycle-fix-v1-1-1444e9fe5c32@kernel.org
Signed-off-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
Michael Ellerman
2026-03-24 23:29:20 +11:00
committed by Anup Patel
parent 3d8f1f3864
commit 65bb705f7b
+12 -5
View File
@@ -830,13 +830,20 @@ static int pmu_ctr_find_hw(struct sbi_pmu_hart_state *phs,
if (ctr_idx == SBI_ENOTSUPP) { if (ctr_idx == SBI_ENOTSUPP) {
/** /**
* We can't find any programmable counters for cycle/instret. * We can't find a programmable counter, see if we can use a
* Return the fixed counter as they are mandatory anyways. * fixed counter instead if one was found for this event.
*
* If sscofpmf is present but smcntrpmf is not, we can't
* fallback to a fixed counter, because the fixed counter
* doesn't support filtering whereas a programmable counter
* would.
*/ */
if (fixed_ctr >= 0) if (fixed_ctr < 0 ||
return pmu_fixed_ctr_update_inhibit_bits(fixed_ctr, flags); ((sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF) &&
else !sbi_hart_has_extension(scratch, SBI_HART_EXT_SMCNTRPMF))))
return SBI_EFAIL; return SBI_EFAIL;
return pmu_fixed_ctr_update_inhibit_bits(fixed_ctr, flags);
} }
ret = pmu_update_hw_mhpmevent(temp, ctr_idx, flags, event_idx, data); ret = pmu_update_hw_mhpmevent(temp, ctr_idx, flags, event_idx, data);