[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] Add a per-vcpu lock-free cache of mappings to map_domain_page.
# HG changeset patch # User kaf24@xxxxxxxxxxxxxxxxxxxx # Node ID 57c50578414ddabd8064b12826fc13c2a2ed1706 # Parent 1c70b9d8173196c83ebf5d1567ce3b7f4ad9d749 Add a per-vcpu lock-free cache of mappings to map_domain_page. Improve mapping slot search by using a free bitmap. Removed map_domain_pages (multi-page mapping functionality). Noone uses it, nor should they use it in future, Signed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx> diff -r 1c70b9d81731 -r 57c50578414d xen/arch/x86/domain.c --- a/xen/arch/x86/domain.c Wed Jan 11 16:26:04 2006 +++ b/xen/arch/x86/domain.c Wed Jan 11 18:18:21 2006 @@ -288,9 +288,7 @@ #if defined(__i386__) - d->arch.mapcache.l1tab = d->arch.mm_perdomain_pt + - (GDT_LDT_MBYTES << (20 - PAGE_SHIFT)); - spin_lock_init(&d->arch.mapcache.lock); + mapcache_init(d); #else /* __x86_64__ */ diff -r 1c70b9d81731 -r 57c50578414d xen/arch/x86/x86_32/domain_page.c --- a/xen/arch/x86/x86_32/domain_page.c Wed Jan 11 16:26:04 2006 +++ b/xen/arch/x86/x86_32/domain_page.c Wed Jan 11 18:18:21 2006 @@ -20,33 +20,16 @@ #include <asm/flushtlb.h> #include <asm/hardirq.h> -#define MAPCACHE_ORDER 10 -#define MAPCACHE_ENTRIES (1 << MAPCACHE_ORDER) - -/* Use a spare PTE bit to mark entries ready for recycling. */ -#define READY_FOR_TLB_FLUSH (1<<10) - -static void flush_all_ready_maps(void) -{ - struct mapcache *cache = ¤t->domain->arch.mapcache; - unsigned int i; - - for ( i = 0; i < MAPCACHE_ENTRIES; i++ ) - if ( (l1e_get_flags(cache->l1tab[i]) & READY_FOR_TLB_FLUSH) ) - cache->l1tab[i] = l1e_empty(); -} - -void *map_domain_pages(unsigned long pfn, unsigned int order) +void *map_domain_page(unsigned long pfn) { unsigned long va; - unsigned int idx, i, flags, vcpu = current->vcpu_id; + unsigned int idx, i, vcpu = current->vcpu_id; struct domain *d; struct mapcache *cache; -#ifndef NDEBUG - unsigned int flush_count = 0; -#endif + struct vcpu_maphash_entry *hashent; ASSERT(!in_irq()); + perfc_incrc(map_domain_page_count); /* If we are the idle domain, ensure that we run on our own page tables. */ @@ -56,6 +39,16 @@ cache = &d->arch.mapcache; + hashent = &cache->vcpu_maphash[vcpu].hash[MAPHASH_HASHFN(pfn)]; + if ( hashent->pfn == pfn ) + { + idx = hashent->idx; + hashent->refcnt++; + ASSERT(hashent->refcnt != 0); + ASSERT(l1e_get_pfn(cache->l1tab[idx]) == pfn); + goto out; + } + spin_lock(&cache->lock); /* Has some other CPU caused a wrap? We must flush if so. */ @@ -70,45 +63,97 @@ } } - do { - idx = cache->cursor = (cache->cursor + 1) & (MAPCACHE_ENTRIES - 1); - if ( unlikely(idx == 0) ) - { - ASSERT(flush_count++ == 0); - flush_all_ready_maps(); - perfc_incrc(domain_page_tlb_flush); - local_flush_tlb(); - cache->shadow_epoch[vcpu] = ++cache->epoch; - cache->tlbflush_timestamp = tlbflush_current_time(); - } - - flags = 0; - for ( i = 0; i < (1U << order); i++ ) - flags |= l1e_get_flags(cache->l1tab[idx+i]); - } - while ( flags & _PAGE_PRESENT ); - - for ( i = 0; i < (1U << order); i++ ) - cache->l1tab[idx+i] = l1e_from_pfn(pfn+i, __PAGE_HYPERVISOR); + idx = find_next_zero_bit(cache->inuse, MAPCACHE_ENTRIES, cache->cursor); + if ( unlikely(idx >= MAPCACHE_ENTRIES) ) + { + /* /First/, clean the garbage map and update the inuse list. */ + for ( i = 0; i < ARRAY_SIZE(cache->garbage); i++ ) + { + unsigned long x = xchg(&cache->garbage[i], 0); + cache->inuse[i] &= ~x; + } + + /* /Second/, flush TLBs. */ + perfc_incrc(domain_page_tlb_flush); + local_flush_tlb(); + cache->shadow_epoch[vcpu] = ++cache->epoch; + cache->tlbflush_timestamp = tlbflush_current_time(); + + idx = find_first_zero_bit(cache->inuse, MAPCACHE_ENTRIES); + ASSERT(idx < MAPCACHE_ENTRIES); + } + + set_bit(idx, cache->inuse); + cache->cursor = idx + 1; spin_unlock(&cache->lock); + cache->l1tab[idx] = l1e_from_pfn(pfn, __PAGE_HYPERVISOR); + + out: va = MAPCACHE_VIRT_START + (idx << PAGE_SHIFT); return (void *)va; } -void unmap_domain_pages(void *va, unsigned int order) -{ - unsigned int idx, i; +void unmap_domain_page(void *va) +{ + unsigned int idx; struct mapcache *cache = ¤t->domain->arch.mapcache; + unsigned long pfn; + struct vcpu_maphash_entry *hashent; + + ASSERT(!in_irq()); ASSERT((void *)MAPCACHE_VIRT_START <= va); ASSERT(va < (void *)MAPCACHE_VIRT_END); idx = ((unsigned long)va - MAPCACHE_VIRT_START) >> PAGE_SHIFT; - - for ( i = 0; i < (1U << order); i++ ) - l1e_add_flags(cache->l1tab[idx+i], READY_FOR_TLB_FLUSH); + pfn = l1e_get_pfn(cache->l1tab[idx]); + hashent = &cache->vcpu_maphash[current->vcpu_id].hash[MAPHASH_HASHFN(pfn)]; + + if ( hashent->idx == idx ) + { + ASSERT(hashent->pfn == pfn); + ASSERT(hashent->refcnt != 0); + hashent->refcnt--; + } + else if ( hashent->refcnt == 0 ) + { + if ( hashent->idx != MAPHASHENT_NOTINUSE ) + { + /* /First/, zap the PTE. */ + ASSERT(l1e_get_pfn(cache->l1tab[hashent->idx]) == hashent->pfn); + cache->l1tab[hashent->idx] = l1e_empty(); + /* /Second/, mark as garbage. */ + set_bit(hashent->idx, cache->garbage); + } + + /* Add newly-freed mapping to the maphash. */ + hashent->pfn = pfn; + hashent->idx = idx; + } + else + { + /* /First/, zap the PTE. */ + cache->l1tab[idx] = l1e_empty(); + /* /Second/, mark as garbage. */ + set_bit(idx, cache->garbage); + } +} + +void mapcache_init(struct domain *d) +{ + unsigned int i, j; + + d->arch.mapcache.l1tab = d->arch.mm_perdomain_pt + + (GDT_LDT_MBYTES << (20 - PAGE_SHIFT)); + spin_lock_init(&d->arch.mapcache.lock); + + /* Mark all maphash entries as not in use. */ + for ( i = 0; i < MAX_VIRT_CPUS; i++ ) + for ( j = 0; j < MAPHASH_ENTRIES; j++ ) + d->arch.mapcache.vcpu_maphash[i].hash[j].idx = + MAPHASHENT_NOTINUSE; } #define GLOBALMAP_BITS (IOREMAP_MBYTES << (20 - PAGE_SHIFT)) @@ -128,15 +173,10 @@ spin_lock(&globalmap_lock); - for ( ; ; ) - { - idx = find_next_zero_bit(inuse, GLOBALMAP_BITS, inuse_cursor); - va = IOREMAP_VIRT_START + (idx << PAGE_SHIFT); - - /* End of round? If not then we're done in this loop. */ - if ( va < FIXADDR_START ) - break; - + idx = find_next_zero_bit(inuse, GLOBALMAP_BITS, inuse_cursor); + va = IOREMAP_VIRT_START + (idx << PAGE_SHIFT); + if ( unlikely(va >= FIXADDR_START) ) + { /* /First/, clean the garbage map and update the inuse list. */ for ( i = 0; i < ARRAY_SIZE(garbage); i++ ) { @@ -147,7 +187,9 @@ /* /Second/, flush all TLBs to get rid of stale garbage mappings. */ flush_tlb_all(); - inuse_cursor = 0; + idx = find_first_zero_bit(inuse, GLOBALMAP_BITS); + va = IOREMAP_VIRT_START + (idx << PAGE_SHIFT); + ASSERT(va < FIXADDR_START); } set_bit(idx, inuse); diff -r 1c70b9d81731 -r 57c50578414d xen/include/asm-x86/domain.h --- a/xen/include/asm-x86/domain.h Wed Jan 11 16:26:04 2006 +++ b/xen/include/asm-x86/domain.h Wed Jan 11 18:18:21 2006 @@ -13,13 +13,40 @@ unsigned long eip; }; +#define MAPHASH_ENTRIES 8 +#define MAPHASH_HASHFN(pfn) ((pfn) & (MAPHASH_ENTRIES-1)) +#define MAPHASHENT_NOTINUSE ((u16)~0U) +struct vcpu_maphash { + struct vcpu_maphash_entry { + unsigned long pfn; + uint16_t idx; + uint16_t refcnt; + } hash[MAPHASH_ENTRIES]; +} __cacheline_aligned; + +#define MAPCACHE_ORDER 10 +#define MAPCACHE_ENTRIES (1 << MAPCACHE_ORDER) struct mapcache { + /* The PTEs that provide the mappings, and a cursor into the array. */ l1_pgentry_t *l1tab; unsigned int cursor; + + /* Protects map_domain_page(). */ + spinlock_t lock; + + /* Garbage mappings are flushed from TLBs in batches called 'epochs'. */ unsigned int epoch, shadow_epoch[MAX_VIRT_CPUS]; u32 tlbflush_timestamp; - spinlock_t lock; + + /* Which mappings are in use, and which are garbage to reap next epoch? */ + unsigned long inuse[BITS_TO_LONGS(MAPCACHE_ENTRIES)]; + unsigned long garbage[BITS_TO_LONGS(MAPCACHE_ENTRIES)]; + + /* Lock-free per-VCPU hash of recently-used mappings. */ + struct vcpu_maphash vcpu_maphash[MAX_VIRT_CPUS]; }; + +extern void mapcache_init(struct domain *d); struct arch_domain { diff -r 1c70b9d81731 -r 57c50578414d xen/include/xen/domain_page.h --- a/xen/include/xen/domain_page.h Wed Jan 11 16:26:04 2006 +++ b/xen/include/xen/domain_page.h Wed Jan 11 18:18:21 2006 @@ -10,24 +10,19 @@ #include <xen/config.h> #include <xen/mm.h> -#define map_domain_page(pfn) map_domain_pages(pfn,0) -#define unmap_domain_page(va) unmap_domain_pages(va,0) - #ifdef CONFIG_DOMAIN_PAGE /* - * Maps a given range of page frames, returning the mapped virtual address. The - * pages are now accessible within the current VCPU until a corresponding - * call to unmap_domain_page(). + * Map a given page frame, returning the mapped virtual address. The page is + * then accessible within the current VCPU until a corresponding unmap call. */ -extern void *map_domain_pages(unsigned long pfn, unsigned int order); +extern void *map_domain_page(unsigned long pfn); /* - * Pass a VA within the first page of a range previously mapped in the context - * of the currently-executing VCPU via a call to map_domain_pages(). Those - * pages will then be removed from the mapping lists. + * Pass a VA within a page previously mapped in the context of the + * currently-executing VCPU via a call to map_domain_pages(). */ -extern void unmap_domain_pages(void *va, unsigned int order); +extern void unmap_domain_page(void *va); /* * Similar to the above calls, except the mapping is accessible in all @@ -97,8 +92,8 @@ #else /* !CONFIG_DOMAIN_PAGE */ -#define map_domain_pages(pfn,order) phys_to_virt((pfn)<<PAGE_SHIFT) -#define unmap_domain_pages(va,order) ((void)((void)(va),(void)(order))) +#define map_domain_page(pfn) phys_to_virt((pfn)<<PAGE_SHIFT) +#define unmap_domain_page(va) ((void)(va)) #define map_domain_page_global(pfn) phys_to_virt((pfn)<<PAGE_SHIFT) #define unmap_domain_page_global(va) ((void)(va)) _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |