* [PATCH v1 0/2] perf: RISC-V: fix SBI PMU masks for RV32
@ 2026-08-07 8:50 Xixin Liu
2026-08-07 8:50 ` [PATCH v1 1/2] perf: RISC-V: use BIT_ULL for u64 overflow masks Xixin Liu
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Xixin Liu @ 2026-08-07 8:50 UTC (permalink / raw)
To: linux-riscv
Cc: atish.patra, anup, will, mark.rutland, pjw, palmer, aou, alex,
linux-arm-kernel, linux-perf-users, linux-kernel, liuxixin
Hi,
This series fixes two RV32 mask-width issues in the SBI PMU driver:
1) Overflow status / restart tracking is u64 but used BIT(), which is
an unsigned long shift and breaks for indices >= 32 on RV32
2) The available-counter mask was a single unsigned long while
iteration uses RISCV_MAX_COUNTERS (64), so RV32 can read past the
object; store it as a DECLARE_BITMAP
Patches are independent. Please review.
Thanks,
Xixin Liu
---
Xixin Liu (2):
perf: RISC-V: use BIT_ULL for u64 overflow masks
perf: RISC-V: store available counter mask as bitmap
drivers/perf/riscv_pmu_legacy.c | 5 +++--
drivers/perf/riscv_pmu_sbi.c | 35 ++++++++++++++++++++++------------
include/linux/perf/riscv_pmu.h | 2 +-
3 files changed, 27 insertions(+), 15 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1 1/2] perf: RISC-V: use BIT_ULL for u64 overflow masks
2026-08-07 8:50 [PATCH v1 0/2] perf: RISC-V: fix SBI PMU masks for RV32 Xixin Liu
@ 2026-08-07 8:50 ` Xixin Liu
2026-08-08 0:46 ` Paul Walmsley
2026-08-07 8:50 ` [PATCH v1 2/2] perf: RISC-V: store available counter mask as bitmap Xixin Liu
2026-08-18 9:10 ` [PATCH v2 0/2] perf: RISC-V: fix SBI PMU masks for RV32 Xixin Liu
2 siblings, 1 reply; 9+ messages in thread
From: Xixin Liu @ 2026-08-07 8:50 UTC (permalink / raw)
To: linux-riscv
Cc: atish.patra, anup, will, mark.rutland, pjw, palmer, aou, alex,
linux-arm-kernel, linux-perf-users, linux-kernel, liuxixin
Overflow status and restart masks are u64, but bits were built with
BIT(). On RV32 that is an unsigned long shift, so indices >= 32 truncate
or wrap and corrupt the mask.
Use BIT_ULL() for those u64 bitops.
Signed-off-by: Xixin Liu <liuxixin@kylinos.cn>
---
drivers/perf/riscv_pmu_sbi.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index dfc886dee..93a31d5d5 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -1002,7 +1002,7 @@ static inline void pmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_hw_
struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr;
for_each_set_bit(idx, cpu_hw_evt->used_hw_ctrs, RISCV_MAX_COUNTERS) {
- if (ctr_ovf_mask & BIT(idx)) {
+ if (ctr_ovf_mask & BIT_ULL(idx)) {
event = cpu_hw_evt->events[idx];
hwc = &event->hw;
max_period = riscv_pmu_ctr_get_width_mask(event);
@@ -1109,14 +1109,14 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)
hidx = info->csr - CSR_CYCLE;
/* check if the corresponding bit is set in scountovf or overflow mask in shmem */
- if (!(overflow & BIT(hidx)))
+ if (!(overflow & BIT_ULL(hidx)))
continue;
/*
* Keep a track of overflowed counters so that they can be started
* with updated initial value.
*/
- overflowed_ctrs |= BIT(lidx);
+ overflowed_ctrs |= BIT_ULL(lidx);
hw_evt = &event->hw;
/* Update the event states here so that we know the state while reading */
hw_evt->state |= PERF_HES_STOPPED;
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v1 2/2] perf: RISC-V: store available counter mask as bitmap
2026-08-07 8:50 [PATCH v1 0/2] perf: RISC-V: fix SBI PMU masks for RV32 Xixin Liu
2026-08-07 8:50 ` [PATCH v1 1/2] perf: RISC-V: use BIT_ULL for u64 overflow masks Xixin Liu
@ 2026-08-07 8:50 ` Xixin Liu
2026-08-08 0:32 ` Paul Walmsley
2026-08-18 9:10 ` [PATCH v2 0/2] perf: RISC-V: fix SBI PMU masks for RV32 Xixin Liu
2 siblings, 1 reply; 9+ messages in thread
From: Xixin Liu @ 2026-08-07 8:50 UTC (permalink / raw)
To: linux-riscv
Cc: atish.patra, anup, will, mark.rutland, pjw, palmer, aou, alex,
linux-arm-kernel, linux-perf-users, linux-kernel, liuxixin
The available-counter mask was a single unsigned long, but iteration
uses RISCV_MAX_COUNTERS (64). On RV32 that reads past the object. Filling
with BIT(i) is also wrong for i >= 32.
Use DECLARE_BITMAP, set_bit/bitmap helpers, and stop counters one word
at a time.
Signed-off-by: Xixin Liu <liuxixin@kylinos.cn>
---
drivers/perf/riscv_pmu_legacy.c | 5 +++--
drivers/perf/riscv_pmu_sbi.c | 29 ++++++++++++++++++-----------
include/linux/perf/riscv_pmu.h | 2 +-
3 files changed, 22 insertions(+), 14 deletions(-)
diff --git a/drivers/perf/riscv_pmu_legacy.c b/drivers/perf/riscv_pmu_legacy.c
index 4d6461d6a..1b8e4789c 100644
--- a/drivers/perf/riscv_pmu_legacy.c
+++ b/drivers/perf/riscv_pmu_legacy.c
@@ -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;
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index d6c66e375..c913d73f8 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -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 {
@@ -364,7 +364,7 @@ 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);
+ 0, cmask[0], 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);
@@ -488,10 +488,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;
@@ -541,7 +541,7 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event)
struct cpu_hw_events *cpuc = this_cpu_ptr(rvpmu->hw_events);
struct sbiret ret;
int idx;
- uint64_t cbase = 0, cmask = rvpmu->cmask;
+ uint64_t cbase = 0, cmask = rvpmu->cmask[0];
unsigned long cflags = 0;
cflags = pmu_sbi_get_filter_flags(event);
@@ -577,7 +577,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 +881,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 +898,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)
@@ -1442,7 +1449,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);
@@ -1454,7 +1461,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;
diff --git a/include/linux/perf/riscv_pmu.h b/include/linux/perf/riscv_pmu.h
index f82a28040..ecaa40370 100644
--- a/include/linux/perf/riscv_pmu.h
+++ b/include/linux/perf/riscv_pmu.h
@@ -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);
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v1 2/2] perf: RISC-V: store available counter mask as bitmap
2026-08-07 8:50 ` [PATCH v1 2/2] perf: RISC-V: store available counter mask as bitmap Xixin Liu
@ 2026-08-08 0:32 ` Paul Walmsley
2026-08-18 9:15 ` Xixin Liu
0 siblings, 1 reply; 9+ messages in thread
From: Paul Walmsley @ 2026-08-08 0:32 UTC (permalink / raw)
To: Xixin Liu
Cc: linux-riscv, atish.patra, anup, will, mark.rutland, pjw, palmer,
aou, alex, linux-arm-kernel, linux-perf-users, linux-kernel
Hi,
On Fri, 7 Aug 2026, Xixin Liu wrote:
> The available-counter mask was a single unsigned long, but iteration
> uses RISCV_MAX_COUNTERS (64). On RV32 that reads past the object. Filling
> with BIT(i) is also wrong for i >= 32.
>
> Use DECLARE_BITMAP, set_bit/bitmap helpers, and stop counters one word
> at a time.
>
> Signed-off-by: Xixin Liu <liuxixin@kylinos.cn>
This patch contains several array indexes to element 0 of the cmask
bitmap, but that element is going to be XLEN bits wide. Does that
actually work on RV32 systems?
- Paul
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v1 1/2] perf: RISC-V: use BIT_ULL for u64 overflow masks
2026-08-07 8:50 ` [PATCH v1 1/2] perf: RISC-V: use BIT_ULL for u64 overflow masks Xixin Liu
@ 2026-08-08 0:46 ` Paul Walmsley
0 siblings, 0 replies; 9+ messages in thread
From: Paul Walmsley @ 2026-08-08 0:46 UTC (permalink / raw)
To: Xixin Liu
Cc: linux-riscv, atish.patra, anup, will, mark.rutland, pjw, palmer,
aou, alex, linux-arm-kernel, linux-perf-users, linux-kernel
Hi,
On Fri, 7 Aug 2026, Xixin Liu wrote:
> Overflow status and restart masks are u64, but bits were built with
> BIT(). On RV32 that is an unsigned long shift, so indices >= 32 truncate
> or wrap and corrupt the mask.
>
> Use BIT_ULL() for those u64 bitops.
>
> Signed-off-by: Xixin Liu <liuxixin@kylinos.cn>
Thanks for the patch, but, same comments as on
https://lore.kernel.org/linux-riscv/9109b689-1145-c714-ebe0-ec368f242a78@kernel.org/T/#m6fa97e46bde0d100fa0ec2854d47a57bd271c0b3
- Paul
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 0/2] perf: RISC-V: fix SBI PMU masks for RV32
2026-08-07 8:50 [PATCH v1 0/2] perf: RISC-V: fix SBI PMU masks for RV32 Xixin Liu
2026-08-07 8:50 ` [PATCH v1 1/2] perf: RISC-V: use BIT_ULL for u64 overflow masks Xixin Liu
2026-08-07 8:50 ` [PATCH v1 2/2] perf: RISC-V: store available counter mask as bitmap Xixin Liu
@ 2026-08-18 9:10 ` Xixin Liu
2026-08-18 9:10 ` [PATCH v2 2/2] perf: RISC-V: store available counter mask as bitmap Xixin Liu
2026-08-18 9:10 ` [PATCH v2 1/2] perf: RISC-V: use BIT_ULL for u64 overflow masks Xixin Liu
2 siblings, 2 replies; 9+ messages in thread
From: Xixin Liu @ 2026-08-18 9:10 UTC (permalink / raw)
To: linux-riscv
Cc: atish.patra, anup, will, mark.rutland, pjw, palmer, aou, alex,
linux-arm-kernel, linux-perf-users, linux-kernel, liuxixin
Hi,
Thanks for the review, Paul.
v2 for 1/2: add Fixes and Assisted-by. Overflow masks are u64. On RV32
an unsigned-long bit at index 32 is 0, and BIT_ULL at index 32 is
0x100000000.
v2 for 2/2: same as v1, but do not pass only the first bitmap word into
CFG_MATCH. Walk each word like the stop-all path, with the counter base
set to i times BITS_PER_LONG. Share one helper for the 32-bit argument
split. Also add Fixes and Assisted-by.
Tested on qemu-system-riscv32 with CONFIG_ARCH_RV32I=y: 34 counters,
second word 0x7, CFG_MATCH retried at base 32 when the first word fails.
qemu-system-riscv64 still uses one word. A legacy-only RV32 kernel sets
cycle and instret bits and opens a cycles event. Overflow IRQ was not
taken on this QEMU virt guest, because sscof was not available to Linux.
Thanks,
Xixin Liu
---
Xixin Liu (2):
perf: RISC-V: use BIT_ULL for u64 overflow masks
perf: RISC-V: store available counter mask as bitmap
drivers/perf/riscv_pmu_legacy.c | 5 ++-
drivers/perf/riscv_pmu_sbi.c | 88 ++++++++++++++++++++++++++++------------
include/linux/perf/riscv_pmu.h | 2 +-
3 files changed, 66 insertions(+), 29 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 2/2] perf: RISC-V: store available counter mask as bitmap
2026-08-18 9:10 ` [PATCH v2 0/2] perf: RISC-V: fix SBI PMU masks for RV32 Xixin Liu
@ 2026-08-18 9:10 ` Xixin Liu
2026-08-18 9:10 ` [PATCH v2 1/2] perf: RISC-V: use BIT_ULL for u64 overflow masks Xixin Liu
1 sibling, 0 replies; 9+ messages in thread
From: Xixin Liu @ 2026-08-18 9:10 UTC (permalink / raw)
To: linux-riscv
Cc: atish.patra, anup, will, mark.rutland, pjw, palmer, aou, alex,
linux-arm-kernel, linux-perf-users, linux-kernel, liuxixin
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: e9991434596f ("RISC-V: Add perf platform driver based on SBI PMU extension")
Assisted-by: DeepSeek:deepseek-v3
Signed-off-by: Xixin Liu <liuxixin@kylinos.cn>
---
drivers/perf/riscv_pmu_legacy.c | 5 +-
drivers/perf/riscv_pmu_sbi.c | 88 ++++++++++++++++++++++++++++------------
include/linux/perf/riscv_pmu.h | 2
3 files changed, 66 insertions(+), 29 deletions(-)
diff --git a/drivers/perf/riscv_pmu_legacy.c b/drivers/perf/riscv_pmu_legacy.c
--- a/drivers/perf/riscv_pmu_legacy.c
+++ b/drivers/perf/riscv_pmu_legacy.c
@@ -111,8 +111,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;
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -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);
struct sbi_pmu_event_data {
union {
@@ -298,16 +298,38 @@ static struct sbi_pmu_event_data pmu_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
},
};
+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;
}
@@ -353,10 +375,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;
@@ -405,8 +427,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;
+ uint64_t cbase = 0, cmask = 0;
unsigned long cflags = 0;
cflags = pmu_sbi_get_filter_flags(event);
@@ -427,14 +449,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);
@@ -442,7 +471,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 */
@@ -794,7 +823,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)
@@ -811,12 +840,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)
@@ -1353,7 +1389,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);
@@ -1365,7 +1401,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;
diff --git a/include/linux/perf/riscv_pmu.h b/include/linux/perf/riscv_pmu.h
--- a/include/linux/perf/riscv_pmu.h
+++ b/include/linux/perf/riscv_pmu.h
@@ -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);
--
2.43.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 1/2] perf: RISC-V: use BIT_ULL for u64 overflow masks
2026-08-18 9:10 ` [PATCH v2 0/2] perf: RISC-V: fix SBI PMU masks for RV32 Xixin Liu
2026-08-18 9:10 ` [PATCH v2 2/2] perf: RISC-V: store available counter mask as bitmap Xixin Liu
@ 2026-08-18 9:10 ` Xixin Liu
1 sibling, 0 replies; 9+ messages in thread
From: Xixin Liu @ 2026-08-18 9:10 UTC (permalink / raw)
To: linux-riscv
Cc: atish.patra, anup, will, mark.rutland, pjw, palmer, aou, alex,
linux-arm-kernel, linux-perf-users, linux-kernel, liuxixin
Overflow status and restart masks are u64, but bits were built with
BIT(). On RV32 that is an unsigned long shift, so indices >= 32 truncate
or wrap and corrupt the mask.
Use BIT_ULL() for those u64 bitops.
Fixes: a8625217a054 ("drivers/perf: riscv: Implement SBI PMU snapshot function")
Assisted-by: DeepSeek:deepseek-v3
Signed-off-by: Xixin Liu <liuxixin@kylinos.cn>
---
drivers/perf/riscv_pmu_sbi.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -913,7 +913,7 @@ static inline void pmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_hw_
struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr;
for_each_set_bit(idx, cpu_hw_evt->used_hw_ctrs, RISCV_MAX_COUNTERS) {
- if (ctr_ovf_mask & BIT(idx)) {
+ if (ctr_ovf_mask & BIT_ULL(idx)) {
event = cpu_hw_evt->events[idx];
hwc = &event->hw;
max_period = riscv_pmu_ctr_get_width_mask(event);
@@ -1020,14 +1020,14 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)
hidx = info->csr - CSR_CYCLE;
/* check if the corresponding bit is set in sscountovf or overflow mask in shmem */
- if (!(overflow & BIT(hidx)))
+ if (!(overflow & BIT_ULL(hidx)))
continue;
/*
* Keep a track of overflowed counters so that they can be started
* with updated initial value.
*/
- overflowed_ctrs |= BIT(lidx);
+ overflowed_ctrs |= BIT_ULL(lidx);
hw_evt = &event->hw;
/* Update the event states here so that we know the state while reading */
hw_evt->state |= PERF_HES_STOPPED;
--
2.43.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v1 2/2] perf: RISC-V: store available counter mask as bitmap
2026-08-08 0:32 ` Paul Walmsley
@ 2026-08-18 9:15 ` Xixin Liu
0 siblings, 0 replies; 9+ messages in thread
From: Xixin Liu @ 2026-08-18 9:15 UTC (permalink / raw)
To: pjw
Cc: linux-riscv, atish.patra, anup, will, mark.rutland, palmer, aou,
alex, linux-arm-kernel, linux-perf-users, linux-kernel, liuxixin
Hi Paul,
On Fri, 7 Aug 2026, Paul Walmsley wrote:
> This patch contains several array indexes to element 0 of the cmask
> bitmap, but that element is going to be XLEN bits wide. Does that
> actually work on RV32 systems?
I built with CONFIG_ARCH_RV32I=y and booted qemu-system-riscv32.
Probe reports 16 firmware and 18 hardware counters. The available
bitmap is two words: 0xfffffffd then 0x7. Three bits sit at index 32
and above.
When CFG_MATCH fails on the first word, the same call is retried with
base 32 and the second word. That is the walk the stop-all path already
used. Clearing the first word still issues that second call with
base 32.
I also booted qemu-system-riscv64. Probe reports the same 16 firmware
and 18 hardware counters. The available bitmap is one word:
0x7fffffffd. CFG_MATCH and stop-all stay on that word. Opening a
cycles event from userspace succeeds on both guests.
A legacy-only RV32 kernel sets cycle and instret in the bitmap. Opening
a cycles event from userspace succeeds there too.
v2 walks every bitmap word for the remaining CFG_MATCH sites, same as
stop-all, with the counter base set to i times BITS_PER_LONG.
Thanks,
Xixin
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-08-18 9:44 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-07 8:50 [PATCH v1 0/2] perf: RISC-V: fix SBI PMU masks for RV32 Xixin Liu
2026-08-07 8:50 ` [PATCH v1 1/2] perf: RISC-V: use BIT_ULL for u64 overflow masks Xixin Liu
2026-08-08 0:46 ` Paul Walmsley
2026-08-07 8:50 ` [PATCH v1 2/2] perf: RISC-V: store available counter mask as bitmap Xixin Liu
2026-08-08 0:32 ` Paul Walmsley
2026-08-18 9:15 ` Xixin Liu
2026-08-18 9:10 ` [PATCH v2 0/2] perf: RISC-V: fix SBI PMU masks for RV32 Xixin Liu
2026-08-18 9:10 ` [PATCH v2 2/2] perf: RISC-V: store available counter mask as bitmap Xixin Liu
2026-08-18 9:10 ` [PATCH v2 1/2] perf: RISC-V: use BIT_ULL for u64 overflow masks Xixin Liu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox