[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 2/3] x86/mem_sharing: replace use of page_lock/unlock with our own lock
The page_lock/unlock functions were designed to be working with PV pagetable updates. It's recycled use in mem_sharing is somewhat odd and results in unecessary complications. Replace it with a separate per-page lock. Signed-off-by: Tamas K Lengyel <tamas@xxxxxxxxxxxxx> Cc: George Dunlap <george.dunlap@xxxxxxxxxxxxx> Cc: Jan Beulich <jbeulich@xxxxxxxx> Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> Cc: Wei Liu <wei.liu2@xxxxxxxxxx> Cc: Roger Pau Monne <roger.pau@xxxxxxxxxx> --- xen/arch/x86/mm/mem_sharing.c | 138 ++++++++++++++-------------------- xen/include/asm-x86/mm.h | 5 +- 2 files changed, 60 insertions(+), 83 deletions(-) diff --git a/xen/arch/x86/mm/mem_sharing.c b/xen/arch/x86/mm/mem_sharing.c index dfc279d371..f63fe9a415 100644 --- a/xen/arch/x86/mm/mem_sharing.c +++ b/xen/arch/x86/mm/mem_sharing.c @@ -57,7 +57,7 @@ static DEFINE_PER_CPU(pg_lock_data_t, __pld); #define RMAP_HASHTAB_SIZE \ ((PAGE_SIZE << RMAP_HASHTAB_ORDER) / sizeof(struct list_head)) #define RMAP_USES_HASHTAB(page) \ - ((page)->sharing->hash_table.flag == NULL) + ((page)->sharing.info->hash_table.flag == NULL) #define RMAP_HEAVY_SHARED_PAGE RMAP_HASHTAB_SIZE /* A bit of hysteresis. We don't want to be mutating between list and hash * table constantly. */ @@ -77,9 +77,9 @@ static void _free_pg_shared_info(struct rcu_head *head) static inline void audit_add_list(struct page_info *page) { - INIT_LIST_HEAD(&page->sharing->entry); + INIT_LIST_HEAD(&page->sharing.info->entry); spin_lock(&shr_audit_lock); - list_add_rcu(&page->sharing->entry, &shr_audit_list); + list_add_rcu(&page->sharing.info->entry, &shr_audit_list); spin_unlock(&shr_audit_lock); } @@ -88,14 +88,14 @@ static inline void page_sharing_dispose(struct page_info *page) { /* Unlikely given our thresholds, but we should be careful. */ if ( unlikely(RMAP_USES_HASHTAB(page)) ) - free_xenheap_pages(page->sharing->hash_table.bucket, - RMAP_HASHTAB_ORDER); + free_xenheap_pages(page->sharing.info->hash_table.bucket, + RMAP_HASHTAB_ORDER); spin_lock(&shr_audit_lock); - list_del_rcu(&page->sharing->entry); + list_del_rcu(&page->sharing.info->entry); spin_unlock(&shr_audit_lock); - INIT_RCU_HEAD(&page->sharing->rcu_head); - call_rcu(&page->sharing->rcu_head, _free_pg_shared_info); + INIT_RCU_HEAD(&page->sharing.info->rcu_head); + call_rcu(&page->sharing.info->rcu_head, _free_pg_shared_info); } #else @@ -105,27 +105,22 @@ static inline void page_sharing_dispose(struct page_info *page) { /* Unlikely given our thresholds, but we should be careful. */ if ( unlikely(RMAP_USES_HASHTAB(page)) ) - free_xenheap_pages(page->sharing->hash_table.bucket, - RMAP_HASHTAB_ORDER); - xfree(page->sharing); + free_xenheap_pages(page->sharing.info->hash_table.bucket, + RMAP_HASHTAB_ORDER); + xfree(page->sharing.info); } #endif /* MEM_SHARING_AUDIT */ -static inline int mem_sharing_page_lock(struct page_info *pg) +static inline void mem_sharing_page_lock(struct page_info *pg) { - int rc; pg_lock_data_t *pld = &(this_cpu(__pld)); page_sharing_mm_pre_lock(); - rc = page_lock(pg); - if ( rc ) - { - preempt_disable(); - page_sharing_mm_post_lock(&pld->mm_unlock_level, - &pld->recurse_count); - } - return rc; + write_lock(&pg->sharing.lock); + preempt_disable(); + page_sharing_mm_post_lock(&pld->mm_unlock_level, + &pld->recurse_count); } static inline void mem_sharing_page_unlock(struct page_info *pg) @@ -135,7 +130,7 @@ static inline void mem_sharing_page_unlock(struct page_info *pg) page_sharing_mm_unlock(pld->mm_unlock_level, &pld->recurse_count); preempt_enable(); - page_unlock(pg); + write_unlock(&pg->sharing.lock); } static inline shr_handle_t get_next_handle(void) @@ -172,7 +167,7 @@ static inline void rmap_init(struct page_info *page) { /* We always start off as a doubly linked list. */ - INIT_LIST_HEAD(&page->sharing->gfns); + INIT_LIST_HEAD(&page->sharing.info->gfns); } /* Exceedingly simple "hash function" */ @@ -194,7 +189,7 @@ rmap_list_to_hash_table(struct page_info *page) for ( i = 0; i < RMAP_HASHTAB_SIZE; i++ ) INIT_LIST_HEAD(b + i); - list_for_each_safe(pos, tmp, &page->sharing->gfns) + list_for_each_safe(pos, tmp, &page->sharing.info->gfns) { gfn_info_t *gfn_info = list_entry(pos, gfn_info_t, list); struct list_head *bucket = b + HASH(gfn_info->domain, gfn_info->gfn); @@ -202,8 +197,8 @@ rmap_list_to_hash_table(struct page_info *page) list_add(pos, bucket); } - page->sharing->hash_table.bucket = b; - page->sharing->hash_table.flag = NULL; + page->sharing.info->hash_table.bucket = b; + page->sharing.info->hash_table.flag = NULL; return 0; } @@ -212,9 +207,9 @@ static inline void rmap_hash_table_to_list(struct page_info *page) { unsigned int i; - struct list_head *bucket = page->sharing->hash_table.bucket; + struct list_head *bucket = page->sharing.info->hash_table.bucket; - INIT_LIST_HEAD(&page->sharing->gfns); + INIT_LIST_HEAD(&page->sharing.info->gfns); for ( i = 0; i < RMAP_HASHTAB_SIZE; i++ ) { @@ -222,7 +217,7 @@ rmap_hash_table_to_list(struct page_info *page) list_for_each_safe(pos, tmp, head) { list_del(pos); - list_add(pos, &page->sharing->gfns); + list_add(pos, &page->sharing.info->gfns); } } @@ -268,9 +263,9 @@ rmap_add(gfn_info_t *gfn_info, struct page_info *page) (void)rmap_list_to_hash_table(page); head = (RMAP_USES_HASHTAB(page)) ? - page->sharing->hash_table.bucket + + page->sharing.info->hash_table.bucket + HASH(gfn_info->domain, gfn_info->gfn) : - &page->sharing->gfns; + &page->sharing.info->gfns; INIT_LIST_HEAD(&gfn_info->list); list_add(&gfn_info->list, head); @@ -284,8 +279,8 @@ rmap_retrieve(uint16_t domain_id, unsigned long gfn, struct list_head *le, *head; head = (RMAP_USES_HASHTAB(page)) ? - page->sharing->hash_table.bucket + HASH(domain_id, gfn) : - &page->sharing->gfns; + page->sharing.info->hash_table.bucket + HASH(domain_id, gfn) : + &page->sharing.info->gfns; list_for_each(le, head) { @@ -322,8 +317,8 @@ static inline void rmap_seed_iterator(struct page_info *page, struct rmap_iterator *ri) { ri->curr = (RMAP_USES_HASHTAB(page)) ? - page->sharing->hash_table.bucket : - &page->sharing->gfns; + page->sharing.info->hash_table.bucket : + &page->sharing.info->gfns; ri->next = ri->curr->next; ri->bucket = 0; } @@ -332,8 +327,8 @@ static inline gfn_info_t * rmap_iterate(struct page_info *page, struct rmap_iterator *ri) { struct list_head *head = (RMAP_USES_HASHTAB(page)) ? - page->sharing->hash_table.bucket + ri->bucket : - &page->sharing->gfns; + page->sharing.info->hash_table.bucket + ri->bucket : + &page->sharing.info->gfns; retry: if ( ri->next == head) @@ -344,7 +339,7 @@ retry: if ( ri->bucket >= RMAP_HASHTAB_SIZE ) /* No more hash table buckets */ return NULL; - head = page->sharing->hash_table.bucket + ri->bucket; + head = page->sharing.info->hash_table.bucket + ri->bucket; ri->curr = head; ri->next = ri->curr->next; goto retry; @@ -398,12 +393,8 @@ static struct page_info* mem_sharing_lookup(unsigned long mfn) struct page_info* page = mfn_to_page(_mfn(mfn)); if ( page_get_owner(page) == dom_cow ) { - /* Count has to be at least two, because we're called - * with the mfn locked (1) and this is supposed to be - * a shared page (1). */ unsigned long t = read_atomic(&page->u.inuse.type_info); ASSERT((t & PGT_type_mask) == PGT_shared_page); - ASSERT((t & PGT_count_mask) >= 2); ASSERT(SHARED_M2P(get_gpfn_from_mfn(mfn))); return page; } @@ -437,14 +428,7 @@ static int audit(void) pg = pg_shared_info->pg; mfn = page_to_mfn(pg); - /* If we can't lock it, it's definitely not a shared page */ - if ( !mem_sharing_page_lock(pg) ) - { - MEM_SHARING_DEBUG("mfn %lx in audit list, but cannot be locked (%lx)!\n", - mfn_x(mfn), pg->u.inuse.type_info); - errors++; - continue; - } + mem_sharing_page_lock(pg); /* Check if the MFN has correct type, owner and handle. */ if ( (pg->u.inuse.type_info & PGT_type_mask) != PGT_shared_page ) @@ -472,7 +456,7 @@ static int audit(void) } /* Check we have a list */ - if ( (!pg->sharing) || !rmap_has_entries(pg) ) + if ( (!pg->sharing.info) || !rmap_has_entries(pg) ) { MEM_SHARING_DEBUG("mfn %lx shared, but empty gfn list!\n", mfn_x(mfn)); @@ -517,8 +501,8 @@ static int audit(void) put_domain(d); nr_gfns++; } - /* The type count has an extra ref because we have locked the page */ - if ( (nr_gfns + 1) != (pg->u.inuse.type_info & PGT_count_mask) ) + + if ( nr_gfns != (pg->u.inuse.type_info & PGT_count_mask) ) { MEM_SHARING_DEBUG("Mismatched counts for MFN=%lx." "nr_gfns in list %lu, in type_info %lx\n", @@ -622,6 +606,7 @@ static int page_make_sharable(struct domain *d, return -E2BIG; } + rwlock_init(&page->sharing.lock); page_set_owner(page, dom_cow); drop_dom_ref = !domain_adjust_tot_pages(d, -1); page_list_del(page, &d->page_list); @@ -648,11 +633,7 @@ static int page_make_private(struct domain *d, struct page_info *page) return -EBUSY; } - /* We can only change the type if count is one */ - /* Because we are locking pages individually, we need to drop - * the lock here, while the page is typed. We cannot risk the - * race of page_unlock and then put_page_type. */ - expected_type = (PGT_shared_page | PGT_validated | PGT_locked | 2); + expected_type = (PGT_shared_page | PGT_validated | 1); if ( page->u.inuse.type_info != expected_type ) { spin_unlock(&d->page_alloc_lock); @@ -688,10 +669,7 @@ static inline struct page_info *__grab_shared_page(mfn_t mfn) return NULL; pg = mfn_to_page(mfn); - /* If the page is not validated we can't lock it, and if it's - * not validated it's obviously not shared. */ - if ( !mem_sharing_page_lock(pg) ) - return NULL; + mem_sharing_page_lock(pg); if ( mem_sharing_lookup(mfn_x(mfn)) == NULL ) { @@ -720,8 +698,7 @@ static int debug_mfn(mfn_t mfn) page->u.inuse.type_info, page_get_owner(page)->domain_id); - /* -1 because the page is locked and that's an additional type ref */ - num_refs = ((int) (page->u.inuse.type_info & PGT_count_mask)) - 1; + num_refs = (int) (page->u.inuse.type_info & PGT_count_mask) ; mem_sharing_page_unlock(page); return num_refs; } @@ -792,7 +769,7 @@ static int nominate_page(struct domain *d, gfn_t gfn, gfn_x(gfn), mfn_x(mfn), d->domain_id); BUG(); } - *phandle = pg->sharing->handle; + *phandle = pg->sharing.info->handle; ret = 0; mem_sharing_page_unlock(pg); goto out; @@ -839,33 +816,30 @@ static int nominate_page(struct domain *d, gfn_t gfn, if ( ret ) goto out; - /* Now that the page is validated, we can lock it. There is no - * race because we're holding the p2m entry, so no one else + /* There is no race because we're holding the p2m entry, so no one else * could be nominating this gfn */ - ret = -ENOENT; - if ( !mem_sharing_page_lock(page) ) - goto out; + mem_sharing_page_lock(page); /* Initialize the shared state */ ret = -ENOMEM; - if ( (page->sharing = + if ( (page->sharing.info = xmalloc(struct page_sharing_info)) == NULL ) { /* Making a page private atomically unlocks it */ BUG_ON(page_make_private(d, page) != 0); goto out; } - page->sharing->pg = page; + page->sharing.info->pg = page; rmap_init(page); /* Create the handle */ - page->sharing->handle = get_next_handle(); + page->sharing.info->handle = get_next_handle(); /* Create the local gfn info */ if ( mem_sharing_gfn_alloc(page, d, gfn_x(gfn)) == NULL ) { - xfree(page->sharing); - page->sharing = NULL; + xfree(page->sharing.info); + page->sharing.info = NULL; BUG_ON(page_make_private(d, page) != 0); goto out; } @@ -879,7 +853,7 @@ static int nominate_page(struct domain *d, gfn_t gfn, /* Update m2p entry to SHARED_M2P_ENTRY */ set_gpfn_from_mfn(mfn_x(mfn), SHARED_M2P_ENTRY); - *phandle = page->sharing->handle; + *phandle = page->sharing.info->handle; audit_add_list(page); mem_sharing_page_unlock(page); ret = 0; @@ -949,14 +923,14 @@ static int share_pages(struct domain *sd, gfn_t sgfn, shr_handle_t sh, ASSERT(cmfn_type == p2m_ram_shared); /* Check that the handles match */ - if ( spage->sharing->handle != sh ) + if ( spage->sharing.info->handle != sh ) { ret = XENMEM_SHARING_OP_S_HANDLE_INVALID; mem_sharing_page_unlock(secondpg); mem_sharing_page_unlock(firstpg); goto err_out; } - if ( cpage->sharing->handle != ch ) + if ( cpage->sharing.info->handle != ch ) { ret = XENMEM_SHARING_OP_C_HANDLE_INVALID; mem_sharing_page_unlock(secondpg); @@ -990,11 +964,11 @@ static int share_pages(struct domain *sd, gfn_t sgfn, shr_handle_t sh, BUG_ON(set_shared_p2m_entry(d, gfn->gfn, smfn)); put_domain(d); } - ASSERT(list_empty(&cpage->sharing->gfns)); + ASSERT(list_empty(&cpage->sharing.info->gfns)); /* Clear the rest of the shared state */ page_sharing_dispose(cpage); - cpage->sharing = NULL; + cpage->sharing.info = NULL; mem_sharing_page_unlock(secondpg); mem_sharing_page_unlock(firstpg); @@ -1037,7 +1011,7 @@ int mem_sharing_add_to_physmap(struct domain *sd, unsigned long sgfn, shr_handle ASSERT(smfn_type == p2m_ram_shared); /* Check that the handles match */ - if ( spage->sharing->handle != sh ) + if ( spage->sharing.info->handle != sh ) goto err_unlock; /* Make sure the target page is a hole in the physmap. These are typically @@ -1155,7 +1129,7 @@ int __mem_sharing_unshare_page(struct domain *d, * before destroying the rmap. */ mem_sharing_gfn_destroy(page, d, gfn_info); page_sharing_dispose(page); - page->sharing = NULL; + page->sharing.info = NULL; atomic_dec(&nr_shared_mfns); } else diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h index 6faa563167..594de6148f 100644 --- a/xen/include/asm-x86/mm.h +++ b/xen/include/asm-x86/mm.h @@ -133,7 +133,10 @@ struct page_info * of sharing share the version they expect to. * This list is allocated and freed when a page is shared/unshared. */ - struct page_sharing_info *sharing; + struct { + struct page_sharing_info *info; + rwlock_t lock; + } sharing; }; /* Reference count and various PGC_xxx flags and fields. */ -- 2.20.1 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |