[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v3 1/2] x86/mem-sharing: Bulk mem-sharing entire domains
Currently mem-sharing can be performed on a page-by-page base from the control domain. However, when completely deduplicating (cloning) a VM, this requires at least 3 hypercalls per page. As the user has to loop through all pages up to max_gpfn, this process is very slow and wasteful. This patch introduces a new mem_sharing memop for bulk deduplication where the user doesn't have to separately nominate each page in both the source and destination domain, and the looping over all pages happen in the hypervisor. This significantly reduces the overhead of completely deduplicating entire domains. Signed-off-by: Tamas K Lengyel <tamas@xxxxxxxxxxxxx> Acked-by: Wei Liu <wei.liu2@xxxxxxxxxx> --- Ian Jackson <ian.jackson@xxxxxxxxxxxxx> George Dunlap <george.dunlap@xxxxxxxxxxxxx> Jan Beulich <jbeulich@xxxxxxxx> Andrew Cooper <andrew.cooper3@xxxxxxxxxx> v3: Bail if domains are not paused Rename bulk_share struct to just bulk Return -ENOMEM error if nomination fails Return total number of shared pages (not keeping separate count) v2: Stash hypercall continuation start point in xen_mem_sharing_op_t Return number of successfully shared pages in xen_mem_sharing_op_t --- tools/libxc/include/xenctrl.h | 15 +++++++ tools/libxc/xc_memshr.c | 19 ++++++++ xen/arch/x86/mm/mem_sharing.c | 100 ++++++++++++++++++++++++++++++++++++++++++ xen/include/public/memory.h | 14 +++++- 4 files changed, 147 insertions(+), 1 deletion(-) diff --git a/tools/libxc/include/xenctrl.h b/tools/libxc/include/xenctrl.h index dc54612..29ff13e 100644 --- a/tools/libxc/include/xenctrl.h +++ b/tools/libxc/include/xenctrl.h @@ -2327,6 +2327,21 @@ int xc_memshr_add_to_physmap(xc_interface *xch, domid_t client_domain, unsigned long client_gfn); +/* Allows to deduplicate the entire memory of a client domain in bulk. Using + * this function is equivalent of calling xc_memshr_nominate_gfn for each gfn + * in the two domains followed by xc_memshr_share_gfns. If successfull, + * returns the number of shared pages in 'shared'. Both domains must be paused. + * + * May fail with -EINVAL if the source and client domain have different + * memory size or if memory sharing is not enabled on either of the domains. + * May also fail with -ENOMEM if there isn't enough memory available to store + * the sharing metadata before deduplication can happen. + */ +int xc_memshr_bulk_share(xc_interface *xch, + domid_t source_domain, + domid_t client_domain, + uint64_t *shared); + /* Debug calls: return the number of pages referencing the shared frame backing * the input argument. Should be one or greater. * diff --git a/tools/libxc/xc_memshr.c b/tools/libxc/xc_memshr.c index deb0aa4..71350d2 100644 --- a/tools/libxc/xc_memshr.c +++ b/tools/libxc/xc_memshr.c @@ -181,6 +181,25 @@ int xc_memshr_add_to_physmap(xc_interface *xch, return xc_memshr_memop(xch, source_domain, &mso); } +int xc_memshr_bulk_share(xc_interface *xch, + domid_t source_domain, + domid_t client_domain, + uint64_t *shared) +{ + int rc; + xen_mem_sharing_op_t mso; + + memset(&mso, 0, sizeof(mso)); + + mso.op = XENMEM_sharing_op_bulk_share; + mso.u.bulk.client_domain = client_domain; + + rc = xc_memshr_memop(xch, source_domain, &mso); + if ( !rc && shared ) *shared = mso.u.bulk.shared; + + return rc; +} + int xc_memshr_domain_resume(xc_interface *xch, domid_t domid) { diff --git a/xen/arch/x86/mm/mem_sharing.c b/xen/arch/x86/mm/mem_sharing.c index a522423..06176aa 100644 --- a/xen/arch/x86/mm/mem_sharing.c +++ b/xen/arch/x86/mm/mem_sharing.c @@ -1294,6 +1294,43 @@ int relinquish_shared_pages(struct domain *d) return rc; } +static int bulk_share(struct domain *d, struct domain *cd, unsigned long max, + struct mem_sharing_op_bulk *bulk) +{ + int rc; + shr_handle_t sh, ch; + + while( bulk->start <= max ) + { + rc = mem_sharing_nominate_page(d, bulk->start, 0, &sh); + if ( rc == -ENOMEM ) + break; + if ( !rc ) + { + rc = mem_sharing_nominate_page(cd, bulk->start, 0, &ch); + if ( rc == -ENOMEM ) + break; + if ( !rc ) + mem_sharing_share_pages(d, bulk->start, sh, cd, bulk->start, ch); + } + + ++(bulk->start); + + /* Check for continuation if it's not the last iteration. */ + if ( bulk->start < max && hypercall_preempt_check() ) + { + rc = 1; + break; + } + } + + /* We only propagate -ENOMEM so reset rc here */ + if ( rc < 0 && rc != -ENOMEM ) + rc = 0; + + return rc; +} + int mem_sharing_memop(XEN_GUEST_HANDLE_PARAM(xen_mem_sharing_op_t) arg) { int rc; @@ -1468,6 +1505,69 @@ int mem_sharing_memop(XEN_GUEST_HANDLE_PARAM(xen_mem_sharing_op_t) arg) } break; + case XENMEM_sharing_op_bulk_share: + { + unsigned long max_sgfn, max_cgfn; + struct domain *cd; + + rc = -EINVAL; + if ( !mem_sharing_enabled(d) ) + goto out; + + rc = rcu_lock_live_remote_domain_by_id(mso.u.bulk.client_domain, + &cd); + if ( rc ) + goto out; + + rc = xsm_mem_sharing_op(XSM_DM_PRIV, d, cd, mso.op); + if ( rc ) + { + rcu_unlock_domain(cd); + goto out; + } + + if ( !mem_sharing_enabled(cd) ) + { + rcu_unlock_domain(cd); + rc = -EINVAL; + goto out; + } + + if ( !atomic_read(&d->pause_count) || + !atomic_read(&cd->pause_count) ) + { + rcu_unlock_domain(cd); + rc = -EINVAL; + goto out; + } + + max_sgfn = domain_get_maximum_gpfn(d); + max_cgfn = domain_get_maximum_gpfn(cd); + + if ( max_sgfn != max_cgfn || max_sgfn < mso.u.bulk.start ) + { + rcu_unlock_domain(cd); + rc = -EINVAL; + goto out; + } + + rc = bulk_share(d, cd, max_sgfn, &mso.u.bulk); + if ( rc > 0 ) + { + if ( __copy_to_guest(arg, &mso, 1) ) + rc = -EFAULT; + else + rc = hypercall_create_continuation(__HYPERVISOR_memory_op, + "lh", XENMEM_sharing_op, + arg); + } + else + mso.u.bulk.shared = atomic_read(&cd->shr_pages); + + rcu_unlock_domain(cd); + } + break; + case XENMEM_sharing_op_debug_gfn: { unsigned long gfn = mso.u.debug.u.gfn; diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h index fe52ee1..202250a 100644 --- a/xen/include/public/memory.h +++ b/xen/include/public/memory.h @@ -453,6 +453,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_mem_access_op_t); #define XENMEM_sharing_op_debug_gref 5 #define XENMEM_sharing_op_add_physmap 6 #define XENMEM_sharing_op_audit 7 +#define XENMEM_sharing_op_bulk_share 8 #define XENMEM_SHARING_OP_S_HANDLE_INVALID (-10) #define XENMEM_SHARING_OP_C_HANDLE_INVALID (-9) @@ -488,7 +489,18 @@ struct xen_mem_sharing_op { uint64_aligned_t client_gfn; /* IN: the client gfn */ uint64_aligned_t client_handle; /* IN: handle to the client page */ domid_t client_domain; /* IN: the client domain id */ - } share; + } share; + struct mem_sharing_op_bulk { /* OP_BULK_SHARE */ + uint64_aligned_t start; /* IN: start gfn. Set to 0 for + full deduplication. Field is + used internally and may change + when the hypercall returns. */ + uint64_aligned_t shared; /* OUT: the number of gfns + that are shared after this + operation including pages + already shared before */ + domid_t client_domain; /* IN: the client domain id */ + } bulk; struct mem_sharing_op_debug { /* OP_DEBUG_xxx */ union { uint64_aligned_t gfn; /* IN: gfn to debug */ -- 2.8.1 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |