From nobody Wed Sep 30 16:19:05 2026 Received: from mail-pf1-f175.google.com (mail-pf1-f175.google.com [209.85.210.175]) (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 4E9D4431498 for ; Fri, 7 Aug 2026 05:32:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786080776; cv=none; b=VYWiexEE0EaX2iE7kCE6ghZcbo0QBniyGPmRO82b53uPzxEEvrmseHaIt+7wB2MqFjUc1gAMptKUrht0ELHZZrPJAJCen+U5upXqvwTNyYXtHbrh5UbK/Ix7tu5P2bY+8wW5Jy4MLS39avYX1CH28+42zYLl5XO+8Qvx1a3QIP8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786080776; c=relaxed/simple; bh=WTgtCBbzxa7tjxkXGh9oWCGFMnuvuWmw8cQGqIT0GMo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=t+dmTva+bgk0wkGfUg+5NMJgmg/XgOKd1UI3+oUCPUXQTPh6FmmSFoGPvHprnIDD7hHBLFVWdo5CO/S3N8Az6SJ6/kcmfzX1kj+hspqEcrtz5X2G7k+9699VQnvN+oxUxLkcc3WS7UGT/70oNEFz06Y6qIazRKTcP1XxBUdp1rI= 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=BkyiP8qg; arc=none smtp.client-ip=209.85.210.175 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="BkyiP8qg" Received: by mail-pf1-f175.google.com with SMTP id d2e1a72fcca58-8487214ad2bso4623700b3a.1 for ; Thu, 06 Aug 2026 22:32:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786080771; x=1786685571; 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=t4URKGYUdKxYgKTjeOoH/uROuHrVlAZjzUU1uImLcd0=; b=BkyiP8qgzX2hZ1bdcP8/rBBKbsm/d1g06XJ9YtwYa8OQI2eQYhdKf9/OvpGFjwAnrs DSMsrQx0eV5SX5OarlaMZrPmk6lojknU4Ffz5w9lIa1cC3XxDOZBIbs0nwPNZkblHjUX +3SeCATsHpJgePDnDvCb3Mg25Z/Re6nCw26usCccoXzmwsQSD8zrmkOQzWPJXk1ld7jm ROhUkzMI06Wo3q02LEd/DFLN0e1AZvKxfjJSHeWEIACET3hJGkb/8HFdwnBpFXPQirGz aLir5jO5id7EdbuJMyK8b4F8AlVuyEEklHV+yVLHEMdMYojJnFeSiE+FB1Gx7v1AbWMk VN1A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786080771; x=1786685571; 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=t4URKGYUdKxYgKTjeOoH/uROuHrVlAZjzUU1uImLcd0=; b=O+cWMqcO+RfPkAQaD5YYHJ4UVTtOOJYBYQsrYobAPIQJVKIQ4+zMnQqdS6B69BCoVs 2IJg4R/C/jc7pi8XDttTi+JlV+3ztqwHL1RdIEyNbAdnBcYlsKzkXgt8Bjsw+G+IYiC3 4FGsaYKSGPdC7FKw3v05Y4NoHmJIm+2bmc0yXRmxvoBfYHCExi5WWuxmbj136vytvFNl zZE66RKKe0FN1pXbh2MKESo9cfbSk7LBGnA70DGdoj01MXUVAS8LQ2/Uh8Zk9bMwHyMW AsCDC2TJQ+W8v9VuuocZylPi4sENl+BZANk8JKs9d9fPN8fVnSQ2cHtPpfxCuVpo1fhz cuLQ== X-Forwarded-Encrypted: i=1; AHgh+RpMCkaNI2R58sSu8f2X/W8xzXR92afvb6juf/yetfP8P25AgtcM1+h+cipoSONtkMx2nPYdikMFmLgBU+0=@vger.kernel.org X-Gm-Message-State: AOJu0YyvzsHEbM0oCgpMzXQzgpFeEovIssFSwSnIw4Eis1AvlCTXzOsR VEF7JdyuvwR3Z7hCKqdwvQqWl0nHze5whUb/McHiViwxmWPBuVNBkbMn X-Gm-Gg: AR+sD124qFNBvH0JVL8E82oA5pPhtjJVYAWRkuBM/57Nft+u4ZARtMRSUUKSfkkkuZF OAcsDisovBUrjRwza/4Vo0/BYCa1C1oRrV6F52juAlXL1zwltglWiMAvN5eAxFl9Q8HwlPLg04U dtY5UyCzogbXg2noe+dn3IpWG9M/uR3wwlj2c+k1VQIS753auV9LwzTV4HXdu3IWEK8Uf39KgNx /cgjOOI6+ovJpIqyHUbrTFL+0UcKicw1lux0O2U5BcwtRwrXV/rHurwwRciDBHuDxzNVYU3fJhz HfI8CinQ2RbTHeMo5BVCbk5pHD0YK+q+cK5tQYPZEIcVqTY4n9S4FGJ/yb5Xrxg8EnuEyJsPYFv 21wVlaJfnsXuECsAZ+uSVzK3AxYqX2GfXkJKlfRpfkjJjS/UIl0bRpA7nK7pYGu+vzwOazCEoBP lDnsaJSr9LJcCPegG7fvYAfR8JTlfZg0sqh3Bhm2ljn0fJdi1B36uVRurezSUfITpaVXirIgA30 +xLRwM= X-Received: by 2002:a05:6a00:3021:b0:848:8715:c2d9 with SMTP id d2e1a72fcca58-84f5df36a04mr1309064b3a.2.1786080771542; Thu, 06 Aug 2026 22:32:51 -0700 (PDT) Received: from cyh-System-Product-Name.. ([129.227.183.200]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-cbe8f19634dsm108940a12.7.2026.08.06.22.32.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2026 22:32:51 -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 v2 1/2] RISC-V: KVM: Add PMU event filter support Date: Fri, 7 Aug 2026 13:32:26 +0800 Message-Id: <20260807053227.341700-2-yhchen312@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260807053227.341700-1-yhchen312@gmail.com> References: <20260807053227.341700-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(). 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 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 | 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 { =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..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; } =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; + + /* Reserved high bits must not bypass the filter. */ + eidx &=3D SBI_PMU_EVENT_IDX_MASK; + + 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 +799,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..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); =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,71 @@ 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.pad) + 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 16:19:05 2026 Received: from mail-pf1-f173.google.com (mail-pf1-f173.google.com [209.85.210.173]) (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 E712642C501 for ; Fri, 7 Aug 2026 05:32:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786080781; cv=none; b=kVDAJ/0uKlNojj+q3oI+Gxz0MmhmrOXV4pTsjwG6aQYHefG+2ePWBZYCSsE5aQ6yP8huVCiSx1jrXpoFY688NShcR0iZt5n2kr8/MDQ9BMx4+0prut92+7sr53XpKMKBhMMvXkYJ1NLmeQI7axh6tNZwz0/H8aSREvhAiVfmOpA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786080781; c=relaxed/simple; bh=DsE3iVom0V6Bg34N2TQD9xICdojiH9gD7eZ+FHQsqVI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=VBPH0iN8vgHrXG/sEEhVeeEjreJP9D4ObKU2bedoZ1ri2v4Dnrnyac5dXIbhjdx57Heb5hbs13qBu/esvDrUquHtdUsQLBzu77/cpdnelis65JUpuDo5RcA/2wxz6if3wd/4pFCIk6QIUzVaB+9Tbc/8tLGNcNfva/3jysPznvk= 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=oclkTniV; arc=none smtp.client-ip=209.85.210.173 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="oclkTniV" Received: by mail-pf1-f173.google.com with SMTP id d2e1a72fcca58-8453427d3f4so3502938b3a.3 for ; Thu, 06 Aug 2026 22:32:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786080777; x=1786685577; 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=oclkTniVehEibzwkhAwUT1Lh5T03Uv50svuTx3YWSbWYQ3LkacJh6U844T2FRNCJ1x ykAHXxR8mzf1njf1cB4HUG51jmL/bX8WJYt9Ml1k2HLhKj3PQbg03GpRyHl1NnjbN6Xk +GX/6ij+k/6SbEaZp02ms//PR4tIxvyvNI4HSdnPWXGKttprnuNbsqYxpKL7xCOVGB2G 1JDq0yCvjSmbTU/Z1TU/UJCWkQdz1rOrmT2nhyEE5fGYNan/RP3LOJNZS5FbB7ehRlbg ihOIY4WJeNQgQrOgra75cHufaJcYtJoRTjnPNBV/BcvZjuJ8WOhWwYfxlTKyur6mSrCO roFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786080777; x=1786685577; 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=qEyPfC7tmQ31ePOZ9QOWdXaqQ5W3bjLpfbQEap5rZvUFXw0ZggLt1hOD5VXk1BFIS/ b0FRXGLGFrDlhTzYiQrQ7bB2bHh/Y5w7CDGFvsyxyG5sJ6PqJp63Uo+/zZgc1puQFwEc pU84Pig53FD7PGisdWmUgVEy601KmGJnzJ7CLTMqFLHzQNR+rZNm8VELDQRZrpO4m17d hv8JyB3zV7uFkWQgiSL/ANU7o6R3W1Ar3G2iHNhfBH7tyY8pnUVyed+cJ5WZ7pDjSIWX SCxU2XzpyiY3Hrj0SJJZH/zKbtpj6LsyM4MXrpA4aGo38ZJes0U91pFPVcnTy/jyKVXj 46cQ== X-Forwarded-Encrypted: i=1; AHgh+RpDGuN9xE3T7WFfAxG16OZEBzGadlHet6v8epiq1uyR7BEFIPxtWG12kr1Tp9Raz50mVUTzJLKhrG5W7es=@vger.kernel.org X-Gm-Message-State: AOJu0YxqRxGD8k7KyCv7N1iw9w9VS9vfaKgrB1N2zOS+qRks191EIm4+ AYJTg1V3m+4eeHLX4J61a84ZtADXCkOTArlk46sRWXdS2V0sAotFnpuj X-Gm-Gg: AR+sD13Xyu7DFiBDrR/hhdemfsBQkM7sclw3Y4GbLNAeXcqjWEdatl/qokqaGRfx0uu y9ZttMmhjpIUPde5RB6wEU/lvkL7jKvAUySt02qAAkT8+8Bl1cAv2zOJHoNmL4kKxytiiGmgrRX GQaIiQoA/CZzLtmWz4xx/qGY89oOS8v5nvUmLBGvmoVcfb+KLJ8E4K8wDttMtB8CIzn+S6UQ8tb bXU73FX0wQwI3fLSJwZ3/XhvErtj52o0DYyMvnzz8vzOBlqXkvsKy3VE5QJXuLbwdbN9W+VYHiA 2ud3UCLNZZ22r75EDEOGVSOmHo/RnueJmGRzO932ztfAirvqEr5cEATujCzCfw8jCsLGD79Dzgx pEeodGggt3haYpBAt9EBiDX5twMLKnZqyGiIJlJYLfwAGvZYZd808AUSGB0lry5HM6VA3MG/dxt LwBohDRkS+Le7ARWBGysonsHR1X8CPHMD5c75oQ4aaWoeVEqrRTJMpF+ez0T2oKvf//eoelTgun /EJKcs= X-Received: by 2002:a05:6a00:4fc4:b0:847:99bb:b6d8 with SMTP id d2e1a72fcca58-84f2e009b31mr21558233b3a.26.1786080776836; Thu, 06 Aug 2026 22:32:56 -0700 (PDT) Received: from cyh-System-Product-Name.. ([129.227.183.200]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-cbe8f19634dsm108940a12.7.2026.08.06.22.32.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2026 22:32:56 -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 v2 2/2] RISC-V: KVM: selftests: Add PMU event filter test Date: Fri, 7 Aug 2026 13:32:27 +0800 Message-Id: <20260807053227.341700-3-yhchen312@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260807053227.341700-1-yhchen312@gmail.com> References: <20260807053227.341700-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