New operation sets the 'recipient' domain which will recieve all memory pages from a particular domain and kills the original domain.
Signed-off-by: Vitaly Kuznetsov <vkuzn...@redhat.com> --- xen/common/domain.c | 3 +++ xen/common/domctl.c | 33 +++++++++++++++++++++++++++++++++ xen/common/page_alloc.c | 28 ++++++++++++++++++++++++---- xen/include/public/domctl.h | 15 +++++++++++++++ xen/include/xen/sched.h | 2 ++ 5 files changed, 77 insertions(+), 4 deletions(-) diff --git a/xen/common/domain.c b/xen/common/domain.c index c13a7cf..f26267a 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -825,6 +825,9 @@ static void complete_domain_destroy(struct rcu_head *head) if ( d->target != NULL ) put_domain(d->target); + if ( d->recipient != NULL ) + put_domain(d->recipient); + evtchn_destroy_final(d); radix_tree_destroy(&d->pirq_tree, free_pirq_struct); diff --git a/xen/common/domctl.c b/xen/common/domctl.c index f15dcfe..7e7fb47 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -1177,6 +1177,39 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) } break; + case XEN_DOMCTL_devour: + { + struct domain *recipient_dom; + + if ( !d->recipient ) + { + recipient_dom = get_domain_by_id(op->u.devour.recipient); + if ( recipient_dom == NULL ) + { + ret = -ESRCH; + break; + } + + if ( recipient_dom->tot_pages != 0 ) + { + put_domain(recipient_dom); + ret = -EINVAL; + break; + } + /* + * Make sure no allocation/remapping is ongoing and set is_dying + * flag to prevent such actions in future. + */ + spin_lock(&d->page_alloc_lock); + d->is_dying = DOMDYING_locked; + d->recipient = recipient_dom; + smp_wmb(); /* make sure recipient was set before domain_kill() */ + spin_unlock(&d->page_alloc_lock); + } + ret = domain_kill(d); + } + break; + default: ret = arch_do_domctl(op, d, u_domctl); break; diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c index 7b4092d..7eb4404 100644 --- a/xen/common/page_alloc.c +++ b/xen/common/page_alloc.c @@ -1707,6 +1707,7 @@ void free_domheap_pages(struct page_info *pg, unsigned int order) { struct domain *d = page_get_owner(pg); unsigned int i; + unsigned long mfn, gmfn; bool_t drop_dom_ref; ASSERT(!in_irq()); @@ -1764,13 +1765,32 @@ void free_domheap_pages(struct page_info *pg, unsigned int order) scrub = 1; } - if ( unlikely(scrub) ) - for ( i = 0; i < (1 << order); i++ ) - scrub_one_page(&pg[i]); + if ( !d || !d->recipient || d->recipient->is_dying ) + { + if ( unlikely(scrub) ) + for ( i = 0; i < (1 << order); i++ ) + scrub_one_page(&pg[i]); - free_heap_pages(pg, order); + free_heap_pages(pg, order); + } + else + { + mfn = page_to_mfn(pg); + gmfn = mfn_to_gmfn(d, mfn); + + page_set_owner(pg, NULL); + if ( assign_pages(d->recipient, pg, order, 0) ) + /* assign_pages reports the error by itself */ + goto out; + + if ( guest_physmap_add_page(d->recipient, gmfn, mfn, order) ) + printk(XENLOG_G_INFO + "Failed to add MFN %lx (GFN %lx) to Dom%d's physmap\n", + mfn, gmfn, d->recipient->domain_id); + } } +out: if ( drop_dom_ref ) put_domain(d); } diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index 57e2ed7..871fa5e 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -995,6 +995,19 @@ struct xen_domctl_psr_cmt_op { typedef struct xen_domctl_psr_cmt_op xen_domctl_psr_cmt_op_t; DEFINE_XEN_GUEST_HANDLE(xen_domctl_psr_cmt_op_t); +/* + * XEN_DOMCTL_devour - kills the domain reassigning all of its domheap pages + * to the 'recipient' domain. Pages from xen heap belonging to the domain + * are not copied. Reassigned pages are mapped to the same GMFNs in the + * recipient domain as they were mapped in the original. The recipient domain + * is supposed to not have any domheap pages to avoid MFN-GMFN collisions. + */ +struct xen_domctl_devour { + domid_t recipient; +}; +typedef struct xen_domctl_devour xen_domctl_devour_t; +DEFINE_XEN_GUEST_HANDLE(xen_domctl_devour_t); + struct xen_domctl { uint32_t cmd; #define XEN_DOMCTL_createdomain 1 @@ -1070,6 +1083,7 @@ struct xen_domctl { #define XEN_DOMCTL_setvnumainfo 74 #define XEN_DOMCTL_psr_cmt_op 75 #define XEN_DOMCTL_arm_configure_domain 76 +#define XEN_DOMCTL_devour 77 #define XEN_DOMCTL_gdbsx_guestmemio 1000 #define XEN_DOMCTL_gdbsx_pausevcpu 1001 #define XEN_DOMCTL_gdbsx_unpausevcpu 1002 @@ -1135,6 +1149,7 @@ struct xen_domctl { struct xen_domctl_gdbsx_domstatus gdbsx_domstatus; struct xen_domctl_vnuma vnuma; struct xen_domctl_psr_cmt_op psr_cmt_op; + struct xen_domctl_devour devour; uint8_t pad[128]; } u; }; diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h index a42d0b8..552e4a3 100644 --- a/xen/include/xen/sched.h +++ b/xen/include/xen/sched.h @@ -366,6 +366,8 @@ struct domain bool_t is_privileged; /* Which guest this guest has privileges on */ struct domain *target; + /* Which guest receives freed memory pages */ + struct domain *recipient; /* Is this guest being debugged by dom0? */ bool_t debugger_attached; /* Is this guest dying (i.e., a zombie)? */ -- 1.9.3 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xen.org http://lists.xen.org/xen-devel