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

Re: [Xen-devel] [PATCH] x86/domain_page: implement pure per-vCPU mapping infrastructure



On Mon, Feb 03, 2020 at 06:36:53PM +0000, Hongyan Xia wrote:
[...]
> diff --git a/xen/arch/x86/domain_page.c b/xen/arch/x86/domain_page.c
> index dd32712d2f..52971d2ecc 100644
> --- a/xen/arch/x86/domain_page.c
> +++ b/xen/arch/x86/domain_page.c
> @@ -69,12 +69,11 @@ void __init mapcache_override_current(struct vcpu *v)
>  
>  void *map_domain_page(mfn_t mfn)
>  {
> -    unsigned long flags;
> -    unsigned int idx, i;
> +    unsigned long flags, *phashmfn;
> +    unsigned int idx, glb_idx, *phashidx, ohashidx;
>      struct vcpu *v;
> -    struct mapcache_domain *dcache;
>      struct mapcache_vcpu *vcache;
> -    struct vcpu_maphash_entry *hashent;
> +    void *ret;
>  
>  #ifdef NDEBUG
>      if ( mfn_x(mfn) <= PFN_DOWN(__pa(HYPERVISOR_VIRT_END - 1)) )
> @@ -82,104 +81,59 @@ void *map_domain_page(mfn_t mfn)
>  #endif
>  
>      v = mapcache_current_vcpu();
> -    if ( !v || !is_pv_vcpu(v) )
> +    if ( !v || !is_pv_vcpu(v) || !v->arch.pv.mapcache )
>          return mfn_to_virt(mfn_x(mfn));
>  
> -    dcache = &v->domain->arch.pv.mapcache;
> -    vcache = &v->arch.pv.mapcache;
> -    if ( !dcache->inuse )
> -        return mfn_to_virt(mfn_x(mfn));
> +    vcache = v->arch.pv.mapcache;
> +    phashmfn = &vcache->hash_mfn[MAPHASH_HASHFN(mfn_x(mfn))];
> +    phashidx = &vcache->hash_idx[MAPHASH_HASHFN(mfn_x(mfn))];
>  
>      perfc_incr(map_domain_page_count);
>  
>      local_irq_save(flags);
>  
> -    hashent = &vcache->hash[MAPHASH_HASHFN(mfn_x(mfn))];
> -    if ( hashent->mfn == mfn_x(mfn) )
> +    ohashidx = *phashidx;

This is a bit redundant because you never rewrite *phashidx until the
last minute. I think ohashidx can be removed, but it's up to you.

> +    if ( *phashmfn != mfn_x(mfn) )
>      {
> -        idx = hashent->idx;
> -        ASSERT(idx < dcache->entries);
> -        hashent->refcnt++;
> -        ASSERT(hashent->refcnt);
> -        ASSERT(mfn_eq(l1e_get_mfn(MAPCACHE_L1ENT(idx)), mfn));
> -        goto out;
> -    }
> +        idx = find_first_zero_bit(vcache->inuse, MAPCACHE_VCPU_ENTRIES);
> +        BUG_ON(idx >= MAPCACHE_VCPU_ENTRIES);
>  
> -    spin_lock(&dcache->lock);
> +        ASSERT(vcache->refcnt[idx] == 0);
> +        __set_bit(idx, vcache->inuse);
>  
> -    /* Has some other CPU caused a wrap? We must flush if so. */
[...]
>  void unmap_domain_page(const void *ptr)
>  {
> -    unsigned int idx;
> +    unsigned int idx, glb_idx;
>      struct vcpu *v;
> -    struct mapcache_domain *dcache;
> -    unsigned long va = (unsigned long)ptr, mfn, flags;
> -    struct vcpu_maphash_entry *hashent;
> +    struct mapcache_vcpu *vcache;
> +    unsigned long va = (unsigned long)ptr, mfn, hashmfn, flags;
>  
>      if ( va >= DIRECTMAP_VIRT_START )
>          return;
> @@ -189,73 +143,21 @@ void unmap_domain_page(const void *ptr)
>      v = mapcache_current_vcpu();
>      ASSERT(v && is_pv_vcpu(v));
>  
> -    dcache = &v->domain->arch.pv.mapcache;
> -    ASSERT(dcache->inuse);
> -
> -    idx = PFN_DOWN(va - MAPCACHE_VIRT_START);
> -    mfn = l1e_get_pfn(MAPCACHE_L1ENT(idx));
> -    hashent = &v->arch.pv.mapcache.hash[MAPHASH_HASHFN(mfn)];
> +    vcache = v->arch.pv.mapcache;
> +    ASSERT(vcache);
>  
> +    glb_idx = PFN_DOWN(va - MAPCACHE_VIRT_START);
> +    idx = glb_idx - v->vcpu_id * MAPCACHE_VCPU_ENTRIES;

Add a blank line here please.

>      local_irq_save(flags);
>  
> -    if ( hashent->idx == idx )
> -    {
> -        ASSERT(hashent->mfn == mfn);
> -        ASSERT(hashent->refcnt);
> -        hashent->refcnt--;
> -    }
> -    else if ( !hashent->refcnt )
> -    {
> -        if ( hashent->idx != MAPHASHENT_NOTINUSE )
> -        {
> -            /* /First/, zap the PTE. */
> -            ASSERT(l1e_get_pfn(MAPCACHE_L1ENT(hashent->idx)) ==
> -                   hashent->mfn);
> -            l1e_write(&MAPCACHE_L1ENT(hashent->idx), l1e_empty());
> -            /* /Second/, mark as garbage. */
> -            set_bit(hashent->idx, dcache->garbage);
> -        }
> -
> -        /* Add newly-freed mapping to the maphash. */
> -        hashent->mfn = mfn;
> -        hashent->idx = idx;
> -    }
> -    else
> -    {
> -        /* /First/, zap the PTE. */
> -        l1e_write(&MAPCACHE_L1ENT(idx), l1e_empty());
> -        /* /Second/, mark as garbage. */
> -        set_bit(idx, dcache->garbage);
> -    }
> -
> -    local_irq_restore(flags);
> -}
> -
> -int mapcache_domain_init(struct domain *d)
> -{
> -    struct mapcache_domain *dcache = &d->arch.pv.mapcache;
> -    unsigned int bitmap_pages;
> +    mfn = l1e_get_pfn(MAPCACHE_L1ENT(glb_idx));
> +    hashmfn = vcache->hash_mfn[MAPHASH_HASHFN(mfn)];
>  
> -    ASSERT(is_pv_domain(d));
> +    vcache->refcnt[idx]--;
> +    if ( hashmfn != mfn && !vcache->refcnt[idx] )
> +        __clear_bit(idx, vcache->inuse);
>  
> -#ifdef NDEBUG
> -    if ( !mem_hotplug && max_page <= PFN_DOWN(__pa(HYPERVISOR_VIRT_END - 1)) 
> )
> -        return 0;
> -#endif

Would be great if some form or shape of this check can be added to
mapcache_vcpu_init such that you don't unnecessarily initialise data
structures that will never be used.

> -
> -    BUILD_BUG_ON(MAPCACHE_VIRT_END + PAGE_SIZE * (3 +
> -                 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) 
> >
> -                 MAPCACHE_VIRT_START + (PERDOMAIN_SLOT_MBYTES << 20));
> -    bitmap_pages = PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long));
> -    dcache->inuse = (void *)MAPCACHE_VIRT_END + PAGE_SIZE;
> -    dcache->garbage = dcache->inuse +
> -                      (bitmap_pages + 1) * PAGE_SIZE / sizeof(long);
> -
> -    spin_lock_init(&dcache->lock);
> -
> -    return create_perdomain_mapping(d, (unsigned long)dcache->inuse,
> -                                    2 * bitmap_pages + 1,
> -                                    NIL(l1_pgentry_t *), NULL);
> +    local_irq_restore(flags);
>  }
>  
[...]
> diff --git a/xen/include/asm-x86/config.h b/xen/include/asm-x86/config.h
> index d0cfbb70a8..4b2217151b 100644
> --- a/xen/include/asm-x86/config.h
> +++ b/xen/include/asm-x86/config.h
> @@ -296,7 +296,7 @@ extern unsigned long xen_phys_start;
>      (GDT_VIRT_START(v) + (64*1024))
>  
>  /* map_domain_page() map cache. The second per-domain-mapping sub-area. */
> -#define MAPCACHE_VCPU_ENTRIES    (CONFIG_PAGING_LEVELS * 
> CONFIG_PAGING_LEVELS)
> +#define MAPCACHE_VCPU_ENTRIES    32
>  #define MAPCACHE_ENTRIES         (MAX_VIRT_CPUS * MAPCACHE_VCPU_ENTRIES)
>  #define MAPCACHE_VIRT_START      PERDOMAIN_VIRT_SLOT(1)
>  #define MAPCACHE_VIRT_END        (MAPCACHE_VIRT_START + \
> diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h
> index a3ae5d9a20..367bba7110 100644
> --- a/xen/include/asm-x86/domain.h
> +++ b/xen/include/asm-x86/domain.h
> @@ -40,35 +40,17 @@ struct trap_bounce {
>  #define MAPHASH_HASHFN(pfn) ((pfn) & (MAPHASH_ENTRIES-1))
>  #define MAPHASHENT_NOTINUSE ((u32)~0U)
>  struct mapcache_vcpu {
> -    /* Shadow of mapcache_domain.epoch. */
> -    unsigned int shadow_epoch;
> -
> -    /* Lock-free per-VCPU hash of recently-used mappings. */
> -    struct vcpu_maphash_entry {
> -        unsigned long mfn;
> -        uint32_t      idx;
> -        uint32_t      refcnt;
> -    } hash[MAPHASH_ENTRIES];
> +    unsigned long hash_mfn[MAPHASH_ENTRIES];
> +    uint32_t hash_idx[MAPHASH_ENTRIES];
> +
> +    uint8_t refcnt[MAPCACHE_VCPU_ENTRIES];

What's the reason for breaking vcpu_maphash_entry into three distinct
arrays?

Would it make the code shorter/cleaner if you make everything
MAPCACHE_VCPU_ENTRIES? A vcpu now only needs to manage its own subregion
after all.

> +    unsigned long inuse[BITS_TO_LONGS(MAPCACHE_VCPU_ENTRIES)];
>  };
>  
>  struct mapcache_domain {
> -    /* The number of array entries, and a cursor into the array. */
>      unsigned int entries;
> -    unsigned int cursor;
> -
> -    /* Protects map_domain_page(). */
> -    spinlock_t lock;
> -
> -    /* Garbage mappings are flushed from TLBs in batches called 'epochs'. */
> -    unsigned int epoch;
> -    u32 tlbflush_timestamp;
> -
> -    /* Which mappings are in use, and which are garbage to reap next epoch? 
> */
> -    unsigned long *inuse;
> -    unsigned long *garbage;
>  };
>  
> -int mapcache_domain_init(struct domain *);
>  int mapcache_vcpu_init(struct vcpu *);
>  void mapcache_override_current(struct vcpu *);
>  
> @@ -473,7 +455,7 @@ struct arch_domain
>  struct pv_vcpu
>  {
>      /* map_domain_page() mapping cache. */
> -    struct mapcache_vcpu mapcache;
> +    struct mapcache_vcpu *mapcache;

And why do you need to change this to a pointer? Is it now very large
and causing issue(s)?

Wei.

>  
>      unsigned int vgc_flags;
>  
> -- 
> 2.17.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.