Skip to contentSkip to conversation

KVM: PPC: Misc fixes and nestedv2 vCPU limit fixes

PATCH New Ritesh Harjani (IBM) 2 participants 9 messages Nov 1

Subsystems (3)
lore
Share & tools
lore

Ritesh Harjani (IBM)

author reviewer
#0 lore raw

v3 cover KVM: PPC: Misc fixes and nestedv2 vCPU limit fixes

v2 -> v3:
=========
- Fixed logic bug in patch-2 & patch-6. (realized after sending + sashiko caught this as well).

RFC -> v2:
=========
- Addressed sashiko review comments
- added patch-3 to address similar __ffs(0) problem on booke
- fixed build issues in patch-4
- added patch-5 to reject vcpu ids beyond the PAPR limit
- Few more patches were added. Few of them were reported by Sashiko and few others were caught while doing vcpu create path code walkthrough

Most of these issues were identified while adding kvm powerpc selftests infrastructure support [1] (which is now merged upstream). Patch-1 and Patch-2 were identified while running the selftests on Qemu+TCG+powernv+BE, while Patch-4 can also be reproduced on a real pseries LPAR with nestedv2 capabilities.

[1]: https://lore.kernel.org/linuxppc-dev/179211526968.809012.4905747914183363587.b4-ty@b4/
[RFC]: https://lore.kernel.org/linuxppc-dev/[email protected]/

Ritesh Harjani (IBM) (7):
KVM: PPC: Book3S HV: Read EX_DSISR as a 32-bit field on guest exit
KVM: PPC: Book3S: Don't call __ffs() on an empty pending_exceptions bitmap
KVM: PPC: BookE: Don't call __ffs() on an empty pending_exceptions bitmap
KVM: PPC: Book3S HV nestedv2: cap max vcpus to PAPR limit
KVM: PPC: Book3S HV nestedv2: reject vCPU ids beyond the PAPR limit
KVM: PPC: Book3S HV: Fix error handling during vcore create
KVM: PPC: Book3s: nestedv2: Free the guest state buffers if vcpu create fails

 arch/powerpc/include/asm/kvm_host.h   |  1 +
 arch/powerpc/kvm/book3s.c             |  2 +-
 arch/powerpc/kvm/book3s_hv.c          | 15 ++++++++++-----
 arch/powerpc/kvm/book3s_hv_p9_entry.c | 13 +++++++++++--
 arch/powerpc/kvm/booke.c              |  2 +-
 arch/powerpc/kvm/powerpc.c            |  9 +++++++++
 6 files changed, 33 insertions(+), 9 deletions(-)

--
2.39.5

author reviewer
#1 lore raw

PATCH v3 1/7 KVM: PPC: Book3S HV: Read EX_DSISR as a 32-bit field on guest exit

kvmhv_vcpu_entry_p9() recovers the guest fault state from the paca exception save area with

	vcpu->arch.fault_dsisr = exsave[EX_DSISR/sizeof(u64)];

but EX_DSISR is not a doubleword. It is a 32-bit field at offset 48 that shares a doubleword with EX_CCR at offset 52 and the exception entry in exceptions-64s.S stores it accordingly. For e.g.

	stw	r10,IAREA+EX_DSISR(r13)

This issue was caught when running kvm selftests on big-endian, where this read would land in the high half and the truncation yields EX_CCR instead - KVM acts on the guest's condition register as if it were HDSISR.

The result is that every HDSI on a big-endian host is evaluated against a bogus fault status i.e. it fails with below error:

KVM: Got radix HV page fault with DSISR=84004840

Let's fix this by reading this field at it's natural width.

Fixes 89d35b239101 ("KVM: PPC: Book3S HV P9: Implement the rest of the P9 path in C")
---
 arch/powerpc/kvm/book3s_hv_p9_entry.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
arch/powerpc/kvm/book3s_hv_p9_entry.c +11 -2
index 34bc0a8a1288..16aa8582b888 100644
@@ -529,6 +529,15 @@ unsigned long kvmppc_msr_hard_disable_set_facilities(struct kvm_vcpu *vcpu, unsi
 }
 EXPORT_SYMBOL_GPL(kvmppc_msr_hard_disable_set_facilities);
 
+/*
+ * EX_DSISR is a 32-bit field that shares a doubleword with EX_CCR.
+ * Small helper for reading it back at its natural width.
+ */
+static u32 exsave_dsisr(u64 *exsave)
+{
+	return *(u32 *)((void *)exsave + EX_DSISR);
+}
+
 int kvmhv_vcpu_entry_p9(struct kvm_vcpu *vcpu, u64 time_limit, unsigned long lpcr, u64 *tb)
 {
 	struct p9_host_os_sprs host_os_sprs;
@@ -770,7 +779,7 @@ int kvmhv_vcpu_entry_p9(struct kvm_vcpu *vcpu, u64 time_limit, unsigned long lpc
 
 	if (unlikely(trap == BOOK3S_INTERRUPT_MACHINE_CHECK)) {
 		vcpu->arch.fault_dar = exsave[EX_DAR/sizeof(u64)];
-		vcpu->arch.fault_dsisr = exsave[EX_DSISR/sizeof(u64)];
+		vcpu->arch.fault_dsisr = exsave_dsisr(exsave);
 		kvmppc_realmode_machine_check(vcpu);
 
 	} else if (unlikely(trap == BOOK3S_INTERRUPT_HMI)) {
@@ -781,7 +790,7 @@ int kvmhv_vcpu_entry_p9(struct kvm_vcpu *vcpu, u64 time_limit, unsigned long lpc
 
 	} else if (trap == BOOK3S_INTERRUPT_H_DATA_STORAGE) {
 		vcpu->arch.fault_dar = exsave[EX_DAR/sizeof(u64)];
-		vcpu->arch.fault_dsisr = exsave[EX_DSISR/sizeof(u64)];
+		vcpu->arch.fault_dsisr = exsave_dsisr(exsave);
 		vcpu->arch.fault_gpa = mfspr(SPRN_ASDR);
 
 	} else if (trap == BOOK3S_INTERRUPT_H_INST_STORAGE) {
Signature · 1 line
2.39.5
author reviewer
#2 lore raw

PATCH v3 2/7 KVM: PPC: Book3S: Don't call __ffs() on an empty pending_exceptions bitmap

In reply to Ritesh Harjani (IBM)

This problem was caught when running KVM PPC selftests on big-endian. __ffs() doc clearly says:
"Undefined if no bit exists, so code should check against 0 first"

This cause KVM to deliever an arbitary interrupt to the guest and was causing guest to hang up while running these selftests.

On checking further - GCC was emitting 0 in this case, which caused System Reset in guest while running kvm selftests.

Use find_first_bit() instead, which reads the word once and returns the bitmap size when no bit is set, instead of reading *pending twice thereby causing TOCTOU as pointed by Sashiko.

---
 arch/powerpc/kvm/book3s.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/kvm/book3s.c +1 -1
index 2efbe05caed7..5de4b6e86656 100644
@@ -401,7 +401,7 @@ int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu)
 	if (vcpu->arch.pending_exceptions)
 		printk(KERN_EMERG "KVM: Check pending: %lx\n", vcpu->arch.pending_exceptions);
 #endif
-	priority = __ffs(*pending);
+	priority = find_first_bit(pending, BITS_PER_BYTE * sizeof(*pending));
 	while (priority < BOOK3S_IRQPRIO_MAX) {
 		if (kvmppc_book3s_irqprio_deliver(vcpu, priority) &&
 		    clear_irqprio(vcpu, priority)) {
Signature · 1 line
2.39.5
author reviewer
#3 lore raw

PATCH v3 3/7 KVM: PPC: BookE: Don't call __ffs() on an empty pending_exceptions bitmap

In reply to Ritesh Harjani (IBM)

kvmppc_core_check_exceptions() has the same pattern as the Book3S code:
it starts the scan of the pending exception bitmap with __ffs(*pending) and is called from kvmppc_core_prepare_to_enter() on every guest entry, so it routinely passes 0 to __ffs(), which is undefined.

To be safe here, let's use find_first_bit(), which returns the bitmap size when no bit is set.

---
 arch/powerpc/kvm/booke.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/kvm/booke.c +1 -1
index 13ad4cf5fa71..9794ee6f7204 100644
@@ -692,7 +692,7 @@ static void kvmppc_core_check_exceptions(struct kvm_vcpu *vcpu)
 	unsigned long *pending = &vcpu->arch.pending_exceptions;
 	unsigned int priority;
 
-	priority = __ffs(*pending);
+	priority = find_first_bit(pending, BITS_PER_BYTE * sizeof(*pending));
 	while (priority < BOOKE_IRQPRIO_MAX) {
 		if (kvmppc_booke_irqprio_deliver(vcpu, priority))
 			break;
Signature · 1 line
2.39.5
author reviewer
#4 lore raw

PATCH v3 4/7 KVM: PPC: Book3S HV nestedv2: cap max vcpus to PAPR limit

In reply to Ritesh Harjani (IBM)

PAPR spec for H_GUEST_CREATE_VCPU says:

"Only 2048 VCPUs can be created, and the specified ID must be in the range of 0
to 2047"

However, KVM reports KVM_CAP_MAX_VCPUS as NR_CPUS and KVM_CAP_MAX_VCPU_ID as 8 * NR_CPUS. It also passes the userspace vCPU id through to the hcall unchanged. On a nestedv2 host (KVM on PowerVM / pseries nested PAPR) built with NR_CPUS > 2048, kvm selftest e.g. kvm_create_max_vcpus will create vCPU 2048 and fail with EINVAL when the hypervisor returns H_P3.

Following error gets reported (with a debug print added to selftest to print failed vcpu_id number):

    ./kvm_create_max_vcpus
    Random seed: 0x6ec3f539
    KVM_CAP_MAX_VCPU_ID: 65536
    KVM_CAP_MAX_VCPUS: 8192
    Testing creating 8192 vCPUs, with IDs 0...8191.
    [   62.655458][  T302] KVM: Create Guest vcpu hcall failed, rc=-56
    [   62.655458][  T302] KVM: Create Guest vcpu hcall failed, rc=-56
    Failed KVM_CREATE_VCPU for vcpu_id 2048 with errno 22

    ==== Test Assertion Failure ====
      lib/kvm_util.c:1395: vcpu->fd >= 0
      pid=302 tid=302 errno=22 - Invalid argument
         1  0x00000000100061d3: __vm_vcpu_add at kvm_util.c:1395 (discriminator 6)
         2  0x0000000010001a6b: test_vcpu_creation at kvm_create_max_vcpus.c:32 (discriminator 3)
         3  0x000000001000160b: main at kvm_create_max_vcpus.c:59
         4  0x000000001001b713: __libc_start_call_main at libc-start.o:?
         5  0x000000001001bb47: __libc_start_main_impl at ??:?
      KVM_CREATE_VCPU failed, rc: -1 errno: 22 (Invalid argument)
    [   65.316986][  T302] KVM: TLB LPID invalidation hcall failed, rc=-2
    [   65.316986][  T302] KVM: TLB LPID invalidation hcall failed, rc=-2

Cap both extensions at 2048 when kvmhv_is_nestedv2() is set, so userspace never requests an id the L0 cannot create. PowerNV and nestedv1 are unchanged. The issue is only seen with nestedv2. While at it, this also fixes the limit for KVM_CAP_NR_VCPUS.

---
 arch/powerpc/include/asm/kvm_host.h | 1 +
 arch/powerpc/kvm/powerpc.c          | 6 ++++++
 2 files changed, 7 insertions(+)
arch/powerpc/include/asm/kvm_host.h +1 -0
index 708a32f89776..c068cc1dd51f 100644
@@ -35,6 +35,7 @@
 
 #define KVM_MAX_VCPUS		NR_CPUS
 #define KVM_MAX_VCORES		NR_CPUS
+#define KVM_MAX_VCPU_IDS_NESTEDv2	2048	/* PAPR H_GUEST_CREATE_VCPU vcpuId */
 
 #include <asm/cputhreads.h>
 
arch/powerpc/kvm/powerpc.c +6 -0
index 9194cf492d1c..cf620b6534de 100644
@@ -663,12 +663,18 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 			r = min(num_present_cpus(), KVM_MAX_VCPUS);
 		else
 			r = min(num_online_cpus(), KVM_MAX_VCPUS);
+		if (kvmhv_is_nestedv2())
+			r = min(r, KVM_MAX_VCPU_IDS_NESTEDv2);
 		break;
 	case KVM_CAP_MAX_VCPUS:
 		r = KVM_MAX_VCPUS;
+		if (kvmhv_is_nestedv2())
+			r = min(r, KVM_MAX_VCPU_IDS_NESTEDv2);
 		break;
 	case KVM_CAP_MAX_VCPU_ID:
 		r = KVM_MAX_VCPU_IDS;
+		if (kvmhv_is_nestedv2())
+			r = min(r, KVM_MAX_VCPU_IDS_NESTEDv2);
 		break;
 #ifdef CONFIG_PPC_BOOK3S_64
 	case KVM_CAP_PPC_GET_SMMU_INFO:
Signature · 1 line
2.39.5
author reviewer
#5 lore raw

PATCH v3 5/7 KVM: PPC: Book3S HV nestedv2: reject vCPU ids beyond the PAPR limit

In reply to Ritesh Harjani (IBM)

The previous patch caps KVM_CAP_MAX_VCPU_ID at 2048 on nestedv2 but nothing actually enforces it. The generic KVM_CREATE_VCPU path only checks the id against KVM_MAX_VCPU_IDS, which is 8 * NR_CPUS on HV. That means a VMM that ignores the capability can still pass a larger id. That id goes all the way down to H_GUEST_CREATE_VCPU, where the L0 rejects it and prints, ...

KVM: Create Guest vcpu hcall failed, rc=-56

...for every such attempt before failing the ioctl with -EINVAL anyway.

Check the id in kvm_arch_vcpu_precreate() instead so it fails early with the same -EINVAL. Note that PowerNV and nestedv1 are not affected.

---
 arch/powerpc/kvm/powerpc.c | 3 +++
 1 file changed, 3 insertions(+)
arch/powerpc/kvm/powerpc.c +3 -0
index cf620b6534de..5f9cfc34bec2 100644
@@ -778,6 +778,9 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
 
 int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
 {
+	if (kvmhv_is_nestedv2() && id >= KVM_MAX_VCPU_IDS_NESTEDv2)
+		return -EINVAL;
+
 	return 0;
 }
 
Signature · 1 line
2.39.5
#6 lore raw

Re: [PATCHv3 5/7] KVM: PPC: Book3S HV nestedv2: reject vCPU ids beyond the PAPR limit

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] KVM: PPC: undeclared KVM_MAX_VCPU_IDS_NESTEDv2 in kvm_arch_vcpu_precreate() --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] KVM: PPC: undeclared KVM_MAX_VCPU_IDS_NESTEDv2 in kvm_arch_vcpu_precreate() --

commit 6b87f3d0f26a22dc1fab90af1a0540a7a33c6a90
Author: Ritesh Harjani (IBM) <[email protected]>

KVM: PPC: Book3S HV nestedv2: reject vCPU ids beyond the PAPR limit

This commit adds a check in kvm_arch_vcpu_precreate() to reject vCPU IDs at or above KVM_MAX_VCPU_IDS_NESTEDv2 with -EINVAL when running on nestedv2, avoiding failing later in H_GUEST_CREATE_VCPU.

arch/powerpc/kvm/powerpc.c void kvm_arch_flush_shadow_memslot(struct kvm *kvm, · line 776

Quoted 10 lines
arch/powerpc/kvm/powerpc.c +2 -0
index 9194cf492d1ca..f82d3545b39ad 100644
@@ -772,6 +772,9 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
 
 int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
 {
+	if (kvmhv_is_nestedv2() && id >= KVM_MAX_VCPU_IDS_NESTEDv2)
+		return -EINVAL;

[Severity: Medium]
Is this commit missing the prerequisite patch referenced in the commit message?

In arch/powerpc/kvm/powerpc.c, kvm_arch_vcpu_precreate() checks id against KVM_MAX_VCPU_IDS_NESTEDv2, but KVM_MAX_VCPU_IDS_NESTEDv2 is not defined in arch/powerpc/include/asm/kvm_host.h:

#define KVM_MAX_VCPU_IDS (MAX_SMT_THREADS * KVM_MAX_VCORES)

or anywhere else in the tree, causing compilation of arch/powerpc/kvm/powerpc.c to fail when CONFIG_KVM is enabled.

In addition, the commit message notes that the previous patch caps KVM_CAP_MAX_VCPU_ID at 2048 on nestedv2, yet kvm_vm_ioctl_check_extension() in arch/powerpc/kvm/powerpc.c still returns KVM_MAX_VCPU_IDS unconditionally:

	case KVM_CAP_MAX_VCPU_ID:
		r = KVM_MAX_VCPU_IDS;
		break;

Should the prerequisite patch defining KVM_MAX_VCPU_IDS_NESTEDv2 and capping KVM_CAP_MAX_VCPU_ID in kvm_vm_ioctl_check_extension() be applied before this change?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/3b233fa135d54d3ffb569d7febdc1cb8e0ed406a.1793546707.git.ritesh.list@gmail.com?part=1
author reviewer
#7 lore raw

PATCH v3 6/7 KVM: PPC: Book3S HV: Fix error handling during vcore create

In reply to Ritesh Harjani (IBM)

kvmppc_vcore_create() can fail and return NULL, this patch fixes the error handling when this happens.

vcore and online_vcores count should only be updated when vcore was actually created, otherwise it's state gets messed up for the VM run.

---
 arch/powerpc/kvm/book3s_hv.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)
arch/powerpc/kvm/book3s_hv.c +6 -4
index 22283d46e8fe..e39491dfb975 100644
@@ -3090,10 +3090,12 @@ static int kvmppc_core_vcpu_create_hv(struct kvm_vcpu *vcpu)
 			err = -ENOMEM;
 			vcore = kvmppc_vcore_create(kvm,
 					id & ~(kvm->arch.smt_mode - 1));
-			mutex_lock(&kvm->arch.mmu_setup_lock);
-			kvm->arch.vcores[core] = vcore;
-			kvm->arch.online_vcores++;
-			mutex_unlock(&kvm->arch.mmu_setup_lock);
+			if (vcore) {
+				mutex_lock(&kvm->arch.mmu_setup_lock);
+				kvm->arch.vcores[core] = vcore;
+				kvm->arch.online_vcores++;
+				mutex_unlock(&kvm->arch.mmu_setup_lock);
+			}
 		}
 	}
 
Signature · 1 line
2.39.5
author reviewer
#8 lore raw

PATCH v3 7/7 KVM: PPC: Book3s: nestedv2: Free the guest state buffers if vcpu create fails

In reply to Ritesh Harjani (IBM)

On a nestedv2 host, kvmppc_core_vcpu_create_hv() first creates the vCPU in the L0 with kvmhv_nestedv2_vcpu_create(), which also allocates the guest state messages and the run input/output buffers. Only after that it picks a vcore for the vCPU - which can actually fail.

This patch therefore takes care of freeing the GSM abd GSB for nestedv2 if an error occurs later as part of the cleanup routine.

---
 arch/powerpc/kvm/book3s_hv.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
arch/powerpc/kvm/book3s_hv.c +4 -1
index e39491dfb975..8de753e302c4 100644
@@ -3099,8 +3099,11 @@ static int kvmppc_core_vcpu_create_hv(struct kvm_vcpu *vcpu)
 		}
 	}
 
-	if (!vcore)
+	if (!vcore) {
+		if (kvmhv_is_nestedv2())
+			kvmhv_nestedv2_vcpu_free(vcpu, &vcpu->arch.nestedv2_io);
 		return err;
+	}
 
 	spin_lock(&vcore->lock);
 	++vcore->num_threads;
Signature · 1 line
2.39.5