* [PATCH v2 0/2] RISC-V: KVM: Add PMU event filter support
@ 2026-08-07 5:32 Yuhang.chen
2026-08-07 5:32 ` [PATCH v2 1/2] " Yuhang.chen
2026-08-07 5:32 ` [PATCH v2 2/2] RISC-V: KVM: selftests: Add PMU event filter test Yuhang.chen
0 siblings, 2 replies; 5+ messages in thread
From: Yuhang.chen @ 2026-08-07 5:32 UTC (permalink / raw)
To: anup
Cc: atish.patra, palmer, pjw, aou, alex, pbonzini, shuah, kvm,
kvm-riscv, linux-riscv, linux-kernel, linux-kselftest, zhouquan,
Yuhang.chen
This series adds PMU event filter support to RISC-V KVM, allowing a
userspace VMM to restrict which SBI PMU events a guest is permitted to
program.
The KVM_SET_PMU_EVENT_FILTER ioctl and KVM_CAP_PMU_EVENT_FILTER already
exist as generic KVM uAPI (include/uapi/linux/kvm.h); RISC-V KVM does
not yet implement them. Wiring them up lets a VMM limit a guest's PMU
event access, which is useful for sandboxing and for withholding host
PMU events the host does not intend to expose.
Patch 1 implements the kernel side: the uapi struct, the ioctl handler
with an SRCU-protected filter, enforcement in
kvm_riscv_vcpu_pmu_ctr_cfg_match() (a disallowed event fails with
SBI_ERR_NOT_SUPPORTED), and advertisement of KVM_CAP_PMU_EVENT_FILTER.
Like the other arch-specific KVM uAPI structs, struct
kvm_pmu_event_filter is defined per-architecture; its layout reflects
RISC-V PMU events and the ioctl number encodes the struct size.
Patch 2 adds a selftest that covers the ALLOW/DENY semantics with the
event in and out of the list, filter clearing, and ioctl argument
rejection (EINVAL/E2BIG).
Testing: built and run under QEMU TCG with nested KVM (an L1 KVM host
running an L2 guest). The selftest (patch 2) passes and dmesg stays
clean throughout the run.
Based on v7.2-rc2 (a635d6748234).
Changes in v2:
- Mask reserved high bits of the SBI PMU event index before comparing
against the filter, so a guest cannot bypass a DENY filter by setting
bits above bit 19. Noted by sashiko-bot review.
- Reject non-zero pad in the ioctl argument to keep the field usable
for future extensions.
Assisted-by: YuanSheng:deepseek-v4-pro
Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
Signed-off-by: Yuhang.chen <yhchen312@gmail.com>
Yuhang.chen (2):
RISC-V: KVM: Add PMU event filter support
RISC-V: KVM: selftests: Add PMU event filter test
arch/riscv/include/asm/kvm_host.h | 3 +
arch/riscv/include/uapi/asm/kvm.h | 20 ++
arch/riscv/kvm/vcpu_pmu.c | 31 +++
arch/riscv/kvm/vm.c | 69 +++++-
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../kvm/riscv/pmu_event_filter_test.c | 199 ++++++++++++++++++
6 files changed, 322 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/kvm/riscv/pmu_event_filter_test.c
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] RISC-V: KVM: Add PMU event filter support
2026-08-07 5:32 [PATCH v2 0/2] RISC-V: KVM: Add PMU event filter support Yuhang.chen
@ 2026-08-07 5:32 ` Yuhang.chen
2026-08-07 5:55 ` sashiko-bot
2026-08-07 5:32 ` [PATCH v2 2/2] RISC-V: KVM: selftests: Add PMU event filter test Yuhang.chen
1 sibling, 1 reply; 5+ messages in thread
From: Yuhang.chen @ 2026-08-07 5:32 UTC (permalink / raw)
To: anup
Cc: atish.patra, palmer, pjw, aou, alex, pbonzini, shuah, kvm,
kvm-riscv, linux-riscv, linux-kernel, linux-kselftest, zhouquan,
Yuhang.chen
Allow userspace to restrict which SBI PMU events a guest is permitted
to program via the new VM ioctl KVM_SET_PMU_EVENT_FILTER. It takes a
struct kvm_pmu_event_filter whose events[] array holds SBI PMU event
indices encoded as (type << 16) | code. The action field selects ALLOW
(only listed events may be programmed) or DENY (listed events are
rejected); nevents == 0 clears any active filter.
The filter is enforced in kvm_riscv_vcpu_pmu_ctr_cfg_match(), where a
disallowed event fails configuration with SBI_ERR_NOT_SUPPORTED. It
governs new counter configuration only and is not retroactive. The
filter lives in struct kvm_arch, read via SRCU on the vCPU run path and
replaced under kvm->lock with synchronize_srcu_expedited(). Event
indices are masked to their valid bits before comparison, so reserved
high bits cannot bypass a DENY filter.
Advertise the feature with KVM_CAP_PMU_EVENT_FILTER.
Assisted-by: YuanSheng:deepseek-v4-pro
Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
Signed-off-by: Yuhang.chen <yhchen312@gmail.com>
---
arch/riscv/include/asm/kvm_host.h | 3 ++
arch/riscv/include/uapi/asm/kvm.h | 20 +++++++++
arch/riscv/kvm/vcpu_pmu.c | 31 ++++++++++++++
arch/riscv/kvm/vm.c | 69 ++++++++++++++++++++++++++++++-
4 files changed, 122 insertions(+), 1 deletion(-)
diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h
index 60017ceec9d2..1cd3d6a11057 100644
--- a/arch/riscv/include/asm/kvm_host.h
+++ b/arch/riscv/include/asm/kvm_host.h
@@ -95,6 +95,9 @@ struct kvm_arch {
/* KVM_CAP_RISCV_MP_STATE_RESET */
bool mp_state_reset;
+
+ /* KVM_SET_PMU_EVENT_FILTER */
+ struct kvm_pmu_event_filter __rcu *pmu_event_filter;
};
struct kvm_cpu_trap {
diff --git a/arch/riscv/include/uapi/asm/kvm.h b/arch/riscv/include/uapi/asm/kvm.h
index 504e73305343..da4f639fa89f 100644
--- a/arch/riscv/include/uapi/asm/kvm.h
+++ b/arch/riscv/include/uapi/asm/kvm.h
@@ -12,6 +12,7 @@
#ifndef __ASSEMBLER__
#include <linux/types.h>
+#include <linux/stddef.h>
#include <asm/bitsperlong.h>
#include <asm/ptrace.h>
@@ -396,6 +397,25 @@ struct kvm_riscv_sbi_fwft {
/* One single KVM irqchip, ie. the AIA */
#define KVM_NR_IRQCHIPS 1
+/* for KVM_CAP_PMU_EVENT_FILTER */
+#define KVM_PMU_EVENT_ALLOW 0
+#define KVM_PMU_EVENT_DENY 1
+
+/*
+ * For KVM_SET_PMU_EVENT_FILTER: restrict which SBI PMU events a guest may
+ * configure. Each @events entry is a SBI PMU event index (type in bits
+ * 19:16, code in bits 15:0). %KVM_PMU_EVENT_ALLOW permits only listed
+ * events; %KVM_PMU_EVENT_DENY rejects them. Enforced at counter
+ * configuration (SBI PMU COUNTER_CFG_MATCH), not retroactively.
+ */
+struct kvm_pmu_event_filter {
+ __u32 action;
+ __u32 nevents;
+ __u32 flags;
+ __u32 pad;
+ __DECLARE_FLEX_ARRAY(__u64, events);
+};
+
#endif
#endif /* __LINUX_KVM_RISCV_H */
diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c
index bb46dcbfb24d..756040913468 100644
--- a/arch/riscv/kvm/vcpu_pmu.c
+++ b/arch/riscv/kvm/vcpu_pmu.c
@@ -733,6 +733,32 @@ int kvm_riscv_vcpu_pmu_ctr_stop(struct kvm_vcpu *vcpu, unsigned long ctr_base,
return 0;
}
+static bool kvm_riscv_pmu_event_allowed(struct kvm *kvm, unsigned long eidx)
+{
+ struct kvm_pmu_event_filter *filter;
+ bool in_list = false;
+ unsigned int i;
+
+ /* Reserved high bits must not bypass the filter. */
+ eidx &= SBI_PMU_EVENT_IDX_MASK;
+
+ filter = srcu_dereference(kvm->arch.pmu_event_filter, &kvm->srcu);
+ if (!filter)
+ return true;
+
+ for (i = 0; i < filter->nevents; i++) {
+ if ((unsigned long)filter->events[i] == eidx) {
+ in_list = true;
+ break;
+ }
+ }
+
+ /* ALLOW: permit only listed events; DENY: reject them. */
+ if (filter->action == KVM_PMU_EVENT_ALLOW)
+ return in_list;
+ return !in_list;
+}
+
int kvm_riscv_vcpu_pmu_ctr_cfg_match(struct kvm_vcpu *vcpu, unsigned long ctr_base,
unsigned long ctr_mask, unsigned long flags,
unsigned long eidx, u64 evtdata,
@@ -773,6 +799,11 @@ int kvm_riscv_vcpu_pmu_ctr_cfg_match(struct kvm_vcpu *vcpu, unsigned long ctr_ba
goto out;
}
+ if (!kvm_riscv_pmu_event_allowed(vcpu->kvm, eidx)) {
+ sbiret = SBI_ERR_NOT_SUPPORTED;
+ goto out;
+ }
+
/*
* SKIP_MATCH flag indicates the caller is aware of the assigned counter
* for this event. Just do a sanity check if it already marked used.
diff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c
index a9f083feeb76..a5a3a2182d1e 100644
--- a/arch/riscv/kvm/vm.c
+++ b/arch/riscv/kvm/vm.c
@@ -53,6 +53,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
{
kvm_destroy_vcpus(kvm);
+ kfree(srcu_dereference_check(kvm->arch.pmu_event_filter, &kvm->srcu, 1));
+
kvm_riscv_aia_destroy_vm(kvm);
}
@@ -187,6 +189,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_MP_STATE:
case KVM_CAP_IMMEDIATE_EXIT:
case KVM_CAP_SET_GUEST_DEBUG:
+ case KVM_CAP_PMU_EVENT_FILTER:
r = 1;
break;
case KVM_CAP_NR_VCPUS:
@@ -265,7 +268,71 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap)
}
}
+#define KVM_PMU_EVENT_FILTER_MAX_EVENTS 256
+
+static int kvm_riscv_vm_ioctl_set_pmu_event_filter(struct kvm *kvm,
+ void __user *argp)
+{
+ struct kvm_pmu_event_filter __user *user_filter = argp;
+ struct kvm_pmu_event_filter *filter, tmp;
+ size_t size;
+ int r = 0;
+
+ if (copy_from_user(&tmp, user_filter, sizeof(tmp)))
+ return -EFAULT;
+
+ if (tmp.action != KVM_PMU_EVENT_ALLOW &&
+ tmp.action != KVM_PMU_EVENT_DENY)
+ return -EINVAL;
+
+ if (tmp.flags)
+ return -EINVAL;
+
+ if (tmp.pad)
+ return -EINVAL;
+
+ if (tmp.nevents > KVM_PMU_EVENT_FILTER_MAX_EVENTS)
+ return -E2BIG;
+
+ size = struct_size(filter, events, tmp.nevents);
+ filter = kzalloc(size, GFP_KERNEL_ACCOUNT);
+ if (!filter)
+ return -ENOMEM;
+
+ filter->action = tmp.action;
+ filter->nevents = tmp.nevents;
+ filter->flags = tmp.flags;
+
+ if (copy_from_user(filter->events, user_filter->events,
+ flex_array_size(filter, events, filter->nevents))) {
+ r = -EFAULT;
+ goto cleanup;
+ }
+
+ mutex_lock(&kvm->lock);
+ filter = rcu_replace_pointer(kvm->arch.pmu_event_filter, filter,
+ mutex_is_locked(&kvm->lock));
+ mutex_unlock(&kvm->lock);
+ synchronize_srcu_expedited(&kvm->srcu);
+
+cleanup:
+ kfree(filter);
+ return r;
+}
+
int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
{
- return -EINVAL;
+ struct kvm *kvm = filp->private_data;
+ void __user *argp = (void __user *)arg;
+ int r;
+
+ switch (ioctl) {
+ case KVM_SET_PMU_EVENT_FILTER:
+ r = kvm_riscv_vm_ioctl_set_pmu_event_filter(kvm, argp);
+ break;
+ default:
+ r = -EINVAL;
+ }
+
+ return r;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] RISC-V: KVM: selftests: Add PMU event filter test
2026-08-07 5:32 [PATCH v2 0/2] RISC-V: KVM: Add PMU event filter support Yuhang.chen
2026-08-07 5:32 ` [PATCH v2 1/2] " Yuhang.chen
@ 2026-08-07 5:32 ` Yuhang.chen
2026-08-07 5:40 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: Yuhang.chen @ 2026-08-07 5:32 UTC (permalink / raw)
To: anup
Cc: atish.patra, palmer, pjw, aou, alex, pbonzini, shuah, kvm,
kvm-riscv, linux-riscv, linux-kernel, linux-kselftest, zhouquan,
Yuhang.chen
Add a selftest that exercises KVM_SET_PMU_EVENT_FILTER on RISC-V. The
guest programs the CPU cycles and instructions SBI PMU events through
SBI_EXT_PMU_COUNTER_CFG_MATCH while the host installs filters with the
ALLOW and DENY actions, asserting that disallowed events return
SBI_ERR_NOT_SUPPORTED and allowed events succeed.
The test also validates ioctl argument rejection: an invalid action, a
non-zero flags field, and an over-large nevents value are each expected
to fail with -EINVAL or -E2BIG. A baseline run verifies PMU
availability and the test skips (KSFT_SKIP) when PMU or the filter
capability is absent.
Assisted-by: YuanSheng:deepseek-v4-pro
Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
Signed-off-by: Yuhang.chen <yhchen312@gmail.com>
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../kvm/riscv/pmu_event_filter_test.c | 199 ++++++++++++++++++
2 files changed, 200 insertions(+)
create mode 100644 tools/testing/selftests/kvm/riscv/pmu_event_filter_test.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index d28a057fa6c2..5d2dc3b25eac 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -218,6 +218,7 @@ TEST_GEN_PROGS_s390 += pre_fault_memory_test
TEST_GEN_PROGS_riscv = $(TEST_GEN_PROGS_COMMON)
TEST_GEN_PROGS_riscv += riscv/sbi_pmu_test
TEST_GEN_PROGS_riscv += riscv/ebreak_test
+TEST_GEN_PROGS_riscv += riscv/pmu_event_filter_test
TEST_GEN_PROGS_riscv += access_tracking_perf_test
TEST_GEN_PROGS_riscv += arch_timer
TEST_GEN_PROGS_riscv += coalesced_io_test
diff --git a/tools/testing/selftests/kvm/riscv/pmu_event_filter_test.c b/tools/testing/selftests/kvm/riscv/pmu_event_filter_test.c
new file mode 100644
index 000000000000..1a76fce2aca6
--- /dev/null
+++ b/tools/testing/selftests/kvm/riscv/pmu_event_filter_test.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test for RISC-V KVM_SET_PMU_EVENT_FILTER.
+ *
+ * Verify that a VM-scoped PMU event filter installed via the
+ * KVM_SET_PMU_EVENT_FILTER ioctl is enforced when a guest configures a
+ * counter through the SBI PMU COUNTER_CFG_MATCH call:
+ *
+ * - with no filter, events are programmable (baseline / PMU probe);
+ * - KVM_PMU_EVENT_DENY rejects the listed events;
+ * - KVM_PMU_EVENT_ALLOW admits only the listed events;
+ * - replacing the filter with an empty DENY list re-enables everything.
+ *
+ * The filter is checked before any perf event is created, so the test only
+ * ever programs the cycle event (always supported by the host PMU) and varies
+ * the filter *list* contents to exercise membership without depending on host
+ * support for other events. Counter management and SBI error reporting happen
+ * in the guest; the host installs filters and checks the reported errors.
+ */
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "kvm_util.h"
+#include "test_util.h"
+#include "processor.h"
+#include "ucall_common.h"
+#include "sbi.h"
+
+/* SBI PMU hardware event indexes (type == HW == 0, so eidx == code). */
+#define EV_CYCLES SBI_PMU_HW_CPU_CYCLES /* 1 */
+#define EV_INSTR SBI_PMU_HW_INSTRUCTIONS /* 2 */
+
+/* Must match KVM_PMU_EVENT_FILTER_MAX_EVENTS in arch/riscv/kvm/vm.c. */
+#define MAX_EVENTS 256
+
+static void guest_code(void)
+{
+ struct sbiret ret;
+ unsigned long ctr;
+ long err;
+
+ for (;;) {
+ /*
+ * Request the fixed cycle counter (cbase=0, cmask=1) for the
+ * cycle event. The host installs (or clears) the filter
+ * before each entry, so the result reflects the active policy.
+ */
+ ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH,
+ 0, 1, 0, EV_CYCLES, 0, 0);
+ err = ret.error;
+ ctr = ret.value;
+
+ /* Release the counter on success so the next iteration reuses it. */
+ if (!err)
+ sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP,
+ ctr, 1, SBI_PMU_STOP_FLAG_RESET, 0, 0, 0);
+
+ GUEST_SYNC1(err);
+ }
+}
+
+static struct kvm_pmu_event_filter *
+build_filter(__u32 action, __u32 flags, const __u64 *events, __u32 nevents)
+{
+ struct kvm_pmu_event_filter *f;
+ size_t size = sizeof(*f) + (size_t)nevents * sizeof(__u64);
+
+ f = calloc(1, size);
+ TEST_ASSERT(f, "calloc(pmu_event_filter)");
+ f->action = action;
+ f->nevents = nevents;
+ f->flags = flags;
+ if (nevents && events)
+ memcpy(f->events, events, nevents * sizeof(__u64));
+ return f;
+}
+
+/* Install a filter, asserting success. */
+static void set_filter(struct kvm_vm *vm, __u32 action,
+ const __u64 *events, __u32 nevents)
+{
+ struct kvm_pmu_event_filter *f = build_filter(action, 0, events, nevents);
+
+ vm_ioctl(vm, KVM_SET_PMU_EVENT_FILTER, f);
+ free(f);
+}
+
+/* Install a filter and return the raw ioctl result (for negative tests). */
+static int try_set_filter(struct kvm_vm *vm, __u32 action, __u32 flags,
+ const __u64 *events, __u32 nevents)
+{
+ struct kvm_pmu_event_filter *f = build_filter(action, flags, events, nevents);
+ int ret = __vm_ioctl(vm, KVM_SET_PMU_EVENT_FILTER, f);
+
+ free(f);
+ return ret;
+}
+
+/* Run the guest one step and return the cfg_match error code it reports. */
+static long run_one(struct kvm_vcpu *vcpu)
+{
+ struct ucall uc;
+
+ vcpu_run(vcpu);
+ TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC);
+ return (long)uc.args[0];
+}
+
+static void test_filter_case(struct kvm_vm *vm, struct kvm_vcpu *vcpu,
+ __u32 action, const __u64 *events, __u32 nevents,
+ long expect, const char *desc)
+{
+ long err;
+
+ set_filter(vm, action, events, nevents);
+ err = run_one(vcpu);
+ TEST_ASSERT_EQ(err, expect);
+ pr_info("%s: err=%ld (expected %ld)\n", desc, err, expect);
+}
+
+static void test_bad_args(struct kvm_vm *vm)
+{
+ __u64 ev = EV_CYCLES;
+ int ret;
+
+ /* Invalid action. */
+ errno = 0;
+ ret = try_set_filter(vm, 2, 0, &ev, 1);
+ TEST_ASSERT(ret < 0 && errno == EINVAL,
+ "invalid action should fail with EINVAL, got ret=%d errno=%d",
+ ret, errno);
+
+ /* Non-zero flags are not supported. */
+ errno = 0;
+ ret = try_set_filter(vm, KVM_PMU_EVENT_ALLOW, 1, &ev, 1);
+ TEST_ASSERT(ret < 0 && errno == EINVAL,
+ "non-zero flags should fail with EINVAL, got ret=%d errno=%d",
+ ret, errno);
+
+ /* Too many events. */
+ errno = 0;
+ ret = try_set_filter(vm, KVM_PMU_EVENT_DENY, 0, NULL, MAX_EVENTS + 1);
+ TEST_ASSERT(ret < 0 && errno == E2BIG,
+ "nevents > max should fail with E2BIG, got ret=%d errno=%d",
+ ret, errno);
+}
+
+int main(void)
+{
+ struct kvm_vm *vm;
+ struct kvm_vcpu *vcpu;
+ long err;
+
+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_PMU_EVENT_FILTER));
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+
+ /*
+ * Baseline / PMU probe: with no filter the cycle event must be
+ * programmable. If it isn't, the host PMU is unusable in this
+ * environment (e.g. Sscofpmf unavailable under TCG); skip the rest.
+ */
+ err = run_one(vcpu);
+ if (err) {
+ pr_info("PMU unavailable (baseline cfg_match err=%ld), skipping\n",
+ err);
+ kvm_vm_free(vm);
+ exit(KSFT_SKIP);
+ }
+
+ /* DENY{cycles}: the cycle event is rejected. */
+ test_filter_case(vm, vcpu, KVM_PMU_EVENT_DENY,
+ &(__u64){ EV_CYCLES }, 1,
+ SBI_ERR_NOT_SUPPORTED, "deny cycles");
+
+ /* ALLOW{cycles}: the cycle event is admitted. */
+ test_filter_case(vm, vcpu, KVM_PMU_EVENT_ALLOW,
+ &(__u64){ EV_CYCLES }, 1,
+ 0, "allow cycles");
+
+ /*
+ * ALLOW{instructions}: cycles is not in the allow list, so it is
+ * rejected. Instructions itself is never programmed, so host support
+ * for it is irrelevant.
+ */
+ test_filter_case(vm, vcpu, KVM_PMU_EVENT_ALLOW,
+ &(__u64){ EV_INSTR }, 1,
+ SBI_ERR_NOT_SUPPORTED, "cycles not in allow{instr}");
+
+ /* Empty DENY list: nothing is denied, cycles is programmable again. */
+ test_filter_case(vm, vcpu, KVM_PMU_EVENT_DENY, NULL, 0,
+ 0, "clear (deny empty)");
+
+ test_bad_args(vm);
+
+ kvm_vm_free(vm);
+ return 0;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/2] RISC-V: KVM: selftests: Add PMU event filter test
2026-08-07 5:32 ` [PATCH v2 2/2] RISC-V: KVM: selftests: Add PMU event filter test Yuhang.chen
@ 2026-08-07 5:40 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-08-07 5:40 UTC (permalink / raw)
To: Yuhang.chen; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The commit message falsely claims that the guest programs the 'instructions' SBI PMU event, but the implementation only ever programs the 'cycles' event.
--
--- Patch [2]: [PATCH v2 2/2] RISC-V: KVM: selftests: Add PMU event filter test ---
commit 828aa8f61ee14a213c9f6491df6c9b1b9ddbd337
Author: Yuhang.chen <yhchen312@gmail.com>
RISC-V: KVM: selftests: Add PMU event filter test
This patch adds a selftest to verify KVM_SET_PMU_EVENT_FILTER on RISC-V.
The host applies ALLOW and DENY filters, and asserts that the guest receives
the expected success or failure when configuring PMU counters.
> Add a selftest that exercises KVM_SET_PMU_EVENT_FILTER on RISC-V. The
> guest programs the CPU cycles and instructions SBI PMU events through
> SBI_EXT_PMU_COUNTER_CFG_MATCH while the host installs filters with the
> ALLOW and DENY actions, asserting that disallowed events return
> SBI_ERR_NOT_SUPPORTED and allowed events succeed.
[Severity: Medium]
Is this description accurate?
The commit message states that the guest programs both the CPU cycles and
instructions SBI PMU events.
However, looking at guest_code(), it appears to only ever program EV_CYCLES
via the SBI_EXT_PMU_COUNTER_CFG_MATCH call:
guest_code() {
...
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH,
0, 1, 0, EV_CYCLES, 0, 0);
...
}
An inline comment in the test code also explicitly notes that the
instructions event is never programmed by the guest: "Instructions itself is
never programmed, so host support for it is irrelevant."
Could the commit message be updated to reflect that only the CPU cycles
event is actually programmed by the guest code?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260807053227.341700-1-yhchen312@gmail.com?part=2
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] RISC-V: KVM: Add PMU event filter support
2026-08-07 5:32 ` [PATCH v2 1/2] " Yuhang.chen
@ 2026-08-07 5:55 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-08-07 5:55 UTC (permalink / raw)
To: Yuhang.chen; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The implementation fails to clear the active filter when `nevents == 0` as documented in the commit message.
--
--- Patch [1]: [PATCH v2 1/2] RISC-V: KVM: Add PMU event filter support ---
commit f5fab5adc86a94658a09438bfb27fb33aa49c8df
Author: Yuhang.chen <yhchen312@gmail.com>
RISC-V: KVM: Add PMU event filter support
Allow userspace to restrict which SBI PMU events a guest is permitted
to program via the new VM ioctl KVM_SET_PMU_EVENT_FILTER.
> diff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c
> index a9f083feeb767..a5a3a2182d1e4 100644
> --- a/arch/riscv/kvm/vm.c
> +++ b/arch/riscv/kvm/vm.c
[ ... ]
> @@ -265,7 +268,71 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap)
[ ... ]
> + if (tmp.nevents > KVM_PMU_EVENT_FILTER_MAX_EVENTS)
> + return -E2BIG;
> +
> + size = struct_size(filter, events, tmp.nevents);
> + filter = kzalloc(size, GFP_KERNEL_ACCOUNT);
> + if (!filter)
> + return -ENOMEM;
[Severity: Medium]
The commit message states that "nevents == 0 clears any active filter".
However, this code unconditionally allocates and installs a new filter even
when nevents == 0, instead of skipping the allocation and setting the pointer
to NULL.
If userspace passes a zeroed structure to clear the filter (meaning nevents
is 0 and action defaults to 0, which is KVM_PMU_EVENT_ALLOW), won't this
install a 0-event ALLOW filter?
> diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c
> index 2025b664961c8..cf360f8496e5e 100644
> --- a/arch/riscv/kvm/vcpu_pmu.c
> +++ b/arch/riscv/kvm/vcpu_pmu.c
[ ... ]
> @@ -733,6 +733,32 @@ int kvm_riscv_vcpu_pmu_ctr_stop(struct kvm_vcpu *vcpu, unsigned long ctr_base,
[ ... ]
> + for (i = 0; i < filter->nevents; i++) {
> + if ((unsigned long)filter->events[i] == eidx) {
> + in_list = true;
> + break;
> + }
> + }
> +
> + /* ALLOW: permit only listed events; DENY: reject them. */
> + if (filter->action == KVM_PMU_EVENT_ALLOW)
> + return in_list;
> + return !in_list;
> +}
[Severity: Medium]
If a 0-event ALLOW filter was installed because nevents was 0, in_list will
be false here.
Does this result in kvm_riscv_pmu_event_allowed() returning false and
rejecting all guest PMU events, breaking PMU functionality rather than
restoring the default allow-all behavior?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260807053227.341700-1-yhchen312@gmail.com?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-07 5:55 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-07 5:32 [PATCH v2 0/2] RISC-V: KVM: Add PMU event filter support Yuhang.chen
2026-08-07 5:32 ` [PATCH v2 1/2] " Yuhang.chen
2026-08-07 5:55 ` sashiko-bot
2026-08-07 5:32 ` [PATCH v2 2/2] RISC-V: KVM: selftests: Add PMU event filter test Yuhang.chen
2026-08-07 5:40 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox