Skip to content

Commit c268f20

Browse files
ouptonMarc Zyngier
authored andcommitted
KVM: arm64: nv: Punt stage-2 recycling to a vCPU request
Currently, when a nested MMU is repurposed for some other MMU context, KVM unmaps everything during vcpu_load() while holding the MMU lock for write. This is quite a performance bottleneck for large nested VMs, as all vCPU scheduling will spin until the unmap completes. Start punting the MMU cleanup to a vCPU request, where it is then possible to periodically release the MMU lock and CPU in the presence of contention. Ensure that no vCPU winds up using a stale MMU by tracking the pending unmap on the S2 MMU itself and requesting an unmap on every vCPU that finds it. Signed-off-by: Oliver Upton <oliver.upton@linux.dev> Link: https://lore.kernel.org/r/20241007233028.2236133-4-oliver.upton@linux.dev Signed-off-by: Marc Zyngier <maz@kernel.org>
1 parent 3c164eb commit c268f20

File tree

4 files changed

+37
-2
lines changed

4 files changed

+37
-2
lines changed

arch/arm64/include/asm/kvm_host.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#define KVM_REQ_RELOAD_PMU KVM_ARCH_REQ(5)
5252
#define KVM_REQ_SUSPEND KVM_ARCH_REQ(6)
5353
#define KVM_REQ_RESYNC_PMU_EL0 KVM_ARCH_REQ(7)
54+
#define KVM_REQ_NESTED_S2_UNMAP KVM_ARCH_REQ(8)
5455

5556
#define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
5657
KVM_DIRTY_LOG_INITIALLY_SET)
@@ -211,6 +212,12 @@ struct kvm_s2_mmu {
211212
*/
212213
bool nested_stage2_enabled;
213214

215+
/*
216+
* true when this MMU needs to be unmapped before being used for a new
217+
* purpose.
218+
*/
219+
bool pending_unmap;
220+
214221
/*
215222
* 0: Nobody is currently using this, check vttbr for validity
216223
* >0: Somebody is actively using this.

arch/arm64/include/asm/kvm_nested.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ extern void kvm_s2_mmu_iterate_by_vmid(struct kvm *kvm, u16 vmid,
7878
extern void kvm_vcpu_load_hw_mmu(struct kvm_vcpu *vcpu);
7979
extern void kvm_vcpu_put_hw_mmu(struct kvm_vcpu *vcpu);
8080

81+
extern void check_nested_vcpu_requests(struct kvm_vcpu *vcpu);
82+
8183
struct kvm_s2_trans {
8284
phys_addr_t output;
8385
unsigned long block_size;

arch/arm64/kvm/arm.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,8 @@ static int check_vcpu_requests(struct kvm_vcpu *vcpu)
10311031

10321032
if (kvm_dirty_ring_check_request(vcpu))
10331033
return 0;
1034+
1035+
check_nested_vcpu_requests(vcpu);
10341036
}
10351037

10361038
return 1;

arch/arm64/kvm/nested.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -632,9 +632,9 @@ static struct kvm_s2_mmu *get_s2_mmu_nested(struct kvm_vcpu *vcpu)
632632
/* Set the scene for the next search */
633633
kvm->arch.nested_mmus_next = (i + 1) % kvm->arch.nested_mmus_size;
634634

635-
/* Clear the old state */
635+
/* Make sure we don't forget to do the laundry */
636636
if (kvm_s2_mmu_valid(s2_mmu))
637-
kvm_stage2_unmap_range(s2_mmu, 0, kvm_phys_size(s2_mmu), false);
637+
s2_mmu->pending_unmap = true;
638638

639639
/*
640640
* The virtual VMID (modulo CnP) will be used as a key when matching
@@ -650,6 +650,16 @@ static struct kvm_s2_mmu *get_s2_mmu_nested(struct kvm_vcpu *vcpu)
650650

651651
out:
652652
atomic_inc(&s2_mmu->refcnt);
653+
654+
/*
655+
* Set the vCPU request to perform an unmap, even if the pending unmap
656+
* originates from another vCPU. This guarantees that the MMU has been
657+
* completely unmapped before any vCPU actually uses it, and allows
658+
* multiple vCPUs to lend a hand with completing the unmap.
659+
*/
660+
if (s2_mmu->pending_unmap)
661+
kvm_make_request(KVM_REQ_NESTED_S2_UNMAP, vcpu);
662+
653663
return s2_mmu;
654664
}
655665

@@ -1199,3 +1209,17 @@ int kvm_init_nv_sysregs(struct kvm *kvm)
11991209

12001210
return 0;
12011211
}
1212+
1213+
void check_nested_vcpu_requests(struct kvm_vcpu *vcpu)
1214+
{
1215+
if (kvm_check_request(KVM_REQ_NESTED_S2_UNMAP, vcpu)) {
1216+
struct kvm_s2_mmu *mmu = vcpu->arch.hw_mmu;
1217+
1218+
write_lock(&vcpu->kvm->mmu_lock);
1219+
if (mmu->pending_unmap) {
1220+
kvm_stage2_unmap_range(mmu, 0, kvm_phys_size(mmu), true);
1221+
mmu->pending_unmap = false;
1222+
}
1223+
write_unlock(&vcpu->kvm->mmu_lock);
1224+
}
1225+
}

0 commit comments

Comments
 (0)