|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH RFC v2 22/23] xen/mem_paging: add a populate_evicted paging op
From: Joshua Otto <jtotto@xxxxxxxxxxxx>
The paging API presently permits only individual, populated pages to be
evicted, and even then only after a previous nomination op on the
candidate page. This works well at steady-state, but is somewhat
awkward and inefficient for pagers attempting to implement startup
demand-paging for guests: in this case it is necessary to populate all
of the holes in the physmap to be demand-paged, only to then nominate
and immediately evict each page one-by-one.
To permit more efficient startup demand-paging, introduce a new
populate_evicted paging op. Given a batch of gfns, it:
- marks gfns corresponding to phymap holes as paged-out directly
- frees the backing frames of previously-populated gfns, and then marks
them as paged-out directly (skipping the nomination step)
The latter behaviour is needed to fully support postcopy live migration:
a page may be populated only to have its contents subsequently
invalidated by a write at the sender, requiring it to ultimately be
demand-paged anyway.
I measured a reduction in time required to evict a batch of 512k
previously-unpopulated pfns from 8.535s to 1.590s (~5.4x speedup).
Note: as a long-running batching memory op, populate_evicted takes
advantage of the existing pre-emption/continuation hack (encoding the
starting offset into the batch in bits [:6] of the op argument). To
make this work, plumb the cmd argument all the way down through
do_memory_op() -> arch_memory_op() -> subarch_memory_op() ->
mem_paging_memop(), fixing up each switch statement along the way to
use only the MEMOP_CMD bits.
Signed-off-by: Joshua Otto <jtotto@xxxxxxxxxxxx>
---
tools/libxc/include/xenctrl.h | 2 +
tools/libxc/xc_mem_paging.c | 31 ++++++++++++
xen/arch/x86/mm.c | 5 +-
xen/arch/x86/mm/mem_paging.c | 34 ++++++++++++-
xen/arch/x86/mm/p2m.c | 101 +++++++++++++++++++++++++++++++++++++++
xen/arch/x86/x86_64/compat/mm.c | 6 ++-
xen/arch/x86/x86_64/mm.c | 6 ++-
xen/include/asm-x86/mem_paging.h | 3 +-
xen/include/asm-x86/p2m.h | 2 +
xen/include/public/memory.h | 13 +++--
10 files changed, 190 insertions(+), 13 deletions(-)
diff --git a/tools/libxc/include/xenctrl.h b/tools/libxc/include/xenctrl.h
index 1629f41..22992b9 100644
--- a/tools/libxc/include/xenctrl.h
+++ b/tools/libxc/include/xenctrl.h
@@ -1945,6 +1945,8 @@ int xc_mem_paging_resume(xc_interface *xch, domid_t
domain_id);
int xc_mem_paging_nominate(xc_interface *xch, domid_t domain_id,
uint64_t gfn);
int xc_mem_paging_evict(xc_interface *xch, domid_t domain_id, uint64_t gfn);
+int xc_mem_paging_populate_evicted(xc_interface *xch, domid_t domain_id,
+ xen_pfn_t *gfns, uint32_t nr);
int xc_mem_paging_prep(xc_interface *xch, domid_t domain_id, uint64_t gfn);
int xc_mem_paging_load(xc_interface *xch, domid_t domain_id,
uint64_t gfn, void *buffer);
diff --git a/tools/libxc/xc_mem_paging.c b/tools/libxc/xc_mem_paging.c
index f314b08..b0416b6 100644
--- a/tools/libxc/xc_mem_paging.c
+++ b/tools/libxc/xc_mem_paging.c
@@ -116,6 +116,37 @@ int xc_mem_paging_load(xc_interface *xch, domid_t
domain_id,
return rc;
}
+int xc_mem_paging_populate_evicted(xc_interface *xch,
+ domid_t domain_id,
+ xen_pfn_t *gfns,
+ uint32_t nr)
+{
+ DECLARE_HYPERCALL_BOUNCE(gfns, nr * sizeof(*gfns),
+ XC_HYPERCALL_BUFFER_BOUNCE_IN);
+ int rc;
+
+ xen_mem_paging_op_t mpo =
+ {
+ .op = XENMEM_paging_op_populate_evicted,
+ .domain = domain_id,
+ .u = { .batch = { .nr = nr } }
+ };
+
+ if ( xc_hypercall_bounce_pre(xch, gfns) )
+ {
+ PERROR("Could not bounce memory for
XENMEM_paging_op_populate_evicted");
+ return -1;
+ }
+
+ set_xen_guest_handle(mpo.u.batch.gfns, gfns);
+
+ rc = do_memory_op(xch, XENMEM_paging_op, &mpo, sizeof(mpo));
+
+ xc_hypercall_bounce_post(xch, gfns);
+
+ return rc;
+}
+
/*
* Local variables:
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index 77b0af1..bc41bde 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -4955,9 +4955,10 @@ int xenmem_add_to_physmap_one(
long arch_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
- int rc;
+ long rc;
+ int op = cmd & MEMOP_CMD_MASK;
- switch ( cmd )
+ switch ( op )
{
case XENMEM_set_memory_map:
{
diff --git a/xen/arch/x86/mm/mem_paging.c b/xen/arch/x86/mm/mem_paging.c
index e23e26c..8f62f58 100644
--- a/xen/arch/x86/mm/mem_paging.c
+++ b/xen/arch/x86/mm/mem_paging.c
@@ -21,12 +21,17 @@
#include <asm/p2m.h>
+#include <xen/event.h>
#include <xen/guest_access.h>
+#include <xen/hypercall.h>
#include <xsm/xsm.h>
-int mem_paging_memop(XEN_GUEST_HANDLE_PARAM(xen_mem_paging_op_t) arg)
+long mem_paging_memop(unsigned long cmd,
+ XEN_GUEST_HANDLE_PARAM(xen_mem_paging_op_t) arg)
{
- int rc;
+ long rc;
+ unsigned long start_gfn = cmd >> MEMOP_EXTENT_SHIFT;
+ xen_pfn_t gfn;
xen_mem_paging_op_t mpo;
struct domain *d;
bool_t copyback = 0;
@@ -56,6 +61,31 @@ int
mem_paging_memop(XEN_GUEST_HANDLE_PARAM(xen_mem_paging_op_t) arg)
rc = p2m_mem_paging_evict(d, mpo.u.single.gfn);
break;
+ case XENMEM_paging_op_populate_evicted:
+ while ( start_gfn < mpo.u.batch.nr )
+ {
+ if ( copy_from_guest_offset(&gfn, mpo.u.batch.gfns, start_gfn, 1) )
+ {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ rc = p2m_mem_paging_populate_evicted(d, gfn);
+ if ( rc )
+ goto out;
+
+ if ( mpo.u.batch.nr > ++start_gfn && hypercall_preempt_check() )
+ {
+ cmd = XENMEM_paging_op | (start_gfn << MEMOP_EXTENT_SHIFT);
+ rc = hypercall_create_continuation(__HYPERVISOR_memory_op,
"lh",
+ cmd, arg);
+ goto out;
+ }
+ }
+
+ rc = 0;
+ break;
+
case XENMEM_paging_op_prep:
rc = p2m_mem_paging_prep(d, mpo.u.single.gfn, mpo.u.single.buffer);
if ( !rc )
diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
index 9eb6dc8..2ad46f6 100644
--- a/xen/arch/x86/mm/p2m.c
+++ b/xen/arch/x86/mm/p2m.c
@@ -1449,6 +1449,107 @@ int p2m_mem_paging_evict(struct domain *d, unsigned
long gfn)
}
/**
+ * p2m_mem_paging_populate_evicted - 'populate' a guest page as paged-out
+ * @d: guest domain
+ * @gfn: guest page to populate
+ *
+ * Returns 0 for success or negative errno values if eviction is not possible.
+ *
+ * p2m_mem_paging_populate_evicted() is mostly commonly called by a pager
+ * during guest restoration to mark a page as evicted so that the guest can be
+ * resumed before memory restoration is complete.
+ *
+ * Ideally, the page has never previously been populated, and it is only
+ * necessary to mark the existing hole in the physmap as an evicted page.
+ * However, to accomodate the common live migration scenario in which a page is
+ * populated but subsequently has its contents invalidated by a write at the
+ * sender, permit @gfn to have already been populated and free its current
+ * backing frame if so.
+ */
+int p2m_mem_paging_populate_evicted(struct domain *d, unsigned long gfn)
+{
+ struct page_info *page = NULL;
+ p2m_type_t p2mt;
+ p2m_access_t a;
+ mfn_t mfn;
+ struct p2m_domain *p2m = p2m_get_hostp2m(d);
+ int rc = -EBUSY;
+
+ gfn_lock(p2m, gfn, 0);
+
+ mfn = p2m->get_entry(p2m, gfn, &p2mt, &a, 0, NULL, NULL);
+
+ if ( mfn_valid(mfn) )
+ {
+ /*
+ * This is the first case we know how to deal with: the page has
+ * previously been populated, but the caller wants it in the evicted
+ * state anyway (e.g. because it was dirtied during live migration and
+ * is now being postcopy migrated).
+ *
+ * Double-check that it's pageable according to the union of the
+ * normal nominate() and evict() criteria, and free its backing page if
+ * so.
+ */
+
+ if ( !p2m_is_pageable(p2mt) )
+ goto out;
+
+ page = mfn_to_page(mfn);
+ if ( !get_page(page, d) )
+ goto out;
+
+ if ( is_iomem_page(mfn) )
+ goto err_put;
+
+ if ( (page->count_info & (PGC_count_mask | PGC_allocated)) !=
+ (2 | PGC_allocated) )
+ goto err_put;
+
+ if ( (page->u.inuse.type_info & PGT_count_mask) != 0 )
+ goto err_put;
+
+ /* Decrement guest domain's ref count of the page. */
+ if ( test_and_clear_bit(_PGC_allocated, &page->count_info) )
+ put_page(page);
+
+ /* Clear content before returning the page to Xen. */
+ scrub_one_page(page);
+
+ /* Finally, drop the ref _we_ took on the page, freeing it fully. */
+ put_page(page);
+ }
+ else if ( p2m_is_hole(p2mt) && !p2m_is_paging(p2mt) )
+ {
+ /*
+ * This is the second case we know how to deal with: the pfn isn't
+ * currently populated, and can transition directly to paged_out. All
+ * we need to do is adjust its p2m entry, which we share with the first
+ * case, so there's nothing further to do along this branch.
+ */
+ }
+ else
+ {
+ /* We can't handle this - error out. */
+ goto out;
+ }
+
+ rc = p2m_set_entry(p2m, gfn, INVALID_MFN, PAGE_ORDER_4K, p2m_ram_paged, a);
+ if ( !rc )
+ atomic_inc(&d->paged_pages);
+
+ /* Hop over the inapplicable put_page(). */
+ goto out;
+
+ err_put:
+ put_page(page);
+
+ out:
+ gfn_unlock(p2m, gfn, 0);
+ return rc;
+}
+
+/**
* p2m_mem_paging_drop_page - Tell pager to drop its reference to a paged page
* @d: guest domain
* @gfn: guest page to drop
diff --git a/xen/arch/x86/x86_64/compat/mm.c b/xen/arch/x86/x86_64/compat/mm.c
index b737af1..f4aff90 100644
--- a/xen/arch/x86/x86_64/compat/mm.c
+++ b/xen/arch/x86/x86_64/compat/mm.c
@@ -53,8 +53,9 @@ int compat_arch_memory_op(unsigned long cmd,
XEN_GUEST_HANDLE_PARAM(void) arg)
compat_pfn_t mfn;
unsigned int i;
int rc = 0;
+ int op = cmd & MEMOP_CMD_MASK;
- switch ( cmd )
+ switch ( op )
{
case XENMEM_set_memory_map:
{
@@ -187,7 +188,8 @@ int compat_arch_memory_op(unsigned long cmd,
XEN_GUEST_HANDLE_PARAM(void) arg)
return mem_sharing_get_nr_shared_mfns();
case XENMEM_paging_op:
- return mem_paging_memop(guest_handle_cast(arg, xen_mem_paging_op_t));
+ return mem_paging_memop(cmd,
+ guest_handle_cast(arg, xen_mem_paging_op_t));
case XENMEM_sharing_op:
return mem_sharing_memop(guest_handle_cast(arg, xen_mem_sharing_op_t));
diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c
index aa1b94f..7394d92 100644
--- a/xen/arch/x86/x86_64/mm.c
+++ b/xen/arch/x86/x86_64/mm.c
@@ -926,8 +926,9 @@ long subarch_memory_op(unsigned long cmd,
XEN_GUEST_HANDLE_PARAM(void) arg)
xen_pfn_t mfn, last_mfn;
unsigned int i;
long rc = 0;
+ int op = cmd & MEMOP_CMD_MASK;
- switch ( cmd )
+ switch ( op )
{
case XENMEM_machphys_mfn_list:
if ( copy_from_guest(&xmml, arg, 1) )
@@ -1004,7 +1005,8 @@ long subarch_memory_op(unsigned long cmd,
XEN_GUEST_HANDLE_PARAM(void) arg)
return mem_sharing_get_nr_shared_mfns();
case XENMEM_paging_op:
- return mem_paging_memop(guest_handle_cast(arg, xen_mem_paging_op_t));
+ return mem_paging_memop(cmd,
+ guest_handle_cast(arg, xen_mem_paging_op_t));
case XENMEM_sharing_op:
return mem_sharing_memop(guest_handle_cast(arg, xen_mem_sharing_op_t));
diff --git a/xen/include/asm-x86/mem_paging.h b/xen/include/asm-x86/mem_paging.h
index 176acaf..7b9a4f6 100644
--- a/xen/include/asm-x86/mem_paging.h
+++ b/xen/include/asm-x86/mem_paging.h
@@ -22,7 +22,8 @@
#ifndef __ASM_X86_MEM_PAGING_H__
#define __ASM_X86_MEM_PAGING_H__
-int mem_paging_memop(XEN_GUEST_HANDLE_PARAM(xen_mem_paging_op_t) arg);
+long mem_paging_memop(unsigned long cmd,
+ XEN_GUEST_HANDLE_PARAM(xen_mem_paging_op_t) arg);
#endif /*__ASM_X86_MEM_PAGING_H__ */
diff --git a/xen/include/asm-x86/p2m.h b/xen/include/asm-x86/p2m.h
index 408f7da..653d413 100644
--- a/xen/include/asm-x86/p2m.h
+++ b/xen/include/asm-x86/p2m.h
@@ -676,6 +676,8 @@ int set_shared_p2m_entry(struct domain *d, unsigned long
gfn, mfn_t mfn);
int p2m_mem_paging_nominate(struct domain *d, unsigned long gfn);
/* Evict a frame */
int p2m_mem_paging_evict(struct domain *d, unsigned long gfn);
+/* If @gfn is populated, evict it. If not, mark it as paged-out directly. */
+int p2m_mem_paging_populate_evicted(struct domain *d, unsigned long gfn);
/* Tell xenpaging to drop a paged out frame */
void p2m_mem_paging_drop_page(struct domain *d, unsigned long gfn,
p2m_type_t p2mt);
diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h
index 49ef162..5196803 100644
--- a/xen/include/public/memory.h
+++ b/xen/include/public/memory.h
@@ -385,10 +385,11 @@ typedef struct xen_pod_target xen_pod_target_t;
#define XENMEM_get_sharing_freed_pages 18
#define XENMEM_get_sharing_shared_pages 19
-#define XENMEM_paging_op 20
-#define XENMEM_paging_op_nominate 0
-#define XENMEM_paging_op_evict 1
-#define XENMEM_paging_op_prep 2
+#define XENMEM_paging_op 20
+#define XENMEM_paging_op_nominate 0
+#define XENMEM_paging_op_evict 1
+#define XENMEM_paging_op_prep 2
+#define XENMEM_paging_op_populate_evicted 3
struct xen_mem_paging_op {
uint8_t op; /* XENMEM_paging_op_* */
@@ -401,6 +402,10 @@ struct xen_mem_paging_op {
/* Other OPs */
uint64_aligned_t gfn; /* IN: gfn of page being operated on */
} single;
+ struct {
+ XEN_GUEST_HANDLE(xen_pfn_t) gfns;
+ uint32_t nr;
+ } batch;
} u;
};
typedef struct xen_mem_paging_op xen_mem_paging_op_t;
--
2.7.4
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |