[RFC PATCH 09/13] KVM: x86: Let userspace choose state for plane twins to share

From: David Woodhouse

Date: Fri Nov 13 2026 - 15:32:23 EST


From: David Woodhouse <dwmw@xxxxxxxxxxxx>

Which per-vCPU state the planes of a vCPU share is policy for the VMM.
Hyper-V VSM shares XCR0 between VTLs, and whether it shares DR6 depends
on the processor, as HvRegisterVsmCapabilities.Dr6Shared reports; a VMM
with a different privilege model may want neither. So far the only knob
is the plane count, which leaves nothing to key such sharing on but the
opt-in itself.

Take a mask of state to share in args[1] of KVM_ENABLE_CAP(KVM_CAP_PLANES),
set once together with the count. Unknown bits, and a non-zero args[2]
or args[3], fail with -EINVAL. A new KVM_CAP_PLANES_SHARE reports the
supported bits on the system fd and the enabled bits on a VM fd, as
KVM_CAP_PLANES reports the ceiling and the live count. No bits are
defined yet; the following patches add XCR0 and DR6.

Signed-off-by: David Woodhouse <dwmw@xxxxxxxxxxxx>
---
Documentation/virt/kvm/api.rst | 12 +++++++++++-
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/x86.c | 8 ++++++++
arch/x86/kvm/x86.h | 11 +++++++++++
include/uapi/linux/kvm.h | 1 +
5 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 4196f3fc5718..a6008cb33e99 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -9316,7 +9316,8 @@ hpage_2g module parameter is not set to 1, -EINVAL is returned.

:Architectures: x86
:Target: VM
-:Parameters: args[0] contains the number of planes to make available
+:Parameters: args[0] contains the number of planes to make available,
+ args[1] a mask of state that the planes of a vCPU share

Opts the VM in to using multiple planes, raising the limit on plane ids
that KVM_CREATE_PLANE accepts to args[0], which must be between 1 and the
@@ -9339,6 +9340,15 @@ This capability must be enabled before any vCPU or extra plane has been
created, and the chosen count cannot be changed afterwards; otherwise
-EEXIST is returned.

+Each plane of a vCPU normally keeps its own copy of the vCPU's register
+state, apart from the CPUID model, which the planes always share. args[1]
+selects state that the planes of a vCPU share instead, so that a write
+through one plane is seen by all of them. KVM_CHECK_EXTENSION
+(KVM_CAP_PLANES_SHARE) returns the bits KVM supports on the system fd, and
+the bits the VM enabled on a VM fd. Setting an unsupported bit, or a
+non-zero args[2] or args[3], fails with -EINVAL. Like the count, the mask
+is set once.
+
8. Other capabilities.
======================

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 596be8ad7154..99a671ac2386 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1195,6 +1195,7 @@ struct kvm_arch {
bool has_protected_eoi;
bool has_protected_pmu;
bool pre_fault_allowed;
+ u8 plane_share;
unsigned int user_max_planes;
struct hlist_head *mmu_page_hash;
struct list_head active_mmu_pages;
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index af28e73e4b9b..4d70e111eeb0 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2460,6 +2460,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_VM_TSC_CONTROL:
r = kvm_caps.has_tsc_control;
break;
+ case KVM_CAP_PLANES_SHARE:
+ r = kvm ? READ_ONCE(kvm->arch.plane_share) : KVM_PLANE_SHARE_VALID;
+ break;
case KVM_CAP_X2APIC_API:
r = KVM_X2APIC_API_VALID_FLAGS;
if (kvm && !irqchip_split(kvm))
@@ -4174,6 +4177,10 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
r = -EINVAL;
if (cap->args[0] < 1 || cap->args[0] > KVM_MAX_USER_PLANES)
goto planes_unlock;
+ if (cap->args[1] & ~KVM_PLANE_SHARE_VALID)
+ goto planes_unlock;
+ if (cap->args[2] || cap->args[3])
+ goto planes_unlock;
/* Planes are only supported with a split irqchip. */
if (!irqchip_split(kvm))
goto planes_unlock;
@@ -4195,6 +4202,7 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
if (kvm->created_vcpus || kvm->has_planes ||
kvm->arch.user_max_planes)
goto planes_unlock;
+ WRITE_ONCE(kvm->arch.plane_share, cap->args[1]);
WRITE_ONCE(kvm->arch.user_max_planes, cap->args[0]);
r = 0;
planes_unlock:
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index fb87ceaa9563..2f51b476dbd1 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -923,4 +923,15 @@ struct kvm_plane *x86_alloc_plane(void);
void x86_free_plane(struct kvm_plane *plane);
unsigned kvm_x86_default_max_planes(struct kvm *kvm);

+/*
+ * The per-vCPU state that userspace may ask the planes of a vCPU to share,
+ * in args[1] of KVM_ENABLE_CAP(KVM_CAP_PLANES).
+ */
+#define KVM_PLANE_SHARE_VALID 0
+
+static inline bool kvm_plane_shares(struct kvm *kvm, u64 state)
+{
+ return READ_ONCE(kvm->arch.plane_share) & state;
+}
+
#endif
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index abf023ed78c4..aef86ef3976f 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1017,6 +1017,7 @@ struct kvm_enable_cap {
#define KVM_CAP_ARM_PMU_V3_STRICT 251
#define KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES 252
#define KVM_CAP_PLANES 254
+#define KVM_CAP_PLANES_SHARE 256

struct kvm_irq_routing_irqchip {
__u32 irqchip;
--
2.50.1