mirror of
https://github.com/torvalds/linux.git
synced 2026-09-13 15:40:03 +02:00
perf: RISC-V: store available counter mask as bitmap
The available-counter mask was a single unsigned long, but iteration
uses RISCV_MAX_COUNTERS, which is 64. On RV32 that reads past the object.
Filling with an unsigned-long bit at index 32 and above is also wrong.
Use DECLARE_BITMAP and set_bit/bitmap helpers. Walk each bitmap word
into CFG_MATCH when checking events, when allocating an index, and when
stopping all counters. Set the counter base to i times BITS_PER_LONG.
Share the CFG_MATCH ecall through a small helper so the 32-bit argument
split is not duplicated. On qemu-system-riscv32 the probe bitmap has bits
above XLEN set, so the first word alone is not enough.
Fixes: e999143459 ("RISC-V: Add perf platform driver based on SBI PMU extension")
Assisted-by: DeepSeek:deepseek-v3
Signed-off-by: Xixin Liu <liuxixin@kylinos.cn>
Link: https://patch.msgid.link/prpmask02cmap.v2.1786434000.git.liuxixin@kylinos.cn
Cc: stable@kernel.org
[pjw@kernel.org: updated to apply; fixed checkpatch.pl issues]
Signed-off-by: Paul Walmsley <pjw@kernel.org>
This commit is contained in:
parent
6693171c8c
commit
6809da6e9c
|
|
@ -110,8 +110,9 @@ static void pmu_legacy_init(struct riscv_pmu *pmu)
|
|||
{
|
||||
pr_info("Legacy PMU implementation is available\n");
|
||||
|
||||
pmu->cmask = BIT(RISCV_PMU_LEGACY_CYCLE) |
|
||||
BIT(RISCV_PMU_LEGACY_INSTRET);
|
||||
bitmap_zero(pmu->cmask, RISCV_MAX_COUNTERS);
|
||||
set_bit(RISCV_PMU_LEGACY_CYCLE, pmu->cmask);
|
||||
set_bit(RISCV_PMU_LEGACY_INSTRET, pmu->cmask);
|
||||
pmu->ctr_start = pmu_legacy_ctr_start;
|
||||
pmu->ctr_stop = NULL;
|
||||
pmu->event_map = pmu_legacy_event_map;
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ static unsigned int riscv_pmu_irq_mask;
|
|||
static unsigned int riscv_pmu_irq;
|
||||
|
||||
/* Cache the available counters in a bitmask */
|
||||
static unsigned long cmask;
|
||||
static DECLARE_BITMAP(cmask, RISCV_MAX_COUNTERS);
|
||||
|
||||
static int pmu_event_find_cache(u64 config);
|
||||
struct sbi_pmu_event_data {
|
||||
|
|
@ -359,16 +359,38 @@ static int pmu_sbi_check_event_info(void)
|
|||
return result;
|
||||
}
|
||||
|
||||
static struct sbiret pmu_sbi_ctr_cfg_match(unsigned long cbase,
|
||||
unsigned long ctr_mask,
|
||||
unsigned long cflags,
|
||||
unsigned long event_idx,
|
||||
u64 config)
|
||||
{
|
||||
#if defined(CONFIG_32BIT)
|
||||
return sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase,
|
||||
ctr_mask, cflags, event_idx, config, config >> 32);
|
||||
#else
|
||||
return sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase,
|
||||
ctr_mask, cflags, event_idx, config, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void pmu_sbi_check_event(struct sbi_pmu_event_data *edata)
|
||||
{
|
||||
struct sbiret ret;
|
||||
struct sbiret ret = { .error = SBI_ERR_NOT_SUPPORTED };
|
||||
int i;
|
||||
|
||||
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) {
|
||||
for (i = 0; i < BITS_TO_LONGS(RISCV_MAX_COUNTERS); i++) {
|
||||
if (!cmask[i])
|
||||
continue;
|
||||
ret = pmu_sbi_ctr_cfg_match(i * BITS_PER_LONG, cmask[i], 0,
|
||||
edata->event_idx, 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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (ret.error == SBI_ERR_NOT_SUPPORTED) {
|
||||
/* This event cannot be monitored by any counter */
|
||||
edata->event_idx = -ENOENT;
|
||||
}
|
||||
|
|
@ -488,10 +510,10 @@ int riscv_pmu_get_hpm_info(u32 *hw_ctr_width, u32 *num_hw_ctr)
|
|||
union sbi_pmu_ctr_info *info;
|
||||
u32 hpm_width = 0, hpm_count = 0;
|
||||
|
||||
if (!cmask)
|
||||
if (bitmap_empty(cmask, RISCV_MAX_COUNTERS))
|
||||
return -EINVAL;
|
||||
|
||||
for_each_set_bit(i, &cmask, RISCV_MAX_COUNTERS) {
|
||||
for_each_set_bit(i, cmask, RISCV_MAX_COUNTERS) {
|
||||
info = &pmu_ctr_list[i];
|
||||
if (!info)
|
||||
continue;
|
||||
|
|
@ -540,8 +562,8 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event)
|
|||
struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
|
||||
struct cpu_hw_events *cpuc = this_cpu_ptr(rvpmu->hw_events);
|
||||
struct sbiret ret;
|
||||
int idx;
|
||||
uint64_t cbase = 0, cmask = rvpmu->cmask;
|
||||
int idx, i;
|
||||
u64 cbase = 0, cmask = 0;
|
||||
unsigned long cflags = 0;
|
||||
|
||||
cflags = pmu_sbi_get_filter_flags(event);
|
||||
|
|
@ -562,14 +584,21 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event)
|
|||
}
|
||||
|
||||
/* retrieve the available counter index */
|
||||
#if defined(CONFIG_32BIT)
|
||||
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase,
|
||||
cmask, cflags, hwc->event_base, hwc->config,
|
||||
hwc->config >> 32);
|
||||
#else
|
||||
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase,
|
||||
cmask, cflags, hwc->event_base, hwc->config, 0);
|
||||
#endif
|
||||
if (cmask) {
|
||||
ret = pmu_sbi_ctr_cfg_match(cbase, cmask, cflags, hwc->event_base,
|
||||
hwc->config);
|
||||
} else {
|
||||
ret.error = SBI_ERR_NOT_SUPPORTED;
|
||||
for (i = 0; i < BITS_TO_LONGS(RISCV_MAX_COUNTERS); i++) {
|
||||
if (!rvpmu->cmask[i])
|
||||
continue;
|
||||
cbase = i * BITS_PER_LONG;
|
||||
ret = pmu_sbi_ctr_cfg_match(cbase, rvpmu->cmask[i], cflags,
|
||||
hwc->event_base, hwc->config);
|
||||
if (!ret.error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret.error) {
|
||||
pr_debug("Not able to find a counter for event %lx config %llx\n",
|
||||
hwc->event_base, hwc->config);
|
||||
|
|
@ -577,7 +606,7 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event)
|
|||
}
|
||||
|
||||
idx = ret.value;
|
||||
if (!test_bit(idx, &rvpmu->cmask) || !pmu_ctr_list[idx].value)
|
||||
if (!test_bit(idx, rvpmu->cmask) || !pmu_ctr_list[idx].value)
|
||||
return -ENOENT;
|
||||
|
||||
/* Additional sanity check for the counter id */
|
||||
|
|
@ -881,7 +910,7 @@ static int pmu_sbi_get_ctrinfo(int nctr, unsigned long *mask)
|
|||
/* The logical counter ids are not expected to be contiguous */
|
||||
continue;
|
||||
|
||||
*mask |= BIT(i);
|
||||
set_bit(i, mask);
|
||||
|
||||
cinfo.value = ret.value;
|
||||
if (cinfo.type == SBI_PMU_CTR_TYPE_FW)
|
||||
|
|
@ -898,12 +927,19 @@ static int pmu_sbi_get_ctrinfo(int nctr, unsigned long *mask)
|
|||
|
||||
static inline void pmu_sbi_stop_all(struct riscv_pmu *pmu)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* No need to check the error because we are disabling all the counters
|
||||
* which may include counters that are not enabled yet.
|
||||
*/
|
||||
sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP,
|
||||
0, pmu->cmask, SBI_PMU_STOP_FLAG_RESET, 0, 0, 0);
|
||||
for (i = 0; i < BITS_TO_LONGS(RISCV_MAX_COUNTERS); i++) {
|
||||
if (!pmu->cmask[i])
|
||||
continue;
|
||||
sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP,
|
||||
i * BITS_PER_LONG, pmu->cmask[i],
|
||||
SBI_PMU_STOP_FLAG_RESET, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void pmu_sbi_stop_hw_ctrs(struct riscv_pmu *pmu)
|
||||
|
|
@ -1451,7 +1487,7 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* cache all the information about counters now */
|
||||
if (pmu_sbi_get_ctrinfo(num_counters, &cmask))
|
||||
if (pmu_sbi_get_ctrinfo(num_counters, cmask))
|
||||
goto out_free;
|
||||
|
||||
ret = pmu_sbi_setup_irqs(pmu, pdev);
|
||||
|
|
@ -1464,7 +1500,7 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
|
|||
|
||||
pmu->pmu.attr_groups = riscv_pmu_attr_groups;
|
||||
pmu->pmu.parent = &pdev->dev;
|
||||
pmu->cmask = cmask;
|
||||
bitmap_copy(pmu->cmask, cmask, RISCV_MAX_COUNTERS);
|
||||
pmu->ctr_start = pmu_sbi_ctr_start;
|
||||
pmu->ctr_stop = pmu_sbi_ctr_stop;
|
||||
pmu->event_map = pmu_sbi_event_map;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ struct riscv_pmu {
|
|||
|
||||
irqreturn_t (*handle_irq)(int irq_num, void *dev);
|
||||
|
||||
unsigned long cmask;
|
||||
DECLARE_BITMAP(cmask, RISCV_MAX_COUNTERS);
|
||||
u64 (*ctr_read)(struct perf_event *event);
|
||||
int (*ctr_get_idx)(struct perf_event *event);
|
||||
int (*ctr_get_width)(int idx);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user