From nobody Wed Sep 30 15:29:06 2026 Received: from mail-pf1-f172.google.com (mail-pf1-f172.google.com [209.85.210.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4A4E718DB35 for ; Fri, 7 Aug 2026 03:51:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786074683; cv=none; b=sAOGbJA6wHulAxWqVKH7XXZwF1ZAnfhCyxO0QtfHFfBUfctaQnolZqN4We+OS75vbJryFHv4MbZk7ZfE0yf65N81RvWSFNCmr9eUE1lK1kU8wsVIkMt/Yq9g36WuUDfjZIli8479RPC25+jPv7lwxJmndBEMa1I0MKLR0Z1+lJg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786074683; c=relaxed/simple; bh=H76/QGVHjgoBCmbEg8m4levfeDQTMHbDutu6CiCCVkw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=cq71dQyd2sDY4XcqxdWMHX9uYW+Z2eSTv89WUYb5DoJSrhJeAUbY/3s6/hg7HvxaAdLWozSXMGw8kCsHfjNkSaXH1rtXvnfMVKE1gzXpXEcihOwDyoIU95G9cnxz/TRllyvglbRnR62T7EMM2qOKlr19VS+/jo4DK3BuMzyHZHE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=oh+8ldbV; arc=none smtp.client-ip=209.85.210.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="oh+8ldbV" Received: by mail-pf1-f172.google.com with SMTP id d2e1a72fcca58-84874b52eabso3984386b3a.0 for ; Thu, 06 Aug 2026 20:51:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786074681; x=1786679481; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=c/3ISunVfIRnbCDBwOOCuYiYx2dMKx7N5JMFDQezhpM=; b=oh+8ldbV083d7AEX3zfOhsYngBBZU7j/RJWGwggfOo8CsDjDdp+p2qvj70zZ4scQDw EHe4R+XCphRhbfq/3u25ucFW0noHnG1z4QkJBtMOeakrCzOSx0Wf5OoiExbeKke1fEYZ 9P41BprmVku1xFX0viJUaj8KIaBdFJBCFgn+2SZ9E0CaOX5aix8DNN3yxOM8pqFyPz7p 69XdZrHOeOXsKBEMm8jOJeSon6bVAkqvpUuQiBkuUGrdV4YPHgGw5affQZ9X/CuFnfi9 KyIodE4EW0vN9iaY46uIofYb+Mofu3gcs5xoOoqQFOeWBrLnN8qn4kJyvQmOnqJ9vJJG w4Sw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786074681; x=1786679481; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=c/3ISunVfIRnbCDBwOOCuYiYx2dMKx7N5JMFDQezhpM=; b=sn4CZr5wxxxEUx6W6lKglBCLbKC8x1xxl/XaAaj1nOKlcKKrxwpB3TMlYMuYZmKE04 xlLn/VrC5VaFh99U4gPFQLIfhZY9mffT5XvvnSuyDFu8ZNtOKr5PayzkEH2ZAtRVKBMz uUCasWNKSQqfI3Vuxy5CtQw+vEfqp1ZZYzKs+Sy26B5oiiqXDaCwCI0ElR8+RH4drCuS 3Wi+SjyP3PJR+3gcxh3XIekUjCOkWAxncjb5GYWvNfY2gsO5G0OdKm5Tivpz6YTxM3Xl jLvMVulca+7x2yBMSf+j3ndY510N50sFocqFaJAtaU/m0dMWwQtFueQvhH56hfpeL0+c qpUg== X-Forwarded-Encrypted: i=1; AHgh+RrtvP+gSBzBm6eNx5BIu6Q1aamMbbuIuQK9ykzvVZV4FyXSH9zL5qpfS2EIpuLiQWm8dY7O59mBkAJOceU=@vger.kernel.org X-Gm-Message-State: AOJu0Yzd8BatbtRNpDZ2dkTP8Vo9FU6Heg3nYG4DuOon4wC+S5FDTqU5 3r3uNI7muwHgyDqvaF7YVLFTNga/0mbdKgqRJASJSIO5yfwjs+vY07Bk X-Gm-Gg: AR+sD11i44VwEMA85TC0rxzaMas8pTKkoIqlv68ySK1xfgioiOXtWeyYruka74XJJMa gyiYTo/x5q9dehw2wOXcdTG3PR5SsnDxdXvNNrnkuoJeDBOkJ5KREwYDdo3nUGWu4pXU5FuJ1k8 +XOLLwCzZrlxtvkRlm/x2MinlsqGIDIhgoBrUh85Iqg3ozdHVHCGavkf8hpSyVZc+cO+B9AC/dA uJUUiQKJl5ZWV2dV5e25f3egB80MISzrvtADjsTsMck3Oazmh0YLCT+ULQ7nL4saSupnKpj45Ab TE7hAI3byZKUFcMWEg/qLdJQG/GFPNZbeNLNzO8uSoJRxcsT7LWxsB86SW+2Q+MX8st7y4WwaVZ jNy2V9wl7ggItlIgX19gsJI8ag41D+wttvBwg/MuvPVXLCKZZGXFU9JCYE8JWplQHtRZwW3xfD0 SuZpaJGBNjd+1L7ybyBTQIBPvRceaqO3dFtcJAb6nvl6b9+7VnQJwjzBHxW30CdoYKKHGTomqQC Ps/Upk= X-Received: by 2002:a05:6a00:170b:b0:82f:50cd:e586 with SMTP id d2e1a72fcca58-84f5df8e917mr782211b3a.13.1786074681506; Thu, 06 Aug 2026 20:51:21 -0700 (PDT) Received: from cyh-System-Product-Name.. ([129.227.183.200]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84f5a3d0d96sm287586b3a.18.2026.08.06.20.51.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2026 20:51:21 -0700 (PDT) From: "Yuhang.chen" To: anup@brainfault.org Cc: atish.patra@linux.dev, palmer@dabbelt.com, pjw@kernel.org, aou@eecs.berkeley.edu, alex@ghiti.fr, pbonzini@redhat.com, shuah@kernel.org, kvm@vger.kernel.org, kvm-riscv@lists.infradead.org, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, zhouquan@iscas.ac.cn, "Yuhang.chen" Subject: [PATCH 1/2] RISC-V: KVM: Add PMU event filter support Date: Fri, 7 Aug 2026 11:50:38 +0800 Message-Id: <20260807035039.294998-2-yhchen312@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260807035039.294998-1-yhchen312@gmail.com> References: <20260807035039.294998-1-yhchen312@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 =3D=3D 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(). Advertise the feature with KVM_CAP_PMU_EVENT_FILTER. Assisted-by: YuanSheng:deepseek-v4-pro Co-developed-by: Quan Zhou Signed-off-by: Quan Zhou Signed-off-by: Yuhang.chen --- arch/riscv/include/asm/kvm_host.h | 3 ++ arch/riscv/include/uapi/asm/kvm.h | 20 ++++++++++ arch/riscv/kvm/vcpu_pmu.c | 28 +++++++++++++ arch/riscv/kvm/vm.c | 66 ++++++++++++++++++++++++++++++- 4 files changed, 116 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 { =20 /* KVM_CAP_RISCV_MP_STATE_RESET */ bool mp_state_reset; + + /* KVM_SET_PMU_EVENT_FILTER */ + struct kvm_pmu_event_filter __rcu *pmu_event_filter; }; =20 struct kvm_cpu_trap { diff --git a/arch/riscv/include/uapi/asm/kvm.h b/arch/riscv/include/uapi/as= m/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__ =20 #include +#include #include #include =20 @@ -396,6 +397,25 @@ struct kvm_riscv_sbi_fwft { /* One single KVM irqchip, ie. the AIA */ #define KVM_NR_IRQCHIPS 1 =20 +/* 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 =20 #endif /* __LINUX_KVM_RISCV_H */ diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c index bb46dcbfb24d..bc7d297a63ce 100644 --- a/arch/riscv/kvm/vcpu_pmu.c +++ b/arch/riscv/kvm/vcpu_pmu.c @@ -733,6 +733,29 @@ int kvm_riscv_vcpu_pmu_ctr_stop(struct kvm_vcpu *vcpu,= unsigned long ctr_base, return 0; } =20 +static bool kvm_riscv_pmu_event_allowed(struct kvm *kvm, unsigned long eid= x) +{ + struct kvm_pmu_event_filter *filter; + bool in_list =3D false; + unsigned int i; + + filter =3D srcu_dereference(kvm->arch.pmu_event_filter, &kvm->srcu); + if (!filter) + return true; + + for (i =3D 0; i < filter->nevents; i++) { + if ((unsigned long)filter->events[i] =3D=3D eidx) { + in_list =3D true; + break; + } + } + + /* ALLOW: permit only listed events; DENY: reject them. */ + if (filter->action =3D=3D 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 +796,11 @@ int kvm_riscv_vcpu_pmu_ctr_cfg_match(struct kvm_vcpu *= vcpu, unsigned long ctr_ba goto out; } =20 + if (!kvm_riscv_pmu_event_allowed(vcpu->kvm, eidx)) { + sbiret =3D 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..6f822c43f156 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); =20 + kfree(srcu_dereference_check(kvm->arch.pmu_event_filter, &kvm->srcu, 1)); + kvm_riscv_aia_destroy_vm(kvm); } =20 @@ -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 =3D 1; break; case KVM_CAP_NR_VCPUS: @@ -265,7 +268,68 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kv= m_enable_cap *cap) } } =20 +#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 =3D argp; + struct kvm_pmu_event_filter *filter, tmp; + size_t size; + int r =3D 0; + + if (copy_from_user(&tmp, user_filter, sizeof(tmp))) + return -EFAULT; + + if (tmp.action !=3D KVM_PMU_EVENT_ALLOW && + tmp.action !=3D KVM_PMU_EVENT_DENY) + return -EINVAL; + + if (tmp.flags) + return -EINVAL; + + if (tmp.nevents > KVM_PMU_EVENT_FILTER_MAX_EVENTS) + return -E2BIG; + + size =3D struct_size(filter, events, tmp.nevents); + filter =3D kzalloc(size, GFP_KERNEL_ACCOUNT); + if (!filter) + return -ENOMEM; + + filter->action =3D tmp.action; + filter->nevents =3D tmp.nevents; + filter->flags =3D tmp.flags; + + if (copy_from_user(filter->events, user_filter->events, + flex_array_size(filter, events, filter->nevents))) { + r =3D -EFAULT; + goto cleanup; + } + + mutex_lock(&kvm->lock); + filter =3D 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 =3D filp->private_data; + void __user *argp =3D (void __user *)arg; + int r; + + switch (ioctl) { + case KVM_SET_PMU_EVENT_FILTER: + r =3D kvm_riscv_vm_ioctl_set_pmu_event_filter(kvm, argp); + break; + default: + r =3D -EINVAL; + } + + return r; } --=20 2.34.1 From nobody Wed Sep 30 15:29:06 2026 Received: from mail-pf1-f169.google.com (mail-pf1-f169.google.com [209.85.210.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DCDC37082D for ; Fri, 7 Aug 2026 03:51:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786074690; cv=none; b=kZQLqNLK5cpxQv+jQZx7uuf+xnRyuaU484G+sej4q8YabZi/uBBviksosZxJ63rJeP4hTSgCOgRN7DBn41kgcxWTzY+0l6G35gKskIbufl3F1Jzmg0lC2q1BjnZoYH8ivaASmmH6vMdJPNO2wCJjEwI7SXgTYOEHtrgE/QEoSco= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786074690; c=relaxed/simple; bh=DsE3iVom0V6Bg34N2TQD9xICdojiH9gD7eZ+FHQsqVI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ryAnQnv6/lveZrZUrdntZWXdfNZZo1mGY5/aUATSJUrW6d37crXI2EBbgz4b8P24aYJ2wRIT1QsaTfMJbnTUiSLINWLeBt9nN+0vSlhDYDYZm8WWgtypyfYm5P3ZCGQ4dviYl6pXpcUbKscpZVI55m3A/vXCnG+xW8mg0yAWyoU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=d/gqUODd; arc=none smtp.client-ip=209.85.210.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="d/gqUODd" Received: by mail-pf1-f169.google.com with SMTP id d2e1a72fcca58-84864086bfeso2450399b3a.1 for ; Thu, 06 Aug 2026 20:51:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786074688; x=1786679488; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=roc/IS/aTyNJNRiP/nrbSlyNrCVYc83nltBjgP79s1s=; b=d/gqUODd9JuqNLzNNtueXFzHV2UdoLKI6RmLPYbdYbHtRwq2amisvWzvC0bBcFQ6T/ srFnhWhn0ny0vNY/Jn6uB40lwbbFDpiVAqhx9g+1oEXOeAo+/F/fJj9tK0W1joIiZ7f+ x0/IbF4W62vrYiTaz2TzNkzfB7EKN2MgjvYDnfNdEUmjW8Rv7TI1eZqw13wrISBjsBMF QkH4bw48cSo74fADgoz55ZbuV/LFHQTxv4C45qVjm5Eg6MGYotEv65Xmmy4GDH0JGEKs JXFboOCeiC0VS/mwFXmCHAZyetcV/dV+cxMufo1aPRnKlSTLN3905yEm+XlwA0f9ZQ+6 oOIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786074688; x=1786679488; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=roc/IS/aTyNJNRiP/nrbSlyNrCVYc83nltBjgP79s1s=; b=HXBGg6nXyd3UuL4y0Ccj4BR9dnHl4RbpLhMTIlSdCU2xWXLq3hHmHUIJsJGd3IyIDK S2K294w8dC9LK9pHP706d/K4BrEAWfHmky/3wnQ5RbrAzgtLI8vnRoQxCEG/SbFhTmxX 3tIQixnsVQfxswiQXuJymcHB0ctcc3+4RLH75sHim9zYgyN8c4Y/rqNaoI5tHu9yc3ja hNaalo1DaJVxaCIYLGLh6fcskuUsKi+zW+jOEN7bW2yOs2D3CkgqwTqRVwJ1yeUf09sW Z7lIjrB9tgl33rNDAIQG2+b2aAHBj/NOQ/Wu6dMwDXvk/ieyfE0JE6U2K//Rp9VLFofR V47A== X-Forwarded-Encrypted: i=1; AHgh+RruRYxcgjju3QJMYZtPkft0zK1NO2Tv9MBm3BAU4/PmKEnE64z6wn/KPI+4/mYTE88rGxV9nu+mnSP4f7s=@vger.kernel.org X-Gm-Message-State: AOJu0Yy7m459RaTiturL3vWKZmexynOPUDVo9EgvmfMIfdT0NPH+BK7M 9aldvT1k3i7IgRn7s4+8/dDcuSo3xcjKCZYqRR0pDKnCstdhnvAkO2h8 X-Gm-Gg: AR+sD10lKkYRwu7USSNziSYsjIJlKTAAFjMYZHkwAHW+l2OL6njsKeVsSJigbvOymP1 PnwCJKOYIAoA3wK+rxsllQrqZgTw/sOvxSNW5HVZNxKu/mhvKwNdFp9oRVGUyefbNsgpHo7Wme3 5LVVRJ0eI3YZ5ex5rtKrOoVyiouQsnYnKEbktWM+OAd2Mm8vM8ecciDzj+90zHijVhfXe2Oo+26 YI/k8OSL4bYoBHIjgmVtthLDI3JAvmLYdFJmFMkiQ+3ZeOwRSqSx85kUHjePMF+g6JWYYfd3Y6V /5H3pIwqhwJ2KsKwX0O1P+Dc9HgZrU/WLxx/qGNfm+5XmWPQcN1sWNU1QRciH6WQTqsuKhQJRto 5ou6P/PJ7w4miCOQ1a5DRmPpr/28zaoU0Rf0doRLEkZBzdvaGBh074i9ufodqFIXP7SIpXTUo61 tCZa0GuR0wQIS+TLWlPWgl7wdgkEWcOaHdqFVi52px3w1QIDx22lDCZyNqMSPsf8ZaFBA2zo6Bx DkokMg= X-Received: by 2002:a05:6a00:4504:b0:845:da74:5d7c with SMTP id d2e1a72fcca58-84f2e0dba2cmr23794104b3a.32.1786074688151; Thu, 06 Aug 2026 20:51:28 -0700 (PDT) Received: from cyh-System-Product-Name.. ([129.227.183.200]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84f5a3d0d96sm287586b3a.18.2026.08.06.20.51.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2026 20:51:27 -0700 (PDT) From: "Yuhang.chen" To: anup@brainfault.org Cc: atish.patra@linux.dev, palmer@dabbelt.com, pjw@kernel.org, aou@eecs.berkeley.edu, alex@ghiti.fr, pbonzini@redhat.com, shuah@kernel.org, kvm@vger.kernel.org, kvm-riscv@lists.infradead.org, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, zhouquan@iscas.ac.cn, "Yuhang.chen" Subject: [PATCH 2/2] RISC-V: KVM: selftests: Add PMU event filter test Date: Fri, 7 Aug 2026 11:50:39 +0800 Message-Id: <20260807035039.294998-3-yhchen312@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260807035039.294998-1-yhchen312@gmail.com> References: <20260807035039.294998-1-yhchen312@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 Signed-off-by: Quan Zhou Signed-off-by: Yuhang.chen --- 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/selft= ests/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 +=3D pre_fault_memory_test TEST_GEN_PROGS_riscv =3D $(TEST_GEN_PROGS_COMMON) TEST_GEN_PROGS_riscv +=3D riscv/sbi_pmu_test TEST_GEN_PROGS_riscv +=3D riscv/ebreak_test +TEST_GEN_PROGS_riscv +=3D riscv/pmu_event_filter_test TEST_GEN_PROGS_riscv +=3D access_tracking_perf_test TEST_GEN_PROGS_riscv +=3D arch_timer TEST_GEN_PROGS_riscv +=3D coalesced_io_test diff --git a/tools/testing/selftests/kvm/riscv/pmu_event_filter_test.c b/to= ols/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 va= ries + * the filter *list* contents to exercise membership without depending on = host + * support for other events. Counter management and SBI error reporting h= appen + * in the guest; the host installs filters and checks the reported errors. + */ +#include +#include +#include + +#include "kvm_util.h" +#include "test_util.h" +#include "processor.h" +#include "ucall_common.h" +#include "sbi.h" + +/* SBI PMU hardware event indexes (type =3D=3D HW =3D=3D 0, so eidx =3D=3D= 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=3D0, cmask=3D1) for the + * cycle event. The host installs (or clears) the filter + * before each entry, so the result reflects the active policy. + */ + ret =3D sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, + 0, 1, 0, EV_CYCLES, 0, 0); + err =3D ret.error; + ctr =3D 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 =3D sizeof(*f) + (size_t)nevents * sizeof(__u64); + + f =3D calloc(1, size); + TEST_ASSERT(f, "calloc(pmu_event_filter)"); + f->action =3D action; + f->nevents =3D nevents; + f->flags =3D 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 =3D build_filter(action, 0, events, nevent= s); + + 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 =3D build_filter(action, flags, events, ne= vents); + int ret =3D __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 =3D run_one(vcpu); + TEST_ASSERT_EQ(err, expect); + pr_info("%s: err=3D%ld (expected %ld)\n", desc, err, expect); +} + +static void test_bad_args(struct kvm_vm *vm) +{ + __u64 ev =3D EV_CYCLES; + int ret; + + /* Invalid action. */ + errno =3D 0; + ret =3D try_set_filter(vm, 2, 0, &ev, 1); + TEST_ASSERT(ret < 0 && errno =3D=3D EINVAL, + "invalid action should fail with EINVAL, got ret=3D%d errno=3D%d", + ret, errno); + + /* Non-zero flags are not supported. */ + errno =3D 0; + ret =3D try_set_filter(vm, KVM_PMU_EVENT_ALLOW, 1, &ev, 1); + TEST_ASSERT(ret < 0 && errno =3D=3D EINVAL, + "non-zero flags should fail with EINVAL, got ret=3D%d errno=3D%d", + ret, errno); + + /* Too many events. */ + errno =3D 0; + ret =3D try_set_filter(vm, KVM_PMU_EVENT_DENY, 0, NULL, MAX_EVENTS + 1); + TEST_ASSERT(ret < 0 && errno =3D=3D E2BIG, + "nevents > max should fail with E2BIG, got ret=3D%d errno=3D%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 =3D 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 =3D run_one(vcpu); + if (err) { + pr_info("PMU unavailable (baseline cfg_match err=3D%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; +} --=20 2.34.1