Re: [PATCH 1/3] KVM: x86: Move mmu_memory_cache functions to common code

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Jan 28, 2019 at 08:19:56AM -0800, Sean Christopherson wrote:
> On Mon, Jan 28, 2019 at 03:48:41PM +0100, Christoffer Dall wrote:
> > We are currently duplicating the mmu memory cache functionality quite
> > heavily between the architectures that support KVM.  As a first step,
> > move the x86 implementation (which seems to have the most recently
> > maintained version of the mmu memory cache) to common code.
> > 
> > We rename the functions and data types to have a kvm_ prefix for
> > anything exported as a symbol to the rest of the kernel and take the
> > chance to rename memory_cache to memcache to avoid overly long lines.
> 
> It'd be helpful to do the rename it a separate patch so that the move
> really is a straight move of code.
> 
> > This is a bit tedious on the callsites but ends up looking more
> > palatable.
> > 
> > We also introduce an arch-specific kvm_types.h which can be used to
> > define the architecture-specific GFP flags for allocating memory to the
> > memory cache, and to specify how many objects are required in the memory
> > cache.  These are the two points where the current implementations
> > diverge across architectures.  Since kvm_host.h defines structures with
> > fields of the memcache object, we define the memcache structure in
> > kvm_types.h, and we include the architecture-specific kvm_types.h to
> > know the size of object in kvm_host.h.
> > 
> > This patch currently only defines the structure and requires valid
> > defines in the architecture-specific kvm_types.h when KVM_NR_MEM_OBJS is
> > defined.  As we move each architecture to the common implementation,
> > this condition will eventually go away.
> > 
> > Signed-off-by: Christoffer Dall <christoffer.dall@xxxxxxx>
> > ---
> >  arch/arm/include/asm/kvm_types.h     |  5 ++
> >  arch/arm64/include/asm/kvm_types.h   |  6 ++
> >  arch/mips/include/asm/kvm_types.h    |  5 ++
> >  arch/powerpc/include/asm/kvm_types.h |  5 ++
> >  arch/s390/include/asm/kvm_types.h    |  5 ++
> >  arch/x86/include/asm/kvm_host.h      | 17 +----
> >  arch/x86/include/asm/kvm_types.h     | 10 +++
> >  arch/x86/kvm/mmu.c                   | 97 ++++++----------------------
> >  arch/x86/kvm/paging_tmpl.h           |  4 +-
> >  include/linux/kvm_host.h             |  9 +++
> >  include/linux/kvm_types.h            | 12 ++++
> >  virt/kvm/kvm_main.c                  | 58 +++++++++++++++++
> >  12 files changed, 139 insertions(+), 94 deletions(-)
> >  create mode 100644 arch/arm/include/asm/kvm_types.h
> >  create mode 100644 arch/arm64/include/asm/kvm_types.h
> >  create mode 100644 arch/mips/include/asm/kvm_types.h
> >  create mode 100644 arch/powerpc/include/asm/kvm_types.h
> >  create mode 100644 arch/s390/include/asm/kvm_types.h
> >  create mode 100644 arch/x86/include/asm/kvm_types.h
> > 
> > diff --git a/arch/arm/include/asm/kvm_types.h b/arch/arm/include/asm/kvm_types.h
> > new file mode 100644
> > index 000000000000..bc389f82e88d
> > --- /dev/null
> > +++ b/arch/arm/include/asm/kvm_types.h
> > @@ -0,0 +1,5 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef _ASM_ARM_KVM_TYPES_H
> > +#define _ASM_ARM_KVM_TYPES_H
> > +
> > +#endif /* _ASM_ARM_KVM_TYPES_H */
> > diff --git a/arch/arm64/include/asm/kvm_types.h b/arch/arm64/include/asm/kvm_types.h
> > new file mode 100644
> > index 000000000000..d0987007d581
> > --- /dev/null
> > +++ b/arch/arm64/include/asm/kvm_types.h
> > @@ -0,0 +1,6 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef _ASM_ARM64_KVM_TYPES_H
> > +#define _ASM_ARM64_KVM_TYPES_H
> > +
> > +#endif /* _ASM_ARM64_KVM_TYPES_H */
> > +
> > diff --git a/arch/mips/include/asm/kvm_types.h b/arch/mips/include/asm/kvm_types.h
> > new file mode 100644
> > index 000000000000..5efeb32a5926
> > --- /dev/null
> > +++ b/arch/mips/include/asm/kvm_types.h
> > @@ -0,0 +1,5 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef _ASM_MIPS_KVM_TYPES_H
> > +#define _ASM_MIPS_KVM_TYPES_H
> > +
> > +#endif /* _ASM_MIPS_KVM_TYPES_H */
> > diff --git a/arch/powerpc/include/asm/kvm_types.h b/arch/powerpc/include/asm/kvm_types.h
> > new file mode 100644
> > index 000000000000..f627eceaa314
> > --- /dev/null
> > +++ b/arch/powerpc/include/asm/kvm_types.h
> > @@ -0,0 +1,5 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef _ASM_POWERPC_KVM_TYPES_H
> > +#define _ASM_POWERPC_KVM_TYPES_H
> > +
> > +#endif /* _ASM_POWERPC_KVM_TYPES_H */
> > diff --git a/arch/s390/include/asm/kvm_types.h b/arch/s390/include/asm/kvm_types.h
> > new file mode 100644
> > index 000000000000..b66a81f8a354
> > --- /dev/null
> > +++ b/arch/s390/include/asm/kvm_types.h
> > @@ -0,0 +1,5 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef _ASM_S390_KVM_TYPES_H
> > +#define _ASM_S390_KVM_TYPES_H
> > +
> > +#endif /* _ASM_S390_KVM_TYPES_H */
> > diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> > index 4660ce90de7f..5c12cba8c2b1 100644
> > --- a/arch/x86/include/asm/kvm_host.h
> > +++ b/arch/x86/include/asm/kvm_host.h
> > @@ -179,8 +179,6 @@ enum {
> >  
> >  #include <asm/kvm_emulate.h>
> >  
> > -#define KVM_NR_MEM_OBJS 40
> > -
> >  #define KVM_NR_DB_REGS	4
> >  
> >  #define DR6_BD		(1 << 13)
> > @@ -238,15 +236,6 @@ enum {
> >  
> >  struct kvm_kernel_irq_routing_entry;
> >  
> > -/*
> > - * We don't want allocation failures within the mmu code, so we preallocate
> > - * enough memory for a single page fault in a cache.
> > - */
> > -struct kvm_mmu_memory_cache {
> > -	int nobjs;
> > -	void *objects[KVM_NR_MEM_OBJS];
> > -};
> > -
> >  /*
> >   * the pages used as guest page table on soft mmu are tracked by
> >   * kvm_memory_slot.arch.gfn_track which is 16 bits, so the role bits used
> > @@ -600,9 +589,9 @@ struct kvm_vcpu_arch {
> >  	 */
> >  	struct kvm_mmu *walk_mmu;
> >  
> > -	struct kvm_mmu_memory_cache mmu_pte_list_desc_cache;
> > -	struct kvm_mmu_memory_cache mmu_page_cache;
> > -	struct kvm_mmu_memory_cache mmu_page_header_cache;
> > +	struct kvm_mmu_memcache mmu_pte_list_desc_cache;
> > +	struct kvm_mmu_memcache mmu_page_cache;
> > +	struct kvm_mmu_memcache mmu_page_header_cache;
> >  
> >  	/*
> >  	 * QEMU userspace and the guest each have their own FPU state.
> > diff --git a/arch/x86/include/asm/kvm_types.h b/arch/x86/include/asm/kvm_types.h
> > new file mode 100644
> > index 000000000000..f71fe5556b6f
> > --- /dev/null
> > +++ b/arch/x86/include/asm/kvm_types.h
> > @@ -0,0 +1,10 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef _ASM_X86_KVM_TYPES_H
> > +#define _ASM_X86_KVM_TYPES_H
> > +
> > +#define KVM_NR_MEM_OBJS 40
> > +
> > +#define KVM_MMU_CACHE_GFP	GFP_KERNEL
> > +#define KVM_MMU_CACHE_PAGE_GFP	GFP_KERNEL_ACCOUNT
> > +
> > +#endif /* _ASM_X86_KVM_TYPES_H */
> > diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
> > index ce770b446238..cf0024849404 100644
> > --- a/arch/x86/kvm/mmu.c
> > +++ b/arch/x86/kvm/mmu.c
> > @@ -951,94 +951,35 @@ static void walk_shadow_page_lockless_end(struct kvm_vcpu *vcpu)
> >  	local_irq_enable();
> >  }
> >  
> > -static int mmu_topup_memory_cache(struct kvm_mmu_memory_cache *cache,
> > -				  struct kmem_cache *base_cache, int min)
> > -{
> > -	void *obj;
> > -
> > -	if (cache->nobjs >= min)
> > -		return 0;
> > -	while (cache->nobjs < ARRAY_SIZE(cache->objects)) {
> > -		obj = kmem_cache_zalloc(base_cache, GFP_KERNEL);
> > -		if (!obj)
> > -			return cache->nobjs >= min ? 0 : -ENOMEM;
> > -		cache->objects[cache->nobjs++] = obj;
> > -	}
> > -	return 0;
> > -}
> > -
> > -static int mmu_memory_cache_free_objects(struct kvm_mmu_memory_cache *cache)
> > -{
> > -	return cache->nobjs;
> > -}
> > -
> > -static void mmu_free_memory_cache(struct kvm_mmu_memory_cache *mc,
> > -				  struct kmem_cache *cache)
> > -{
> > -	while (mc->nobjs)
> > -		kmem_cache_free(cache, mc->objects[--mc->nobjs]);
> > -}
> > -
> > -static int mmu_topup_memory_cache_page(struct kvm_mmu_memory_cache *cache,
> > -				       int min)
> > -{
> > -	void *page;
> > -
> > -	if (cache->nobjs >= min)
> > -		return 0;
> > -	while (cache->nobjs < ARRAY_SIZE(cache->objects)) {
> > -		page = (void *)__get_free_page(GFP_KERNEL_ACCOUNT);
> > -		if (!page)
> > -			return cache->nobjs >= min ? 0 : -ENOMEM;
> > -		cache->objects[cache->nobjs++] = page;
> > -	}
> > -	return 0;
> > -}
> > -
> > -static void mmu_free_memory_cache_page(struct kvm_mmu_memory_cache *mc)
> > -{
> > -	while (mc->nobjs)
> > -		free_page((unsigned long)mc->objects[--mc->nobjs]);
> > -}
> > -
> > -static int mmu_topup_memory_caches(struct kvm_vcpu *vcpu)
> > +static int mmu_topup_memcaches(struct kvm_vcpu *vcpu)
> >  {
> >  	int r;
> >  
> > -	r = mmu_topup_memory_cache(&vcpu->arch.mmu_pte_list_desc_cache,
> > +	r = kvm_mmu_topup_memcache(&vcpu->arch.mmu_pte_list_desc_cache,
> >  				   pte_list_desc_cache, 8 + PTE_PREFETCH_NUM);
> >  	if (r)
> >  		goto out;
> > -	r = mmu_topup_memory_cache_page(&vcpu->arch.mmu_page_cache, 8);
> > +	r = kvm_mmu_topup_memcache_page(&vcpu->arch.mmu_page_cache, 8);
> >  	if (r)
> >  		goto out;
> > -	r = mmu_topup_memory_cache(&vcpu->arch.mmu_page_header_cache,
> > +	r = kvm_mmu_topup_memcache(&vcpu->arch.mmu_page_header_cache,
> >  				   mmu_page_header_cache, 4);
> >  out:
> >  	return r;
> >  }
> >  
> > -static void mmu_free_memory_caches(struct kvm_vcpu *vcpu)
> > +static void mmu_free_memcaches(struct kvm_vcpu *vcpu)
> >  {
> > -	mmu_free_memory_cache(&vcpu->arch.mmu_pte_list_desc_cache,
> > +	kvm_mmu_free_memcache(&vcpu->arch.mmu_pte_list_desc_cache,
> >  				pte_list_desc_cache);
> > -	mmu_free_memory_cache_page(&vcpu->arch.mmu_page_cache);
> > -	mmu_free_memory_cache(&vcpu->arch.mmu_page_header_cache,
> > +	kvm_mmu_free_memcache_page(&vcpu->arch.mmu_page_cache);
> > +	kvm_mmu_free_memcache(&vcpu->arch.mmu_page_header_cache,
> >  				mmu_page_header_cache);
> >  }
> >  
> > -static void *mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc)
> > -{
> > -	void *p;
> > -
> > -	BUG_ON(!mc->nobjs);
> > -	p = mc->objects[--mc->nobjs];
> > -	return p;
> > -}
> > -
> >  static struct pte_list_desc *mmu_alloc_pte_list_desc(struct kvm_vcpu *vcpu)
> >  {
> > -	return mmu_memory_cache_alloc(&vcpu->arch.mmu_pte_list_desc_cache);
> > +	return kvm_mmu_memcache_alloc(&vcpu->arch.mmu_pte_list_desc_cache);
> >  }
> >  
> >  static void mmu_free_pte_list_desc(struct pte_list_desc *pte_list_desc)
> > @@ -1358,10 +1299,10 @@ static struct kvm_rmap_head *gfn_to_rmap(struct kvm *kvm, gfn_t gfn,
> >  
> >  static bool rmap_can_add(struct kvm_vcpu *vcpu)
> >  {
> > -	struct kvm_mmu_memory_cache *cache;
> > +	struct kvm_mmu_memcache *cache;
> >  
> >  	cache = &vcpu->arch.mmu_pte_list_desc_cache;
> > -	return mmu_memory_cache_free_objects(cache);
> > +	return kvm_mmu_memcache_free_objects(cache);
> >  }
> >  
> >  static int rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn)
> > @@ -2044,10 +1985,10 @@ static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, int direct
> >  {
> >  	struct kvm_mmu_page *sp;
> >  
> > -	sp = mmu_memory_cache_alloc(&vcpu->arch.mmu_page_header_cache);
> > -	sp->spt = mmu_memory_cache_alloc(&vcpu->arch.mmu_page_cache);
> > +	sp = kvm_mmu_memcache_alloc(&vcpu->arch.mmu_page_header_cache);
> > +	sp->spt = kvm_mmu_memcache_alloc(&vcpu->arch.mmu_page_cache);
> >  	if (!direct)
> > -		sp->gfns = mmu_memory_cache_alloc(&vcpu->arch.mmu_page_cache);
> > +		sp->gfns = kvm_mmu_memcache_alloc(&vcpu->arch.mmu_page_cache);
> >  	set_page_private(virt_to_page(sp->spt), (unsigned long)sp);
> >  
> >  	/*
> > @@ -3961,7 +3902,7 @@ static int nonpaging_page_fault(struct kvm_vcpu *vcpu, gva_t gva,
> >  	if (page_fault_handle_page_track(vcpu, error_code, gfn))
> >  		return RET_PF_EMULATE;
> >  
> > -	r = mmu_topup_memory_caches(vcpu);
> > +	r = mmu_topup_memcaches(vcpu);
> >  	if (r)
> >  		return r;
> >  
> > @@ -4090,7 +4031,7 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
> >  	if (page_fault_handle_page_track(vcpu, error_code, gfn))
> >  		return RET_PF_EMULATE;
> >  
> > -	r = mmu_topup_memory_caches(vcpu);
> > +	r = mmu_topup_memcaches(vcpu);
> >  	if (r)
> >  		return r;
> >  
> > @@ -5062,7 +5003,7 @@ int kvm_mmu_load(struct kvm_vcpu *vcpu)
> >  {
> >  	int r;
> >  
> > -	r = mmu_topup_memory_caches(vcpu);
> > +	r = mmu_topup_memcaches(vcpu);
> >  	if (r)
> >  		goto out;
> >  	r = mmu_alloc_roots(vcpu);
> > @@ -5240,7 +5181,7 @@ static void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
> >  	 * or not since pte prefetch is skiped if it does not have
> >  	 * enough objects in the cache.
> >  	 */
> > -	mmu_topup_memory_caches(vcpu);
> > +	mmu_topup_memcaches(vcpu);
> >  
> >  	spin_lock(&vcpu->kvm->mmu_lock);
> >  
> > @@ -6052,7 +5993,7 @@ void kvm_mmu_destroy(struct kvm_vcpu *vcpu)
> >  {
> >  	kvm_mmu_unload(vcpu);
> >  	free_mmu_pages(vcpu);
> > -	mmu_free_memory_caches(vcpu);
> > +	mmu_free_memcaches(vcpu);
> >  }
> >  
> >  void kvm_mmu_module_exit(void)
> > diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h
> > index 6bdca39829bc..c32b603639a9 100644
> > --- a/arch/x86/kvm/paging_tmpl.h
> > +++ b/arch/x86/kvm/paging_tmpl.h
> > @@ -747,7 +747,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,
> >  
> >  	pgprintk("%s: addr %lx err %x\n", __func__, addr, error_code);
> >  
> > -	r = mmu_topup_memory_caches(vcpu);
> > +	r = mmu_topup_memcaches(vcpu);
> >  	if (r)
> >  		return r;
> >  
> > @@ -870,7 +870,7 @@ static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva, hpa_t root_hpa)
> >  	 * No need to check return value here, rmap_can_add() can
> >  	 * help us to skip pte prefetch later.
> >  	 */
> > -	mmu_topup_memory_caches(vcpu);
> > +	mmu_topup_memcaches(vcpu);
> >  
> >  	if (!VALID_PAGE(root_hpa)) {
> >  		WARN_ON(1);
> > diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
> > index c38cc5eb7e73..f5768e5d33f7 100644
> > --- a/include/linux/kvm_host.h
> > +++ b/include/linux/kvm_host.h
> > @@ -739,6 +739,15 @@ void kvm_vcpu_on_spin(struct kvm_vcpu *vcpu, bool usermode_vcpu_not_eligible);
> >  void kvm_flush_remote_tlbs(struct kvm *kvm);
> >  void kvm_reload_remote_mmus(struct kvm *kvm);
> >  
> > +int kvm_mmu_topup_memcache(struct kvm_mmu_memcache *cache,
> > +			   struct kmem_cache *base_cache, int min);
> > +int kvm_mmu_memcache_free_objects(struct kvm_mmu_memcache *cache);
> > +void kvm_mmu_free_memcache(struct kvm_mmu_memcache *mc,
> > +			   struct kmem_cache *cache);
> > +int kvm_mmu_topup_memcache_page(struct kvm_mmu_memcache *cache, int min);
> > +void kvm_mmu_free_memcache_page(struct kvm_mmu_memcache *mc);
> > +void *kvm_mmu_memcache_alloc(struct kvm_mmu_memcache *mc);
> > +
> >  bool kvm_make_vcpus_request_mask(struct kvm *kvm, unsigned int req,
> >  				 unsigned long *vcpu_bitmap, cpumask_var_t tmp);
> >  bool kvm_make_all_cpus_request(struct kvm *kvm, unsigned int req);
> > diff --git a/include/linux/kvm_types.h b/include/linux/kvm_types.h
> > index 8bf259dae9f6..a314f0f08e22 100644
> > --- a/include/linux/kvm_types.h
> > +++ b/include/linux/kvm_types.h
> > @@ -32,6 +32,7 @@ struct kvm_memslots;
> >  
> >  enum kvm_mr_change;
> >  
> > +#include <asm/kvm_types.h>
> >  #include <asm/types.h>
> >  
> >  /*
> > @@ -63,4 +64,15 @@ struct gfn_to_hva_cache {
> >  	struct kvm_memory_slot *memslot;
> >  };
> >  
> > +#ifdef KVM_NR_MEM_OBJS
> 
> While you're renaming the functions, would it make sense to also rename
> the define, e.g. KVM_MMU_NR_MEMCACHE_OBJS, or maybe something a little
> less verbose.
> 
> > +/*
> > + * We don't want allocation failures within the mmu code, so we preallocate
> > + * enough memory for a single page fault in a cache.
> > + */
> > +struct kvm_mmu_memcache {
> 
> Wrapping the kvm_mmu_memcache definition in KVM_NR_MEM_OBJS will break
> compilation on any arch that doesn't define KVM_NR_MEM_OBJS.
> 

Yes, this is obviously broken.

I'll respin with your comments addressed.

Thanks,

    Christoffer



[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux