[PATCH] drivers/perf: riscv_pmu_sbi: add perf_user_access sysctl

From: Heiko Stuebner
Date: Fri Aug 26 2022 - 11:22:52 EST


Add a sysctl similar to the one on arm64 to enable/disable
access to counter CSRs from u-mode on RISC-V.

The default is of course set to disabled keeping the current
state of access - to only the TIME CSR.

Signed-off-by: Heiko Stuebner <heiko@xxxxxxxxx>
---
Documentation/admin-guide/sysctl/kernel.rst | 6 +--
drivers/perf/riscv_pmu_sbi.c | 43 ++++++++++++++++++++-
2 files changed, 44 insertions(+), 5 deletions(-)

diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst
index ee6572b1edad..efd4bc385e7a 100644
--- a/Documentation/admin-guide/sysctl/kernel.rst
+++ b/Documentation/admin-guide/sysctl/kernel.rst
@@ -894,15 +894,15 @@ enabled, otherwise writing to this file will return ``-EBUSY``.
The default value is 8.


-perf_user_access (arm64 only)
-=================================
+perf_user_access (arm64 and riscv only)
+=======================================

Controls user space access for reading perf event counters. When set to 1,
user space can read performance monitor counter registers directly.

The default value is 0 (access disabled).

-See Documentation/arm64/perf.rst for more information.
+See Documentation/arm64/perf.rst for more information on arm64


pid_max
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 6f6681bbfd36..7aab8d673357 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -41,6 +41,8 @@ static const struct attribute_group *riscv_pmu_attr_groups[] = {
NULL,
};

+static int sysctl_perf_user_access __read_mostly;
+
/*
* RISC-V doesn't have hetergenous harts yet. This need to be part of
* per_cpu in case of harts with different pmu counters
@@ -640,13 +642,22 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)
return IRQ_HANDLED;
}

+/*
+ * Depending on the perf_user_access setting, enable the access
+ * from usermode either for all counters or for TIME csr only.
+ */
+static void riscv_pmu_update_user_access(void *info)
+{
+ csr_write(CSR_SCOUNTEREN, sysctl_perf_user_access ? GENMASK(31, 0) :
+ 0x2);
+}
+
static int pmu_sbi_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);

- /* Enable the access for TIME csr only from the user mode now */
- csr_write(CSR_SCOUNTEREN, 0x2);
+ riscv_pmu_update_user_access(NULL);

/* Stop all the counters so that they can be enabled from perf */
pmu_sbi_stop_all(pmu);
@@ -785,6 +796,32 @@ static void riscv_pmu_destroy(struct riscv_pmu *pmu)
cpuhp_state_remove_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
}

+static int riscv_pmu_proc_user_access_handler(struct ctl_table *table,
+ int write, void *buffer, size_t *lenp, loff_t *ppos)
+{
+ int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+
+ if (ret || !write)
+ return ret;
+
+ on_each_cpu(riscv_pmu_update_user_access, NULL, 1);
+
+ return 0;
+}
+
+static struct ctl_table sbi_pmu_sysctl_table[] = {
+ {
+ .procname = "perf_user_access",
+ .data = &sysctl_perf_user_access,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = riscv_pmu_proc_user_access_handler,
+ .extra1 = SYSCTL_ZERO,
+ .extra2 = SYSCTL_ONE,
+ },
+ { }
+};
+
static int pmu_sbi_device_probe(struct platform_device *pdev)
{
struct riscv_pmu *pmu = NULL;
@@ -834,6 +871,8 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
if (ret)
goto out_unregister;

+ register_sysctl("kernel", sbi_pmu_sysctl_table);
+
return 0;

out_unregister:
--
2.35.1