On (21/10/20 08:56), David Matlack wrote: > > > The spinlock is overkill. I'd suggest something like this: > > > - When VM-ioctl is invoked to update prefetch count, store it in > > > kvm_arch. No synchronization with vCPUs needed. > > > - When a vCPU takes a fault: Read the prefetch count from kvm_arch. If > > > different than count at last fault, re-allocate vCPU prefetch array. > > > (So you'll need to add prefetch array and count to kvm_vcpu_arch as > > > well.) > > > > > > No extra locks are needed. vCPUs that fault after the VM-ioctl will > > > get the new prefetch count. We don't really care if a prefetch count > > > update races with a vCPU fault as long as vCPUs are careful to only > > > read the count once (i.e. use READ_ONCE(vcpu->kvm.prefetch_count)) and > > > use that. Assuming prefetch count ioctls are rare, the re-allocation > > > on the fault path will be rare as well. > > > > So reallocation from the faul-path should happen before vCPU takes the > > mmu_lock? > > Yes. Take a look at mmu_topup_memory_caches for an example of > allocating in the fault path prior to taking the mmu lock. > > > And READ_ONCE(prefetch_count) should also happen before vCPU > > takes mmu_lock, I assume, so we need to pass it as a parameter to all > > the functions that will access prefetch array. > > Store the value of READ_ONCE(prefetch_count) in struct kvm_vcpu_arch > because you also need to know if it changes on the next fault. Then > you also don't have to add a parameter to a bunch of functions in the > fault path. > > > > > > Note: You could apply this same approach to a module param, except > > > vCPUs would be reading the module param rather than vcpu->kvm during > > > each fault. > > > > > > And the other alternative, like you suggested in the other patch, is > > > to use a vCPU ioctl. That would side-step the synchronization issue > > > because vCPU ioctls require the vCPU mutex. So the reallocation could > > > be done in the ioctl and not at fault time. > > > > One more idea, wonder what do you think: > > > > There is an upper limit on the number of PTEs we prefault, which is 128 as of > > now, but I think 64 will be good enough, or maybe even 32. So we can always > > allocate MAX_PTE_PREFETCH_NUM arrays in vcpu->arch and ioctl() will change > > ->num_ents only, which is always in (0, MAX_PTE_PREFETCH_NUM - 1] range. This > > way we never have to reallocate anything, we just adjust the "maximum index" > > value. > > 128 * 8 would be 1KB per vCPU. That is probably reasonable, but I > don't think the re-allocation would be that complex. 128 is probably too large. What I'm thinking is "32 * 8" per-VCPU. Then it can even be something like: --- diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 5271fce6cd65..b3a436f8fdf5 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -607,6 +607,11 @@ struct kvm_vcpu_xen { u64 runstate_times[4]; }; +struct kvm_mmu_pte_prefetch { + u64 ents[32]; + unsigned int num_ents; /* max prefetch value for this vCPU */ +}; + struct kvm_vcpu_arch { /* * rip and regs accesses must go through @@ -682,6 +687,8 @@ struct kvm_vcpu_arch { struct kvm_mmu_memory_cache mmu_gfn_array_cache; struct kvm_mmu_memory_cache mmu_page_header_cache; + struct kvm_mmu_pte_prefetch mmu_pte_prefetch; + /* * QEMU userspace and the guest each have their own FPU state. * In vcpu_run, we switch between the user and guest FPU contexts. --- > > > > > Taking a step back, can you say a bit more about your usecase? > > > > We are looking at various ways of reducing the number of vm-exits. There > > is only one VM running on the device (a pretty low-end laptop). > > When you say reduce the number of vm-exits, can you be more specific? > Are you trying to reduce the time it takes for vCPUs to fault in > memory during VM startup? VM Boot is the test I'm running, yes, and I see some improvements with pte-prefault == 16. > I just mention because there are likely other techniques you can apply > that would not require modifying KVM code (e.g. prefaulting the host > memory before running the VM, using the TDP MMU instead of the legacy > MMU to allow parallel faults, using hugepages to map in more memory > per fault, etc.) THP would be awesome. We have THP enabled on devices that have 8-plus gigabytes of RAM; but we don't have THP enabled on low end devices, that only have 4 gigabytes of RAM. On low end devices KVM with THP causes host memory regression, that we cannot accept, hence for 4 gig devices we try various "other solutions". We are using TDP. And somehow I never see (literally never) async PFs. It's always either hva_to_pfn_fast() or hva_to_pfn_slow() or __direct_map() from tdp_page_fault().