From: Andres Lagar-Cavilla Date: Wed, 18 Apr 2012 12:38:47 +0000 (+0100) Subject: x86/mm/sharing: Clean ups for relinquishing shared pages on destroy X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=11d33c39eb1ef1d906d54eb3196fee60a3517258;p=xen.git x86/mm/sharing: Clean ups for relinquishing shared pages on destroy When a domain is destroyed, its pages are freed in relinquish_resources in a preemptible mode, in the context of a synchronous domctl. P2m entries pointing to shared pages are, however, released during p2m cleanup in an RCU callback, and in non-preemptible mode. This is an O(n) operation for a very large n, which may include actually freeing shared pages for which the domain is the last holder. To improve responsiveness, move this operation to the preemtible portion of domain destruction, during the synchronous domain_kill hypercall. And remove the bulk of the work from the RCU callback. Signed-off-by: Andres Lagar-Cavilla Acked-by: Tim Deegan Committed-by: Tim Deegan --- diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index 79d1ca229f..cd56b0edcc 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -2132,10 +2132,22 @@ int domain_relinquish_resources(struct domain *d) } } - d->arch.relmem = RELMEM_xen; + d->arch.relmem = RELMEM_shared; /* fallthrough */ - /* Relinquish every page of memory. */ + case RELMEM_shared: + + if ( is_hvm_domain(d) ) + { + /* If the domain has shared pages, relinquish them allowing + * for preemption. */ + ret = relinquish_shared_pages(d); + if ( ret ) + return ret; + } + + d->arch.relmem = RELMEM_xen; + /* Fallthrough. Relinquish every page of memory. */ case RELMEM_xen: ret = relinquish_memory(d, &d->xenpage_list, ~0UL); if ( ret ) diff --git a/xen/arch/x86/mm/mem_sharing.c b/xen/arch/x86/mm/mem_sharing.c index 11c4ff62e5..42e8a976e8 100644 --- a/xen/arch/x86/mm/mem_sharing.c +++ b/xen/arch/x86/mm/mem_sharing.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "mm-locks.h" @@ -1034,6 +1035,50 @@ private_page_found: return 0; } +int relinquish_shared_pages(struct domain *d) +{ + int rc = 0; + struct p2m_domain *p2m = p2m_get_hostp2m(d); + unsigned long gfn, count = 0; + + if ( p2m == NULL ) + return 0; + + p2m_lock(p2m); + for (gfn = p2m->next_shared_gfn_to_relinquish; + gfn < p2m->max_mapped_pfn; gfn++ ) + { + p2m_access_t a; + p2m_type_t t; + mfn_t mfn; + if ( atomic_read(&d->shr_pages) == 0 ) + break; + mfn = p2m->get_entry(p2m, gfn, &t, &a, 0, NULL); + if ( mfn_valid(mfn) && (t == p2m_ram_shared) ) + { + /* Does not fail with ENOMEM given the DESTROY flag */ + BUG_ON(__mem_sharing_unshare_page(d, gfn, + MEM_SHARING_DESTROY_GFN)); + /* Clear out the p2m entry so no one else may try to + * unshare */ + p2m->set_entry(p2m, gfn, _mfn(0), PAGE_ORDER_4K, + p2m_invalid, p2m_access_rwx); + count++; + } + + /* Preempt every 2MiB. Arbitrary */ + if ( (count == 512) && hypercall_preempt_check() ) + { + p2m->next_shared_gfn_to_relinquish = gfn + 1; + rc = -EAGAIN; + break; + } + } + + p2m_unlock(p2m); + return rc; +} + int mem_sharing_memop(struct domain *d, xen_mem_sharing_op_t *mec) { int rc = 0; diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c index 68286f7c86..ee66f21e5e 100644 --- a/xen/arch/x86/mm/p2m.c +++ b/xen/arch/x86/mm/p2m.c @@ -371,9 +371,13 @@ void p2m_teardown(struct p2m_domain *p2m) p2m_lock(p2m); #ifdef __x86_64__ + /* Try to unshare any remaining shared p2m entries. Safeguard + * Since relinquish_shared_pages should have done the work. */ for ( gfn=0; gfn < p2m->max_mapped_pfn; gfn++ ) { p2m_access_t a; + if ( atomic_read(&d->shr_pages) == 0 ) + break; mfn = p2m->get_entry(p2m, gfn, &t, &a, 0, NULL); if ( mfn_valid(mfn) && (t == p2m_ram_shared) ) { diff --git a/xen/include/asm-arm/mm.h b/xen/include/asm-arm/mm.h index 503f0d9c46..ea27e4f13e 100644 --- a/xen/include/asm-arm/mm.h +++ b/xen/include/asm-arm/mm.h @@ -251,6 +251,10 @@ int get_page(struct page_info *page, struct domain *domain); static inline void put_gfn(struct domain *d, unsigned long gfn) {} static inline void mem_event_cleanup(struct domain *d) {} +static inline int relinquish_shared_pages(struct domain *d) +{ + return 0; +} #define INVALID_MFN (~0UL) diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h index 17636192ea..aecee68040 100644 --- a/xen/include/asm-x86/domain.h +++ b/xen/include/asm-x86/domain.h @@ -296,6 +296,7 @@ struct arch_domain /* Continuable domain_relinquish_resources(). */ enum { RELMEM_not_started, + RELMEM_shared, RELMEM_xen, RELMEM_l4, RELMEM_l3, diff --git a/xen/include/asm-x86/mem_sharing.h b/xen/include/asm-x86/mem_sharing.h index 5c5d73fe78..1fbccf7cd7 100644 --- a/xen/include/asm-x86/mem_sharing.h +++ b/xen/include/asm-x86/mem_sharing.h @@ -89,9 +89,19 @@ int mem_sharing_domctl(struct domain *d, int mem_sharing_audit(void); void mem_sharing_init(void); +/* Scans the p2m and relinquishes any shared pages, destroying + * those for which this domain holds the final reference. + * Preemptible. + */ +int relinquish_shared_pages(struct domain *d); + #else #define mem_sharing_init() do { } while (0) +static inline int relinquish_shared_pages(struct domain *d) +{ + return 0; +} #endif /* __x86_64__ */ diff --git a/xen/include/asm-x86/p2m.h b/xen/include/asm-x86/p2m.h index 7271b4e1f4..a207e08ac1 100644 --- a/xen/include/asm-x86/p2m.h +++ b/xen/include/asm-x86/p2m.h @@ -260,6 +260,10 @@ struct p2m_domain { /* Highest guest frame that's ever been mapped in the p2m */ unsigned long max_mapped_pfn; + /* When releasing shared gfn's in a preemptible manner, recall where + * to resume the search */ + unsigned long next_shared_gfn_to_relinquish; + /* Populate-on-demand variables * All variables are protected with the pod lock. We cannot rely on * the p2m lock if it's turned into a fine-grained lock.