From: Vitaly Kuznetsov <vkuznets@redhat.com>
To: xen-devel@lists.xenproject.org
Cc: Andrew Jones <drjones@redhat.com>,
Julien Grall <julien.grall@linaro.org>,
Keir Fraser <keir@xen.org>,
Ian Campbell <ian.campbell@citrix.com>,
Stefano Stabellini <stefano.stabellini@eu.citrix.com>,
Andrew Cooper <andrew.cooper3@citrix.com>,
Ian Jackson <ian.jackson@eu.citrix.com>,
Olaf Hering <olaf@aepfle.de>, Tim Deegan <tim@xen.org>,
David Vrabel <david.vrabel@citrix.com>,
Jan Beulich <jbeulich@suse.com>, Wei Liu <wei.liu2@citrix.com>,
Daniel De Graaf <dgdegra@tycho.nsa.gov>
Subject: [PATCH v7 04/10] xen: Introduce XEN_DOMCTL_soft_reset
Date: Wed, 27 May 2015 17:25:40 +0200 [thread overview]
Message-ID: <1432740346-7887-5-git-send-email-vkuznets@redhat.com> (raw)
In-Reply-To: <1432740346-7887-1-git-send-email-vkuznets@redhat.com>
New operation reassigns all memory pages from source domain to the destination
domain mapping them at exactly the same GFNs. Pages mapped more than once (e.g.
grants) are being copied.
Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
---
Changes in v7:
- is_soft_reset flag added to struct domain to preserve destination domain's
paused state across possible hypercall preemption.
[Jan Beulich]
- XENMEM_soft_reset -> XEN_DOMCTL_soft_reset
- domain_soft_reset() returns int
- no locking for is_dying as it is now proteced by domctl_lock
- print a warning on !mfn_valid case
- check PGC_allocated for pages
- no print on assign_pages failure (it prints error messages on both its failure paths)
- don't re-read page->count_info, use copy_page flag
- minor stucturing change
- pause both source and destination domain while processing the hypercall
- remove nr_transferred from public interface
[Tim Deegan]
- use get_gfn_type_access() in PoD case (x86-only)
---
xen/common/domain.c | 186 ++++++++++++++++++++++++++++++++++++++++++++
xen/common/domctl.c | 72 +++++++++++++++++
xen/include/public/domctl.h | 28 +++++++
xen/include/xen/sched.h | 6 ++
4 files changed, 292 insertions(+)
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 7825c56..824f325 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -1006,6 +1006,192 @@ int domain_unpause_by_systemcontroller(struct domain *d)
return 0;
}
+int domain_soft_reset(struct domain *source_d, struct domain *dest_d,
+ xen_pfn_t *gmfn_start)
+{
+ int rc = 0;
+ unsigned long mfn, mfn_new, gmfn, last_gmfn, count;
+ unsigned int order;
+ p2m_type_t p2mt;
+ struct page_info *page, *new_page, *next_page;
+ int drop_dom_ref, copy_page;
+
+ last_gmfn = domain_get_maximum_gpfn(source_d);
+ gmfn = *gmfn_start;
+ while ( gmfn <= last_gmfn )
+ {
+ page = get_page_from_gfn(source_d, gmfn, &p2mt, 0);
+ if ( unlikely(page == NULL) )
+ {
+#ifdef CONFIG_X86
+ struct p2m_domain *p2m = p2m_get_hostp2m(source_d);
+ p2m_access_t p2ma;
+ mfn_t mfn_p2m;
+
+ order = 0;
+ mfn_p2m = get_gfn_type_access(p2m, gmfn,
+ &p2mt, &p2ma, 0, &order);
+ if ( p2m_is_pod(p2mt) )
+ {
+ rc = guest_physmap_mark_populate_on_demand(dest_d, gmfn,
+ order);
+ if ( unlikely(rc) )
+ {
+ printk(XENLOG_G_ERR "Failed to mark Dom%d's GFN %lx"
+ " (order: %d) as PoD\n", source_d->domain_id,
+ gmfn, order);
+ goto fail;
+ }
+ }
+ put_gfn(source_d, gmfn);
+ gmfn += 1ul << order;
+#else
+ gmfn++;
+#endif
+ goto preempt_check;
+ }
+
+ mfn = page_to_mfn(page);
+ if ( unlikely(!mfn_valid(mfn)) )
+ {
+ printk(XENLOG_G_WARNING "Dom%d's GFN %lx points to invalid MFN\n",
+ source_d->domain_id, gmfn);
+ put_page(page);
+ gmfn++;
+ goto preempt_check;
+ }
+
+ next_page = page;
+ count = 0;
+ copy_page = 0;
+ while ( next_page && !is_xen_heap_page(next_page) &&
+ page_to_mfn(next_page) == mfn + count )
+ {
+ /*
+ * A normal page is supposed to be allocated and have count_info=2
+ * (1 from the domain and 1 from get_page_from_gfn() above).
+ * We should never see an unallocated page here and a page with
+ * count_info > 2 has to be copied.
+ */
+ if ( unlikely(!(next_page->count_info & PGC_allocated)) )
+ {
+ printk(XENLOG_G_ERR "Dom%d's page for GFN %lx is not"
+ " allocated\n", source_d->domain_id, gmfn);
+ put_page(next_page);
+ rc = -EFAULT;
+ goto fail;
+ }
+ else if ( (next_page->count_info & PGC_count_mask) != 2 )
+ {
+ if ( !count )
+ copy_page = 1;
+ break;
+ }
+
+ count++;
+ drop_dom_ref = 0;
+ spin_lock(&source_d->page_alloc_lock);
+ page_set_owner(next_page, NULL);
+ page_list_del(next_page, &source_d->page_list);
+ source_d->tot_pages--;
+ if ( unlikely(source_d->tot_pages == 0) )
+ drop_dom_ref = 1;
+ spin_unlock(&source_d->page_alloc_lock);
+ put_page(next_page);
+ if ( unlikely(drop_dom_ref) )
+ put_domain(source_d);
+
+ if ( unlikely(assign_pages(dest_d, next_page, 0, 0)) )
+ {
+ printk(XENLOG_G_ERR "Failed to assign Dom%d's page (GFN %lx)"
+ " to Dom %d (tot_pages: %u, max_pages: %u)\n",
+ source_d->domain_id, gmfn, dest_d->domain_id,
+ dest_d->tot_pages, dest_d->max_pages);
+ rc = -EFAULT;
+ goto fail;
+ }
+
+ if ( unlikely(gmfn + count > last_gmfn) )
+ {
+ next_page = NULL;
+ break;
+ }
+
+ next_page = get_page_from_gfn(source_d, gmfn + count, &p2mt, 0);
+ }
+
+ if ( count )
+ {
+ if ( next_page )
+ put_page(next_page);
+ }
+ else if ( unlikely(copy_page) )
+ {
+ new_page = alloc_domheap_page(dest_d, 0);
+ if ( unlikely(new_page == NULL) )
+ {
+ printk(XENLOG_G_ERR "Failed to alloc a page to replace"
+ " Dom%d's GFN %lx (MFN %lx) for Dom %d\n",
+ source_d->domain_id, gmfn, mfn, dest_d->domain_id);
+ rc = -ENOMEM;
+ put_page(page);
+ goto fail;
+ }
+ mfn_new = page_to_mfn(new_page);
+ copy_domain_page(mfn_new, mfn);
+ mfn = mfn_new;
+ put_page(page);
+ if ( unlikely(guest_physmap_add_page(dest_d, gmfn, mfn, 0)) )
+ {
+ printk(XENLOG_G_ERR "Failed to add new GFN %lx"
+ " (MFN %lx) to Dom%d\n",
+ gmfn, mfn, dest_d->domain_id);
+ rc = -EFAULT;
+ goto fail;
+ }
+ gmfn++;
+ }
+ else
+ {
+ put_page(page);
+ gmfn++;
+ goto preempt_check;
+ }
+
+ while ( count )
+ {
+ order = get_order_from_pages(count);
+ if ( (1ul << order) > count )
+ order--;
+
+ guest_physmap_remove_page(source_d, gmfn, mfn, order);
+
+ if ( unlikely(guest_physmap_add_page(dest_d, gmfn, mfn, order)) )
+ {
+ printk(XENLOG_G_ERR "Failed to re-add Dom%d's GFN %lx"
+ " (MFN %lx, order: %u) to Dom%d\n", source_d->domain_id,
+ gmfn, mfn, order, dest_d->domain_id);
+ rc = -EFAULT;
+ goto fail;
+ }
+
+ count -= 1ul << order;
+ gmfn += 1ul << order;
+ mfn += 1ul << order;
+ }
+
+ preempt_check:
+ if ( hypercall_preempt_check() && gmfn <= last_gmfn )
+ {
+ *gmfn_start = gmfn;
+ return -ERESTART;
+ }
+ }
+
+ fail:
+ return rc;
+}
+
int vcpu_reset(struct vcpu *v)
{
struct domain *d = v->domain;
diff --git a/xen/common/domctl.c b/xen/common/domctl.c
index e571e76..2fd21cb 100644
--- a/xen/common/domctl.c
+++ b/xen/common/domctl.c
@@ -703,6 +703,78 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
break;
}
+ case XEN_DOMCTL_soft_reset:
+ {
+ struct domain *dest_d;
+
+ if ( d == current->domain )
+ {
+ ret = -EINVAL;
+ break;
+ }
+
+ dest_d = rcu_lock_domain_by_any_id(op->u.soft_reset.dest_domain);
+ if ( dest_d == NULL )
+ {
+ ret = -ESRCH;
+ break;
+ }
+
+ if ( d == dest_d || dest_d->is_dying || dest_d == current->domain )
+ {
+ ret = -EINVAL;
+ rcu_unlock_domain(dest_d);
+ break;
+ }
+
+ /*
+ * Mark the source domain as dying to prevent further changes of its
+ * mappings. is_dying flag is protected by domctl_lock.
+ */
+ if ( !d->is_dying )
+ {
+ domain_pause(d);
+ d->is_dying = DOMDYING_locked;
+ }
+ else if ( d->is_dying != DOMDYING_locked )
+ {
+ ret = -EINVAL;
+ rcu_unlock_domain(dest_d);
+ break;
+ }
+
+ /*
+ * Destination domain's paused state is preserved across possible
+ * hypercall preemption.
+ */
+ if ( !dest_d->is_soft_reset )
+ {
+ domain_pause(dest_d);
+ dest_d->is_soft_reset = 1;
+ }
+
+ copyback = 1;
+ ret = domain_soft_reset(d, dest_d, &op->u.soft_reset.gmfn_start);
+
+ if ( ret != -ERESTART )
+ {
+ dest_d->is_soft_reset = 0;
+ domain_unpause(dest_d);
+ rcu_unlock_domain(dest_d);
+ }
+ else
+ {
+ rcu_unlock_domain(dest_d);
+ ret = hypercall_create_continuation(
+ __HYPERVISOR_domctl, "h", u_domctl);
+ break;
+ }
+
+ /* Change cmd to support possible preemption of domain_kill() */
+ op->cmd = XEN_DOMCTL_destroydomain;
+ /* fall through */
+ }
+
case XEN_DOMCTL_destroydomain:
ret = domain_kill(d);
if ( ret == -ERESTART )
diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
index 0c0ea4a..8a5db55 100644
--- a/xen/include/public/domctl.h
+++ b/xen/include/public/domctl.h
@@ -1065,6 +1065,32 @@ struct xen_domctl_monitor_op {
typedef struct xen_domctl__op xen_domctl_monitor_op_t;
DEFINE_XEN_GUEST_HANDLE(xen_domctl_monitor_op_t);
+/*
+ * Transfer all memory pages from one domain to the other. Pages are unmapped
+ * from the source domain and mapped at the same GFNs to the destination
+ * domain. The source domain is being destroyed.
+ *
+ * If a particular page is mapped more then once in the source domain a new
+ * empty page is being allocated for the destination domain and the content is
+ * being copied.
+ *
+ * The caller has to be priviliged and it is supposed to set gmfn_start to 0,
+ * this field is required for the hypercall continuation.
+ */
+struct xen_domctl_soft_reset {
+ /*
+ * [IN] Destination domain.
+ */
+ domid_t dest_domain; /* destination domain */
+
+ /*
+ * [IN] Start from GMFN.
+ */
+ xen_pfn_t gmfn_start;
+};
+typedef struct xen_domctl_soft_reset xen_domctl_soft_reset_t;
+DEFINE_XEN_GUEST_HANDLE(xen_domctl_soft_reset_t);
+
struct xen_domctl {
uint32_t cmd;
#define XEN_DOMCTL_createdomain 1
@@ -1140,6 +1166,7 @@ struct xen_domctl {
#define XEN_DOMCTL_setvnumainfo 74
#define XEN_DOMCTL_psr_cmt_op 75
#define XEN_DOMCTL_monitor_op 77
+#define XEN_DOMCTL_soft_reset 78
#define XEN_DOMCTL_gdbsx_guestmemio 1000
#define XEN_DOMCTL_gdbsx_pausevcpu 1001
#define XEN_DOMCTL_gdbsx_unpausevcpu 1002
@@ -1203,6 +1230,7 @@ struct xen_domctl {
struct xen_domctl_vnuma vnuma;
struct xen_domctl_psr_cmt_op psr_cmt_op;
struct xen_domctl_monitor_op monitor_op;
+ struct xen_domctl_soft_reset soft_reset;
uint8_t pad[128];
} u;
};
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index f53b91d..48b8272 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -396,6 +396,9 @@ struct domain
bool_t is_shut_down; /* fully shut down? */
int shutdown_code;
+ /* Destination domain in the middle of soft reset operation */
+ bool_t is_soft_reset;
+
/* If this is not 0, send suspend notification here instead of
* raising DOM_EXC */
evtchn_port_t suspend_evtchn;
@@ -605,6 +608,9 @@ void domain_shutdown(struct domain *d, u8 reason);
void domain_resume(struct domain *d);
void domain_pause_for_debugger(void);
+int domain_soft_reset(struct domain *source_d, struct domain *dest_d,
+ xen_pfn_t *gmfn_start);
+
int vcpu_start_shutdown_deferral(struct vcpu *v);
void vcpu_end_shutdown_deferral(struct vcpu *v);
--
1.9.3
next prev parent reply other threads:[~2015-05-27 15:26 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-05-27 15:25 [PATCH v7 00/10] toolstack-based approach to pvhvm guest kexec Vitaly Kuznetsov
2015-05-27 15:25 ` [PATCH v7 01/10] xen: introduce SHUTDOWN_soft_reset shutdown reason Vitaly Kuznetsov
2015-05-27 15:25 ` [PATCH v7 02/10] libxl: support " Vitaly Kuznetsov
2015-06-02 14:58 ` Ian Campbell
2015-05-27 15:25 ` [PATCH v7 03/10] xen: introduce DOMDYING_locked state Vitaly Kuznetsov
2015-05-27 15:25 ` Vitaly Kuznetsov [this message]
2015-05-28 10:06 ` [PATCH v7 04/10] xen: Introduce XEN_DOMCTL_soft_reset Tim Deegan
2015-05-28 11:56 ` Vitaly Kuznetsov
2015-05-28 12:52 ` Tim Deegan
2015-05-28 13:11 ` Vitaly Kuznetsov
2015-05-28 13:37 ` Tim Deegan
2015-05-28 13:53 ` Vitaly Kuznetsov
2015-05-28 15:02 ` Jan Beulich
2015-05-27 15:25 ` [PATCH v7 05/10] xsm: add XEN_DOMCTL_soft_reset support Vitaly Kuznetsov
2015-05-27 20:22 ` Daniel De Graaf
2015-05-29 16:16 ` Jan Beulich
2015-05-27 15:25 ` [PATCH v7 06/10] libxc: support XEN_DOMCTL_soft_reset operation Vitaly Kuznetsov
2015-06-02 15:00 ` Ian Campbell
2015-05-27 15:25 ` [PATCH v7 07/10] libxc: introduce soft reset for HVM domains Vitaly Kuznetsov
2015-06-02 15:09 ` Ian Campbell
2015-05-27 15:25 ` [PATCH v7 08/10] xl: introduce enum domain_restart_type Vitaly Kuznetsov
2015-06-02 15:11 ` Ian Campbell
2015-05-27 15:25 ` [PATCH v7 09/10] libxc: add XC_DEVICE_MODEL_SAVE_FILE Vitaly Kuznetsov
2015-06-02 15:12 ` Ian Campbell
2015-05-27 15:25 ` [PATCH v7 10/10] (lib)xl: soft reset support Vitaly Kuznetsov
2015-06-02 15:25 ` Ian Campbell
2015-05-28 12:20 ` [PATCH v7 00/10] toolstack-based approach to pvhvm guest kexec Jan Beulich
2015-05-28 12:27 ` Vitaly Kuznetsov
2015-05-28 13:05 ` Jan Beulich
2015-05-28 13:41 ` Vitaly Kuznetsov
2015-06-02 14:58 ` Ian Campbell
2015-06-02 16:26 ` Vitaly Kuznetsov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1432740346-7887-5-git-send-email-vkuznets@redhat.com \
--to=vkuznets@redhat.com \
--cc=andrew.cooper3@citrix.com \
--cc=david.vrabel@citrix.com \
--cc=dgdegra@tycho.nsa.gov \
--cc=drjones@redhat.com \
--cc=ian.campbell@citrix.com \
--cc=ian.jackson@eu.citrix.com \
--cc=jbeulich@suse.com \
--cc=julien.grall@linaro.org \
--cc=keir@xen.org \
--cc=olaf@aepfle.de \
--cc=stefano.stabellini@eu.citrix.com \
--cc=tim@xen.org \
--cc=wei.liu2@citrix.com \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).