[PATCH 33/60] KVM: Implement KVM_CREATE_VCPU ioctl for planes
From: Jörg Rödel <joro@8bytes.org>
Date: 2026-06-08 14:43:21
Also in:
kvm, kvm-riscv, kvmarm, linux-mips, lkml, loongarch
Subsystem:
arm64 port (aarch64 architecture), kernel virtual machine (kvm), kernel virtual machine for arm64 (kvm/arm64), kernel virtual machine for loongarch (kvm/loongarch), kernel virtual machine for mips (kvm/mips), kernel virtual machine for powerpc (kvm/powerpc), kernel virtual machine for risc-v (kvm/riscv), kernel virtual machine for x86 (kvm/x86), kernel virtual machine for s390 (kvm/s390), linux for powerpc (32-bit and 64-bit), loongarch, mips, risc-v architecture, s390 architecture, the rest, x86 architecture (32-bit and 64-bit) · Maintainers:
Catalin Marinas, Will Deacon, Paolo Bonzini, Marc Zyngier, Oliver Upton, Tianrui Zhao, Bibo Mao, Huacai Chen, Madhavan Srinivasan, Anup Patel, Sean Christopherson, Christian Borntraeger, Janosch Frank, Claudio Imbrenda, Michael Ellerman, Thomas Bogendoerfer, Paul Walmsley, Palmer Dabbelt, Albert Ou, Heiko Carstens, Vasily Gorbik, Alexander Gordeev, Linus Torvalds, Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen
From: Joerg Roedel <redacted> Implement the KVM_CREATE_VCPU ioctl per plane. Also introduce an empty IOCTL path for the plane-vcpus, including per-architecture call-backs. Co-developed-by: Carlos López <redacted> Signed-off-by: Joerg Roedel <redacted> --- arch/arm64/kvm/arm.c | 5 ++ arch/loongarch/kvm/vcpu.c | 5 ++ arch/mips/kvm/mips.c | 5 ++ arch/powerpc/kvm/powerpc.c | 5 ++ arch/riscv/kvm/vcpu.c | 5 ++ arch/s390/kvm/kvm-s390.c | 5 ++ arch/x86/kvm/x86.c | 29 ++++++++++++ include/linux/kvm_host.h | 12 +++-- virt/kvm/kvm_main.c | 97 ++++++++++++++++++++++++++++---------- 9 files changed, 141 insertions(+), 27 deletions(-)
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index d7a4b9b239dc..b2bfea5df7e0 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c@@ -1824,6 +1824,11 @@ static int kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu, return __kvm_arm_vcpu_set_events(vcpu, events); } +bool kvm_arch_is_vcpu_plane_ioctl(unsigned ioctl) +{ + return false; +} + long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) {
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index bab3c66ae58d..0b66b8186923 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c@@ -1232,6 +1232,11 @@ static int kvm_loongarch_vcpu_set_attr(struct kvm_vcpu *vcpu, return ret; } +bool kvm_arch_is_vcpu_plane_ioctl(unsigned ioctl) +{ + return false; +} + long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) {
diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c
index e22d2a267e03..28795bad178b 100644
--- a/arch/mips/kvm/mips.c
+++ b/arch/mips/kvm/mips.c@@ -933,6 +933,11 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl, return -ENOIOCTLCMD; } +bool kvm_arch_is_vcpu_plane_ioctl(unsigned ioctl) +{ + return false; +} + long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) {
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index 35658cded0cb..476f7ea02c79 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c@@ -2055,6 +2055,11 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl, return -ENOIOCTLCMD; } +bool kvm_arch_is_vcpu_plane_ioctl(unsigned ioctl) +{ + return false; +} + long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) {
diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c
index 66cde226eb87..17680b659bdd 100644
--- a/arch/riscv/kvm/vcpu.c
+++ b/arch/riscv/kvm/vcpu.c@@ -263,6 +263,11 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl, return -ENOIOCTLCMD; } +bool kvm_arch_is_vcpu_plane_ioctl(unsigned ioctl) +{ + return false; +} + long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) {
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 94c40b2aa759..261859cb1bb6 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c@@ -5454,6 +5454,11 @@ static int kvm_s390_handle_pv_vcpu_dump(struct kvm_vcpu *vcpu, return ret; } +bool kvm_arch_is_vcpu_plane_ioctl(unsigned ioctl) +{ + return false; +} + long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) {
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index d6bf0425525c..623838885753 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c@@ -6227,6 +6227,35 @@ static int kvm_get_reg_list(struct kvm_vcpu *vcpu, return 0; } +bool kvm_arch_is_vcpu_plane_ioctl(unsigned ioctl) +{ + switch (ioctl) { + case KVM_GET_DEBUGREGS: + case KVM_SET_DEBUGREGS: + case KVM_GET_LAPIC: + case KVM_SET_LAPIC: + case KVM_GET_MSRS: + case KVM_SET_MSRS: + case KVM_GET_NESTED_STATE: + case KVM_SET_NESTED_STATE: + case KVM_GET_ONE_REG: + case KVM_SET_ONE_REG: + case KVM_GET_SREGS2: + case KVM_SET_SREGS2: + case KVM_GET_VCPU_EVENTS: + case KVM_SET_VCPU_EVENTS: + case KVM_GET_XCRS: + case KVM_SET_XCRS: + case KVM_GET_XSAVE: + case KVM_GET_XSAVE2: + case KVM_SET_XSAVE: + case KVM_GET_REG_LIST: + return true; + default: + return false; + } +} + long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) {
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 385e1ee8fd3a..b8c3f8f11cb4 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h@@ -1126,7 +1126,7 @@ static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i) #define kvm_for_each_vcpu(idx, vcpup, kvm) \ plane_for_each_vcpu(idx, vcpup, kvm->planes[0]) -static inline struct kvm_vcpu *kvm_get_vcpu_by_id(struct kvm *kvm, int id) +static inline struct kvm_vcpu *plane_get_vcpu_by_id(struct kvm_plane *plane, int id) { struct kvm_vcpu *vcpu = NULL; unsigned long i;
@@ -1134,15 +1134,20 @@ static inline struct kvm_vcpu *kvm_get_vcpu_by_id(struct kvm *kvm, int id) if (id < 0) return NULL; if (id < KVM_MAX_VCPUS) - vcpu = kvm_get_vcpu(kvm, id); + vcpu = plane_get_vcpu(plane, id); if (vcpu && vcpu->vcpu_id == id) return vcpu; - kvm_for_each_vcpu(i, vcpu, kvm) + plane_for_each_vcpu(i, vcpu, plane) if (vcpu->vcpu_id == id) return vcpu; return NULL; } +static inline struct kvm_vcpu *kvm_get_vcpu_by_id(struct kvm *kvm, int id) +{ + return plane_get_vcpu_by_id(kvm->planes[0], id); +} + static inline bool kvm_is_vcpu_creation_in_progress(struct kvm *kvm) { lockdep_assert_held(&kvm->lock);
@@ -1688,6 +1693,7 @@ bool kvm_mmu_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range); long kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg); +bool kvm_arch_is_vcpu_plane_ioctl(unsigned ioctl); long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg); long kvm_arch_vcpu_unlocked_ioctl(struct file *filp,
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 2d0d5f4fd356..8839f91fd15e 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c@@ -538,14 +538,11 @@ static void kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id) { vcpu->cpu = -1; vcpu->kvm = kvm; - vcpu->plane = kvm->planes[0]; vcpu->vcpu_id = id; kvm_async_pf_vcpu_init(vcpu); vcpu->last_used_slot = NULL; - vcpu->plane_level = 0; - /* Fill the stats id string for the vcpu */ snprintf(vcpu->stats_id, sizeof(vcpu->stats_id), "kvm-%d/vcpu-%d", task_pid_nr(current), id);
@@ -4306,9 +4303,13 @@ static struct file_operations kvm_vcpu_fops = { */ static int create_vcpu_fd(struct kvm_vcpu *vcpu) { - char name[8 + 1 + ITOA_MAX_LEN + 1]; + char name[14 + 1 + (2 * ITOA_MAX_LEN) + 1]; + + if (vcpu->plane_level == 0) + snprintf(name, sizeof(name), "kvm-vcpu:%d", vcpu->vcpu_id); + else + snprintf(name, sizeof(name), "kvm-vcpu-plane%d:%d", vcpu->plane_level, vcpu->vcpu_id); - snprintf(name, sizeof(name), "kvm-vcpu:%d", vcpu->vcpu_id); return anon_inode_getfd(name, &kvm_vcpu_fops, vcpu, O_RDWR | O_CLOEXEC); }
@@ -4327,13 +4328,17 @@ DEFINE_SIMPLE_ATTRIBUTE(vcpu_get_pid_fops, vcpu_get_pid, NULL, "%llu\n"); static void kvm_create_vcpu_debugfs(struct kvm_vcpu *vcpu) { + char dir_name[10 + (2 * ITOA_MAX_LEN) + 1]; struct dentry *debugfs_dentry; - char dir_name[ITOA_MAX_LEN * 2]; if (!debugfs_initialized()) return; - snprintf(dir_name, sizeof(dir_name), "vcpu%d", vcpu->vcpu_id); + if (vcpu->plane_level == 0) + snprintf(dir_name, sizeof(dir_name), "vcpu%d", vcpu->vcpu_id); + else + snprintf(dir_name, sizeof(dir_name), "vcpu%d-plane%d", vcpu->plane_level, vcpu->vcpu_id); + debugfs_dentry = debugfs_create_dir(dir_name, vcpu->kvm->debugfs_dentry); debugfs_create_file("pid", 0444, debugfs_dentry, vcpu,
@@ -4346,10 +4351,11 @@ static void kvm_create_vcpu_debugfs(struct kvm_vcpu *vcpu) /* * Creates some virtual cpus. Good luck creating more than one. */ -static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, unsigned long id) +static int kvm_plane_ioctl_create_vcpu(struct kvm_plane *plane, unsigned long id) { - int r = -EINVAL; + struct kvm *kvm = plane->kvm; struct kvm_vcpu *vcpu; + int r; mutex_lock(&kvm->lock); if (kvm->created_vcpus >= kvm->max_vcpus) {
@@ -4366,11 +4372,28 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, unsigned long id) if (!vcpu) return -ENOMEM; - r = kvm_vcpu_init_common(vcpu, kvm, id); - if (r) + r = -EEXIST; + if (plane_get_vcpu_by_id(plane, id)) goto vcpu_free; + if (plane->level > 0) { + struct kvm_vcpu *vcpu_plane0 = kvm_get_vcpu_by_id(kvm, id); + + /* Plane0 VCPU must exist before creating non-plane0 VCPUs */ + r = -EINVAL; + if (vcpu_plane0 == NULL) + goto vcpu_free; + + vcpu->common = vcpu_plane0->common; + } else { + r = kvm_vcpu_init_common(vcpu, kvm, id); + if (r) + goto vcpu_free; + } + vcpu->vcpu_idx = vcpu->common->vcpu_idx; + vcpu->plane = plane; + vcpu->plane_level = plane->level; vcpu->run = vcpu->common->run; kvm_vcpu_init(vcpu, kvm, id);
@@ -4381,12 +4404,7 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, unsigned long id) mutex_lock(&kvm->lock); - if (kvm_get_vcpu_by_id(kvm, id)) { - r = -EEXIST; - goto unlock_vcpu_destroy; - } - - r = xa_insert(&kvm->planes[0]->vcpu_array, vcpu->vcpu_idx, vcpu, GFP_KERNEL_ACCOUNT); + r = xa_insert(&plane->vcpu_array, vcpu->vcpu_idx, vcpu, GFP_KERNEL_ACCOUNT); WARN_ON_ONCE(r == -EBUSY); if (r) goto unlock_vcpu_destroy;
@@ -4416,7 +4434,7 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, unsigned long id) kvm_put_xa_erase: kvm_vcpu_unlock(vcpu); kvm_put_kvm_no_destroy(kvm); - xa_erase(&kvm->planes[0]->vcpu_array, vcpu->vcpu_idx); + xa_erase(&plane->vcpu_array, vcpu->vcpu_idx); unlock_vcpu_destroy: mutex_unlock(&kvm->lock); kvm_arch_vcpu_destroy(vcpu);
@@ -4550,7 +4568,7 @@ static int kvm_wait_for_vcpu_online(struct kvm_vcpu *vcpu) /* * Acquire and release the vCPU's mutex to wait for vCPU creation to - * complete (kvm_vm_ioctl_create_vcpu() holds the mutex until the vCPU + * complete (kvm_plane_ioctl_create_vcpu() holds the mutex until the vCPU * is fully online). */ if (mutex_lock_killable(kvm_vcpu_mutex(vcpu)))
@@ -4564,6 +4582,22 @@ static int kvm_wait_for_vcpu_online(struct kvm_vcpu *vcpu) return 0; } +static inline bool kvm_is_vcpu_plane_ioctl(unsigned ioctl) +{ + switch (ioctl) { + case KVM_GET_FPU: + case KVM_SET_FPU: + case KVM_GET_REGS: + case KVM_SET_REGS: + case KVM_GET_SREGS: + case KVM_SET_SREGS: + case KVM_TRANSLATE: + return true; + default: + return kvm_arch_is_vcpu_plane_ioctl(ioctl); + } +} + static long kvm_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) {
@@ -4576,6 +4610,9 @@ static long kvm_vcpu_ioctl(struct file *filp, if (vcpu->kvm->mm != current->mm || vcpu->kvm->vm_dead) return -EIO; + if (vcpu->plane_level > 0 && !kvm_is_vcpu_plane_ioctl(ioctl)) + return -EINVAL; + if (unlikely(_IOC_TYPE(ioctl) != KVMIO)) return -EINVAL;
@@ -4858,6 +4895,21 @@ static long kvm_vcpu_compat_ioctl(struct file *filp, } #endif +static long __kvm_plane_ioctl(struct kvm_plane *plane, unsigned int ioctl, unsigned long arg) +{ + long r; + + switch (ioctl) { + case KVM_CREATE_VCPU: + r = kvm_plane_ioctl_create_vcpu(plane, arg); + break; + default: + r = -ENOTTY; + } + + return r; +} + static long kvm_plane_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) {
@@ -4866,10 +4918,7 @@ static long kvm_plane_ioctl(struct file *filp, unsigned int ioctl, if (plane->kvm->mm != current->mm || plane->kvm->vm_dead) return -EIO; - switch (ioctl) { - default: - return -ENOTTY; - } + return __kvm_plane_ioctl(plane, ioctl, arg); } static int kvm_plane_release(struct inode *inode, struct file *filp)
@@ -5396,7 +5445,7 @@ static long kvm_vm_ioctl(struct file *filp, r = kvm_vm_ioctl_create_plane(kvm, arg); break; case KVM_CREATE_VCPU: - r = kvm_vm_ioctl_create_vcpu(kvm, arg); + r = __kvm_plane_ioctl(kvm->planes[0], ioctl, arg); break; case KVM_ENABLE_CAP: { struct kvm_enable_cap cap;
--
2.53.0