mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
perf: RISC-V: Check standard event availability
The RISC-V SBI PMU specification defines several standard hardware and
cache events. Currently, all of these events are exposed to userspace,
even when not actually implemented. They appear in the `perf list`
output, and commands like `perf stat` try to use them.
This is more than just a cosmetic issue, because the PMU driver's .add
function fails for these events, which causes pmu_groups_sched_in() to
prematurely stop scheduling in other (possibly valid) hardware events.
Add logic to check which events are supported by the hardware (i.e. can
be mapped to some counter), so only usable events are reported to
userspace. Since the kernel does not know the mapping between events and
possible counters, this check must happen during boot, when no counters
are in use. Make the check asynchronous to minimize impact on boot time.
Fixes: e999143459
("RISC-V: Add perf platform driver based on SBI PMU extension")
Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
Reviewed-by: Atish Patra <atishp@rivosinc.com>
Tested-by: Atish Patra <atishp@rivosinc.com>
Signed-off-by: Atish Patra <atishp@rivosinc.com>
Link: https://lore.kernel.org/r/20240628-misc_perf_fixes-v4-3-e01cfddcf035@rivosinc.com
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
This commit is contained in:
parent
7dd646cf74
commit
16d3b1af09
@ -327,7 +327,7 @@ static long kvm_pmu_create_perf_event(struct kvm_pmc *pmc, struct perf_event_att
|
||||
|
||||
event = perf_event_create_kernel_counter(attr, -1, current, kvm_riscv_pmu_overflow, pmc);
|
||||
if (IS_ERR(event)) {
|
||||
pr_err("kvm pmu event creation failed for eidx %lx: %ld\n", eidx, PTR_ERR(event));
|
||||
pr_debug("kvm pmu event creation failed for eidx %lx: %ld\n", eidx, PTR_ERR(event));
|
||||
return PTR_ERR(event);
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/sched/clock.h>
|
||||
#include <linux/soc/andes/irq.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <asm/errata_list.h>
|
||||
#include <asm/sbi.h>
|
||||
@ -114,7 +115,7 @@ struct sbi_pmu_event_data {
|
||||
};
|
||||
};
|
||||
|
||||
static const struct sbi_pmu_event_data pmu_hw_event_map[] = {
|
||||
static struct sbi_pmu_event_data pmu_hw_event_map[] = {
|
||||
[PERF_COUNT_HW_CPU_CYCLES] = {.hw_gen_event = {
|
||||
SBI_PMU_HW_CPU_CYCLES,
|
||||
SBI_PMU_EVENT_TYPE_HW, 0}},
|
||||
@ -148,7 +149,7 @@ static const struct sbi_pmu_event_data pmu_hw_event_map[] = {
|
||||
};
|
||||
|
||||
#define C(x) PERF_COUNT_HW_CACHE_##x
|
||||
static const struct sbi_pmu_event_data pmu_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
|
||||
static struct sbi_pmu_event_data pmu_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
|
||||
[C(L1D)] = {
|
||||
@ -293,6 +294,34 @@ static const struct sbi_pmu_event_data pmu_cache_event_map[PERF_COUNT_HW_CACHE_M
|
||||
},
|
||||
};
|
||||
|
||||
static void pmu_sbi_check_event(struct sbi_pmu_event_data *edata)
|
||||
{
|
||||
struct sbiret ret;
|
||||
|
||||
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH,
|
||||
0, cmask, 0, edata->event_idx, 0, 0);
|
||||
if (!ret.error) {
|
||||
sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP,
|
||||
ret.value, 0x1, SBI_PMU_STOP_FLAG_RESET, 0, 0, 0);
|
||||
} else if (ret.error == SBI_ERR_NOT_SUPPORTED) {
|
||||
/* This event cannot be monitored by any counter */
|
||||
edata->event_idx = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void pmu_sbi_check_std_events(struct work_struct *work)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(pmu_hw_event_map); i++)
|
||||
pmu_sbi_check_event(&pmu_hw_event_map[i]);
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(pmu_cache_event_map); i++)
|
||||
for (int j = 0; j < ARRAY_SIZE(pmu_cache_event_map[i]); j++)
|
||||
for (int k = 0; k < ARRAY_SIZE(pmu_cache_event_map[i][j]); k++)
|
||||
pmu_sbi_check_event(&pmu_cache_event_map[i][j][k]);
|
||||
}
|
||||
|
||||
static DECLARE_WORK(check_std_events_work, pmu_sbi_check_std_events);
|
||||
|
||||
static int pmu_sbi_ctr_get_width(int idx)
|
||||
{
|
||||
return pmu_ctr_list[idx].width;
|
||||
@ -478,6 +507,12 @@ static int pmu_sbi_event_map(struct perf_event *event, u64 *econfig)
|
||||
u64 raw_config_val;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Ensure we are finished checking standard hardware events for
|
||||
* validity before allowing userspace to configure any events.
|
||||
*/
|
||||
flush_work(&check_std_events_work);
|
||||
|
||||
switch (type) {
|
||||
case PERF_TYPE_HARDWARE:
|
||||
if (config >= PERF_COUNT_HW_MAX)
|
||||
@ -1359,6 +1394,9 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto out_unregister;
|
||||
|
||||
/* Asynchronously check which standard events are available */
|
||||
schedule_work(&check_std_events_work);
|
||||
|
||||
return 0;
|
||||
|
||||
out_unregister:
|
||||
|
Loading…
Reference in New Issue
Block a user