Devicetree
 help / color / mirror / Atom feed
* [PATCH v6 00/21] Add Counter delegation ISA extension support
@ 2026-06-09  6:01 Atish Patra
  2026-06-09  6:01 ` [PATCH v6 01/21] RISC-V: Add Sxcsrind ISA extension CSR definitions Atish Patra
                   ` (20 more replies)
  0 siblings, 21 replies; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

This series adds the counter delegation extension support. It is based on
very early PoC work done by Kevin Xue and mostly rewritten after that.
The counter delegation ISA extension(Smcdeleg/Ssccfg) actually depends
on multiple ISA extensions.

1. S[m|s]csrind : The indirect CSR extension[1] which defines additional
   5 ([M|S|VS]IREG2-[M|S|VS]IREG6) register to address size limitation of
   RISC-V CSR address space.
2. Smstateen: The stateen bit[60] controls the access to the registers
   indirectly via the above indirect registers.
3. Smcdeleg/Ssccfg: The counter delegation extensions[2]

The counter delegation extension allows Supervisor mode to program the
hpmevent and hpmcounters directly without needing the assistance from the
M-mode via SBI calls. This results in a faster perf profiling and very
few traps. This extension also introduces a scountinhibit CSR which allows
to stop/start any counter directly from the S-mode. As the counter
delegation extension potentially can have more than 100 CSRs, the specification
leverages the indirect CSR extension to save the precious CSR address range.

Due to the dependency of these extensions, the following extensions must be
enabled in qemu to use the counter delegation feature in S-mode.

"smstateen=true,sscofpmf=true,ssccfg=true,smcdeleg=true,smcsrind=true,sscsrind=true"
or Virt machine users can just "max" cpu instead.

When we access the counters directly in S-mode, we also need to solve the
following problems.

1. Event to counter mapping
2. Event encoding discovery

The RISC-V ISA doesn't define any standard either for event encoding or the
event to counter mapping rules. Until now, the SBI PMU implementation relies
on device tree binding[3] to discover the event to counter mapping in RISC-V
platform in the firmware. The SBI PMU specification[4] defines event encoding
for standard perf events as well. Thus, the kernel can query the appropriate
counter for an given event from the firmware.

However, the kernel doesn't need any firmware interaction for hardware
counters if counter delegation is available in the hardware. Thus, the driver
needs to discover the above mappings/encodings by itself without any assistance
from firmware.

Solution to Problem #1:
This patch series solves the above problem #1 by extending the perf tool in a
way so that event json file can specify the counter constraints of each event
and that can be passed to the driver to choose the best counter for a given
event.

This series introduces a RISC-V specific event field in "CounterIDMask" in
event_fields that describes a bitmask of counters supported for a specific eventi.
This is the similar approach for few other existing properties in the event_fields
which were used by single architecture as well. The counter constraint bitmap is
passed to the perf driver via newly introduced "counterid_mask" property set in "config2".

The platform vendor have these three ways to encode/use the platform specific
events.

1. Directly in driver with appropriate constraints (discouraged due to bloating
of the driver)
2. Encode in Json with with CounterIDMask field (preferred as it is contained
within platform specific json file)
3. Directly pass counterid_mask at while invoking perf 
cpu/event=<code>,counterid_mask=<mask>/

The last two patches show cases these use cases and not intended for merging.

Solution to problem #2:

The event encoding can come from the json or commandline as well.

The Qemu patches are available in upstream now.

The Linux kernel patches can be found here:
https://github.com/atishp04/linux/tree/b4/counter_delegation_v6

[1] https://github.com/riscv/riscv-indirect-csr-access
[2] https://github.com/riscv/riscv-smcdeleg-ssccfg
[3] https://www.kernel.org/doc/Documentation/devicetree/bindings/perf/riscv%2Cpmu.yaml
[4] https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/src/ext-pmu.adoc

To: Paul Walmsley <pjw@kernel.org>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Will Deacon <will@kernel.org>
To: Mark Rutland <mark.rutland@arm.com>
To: Atish Patra <atish.patra@linux.dev>
To: Anup Patel <anup@brainfault.org>
To: Arnaldo Carvalho de Melo <acme@kernel.org>
To: Namhyung Kim <namhyung@kernel.org>
To: Jiri Olsa <jolsa@kernel.org>
To: Ian Rogers <irogers@google.com>
To: James Clark <james.clark@linaro.org>
Cc: linux-riscv@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Cc: Conor Dooley <conor@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-perf-users@vger.kernel.org

Signed-off-by: Atish Patra <atishp@meta.com>

Changes in v6:
- Reverted the file name changes as suggested by Will. Now pmu-sbi.c will continue
  to support both counter delegation and SBI PMU with different function prefixes.
- No longer depends up old upstream patch for reusing the Counter property to
  encode event to counter mapping property. It directly uses additional field in
  json event fields similar to other architectures.
- Added few test patches (not intended for upstreaming) to show case different
  possibilities of providing mapping/event encodings.
- Fixed review comments and miscellenous minor typos/fixes on v5
- Rebased on top of v7.1-rc6

Changes in v5:
- Fixed dt_binding_check errors.
- Added the ISA extension dependancy for counter delegation extensions.
- Replaced the boolean variables with static key conditional check required at boot time.
- Miscellaneous minor code restructuring.
- Link to v4: https://lore.kernel.org/r/20250205-counter_delegation-v4-0-835cfa88e3b1@rivosinc.com

Changes in v4:
- Added ISA dependencies as per dt schema instead of description.
- Fixed few compilation issues due to patch reordering in v3.
- Link to v3: https://lore.kernel.org/r/20250127-counter_delegation-v3-0-64894d7e16d5@rivosinc.com

Changes in v3:
- Fixed the dtb binding check failures.
- Inlcuded the fix reported by Rajnesh Kanwal for guest counter overflow.
- Rearranged the overflow handling more efficiently for better modularity.
- Link to v2: https://lore.kernel.org/r/20250114-counter_delegation-v2-0-8ba74cdb851b@rivosinc.com

Changes in v2:
- Dropped architecture specific overrides for event encoding.
- Dropped hwprobe bits.
- Added a vendor specific event encoding table to support vendor specific event
  encoding and counter mapping.
- Fixed few bugs and cleanup.
- Link to v1: https://lore.kernel.org/r/20240217005738.3744121-1-atishp@rivosinc.com

---
Atish Patra (18):
      RISC-V: Add Sxcsrind ISA extension definition and parsing
      dt-bindings: riscv: add Sxcsrind ISA extension description
      RISC-V: Define indirect CSR access helpers
      RISC-V: Add Smcntrpmf extension parsing
      dt-bindings: riscv: add Smcntrpmf ISA extension description
      RISC-V: Add Ssccfg/Smcdeleg ISA extension definition and parsing
      dt-bindings: riscv: add Counter delegation ISA extensions description
      RISC-V: perf: Restructure the SBI PMU code
      RISC-V: perf: Modify the counter discovery mechanism
      RISC-V: perf: Add a mechanism to defined legacy event encoding
      RISC-V: perf: Implement supervisor counter delegation support
      RISC-V: perf: Use config2/vendor table for event to counter mapping
      RISC-V: perf: Add legacy event encodings via sysfs
      RISC-V: perf: Add Qemu virt machine events
      tools/perf: Support event code for arch standard events
      tools/perf: Add RISC-V CounterIDMask event field
      TEST(do-not-upstream): fake qemu-virt PMU events for cdeleg counter-mask testing
      TEST(do-not-upstream): fake qemu vendor JSON + mapfile entry for CounterIDMask path

Charlie Jenkins (1):
      RISC-V: perf: Skip PMU SBI extension when not implemented

Kaiwen Xue (2):
      RISC-V: Add Sxcsrind ISA extension CSR definitions
      RISC-V: Add Sscfg extension CSR definition

 .../devicetree/bindings/riscv/extensions.yaml      |   67 ++
 arch/riscv/include/asm/csr.h                       |   57 ++
 arch/riscv/include/asm/csr_ind.h                   |   44 +
 arch/riscv/include/asm/hwcap.h                     |    7 +
 arch/riscv/include/asm/vendorid_list.h             |    4 +
 arch/riscv/kernel/cpufeature.c                     |   27 +
 drivers/perf/Kconfig                               |   14 +-
 drivers/perf/riscv_pmu_sbi.c                       | 1044 ++++++++++++++++----
 include/linux/perf/riscv_pmu.h                     |   18 +
 .../perf/pmu-events/arch/riscv/arch-standard.json  |   10 +
 tools/perf/pmu-events/arch/riscv/mapfile.csv       |    1 +
 .../pmu-events/arch/riscv/qemu/virt/events.json    |   26 +
 tools/perf/pmu-events/jevents.py                   |    7 +-
 13 files changed, 1124 insertions(+), 202 deletions(-)
---
base-commit: ba3e43a9e601636f5edb54e259a74f96ca3b8fd8
change-id: 20240715-counter_delegation-628a32f8c9cc

Best regards,
-- 
Atish Patra <atishp@meta.com>


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v6 01/21] RISC-V: Add Sxcsrind ISA extension CSR definitions
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:01 ` [PATCH v6 02/21] RISC-V: Add Sxcsrind ISA extension definition and parsing Atish Patra
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Kaiwen Xue <kaiwenx@rivosinc.com>

This adds definitions of new CSRs and bits defined in Sxcsrind ISA
extension. These CSR enables indirect accesses mechanism to access
any CSRs in M-, S-, and VS-mode. The range of the select values
and ireg will be define by the ISA extension using Sxcsrind extension.

Signed-off-by: Kaiwen Xue <kaiwenx@rivosinc.com>
Reviewed-by: Clément Léger <cleger@rivosinc.com>
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 arch/riscv/include/asm/csr.h | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
index 31b8988f4488..b4551a6cf7cb 100644
--- a/arch/riscv/include/asm/csr.h
+++ b/arch/riscv/include/asm/csr.h
@@ -347,6 +347,12 @@
 /* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */
 #define CSR_SISELECT		0x150
 #define CSR_SIREG		0x151
+/* Supervisor-Level Window to Indirectly Accessed Registers (Sxcsrind) */
+#define CSR_SIREG2		0x152
+#define CSR_SIREG3		0x153
+#define CSR_SIREG4		0x155
+#define CSR_SIREG5		0x156
+#define CSR_SIREG6		0x157
 
 /* Supervisor-Level Interrupts (AIA) */
 #define CSR_STOPEI		0x15c
@@ -394,6 +400,14 @@
 /* VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */
 #define CSR_VSISELECT		0x250
 #define CSR_VSIREG		0x251
+/*
+ * VS-Level Window to Indirectly Accessed Registers (H-extension with Sxcsrind)
+ */
+#define CSR_VSIREG2		0x252
+#define CSR_VSIREG3		0x253
+#define CSR_VSIREG4		0x255
+#define CSR_VSIREG5		0x256
+#define CSR_VSIREG6		0x257
 
 /* VS-Level Interrupts (H-extension with AIA) */
 #define CSR_VSTOPEI		0x25c
@@ -436,6 +450,12 @@
 /* Machine-Level Window to Indirectly Accessed Registers (AIA) */
 #define CSR_MISELECT		0x350
 #define CSR_MIREG		0x351
+/* Machine-Level Window to Indirectly Accessed Registers (Sxcsrind) */
+#define CSR_MIREG2		0x352
+#define CSR_MIREG3		0x353
+#define CSR_MIREG4		0x355
+#define CSR_MIREG5		0x356
+#define CSR_MIREG6		0x357
 
 /* Machine-Level Interrupts (AIA) */
 #define CSR_MTOPEI		0x35c
@@ -498,6 +518,11 @@
 # define CSR_IEH		CSR_MIEH
 # define CSR_ISELECT	CSR_MISELECT
 # define CSR_IREG	CSR_MIREG
+# define CSR_IREG2	CSR_MIREG2
+# define CSR_IREG3	CSR_MIREG3
+# define CSR_IREG4	CSR_MIREG4
+# define CSR_IREG5	CSR_MIREG5
+# define CSR_IREG6	CSR_MIREG6
 # define CSR_IPH		CSR_MIPH
 # define CSR_TOPEI	CSR_MTOPEI
 # define CSR_TOPI	CSR_MTOPI
@@ -523,6 +548,11 @@
 # define CSR_IEH		CSR_SIEH
 # define CSR_ISELECT	CSR_SISELECT
 # define CSR_IREG	CSR_SIREG
+# define CSR_IREG2	CSR_SIREG2
+# define CSR_IREG3	CSR_SIREG3
+# define CSR_IREG4	CSR_SIREG4
+# define CSR_IREG5	CSR_SIREG5
+# define CSR_IREG6	CSR_SIREG6
 # define CSR_IPH		CSR_SIPH
 # define CSR_TOPEI	CSR_STOPEI
 # define CSR_TOPI	CSR_STOPI

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 02/21] RISC-V: Add Sxcsrind ISA extension definition and parsing
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
  2026-06-09  6:01 ` [PATCH v6 01/21] RISC-V: Add Sxcsrind ISA extension CSR definitions Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:01 ` [PATCH v6 03/21] dt-bindings: riscv: add Sxcsrind ISA extension description Atish Patra
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

The S[m|s]csrind extension extends the indirect CSR access mechanism
defined in Smaia/Ssaia extensions.

This patch just enables the definition and parsing.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 arch/riscv/include/asm/hwcap.h | 4 ++++
 arch/riscv/kernel/cpufeature.c | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
index 7ef8e5f55c8d..d4a7b90e2d78 100644
--- a/arch/riscv/include/asm/hwcap.h
+++ b/arch/riscv/include/asm/hwcap.h
@@ -112,6 +112,8 @@
 #define RISCV_ISA_EXT_ZCLSD		103
 #define RISCV_ISA_EXT_ZICFILP		104
 #define RISCV_ISA_EXT_ZICFISS		105
+#define RISCV_ISA_EXT_SSCSRIND		106
+#define RISCV_ISA_EXT_SMCSRIND		107
 
 #define RISCV_ISA_EXT_XLINUXENVCFG	127
 
@@ -121,9 +123,11 @@
 #ifdef CONFIG_RISCV_M_MODE
 #define RISCV_ISA_EXT_SxAIA		RISCV_ISA_EXT_SMAIA
 #define RISCV_ISA_EXT_SUPM		RISCV_ISA_EXT_SMNPM
+#define RISCV_ISA_EXT_SxCSRIND		RISCV_ISA_EXT_SMCSRIND
 #else
 #define RISCV_ISA_EXT_SxAIA		RISCV_ISA_EXT_SSAIA
 #define RISCV_ISA_EXT_SUPM		RISCV_ISA_EXT_SSNPM
+#define RISCV_ISA_EXT_SxCSRIND		RISCV_ISA_EXT_SSCSRIND
 #endif
 
 #endif /* _ASM_RISCV_HWCAP_H */
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index f46aa5602d74..3fa0a563fb21 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -576,11 +576,13 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
 	__RISCV_ISA_EXT_BUNDLE_VALIDATE(zvksg, riscv_zvksg_bundled_exts, riscv_ext_vector_crypto_validate),
 	__RISCV_ISA_EXT_DATA_VALIDATE(zvkt, RISCV_ISA_EXT_ZVKT, riscv_ext_vector_crypto_validate),
 	__RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
+	__RISCV_ISA_EXT_DATA(smcsrind, RISCV_ISA_EXT_SMCSRIND),
 	__RISCV_ISA_EXT_DATA(smmpm, RISCV_ISA_EXT_SMMPM),
 	__RISCV_ISA_EXT_SUPERSET(smnpm, RISCV_ISA_EXT_SMNPM, riscv_xlinuxenvcfg_exts),
 	__RISCV_ISA_EXT_DATA(smstateen, RISCV_ISA_EXT_SMSTATEEN),
 	__RISCV_ISA_EXT_DATA(ssaia, RISCV_ISA_EXT_SSAIA),
 	__RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
+	__RISCV_ISA_EXT_DATA(sscsrind, RISCV_ISA_EXT_SSCSRIND),
 	__RISCV_ISA_EXT_SUPERSET(ssnpm, RISCV_ISA_EXT_SSNPM, riscv_xlinuxenvcfg_exts),
 	__RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
 	__RISCV_ISA_EXT_DATA(svade, RISCV_ISA_EXT_SVADE),

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 03/21] dt-bindings: riscv: add Sxcsrind ISA extension description
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
  2026-06-09  6:01 ` [PATCH v6 01/21] RISC-V: Add Sxcsrind ISA extension CSR definitions Atish Patra
  2026-06-09  6:01 ` [PATCH v6 02/21] RISC-V: Add Sxcsrind ISA extension definition and parsing Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:09   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 04/21] RISC-V: Define indirect CSR access helpers Atish Patra
                   ` (17 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

Add the S[m|s]csrind ISA extension description.

Acked-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 Documentation/devicetree/bindings/riscv/extensions.yaml | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
index 2b0a8a93bb21..4be557dc215d 100644
--- a/Documentation/devicetree/bindings/riscv/extensions.yaml
+++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
@@ -181,6 +181,14 @@ properties:
             changes to interrupts as frozen at commit ccbddab ("Merge pull
             request #42 from riscv/jhauser-2023-RC4") of riscv-aia.
 
+        - const: smcsrind
+          description: |
+            The standard Smcsrind supervisor-level extension extends the
+            indirect CSR access mechanism defined by the Smaia extension. This
+            extension allows other ISA extension to use indirect CSR access
+            mechanism in M-mode as ratified in the 20240326 version of the
+            privileged ISA specification.
+
         - const: smmpm
           description: |
             The standard Smmpm extension for M-mode pointer masking as
@@ -199,6 +207,14 @@ properties:
             added by other RISC-V extensions in H/S/VS/U/VU modes and as
             ratified at commit a28bfae (Ratified (#7)) of riscv-state-enable.
 
+        - const: sscsrind
+          description: |
+            The standard Sscsrind supervisor-level extension extends the
+            indirect CSR access mechanism defined by the Ssaia extension. This
+            extension allows other ISA extension to use indirect CSR access
+            mechanism in S-mode as ratified in the 20240326 version of the
+            privileged ISA specification.
+
         - const: ssaia
           description: |
             The standard Ssaia supervisor-level extension for the advanced

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 04/21] RISC-V: Define indirect CSR access helpers
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (2 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 03/21] dt-bindings: riscv: add Sxcsrind ISA extension description Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:15   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 05/21] RISC-V: Add Smcntrpmf extension parsing Atish Patra
                   ` (16 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

The indriect CSR requires multiple instructions to read/write CSR.
Add a few helper functions for ease of usage.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 arch/riscv/include/asm/csr_ind.h | 44 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/arch/riscv/include/asm/csr_ind.h b/arch/riscv/include/asm/csr_ind.h
new file mode 100644
index 000000000000..6fd7d44dc640
--- /dev/null
+++ b/arch/riscv/include/asm/csr_ind.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 Rivos Inc.
+ */
+
+#ifndef _ASM_RISCV_CSR_IND_H
+#define _ASM_RISCV_CSR_IND_H
+
+#include <linux/irqflags.h>
+
+#include <asm/csr.h>
+
+#define csr_ind_read(iregcsr, iselbase, iseloff) ({		\
+	unsigned long __value = 0;				\
+	unsigned long __flags;					\
+	local_irq_save(__flags);				\
+	csr_write(CSR_ISELECT, (iselbase) + (iseloff));		\
+	__value = csr_read(iregcsr);				\
+	local_irq_restore(__flags);				\
+	__value;						\
+})
+
+#define csr_ind_write(iregcsr, iselbase, iseloff, value) ({	\
+	unsigned long __flags;					\
+	local_irq_save(__flags);				\
+	csr_write(CSR_ISELECT, (iselbase) + (iseloff));		\
+	csr_write(iregcsr, (value));				\
+	local_irq_restore(__flags);				\
+})
+
+#define csr_ind_warl(iregcsr, iselbase, iseloff, warl_val) ({	\
+	unsigned long __old_val = 0, __value = 0;		\
+	unsigned long __flags;					\
+	local_irq_save(__flags);				\
+	csr_write(CSR_ISELECT, (iselbase) + (iseloff));		\
+	__old_val = csr_read(iregcsr);				\
+	csr_write(iregcsr, (warl_val));				\
+	__value = csr_read(iregcsr);				\
+	csr_write(iregcsr, __old_val);				\
+	local_irq_restore(__flags);				\
+	__value;						\
+})
+
+#endif

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 05/21] RISC-V: Add Smcntrpmf extension parsing
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (3 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 04/21] RISC-V: Define indirect CSR access helpers Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:01 ` [PATCH v6 06/21] dt-bindings: riscv: add Smcntrpmf ISA extension description Atish Patra
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

Smcntrpmf extension allows M-mode to enable privilege mode filtering
for cycle/instret counters. However, the cyclecfg/instretcfg CSRs are
only available only in Ssccfg only Smcntrpmf is present.

That's why, kernel needs to detect presence of Smcntrpmf extension and
enable privilege mode filtering for cycle/instret counters.

Reviewed-by: Clément Léger <cleger@rivosinc.com>
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 arch/riscv/include/asm/hwcap.h | 1 +
 arch/riscv/kernel/cpufeature.c | 1 +
 2 files changed, 2 insertions(+)

diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
index d4a7b90e2d78..51ad55b9677a 100644
--- a/arch/riscv/include/asm/hwcap.h
+++ b/arch/riscv/include/asm/hwcap.h
@@ -114,6 +114,7 @@
 #define RISCV_ISA_EXT_ZICFISS		105
 #define RISCV_ISA_EXT_SSCSRIND		106
 #define RISCV_ISA_EXT_SMCSRIND		107
+#define RISCV_ISA_EXT_SMCNTRPMF		108
 
 #define RISCV_ISA_EXT_XLINUXENVCFG	127
 
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index 3fa0a563fb21..1452521d740a 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -576,6 +576,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
 	__RISCV_ISA_EXT_BUNDLE_VALIDATE(zvksg, riscv_zvksg_bundled_exts, riscv_ext_vector_crypto_validate),
 	__RISCV_ISA_EXT_DATA_VALIDATE(zvkt, RISCV_ISA_EXT_ZVKT, riscv_ext_vector_crypto_validate),
 	__RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
+	__RISCV_ISA_EXT_DATA(smcntrpmf, RISCV_ISA_EXT_SMCNTRPMF),
 	__RISCV_ISA_EXT_DATA(smcsrind, RISCV_ISA_EXT_SMCSRIND),
 	__RISCV_ISA_EXT_DATA(smmpm, RISCV_ISA_EXT_SMMPM),
 	__RISCV_ISA_EXT_SUPERSET(smnpm, RISCV_ISA_EXT_SMNPM, riscv_xlinuxenvcfg_exts),

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 06/21] dt-bindings: riscv: add Smcntrpmf ISA extension description
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (4 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 05/21] RISC-V: Add Smcntrpmf extension parsing Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:09   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 07/21] RISC-V: Add Sscfg extension CSR definition Atish Patra
                   ` (14 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

Add the description for Smcntrpmf ISA extension

Acked-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 Documentation/devicetree/bindings/riscv/extensions.yaml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
index 4be557dc215d..ece3edccee42 100644
--- a/Documentation/devicetree/bindings/riscv/extensions.yaml
+++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
@@ -189,6 +189,12 @@ properties:
             mechanism in M-mode as ratified in the 20240326 version of the
             privileged ISA specification.
 
+        - const: smcntrpmf
+          description: |
+            The standard Smcntrpmf supervisor-level extension for the machine mode
+            to enable privilege mode filtering for cycle and instret counters as
+            ratified in the 20240326 version of the privileged ISA specification.
+
         - const: smmpm
           description: |
             The standard Smmpm extension for M-mode pointer masking as

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 07/21] RISC-V: Add Sscfg extension CSR definition
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (5 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 06/21] dt-bindings: riscv: add Smcntrpmf ISA extension description Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:01 ` [PATCH v6 08/21] RISC-V: Add Ssccfg/Smcdeleg ISA extension definition and parsing Atish Patra
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Kaiwen Xue <kaiwenx@rivosinc.com>

This adds the scountinhibit CSR definition and S-mode accessible hpmevent
bits defined by smcdeleg/ssccfg. scountinhibit allows S-mode to start/stop
counters directly from S-mode without invoking SBI calls to M-mode. It is
also used to figure out the counters delegated to S-mode by the M-mode as
well.

Signed-off-by: Kaiwen Xue <kaiwenx@rivosinc.com>
Reviewed-by: Clément Léger <cleger@rivosinc.com>
---
 arch/riscv/include/asm/csr.h | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
index b4551a6cf7cb..26cb78dee2fd 100644
--- a/arch/riscv/include/asm/csr.h
+++ b/arch/riscv/include/asm/csr.h
@@ -241,6 +241,31 @@
 #define SMSTATEEN0_HSENVCFG		(_ULL(1) << SMSTATEEN0_HSENVCFG_SHIFT)
 #define SMSTATEEN0_SSTATEEN0_SHIFT	63
 #define SMSTATEEN0_SSTATEEN0		(_ULL(1) << SMSTATEEN0_SSTATEEN0_SHIFT)
+/* HPMEVENT bits. These are accessible in S-mode via Smcdeleg/Ssccfg */
+#ifdef CONFIG_64BIT
+#define HPMEVENT_OF			(BIT_ULL(63))
+#define HPMEVENT_MINH			(BIT_ULL(62))
+#define HPMEVENT_SINH			(BIT_ULL(61))
+#define HPMEVENT_UINH			(BIT_ULL(60))
+#define HPMEVENT_VSINH			(BIT_ULL(59))
+#define HPMEVENT_VUINH			(BIT_ULL(58))
+#else
+#define HPMEVENTH_OF			(BIT_ULL(31))
+#define HPMEVENTH_MINH			(BIT_ULL(30))
+#define HPMEVENTH_SINH			(BIT_ULL(29))
+#define HPMEVENTH_UINH			(BIT_ULL(28))
+#define HPMEVENTH_VSINH			(BIT_ULL(27))
+#define HPMEVENTH_VUINH			(BIT_ULL(26))
+
+#define HPMEVENT_OF			(HPMEVENTH_OF << 32)
+#define HPMEVENT_MINH			(HPMEVENTH_MINH << 32)
+#define HPMEVENT_SINH			(HPMEVENTH_SINH << 32)
+#define HPMEVENT_UINH			(HPMEVENTH_UINH << 32)
+#define HPMEVENT_VSINH			(HPMEVENTH_VSINH << 32)
+#define HPMEVENT_VUINH			(HPMEVENTH_VUINH << 32)
+#endif
+
+#define SISELECT_SSCCFG_BASE		0x40
 
 /* mseccfg bits */
 #define MSECCFG_PMM			ENVCFG_PMM
@@ -322,6 +347,7 @@
 #define CSR_SCOUNTEREN		0x106
 #define CSR_SENVCFG		0x10a
 #define CSR_SSTATEEN0		0x10c
+#define CSR_SCOUNTINHIBIT	0x120
 #define CSR_SSCRATCH		0x140
 #define CSR_SEPC		0x141
 #define CSR_SCAUSE		0x142

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 08/21] RISC-V: Add Ssccfg/Smcdeleg ISA extension definition and parsing
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (6 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 07/21] RISC-V: Add Sscfg extension CSR definition Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:14   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 09/21] dt-bindings: riscv: add Counter delegation ISA extensions description Atish Patra
                   ` (12 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

Smcdeleg extension allows the M-mode to delegate selected counters
to S-mode so that it can access those counters and correpsonding
hpmevent CSRs without M-mode.

Ssccfg (‘Ss’ for Privileged architecture and Supervisor-level
extension, ‘ccfg’ for Counter Configuration) provides access to
delegated counters and new supervisor-level state.

This patch just enables these definitions and enable parsing.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 arch/riscv/include/asm/hwcap.h |  2 ++
 arch/riscv/kernel/cpufeature.c | 24 ++++++++++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
index 51ad55b9677a..089353b250b0 100644
--- a/arch/riscv/include/asm/hwcap.h
+++ b/arch/riscv/include/asm/hwcap.h
@@ -115,6 +115,8 @@
 #define RISCV_ISA_EXT_SSCSRIND		106
 #define RISCV_ISA_EXT_SMCSRIND		107
 #define RISCV_ISA_EXT_SMCNTRPMF		108
+#define RISCV_ISA_EXT_SSCCFG		109
+#define RISCV_ISA_EXT_SMCDELEG		110
 
 #define RISCV_ISA_EXT_XLINUXENVCFG	127
 
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index 1452521d740a..1fe647e03515 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -330,6 +330,27 @@ static const unsigned int riscv_a_exts[] = {
 	RISCV_ISA_EXT_ZKNE,	\
 	RISCV_ISA_EXT_ZKNH
 
+static int riscv_ext_smcdeleg_validate(const struct riscv_isa_ext_data *data,
+				       const unsigned long *isa_bitmap)
+{
+	if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SSCSRIND) &&
+	    __riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_ZIHPM) &&
+	    __riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_ZICNTR))
+		return 0;
+
+	return -EPROBE_DEFER;
+}
+
+static int riscv_ext_ssccfg_validate(const struct riscv_isa_ext_data *data,
+				     const unsigned long *isa_bitmap)
+{
+	if (!riscv_ext_smcdeleg_validate(data, isa_bitmap) &&
+	    __riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SMCDELEG))
+		return 0;
+
+	return -EPROBE_DEFER;
+}
+
 static const unsigned int riscv_zk_bundled_exts[] = {
 	RISCV_ISA_EXT_ZKN,
 	RISCV_ISA_EXT_ZKR,
@@ -576,12 +597,15 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
 	__RISCV_ISA_EXT_BUNDLE_VALIDATE(zvksg, riscv_zvksg_bundled_exts, riscv_ext_vector_crypto_validate),
 	__RISCV_ISA_EXT_DATA_VALIDATE(zvkt, RISCV_ISA_EXT_ZVKT, riscv_ext_vector_crypto_validate),
 	__RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
+	__RISCV_ISA_EXT_DATA_VALIDATE(smcdeleg, RISCV_ISA_EXT_SMCDELEG,
+				      riscv_ext_smcdeleg_validate),
 	__RISCV_ISA_EXT_DATA(smcntrpmf, RISCV_ISA_EXT_SMCNTRPMF),
 	__RISCV_ISA_EXT_DATA(smcsrind, RISCV_ISA_EXT_SMCSRIND),
 	__RISCV_ISA_EXT_DATA(smmpm, RISCV_ISA_EXT_SMMPM),
 	__RISCV_ISA_EXT_SUPERSET(smnpm, RISCV_ISA_EXT_SMNPM, riscv_xlinuxenvcfg_exts),
 	__RISCV_ISA_EXT_DATA(smstateen, RISCV_ISA_EXT_SMSTATEEN),
 	__RISCV_ISA_EXT_DATA(ssaia, RISCV_ISA_EXT_SSAIA),
+	__RISCV_ISA_EXT_DATA_VALIDATE(ssccfg, RISCV_ISA_EXT_SSCCFG, riscv_ext_ssccfg_validate),
 	__RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
 	__RISCV_ISA_EXT_DATA(sscsrind, RISCV_ISA_EXT_SSCSRIND),
 	__RISCV_ISA_EXT_SUPERSET(ssnpm, RISCV_ISA_EXT_SSNPM, riscv_xlinuxenvcfg_exts),

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 09/21] dt-bindings: riscv: add Counter delegation ISA extensions description
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (7 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 08/21] RISC-V: Add Ssccfg/Smcdeleg ISA extension definition and parsing Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:12   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 10/21] RISC-V: perf: Restructure the SBI PMU code Atish Patra
                   ` (11 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

Add description for the Smcdeleg/Ssccfg extension.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
 .../devicetree/bindings/riscv/extensions.yaml      | 45 ++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
index ece3edccee42..2845e8e2999a 100644
--- a/Documentation/devicetree/bindings/riscv/extensions.yaml
+++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
@@ -181,6 +181,13 @@ properties:
             changes to interrupts as frozen at commit ccbddab ("Merge pull
             request #42 from riscv/jhauser-2023-RC4") of riscv-aia.
 
+        - const: smcdeleg
+          description: |
+            The standard Smcdeleg supervisor-level extension for the machine mode
+            to delegate the hpmcounters to supervisor mode so that they are
+            directly accessible in the supervisor mode as ratified in the
+            20240213 version of the privileged ISA specification.
+
         - const: smcsrind
           description: |
             The standard Smcsrind supervisor-level extension extends the
@@ -228,6 +235,14 @@ properties:
             behavioural changes to interrupts as frozen at commit ccbddab
             ("Merge pull request #42 from riscv/jhauser-2023-RC4") of riscv-aia.
 
+        - const: ssccfg
+          description: |
+            The standard Ssccfg supervisor-level extension for configuring
+            the delegated hpmcounters to be accessible directly in supervisor
+            mode as ratified in the 20240213 version of the privileged ISA
+            specification. This extension depends on Sscsrind, Smcdeleg, Sscofpmf,
+            Smcntrpmf, Zihpm, Zicntr extensions.
+
         - const: ssccptr
           description: |
             The standard Ssccptr extension for main memory (cacheability and
@@ -1135,6 +1150,36 @@ properties:
             allOf:
               - const: zilsd
               - const: zca
+      # Smcdeleg depends on Sscsrind, Zihpm, Zicntr
+      - if:
+          contains:
+            const: smcdeleg
+        then:
+          allOf:
+            - contains:
+                const: sscsrind
+            - contains:
+                const: zihpm
+            - contains:
+                const: zicntr
+      # Ssccfg depends on Smcdeleg, Sscsrind, Zihpm, Zicntr, Sscofpmf, Smcntrpmf
+      - if:
+          contains:
+            const: ssccfg
+        then:
+          allOf:
+            - contains:
+                const: smcdeleg
+            - contains:
+                const: sscsrind
+            - contains:
+                const: sscofpmf
+            - contains:
+                const: smcntrpmf
+            - contains:
+                const: zihpm
+            - contains:
+                const: zicntr
 
 allOf:
   # Zcf extension does not exist on rv64

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 10/21] RISC-V: perf: Restructure the SBI PMU code
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (8 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 09/21] dt-bindings: riscv: add Counter delegation ISA extensions description Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:18   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 11/21] RISC-V: perf: Modify the counter discovery mechanism Atish Patra
                   ` (10 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

With Ssccfg/Smcdeleg, supervisor mode can program and access the hpmcounters
and events directly, without the SBI PMU extension. The SBI PMU extension is
still required for firmware counters. Restructure the existing SBI PMU code so
the hpmcounter/event helpers can be shared between the SBI and the counter
delegation paths that follow.

The driver, file, module and Kconfig names are intentionally kept unchanged to
avoid backport churn and userspace breakage (module listings, udev rules,
cmdline options).

No functional change intended.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 drivers/perf/Kconfig         |  14 ++-
 drivers/perf/riscv_pmu_sbi.c | 238 +++++++++++++++++++++++++------------------
 2 files changed, 150 insertions(+), 102 deletions(-)

diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index ab90932fc2d0..3245bb2969e1 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -97,13 +97,17 @@ config RISCV_PMU_LEGACY
 
 config RISCV_PMU_SBI
 	depends on RISCV_PMU && RISCV_SBI
-	bool "RISC-V PMU based on SBI PMU extension"
+	bool "RISC-V PMU based on SBI PMU extension and/or counter delegation"
 	default y
 	help
-	  Say y if you want to use the CPU performance monitor
-	  using SBI PMU extension on RISC-V based systems. This option provides
-	  full perf feature support i.e. counter overflow, privilege mode
-	  filtering, counter configuration.
+	  Say y if you want to use the CPU performance monitor on RISC-V based
+	  systems. This single driver supports both hardware counter access
+	  mechanisms: it uses the counter delegation (Smcdeleg/Ssccfg) ISA
+	  extension to program and read the hpmcounters directly in supervisor
+	  mode when available, and uses the SBI PMU extension for firmware
+	  counters and when counter delegation is not present. This option
+	  provides full perf feature support i.e. counter overflow, privilege
+	  mode filtering, counter configuration.
 
 config STARFIVE_STARLINK_PMU
 	depends on ARCH_STARFIVE || COMPILE_TEST
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 385af5e6e6d0..7f21c16003f0 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -88,6 +88,8 @@ static const struct attribute_group *riscv_pmu_attr_groups[] = {
 static int sysctl_perf_user_access __read_mostly = SYSCTL_USER_ACCESS;
 
 /*
+ * This structure is SBI specific but counter delegation also require counter
+ * width, csr mapping. Reuse it for now.
  * RISC-V doesn't have heterogeneous harts yet. This need to be part of
  * per_cpu in case of harts with different pmu counters
  */
@@ -100,7 +102,7 @@ static unsigned int riscv_pmu_irq;
 /* Cache the available counters in a bitmask */
 static unsigned long cmask;
 
-static int pmu_event_find_cache(u64 config);
+static int sbi_pmu_event_find_cache(u64 config);
 struct sbi_pmu_event_data {
 	union {
 		union {
@@ -121,7 +123,7 @@ struct sbi_pmu_event_data {
 	};
 };
 
-static struct sbi_pmu_event_data pmu_hw_event_map[] = {
+static struct sbi_pmu_event_data pmu_hw_event_sbi_map[] = {
 	[PERF_COUNT_HW_CPU_CYCLES]		= {.hw_gen_event = {
 							SBI_PMU_HW_CPU_CYCLES,
 							SBI_PMU_EVENT_TYPE_HW, 0}},
@@ -155,7 +157,7 @@ static struct sbi_pmu_event_data pmu_hw_event_map[] = {
 };
 
 #define C(x) PERF_COUNT_HW_CACHE_##x
-static struct sbi_pmu_event_data pmu_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
+static struct sbi_pmu_event_data pmu_cache_event_sbi_map[PERF_COUNT_HW_CACHE_MAX]
 [PERF_COUNT_HW_CACHE_OP_MAX]
 [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
 	[C(L1D)] = {
@@ -302,7 +304,7 @@ static struct sbi_pmu_event_data pmu_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
 
 static int pmu_sbi_check_event_info(void)
 {
-	int num_events = ARRAY_SIZE(pmu_hw_event_map) + PERF_COUNT_HW_CACHE_MAX *
+	int num_events = ARRAY_SIZE(pmu_hw_event_sbi_map) + PERF_COUNT_HW_CACHE_MAX *
 			 PERF_COUNT_HW_CACHE_OP_MAX * PERF_COUNT_HW_CACHE_RESULT_MAX;
 	struct riscv_pmu_event_info *event_info_shmem;
 	phys_addr_t base_addr;
@@ -313,14 +315,14 @@ static int pmu_sbi_check_event_info(void)
 	if (!event_info_shmem)
 		return -ENOMEM;
 
-	for (i = 0; i < ARRAY_SIZE(pmu_hw_event_map); i++)
-		event_info_shmem[count++].event_idx = pmu_hw_event_map[i].event_idx;
+	for (i = 0; i < ARRAY_SIZE(pmu_hw_event_sbi_map); i++)
+		event_info_shmem[count++].event_idx = pmu_hw_event_sbi_map[i].event_idx;
 
-	for (i = 0; i < ARRAY_SIZE(pmu_cache_event_map); i++) {
-		for (j = 0; j < ARRAY_SIZE(pmu_cache_event_map[i]); j++) {
-			for (k = 0; k < ARRAY_SIZE(pmu_cache_event_map[i][j]); k++)
+	for (i = 0; i < ARRAY_SIZE(pmu_cache_event_sbi_map); i++) {
+		for (j = 0; j < ARRAY_SIZE(pmu_cache_event_sbi_map[i]); j++) {
+			for (k = 0; k < ARRAY_SIZE(pmu_cache_event_sbi_map[i][j]); k++)
 				event_info_shmem[count++].event_idx =
-							pmu_cache_event_map[i][j][k].event_idx;
+							pmu_cache_event_sbi_map[i][j][k].event_idx;
 		}
 	}
 
@@ -336,19 +338,19 @@ static int pmu_sbi_check_event_info(void)
 		goto free_mem;
 	}
 
-	for (i = 0; i < ARRAY_SIZE(pmu_hw_event_map); i++) {
+	for (i = 0; i < ARRAY_SIZE(pmu_hw_event_sbi_map); i++) {
 		if (!(event_info_shmem[i].output & RISCV_PMU_EVENT_INFO_OUTPUT_MASK))
-			pmu_hw_event_map[i].event_idx = -ENOENT;
+			pmu_hw_event_sbi_map[i].event_idx = -ENOENT;
 	}
 
-	count = ARRAY_SIZE(pmu_hw_event_map);
+	count = ARRAY_SIZE(pmu_hw_event_sbi_map);
 
-	for (i = 0; i < ARRAY_SIZE(pmu_cache_event_map); i++) {
-		for (j = 0; j < ARRAY_SIZE(pmu_cache_event_map[i]); j++) {
-			for (k = 0; k < ARRAY_SIZE(pmu_cache_event_map[i][j]); k++) {
+	for (i = 0; i < ARRAY_SIZE(pmu_cache_event_sbi_map); i++) {
+		for (j = 0; j < ARRAY_SIZE(pmu_cache_event_sbi_map[i]); j++) {
+			for (k = 0; k < ARRAY_SIZE(pmu_cache_event_sbi_map[i][j]); k++) {
 				if (!(event_info_shmem[count].output &
 				      RISCV_PMU_EVENT_INFO_OUTPUT_MASK))
-					pmu_cache_event_map[i][j][k].event_idx = -ENOENT;
+					pmu_cache_event_sbi_map[i][j][k].event_idx = -ENOENT;
 				count++;
 			}
 		}
@@ -360,7 +362,7 @@ static int pmu_sbi_check_event_info(void)
 	return result;
 }
 
-static void pmu_sbi_check_event(struct sbi_pmu_event_data *edata)
+static void rvpmu_sbi_check_event(struct sbi_pmu_event_data *edata)
 {
 	struct sbiret ret;
 
@@ -375,7 +377,7 @@ static void pmu_sbi_check_event(struct sbi_pmu_event_data *edata)
 	}
 }
 
-static void pmu_sbi_check_std_events(struct work_struct *work)
+static void rvpmu_sbi_check_std_events(struct work_struct *work)
 {
 	int ret;
 
@@ -386,23 +388,23 @@ static void pmu_sbi_check_std_events(struct work_struct *work)
 		return;
 	}
 
-	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_hw_event_sbi_map); i++)
+		rvpmu_sbi_check_event(&pmu_hw_event_sbi_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]);
+	for (int i = 0; i < ARRAY_SIZE(pmu_cache_event_sbi_map); i++)
+		for (int j = 0; j < ARRAY_SIZE(pmu_cache_event_sbi_map[i]); j++)
+			for (int k = 0; k < ARRAY_SIZE(pmu_cache_event_sbi_map[i][j]); k++)
+				rvpmu_sbi_check_event(&pmu_cache_event_sbi_map[i][j][k]);
 }
 
-static DECLARE_WORK(check_std_events_work, pmu_sbi_check_std_events);
+static DECLARE_WORK(check_std_events_work, rvpmu_sbi_check_std_events);
 
-static int pmu_sbi_ctr_get_width(int idx)
+static int rvpmu_ctr_get_width(int idx)
 {
 	return pmu_ctr_list[idx].width;
 }
 
-static bool pmu_sbi_ctr_is_fw(int cidx)
+static bool rvpmu_ctr_is_fw(int cidx)
 {
 	union sbi_pmu_ctr_info *info;
 
@@ -421,10 +423,10 @@ int riscv_pmu_get_event_info(u32 type, u64 config, u64 *econfig)
 	case PERF_TYPE_HARDWARE:
 		if (config >= PERF_COUNT_HW_MAX)
 			return -EINVAL;
-		ret = pmu_hw_event_map[config].event_idx;
+		ret = pmu_hw_event_sbi_map[config].event_idx;
 		break;
 	case PERF_TYPE_HW_CACHE:
-		ret = pmu_event_find_cache(config);
+		ret = sbi_pmu_event_find_cache(config);
 		break;
 	case PERF_TYPE_RAW:
 		/*
@@ -509,12 +511,12 @@ int riscv_pmu_get_hpm_info(u32 *hw_ctr_width, u32 *num_hw_ctr)
 }
 EXPORT_SYMBOL_GPL(riscv_pmu_get_hpm_info);
 
-static uint8_t pmu_sbi_csr_index(struct perf_event *event)
+static uint8_t rvpmu_csr_index(struct perf_event *event)
 {
 	return pmu_ctr_list[event->hw.idx].csr - CSR_CYCLE;
 }
 
-static unsigned long pmu_sbi_get_filter_flags(struct perf_event *event)
+static unsigned long rvpmu_sbi_get_filter_flags(struct perf_event *event)
 {
 	unsigned long cflags = 0;
 	bool guest_events = false;
@@ -535,7 +537,7 @@ static unsigned long pmu_sbi_get_filter_flags(struct perf_event *event)
 	return cflags;
 }
 
-static int pmu_sbi_ctr_get_idx(struct perf_event *event)
+static int rvpmu_sbi_ctr_get_idx(struct perf_event *event)
 {
 	struct hw_perf_event *hwc = &event->hw;
 	struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
@@ -545,7 +547,7 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event)
 	uint64_t cbase = 0, cmask = rvpmu->cmask;
 	unsigned long cflags = 0;
 
-	cflags = pmu_sbi_get_filter_flags(event);
+	cflags = rvpmu_sbi_get_filter_flags(event);
 
 	/*
 	 * In legacy mode, we have to force the fixed counters for those events
@@ -582,7 +584,7 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event)
 		return -ENOENT;
 
 	/* Additional sanity check for the counter id */
-	if (pmu_sbi_ctr_is_fw(idx)) {
+	if (rvpmu_ctr_is_fw(idx)) {
 		if (!test_and_set_bit(idx, cpuc->used_fw_ctrs))
 			return idx;
 	} else {
@@ -593,7 +595,7 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event)
 	return -ENOENT;
 }
 
-static void pmu_sbi_ctr_clear_idx(struct perf_event *event)
+static void rvpmu_ctr_clear_idx(struct perf_event *event)
 {
 
 	struct hw_perf_event *hwc = &event->hw;
@@ -601,13 +603,13 @@ static void pmu_sbi_ctr_clear_idx(struct perf_event *event)
 	struct cpu_hw_events *cpuc = this_cpu_ptr(rvpmu->hw_events);
 	int idx = hwc->idx;
 
-	if (pmu_sbi_ctr_is_fw(idx))
+	if (rvpmu_ctr_is_fw(idx))
 		clear_bit(idx, cpuc->used_fw_ctrs);
 	else
 		clear_bit(idx, cpuc->used_hw_ctrs);
 }
 
-static int pmu_event_find_cache(u64 config)
+static int sbi_pmu_event_find_cache(u64 config)
 {
 	unsigned int cache_type, cache_op, cache_result, ret;
 
@@ -623,7 +625,7 @@ static int pmu_event_find_cache(u64 config)
 	if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
 		return -EINVAL;
 
-	ret = pmu_cache_event_map[cache_type][cache_op][cache_result].event_idx;
+	ret = pmu_cache_event_sbi_map[cache_type][cache_op][cache_result].event_idx;
 
 	return ret;
 }
@@ -639,7 +641,7 @@ static bool pmu_sbi_is_fw_event(struct perf_event *event)
 		return false;
 }
 
-static int pmu_sbi_event_map(struct perf_event *event, u64 *econfig)
+static int rvpmu_sbi_event_map(struct perf_event *event, u64 *econfig)
 {
 	u32 type = event->attr.type;
 	u64 config = event->attr.config;
@@ -736,7 +738,7 @@ static int pmu_sbi_snapshot_setup(struct riscv_pmu *pmu, int cpu)
 	return 0;
 }
 
-static u64 pmu_sbi_ctr_read(struct perf_event *event)
+static u64 rvpmu_sbi_ctr_read(struct perf_event *event)
 {
 	struct hw_perf_event *hwc = &event->hw;
 	int idx = hwc->idx;
@@ -778,25 +780,25 @@ static u64 pmu_sbi_ctr_read(struct perf_event *event)
 	return val;
 }
 
-static void pmu_sbi_set_scounteren(void *arg)
+static void rvpmu_set_scounteren(void *arg)
 {
 	struct perf_event *event = (struct perf_event *)arg;
 
 	if (event->hw.idx != -1)
 		csr_write(CSR_SCOUNTEREN,
-			  csr_read(CSR_SCOUNTEREN) | BIT(pmu_sbi_csr_index(event)));
+			  csr_read(CSR_SCOUNTEREN) | BIT(rvpmu_csr_index(event)));
 }
 
-static void pmu_sbi_reset_scounteren(void *arg)
+static void rvpmu_reset_scounteren(void *arg)
 {
 	struct perf_event *event = (struct perf_event *)arg;
 
 	if (event->hw.idx != -1)
 		csr_write(CSR_SCOUNTEREN,
-			  csr_read(CSR_SCOUNTEREN) & ~BIT(pmu_sbi_csr_index(event)));
+			  csr_read(CSR_SCOUNTEREN) & ~BIT(rvpmu_csr_index(event)));
 }
 
-static void pmu_sbi_ctr_start(struct perf_event *event, u64 ival)
+static void rvpmu_sbi_ctr_start(struct perf_event *event, u64 ival)
 {
 	struct sbiret ret;
 	struct hw_perf_event *hwc = &event->hw;
@@ -816,10 +818,10 @@ static void pmu_sbi_ctr_start(struct perf_event *event, u64 ival)
 
 	if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
 	    (hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
-		pmu_sbi_set_scounteren((void *)event);
+		rvpmu_set_scounteren((void *)event);
 }
 
-static void pmu_sbi_ctr_stop(struct perf_event *event, unsigned long flag)
+static void rvpmu_sbi_ctr_stop(struct perf_event *event, unsigned long flag)
 {
 	struct sbiret ret;
 	struct hw_perf_event *hwc = &event->hw;
@@ -829,7 +831,7 @@ static void pmu_sbi_ctr_stop(struct perf_event *event, unsigned long flag)
 
 	if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
 	    (hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
-		pmu_sbi_reset_scounteren((void *)event);
+		rvpmu_reset_scounteren((void *)event);
 
 	if (sbi_pmu_snapshot_available())
 		flag |= SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT;
@@ -855,7 +857,7 @@ static void pmu_sbi_ctr_stop(struct perf_event *event, unsigned long flag)
 	}
 }
 
-static int pmu_sbi_find_num_ctrs(void)
+static int rvpmu_sbi_find_num_ctrs(void)
 {
 	struct sbiret ret;
 
@@ -866,7 +868,7 @@ static int pmu_sbi_find_num_ctrs(void)
 		return sbi_err_map_linux_errno(ret.error);
 }
 
-static int pmu_sbi_get_ctrinfo(int nctr, unsigned long *mask)
+static int rvpmu_sbi_get_ctrinfo(int nctr, unsigned long *mask)
 {
 	struct sbiret ret;
 	int i, num_hw_ctr = 0, num_fw_ctr = 0;
@@ -897,7 +899,7 @@ static int pmu_sbi_get_ctrinfo(int nctr, unsigned long *mask)
 	return 0;
 }
 
-static inline void pmu_sbi_stop_all(struct riscv_pmu *pmu)
+static inline void rvpmu_sbi_stop_all(struct riscv_pmu *pmu)
 {
 	/*
 	 * No need to check the error because we are disabling all the counters
@@ -907,7 +909,7 @@ static inline void pmu_sbi_stop_all(struct riscv_pmu *pmu)
 		  0, pmu->cmask, SBI_PMU_STOP_FLAG_RESET, 0, 0, 0);
 }
 
-static inline void pmu_sbi_stop_hw_ctrs(struct riscv_pmu *pmu)
+static inline void rvpmu_sbi_stop_hw_ctrs(struct riscv_pmu *pmu)
 {
 	struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
 	struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr;
@@ -951,8 +953,8 @@ static inline void pmu_sbi_stop_hw_ctrs(struct riscv_pmu *pmu)
  * while the overflowed counters need to be started with updated initialization
  * value.
  */
-static inline void pmu_sbi_start_ovf_ctrs_sbi(struct cpu_hw_events *cpu_hw_evt,
-					      u64 ctr_ovf_mask)
+static inline void rvpmu_sbi_start_ovf_ctrs_sbi(struct cpu_hw_events *cpu_hw_evt,
+						u64 ctr_ovf_mask)
 {
 	int idx = 0, i;
 	struct perf_event *event;
@@ -992,8 +994,8 @@ static inline void pmu_sbi_start_ovf_ctrs_sbi(struct cpu_hw_events *cpu_hw_evt,
 	}
 }
 
-static inline void pmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_hw_evt,
-						   u64 ctr_ovf_mask)
+static inline void rvpmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_hw_evt,
+						     u64 ctr_ovf_mask)
 {
 	int i, idx = 0;
 	struct perf_event *event;
@@ -1027,18 +1029,18 @@ static inline void pmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_hw_
 	}
 }
 
-static void pmu_sbi_start_overflow_mask(struct riscv_pmu *pmu,
-					u64 ctr_ovf_mask)
+static void rvpmu_sbi_start_overflow_mask(struct riscv_pmu *pmu,
+					  u64 ctr_ovf_mask)
 {
 	struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
 
 	if (sbi_pmu_snapshot_available())
-		pmu_sbi_start_ovf_ctrs_snapshot(cpu_hw_evt, ctr_ovf_mask);
+		rvpmu_sbi_start_ovf_ctrs_snapshot(cpu_hw_evt, ctr_ovf_mask);
 	else
-		pmu_sbi_start_ovf_ctrs_sbi(cpu_hw_evt, ctr_ovf_mask);
+		rvpmu_sbi_start_ovf_ctrs_sbi(cpu_hw_evt, ctr_ovf_mask);
 }
 
-static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)
+static irqreturn_t rvpmu_ovf_handler(int irq, void *dev)
 {
 	struct perf_sample_data data;
 	struct pt_regs *regs;
@@ -1070,7 +1072,7 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)
 	}
 
 	pmu = to_riscv_pmu(event->pmu);
-	pmu_sbi_stop_hw_ctrs(pmu);
+	rvpmu_sbi_stop_hw_ctrs(pmu);
 
 	/* Overflow status register should only be read after counter are stopped */
 	if (sbi_pmu_snapshot_available())
@@ -1139,13 +1141,55 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)
 		hw_evt->state = 0;
 	}
 
-	pmu_sbi_start_overflow_mask(pmu, overflowed_ctrs);
+	rvpmu_sbi_start_overflow_mask(pmu, overflowed_ctrs);
 	perf_sample_event_took(sched_clock() - start_clock);
 
 	return IRQ_HANDLED;
 }
 
-static int pmu_sbi_starting_cpu(unsigned int cpu, struct hlist_node *node)
+static void rvpmu_ctr_start(struct perf_event *event, u64 ival)
+{
+	rvpmu_sbi_ctr_start(event, ival);
+	/* TODO: Counter delegation implementation */
+}
+
+static void rvpmu_ctr_stop(struct perf_event *event, unsigned long flag)
+{
+	rvpmu_sbi_ctr_stop(event, flag);
+	/* TODO: Counter delegation implementation */
+}
+
+static int rvpmu_find_num_ctrs(void)
+{
+	return rvpmu_sbi_find_num_ctrs();
+	/* TODO: Counter delegation implementation */
+}
+
+static int rvpmu_get_ctrinfo(int nctr, unsigned long *mask)
+{
+	return rvpmu_sbi_get_ctrinfo(nctr, mask);
+	/* TODO: Counter delegation implementation */
+}
+
+static int rvpmu_event_map(struct perf_event *event, u64 *econfig)
+{
+	return rvpmu_sbi_event_map(event, econfig);
+	/* TODO: Counter delegation implementation */
+}
+
+static int rvpmu_ctr_get_idx(struct perf_event *event)
+{
+	return rvpmu_sbi_ctr_get_idx(event);
+	/* TODO: Counter delegation implementation */
+}
+
+static u64 rvpmu_ctr_read(struct perf_event *event)
+{
+	return rvpmu_sbi_ctr_read(event);
+	/* TODO: Counter delegation implementation */
+}
+
+static int rvpmu_starting_cpu(unsigned int cpu, struct hlist_node *node)
 {
 	struct riscv_pmu *pmu = hlist_entry_safe(node, struct riscv_pmu, node);
 	struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
@@ -1160,7 +1204,7 @@ static int pmu_sbi_starting_cpu(unsigned int cpu, struct hlist_node *node)
 		csr_write(CSR_SCOUNTEREN, 0x2);
 
 	/* Stop all the counters so that they can be enabled from perf */
-	pmu_sbi_stop_all(pmu);
+	rvpmu_sbi_stop_all(pmu);
 
 	if (riscv_pmu_use_irq) {
 		cpu_hw_evt->irq = riscv_pmu_irq;
@@ -1174,7 +1218,7 @@ static int pmu_sbi_starting_cpu(unsigned int cpu, struct hlist_node *node)
 	return 0;
 }
 
-static int pmu_sbi_dying_cpu(unsigned int cpu, struct hlist_node *node)
+static int rvpmu_dying_cpu(unsigned int cpu, struct hlist_node *node)
 {
 	if (riscv_pmu_use_irq) {
 		disable_percpu_irq(riscv_pmu_irq);
@@ -1189,7 +1233,7 @@ static int pmu_sbi_dying_cpu(unsigned int cpu, struct hlist_node *node)
 	return 0;
 }
 
-static int pmu_sbi_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pdev)
+static int rvpmu_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pdev)
 {
 	int ret;
 	struct cpu_hw_events __percpu *hw_events = pmu->hw_events;
@@ -1229,7 +1273,7 @@ static int pmu_sbi_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pde
 		return -ENODEV;
 	}
 
-	ret = request_percpu_irq(riscv_pmu_irq, pmu_sbi_ovf_handler, "riscv-pmu", hw_events);
+	ret = request_percpu_irq(riscv_pmu_irq, rvpmu_ovf_handler, "riscv-pmu", hw_events);
 	if (ret) {
 		pr_err("registering percpu irq failed [%d]\n", ret);
 		return ret;
@@ -1305,7 +1349,7 @@ static void riscv_pmu_destroy(struct riscv_pmu *pmu)
 	cpuhp_state_remove_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
 }
 
-static void pmu_sbi_event_init(struct perf_event *event)
+static void rvpmu_event_init(struct perf_event *event)
 {
 	/*
 	 * The permissions are set at event_init so that we do not depend
@@ -1319,7 +1363,7 @@ static void pmu_sbi_event_init(struct perf_event *event)
 		event->hw.flags |= PERF_EVENT_FLAG_LEGACY;
 }
 
-static void pmu_sbi_event_mapped(struct perf_event *event, struct mm_struct *mm)
+static void rvpmu_event_mapped(struct perf_event *event, struct mm_struct *mm)
 {
 	if (event->hw.flags & PERF_EVENT_FLAG_NO_USER_ACCESS)
 		return;
@@ -1347,14 +1391,14 @@ static void pmu_sbi_event_mapped(struct perf_event *event, struct mm_struct *mm)
 	 * that it is possible to do so to avoid any race.
 	 * And we must notify all cpus here because threads that currently run
 	 * on other cpus will try to directly access the counter too without
-	 * calling pmu_sbi_ctr_start.
+	 * calling rvpmu_sbi_ctr_start.
 	 */
 	if (event->hw.flags & PERF_EVENT_FLAG_USER_ACCESS)
 		on_each_cpu_mask(mm_cpumask(mm),
-				 pmu_sbi_set_scounteren, (void *)event, 1);
+				 rvpmu_set_scounteren, (void *)event, 1);
 }
 
-static void pmu_sbi_event_unmapped(struct perf_event *event, struct mm_struct *mm)
+static void rvpmu_event_unmapped(struct perf_event *event, struct mm_struct *mm)
 {
 	if (event->hw.flags & PERF_EVENT_FLAG_NO_USER_ACCESS)
 		return;
@@ -1376,7 +1420,7 @@ static void pmu_sbi_event_unmapped(struct perf_event *event, struct mm_struct *m
 
 	if (event->hw.flags & PERF_EVENT_FLAG_USER_ACCESS)
 		on_each_cpu_mask(mm_cpumask(mm),
-				 pmu_sbi_reset_scounteren, (void *)event, 1);
+				 rvpmu_reset_scounteren, (void *)event, 1);
 }
 
 static void riscv_pmu_update_counter_access(void *info)
@@ -1419,7 +1463,7 @@ static const struct ctl_table sbi_pmu_sysctl_table[] = {
 	},
 };
 
-static int pmu_sbi_device_probe(struct platform_device *pdev)
+static int rvpmu_device_probe(struct platform_device *pdev)
 {
 	struct riscv_pmu *pmu = NULL;
 	int ret = -ENODEV;
@@ -1430,7 +1474,7 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
 	if (!pmu)
 		return -ENOMEM;
 
-	num_counters = pmu_sbi_find_num_ctrs();
+	num_counters = rvpmu_find_num_ctrs();
 	if (num_counters < 0) {
 		pr_err("SBI PMU extension doesn't provide any counters\n");
 		goto out_free;
@@ -1443,10 +1487,10 @@ 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 (rvpmu_get_ctrinfo(num_counters, &cmask))
 		goto out_free;
 
-	ret = pmu_sbi_setup_irqs(pmu, pdev);
+	ret = rvpmu_setup_irqs(pmu, pdev);
 	if (ret < 0) {
 		pr_info("Perf sampling/filtering is not supported as sscof extension is not available\n");
 		pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
@@ -1456,17 +1500,17 @@ 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;
-	pmu->ctr_start = pmu_sbi_ctr_start;
-	pmu->ctr_stop = pmu_sbi_ctr_stop;
-	pmu->event_map = pmu_sbi_event_map;
-	pmu->ctr_get_idx = pmu_sbi_ctr_get_idx;
-	pmu->ctr_get_width = pmu_sbi_ctr_get_width;
-	pmu->ctr_clear_idx = pmu_sbi_ctr_clear_idx;
-	pmu->ctr_read = pmu_sbi_ctr_read;
-	pmu->event_init = pmu_sbi_event_init;
-	pmu->event_mapped = pmu_sbi_event_mapped;
-	pmu->event_unmapped = pmu_sbi_event_unmapped;
-	pmu->csr_index = pmu_sbi_csr_index;
+	pmu->ctr_start = rvpmu_ctr_start;
+	pmu->ctr_stop = rvpmu_ctr_stop;
+	pmu->event_map = rvpmu_event_map;
+	pmu->ctr_get_idx = rvpmu_ctr_get_idx;
+	pmu->ctr_get_width = rvpmu_ctr_get_width;
+	pmu->ctr_clear_idx = rvpmu_ctr_clear_idx;
+	pmu->ctr_read = rvpmu_ctr_read;
+	pmu->event_init = rvpmu_event_init;
+	pmu->event_mapped = rvpmu_event_mapped;
+	pmu->event_unmapped = rvpmu_event_unmapped;
+	pmu->csr_index = rvpmu_csr_index;
 
 	ret = riscv_pm_pmu_register(pmu);
 	if (ret)
@@ -1522,14 +1566,14 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
 	return ret;
 }
 
-static struct platform_driver pmu_sbi_driver = {
-	.probe		= pmu_sbi_device_probe,
+static struct platform_driver rvpmu_driver = {
+	.probe		= rvpmu_device_probe,
 	.driver		= {
 		.name	= RISCV_PMU_SBI_PDEV_NAME,
 	},
 };
 
-static int __init pmu_sbi_devinit(void)
+static int __init rvpmu_devinit(void)
 {
 	int ret;
 	struct platform_device *pdev;
@@ -1547,20 +1591,20 @@ static int __init pmu_sbi_devinit(void)
 
 	ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_RISCV_STARTING,
 				      "perf/riscv/pmu:starting",
-				      pmu_sbi_starting_cpu, pmu_sbi_dying_cpu);
+				      rvpmu_starting_cpu, rvpmu_dying_cpu);
 	if (ret) {
 		pr_err("CPU hotplug notifier could not be registered: %d\n",
 		       ret);
 		return ret;
 	}
 
-	ret = platform_driver_register(&pmu_sbi_driver);
+	ret = platform_driver_register(&rvpmu_driver);
 	if (ret)
 		return ret;
 
 	pdev = platform_device_register_simple(RISCV_PMU_SBI_PDEV_NAME, -1, NULL, 0);
 	if (IS_ERR(pdev)) {
-		platform_driver_unregister(&pmu_sbi_driver);
+		platform_driver_unregister(&rvpmu_driver);
 		return PTR_ERR(pdev);
 	}
 
@@ -1569,4 +1613,4 @@ static int __init pmu_sbi_devinit(void)
 
 	return ret;
 }
-device_initcall(pmu_sbi_devinit)
+device_initcall(rvpmu_devinit)

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 11/21] RISC-V: perf: Modify the counter discovery mechanism
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (9 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 10/21] RISC-V: perf: Restructure the SBI PMU code Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:17   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 12/21] RISC-V: perf: Add a mechanism to defined legacy event encoding Atish Patra
                   ` (9 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

If both counter delegation and SBI PMU is present, the counter
delegation will be used for hardware pmu counters while the SBI PMU
will be used for firmware counters. Thus, the driver has to probe
the counters info via SBI PMU to distinguish the firmware counters.

The hybrid scheme also requires improvements of the informational
logging messages to indicate the user about underlying interface
used for each use case.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 drivers/perf/riscv_pmu_sbi.c | 131 ++++++++++++++++++++++++++++++++-----------
 1 file changed, 97 insertions(+), 34 deletions(-)

diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 7f21c16003f0..57ab15beab3e 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -67,6 +67,20 @@ static bool sbi_v3_available;
 static DEFINE_STATIC_KEY_FALSE(sbi_pmu_snapshot_available);
 #define sbi_pmu_snapshot_available() \
 	static_branch_unlikely(&sbi_pmu_snapshot_available)
+static DEFINE_STATIC_KEY_FALSE(riscv_pmu_sbi_available);
+static DEFINE_STATIC_KEY_FALSE(riscv_pmu_cdeleg_available);
+
+/* Avoid unnecessary code patching in the one time booting path*/
+#define riscv_pmu_cdeleg_available_boot() \
+	static_key_enabled(&riscv_pmu_cdeleg_available)
+#define riscv_pmu_sbi_available_boot() \
+	static_key_enabled(&riscv_pmu_sbi_available)
+
+/* Perform a runtime code patching with static key */
+#define riscv_pmu_cdeleg_available() \
+	static_branch_unlikely(&riscv_pmu_cdeleg_available)
+#define riscv_pmu_sbi_available() \
+		static_branch_likely(&riscv_pmu_sbi_available)
 
 static struct attribute *riscv_arch_formats_attr[] = {
 	&format_attr_event.attr,
@@ -89,7 +103,8 @@ static int sysctl_perf_user_access __read_mostly = SYSCTL_USER_ACCESS;
 
 /*
  * This structure is SBI specific but counter delegation also require counter
- * width, csr mapping. Reuse it for now.
+ * width, csr mapping. Reuse it for now we can have firmware counters for
+ * platfroms with counter delegation support.
  * RISC-V doesn't have heterogeneous harts yet. This need to be part of
  * per_cpu in case of harts with different pmu counters
  */
@@ -101,6 +116,8 @@ static unsigned int riscv_pmu_irq;
 
 /* Cache the available counters in a bitmask */
 static unsigned long cmask;
+/* Cache the available firmware counters in another bitmask */
+static unsigned long firmware_cmask;
 
 static int sbi_pmu_event_find_cache(u64 config);
 struct sbi_pmu_event_data {
@@ -868,34 +885,38 @@ static int rvpmu_sbi_find_num_ctrs(void)
 		return sbi_err_map_linux_errno(ret.error);
 }
 
-static int rvpmu_sbi_get_ctrinfo(int nctr, unsigned long *mask)
+static u32 rvpmu_deleg_find_ctrs(void)
+{
+	/* TODO */
+	return 0;
+}
+
+static int rvpmu_sbi_get_ctrinfo(u32 nsbi_ctr, u32 *num_fw_ctr, u32 *num_hw_ctr)
 {
 	struct sbiret ret;
-	int i, num_hw_ctr = 0, num_fw_ctr = 0;
+	int i;
 	union sbi_pmu_ctr_info cinfo;
 
-	pmu_ctr_list = kzalloc_objs(*pmu_ctr_list, nctr);
-	if (!pmu_ctr_list)
-		return -ENOMEM;
-
-	for (i = 0; i < nctr; i++) {
+	for (i = 0; i < nsbi_ctr; i++) {
 		ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_GET_INFO, i, 0, 0, 0, 0, 0);
 		if (ret.error)
 			/* The logical counter ids are not expected to be contiguous */
 			continue;
 
-		*mask |= BIT(i);
-
 		cinfo.value = ret.value;
-		if (cinfo.type == SBI_PMU_CTR_TYPE_FW)
-			num_fw_ctr++;
-		else
-			num_hw_ctr++;
-		pmu_ctr_list[i].value = cinfo.value;
+		if (cinfo.type == SBI_PMU_CTR_TYPE_FW) {
+			/* Track firmware counters in a different mask */
+			firmware_cmask |= BIT(i);
+			pmu_ctr_list[i].value = cinfo.value;
+			*num_fw_ctr = *num_fw_ctr + 1;
+		} else if (cinfo.type == SBI_PMU_CTR_TYPE_HW &&
+			   !riscv_pmu_cdeleg_available_boot()) {
+			*num_hw_ctr = *num_hw_ctr + 1;
+			cmask |= BIT(i);
+			pmu_ctr_list[i].value = cinfo.value;
+		}
 	}
 
-	pr_info("%d firmware and %d hardware counters\n", num_fw_ctr, num_hw_ctr);
-
 	return 0;
 }
 
@@ -1159,16 +1180,42 @@ static void rvpmu_ctr_stop(struct perf_event *event, unsigned long flag)
 	/* TODO: Counter delegation implementation */
 }
 
-static int rvpmu_find_num_ctrs(void)
+static int rvpmu_find_ctrs(void)
 {
-	return rvpmu_sbi_find_num_ctrs();
-	/* TODO: Counter delegation implementation */
-}
+	u32 num_sbi_counters = 0, num_deleg_counters = 0;
+	u32 num_hw_ctr = 0, num_fw_ctr = 0, num_ctr = 0;
+	/*
+	 * We don't know how many firmware counters are available. Just allocate
+	 * for maximum counters the driver can support. The default is 64 anyways.
+	 */
+	pmu_ctr_list = kcalloc(RISCV_MAX_COUNTERS, sizeof(*pmu_ctr_list),
+			       GFP_KERNEL);
+	if (!pmu_ctr_list)
+		return -ENOMEM;
 
-static int rvpmu_get_ctrinfo(int nctr, unsigned long *mask)
-{
-	return rvpmu_sbi_get_ctrinfo(nctr, mask);
-	/* TODO: Counter delegation implementation */
+	if (riscv_pmu_cdeleg_available_boot())
+		num_deleg_counters = rvpmu_deleg_find_ctrs();
+
+	/* This is required for firmware counters even if the above is true */
+	if (riscv_pmu_sbi_available_boot())
+		num_sbi_counters = rvpmu_sbi_find_num_ctrs();
+
+	if (num_sbi_counters > RISCV_MAX_COUNTERS || num_deleg_counters > RISCV_MAX_COUNTERS)
+		return -ENOSPC;
+
+	/* cache all the information about counters now */
+	if (riscv_pmu_sbi_available_boot())
+		rvpmu_sbi_get_ctrinfo(num_sbi_counters, &num_fw_ctr, &num_hw_ctr);
+
+	if (riscv_pmu_cdeleg_available_boot()) {
+		pr_info("%u firmware and %u hardware counters\n", num_fw_ctr, num_deleg_counters);
+		num_ctr = num_fw_ctr + num_deleg_counters;
+	} else {
+		pr_info("%u firmware and %u hardware counters\n", num_fw_ctr, num_hw_ctr);
+		num_ctr = num_sbi_counters;
+	}
+
+	return num_ctr;
 }
 
 static int rvpmu_event_map(struct perf_event *event, u64 *econfig)
@@ -1469,12 +1516,21 @@ static int rvpmu_device_probe(struct platform_device *pdev)
 	int ret = -ENODEV;
 	int num_counters;
 
-	pr_info("SBI PMU extension is available\n");
+	if (riscv_pmu_cdeleg_available_boot()) {
+		pr_info("hpmcounters will use the counter delegation ISA extension\n");
+		if (riscv_pmu_sbi_available_boot())
+			pr_info("Firmware counters will use SBI PMU extension\n");
+		else
+			pr_info("Firmware counters will not be available as SBI PMU extension is not present\n");
+	} else if (riscv_pmu_sbi_available_boot()) {
+		pr_info("Both hpmcounters and firmware counters will use SBI PMU extension\n");
+	}
+
 	pmu = riscv_pmu_alloc();
 	if (!pmu)
 		return -ENOMEM;
 
-	num_counters = rvpmu_find_num_ctrs();
+	num_counters = rvpmu_find_ctrs();
 	if (num_counters < 0) {
 		pr_err("SBI PMU extension doesn't provide any counters\n");
 		goto out_free;
@@ -1486,9 +1542,6 @@ static int rvpmu_device_probe(struct platform_device *pdev)
 		pr_info("SBI returned more than maximum number of counters. Limiting the number of counters to %d\n", num_counters);
 	}
 
-	/* cache all the information about counters now */
-	if (rvpmu_get_ctrinfo(num_counters, &cmask))
-		goto out_free;
 
 	ret = rvpmu_setup_irqs(pmu, pdev);
 	if (ret < 0) {
@@ -1578,13 +1631,23 @@ static int __init rvpmu_devinit(void)
 	int ret;
 	struct platform_device *pdev;
 
-	if (sbi_spec_version < sbi_mk_version(0, 3) ||
-	    !sbi_probe_extension(SBI_EXT_PMU)) {
-		return 0;
-	}
+	if (sbi_spec_version >= sbi_mk_version(0, 3) &&
+	    sbi_probe_extension(SBI_EXT_PMU))
+		static_branch_enable(&riscv_pmu_sbi_available);
 
 	if (sbi_spec_version >= sbi_mk_version(2, 0))
 		sbi_v2_available = true;
+	/*
+	 * We need all three extensions to be present to access the counters
+	 * in S-mode via Supervisor Counter delegation.
+	 */
+	if (riscv_isa_extension_available(NULL, SSCCFG) &&
+	    riscv_isa_extension_available(NULL, SMCDELEG) &&
+	    riscv_isa_extension_available(NULL, SSCSRIND))
+		static_branch_enable(&riscv_pmu_cdeleg_available);
+
+	if (!(riscv_pmu_sbi_available_boot() || riscv_pmu_cdeleg_available_boot()))
+		return 0;
 
 	if (sbi_spec_version >= sbi_mk_version(3, 0))
 		sbi_v3_available = true;

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 12/21] RISC-V: perf: Add a mechanism to defined legacy event encoding
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (10 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 11/21] RISC-V: perf: Modify the counter discovery mechanism Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:16   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 13/21] RISC-V: perf: Implement supervisor counter delegation support Atish Patra
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

RISC-V ISA doesn't define any standard event encodings or specify
any event to counter mapping. Thus, event encoding information
and corresponding counter mapping fot those events needs to be
provided in the driver for each vendor.

Add a framework to support that. The individual platform events
will be added later.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 drivers/perf/riscv_pmu_sbi.c   | 54 +++++++++++++++++++++++++++++++++++++++++-
 include/linux/perf/riscv_pmu.h | 13 ++++++++++
 2 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 57ab15beab3e..46a25979e95e 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -379,6 +379,56 @@ static int pmu_sbi_check_event_info(void)
 	return result;
 }
 
+/*
+ * Vendor specific PMU events.
+ */
+struct riscv_pmu_event {
+	u64 event_id;
+	u32 counter_mask;
+};
+
+struct riscv_vendor_pmu_events {
+	unsigned long vendorid;
+	unsigned long archid;
+	unsigned long implid;
+	const struct riscv_pmu_event *hw_event_map;
+	const struct riscv_pmu_event (*cache_event_map)[PERF_COUNT_HW_CACHE_OP_MAX]
+						       [PERF_COUNT_HW_CACHE_RESULT_MAX];
+};
+
+#define RISCV_VENDOR_PMU_EVENTS(_vendorid, _archid, _implid, _hw_event_map, _cache_event_map) \
+	{ .vendorid = _vendorid, .archid = _archid, .implid = _implid, \
+	  .hw_event_map = _hw_event_map, .cache_event_map = _cache_event_map },
+
+static struct riscv_vendor_pmu_events pmu_vendor_events_table[] = {
+};
+
+static const struct riscv_pmu_event *current_pmu_hw_event_map;
+static const struct riscv_pmu_event (*current_pmu_cache_event_map)[PERF_COUNT_HW_CACHE_OP_MAX]
+							   [PERF_COUNT_HW_CACHE_RESULT_MAX];
+
+static void rvpmu_vendor_register_events(void)
+{
+	int cpu = raw_smp_processor_id();
+	unsigned long vendor_id = riscv_cached_mvendorid(cpu);
+	unsigned long impl_id = riscv_cached_mimpid(cpu);
+	unsigned long arch_id = riscv_cached_marchid(cpu);
+
+	for (int i = 0; i < ARRAY_SIZE(pmu_vendor_events_table); i++) {
+		if (pmu_vendor_events_table[i].vendorid == vendor_id &&
+		    pmu_vendor_events_table[i].implid == impl_id &&
+		    pmu_vendor_events_table[i].archid == arch_id) {
+			current_pmu_hw_event_map = pmu_vendor_events_table[i].hw_event_map;
+			current_pmu_cache_event_map = pmu_vendor_events_table[i].cache_event_map;
+			break;
+		}
+	}
+
+	if (!current_pmu_hw_event_map || !current_pmu_cache_event_map) {
+		pr_info("No default PMU events found\n");
+	}
+}
+
 static void rvpmu_sbi_check_event(struct sbi_pmu_event_data *edata)
 {
 	struct sbiret ret;
@@ -1643,8 +1693,10 @@ static int __init rvpmu_devinit(void)
 	 */
 	if (riscv_isa_extension_available(NULL, SSCCFG) &&
 	    riscv_isa_extension_available(NULL, SMCDELEG) &&
-	    riscv_isa_extension_available(NULL, SSCSRIND))
+	    riscv_isa_extension_available(NULL, SSCSRIND)) {
 		static_branch_enable(&riscv_pmu_cdeleg_available);
+		rvpmu_vendor_register_events();
+	}
 
 	if (!(riscv_pmu_sbi_available_boot() || riscv_pmu_cdeleg_available_boot()))
 		return 0;
diff --git a/include/linux/perf/riscv_pmu.h b/include/linux/perf/riscv_pmu.h
index f82a28040594..6c75106989b6 100644
--- a/include/linux/perf/riscv_pmu.h
+++ b/include/linux/perf/riscv_pmu.h
@@ -28,6 +28,19 @@
 
 #define RISCV_PMU_CONFIG1_GUEST_EVENTS 0x1
 
+#define HW_OP_UNSUPPORTED		0xFFFF
+#define CACHE_OP_UNSUPPORTED		0xFFFF
+
+#define PERF_MAP_ALL_UNSUPPORTED					\
+	[0 ... PERF_COUNT_HW_MAX - 1] = {HW_OP_UNSUPPORTED, 0x0}
+
+#define PERF_CACHE_MAP_ALL_UNSUPPORTED					\
+[0 ... C(MAX) - 1] = {							\
+	[0 ... C(OP_MAX) - 1] = {					\
+		[0 ... C(RESULT_MAX) - 1] = {CACHE_OP_UNSUPPORTED, 0x0}	\
+	},								\
+}
+
 struct cpu_hw_events {
 	/* currently enabled events */
 	int			n_events;

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 13/21] RISC-V: perf: Implement supervisor counter delegation support
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (11 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 12/21] RISC-V: perf: Add a mechanism to defined legacy event encoding Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:23   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 14/21] RISC-V: perf: Skip PMU SBI extension when not implemented Atish Patra
                   ` (7 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

There are few new RISC-V ISA exensions (ssccfg, sscsrind, smcntrpmf) which
allows the hpmcounter/hpmevents to be programmed directly from S-mode. The
implementation detects the ISA extension at runtime and uses them if
available instead of SBI PMU extension. SBI PMU extension will still be
used for firmware counters if the user requests it.

The current linux driver relies on event encoding defined by SBI PMU
specification for standard perf events. However, there are no standard
event encoding available in the ISA. In the future, we may want to
decouple the counter delegation and SBI PMU completely. In that case,
counter delegation supported platforms must rely on the event encoding
defined in the perf json file or in the pmu driver.

For firmware events, it will continue to use the SBI PMU encoding as
one can not support firmware event without SBI PMU.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 arch/riscv/include/asm/csr.h   |   1 +
 drivers/perf/riscv_pmu_sbi.c   | 574 +++++++++++++++++++++++++++++++++--------
 include/linux/perf/riscv_pmu.h |   3 +
 3 files changed, 473 insertions(+), 105 deletions(-)

diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
index 26cb78dee2fd..25ebf853bfef 100644
--- a/arch/riscv/include/asm/csr.h
+++ b/arch/riscv/include/asm/csr.h
@@ -266,6 +266,7 @@
 #endif
 
 #define SISELECT_SSCCFG_BASE		0x40
+#define HPMEVENT_MASK			GENMASK_ULL(63, 56)
 
 /* mseccfg bits */
 #define MSECCFG_PMM			ENVCFG_PMM
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 46a25979e95e..1f16df9d0dd0 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -27,6 +27,8 @@
 #include <asm/cpufeature.h>
 #include <asm/vendor_extensions.h>
 #include <asm/vendor_extensions/andes.h>
+#include <asm/hwcap.h>
+#include <asm/csr_ind.h>
 
 #define ALT_SBI_PMU_OVERFLOW(__ovl)					\
 asm volatile(ALTERNATIVE_2(						\
@@ -59,7 +61,20 @@ asm volatile(ALTERNATIVE(						\
 #define PERF_EVENT_FLAG_USER_ACCESS	BIT(SYSCTL_USER_ACCESS)
 #define PERF_EVENT_FLAG_LEGACY		BIT(SYSCTL_LEGACY)
 
-PMU_FORMAT_ATTR(event, "config:0-55");
+#define RVPMU_SBI_PMU_FORMAT_ATTR	"config:0-47"
+#define RVPMU_CDELEG_PMU_FORMAT_ATTR	"config:0-55"
+
+static ssize_t __maybe_unused rvpmu_format_show(struct device *dev, struct device_attribute *attr,
+						char *buf);
+
+#define RVPMU_ATTR_ENTRY(_name, _func, _config)	(			\
+	&((struct dev_ext_attribute[]) {				\
+		{ __ATTR(_name, 0444, _func, NULL), (void *)_config }	\
+	})[0].attr.attr)
+
+#define RVPMU_FORMAT_ATTR_ENTRY(_name, _config) \
+	RVPMU_ATTR_ENTRY(_name, rvpmu_format_show, (char *)_config)
+
 PMU_FORMAT_ATTR(firmware, "config:62-63");
 
 static bool sbi_v2_available;
@@ -67,7 +82,11 @@ static bool sbi_v3_available;
 static DEFINE_STATIC_KEY_FALSE(sbi_pmu_snapshot_available);
 #define sbi_pmu_snapshot_available() \
 	static_branch_unlikely(&sbi_pmu_snapshot_available)
+
 static DEFINE_STATIC_KEY_FALSE(riscv_pmu_sbi_available);
+#define riscv_pmu_sbi_available() \
+		static_branch_likely(&riscv_pmu_sbi_available)
+
 static DEFINE_STATIC_KEY_FALSE(riscv_pmu_cdeleg_available);
 
 /* Avoid unnecessary code patching in the one time booting path*/
@@ -82,19 +101,35 @@ static DEFINE_STATIC_KEY_FALSE(riscv_pmu_cdeleg_available);
 #define riscv_pmu_sbi_available() \
 		static_branch_likely(&riscv_pmu_sbi_available)
 
-static struct attribute *riscv_arch_formats_attr[] = {
-	&format_attr_event.attr,
+static struct attribute *riscv_sbi_pmu_formats_attr[] = {
+	RVPMU_FORMAT_ATTR_ENTRY(event, RVPMU_SBI_PMU_FORMAT_ATTR),
+	&format_attr_firmware.attr,
+	NULL,
+};
+
+static struct attribute_group riscv_sbi_pmu_format_group = {
+	.name = "format",
+	.attrs = riscv_sbi_pmu_formats_attr,
+};
+
+static const struct attribute_group *riscv_sbi_pmu_attr_groups[] = {
+	&riscv_sbi_pmu_format_group,
+	NULL,
+};
+
+static struct attribute *riscv_cdeleg_pmu_formats_attr[] = {
+	RVPMU_FORMAT_ATTR_ENTRY(event, RVPMU_CDELEG_PMU_FORMAT_ATTR),
 	&format_attr_firmware.attr,
 	NULL,
 };
 
-static struct attribute_group riscv_pmu_format_group = {
+static struct attribute_group riscv_cdeleg_pmu_format_group = {
 	.name = "format",
-	.attrs = riscv_arch_formats_attr,
+	.attrs = riscv_cdeleg_pmu_formats_attr,
 };
 
-static const struct attribute_group *riscv_pmu_attr_groups[] = {
-	&riscv_pmu_format_group,
+static const struct attribute_group *riscv_cdeleg_pmu_attr_groups[] = {
+	&riscv_cdeleg_pmu_format_group,
 	NULL,
 };
 
@@ -466,6 +501,14 @@ static void rvpmu_sbi_check_std_events(struct work_struct *work)
 
 static DECLARE_WORK(check_std_events_work, rvpmu_sbi_check_std_events);
 
+static ssize_t rvpmu_format_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct dev_ext_attribute *eattr = container_of(attr,
+				struct dev_ext_attribute, attr);
+	return sysfs_emit(buf, "%s\n", (char *)eattr->var);
+}
+
 static int rvpmu_ctr_get_width(int idx)
 {
 	return pmu_ctr_list[idx].width;
@@ -583,6 +626,38 @@ static uint8_t rvpmu_csr_index(struct perf_event *event)
 	return pmu_ctr_list[event->hw.idx].csr - CSR_CYCLE;
 }
 
+static uint64_t get_deleg_priv_filter_bits(struct perf_event *event)
+{
+	u64 priv_filter_bits = 0;
+	bool guest_events = false;
+
+	if (event->attr.config1 & RISCV_PMU_CONFIG1_GUEST_EVENTS)
+		guest_events = true;
+	if (event->attr.exclude_kernel)
+		priv_filter_bits |= guest_events ? HPMEVENT_VSINH : HPMEVENT_SINH;
+	if (event->attr.exclude_user)
+		priv_filter_bits |= guest_events ? HPMEVENT_VUINH : HPMEVENT_UINH;
+	if (guest_events && event->attr.exclude_hv)
+		priv_filter_bits |= HPMEVENT_SINH;
+	if (event->attr.exclude_host)
+		priv_filter_bits |= HPMEVENT_UINH | HPMEVENT_SINH;
+	if (event->attr.exclude_guest)
+		priv_filter_bits |= HPMEVENT_VSINH | HPMEVENT_VUINH;
+
+	return priv_filter_bits;
+}
+
+static bool pmu_sbi_is_fw_event(struct perf_event *event)
+{
+	u32 type = event->attr.type;
+	u64 config = event->attr.config;
+
+	if (type == PERF_TYPE_RAW && ((config >> 63) == 1))
+		return true;
+	else
+		return false;
+}
+
 static unsigned long rvpmu_sbi_get_filter_flags(struct perf_event *event)
 {
 	unsigned long cflags = 0;
@@ -611,7 +686,8 @@ static int rvpmu_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;
+	u64 cbase = 0;
+	unsigned long ctr_mask = rvpmu->cmask;
 	unsigned long cflags = 0;
 
 	cflags = rvpmu_sbi_get_filter_flags(event);
@@ -624,21 +700,23 @@ static int rvpmu_sbi_ctr_get_idx(struct perf_event *event)
 	if ((hwc->flags & PERF_EVENT_FLAG_LEGACY) && (event->attr.type == PERF_TYPE_HARDWARE)) {
 		if (event->attr.config == PERF_COUNT_HW_CPU_CYCLES) {
 			cflags |= SBI_PMU_CFG_FLAG_SKIP_MATCH;
-			cmask = 1;
+			ctr_mask = 1;
 		} else if (event->attr.config == PERF_COUNT_HW_INSTRUCTIONS) {
 			cflags |= SBI_PMU_CFG_FLAG_SKIP_MATCH;
-			cmask = BIT(CSR_INSTRET - CSR_CYCLE);
+			ctr_mask = BIT(CSR_INSTRET - CSR_CYCLE);
 		}
+	} else if (pmu_sbi_is_fw_event(event)) {
+		ctr_mask = firmware_cmask;
 	}
 
 	/* 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,
+			ctr_mask, 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);
+			ctr_mask, cflags, hwc->event_base, hwc->config, 0);
 #endif
 	if (ret.error) {
 		pr_debug("Not able to find a counter for event %lx config %llx\n",
@@ -647,7 +725,7 @@ static int rvpmu_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, &ctr_mask) || !pmu_ctr_list[idx].value)
 		return -ENOENT;
 
 	/* Additional sanity check for the counter id */
@@ -697,29 +775,96 @@ static int sbi_pmu_event_find_cache(u64 config)
 	return ret;
 }
 
-static bool pmu_sbi_is_fw_event(struct perf_event *event)
+static int rvpmu_sbi_event_map(struct perf_event *event, u64 *econfig)
 {
 	u32 type = event->attr.type;
 	u64 config = event->attr.config;
 
-	if ((type == PERF_TYPE_RAW) && ((config >> 63) == 1))
-		return true;
-	else
-		return false;
+	/*
+	 * Ensure we are finished checking standard hardware events for
+	 * validity before allowing userspace to configure any events.
+	 */
+	flush_work(&check_std_events_work);
+
+	return riscv_pmu_get_event_info(type, config, econfig);
 }
 
-static int rvpmu_sbi_event_map(struct perf_event *event, u64 *econfig)
+static int cdeleg_pmu_event_find_cache(u64 config, u64 *eventid, uint32_t *counter_mask)
+{
+	unsigned int cache_type, cache_op, cache_result;
+
+	if (!current_pmu_cache_event_map)
+		return -ENOENT;
+
+	cache_type = (config >>  0) & 0xff;
+	if (cache_type >= PERF_COUNT_HW_CACHE_MAX)
+		return -EINVAL;
+
+	cache_op = (config >>  8) & 0xff;
+	if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX)
+		return -EINVAL;
+
+	cache_result = (config >> 16) & 0xff;
+	if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
+		return -EINVAL;
+
+	if (eventid)
+		*eventid = current_pmu_cache_event_map[cache_type][cache_op]
+						      [cache_result].event_id;
+	if (counter_mask)
+		*counter_mask = current_pmu_cache_event_map[cache_type][cache_op]
+							   [cache_result].counter_mask;
+
+	return 0;
+}
+
+static int rvpmu_cdeleg_event_map(struct perf_event *event, u64 *econfig)
 {
 	u32 type = event->attr.type;
 	u64 config = event->attr.config;
+	int ret = 0;
 
 	/*
-	 * Ensure we are finished checking standard hardware events for
-	 * validity before allowing userspace to configure any events.
+	 * There are two ways standard perf events can be mapped to platform specific
+	 * encoding.
+	 * 1. The vendor may specify the encodings in the driver.
+	 * 2. The Perf tool for RISC-V may remap the standard perf event to platform
+	 * specific encoding.
+	 *
+	 * As RISC-V ISA doesn't define any standard event encoding. Thus, perf tool allows
+	 * vendor to define it via json file. The encoding defined in the json will override
+	 * the perf legacy encoding. However, some user may want to run performance
+	 * monitoring without perf tool as well. That's why, vendors may specify the event
+	 * encoding in the driver as well if they want to support that use case too.
+	 * If an encoding is defined in the json, it will be encoded as a raw event.
 	 */
-	flush_work(&check_std_events_work);
 
-	return riscv_pmu_get_event_info(type, config, econfig);
+	switch (type) {
+	case PERF_TYPE_HARDWARE:
+		if (config >= PERF_COUNT_HW_MAX)
+			return -EINVAL;
+		if (!current_pmu_hw_event_map)
+			return -ENOENT;
+
+		*econfig = current_pmu_hw_event_map[config].event_id;
+		if (*econfig == HW_OP_UNSUPPORTED)
+			ret = -ENOENT;
+		break;
+	case PERF_TYPE_HW_CACHE:
+		ret = cdeleg_pmu_event_find_cache(config, econfig, NULL);
+		if (*econfig == HW_OP_UNSUPPORTED)
+			ret = -ENOENT;
+		break;
+	case PERF_TYPE_RAW:
+		*econfig = config & RISCV_PMU_DELEG_RAW_EVENT_MASK;
+		break;
+	default:
+		ret = -ENOENT;
+		break;
+	}
+
+	/* event_base is not used for counter delegation */
+	return ret;
 }
 
 static void pmu_sbi_snapshot_free(struct riscv_pmu *pmu)
@@ -805,7 +950,7 @@ static int pmu_sbi_snapshot_setup(struct riscv_pmu *pmu, int cpu)
 	return 0;
 }
 
-static u64 rvpmu_sbi_ctr_read(struct perf_event *event)
+static u64 rvpmu_ctr_read(struct perf_event *event)
 {
 	struct hw_perf_event *hwc = &event->hw;
 	int idx = hwc->idx;
@@ -882,10 +1027,6 @@ static void rvpmu_sbi_ctr_start(struct perf_event *event, u64 ival)
 	if (ret.error && (ret.error != SBI_ERR_ALREADY_STARTED))
 		pr_err("Starting counter idx %d failed with error %d\n",
 			hwc->idx, sbi_err_map_linux_errno(ret.error));
-
-	if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
-	    (hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
-		rvpmu_set_scounteren((void *)event);
 }
 
 static void rvpmu_sbi_ctr_stop(struct perf_event *event, unsigned long flag)
@@ -896,10 +1037,6 @@ static void rvpmu_sbi_ctr_stop(struct perf_event *event, unsigned long flag)
 	struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
 	struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr;
 
-	if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
-	    (hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
-		rvpmu_reset_scounteren((void *)event);
-
 	if (sbi_pmu_snapshot_available())
 		flag |= SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT;
 
@@ -935,12 +1072,6 @@ static int rvpmu_sbi_find_num_ctrs(void)
 		return sbi_err_map_linux_errno(ret.error);
 }
 
-static u32 rvpmu_deleg_find_ctrs(void)
-{
-	/* TODO */
-	return 0;
-}
-
 static int rvpmu_sbi_get_ctrinfo(u32 nsbi_ctr, u32 *num_fw_ctr, u32 *num_hw_ctr)
 {
 	struct sbiret ret;
@@ -1018,55 +1149,75 @@ static inline void rvpmu_sbi_stop_hw_ctrs(struct riscv_pmu *pmu)
 	}
 }
 
-/*
- * This function starts all the used counters in two step approach.
- * Any counter that did not overflow can be start in a single step
- * while the overflowed counters need to be started with updated initialization
- * value.
- */
-static inline void rvpmu_sbi_start_ovf_ctrs_sbi(struct cpu_hw_events *cpu_hw_evt,
-						u64 ctr_ovf_mask)
+static void rvpmu_deleg_ctr_start_mask(unsigned long mask)
 {
-	int idx = 0, i;
-	struct perf_event *event;
-	unsigned long flag = SBI_PMU_START_FLAG_SET_INIT_VALUE;
-	unsigned long ctr_start_mask = 0;
-	uint64_t max_period;
-	struct hw_perf_event *hwc;
-	u64 init_val = 0;
+	unsigned long scountinhibit_val = 0;
 
-	for (i = 0; i < BITS_TO_LONGS(RISCV_MAX_COUNTERS); i++) {
-		ctr_start_mask = cpu_hw_evt->used_hw_ctrs[i] & ~ctr_ovf_mask;
-		/* Start all the counters that did not overflow in a single shot */
-		if (ctr_start_mask) {
-			sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, i * BITS_PER_LONG,
-				  ctr_start_mask, 0, 0, 0, 0);
-		}
-	}
+	scountinhibit_val = csr_read(CSR_SCOUNTINHIBIT);
+	scountinhibit_val &= ~mask;
+
+	csr_write(CSR_SCOUNTINHIBIT, scountinhibit_val);
+}
+
+static void rvpmu_deleg_ctr_enable_irq(struct perf_event *event)
+{
+	unsigned long hpmevent_curr;
+	unsigned long of_mask;
+	struct hw_perf_event *hwc = &event->hw;
+	int counter_idx = hwc->idx;
+	unsigned long sip_val = csr_read(CSR_SIP);
+
+	if (!is_sampling_event(event) || (sip_val & SIP_LCOFIP))
+		return;
 
-	/* Reinitialize and start all the counter that overflowed */
-	while (ctr_ovf_mask) {
-		if (ctr_ovf_mask & 0x01) {
-			event = cpu_hw_evt->events[idx];
-			hwc = &event->hw;
-			max_period = riscv_pmu_ctr_get_width_mask(event);
-			init_val = local64_read(&hwc->prev_count) & max_period;
 #if defined(CONFIG_32BIT)
-			sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, idx, 1,
-				  flag, init_val, init_val >> 32, 0);
+	hpmevent_curr = csr_ind_read(CSR_SIREG5, SISELECT_SSCCFG_BASE, counter_idx);
+	of_mask = (u32)~HPMEVENTH_OF;
 #else
-			sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, idx, 1,
-				  flag, init_val, 0, 0);
+	hpmevent_curr = csr_ind_read(CSR_SIREG2, SISELECT_SSCCFG_BASE, counter_idx);
+	of_mask = ~HPMEVENT_OF;
 #endif
-			perf_event_update_userpage(event);
-		}
-		ctr_ovf_mask = ctr_ovf_mask >> 1;
-		idx++;
-	}
+
+	hpmevent_curr &= of_mask;
+#if defined(CONFIG_32BIT)
+	csr_ind_write(CSR_SIREG4, SISELECT_SSCCFG_BASE, counter_idx, hpmevent_curr);
+#else
+	csr_ind_write(CSR_SIREG2, SISELECT_SSCCFG_BASE, counter_idx, hpmevent_curr);
+#endif
+}
+
+static void rvpmu_deleg_ctr_start(struct perf_event *event, u64 ival)
+{
+	unsigned long scountinhibit_val = 0;
+	struct hw_perf_event *hwc = &event->hw;
+
+#if defined(CONFIG_32BIT)
+	csr_ind_write(CSR_SIREG, SISELECT_SSCCFG_BASE, hwc->idx, ival & 0xFFFFFFFF);
+	csr_ind_write(CSR_SIREG4, SISELECT_SSCCFG_BASE, hwc->idx, ival >> BITS_PER_LONG);
+#else
+	csr_ind_write(CSR_SIREG, SISELECT_SSCCFG_BASE, hwc->idx, ival);
+#endif
+
+	rvpmu_deleg_ctr_enable_irq(event);
+
+	scountinhibit_val = csr_read(CSR_SCOUNTINHIBIT);
+	scountinhibit_val &= ~(1 << hwc->idx);
+
+	csr_write(CSR_SCOUNTINHIBIT, scountinhibit_val);
+}
+
+static void rvpmu_deleg_ctr_stop_mask(unsigned long mask)
+{
+	unsigned long scountinhibit_val = 0;
+
+	scountinhibit_val = csr_read(CSR_SCOUNTINHIBIT);
+	scountinhibit_val |= mask;
+
+	csr_write(CSR_SCOUNTINHIBIT, scountinhibit_val);
 }
 
-static inline void rvpmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_hw_evt,
-						     u64 ctr_ovf_mask)
+static void rvpmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_hw_evt,
+					      u64 ctr_ovf_mask)
 {
 	int i, idx = 0;
 	struct perf_event *event;
@@ -1100,15 +1251,53 @@ static inline void rvpmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_h
 	}
 }
 
-static void rvpmu_sbi_start_overflow_mask(struct riscv_pmu *pmu,
-					  u64 ctr_ovf_mask)
+/*
+ * This function starts all the used counters in two step approach.
+ * Any counter that did not overflow can be start in a single step
+ * while the overflowed counters need to be started with updated initialization
+ * value.
+ */
+static void rvpmu_start_overflow_mask(struct riscv_pmu *pmu, u64 ctr_ovf_mask)
 {
+	int idx = 0, i;
+	struct perf_event *event;
+	unsigned long ctr_start_mask = 0;
+	u64 max_period, init_val = 0;
+	struct hw_perf_event *hwc;
 	struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
 
 	if (sbi_pmu_snapshot_available())
-		rvpmu_sbi_start_ovf_ctrs_snapshot(cpu_hw_evt, ctr_ovf_mask);
-	else
-		rvpmu_sbi_start_ovf_ctrs_sbi(cpu_hw_evt, ctr_ovf_mask);
+		return rvpmu_sbi_start_ovf_ctrs_snapshot(cpu_hw_evt, ctr_ovf_mask);
+
+	/* Start all the counters that did not overflow */
+	if (riscv_pmu_cdeleg_available()) {
+		ctr_start_mask = cpu_hw_evt->used_hw_ctrs[0] & ~ctr_ovf_mask;
+		rvpmu_deleg_ctr_start_mask(ctr_start_mask);
+	} else {
+		for (i = 0; i < BITS_TO_LONGS(RISCV_MAX_COUNTERS); i++) {
+			ctr_start_mask = cpu_hw_evt->used_hw_ctrs[i] & ~ctr_ovf_mask;
+			/* Start all the counters that did not overflow in a single shot */
+			sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, i * BITS_PER_LONG,
+				  ctr_start_mask, 0, 0, 0, 0);
+		}
+	}
+
+	/* Reinitialize and start all the counter that overflowed */
+	while (ctr_ovf_mask) {
+		if (ctr_ovf_mask & 0x01) {
+			event = cpu_hw_evt->events[idx];
+			hwc = &event->hw;
+			max_period = riscv_pmu_ctr_get_width_mask(event);
+			init_val = local64_read(&hwc->prev_count) & max_period;
+			if (riscv_pmu_cdeleg_available())
+				rvpmu_deleg_ctr_start(event, init_val);
+			else
+				rvpmu_sbi_ctr_start(event, init_val);
+			perf_event_update_userpage(event);
+		}
+		ctr_ovf_mask = ctr_ovf_mask >> 1;
+		idx++;
+	}
 }
 
 static irqreturn_t rvpmu_ovf_handler(int irq, void *dev)
@@ -1143,10 +1332,18 @@ static irqreturn_t rvpmu_ovf_handler(int irq, void *dev)
 	}
 
 	pmu = to_riscv_pmu(event->pmu);
-	rvpmu_sbi_stop_hw_ctrs(pmu);
+	if (riscv_pmu_cdeleg_available())
+		rvpmu_deleg_ctr_stop_mask(cpu_hw_evt->used_hw_ctrs[0]);
+	else
+		rvpmu_sbi_stop_hw_ctrs(pmu);
 
-	/* Overflow status register should only be read after counter are stopped */
-	if (sbi_pmu_snapshot_available())
+	/*
+	 * Overflow status register should only be read after counter are stopped.
+	 * In counter delegation mode the overflows are reported in scountovf, not
+	 * in the SBI snapshot area, so read the CSR directly even when an SBI PMU
+	 * snapshot is also available.
+	 */
+	if (sbi_pmu_snapshot_available() && !riscv_pmu_cdeleg_available())
 		overflow = sdata->ctr_overflow_mask;
 	else
 		ALT_SBI_PMU_OVERFLOW(overflow);
@@ -1212,25 +1409,182 @@ static irqreturn_t rvpmu_ovf_handler(int irq, void *dev)
 		hw_evt->state = 0;
 	}
 
-	rvpmu_sbi_start_overflow_mask(pmu, overflowed_ctrs);
+	rvpmu_start_overflow_mask(pmu, overflowed_ctrs);
 	perf_sample_event_took(sched_clock() - start_clock);
 
 	return IRQ_HANDLED;
 }
 
+static int get_deleg_hw_ctr_width(int counter_offset)
+{
+	unsigned long hpm_warl;
+	int num_bits;
+
+	if (counter_offset < 3 || counter_offset > 31)
+		return 0;
+
+	hpm_warl = csr_ind_warl(CSR_SIREG, SISELECT_SSCCFG_BASE, counter_offset, -1);
+	num_bits = __fls(hpm_warl);
+
+#if defined(CONFIG_32BIT)
+	/*
+	 * The low half contributes a full BITS_PER_LONG bits when the counter is
+	 * wider than 32 bits; the high half's __fls() gives the remaining width.
+	 */
+	hpm_warl = csr_ind_warl(CSR_SIREG4, SISELECT_SSCCFG_BASE, counter_offset, -1);
+	if (hpm_warl)
+		num_bits = BITS_PER_LONG + __fls(hpm_warl);
+#endif
+	return num_bits;
+}
+
+static int rvpmu_deleg_find_ctrs(void)
+{
+	int i, num_hw_ctr = 0;
+	union sbi_pmu_ctr_info cinfo;
+	unsigned long scountinhibit_old = 0;
+
+	/* Do a WARL write/read to detect which hpmcounters have been delegated */
+	scountinhibit_old = csr_read(CSR_SCOUNTINHIBIT);
+	csr_write(CSR_SCOUNTINHIBIT, -1);
+	cmask = csr_read(CSR_SCOUNTINHIBIT);
+
+	csr_write(CSR_SCOUNTINHIBIT, scountinhibit_old);
+
+	for_each_set_bit(i, &cmask, RISCV_MAX_HW_COUNTERS) {
+		if (unlikely(i == 1))
+			continue; /* This should never happen as TM is read only */
+		cinfo.value = 0;
+		cinfo.type = SBI_PMU_CTR_TYPE_HW;
+		/*
+		 * If counter delegation is enabled, the csr stored to the cinfo will
+		 * be a virtual counter that the delegation attempts to read.
+		 */
+		cinfo.csr = CSR_CYCLE + i;
+		if (i == 0 || i == 2)
+			cinfo.width = 63;
+		else
+			cinfo.width = get_deleg_hw_ctr_width(i);
+
+		num_hw_ctr++;
+		pmu_ctr_list[i].value = cinfo.value;
+	}
+
+	return num_hw_ctr;
+}
+
+static int get_deleg_fixed_hw_idx(struct cpu_hw_events *cpuc, struct perf_event *event)
+{
+	return -EINVAL;
+}
+
+static int get_deleg_next_hpm_hw_idx(struct cpu_hw_events *cpuc, struct perf_event *event)
+{
+	unsigned long hw_ctr_mask = 0;
+
+	/*
+	 * TODO: Treat every hpmcounter can monitor every event for now.
+	 * The event to counter mapping should come from the json file.
+	 * The mapping should also tell if sampling is supported or not.
+	 */
+
+	/* Select only hpmcounters */
+	hw_ctr_mask = cmask & (~0x7);
+	hw_ctr_mask &= ~(cpuc->used_hw_ctrs[0]);
+	return __ffs(hw_ctr_mask);
+}
+
+static void update_deleg_hpmevent(int counter_idx, uint64_t event_value, uint64_t filter_bits)
+{
+	u64 hpmevent_value = 0;
+
+	/* OF bit should be enable during the start if sampling is requested */
+	hpmevent_value = (event_value & ~HPMEVENT_MASK) | filter_bits | HPMEVENT_OF;
+#if defined(CONFIG_32BIT)
+	csr_ind_write(CSR_SIREG2, SISELECT_SSCCFG_BASE, counter_idx, hpmevent_value & 0xFFFFFFFF);
+	if (riscv_isa_extension_available(NULL, SSCOFPMF))
+		csr_ind_write(CSR_SIREG5, SISELECT_SSCCFG_BASE, counter_idx,
+			      hpmevent_value >> BITS_PER_LONG);
+#else
+	csr_ind_write(CSR_SIREG2, SISELECT_SSCCFG_BASE, counter_idx, hpmevent_value);
+#endif
+}
+
+static int rvpmu_deleg_ctr_get_idx(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
+	struct cpu_hw_events *cpuc = this_cpu_ptr(rvpmu->hw_events);
+	unsigned long hw_ctr_max_id;
+	u64 priv_filter;
+	int idx;
+
+	/*
+	 * TODO: We should not rely on SBI Perf encoding to check if the event
+	 * is a fixed one or not.
+	 */
+	if (!is_sampling_event(event)) {
+		idx = get_deleg_fixed_hw_idx(cpuc, event);
+		if (idx == 0 || idx == 2) {
+			/* Priv mode filter bits are only available if smcntrpmf is present */
+			if (riscv_isa_extension_available(NULL, SMCNTRPMF))
+				goto found_idx;
+			else
+				goto skip_update;
+		}
+	}
+
+	hw_ctr_max_id = __fls(cmask);
+	idx = get_deleg_next_hpm_hw_idx(cpuc, event);
+	if (idx < 3 || idx > hw_ctr_max_id)
+		goto out_err;
+found_idx:
+	priv_filter = get_deleg_priv_filter_bits(event);
+	update_deleg_hpmevent(idx, hwc->config, priv_filter);
+skip_update:
+	if (!test_and_set_bit(idx, cpuc->used_hw_ctrs))
+		return idx;
+out_err:
+	return -ENOENT;
+}
+
 static void rvpmu_ctr_start(struct perf_event *event, u64 ival)
 {
-	rvpmu_sbi_ctr_start(event, ival);
-	/* TODO: Counter delegation implementation */
+	struct hw_perf_event *hwc = &event->hw;
+
+	if (riscv_pmu_cdeleg_available() && !pmu_sbi_is_fw_event(event))
+		rvpmu_deleg_ctr_start(event, ival);
+	else
+		rvpmu_sbi_ctr_start(event, ival);
+
+	if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
+	    (hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
+		rvpmu_set_scounteren((void *)event);
 }
 
 static void rvpmu_ctr_stop(struct perf_event *event, unsigned long flag)
 {
-	rvpmu_sbi_ctr_stop(event, flag);
-	/* TODO: Counter delegation implementation */
+	struct hw_perf_event *hwc = &event->hw;
+
+	if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
+	    (hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
+		rvpmu_reset_scounteren((void *)event);
+
+	if (riscv_pmu_cdeleg_available() && !pmu_sbi_is_fw_event(event)) {
+		/*
+		 * The counter is already stopped. No need to stop again. Counter
+		 * mapping will be reset in clear_idx function.
+		 */
+		if (flag != RISCV_PMU_STOP_FLAG_RESET)
+			rvpmu_deleg_ctr_stop_mask((1 << hwc->idx));
+		else
+			update_deleg_hpmevent(hwc->idx, 0, 0);
+	} else {
+		rvpmu_sbi_ctr_stop(event, flag);
+	}
 }
 
-static int rvpmu_find_ctrs(void)
+static u32 rvpmu_find_ctrs(void)
 {
 	u32 num_sbi_counters = 0, num_deleg_counters = 0;
 	u32 num_hw_ctr = 0, num_fw_ctr = 0, num_ctr = 0;
@@ -1270,20 +1624,18 @@ static int rvpmu_find_ctrs(void)
 
 static int rvpmu_event_map(struct perf_event *event, u64 *econfig)
 {
-	return rvpmu_sbi_event_map(event, econfig);
-	/* TODO: Counter delegation implementation */
+	if (riscv_pmu_cdeleg_available() && !pmu_sbi_is_fw_event(event))
+		return rvpmu_cdeleg_event_map(event, econfig);
+	else
+		return rvpmu_sbi_event_map(event, econfig);
 }
 
 static int rvpmu_ctr_get_idx(struct perf_event *event)
 {
-	return rvpmu_sbi_ctr_get_idx(event);
-	/* TODO: Counter delegation implementation */
-}
-
-static u64 rvpmu_ctr_read(struct perf_event *event)
-{
-	return rvpmu_sbi_ctr_read(event);
-	/* TODO: Counter delegation implementation */
+	if (riscv_pmu_cdeleg_available() && !pmu_sbi_is_fw_event(event))
+		return rvpmu_deleg_ctr_get_idx(event);
+	else
+		return rvpmu_sbi_ctr_get_idx(event);
 }
 
 static int rvpmu_starting_cpu(unsigned int cpu, struct hlist_node *node)
@@ -1301,7 +1653,16 @@ static int rvpmu_starting_cpu(unsigned int cpu, struct hlist_node *node)
 		csr_write(CSR_SCOUNTEREN, 0x2);
 
 	/* Stop all the counters so that they can be enabled from perf */
-	rvpmu_sbi_stop_all(pmu);
+	if (riscv_pmu_cdeleg_available()) {
+		rvpmu_deleg_ctr_stop_mask(cmask);
+		if (riscv_pmu_sbi_available()) {
+			/* Stop the firmware counters as well */
+			sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP, 0, firmware_cmask,
+				  0, 0, 0, 0);
+		}
+	} else {
+		rvpmu_sbi_stop_all(pmu);
+	}
 
 	if (riscv_pmu_use_irq) {
 		cpu_hw_evt->irq = riscv_pmu_irq;
@@ -1600,8 +1961,11 @@ static int rvpmu_device_probe(struct platform_device *pdev)
 		pmu->pmu.capabilities |= PERF_PMU_CAP_NO_EXCLUDE;
 	}
 
-	pmu->pmu.attr_groups = riscv_pmu_attr_groups;
 	pmu->pmu.parent = &pdev->dev;
+	if (riscv_pmu_cdeleg_available_boot())
+		pmu->pmu.attr_groups = riscv_cdeleg_pmu_attr_groups;
+	else
+		pmu->pmu.attr_groups = riscv_sbi_pmu_attr_groups;
 	pmu->cmask = cmask;
 	pmu->ctr_start = rvpmu_ctr_start;
 	pmu->ctr_stop = rvpmu_ctr_stop;
diff --git a/include/linux/perf/riscv_pmu.h b/include/linux/perf/riscv_pmu.h
index 6c75106989b6..f6a710c83a4c 100644
--- a/include/linux/perf/riscv_pmu.h
+++ b/include/linux/perf/riscv_pmu.h
@@ -20,6 +20,7 @@
  */
 
 #define RISCV_MAX_COUNTERS	64
+#define RISCV_MAX_HW_COUNTERS	32
 #define RISCV_OP_UNSUPP		(-EOPNOTSUPP)
 #define RISCV_PMU_SBI_PDEV_NAME	"riscv-pmu-sbi"
 #define RISCV_PMU_LEGACY_PDEV_NAME	"riscv-pmu-legacy"
@@ -28,6 +29,8 @@
 
 #define RISCV_PMU_CONFIG1_GUEST_EVENTS 0x1
 
+#define RISCV_PMU_DELEG_RAW_EVENT_MASK GENMASK_ULL(55, 0)
+
 #define HW_OP_UNSUPPORTED		0xFFFF
 #define CACHE_OP_UNSUPPORTED		0xFFFF
 

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 14/21] RISC-V: perf: Skip PMU SBI extension when not implemented
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (12 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 13/21] RISC-V: perf: Implement supervisor counter delegation support Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:33   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 15/21] RISC-V: perf: Use config2/vendor table for event to counter mapping Atish Patra
                   ` (6 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Charlie Jenkins <charlie@rivosinc.com>

When the PMU SBI extension is not implemented, sbi_v2_available should
not be set to true. The SBI implementation for counter config matching
and firmware counter read  should also be skipped when the SBI extension
is not implemented.

Signed-off-by: Atish Patra <atishp@meta.com>
Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
---
 drivers/perf/riscv_pmu_sbi.c | 48 ++++++++++++++++++++++++++------------------
 1 file changed, 28 insertions(+), 20 deletions(-)

diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 1f16df9d0dd0..5bfcd3821f57 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -479,27 +479,31 @@ static void rvpmu_sbi_check_event(struct sbi_pmu_event_data *edata)
 	}
 }
 
-static void rvpmu_sbi_check_std_events(struct work_struct *work)
+static void rvpmu_check_std_events(struct work_struct *work)
 {
 	int ret;
 
-	if (sbi_v3_available) {
-		ret = pmu_sbi_check_event_info();
-		if (ret)
-			pr_err("pmu_sbi_check_event_info failed with error %d\n", ret);
-		return;
-	}
+	if (riscv_pmu_sbi_available()) {
+		if (sbi_v3_available) {
+			ret = pmu_sbi_check_event_info();
+			if (ret)
+				pr_err("pmu_sbi_check_event_info failed with error %d\n", ret);
+			return;
+		}
 
-	for (int i = 0; i < ARRAY_SIZE(pmu_hw_event_sbi_map); i++)
-		rvpmu_sbi_check_event(&pmu_hw_event_sbi_map[i]);
+		for (int i = 0; i < ARRAY_SIZE(pmu_hw_event_sbi_map); i++)
+			rvpmu_sbi_check_event(&pmu_hw_event_sbi_map[i]);
 
-	for (int i = 0; i < ARRAY_SIZE(pmu_cache_event_sbi_map); i++)
-		for (int j = 0; j < ARRAY_SIZE(pmu_cache_event_sbi_map[i]); j++)
-			for (int k = 0; k < ARRAY_SIZE(pmu_cache_event_sbi_map[i][j]); k++)
-				rvpmu_sbi_check_event(&pmu_cache_event_sbi_map[i][j][k]);
+		for (int i = 0; i < ARRAY_SIZE(pmu_cache_event_sbi_map); i++)
+			for (int j = 0; j < ARRAY_SIZE(pmu_cache_event_sbi_map[i]); j++)
+				for (int k = 0; k < ARRAY_SIZE(pmu_cache_event_sbi_map[i][j]); k++)
+					rvpmu_sbi_check_event(&pmu_cache_event_sbi_map[i][j][k]);
+	} else {
+		DO_ONCE_LITE_IF(1, pr_info, "Boot time config matching not required for smcdeleg\n");
+	}
 }
 
-static DECLARE_WORK(check_std_events_work, rvpmu_sbi_check_std_events);
+static DECLARE_WORK(check_std_events_work, rvpmu_check_std_events);
 
 static ssize_t rvpmu_format_show(struct device *dev,
 				 struct device_attribute *attr, char *buf)
@@ -692,6 +696,9 @@ static int rvpmu_sbi_ctr_get_idx(struct perf_event *event)
 
 	cflags = rvpmu_sbi_get_filter_flags(event);
 
+	if (!riscv_pmu_sbi_available())
+		return -ENOENT;
+
 	/*
 	 * In legacy mode, we have to force the fixed counters for those events
 	 * but not in the user access mode as we want to use the other counters
@@ -967,7 +974,7 @@ static u64 rvpmu_ctr_read(struct perf_event *event)
 		return val;
 	}
 
-	if (pmu_sbi_is_fw_event(event)) {
+	if (pmu_sbi_is_fw_event(event) && riscv_pmu_sbi_available()) {
 		ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_FW_READ,
 				hwc->idx, 0, 0, 0, 0, 0);
 		if (ret.error)
@@ -2045,12 +2052,13 @@ static int __init rvpmu_devinit(void)
 	int ret;
 	struct platform_device *pdev;
 
-	if (sbi_spec_version >= sbi_mk_version(0, 3) &&
-	    sbi_probe_extension(SBI_EXT_PMU))
-		static_branch_enable(&riscv_pmu_sbi_available);
+	if (sbi_probe_extension(SBI_EXT_PMU)) {
+		if (sbi_spec_version >= sbi_mk_version(0, 3))
+			static_branch_enable(&riscv_pmu_sbi_available);
+		if (sbi_spec_version >= sbi_mk_version(2, 0))
+			sbi_v2_available = true;
+	}
 
-	if (sbi_spec_version >= sbi_mk_version(2, 0))
-		sbi_v2_available = true;
 	/*
 	 * We need all three extensions to be present to access the counters
 	 * in S-mode via Supervisor Counter delegation.

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 15/21] RISC-V: perf: Use config2/vendor table for event to counter mapping
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (13 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 14/21] RISC-V: perf: Skip PMU SBI extension when not implemented Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:23   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 16/21] RISC-V: perf: Add legacy event encodings via sysfs Atish Patra
                   ` (5 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

The counter restriction specified in the json file is passed to
the drivers via config2 paarameter in perf attributes. This allows
any platform vendor to define their custom mapping between event and
hpmcounters without any rules defined in the ISA.

For legacy events, the platform vendor may define the mapping in
the driver in the vendor event table.
The fixed cycle and instruction counters are fixed (0 and 2
respectively) by the ISA and maps to the legacy events. The platform
vendor must specify this in the driver if intended to be used while
profiling. Otherwise, they can just specify the alternate hpmcounters
that may monitor and/or sample the cycle/instruction counts.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 drivers/perf/riscv_pmu_sbi.c   | 90 ++++++++++++++++++++++++++++++++++--------
 include/linux/perf/riscv_pmu.h |  2 +
 2 files changed, 76 insertions(+), 16 deletions(-)

diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 5bfcd3821f57..4b4f151a0744 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -76,6 +76,7 @@ static ssize_t __maybe_unused rvpmu_format_show(struct device *dev, struct devic
 	RVPMU_ATTR_ENTRY(_name, rvpmu_format_show, (char *)_config)
 
 PMU_FORMAT_ATTR(firmware, "config:62-63");
+PMU_FORMAT_ATTR(counterid_mask, "config2:0-31");
 
 static bool sbi_v2_available;
 static bool sbi_v3_available;
@@ -120,6 +121,7 @@ static const struct attribute_group *riscv_sbi_pmu_attr_groups[] = {
 static struct attribute *riscv_cdeleg_pmu_formats_attr[] = {
 	RVPMU_FORMAT_ATTR_ENTRY(event, RVPMU_CDELEG_PMU_FORMAT_ATTR),
 	&format_attr_firmware.attr,
+	&format_attr_counterid_mask.attr,
 	NULL,
 };
 
@@ -1480,24 +1482,80 @@ static int rvpmu_deleg_find_ctrs(void)
 	return num_hw_ctr;
 }
 
+/*
+ * The json file must correctly specify counter 0 or counter 2 is available
+ * in the counter lists for cycle/instret events. Otherwise, the drivers have
+ * no way to figure out if a fixed counter must be used and pick a programmable
+ * counter if available.
+ */
 static int get_deleg_fixed_hw_idx(struct cpu_hw_events *cpuc, struct perf_event *event)
 {
-	return -EINVAL;
+	struct hw_perf_event *hwc = &event->hw;
+	bool guest_events = event->attr.config1 & RISCV_PMU_CONFIG1_GUEST_EVENTS;
+
+	if (guest_events) {
+		if (hwc->event_base == SBI_PMU_HW_CPU_CYCLES)
+			return 0;
+		if (hwc->event_base == SBI_PMU_HW_INSTRUCTIONS)
+			return 2;
+		else
+			return -EINVAL;
+	}
+
+	if (!event->attr.config2)
+		return -EINVAL;
+
+	if (event->attr.config2 & RISCV_PMU_CYCLE_FIXED_CTR_MASK)
+		return 0; /* CY counter */
+	else if (event->attr.config2 & RISCV_PMU_INSTRUCTION_FIXED_CTR_MASK)
+		return 2; /* IR counter */
+	else
+		return -EINVAL;
 }
 
 static int get_deleg_next_hpm_hw_idx(struct cpu_hw_events *cpuc, struct perf_event *event)
 {
-	unsigned long hw_ctr_mask = 0;
+	u32 hw_ctr_mask = 0, temp_mask = 0;
+	u32 type = event->attr.type;
+	u64 config = event->attr.config;
+	int ret;
 
-	/*
-	 * TODO: Treat every hpmcounter can monitor every event for now.
-	 * The event to counter mapping should come from the json file.
-	 * The mapping should also tell if sampling is supported or not.
-	 */
+	/* Select only available hpmcounters */
+	hw_ctr_mask = cmask & (~0x7) & ~(cpuc->used_hw_ctrs[0]);
+
+	switch (type) {
+	case PERF_TYPE_HARDWARE:
+		temp_mask = current_pmu_hw_event_map[config].counter_mask;
+		break;
+	case PERF_TYPE_HW_CACHE:
+		ret = cdeleg_pmu_event_find_cache(config, NULL, &temp_mask);
+		if (ret)
+			return ret;
+		break;
+	case PERF_TYPE_RAW:
+		/*
+		 * Mask off the counters that can't monitor this event (specified via json)
+		 * The counter mask for this event is set in config2 via the property 'Counter'
+		 * in the json file or manual configuration of config2. If the config2 is not set,
+		 * it is assumed all the available hpmcounters can monitor this event.
+		 * Note: This assumption may fail for virtualization use case where they hypervisor
+		 * (e.g. KVM) virtualizes the counter. Any event to counter mapping provided by the
+		 * guest is meaningless from a hypervisor perspective. Thus, the hypervisor doesn't
+		 * set config2 when creating kernel counter and relies default host mapping.
+		 */
+		if (event->attr.config2)
+			temp_mask = event->attr.config2;
+		break;
+	default:
+		break;
+	}
+
+	if (temp_mask)
+		hw_ctr_mask &= temp_mask;
+
+	if (!hw_ctr_mask)
+		return -EINVAL;
 
-	/* Select only hpmcounters */
-	hw_ctr_mask = cmask & (~0x7);
-	hw_ctr_mask &= ~(cpuc->used_hw_ctrs[0]);
 	return __ffs(hw_ctr_mask);
 }
 
@@ -1526,10 +1584,6 @@ static int rvpmu_deleg_ctr_get_idx(struct perf_event *event)
 	u64 priv_filter;
 	int idx;
 
-	/*
-	 * TODO: We should not rely on SBI Perf encoding to check if the event
-	 * is a fixed one or not.
-	 */
 	if (!is_sampling_event(event)) {
 		idx = get_deleg_fixed_hw_idx(cpuc, event);
 		if (idx == 0 || idx == 2) {
@@ -1547,10 +1601,14 @@ static int rvpmu_deleg_ctr_get_idx(struct perf_event *event)
 		goto out_err;
 found_idx:
 	priv_filter = get_deleg_priv_filter_bits(event);
+	if (test_and_set_bit(idx, cpuc->used_hw_ctrs))
+		goto out_err;
 	update_deleg_hpmevent(idx, hwc->config, priv_filter);
+	return idx;
 skip_update:
-	if (!test_and_set_bit(idx, cpuc->used_hw_ctrs))
-		return idx;
+	if (test_and_set_bit(idx, cpuc->used_hw_ctrs))
+		goto out_err;
+	return idx;
 out_err:
 	return -ENOENT;
 }
diff --git a/include/linux/perf/riscv_pmu.h b/include/linux/perf/riscv_pmu.h
index f6a710c83a4c..06171e7aadfb 100644
--- a/include/linux/perf/riscv_pmu.h
+++ b/include/linux/perf/riscv_pmu.h
@@ -30,6 +30,8 @@
 #define RISCV_PMU_CONFIG1_GUEST_EVENTS 0x1
 
 #define RISCV_PMU_DELEG_RAW_EVENT_MASK GENMASK_ULL(55, 0)
+#define RISCV_PMU_CYCLE_FIXED_CTR_MASK 0x01
+#define RISCV_PMU_INSTRUCTION_FIXED_CTR_MASK 0x04
 
 #define HW_OP_UNSUPPORTED		0xFFFF
 #define CACHE_OP_UNSUPPORTED		0xFFFF

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 16/21] RISC-V: perf: Add legacy event encodings via sysfs
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (14 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 15/21] RISC-V: perf: Use config2/vendor table for event to counter mapping Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:21   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 17/21] RISC-V: perf: Add Qemu virt machine events Atish Patra
                   ` (4 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

Define sysfs details for the legacy events so that any tool can
parse these to understand the minimum set of legacy events
supported by the platform. The sysfs entry will describe both event
encoding and corresponding counter map so that an perf event can be
programmed accordingly.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 drivers/perf/riscv_pmu_sbi.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 4b4f151a0744..00b84b28117a 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -130,7 +130,20 @@ static struct attribute_group riscv_cdeleg_pmu_format_group = {
 	.attrs = riscv_cdeleg_pmu_formats_attr,
 };
 
+#define RVPMU_EVENT_ATTR_RESOLVE(m) #m
+#define RVPMU_EVENT_CMASK_ATTR(_name, _var, config, mask) \
+	PMU_EVENT_ATTR_STRING(_name, rvpmu_event_attr_##_var, \
+			      "event=" RVPMU_EVENT_ATTR_RESOLVE(config) \
+			      ",counterid_mask=" RVPMU_EVENT_ATTR_RESOLVE(mask))
+
+#define RVPMU_EVENT_ATTR_PTR(name) (&rvpmu_event_attr_##name.attr.attr)
+
+static struct attribute_group riscv_cdeleg_pmu_event_group __ro_after_init = {
+	.name = "events",
+};
+
 static const struct attribute_group *riscv_cdeleg_pmu_attr_groups[] = {
+	&riscv_cdeleg_pmu_event_group,
 	&riscv_cdeleg_pmu_format_group,
 	NULL,
 };
@@ -431,11 +444,14 @@ struct riscv_vendor_pmu_events {
 	const struct riscv_pmu_event *hw_event_map;
 	const struct riscv_pmu_event (*cache_event_map)[PERF_COUNT_HW_CACHE_OP_MAX]
 						       [PERF_COUNT_HW_CACHE_RESULT_MAX];
+	struct attribute **attrs_events;
 };
 
-#define RISCV_VENDOR_PMU_EVENTS(_vendorid, _archid, _implid, _hw_event_map, _cache_event_map) \
+#define RISCV_VENDOR_PMU_EVENTS(_vendorid, _archid, _implid, _hw_event_map, \
+				_cache_event_map, _attrs) \
 	{ .vendorid = _vendorid, .archid = _archid, .implid = _implid, \
-	  .hw_event_map = _hw_event_map, .cache_event_map = _cache_event_map },
+	  .hw_event_map = _hw_event_map, .cache_event_map = _cache_event_map, \
+	  .attrs_events = _attrs },
 
 static struct riscv_vendor_pmu_events pmu_vendor_events_table[] = {
 };
@@ -457,6 +473,8 @@ static void rvpmu_vendor_register_events(void)
 		    pmu_vendor_events_table[i].archid == arch_id) {
 			current_pmu_hw_event_map = pmu_vendor_events_table[i].hw_event_map;
 			current_pmu_cache_event_map = pmu_vendor_events_table[i].cache_event_map;
+			riscv_cdeleg_pmu_event_group.attrs =
+							pmu_vendor_events_table[i].attrs_events;
 			break;
 		}
 	}

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 17/21] RISC-V: perf: Add Qemu virt machine events
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (15 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 16/21] RISC-V: perf: Add legacy event encodings via sysfs Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:22   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 18/21] tools/perf: Support event code for arch standard events Atish Patra
                   ` (3 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

Qemu virt machine supports a very minimal set of legacy perf events.
Add them to the vendor table so that users can use them when
counter delegation is enabled.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 arch/riscv/include/asm/vendorid_list.h |  4 ++++
 drivers/perf/riscv_pmu_sbi.c           | 36 ++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)

diff --git a/arch/riscv/include/asm/vendorid_list.h b/arch/riscv/include/asm/vendorid_list.h
index 7f5030ee1fcf..603aa2b21c0b 100644
--- a/arch/riscv/include/asm/vendorid_list.h
+++ b/arch/riscv/include/asm/vendorid_list.h
@@ -11,4 +11,8 @@
 #define SIFIVE_VENDOR_ID	0x489
 #define THEAD_VENDOR_ID		0x5b7
 
+#define QEMU_VIRT_VENDOR_ID		0x000
+#define QEMU_VIRT_IMPL_ID		0x000
+#define QEMU_VIRT_ARCH_ID		0x000
+
 #endif
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 00b84b28117a..74acac54328e 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -26,6 +26,7 @@
 #include <asm/sbi.h>
 #include <asm/cpufeature.h>
 #include <asm/vendor_extensions.h>
+#include <asm/vendorid_list.h>
 #include <asm/vendor_extensions/andes.h>
 #include <asm/hwcap.h>
 #include <asm/csr_ind.h>
@@ -453,7 +454,42 @@ struct riscv_vendor_pmu_events {
 	  .hw_event_map = _hw_event_map, .cache_event_map = _cache_event_map, \
 	  .attrs_events = _attrs },
 
+/* QEMU virt PMU events */
+static const struct riscv_pmu_event qemu_virt_hw_event_map[PERF_COUNT_HW_MAX] = {
+	PERF_MAP_ALL_UNSUPPORTED,
+	[PERF_COUNT_HW_CPU_CYCLES]		= {0x01, 0xFFFFFFF8},
+	[PERF_COUNT_HW_INSTRUCTIONS]		= {0x02, 0xFFFFFFF8}
+};
+
+static const struct riscv_pmu_event qemu_virt_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
+						[PERF_COUNT_HW_CACHE_OP_MAX]
+						[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
+	PERF_CACHE_MAP_ALL_UNSUPPORTED,
+	[C(DTLB)][C(OP_READ)][C(RESULT_MISS)]	= {0x10019, 0xFFFFFFF8},
+	[C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)]	= {0x1001B, 0xFFFFFFF8},
+
+	[C(ITLB)][C(OP_READ)][C(RESULT_MISS)]	= {0x10021, 0xFFFFFFF8},
+};
+
+RVPMU_EVENT_CMASK_ATTR(cycles, cycles, 0x01, 0xFFFFFFF8);
+RVPMU_EVENT_CMASK_ATTR(instructions, instructions, 0x02, 0xFFFFFFF8);
+RVPMU_EVENT_CMASK_ATTR(dTLB-load-misses, dTLB_load_miss, 0x10019, 0xFFFFFFF8);
+RVPMU_EVENT_CMASK_ATTR(dTLB-store-misses, dTLB_store_miss, 0x1001B, 0xFFFFFFF8);
+RVPMU_EVENT_CMASK_ATTR(iTLB-load-misses, iTLB_load_miss, 0x10021, 0xFFFFFFF8);
+
+static struct attribute *qemu_virt_event_group[] = {
+	RVPMU_EVENT_ATTR_PTR(cycles),
+	RVPMU_EVENT_ATTR_PTR(instructions),
+	RVPMU_EVENT_ATTR_PTR(dTLB_load_miss),
+	RVPMU_EVENT_ATTR_PTR(dTLB_store_miss),
+	RVPMU_EVENT_ATTR_PTR(iTLB_load_miss),
+	NULL,
+};
+
 static struct riscv_vendor_pmu_events pmu_vendor_events_table[] = {
+	RISCV_VENDOR_PMU_EVENTS(QEMU_VIRT_VENDOR_ID, QEMU_VIRT_ARCH_ID, QEMU_VIRT_IMPL_ID,
+				qemu_virt_hw_event_map, qemu_virt_cache_event_map,
+				qemu_virt_event_group)
 };
 
 static const struct riscv_pmu_event *current_pmu_hw_event_map;

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 18/21] tools/perf: Support event code for arch standard events
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (16 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 17/21] RISC-V: perf: Add Qemu virt machine events Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:18   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 19/21] tools/perf: Add RISC-V CounterIDMask event field Atish Patra
                   ` (2 subsequent siblings)
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

RISC-V relies on the event encoding from the json file. That includes
arch standard events. If event code is present, event is already updated
with correct encoding. No need to update it again which results in losing
the event encoding.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 tools/perf/pmu-events/arch/riscv/arch-standard.json | 10 ++++++++++
 tools/perf/pmu-events/jevents.py                    |  6 +++++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/tools/perf/pmu-events/arch/riscv/arch-standard.json b/tools/perf/pmu-events/arch/riscv/arch-standard.json
new file mode 100644
index 000000000000..96e21f088558
--- /dev/null
+++ b/tools/perf/pmu-events/arch/riscv/arch-standard.json
@@ -0,0 +1,10 @@
+[
+  {
+    "EventName": "cycles",
+    "BriefDescription": "cycle executed"
+  },
+  {
+    "EventName": "instructions",
+    "BriefDescription": "instruction retired"
+  }
+]
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 3a1bcdcdc685..457fce7a5982 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -413,7 +413,11 @@ class JsonEvent:
         self.long_desc = None
     if arch_std:
       if arch_std.lower() in _arch_std_events:
-        event = _arch_std_events[arch_std.lower()].event
+        # If the JSON event already specified an event code, the encoding has
+        # been set above; don't overwrite it with the arch standard event or
+        # the event encoding would be lost.
+        if not eventcode:
+          event = _arch_std_events[arch_std.lower()].event
         # Copy from the architecture standard event to self for undefined fields.
         for attr, value in _arch_std_events[arch_std.lower()].__dict__.items():
           if hasattr(self, attr) and not getattr(self, attr):

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 19/21] tools/perf: Add RISC-V CounterIDMask event field
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (17 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 18/21] tools/perf: Support event code for arch standard events Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:01 ` [PATCH v6 20/21] TEST(do-not-upstream): fake qemu-virt PMU events for cdeleg counter-mask testing Atish Patra
  2026-06-09  6:01 ` [PATCH v6 21/21] TEST(do-not-upstream): fake qemu vendor JSON + mapfile entry for CounterIDMask path Atish Patra
  20 siblings, 0 replies; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@rivosinc.com>

Counter delegation lets supervisor mode choose the hpmcounter for an event,
but the hardware may only allow a given event on a subset of counters. Add a
RISC-V specific "CounterIDMask" json event field, handled like the other
arch-specific entries in event_fields[], that carries the allowed-counter
bitmask through to the driver's existing counterid_mask (config2:0-31) format.

The value is the bitmask directly so no counter-list to bitmask conversion is
needed, and because the field is RISC-V specific it is a no-op for every other
architecture's events (unlike the shared "Counter" field).

Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
 tools/perf/pmu-events/jevents.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 457fce7a5982..c1ed8a05c9a4 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -396,6 +396,7 @@ class JsonEvent:
         ('EnAllSlices', 'enallslices='),
         ('SliceId', 'sliceid='),
         ('ThreadMask', 'threadmask='),
+        ('CounterIDMask', 'counterid_mask='),
     ]
     for key, value in event_fields:
       if key in jd and not is_zero(jd[key]):

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 20/21] TEST(do-not-upstream): fake qemu-virt PMU events for cdeleg counter-mask testing
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (18 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 19/21] tools/perf: Add RISC-V CounterIDMask event field Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:17   ` sashiko-bot
  2026-06-09  6:01 ` [PATCH v6 21/21] TEST(do-not-upstream): fake qemu vendor JSON + mapfile entry for CounterIDMask path Atish Patra
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@meta.com>

Adds fake-any/fake-ctr3/fake-ctr34 (event codes 0xF0x QEMU doesn't model) with
counterid_masks, to exercise the counter-delegation allocation + counter-mask
constraint in QEMU (events read 0 = allocated/programmed, vs 'not supported').

Signed-off-by: Atish Patra <atishp@meta.com>
---
 drivers/perf/riscv_pmu_sbi.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 74acac54328e..3c0829c0a42a 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -476,6 +476,12 @@ RVPMU_EVENT_CMASK_ATTR(instructions, instructions, 0x02, 0xFFFFFFF8);
 RVPMU_EVENT_CMASK_ATTR(dTLB-load-misses, dTLB_load_miss, 0x10019, 0xFFFFFFF8);
 RVPMU_EVENT_CMASK_ATTR(dTLB-store-misses, dTLB_store_miss, 0x1001B, 0xFFFFFFF8);
 RVPMU_EVENT_CMASK_ATTR(iTLB-load-misses, iTLB_load_miss, 0x10021, 0xFFFFFFF8);
+/*
+ * FAKE events for cdeleg mechanism testing: event codes QEMU does NOT model.
+ */
+RVPMU_EVENT_CMASK_ATTR(fake-any, fake_any, 0xF00, 0xFFFFFFF8);
+RVPMU_EVENT_CMASK_ATTR(fake-ctr3, fake_ctr3, 0xF01, 0x8);
+RVPMU_EVENT_CMASK_ATTR(fake-ctr34, fake_ctr34, 0xF02, 0x18);
 
 static struct attribute *qemu_virt_event_group[] = {
 	RVPMU_EVENT_ATTR_PTR(cycles),
@@ -483,6 +489,9 @@ static struct attribute *qemu_virt_event_group[] = {
 	RVPMU_EVENT_ATTR_PTR(dTLB_load_miss),
 	RVPMU_EVENT_ATTR_PTR(dTLB_store_miss),
 	RVPMU_EVENT_ATTR_PTR(iTLB_load_miss),
+	RVPMU_EVENT_ATTR_PTR(fake_any),
+	RVPMU_EVENT_ATTR_PTR(fake_ctr3),
+	RVPMU_EVENT_ATTR_PTR(fake_ctr34),
 	NULL,
 };
 

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH v6 21/21] TEST(do-not-upstream): fake qemu vendor JSON + mapfile entry for CounterIDMask path
  2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
                   ` (19 preceding siblings ...)
  2026-06-09  6:01 ` [PATCH v6 20/21] TEST(do-not-upstream): fake qemu-virt PMU events for cdeleg counter-mask testing Atish Patra
@ 2026-06-09  6:01 ` Atish Patra
  2026-06-09  6:20   ` sashiko-bot
  20 siblings, 1 reply; 38+ messages in thread
From: Atish Patra @ 2026-06-09  6:01 UTC (permalink / raw)
  To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
	Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
	Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
  Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
	devicetree, linux-arm-kernel

From: Atish Patra <atishp@meta.com>

arch/riscv/qemu/virt/events.json: fake-json-{any,ctr3,ctr34,ctr6} with EventCode
+ CounterIDMask; mapfile.csv: 0x0-0x0-0x0 -> qemu/virt. Exercises jevents
CounterIDMask -> counterid_mask= -> config2 -> cdeleg counter allocation.

Signed-off-by: Atish Patra <atishp@meta.com>
---
 tools/perf/pmu-events/arch/riscv/mapfile.csv       |  1 +
 .../pmu-events/arch/riscv/qemu/virt/events.json    | 26 ++++++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/tools/perf/pmu-events/arch/riscv/mapfile.csv b/tools/perf/pmu-events/arch/riscv/mapfile.csv
index 87cfb0e0849f..3533a8c0253f 100644
--- a/tools/perf/pmu-events/arch/riscv/mapfile.csv
+++ b/tools/perf/pmu-events/arch/riscv/mapfile.csv
@@ -24,3 +24,4 @@
 0x602-0x3-0x0,v1,openhwgroup/cva6,core
 0x67e-0x80000000db0000[89]0-0x[[:xdigit:]]+,v1,starfive/dubhe-80,core
 0x31e-0x8000000000008a45-0x[[:xdigit:]]+,v1,andes/ax45,core
+0x0-0x0-0x0,v1,qemu/virt,core
diff --git a/tools/perf/pmu-events/arch/riscv/qemu/virt/events.json b/tools/perf/pmu-events/arch/riscv/qemu/virt/events.json
new file mode 100644
index 000000000000..294c4ed645f6
--- /dev/null
+++ b/tools/perf/pmu-events/arch/riscv/qemu/virt/events.json
@@ -0,0 +1,26 @@
+[
+  {
+    "EventName": "fake-json-any",
+    "EventCode": "0xF10",
+    "CounterIDMask": "0xFFFFFFF8",
+    "BriefDescription": "FAKE json event (any hpmcounter 3-31) - QEMU does not model 0xF10"
+  },
+  {
+    "EventName": "fake-json-ctr3",
+    "EventCode": "0xF11",
+    "CounterIDMask": "0x8",
+    "BriefDescription": "FAKE json event constrained to hpmcounter3"
+  },
+  {
+    "EventName": "fake-json-ctr34",
+    "EventCode": "0xF12",
+    "CounterIDMask": "0x18",
+    "BriefDescription": "FAKE json event constrained to hpmcounter3,4"
+  },
+  {
+    "EventName": "fake-json-ctr6",
+    "EventCode": "0xF13",
+    "CounterIDMask": "0x40",
+    "BriefDescription": "FAKE json event constrained to hpmcounter6 (out of a small pmu-mask)"
+  }
+]

-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 06/21] dt-bindings: riscv: add Smcntrpmf ISA extension description
  2026-06-09  6:01 ` [PATCH v6 06/21] dt-bindings: riscv: add Smcntrpmf ISA extension description Atish Patra
@ 2026-06-09  6:09   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:09 UTC (permalink / raw)
  To: Atish Patra; +Cc: robh, conor+dt, devicetree, linux-perf-users

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] The `smcntrpmf` extension is inserted in the wrong alphabetical order.
- [Low] The description incorrectly refers to `Smcntrpmf` as a 'supervisor-level' extension.
--

commit 9fd028f70a33e1a78db2f635eed4200c33b6b90d
Author: Atish Patra <atishp@rivosinc.com>

dt-bindings: riscv: add Smcntrpmf ISA extension description

Add the description for the Smcntrpmf ISA extension.

> diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
> index 4be557dc215de..ece3edccee42c 100644
> --- a/Documentation/devicetree/bindings/riscv/extensions.yaml
> +++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
> @@ -189,6 +189,12 @@ properties:
>              mechanism in M-mode as ratified in the 20240326 version of the
>              privileged ISA specification.
>  
> +        - const: smcntrpmf

[Severity: Low]
Is this inserted in the correct alphabetical order?

The multi-letter extensions list in this file explicitly requires entries to be
sorted alphanumerically. Alphabetically, smcntrpmf ("c-n") should come before
the preceding extension (smcsrind, "c-s").

> +          description: |
> +            The standard Smcntrpmf supervisor-level extension for the machine mode

[Severity: Low]
Is "supervisor-level" accurate here?

In the RISC-V ISA naming convention, the "Sm" prefix denotes a standard
machine-level extension, while "Ss" denotes a supervisor-level extension.

Calling it a "supervisor-level extension for the machine mode" seems
contradictory. Could this be a copy-paste error?

> +            to enable privilege mode filtering for cycle and instret counters as
> +            ratified in the 20240326 version of the privileged ISA specification.
> +
>          - const: smmpm
>            description: |
>              The standard Smmpm extension for M-mode pointer masking as

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=6

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 03/21] dt-bindings: riscv: add Sxcsrind ISA extension description
  2026-06-09  6:01 ` [PATCH v6 03/21] dt-bindings: riscv: add Sxcsrind ISA extension description Atish Patra
@ 2026-06-09  6:09   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:09 UTC (permalink / raw)
  To: Atish Patra; +Cc: linux-perf-users, conor+dt, robh, devicetree

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] The `sscsrind` extension is inserted out of alphanumeric order, violating the explicit formatting rule in the binding file.
- [Low] The description for `smcsrind` contains a copy-paste error incorrectly labeling it as a 'supervisor-level' extension, and both `smcsrind` and `sscsrind` contain a grammatical typo.
--

commit a4e1326a37463e042ca0a72ff72d93f2be1d22f8
Author: Atish Patra <atishp@rivosinc.com>

dt-bindings: riscv: add Sxcsrind ISA extension description

Add the S[m|s]csrind ISA extension description.

> diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
> index 2b0a8a93bb214..4be557dc215de 100644
> --- a/Documentation/devicetree/bindings/riscv/extensions.yaml
> +++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
> @@ -181,6 +181,14 @@ properties:
>              changes to interrupts as frozen at commit ccbddab ("Merge pull
>              request #42 from riscv/jhauser-2023-RC4") of riscv-aia.
>  
> +        - const: smcsrind
> +          description: |
> +            The standard Smcsrind supervisor-level extension extends the
> +            indirect CSR access mechanism defined by the Smaia extension. This

[Severity: Low]
Is this description accurate for smcsrind? The m in Smcsrind indicates
it is a machine-level extension, rather than a supervisor-level extension.

> +            extension allows other ISA extension to use indirect CSR access

[Severity: Low]
This isn't a bug, but there is a typo here. Should "ISA extension" be
pluralized to "ISA extensions"?

> +            mechanism in M-mode as ratified in the 20240326 version of the
> +            privileged ISA specification.
> +

[ ... ]

> @@ -199,6 +207,14 @@ properties:
>              added by other RISC-V extensions in H/S/VS/U/VU modes and as
>              ratified at commit a28bfae (Ratified (#7)) of riscv-state-enable.
>  
> +        - const: sscsrind
> +          description: |
> +            The standard Sscsrind supervisor-level extension extends the
> +            indirect CSR access mechanism defined by the Ssaia extension. This
> +            extension allows other ISA extension to use indirect CSR access

[Severity: Low]
This isn't a bug, but there is the same typo here. Should "ISA extension" be
pluralized to "ISA extensions"?

> +            mechanism in S-mode as ratified in the 20240326 version of the
> +            privileged ISA specification.
> +
>          - const: ssaia

[Severity: Low]
Should sscsrind be placed after ssaia rather than before it? The
extensions in this file are expected to be sorted alphanumerically.

>            description: |
>              The standard Ssaia supervisor-level extension for the advanced

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 09/21] dt-bindings: riscv: add Counter delegation ISA extensions description
  2026-06-09  6:01 ` [PATCH v6 09/21] dt-bindings: riscv: add Counter delegation ISA extensions description Atish Patra
@ 2026-06-09  6:12   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:12 UTC (permalink / raw)
  To: Atish Patra; +Cc: conor+dt, linux-perf-users, robh, devicetree

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] Incorrect dependency for smcdeleg: depends on sscsrind instead of smcsrind.
- [Medium] The ssccfg extension is over-constrained by strictly requiring smcntrpmf.
- [Low] Contradictory description for the smcdeleg extension.
--

commit 53fb261803de68f9caaa685d63588c9cac6c0f5c
Author: Atish Patra <atishp@rivosinc.com>

dt-bindings: riscv: add Counter delegation ISA extensions description

This commit adds device tree binding descriptions for the Smcdeleg and
Ssccfg RISC-V ISA extensions. It also specifies the conditional dependencies
required for these extensions to pass schema validation.

> diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
> index ece3edccee42c..2845e8e2999a7 100644
> --- a/Documentation/devicetree/bindings/riscv/extensions.yaml
> +++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
> @@ -181,6 +181,13 @@ properties:
>              changes to interrupts as frozen at commit ccbddab ("Merge pull
>              request #42 from riscv/jhauser-2023-RC4") of riscv-aia.
>  
> +        - const: smcdeleg
> +          description: |
> +            The standard Smcdeleg supervisor-level extension for the machine mode

[Severity: Low]
This isn't a bug, but should this say machine-level instead of
supervisor-level? Since it has an Sm prefix and is meant for machine mode,
calling it a supervisor-level extension might be a copy-paste typo.

> +            to delegate the hpmcounters to supervisor mode so that they are
> +            directly accessible in the supervisor mode as ratified in the
> +            20240213 version of the privileged ISA specification.

[ ... ]

> @@ -1135,6 +1150,36 @@ properties:
>              allOf:
>                - const: zilsd
>                - const: zca
> +      # Smcdeleg depends on Sscsrind, Zihpm, Zicntr
> +      - if:
> +          contains:
> +            const: smcdeleg
> +        then:
> +          allOf:
> +            - contains:
> +                const: sscsrind

[Severity: Medium]
Does smcdeleg actually depend on sscsrind here?

Since smcdeleg is a machine-mode extension that uses M-mode indirect CSRs to
delegate counters, shouldn't it depend on the smcsrind extension instead of
sscsrind? If a valid hardware configuration implements smcsrind and smcdeleg
without sscsrind, would this schema incorrectly reject it?

> +            - contains:
> +                const: zihpm
> +            - contains:
> +                const: zicntr
> +      # Ssccfg depends on Smcdeleg, Sscsrind, Zihpm, Zicntr, Sscofpmf, Smcntrpmf
> +      - if:
> +          contains:
> +            const: ssccfg
> +        then:
> +          allOf:
> +            - contains:
> +                const: smcdeleg
> +            - contains:
> +                const: sscsrind
> +            - contains:
> +                const: sscofpmf
> +            - contains:
> +                const: smcntrpmf

[Severity: Medium]
Is smcntrpmf a strict requirement for ssccfg?

The ssccfg extension provides supervisor-mode access to delegated counters,
but the kernel implementation seems to only conditionally expose
cyclecfg/instretcfg CSRs if smcntrpmf is present. If ssccfg can exist
independently, will requiring smcntrpmf in this binding incorrectly fail
validation for valid hardware that omits it?

> +            - contains:
> +                const: zihpm
> +            - contains:
> +                const: zicntr

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=9

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 08/21] RISC-V: Add Ssccfg/Smcdeleg ISA extension definition and parsing
  2026-06-09  6:01 ` [PATCH v6 08/21] RISC-V: Add Ssccfg/Smcdeleg ISA extension definition and parsing Atish Patra
@ 2026-06-09  6:14   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:14 UTC (permalink / raw)
  To: Atish Patra; +Cc: robh, linux-perf-users, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Supervisor extension Ssccfg incorrectly depends on Machine extension Smcdeleg, breaking virtualization.
- [Medium] The Smcdeleg extension validation incorrectly checks for the Supervisor-level CSR indirection extension (SSCSRIND) instead of the Machine-level one (SMCSRIND).
--

commit 53e139b078ec8b41f58b09bfd3d37e7132a15d45
Author: Atish Patra <atishp@rivosinc.com>

    RISC-V: Add Ssccfg/Smcdeleg ISA extension definition and parsing

This commit adds definitions and parsing logic for the RISC-V Ssccfg
and Smcdeleg ISA extensions. It introduces validation functions for
both extensions and adds them to the riscv_isa_ext array.

> diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> index 1452521d740a6..1fe647e035157 100644
> --- a/arch/riscv/kernel/cpufeature.c
> +++ b/arch/riscv/kernel/cpufeature.c
> @@ -330,6 +330,27 @@ static const unsigned int riscv_a_exts[] = {
>  	RISCV_ISA_EXT_ZKNE,	\
>  	RISCV_ISA_EXT_ZKNH
>  
> +static int riscv_ext_smcdeleg_validate(const struct riscv_isa_ext_data *data,
> +				       const unsigned long *isa_bitmap)
> +{
> +	if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SSCSRIND) &&
                                                        ^^^^^^^^^^^^^^^^^^^^^^
[Severity: Medium]
Since Smcdeleg is a Machine-mode extension, shouldn't its validation depend on
the Machine-level CSR indirect access extension (SMCSRIND) rather than the
Supervisor-level extension (SSCSRIND)?

If the kernel runs in M-mode, or on hardware that implements SMCSRIND but not
SSCSRIND, this validation might incorrectly fail.

> +	    __riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_ZIHPM) &&
> +	    __riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_ZICNTR))
> +		return 0;
> +
> +	return -EPROBE_DEFER;
> +}
> +
> +static int riscv_ext_ssccfg_validate(const struct riscv_isa_ext_data *data,
> +				     const unsigned long *isa_bitmap)
> +{
> +	if (!riscv_ext_smcdeleg_validate(data, isa_bitmap) &&
> +	    __riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SMCDELEG))

[Severity: High]
Does this break supervisor counter delegation in virtualized environments?

Smcdeleg is a Machine-mode extension, and hypervisors typically do not expose
M-mode extensions to guest VMs. A guest kernel running in VS-mode won't see
Smcdeleg in its ISA string, causing this validation to unconditionally fail
and return -EPROBE_DEFER.

Should Ssccfg validation only depend on S-mode visible extensions?

> +		return 0;
> +
> +	return -EPROBE_DEFER;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=8

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 04/21] RISC-V: Define indirect CSR access helpers
  2026-06-09  6:01 ` [PATCH v6 04/21] RISC-V: Define indirect CSR access helpers Atish Patra
@ 2026-06-09  6:15   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:15 UTC (permalink / raw)
  To: Atish Patra; +Cc: robh, devicetree, linux-perf-users, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Unsafe statement-expression macro definitions causing delayed evaluation and variable shadowing hazards.
--

commit 572b7555547c1b5bc07701482ba67d379f6a2177
Author: Atish Patra <atishp@rivosinc.com>

RISC-V: Define indirect CSR access helpers

This commit introduces helper macros to read, write, and perform WARL
operations on indirect CSRs. The macros use statement-expressions to handle
the multiple instructions required while disabling interrupts.

> diff --git a/arch/riscv/include/asm/csr_ind.h b/arch/riscv/include/asm/csr_ind.h
> new file mode 100644
> index 0000000000000..6fd7d44dc640a
> --- /dev/null
> +++ b/arch/riscv/include/asm/csr_ind.h
> @@ -0,0 +1,44 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2024 Rivos Inc.
> + */
> +
> +#ifndef _ASM_RISCV_CSR_IND_H
> +#define _ASM_RISCV_CSR_IND_H
> +
> +#include <linux/irqflags.h>
> +
> +#include <asm/csr.h>
> +
> +#define csr_ind_read(iregcsr, iselbase, iseloff) ({		\
> +	unsigned long __value = 0;				\
> +	unsigned long __flags;					\

[Severity: Medium]
Will these generic variable names (__value, __flags) cause shadowing issues if
the caller passes in arguments with the same names?

Evaluating macro arguments into unique local variables before entering the
IRQ-disabled region would avoid this regression.

> +	local_irq_save(__flags);				\
> +	csr_write(CSR_ISELECT, (iselbase) + (iseloff));		\
> +	__value = csr_read(iregcsr);				\
> +	local_irq_restore(__flags);				\
> +	__value;						\
> +})
> +
> +#define csr_ind_write(iregcsr, iselbase, iseloff, value) ({	\
> +	unsigned long __flags;					\
> +	local_irq_save(__flags);				\
> +	csr_write(CSR_ISELECT, (iselbase) + (iseloff));		\
> +	csr_write(iregcsr, (value));				\

[Severity: Medium]
Since value is evaluated here after CSR_ISELECT has been programmed, what
happens if a caller passes an expression that accesses another indirect CSR,
such as a nested csr_ind_read(), as the value argument?

It appears the nested call would overwrite CSR_ISELECT, causing the write to
silently corrupt the wrong hardware register. Evaluating arguments into local
variables before disabling interrupts would prevent this, and also avoid the
risk of evaluating expressions with side effects while interrupts are disabled.

> +	local_irq_restore(__flags);				\
> +})
> +
> +#define csr_ind_warl(iregcsr, iselbase, iseloff, warl_val) ({	\
> +	unsigned long __old_val = 0, __value = 0;		\
> +	unsigned long __flags;					\

[Severity: Medium]
Do these generic local variables risk shadowing caller arguments?

> +	local_irq_save(__flags);				\
> +	csr_write(CSR_ISELECT, (iselbase) + (iseloff));		\
> +	__old_val = csr_read(iregcsr);				\
> +	csr_write(iregcsr, (warl_val));				\

[Severity: Medium]
Is there a risk that evaluating warl_val here could overwrite CSR_ISELECT if
the caller provides an expression that performs another indirect CSR access?

> +	__value = csr_read(iregcsr);				\
> +	csr_write(iregcsr, __old_val);				\
> +	local_irq_restore(__flags);				\
> +	__value;						\
> +})
> +
> +#endif

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=4

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 12/21] RISC-V: perf: Add a mechanism to defined legacy event encoding
  2026-06-09  6:01 ` [PATCH v6 12/21] RISC-V: perf: Add a mechanism to defined legacy event encoding Atish Patra
@ 2026-06-09  6:16   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:16 UTC (permalink / raw)
  To: Atish Patra; +Cc: robh, linux-perf-users, devicetree, conor+dt

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

New issues:
- [Medium] HW_OP_UNSUPPORTED is defined as 0xFFFF but assigned to a 64-bit event_id, risking collision with valid vendor event IDs.
- [Low] Public macros in shared header rely on an undeclared `C()` macro and a private struct.
- [Low] Unconditional boot-time pr_info spam and incorrect logging condition for missing PMU events.
- [Low] Missing `__init` annotation on `rvpmu_vendor_register_events`.

Pre-existing issues:
- [High] Missing `perf_pmu_unregister()` on error paths in `rvpmu_device_probe` causes Use-After-Free.
--

commit 8f8945d4e47e7d93800a2f3a4d8c51c826b9b889
Author: Atish Patra <atishp@rivosinc.com>
RISC-V: perf: Add a mechanism to defined legacy event encoding

This patch introduces a framework to support vendor-specific PMU event
encodings and counter mappings for the RISC-V ISA. It adds vendor event
tables and registers them during PMU initialization.

> --- a/include/linux/perf/riscv_pmu.h
> +++ b/include/linux/perf/riscv_pmu.h
[ ... ]
> +#define HW_OP_UNSUPPORTED		0xFFFF
> +#define CACHE_OP_UNSUPPORTED		0xFFFF

[Severity: Medium]
Does using 0xFFFF as an unsupported marker risk colliding with valid
vendor event IDs?

Because RISC-V hardware event encodings can be up to 64 bits, a vendor
might legitimately define an event ID that evaluates to exactly 0xFFFF
when assigned to the 64-bit event_id field in struct riscv_pmu_event.

Could we use a standard unassigned sentinel such as U64_MAX instead, to
prevent falsely rejecting valid events?

> +
> +#define PERF_MAP_ALL_UNSUPPORTED					\
> +	[0 ... PERF_COUNT_HW_MAX - 1] = {HW_OP_UNSUPPORTED, 0x0}
> +
> +#define PERF_CACHE_MAP_ALL_UNSUPPORTED					\
> +[0 ... C(MAX) - 1] = {							\
> +	[0 ... C(OP_MAX) - 1] = {					\
> +		[0 ... C(RESULT_MAX) - 1] = {CACHE_OP_UNSUPPORTED, 0x0}	\
> +	},								\
> +}

[Severity: Low]
Is it safe to expose this macro in a public header?

This macro relies on the C() macro, which is not defined in this header,
potentially causing build failures if used by external modules.

Additionally, it appears to initialize an array structure meant for
struct riscv_pmu_event, which is defined privately in riscv_pmu_sbi.c.

Does this break encapsulation by requiring external users to have access
to that private struct?

> --- a/drivers/perf/riscv_pmu_sbi.c
> +++ b/drivers/perf/riscv_pmu_sbi.c
[ ... ]
> +static void rvpmu_vendor_register_events(void)

[Severity: Low]
Would it be beneficial to add the __init annotation to this function?

Since this function is exclusively called from rvpmu_devinit(), which is
an __init function itself, lacking the annotation means its instructions
will unnecessarily consume memory for the lifetime of the kernel instead
of being freed after boot.

> +{
> +	int cpu = raw_smp_processor_id();
> +	unsigned long vendor_id = riscv_cached_mvendorid(cpu);
[ ... ]
> +	if (!current_pmu_hw_event_map || !current_pmu_cache_event_map) {
> +		pr_info("No default PMU events found\n");
> +	}
> +}

[Severity: Low]
Can this condition cause unintentional log spam on boot?

Because of the logical OR operator, a platform that provides only one of
the maps will still trigger this log, incorrectly stating that no events
were found.

Furthermore, since the table currently only includes a QEMU entry, real
hardware with counter delegation extensions might unconditionally print
this on every boot.

Would it be better to use a logical AND here, and perhaps pr_debug?

[ ... ]

[Severity: High]
This isn't a bug introduced by this patch, but while reviewing the PMU
registration code, I noticed a potential memory lifecycle issue in the
existing probe error path.

Looking at rvpmu_device_probe(), the PMU is registered with
perf_pmu_register(). If a subsequent step fails, the code jumps to the
out_unregister label, which calls riscv_pmu_destroy() and then frees
the pmu struct.

However, riscv_pmu_destroy() handles hotplug state and CPU-PM, but
does not appear to call perf_pmu_unregister().

Could this leave dangling pointers in the perf core and sysfs to the
freed pmu structure, resulting in a use-after-free if those interfaces
are accessed?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=12

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 20/21] TEST(do-not-upstream): fake qemu-virt PMU events for cdeleg counter-mask testing
  2026-06-09  6:01 ` [PATCH v6 20/21] TEST(do-not-upstream): fake qemu-virt PMU events for cdeleg counter-mask testing Atish Patra
@ 2026-06-09  6:17   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:17 UTC (permalink / raw)
  To: Atish Patra; +Cc: robh, conor+dt, devicetree, linux-perf-users

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The patch adds synthetic test events explicitly marked 'TEST(do-not-upstream)' to the production RISC-V SBI PMU driver, polluting the UAPI.
--

commit 286ddcbf491d3a026da1733168d9b7d29bcd2fbe
Author: Atish Patra <atishp@meta.com>

TEST(do-not-upstream): fake qemu-virt PMU events for cdeleg counter-mask testing

This commit adds fake performance monitoring events to test counter delegation
and counter-mask constraints in Qemu. The events use codes that Qemu does not
model to verify allocation and programming behavior.

> diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
> index 74acac54328e0..3c0829c0a42ab 100644
> --- a/drivers/perf/riscv_pmu_sbi.c
> +++ b/drivers/perf/riscv_pmu_sbi.c
[ ... ]
> @@ -483,6 +489,9 @@ static struct attribute *qemu_virt_event_group[] = {
>  	RVPMU_EVENT_ATTR_PTR(dTLB_load_miss),
>  	RVPMU_EVENT_ATTR_PTR(dTLB_store_miss),
>  	RVPMU_EVENT_ATTR_PTR(iTLB_load_miss),
> +	RVPMU_EVENT_ATTR_PTR(fake_any),
> +	RVPMU_EVENT_ATTR_PTR(fake_ctr3),
> +	RVPMU_EVENT_ATTR_PTR(fake_ctr34),

[Severity: Medium]
Does this code introduce a regression by exposing test functionality?

This patch is explicitly marked "do-not-upstream" and unconditionally adds
synthetic test events to the exposed Qemu platform pmu events array in
qemu_virt_event_group[].

Exposing these fake hardware events (fake-any, fake-ctr3, fake-ctr34)
to userspace pollutes the production sysfs uapi. These will appear in
perf list and could confuse users or automated tools.

>  	NULL,
>  };

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=20

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 11/21] RISC-V: perf: Modify the counter discovery mechanism
  2026-06-09  6:01 ` [PATCH v6 11/21] RISC-V: perf: Modify the counter discovery mechanism Atish Patra
@ 2026-06-09  6:17   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:17 UTC (permalink / raw)
  To: Atish Patra; +Cc: linux-perf-users, devicetree, conor+dt, robh

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Negative error codes returned by SBI are improperly cast to an unsigned integer, and systems with >64 counters are incorrectly aborted instead of clamped.
--

commit ea0f7376968d4f5943d9d279886b0b59e6dfd41c
Author: Atish Patra <atishp@rivosinc.com>
Subject: RISC-V: perf: Modify the counter discovery mechanism

This commit modifies the PMU counter discovery mechanism to use a hybrid
scheme. It utilizes counter delegation for hardware PMU counters while falling
back to the SBI PMU for firmware counters, and updates logging appropriately.

> diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
> index 7f21c16003f04..57ab15beab3e1 100644
> --- a/drivers/perf/riscv_pmu_sbi.c
> +++ b/drivers/perf/riscv_pmu_sbi.c

[ ... ]

> @@ -1159,16 +1180,42 @@ static void rvpmu_ctr_stop(struct perf_event *event, unsigned long flag)
>  	/* TODO: Counter delegation implementation */
>  }
>  
> -static int rvpmu_find_num_ctrs(void)
> +static int rvpmu_find_ctrs(void)
>  {
> -	return rvpmu_sbi_find_num_ctrs();
> -	/* TODO: Counter delegation implementation */
> -}
> +	u32 num_sbi_counters = 0, num_deleg_counters = 0;
> +	u32 num_hw_ctr = 0, num_fw_ctr = 0, num_ctr = 0;
> +	/*
> +	 * We don't know how many firmware counters are available. Just allocate
> +	 * for maximum counters the driver can support. The default is 64 anyways.
> +	 */
> +	pmu_ctr_list = kcalloc(RISCV_MAX_COUNTERS, sizeof(*pmu_ctr_list),
> +			       GFP_KERNEL);
> +	if (!pmu_ctr_list)
> +		return -ENOMEM;
>  
> -static int rvpmu_get_ctrinfo(int nctr, unsigned long *mask)
> -{
> -	return rvpmu_sbi_get_ctrinfo(nctr, mask);
> -	/* TODO: Counter delegation implementation */
> +	if (riscv_pmu_cdeleg_available_boot())
> +		num_deleg_counters = rvpmu_deleg_find_ctrs();
> +
> +	/* This is required for firmware counters even if the above is true */
> +	if (riscv_pmu_sbi_available_boot())
> +		num_sbi_counters = rvpmu_sbi_find_num_ctrs();
> +
> +	if (num_sbi_counters > RISCV_MAX_COUNTERS || num_deleg_counters > RISCV_MAX_COUNTERS)
> +		return -ENOSPC;

[Severity: High]
Does this code inadvertently convert negative error codes from
rvpmu_sbi_find_num_ctrs() into large unsigned values?

Since num_sbi_counters is defined as a u32, if rvpmu_sbi_find_num_ctrs()
returns an error code like -EOPNOTSUPP, it will wrap to a large positive
integer, instantly satisfying the > RISCV_MAX_COUNTERS condition. This seems
to mask the original error code.

If the > RISCV_MAX_COUNTERS condition is met, the function returns -ENOSPC
early. Can this leak the pmu_ctr_list allocated via kcalloc() just above,
since there is no free before the return?

Additionally, could this introduce a regression for platforms legitimately
exposing more than 64 counters? Previously, the driver appeared to safely
clamp the usable count to 64 and continue, whereas this new logic seems to
abort the driver probe entirely.

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=11

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 18/21] tools/perf: Support event code for arch standard events
  2026-06-09  6:01 ` [PATCH v6 18/21] tools/perf: Support event code for arch standard events Atish Patra
@ 2026-06-09  6:18   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:18 UTC (permalink / raw)
  To: Atish Patra; +Cc: conor+dt, robh, devicetree, linux-perf-users

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The `if not eventcode:` check incorrectly evaluates to True for explicit encodings where the event code is `0` or when alternative encodings (like `ConfigCode`) are used, causing them to be overwritten.
--

commit c082fe77b89457807536772c21ae88a13885568d
Author: Atish Patra <atishp@rivosinc.com>

tools/perf: Support event code for arch standard events

This commit updates the perf tool to support custom event encodings for
architecture standard events. It modifies jevents.py to avoid overwriting
the event encoding with the architecture standard event if an event code is
already present in the JSON file.

[ ... ]

> diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
> index 3a1bcdcdc685f..457fce7a59820 100755
> --- a/tools/perf/pmu-events/jevents.py
> +++ b/tools/perf/pmu-events/jevents.py
> @@ -413,7 +413,11 @@ class JsonEvent:
>          self.long_desc = None
>      if arch_std:
>        if arch_std.lower() in _arch_std_events:
> -        event = _arch_std_events[arch_std.lower()].event
> +        # If the JSON event already specified an event code, the encoding has
> +        # been set above; don't overwrite it with the arch standard event or
> +        # the event encoding would be lost.
> +        if not eventcode:
[Severity: Medium]
Does this condition incorrectly evaluate to True for valid 0 encodings?

If a JSON event explicitly specifies an event code of 0 (e.g.,
"EventCode": "0x0"), the parsed eventcode will be 0. Because Python evaluates
0 as false, the not eventcode condition evaluates to True.

This incorrectly overwrites the explicit encoding and discards previously
parsed event fields.

Can the same regression occur if alternative encodings like ConfigCode or
EventidCode are used, leaving the eventcode variable at 0?

Would it be safer to explicitly check if the eventcode is None to distinguish
between an absent event code and a literal zero?

> +          event = _arch_std_events[arch_std.lower()].event
>          # Copy from the architecture standard event to self for undefined fields.
>          for attr, value in _arch_std_events[arch_std.lower()].__dict__.items():
>            if hasattr(self, attr) and not getattr(self, attr):

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=18

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 10/21] RISC-V: perf: Restructure the SBI PMU code
  2026-06-09  6:01 ` [PATCH v6 10/21] RISC-V: perf: Restructure the SBI PMU code Atish Patra
@ 2026-06-09  6:18   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:18 UTC (permalink / raw)
  To: Atish Patra; +Cc: linux-perf-users, robh, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [Critical] NULL pointer dereference when tearing down the PMU on error paths in probe.
- [High] Missing cleanup on error paths in rvpmu_device_probe() leads to Use-After-Free and resource leaks.
- [High] Broadcasting `SCOUNTEREN` updates to all CPUs in `mm_cpumask` leaks counter access to userspace.
--

commit 497e3337649afaedc330eca31dba2dda185d6179
Author: Atish Patra <atishp@rivosinc.com>

    RISC-V: perf: Restructure the SBI PMU code
    
    With Ssccfg/Smcdeleg, supervisor mode can program and access the hpmcounters
    and events directly, without the SBI PMU extension. The SBI PMU extension is
    still required for firmware counters. Restructure the existing SBI PMU code so
    the hpmcounter/event helpers can be shared between the SBI and the counter
    delegation paths that follow.

> diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
> index 385af5e6e6d0d..7f21c16003f04 100644
> --- a/drivers/perf/riscv_pmu_sbi.c
> +++ b/drivers/perf/riscv_pmu_sbi.c

[ ... ]

> @@ -1305,7 +1349,7 @@ static void riscv_pmu_destroy(struct riscv_pmu *pmu)
>  	cpuhp_state_remove_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
>  }

[Severity: Critical]
This is a pre-existing issue, but does this code cause a NULL pointer
dereference on error paths?

In rvpmu_device_probe(), if an error occurs (such as perf_pmu_register(),
pmu_sbi_snapshot_alloc(), or cpuhp_state_add_instance() failing), the error
path jumps to out_unregister and calls riscv_pmu_destroy().

Because cpuhp_state_add_instance() either was not called yet, or failed and
did not hash the node, pmu->node.pprev is NULL. The function
cpuhp_state_remove_instance() does not check if the node is unhashed and
directly calls hlist_del(). This attempts to write to the previous node
pointer, which crashes the kernel with a NULL pointer dereference.

> -static void pmu_sbi_event_init(struct perf_event *event)
> +static void rvpmu_event_init(struct perf_event *event)

[ ... ]

> @@ -1347,14 +1391,14 @@ static void pmu_sbi_event_mapped(struct perf_event *event, struct mm_struct *mm)
>  	 * that it is possible to do so to avoid any race.
>  	 * And we must notify all cpus here because threads that currently run
>  	 * on other cpus will try to directly access the counter too without
> -	 * calling pmu_sbi_ctr_start.
> +	 * calling rvpmu_sbi_ctr_start.
>  	 */
>  	if (event->hw.flags & PERF_EVENT_FLAG_USER_ACCESS)
>  		on_each_cpu_mask(mm_cpumask(mm),
> -				 pmu_sbi_set_scounteren, (void *)event, 1);
> +				 rvpmu_set_scounteren, (void *)event, 1);
>  }

[Severity: High]
This isn't a bug introduced by this patch, but does broadcasting scounteren
updates to all CPUs leak counter access to userspace?

When a user-accessible event is mapped, rvpmu_set_scounteren is broadcast
across all CPUs where the process has threads running. This enables the
scounteren bit corresponding to event->hw.idx.

However, event->hw.idx is a dynamic, per-CPU hardware counter index allocated
only on the CPU where the event was scheduled. Broadcasting this index across
all CPUs in the mask grants userspace access to that specific hardware
counter slot on CPUs where the event is not running.

Could a thread on another CPU read from that counter slot, potentially leaking
the count of whatever unrelated perf event is active on that counter?

> -static void pmu_sbi_event_unmapped(struct perf_event *event, struct mm_struct *mm)
> +static void rvpmu_event_unmapped(struct perf_event *event, struct mm_struct *mm)

[ ... ]

> @@ -1419,7 +1463,7 @@ static const struct ctl_table sbi_pmu_sysctl_table[] = {
>  	},
>  };
>  
> -static int pmu_sbi_device_probe(struct platform_device *pdev)
> +static int rvpmu_device_probe(struct platform_device *pdev)
>  {
>  	struct riscv_pmu *pmu = NULL;
>  	int ret = -ENODEV;

[Severity: High]
This is a pre-existing issue, but does this probe function correctly clean
up resources on error paths?

If initialization fails after perf_pmu_register(), the code jumps to the
out_unregister label:

out_unregister:
	riscv_pmu_destroy(pmu);

out_free:
	kfree(pmu);
	return ret;

However, riscv_pmu_destroy() does not call perf_pmu_unregister(). This leaves
the core perf subsystem with a dangling pointer to the freed pmu structure,
which can lead to a use-after-free when the perf core attempts to access it.

Additionally, the per-CPU memory allocated for pmu->hw_events and the per-CPU
interrupts requested via request_percpu_irq() are never freed on these error
paths, resulting in resource leaks.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=10

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 21/21] TEST(do-not-upstream): fake qemu vendor JSON + mapfile entry for CounterIDMask path
  2026-06-09  6:01 ` [PATCH v6 21/21] TEST(do-not-upstream): fake qemu vendor JSON + mapfile entry for CounterIDMask path Atish Patra
@ 2026-06-09  6:20   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:20 UTC (permalink / raw)
  To: Atish Patra; +Cc: linux-perf-users, robh, devicetree, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [Medium] This patch is explicitly marked 'TEST(do-not-upstream)' and adds fake testing events, which should not be merged into the mainline kernel.

Pre-existing issues:
- [High] Unconditional NULL pointer dereference in `get_cpuid`.
--

commit d0ac889c30ab39821cba995143de6c24abe2efcc
Author: Atish Patra <atishp@meta.com>

TEST(do-not-upstream): fake qemu vendor JSON + mapfile entry for CounterIDMask path

This patch adds fake QEMU vendor JSON events and a corresponding mapfile entry
for the CounterIDMask path to test PMU masking.

> diff --git a/tools/perf/pmu-events/arch/riscv/mapfile.csv b/tools/perf/pmu-events/arch/riscv/mapfile.csv
> index 87cfb0e0849fc..3533a8c0253f7 100644
> --- a/tools/perf/pmu-events/arch/riscv/mapfile.csv
> +++ b/tools/perf/pmu-events/arch/riscv/mapfile.csv
> @@ -24,3 +24,4 @@
>  0x602-0x3-0x0,v1,openhwgroup/cva6,core
>  0x67e-0x80000000db0000[89]0-0x[[:xdigit:]]+,v1,starfive/dubhe-80,core
>  0x31e-0x8000000000008a45-0x[[:xdigit:]]+,v1,andes/ax45,core
> +0x0-0x0-0x0,v1,qemu/virt,core

[Severity: Medium]
Since this patch is explicitly marked "TEST(do-not-upstream)" and adds fake
PMU events strictly for testing, should it be merged into the mainline
kernel?

Merging this will populate the perf tool's PMU event tables with non-existent
hardware events.

> diff --git a/tools/perf/pmu-events/arch/riscv/qemu/virt/events.json b/tools/perf/pmu-events/arch/riscv/qemu/virt/events.json
> new file mode 100644
> index 0000000000000..294c4ed645f67
> --- /dev/null
> +++ b/tools/perf/pmu-events/arch/riscv/qemu/virt/events.json
> @@ -0,0 +1,26 @@
> +[
> +  {
> +    "EventName": "fake-json-any",

[ ... ]

While reviewing this, I also noticed a pre-existing issue in
tools/perf/arch/riscv/util/header.c:

tools/perf/arch/riscv/util/header.c:get_cpuid() {
    ...
	char *cpuid = _get_cpuid();
	int ret = 0;

	if (sz < strlen(cpuid)) {
		ret = -EINVAL;
		goto free;
	}
    ...
}

[Severity: High]
This is a pre-existing issue, but can get_cpuid() cause a segmentation fault
here? If _get_cpuid() returns NULL (for example, if /proc/cpuinfo is missing
or lacks specific fields in a container or QEMU without proper emulation), it
appears the unconditional call to strlen(cpuid) will dereference that NULL
pointer.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=21

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 16/21] RISC-V: perf: Add legacy event encodings via sysfs
  2026-06-09  6:01 ` [PATCH v6 16/21] RISC-V: perf: Add legacy event encodings via sysfs Atish Patra
@ 2026-06-09  6:21   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:21 UTC (permalink / raw)
  To: Atish Patra; +Cc: conor+dt, devicetree, robh, linux-perf-users

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The globally scoped pmu_ctr_list array is leaked if PMU counter discovery fails, compounded by a flawed error code cast.
--

commit 7d64e724e6eff197f10e62cdd709e07f0a5c9a7d
Author: Atish Patra <atishp@rivosinc.com>

RISC-V: perf: Add legacy event encodings via sysfs

Define sysfs details for the legacy events so that any tool can
parse these to understand the minimum set of legacy events
supported by the platform. The sysfs entry will describe both event
encoding and corresponding counter map so that an perf event can be
programmed accordingly.

> diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
> index 4b4f151a0744d..00b84b28117a8 100644
> --- a/drivers/perf/riscv_pmu_sbi.c
> +++ b/drivers/perf/riscv_pmu_sbi.c
> @@ -130,7 +130,20 @@ static struct attribute_group riscv_cdeleg_pmu_format_group = {
>  	.attrs = riscv_cdeleg_pmu_formats_attr,
>  };

[ ... ]

[Severity: Medium]
Does this code leak the pmu_ctr_list array if PMU counter discovery fails?

In rvpmu_find_ctrs(), pmu_ctr_list is allocated via kcalloc(), but the function
bails out without freeing it if num_sbi_counters or num_deleg_counters
exceeds RISCV_MAX_COUNTERS:

drivers/perf/riscv_pmu_sbi.c:rvpmu_find_ctrs() {
...
	pmu_ctr_list = kcalloc(RISCV_MAX_COUNTERS, sizeof(*pmu_ctr_list),
			       GFP_KERNEL);
	if (!pmu_ctr_list)
		return -ENOMEM;

	if (riscv_pmu_cdeleg_available_boot())
		num_deleg_counters = rvpmu_deleg_find_ctrs();

	/* This is required for firmware counters even if the above is true */
	if (riscv_pmu_sbi_available_boot())
		num_sbi_counters = rvpmu_sbi_find_num_ctrs();

	if (num_sbi_counters > RISCV_MAX_COUNTERS || num_deleg_counters > RISCV_MAX_COUNTERS)
		return -ENOSPC;
...
}

Also, could num_sbi_counters inadvertently capture negative error codes? 

rvpmu_sbi_find_num_ctrs() returns an int which can be a negative error 
code. Storing it in the u32 num_sbi_counters implicitly casts 
it to a very large positive value. 

This triggers the RISCV_MAX_COUNTERS check and causes the function to abort 
with -ENOSPC, losing the original error.

The caller rvpmu_device_probe() then fails to clean up the leaked globally 
scoped pmu_ctr_list pointer during its error handling:

drivers/perf/riscv_pmu_sbi.c:rvpmu_device_probe() {
...
	num_counters = rvpmu_find_ctrs();
	if (num_counters < 0) {
		pr_err("SBI PMU extension doesn't provide any counters\n");
		goto out_free;
	}
...
out_free:
	kfree(pmu);
	return ret;
}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=16

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 17/21] RISC-V: perf: Add Qemu virt machine events
  2026-06-09  6:01 ` [PATCH v6 17/21] RISC-V: perf: Add Qemu virt machine events Atish Patra
@ 2026-06-09  6:22   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:22 UTC (permalink / raw)
  To: Atish Patra; +Cc: linux-perf-users, conor+dt, robh, devicetree

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The patch incorrectly overloads the generic 'unimplemented' or 'non-commercial' CPU IDs (`mvendorid=0`, `marchid=0`, `mimpid=0`) to uniquely identify the QEMU virt machine, applying QEMU-specific PMU events globally to any minimal or academic RISC-V core.
--

commit 61b476b524390d76a4db3dcbab4b57114ff0ebc8
Author: Atish Patra <atishp@rivosinc.com>

RISC-V: perf: Add Qemu virt machine events

This patch adds a minimal set of legacy perf events for the QEMU virt
machine to the vendor table, allowing users to use them when counter
delegation is enabled.

> diff --git a/arch/riscv/include/asm/vendorid_list.h b/arch/riscv/include/asm/vendorid_list.h
> index 7f5030ee1fcf8..603aa2b21c0ba 100644
> --- a/arch/riscv/include/asm/vendorid_list.h
> +++ b/arch/riscv/include/asm/vendorid_list.h
> @@ -11,4 +11,8 @@
>  #define SIFIVE_VENDOR_ID	0x489
>  #define THEAD_VENDOR_ID		0x5b7
>  
> +#define QEMU_VIRT_VENDOR_ID		0x000
> +#define QEMU_VIRT_IMPL_ID		0x000
> +#define QEMU_VIRT_ARCH_ID		0x000
> +

[Severity: Medium]
Do these definitions overload the generic "unimplemented" or "non-commercial"
CPU IDs to uniquely identify the QEMU virt machine?

According to the RISC-V Privileged Architecture specification, a value of 0
in the mvendorid, marchid, and mimpid CSRs indicates that the ID is either
unimplemented or represents a non-commercial implementation.

> diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
> index 00b84b28117a8..74acac54328e0 100644
> --- a/drivers/perf/riscv_pmu_sbi.c
> +++ b/drivers/perf/riscv_pmu_sbi.c

[ ... ]

> @@ -453,7 +454,42 @@ struct riscv_vendor_pmu_events {
>  	  .hw_event_map = _hw_event_map, .cache_event_map = _cache_event_map, \
>  	  .attrs_events = _attrs },
>  

[ ... ]

>  static struct riscv_vendor_pmu_events pmu_vendor_events_table[] = {
> +	RISCV_VENDOR_PMU_EVENTS(QEMU_VIRT_VENDOR_ID, QEMU_VIRT_ARCH_ID, QEMU_VIRT_IMPL_ID,
> +				qemu_virt_hw_event_map, qemu_virt_cache_event_map,
> +				qemu_virt_event_group)
>  };

[Severity: Medium]
By binding the 0x000 ID tuple to QEMU's specific PMU event maps here, will
the kernel mistakenly assume any generic core returning 0 is a QEMU virt
machine?

If that non-QEMU core implements PMU counter delegation (Ssccfg/Smcdeleg) but
uses standard or different raw event encodings, this could unconditionally
program QEMU's arbitrary raw event values into the core's PMU configuration
registers, silently corrupting performance monitoring statistics.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=17

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 15/21] RISC-V: perf: Use config2/vendor table for event to counter mapping
  2026-06-09  6:01 ` [PATCH v6 15/21] RISC-V: perf: Use config2/vendor table for event to counter mapping Atish Patra
@ 2026-06-09  6:23   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:23 UTC (permalink / raw)
  To: Atish Patra; +Cc: devicetree, linux-perf-users, conor+dt, robh

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] KVM guest events for CPU cycles and instructions will fail to map to fixed hardware counters when counter delegation is enabled.
- [High] Fixed counter allocation unconditionally prioritizes the fixed counter index without checking its availability, breaking fallback to programmable counters.
- [High] Legacy events restricted to fixed counters via the vendor map fail to schedule.
- [High] `get_deleg_fixed_hw_idx()` assigns fixed counters without verifying if they are actually delegated by hardware (`cmask`), enabling unprivileged DoS.
- [Medium] Missing checks for zero before calling `__fls()` leads to undefined behavior.
--

commit 3c807d0dbddd7902bf02e21b17bb5bf036dd3178
Author: Atish Patra <atishp@rivosinc.com>

RISC-V: perf: Use config2/vendor table for event to counter mapping

The counter restriction specified in the json file is passed to
the drivers via config2 paarameter in perf attributes. This allows
any platform vendor to define their custom mapping between event and
hpmcounters without any rules defined in the ISA.

> diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
> index 5bfcd3821f572..4b4f151a0744d 100644
> --- a/drivers/perf/riscv_pmu_sbi.c
> +++ b/drivers/perf/riscv_pmu_sbi.c

[ ... ]

> @@ -1480,24 +1482,80 @@ static int rvpmu_deleg_find_ctrs(void)
>  	return num_hw_ctr;
>  }
>  
> +/*
> + * The json file must correctly specify counter 0 or counter 2 is available
> + * in the counter lists for cycle/instret events. Otherwise, the drivers have
> + * no way to figure out if a fixed counter must be used and pick a programmable
> + * counter if available.
> + */
>  static int get_deleg_fixed_hw_idx(struct cpu_hw_events *cpuc, struct perf_event *event)
>  {
> -	return -EINVAL;
> +	struct hw_perf_event *hwc = &event->hw;
> +	bool guest_events = event->attr.config1 & RISCV_PMU_CONFIG1_GUEST_EVENTS;
> +
> +	if (guest_events) {
> +		if (hwc->event_base == SBI_PMU_HW_CPU_CYCLES)
> +			return 0;

[Severity: High]
Does this check fail to map KVM guest events to fixed hardware counters
when counter delegation is enabled?

The check relies on hwc->event_base being populated with generic SBI
event IDs. However, under counter delegation, rvpmu_cdeleg_event_map()
sets econfig but returns 0 on success. This return value is assigned to
hwc->event_base, making it permanently 0.

Could this cause the comparison against SBI_PMU_HW_CPU_CYCLES (1) to
always fail and return -EINVAL?

> +		if (hwc->event_base == SBI_PMU_HW_INSTRUCTIONS)
> +			return 2;
> +		else
> +			return -EINVAL;
> +	}
> +
> +	if (!event->attr.config2)
> +		return -EINVAL;

[Severity: High]
Are we inadvertently rejecting fixed counters for legacy events here
unless userspace explicitly provides config2?

For standard legacy events (like perf stat -e cycles), config2 is 0, so
it falls through to get_deleg_next_hpm_hw_idx(). If a vendor restricted
an event to fixed counters via the vendor map, would returning -EINVAL
here cause standard perf commands to fail?

> +
> +	if (event->attr.config2 & RISCV_PMU_CYCLE_FIXED_CTR_MASK)
> +		return 0; /* CY counter */

[Severity: High]
Is it safe to assign fixed counters based directly on userspace config
without validating if they are actually delegated by hardware (cmask)?

If the platform did not delegate these counters, they remain
uninitialized in pmu_ctr_list. Unprivileged users could request these
counters and cause perf to read CSR 0, triggering
pr_err("Invalid performance counter csr...") on every PMU read.

Could this flood the kernel log and enable a Denial of Service?

[Severity: High]
This block unconditionally prioritizes the fixed counter index (0 or 2)
without checking if the counter is already in use.

If the fixed counter is busy, rvpmu_deleg_ctr_get_idx() attempts
test_and_set_bit() on it, fails, and jumps directly to out_err.

Could this break the fallback logic in get_deleg_next_hpm_hw_idx(),
which might have successfully allocated an available programmable
counter allowed by the config2 mask?

> +	else if (event->attr.config2 & RISCV_PMU_INSTRUCTION_FIXED_CTR_MASK)
> +		return 2; /* IR counter */
> +	else
> +		return -EINVAL;
>  }
>  
>  static int get_deleg_next_hpm_hw_idx(struct cpu_hw_events *cpuc, struct perf_event *event)
>  {
> -	unsigned long hw_ctr_mask = 0;
> +	u32 hw_ctr_mask = 0, temp_mask = 0;
> +	u32 type = event->attr.type;
> +	u64 config = event->attr.config;
> +	int ret;
>  
> -	/*
> -	 * TODO: Treat every hpmcounter can monitor every event for now.
> -	 * The event to counter mapping should come from the json file.
> -	 * The mapping should also tell if sampling is supported or not.
> -	 */
> +	/* Select only available hpmcounters */
> +	hw_ctr_mask = cmask & (~0x7) & ~(cpuc->used_hw_ctrs[0]);

[Severity: High]
By explicitly excluding fixed counters (~0x7) from the available mask
here, do we prevent legacy events from scheduling if they are restricted
to fixed counters via the vendor map?

If the event falls back from get_deleg_fixed_hw_idx() due to config2
being 0, and the vendor mapped the event exclusively to a fixed counter
(temp_mask = 1 or 4), hw_ctr_mask &= temp_mask will result in 0.

Would this cause the function to return -EINVAL and break standard perf
commands?

[ ... ]

> @@ -1526,10 +1584,6 @@ static int rvpmu_deleg_ctr_get_idx(struct perf_event *event)
>  	u64 priv_filter;
>  	int idx;
>  
> -	/*
> -	 * TODO: We should not rely on SBI Perf encoding to check if the event
> -	 * is a fixed one or not.
> -	 */
>  	if (!is_sampling_event(event)) {
>  		idx = get_deleg_fixed_hw_idx(cpuc, event);

[Severity: High]
Will KVM guest events that map to fixed hardware counters be skipped
here since they define a sample period?

Because is_sampling_event() evaluates to true for guest events, it
appears they will bypass this fixed counter assignment block entirely.

>  		if (idx == 0 || idx == 2) {

[ ... ]

> @@ -1547,10 +1601,14 @@ static int rvpmu_deleg_ctr_get_idx(struct perf_event *event)
>  		goto out_err;
>  found_idx:
>  	priv_filter = get_deleg_priv_filter_bits(event);
> +	if (test_and_set_bit(idx, cpuc->used_hw_ctrs))
> +		goto out_err;
>  	update_deleg_hpmevent(idx, hwc->config, priv_filter);
> +	return idx;
>  skip_update:
> -	if (!test_and_set_bit(idx, cpuc->used_hw_ctrs))
> -		return idx;
> +	if (test_and_set_bit(idx, cpuc->used_hw_ctrs))
> +		goto out_err;
> +	return idx;

[Severity: Medium]
Are there missing checks for zero before calling __fls() in the
surrounding code within rvpmu_deleg_ctr_get_idx() and
get_deleg_hw_ctr_width()?

The macro __fls() maps directly to __builtin_clzl(), which results in
undefined behavior if the input is 0.

Could this happen in rvpmu_deleg_ctr_get_idx() if no hardware counters
are delegated and cmask is 0, or in get_deleg_hw_ctr_width() if a
hypervisor configures the WARL CSR as hardwired to 0?

>  out_err:
>  	return -ENOENT;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=15

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 13/21] RISC-V: perf: Implement supervisor counter delegation support
  2026-06-09  6:01 ` [PATCH v6 13/21] RISC-V: perf: Implement supervisor counter delegation support Atish Patra
@ 2026-06-09  6:23   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:23 UTC (permalink / raw)
  To: Atish Patra; +Cc: devicetree, robh, conor+dt, linux-perf-users

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Bitwise shift of signed 32-bit integer causes overflow and corrupts SCOUNTINHIBIT on 64-bit systems.
- [High] Non-sampling events are permanently inhibited from counting because HPMEVENT_OF is unconditionally set but never cleared.
- [High] On 32-bit architectures, `rvpmu_deleg_ctr_enable_irq` writes modified event configuration to the wrong CSR (`CSR_SIREG4` instead of `CSR_SIREG5`).
- [High] SBI PMU snapshot logic breaks supervisor counter delegation by unconditionally bypassing S-mode CSR writes and reading stale shared memory.
--

commit ff1e171673cfe94a12c443765bd5b68dacf3b687
Author: Atish Patra <atishp@rivosinc.com>

RISC-V: perf: Implement supervisor counter delegation support

This patch adds support for RISC-V ISA extensions (ssccfg, sscsrind,
smcntrpmf) that allow programming hpmcounters directly from S-mode.
It falls back to the SBI PMU extension if the hardware extensions are
unavailable or for firmware counters.

> diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
> index 46a25979e95e9..1f16df9d0dd00 100644
> --- a/drivers/perf/riscv_pmu_sbi.c
> +++ b/drivers/perf/riscv_pmu_sbi.c

[ ... ]

> -static u64 rvpmu_sbi_ctr_read(struct perf_event *event)
> +static u64 rvpmu_ctr_read(struct perf_event *event)
>  {
>  	struct hw_perf_event *hwc = &event->hw;
>  	int idx = hwc->idx;

[Severity: High]
Does this function still read stale data from the SBI shared memory when
using supervisor counter delegation?

If a platform supports both SBI PMU snapshots and S-mode counter delegation,
the code unconditionally reads from the SBI shared memory for stopped
hardware counters. Since S-mode counters are stopped via direct CSR writes,
the SBI firmware is bypassed and the shared memory is never updated.

[ ... ]

> +static void rvpmu_deleg_ctr_enable_irq(struct perf_event *event)
> +{
> +	unsigned long hpmevent_curr;
> +	unsigned long of_mask;
> +	struct hw_perf_event *hwc = &event->hw;
> +	int counter_idx = hwc->idx;
> +	unsigned long sip_val = csr_read(CSR_SIP);
> +
> +	if (!is_sampling_event(event) || (sip_val & SIP_LCOFIP))
> +		return;

[Severity: High]
Will this early return cause non-sampling events to never be counted?

In update_deleg_hpmevent(), the HPMEVENT_OF bit is unconditionally set for
all events. The Sscofpmf specification states a counter does not increment
while its OF bit is 1. If we return early here for non-sampling events
(like perf stat), the OF bit is never cleared, which would leave these
counters permanently inhibited.

>  
> -	/* Reinitialize and start all the counter that overflowed */
> -	while (ctr_ovf_mask) {
> -		if (ctr_ovf_mask & 0x01) {
> -			event = cpu_hw_evt->events[idx];
> -			hwc = &event->hw;
> -			max_period = riscv_pmu_ctr_get_width_mask(event);
> -			init_val = local64_read(&hwc->prev_count) & max_period;
>  #if defined(CONFIG_32BIT)
> -			sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, idx, 1,
> -				  flag, init_val, init_val >> 32, 0);
> +	hpmevent_curr = csr_ind_read(CSR_SIREG5, SISELECT_SSCCFG_BASE, counter_idx);
> +	of_mask = (u32)~HPMEVENTH_OF;
>  #else
> -			sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, idx, 1,
> -				  flag, init_val, 0, 0);
> +	hpmevent_curr = csr_ind_read(CSR_SIREG2, SISELECT_SSCCFG_BASE, counter_idx);
> +	of_mask = ~HPMEVENT_OF;
>  #endif
> -			perf_event_update_userpage(event);
> -		}
> -		ctr_ovf_mask = ctr_ovf_mask >> 1;
> -		idx++;
> -	}
> +
> +	hpmevent_curr &= of_mask;
> +#if defined(CONFIG_32BIT)
> +	csr_ind_write(CSR_SIREG4, SISELECT_SSCCFG_BASE, counter_idx, hpmevent_curr);

[Severity: High]
Is this writing the event configuration to the wrong CSR?

The code reads the high 32 bits of the event configuration from CSR_SIREG5
and clears the HPMEVENTH_OF bit, but it writes the modified value back to
CSR_SIREG4. Since CSR_SIREG4 maps to hpmcounterh, this might overwrite the
running counter's upper 32 bits with the event configuration value instead
of clearing the OF bit in the hpmevent register.

> +#else
> +	csr_ind_write(CSR_SIREG2, SISELECT_SSCCFG_BASE, counter_idx, hpmevent_curr);
> +#endif
> +}
> +
> +static void rvpmu_deleg_ctr_start(struct perf_event *event, u64 ival)
> +{
> +	unsigned long scountinhibit_val = 0;
> +	struct hw_perf_event *hwc = &event->hw;
> +
> +#if defined(CONFIG_32BIT)
> +	csr_ind_write(CSR_SIREG, SISELECT_SSCCFG_BASE, hwc->idx, ival & 0xFFFFFFFF);
> +	csr_ind_write(CSR_SIREG4, SISELECT_SSCCFG_BASE, hwc->idx, ival >> BITS_PER_LONG);
> +#else
> +	csr_ind_write(CSR_SIREG, SISELECT_SSCCFG_BASE, hwc->idx, ival);
> +#endif
> +
> +	rvpmu_deleg_ctr_enable_irq(event);
> +
> +	scountinhibit_val = csr_read(CSR_SCOUNTINHIBIT);
> +	scountinhibit_val &= ~(1 << hwc->idx);

[Severity: High]
Could this bitwise shift result in sign-extension and corrupt SCOUNTINHIBIT
on 64-bit systems?

Because 1 is a signed 32-bit integer, (1 << 31) results in -2147483648.
When this is negated by a bitwise NOT or cast to an unsigned long on a
64-bit architecture, it gets sign-extended (e.g., 0x000000007FFFFFFF).
This could erroneously clear bits 32-63 of the SCOUNTINHIBIT CSR.

Should this use 1UL << hwc->idx or the BIT() macro instead?

[ ... ]

> -static void rvpmu_sbi_start_overflow_mask(struct riscv_pmu *pmu,
> -					  u64 ctr_ovf_mask)
> +/*
> + * This function starts all the used counters in two step approach.
> + * Any counter that did not overflow can be start in a single step
> + * while the overflowed counters need to be started with updated initialization
> + * value.
> + */
> +static void rvpmu_start_overflow_mask(struct riscv_pmu *pmu, u64 ctr_ovf_mask)
>  {
> +	int idx = 0, i;
> +	struct perf_event *event;
> +	unsigned long ctr_start_mask = 0;
> +	u64 max_period, init_val = 0;
> +	struct hw_perf_event *hwc;
>  	struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
>  
>  	if (sbi_pmu_snapshot_available())
> -		rvpmu_sbi_start_ovf_ctrs_snapshot(cpu_hw_evt, ctr_ovf_mask);
> -	else
> -		rvpmu_sbi_start_ovf_ctrs_sbi(cpu_hw_evt, ctr_ovf_mask);
> +		return rvpmu_sbi_start_ovf_ctrs_snapshot(cpu_hw_evt, ctr_ovf_mask);

[Severity: High]
Does this check bypass S-mode delegation when both snapshot and delegation
are available?

If a platform supports both, this unconditionally calls
rvpmu_sbi_start_ovf_ctrs_snapshot(), which starts S-mode delegated hardware
counters using SBI ECALLs rather than direct S-mode CSR writes.

[ ... ]

> +static void update_deleg_hpmevent(int counter_idx, uint64_t event_value, uint64_t filter_bits)
> +{
> +	u64 hpmevent_value = 0;
> +
> +	/* OF bit should be enable during the start if sampling is requested */
> +	hpmevent_value = (event_value & ~HPMEVENT_MASK) | filter_bits | HPMEVENT_OF;

[Severity: High]
Are we unconditionally setting the HPMEVENT_OF bit here?

Although the comment states it should be enabled if sampling is requested,
the bit is added for all events. Combined with the early return in
rvpmu_deleg_ctr_enable_irq(), this causes non-sampling events to remain
inhibited permanently.

[ ... ]

>  static void rvpmu_ctr_stop(struct perf_event *event, unsigned long flag)
>  {
> -	rvpmu_sbi_ctr_stop(event, flag);
> -	/* TODO: Counter delegation implementation */
> +	struct hw_perf_event *hwc = &event->hw;
> +
> +	if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
> +	    (hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
> +		rvpmu_reset_scounteren((void *)event);
> +
> +	if (riscv_pmu_cdeleg_available() && !pmu_sbi_is_fw_event(event)) {
> +		/*
> +		 * The counter is already stopped. No need to stop again. Counter
> +		 * mapping will be reset in clear_idx function.
> +		 */
> +		if (flag != RISCV_PMU_STOP_FLAG_RESET)
> +			rvpmu_deleg_ctr_stop_mask((1 << hwc->idx));

[Severity: High]
Can this 32-bit shift overflow and corrupt the upper 32 bits of SCOUNTINHIBIT?

Passing (1 << 31) will sign-extend to 0xFFFFFFFF80000000 on 64-bit systems.
When passed to rvpmu_deleg_ctr_stop_mask, it ORs this mask into SCOUNTINHIBIT,
which erroneously sets bits 32-63 to 1.

Consider using 1UL << hwc->idx or the BIT() macro to prevent overflow.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=13

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v6 14/21] RISC-V: perf: Skip PMU SBI extension when not implemented
  2026-06-09  6:01 ` [PATCH v6 14/21] RISC-V: perf: Skip PMU SBI extension when not implemented Atish Patra
@ 2026-06-09  6:33   ` sashiko-bot
  0 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-09  6:33 UTC (permalink / raw)
  To: Atish Patra; +Cc: devicetree, linux-perf-users, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Missing perf_pmu_unregister() on error paths leads to Use-After-Free of the pmu struct.
- [High] Unconditional cpuhp_state_remove_instance() on an uninitialized hlist_node causes a kernel panic.
--

commit bd51c5655be0902a2fc1fd90b8d692c299e7265b
Author: Charlie Jenkins <charlie@rivosinc.com>

RISC-V: perf: Skip PMU SBI extension when not implemented

When the PMU SBI extension is not implemented, sbi_v2_available should
not be set to true. The SBI implementation for counter config matching
and firmware counter read  should also be skipped when the SBI extension
is not implemented.

> diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
> index 1f16df9d0dd00..5bfcd3821f572 100644
> --- a/drivers/perf/riscv_pmu_sbi.c
> +++ b/drivers/perf/riscv_pmu_sbi.c
[ ... ]
> @@ -2045,12 +2052,13 @@ static int __init rvpmu_devinit(void)
>  	int ret;
>  	struct platform_device *pdev;
>  
> -	if (sbi_spec_version >= sbi_mk_version(0, 3) &&
> -	    sbi_probe_extension(SBI_EXT_PMU))
> -		static_branch_enable(&riscv_pmu_sbi_available);
> +	if (sbi_probe_extension(SBI_EXT_PMU)) {
> +		if (sbi_spec_version >= sbi_mk_version(0, 3))
> +			static_branch_enable(&riscv_pmu_sbi_available);
> +		if (sbi_spec_version >= sbi_mk_version(2, 0))
> +			sbi_v2_available = true;
> +	}
>  
> -	if (sbi_spec_version >= sbi_mk_version(2, 0))
> -		sbi_v2_available = true;
>  	/*
>  	 * We need all three extensions to be present to access the counters
>  	 * in S-mode via Supervisor Counter delegation.

[Severity: Critical]
This is a pre-existing issue, but I noticed a potential use-after-free in the
error handling path of rvpmu_device_probe() when allocating or registering
fails after perf_pmu_register() succeeds.

If an error occurs, such as pmu_sbi_snapshot_alloc() failing, the code jumps
to out_unregister:

out_unregister:
	riscv_pmu_destroy(pmu);

out_free:
	kfree(pmu);
	return ret;

However, neither the error path nor riscv_pmu_destroy() calls
perf_pmu_unregister(&pmu->pmu).

This leaves the PMU registered in the perf core subsystem while its backing
memory is freed via kfree(). Any subsequent access to the PMU could result
in a use-after-free.

Should perf_pmu_unregister() be called during teardown to avoid memory
corruption?

[Severity: High]
This is a pre-existing issue, but the error teardown path in
rvpmu_device_probe() seems to have a bug that could trigger a kernel panic.

If rvpmu_device_probe() fails before successfully calling
cpuhp_state_add_instance(), it jumps to out_unregister and calls
riscv_pmu_destroy(pmu).

Inside riscv_pmu_destroy(), it unconditionally removes the hotplug instance:

static void riscv_pmu_destroy(struct riscv_pmu *pmu)
{
	...
	cpuhp_state_remove_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
}

Because the pmu struct is allocated with kzalloc() and the node hasn't been
added to the hotplug list yet, its hlist_node is zero-initialized.

When hlist_del() is called internally by cpuhp_state_remove_instance(), it
attempts to dereference node->pprev (which is NULL), causing a NULL pointer
dereference.

Would it be better to check if the node is actually hashed before attempting
to remove the hotplug instance?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com?part=14

^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, other threads:[~2026-06-09  6:33 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-09  6:01 [PATCH v6 00/21] Add Counter delegation ISA extension support Atish Patra
2026-06-09  6:01 ` [PATCH v6 01/21] RISC-V: Add Sxcsrind ISA extension CSR definitions Atish Patra
2026-06-09  6:01 ` [PATCH v6 02/21] RISC-V: Add Sxcsrind ISA extension definition and parsing Atish Patra
2026-06-09  6:01 ` [PATCH v6 03/21] dt-bindings: riscv: add Sxcsrind ISA extension description Atish Patra
2026-06-09  6:09   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 04/21] RISC-V: Define indirect CSR access helpers Atish Patra
2026-06-09  6:15   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 05/21] RISC-V: Add Smcntrpmf extension parsing Atish Patra
2026-06-09  6:01 ` [PATCH v6 06/21] dt-bindings: riscv: add Smcntrpmf ISA extension description Atish Patra
2026-06-09  6:09   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 07/21] RISC-V: Add Sscfg extension CSR definition Atish Patra
2026-06-09  6:01 ` [PATCH v6 08/21] RISC-V: Add Ssccfg/Smcdeleg ISA extension definition and parsing Atish Patra
2026-06-09  6:14   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 09/21] dt-bindings: riscv: add Counter delegation ISA extensions description Atish Patra
2026-06-09  6:12   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 10/21] RISC-V: perf: Restructure the SBI PMU code Atish Patra
2026-06-09  6:18   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 11/21] RISC-V: perf: Modify the counter discovery mechanism Atish Patra
2026-06-09  6:17   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 12/21] RISC-V: perf: Add a mechanism to defined legacy event encoding Atish Patra
2026-06-09  6:16   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 13/21] RISC-V: perf: Implement supervisor counter delegation support Atish Patra
2026-06-09  6:23   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 14/21] RISC-V: perf: Skip PMU SBI extension when not implemented Atish Patra
2026-06-09  6:33   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 15/21] RISC-V: perf: Use config2/vendor table for event to counter mapping Atish Patra
2026-06-09  6:23   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 16/21] RISC-V: perf: Add legacy event encodings via sysfs Atish Patra
2026-06-09  6:21   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 17/21] RISC-V: perf: Add Qemu virt machine events Atish Patra
2026-06-09  6:22   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 18/21] tools/perf: Support event code for arch standard events Atish Patra
2026-06-09  6:18   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 19/21] tools/perf: Add RISC-V CounterIDMask event field Atish Patra
2026-06-09  6:01 ` [PATCH v6 20/21] TEST(do-not-upstream): fake qemu-virt PMU events for cdeleg counter-mask testing Atish Patra
2026-06-09  6:17   ` sashiko-bot
2026-06-09  6:01 ` [PATCH v6 21/21] TEST(do-not-upstream): fake qemu vendor JSON + mapfile entry for CounterIDMask path Atish Patra
2026-06-09  6:20   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox