|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH RFC v2 10/23] libxc/xc_sr_save: introduce save batch types
From: Joshua Otto <jtotto@xxxxxxxxxxxx>
To write guest pages into the stream, the save logic builds up batches
of pfns to be written and performs all of the work necessary to write
them whenever a full batch has been accumulated. Writing a PAGE_DATA
batch entails determining the types of all pfns in the batch, mapping
the subset of pfns that are backed by real memory constructing a
PAGE_DATA record describing the batch and writing everything into the
stream.
Postcopy live migration introduces several new types of batches. To
enable the postcopy logic to re-use the bulk of the code used to manage
and write PAGE_DATA records, introduce a batch_type member to the save
context (which for now can take on only a single value), and refactor
write_batch() to take the batch_type into account when preparing and
writing each record.
While refactoring write_batch(), factor the operation of querying the
page types of a batch into a subroutine that is useable independently of
write_batch().
No functional change.
Signed-off-by: Joshua Otto <jtotto@xxxxxxxxxxxx>
---
tools/libxc/xc_sr_common.h | 3 +
tools/libxc/xc_sr_save.c | 207 +++++++++++++++++++++++++++---------------
tools/libxc/xg_save_restore.h | 2 +-
3 files changed, 140 insertions(+), 72 deletions(-)
diff --git a/tools/libxc/xc_sr_common.h b/tools/libxc/xc_sr_common.h
index 0da0ffc..fc82e71 100644
--- a/tools/libxc/xc_sr_common.h
+++ b/tools/libxc/xc_sr_common.h
@@ -208,6 +208,9 @@ struct xc_sr_context
struct precopy_stats stats;
int policy_decision;
+ enum {
+ XC_SR_SAVE_BATCH_PRECOPY_PAGE
+ } batch_type;
xen_pfn_t *batch_pfns;
unsigned nr_batch_pfns;
unsigned long *deferred_pages;
diff --git a/tools/libxc/xc_sr_save.c b/tools/libxc/xc_sr_save.c
index 48d403b..9f077a3 100644
--- a/tools/libxc/xc_sr_save.c
+++ b/tools/libxc/xc_sr_save.c
@@ -3,6 +3,23 @@
#include "xc_sr_common.h"
+#define MAX_BATCH_SIZE MAX_PRECOPY_BATCH_SIZE
+
+static const unsigned int batch_sizes[] =
+{
+ [XC_SR_SAVE_BATCH_PRECOPY_PAGE] = MAX_PRECOPY_BATCH_SIZE
+};
+
+static const bool batch_includes_contents[] =
+{
+ [XC_SR_SAVE_BATCH_PRECOPY_PAGE] = true
+};
+
+static const uint32_t batch_rec_types[] =
+{
+ [XC_SR_SAVE_BATCH_PRECOPY_PAGE] = REC_TYPE_PAGE_DATA
+};
+
/*
* Writes an Image header and Domain header into the stream.
*/
@@ -67,19 +84,54 @@ static int write_checkpoint_record(struct xc_sr_context
*ctx)
}
/*
+ * This function:
+ * - maps each pfn in the current batch to its gfn
+ * - gets the type of each pfn in the batch.
+ */
+static int get_batch_info(struct xc_sr_context *ctx, xen_pfn_t *gfns,
+ xen_pfn_t *types)
+{
+ int rc;
+ unsigned int nr_pfns = ctx->save.nr_batch_pfns;
+ xc_interface *xch = ctx->xch;
+ unsigned int i;
+
+ for ( i = 0; i < nr_pfns; ++i )
+ types[i] = gfns[i] = ctx->save.ops.pfn_to_gfn(ctx,
+ ctx->save.batch_pfns[i]);
+
+ /*
+ * The type query domctl accepts batches of at most 1024 pfns, so we need
to
+ * break our batch here into appropriately-sized sub-batches.
+ */
+ for ( i = 0; i < nr_pfns; i += 1024 )
+ {
+ rc = xc_get_pfn_type_batch(xch, ctx->domid, min(1024U, nr_pfns - i),
+ &types[i]);
+ if ( rc )
+ {
+ PERROR("Failed to get types for pfn batch");
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/*
* Writes a batch of memory as a PAGE_DATA record into the stream. The batch
* is constructed in ctx->save.batch_pfns.
*
* This function:
- * - gets the types for each pfn in the batch.
* - for each pfn with real data:
* - maps and attempts to localise the pages.
* - construct and writes a PAGE_DATA record into the stream.
*/
-static int write_batch(struct xc_sr_context *ctx)
+static int write_batch(struct xc_sr_context *ctx, xen_pfn_t *gfns,
+ xen_pfn_t *types)
{
xc_interface *xch = ctx->xch;
- xen_pfn_t *gfns = NULL, *types = NULL;
+ xen_pfn_t *bgfns = NULL;
void *guest_mapping = NULL;
void **guest_data = NULL;
void **local_pages = NULL;
@@ -90,17 +142,16 @@ static int write_batch(struct xc_sr_context *ctx)
uint64_t *rec_pfns = NULL;
struct iovec *iov = NULL; int iovcnt = 0;
struct xc_sr_rec_pages_header hdr = { 0 };
+ bool send_page_contents = batch_includes_contents[ctx->save.batch_type];
struct xc_sr_record rec =
{
- .type = REC_TYPE_PAGE_DATA,
+ .type = batch_rec_types[ctx->save.batch_type],
};
assert(nr_pfns != 0);
- /* Mfns of the batch pfns. */
- gfns = malloc(nr_pfns * sizeof(*gfns));
- /* Types of the batch pfns. */
- types = malloc(nr_pfns * sizeof(*types));
+ /* The subset of gfns that are physically-backed. */
+ bgfns = malloc(nr_pfns * sizeof(*bgfns));
/* Errors from attempting to map the gfns. */
errors = malloc(nr_pfns * sizeof(*errors));
/* Pointers to page data to send. Mapped gfns or local allocations. */
@@ -110,19 +161,16 @@ static int write_batch(struct xc_sr_context *ctx)
/* iovec[] for writev(). */
iov = malloc((nr_pfns + 4) * sizeof(*iov));
- if ( !gfns || !types || !errors || !guest_data || !local_pages || !iov )
+ if ( !bgfns || !errors || !guest_data || !local_pages || !iov )
{
ERROR("Unable to allocate arrays for a batch of %u pages",
nr_pfns);
goto err;
}
+ /* Mark likely-ballooned pages as deferred. */
for ( i = 0; i < nr_pfns; ++i )
{
- types[i] = gfns[i] = ctx->save.ops.pfn_to_gfn(ctx,
- ctx->save.batch_pfns[i]);
-
- /* Likely a ballooned page. */
if ( gfns[i] == INVALID_MFN )
{
set_bit(ctx->save.batch_pfns[i], ctx->save.deferred_pages);
@@ -130,39 +178,9 @@ static int write_batch(struct xc_sr_context *ctx)
}
}
- rc = xc_get_pfn_type_batch(xch, ctx->domid, nr_pfns, types);
- if ( rc )
- {
- PERROR("Failed to get types for pfn batch");
- goto err;
- }
- rc = -1;
-
- for ( i = 0; i < nr_pfns; ++i )
- {
- switch ( types[i] )
- {
- case XEN_DOMCTL_PFINFO_BROKEN:
- case XEN_DOMCTL_PFINFO_XALLOC:
- case XEN_DOMCTL_PFINFO_XTAB:
- continue;
- }
-
- gfns[nr_pages++] = gfns[i];
- }
-
- if ( nr_pages > 0 )
+ if ( send_page_contents )
{
- guest_mapping = xenforeignmemory_map(xch->fmem,
- ctx->domid, PROT_READ, nr_pages, gfns, errors);
- if ( !guest_mapping )
- {
- PERROR("Failed to map guest pages");
- goto err;
- }
- nr_pages_mapped = nr_pages;
-
- for ( i = 0, p = 0; i < nr_pfns; ++i )
+ for ( i = 0; i < nr_pfns; ++i )
{
switch ( types[i] )
{
@@ -172,36 +190,62 @@ static int write_batch(struct xc_sr_context *ctx)
continue;
}
- if ( errors[p] )
+ bgfns[nr_pages++] = gfns[i];
+ }
+
+ if ( nr_pages > 0 )
+ {
+ guest_mapping = xenforeignmemory_map(xch->fmem,
+ ctx->domid, PROT_READ, nr_pages, bgfns, errors);
+ if ( !guest_mapping )
{
- ERROR("Mapping of pfn %#"PRIpfn" (gfn %#"PRIpfn") failed %d",
- ctx->save.batch_pfns[i], gfns[p], errors[p]);
+ PERROR("Failed to map guest pages");
goto err;
}
+ nr_pages_mapped = nr_pages;
+
+ for ( i = 0, p = 0; i < nr_pfns; ++i )
+ {
+ switch ( types[i] )
+ {
+ case XEN_DOMCTL_PFINFO_BROKEN:
+ case XEN_DOMCTL_PFINFO_XALLOC:
+ case XEN_DOMCTL_PFINFO_XTAB:
+ continue;
+ }
- orig_page = page = guest_mapping + (p * PAGE_SIZE);
- rc = ctx->save.ops.normalise_page(ctx, types[i], &page);
+ if ( errors[p] )
+ {
+ ERROR("Mapping of pfn %#"PRIpfn" (mfn %#"PRIpfn") failed
%d",
+ ctx->save.batch_pfns[i], bgfns[p], errors[p]);
+ goto err;
+ }
- if ( orig_page != page )
- local_pages[i] = page;
+ orig_page = page = guest_mapping + (p * PAGE_SIZE);
+ rc = ctx->save.ops.normalise_page(ctx, types[i], &page);
- if ( rc )
- {
- if ( rc == -1 && errno == EAGAIN )
+ if ( orig_page != page )
+ local_pages[i] = page;
+
+ if ( rc )
{
- set_bit(ctx->save.batch_pfns[i], ctx->save.deferred_pages);
- ++ctx->save.nr_deferred_pages;
- types[i] = XEN_DOMCTL_PFINFO_XTAB;
- --nr_pages;
+ if ( rc == -1 && errno == EAGAIN )
+ {
+ set_bit(ctx->save.batch_pfns[i],
+ ctx->save.deferred_pages);
+ ++ctx->save.nr_deferred_pages;
+ types[i] = XEN_DOMCTL_PFINFO_XTAB;
+ --nr_pages;
+ }
+ else
+ goto err;
}
else
- goto err;
- }
- else
- guest_data[i] = page;
+ guest_data[i] = page;
- rc = -1;
- ++p;
+ rc = -1;
+ ++p;
+ }
}
}
@@ -270,8 +314,7 @@ static int write_batch(struct xc_sr_context *ctx)
free(local_pages);
free(guest_data);
free(errors);
- free(types);
- free(gfns);
+ free(bgfns);
return rc;
}
@@ -281,7 +324,7 @@ static int write_batch(struct xc_sr_context *ctx)
*/
static bool batch_full(const struct xc_sr_context *ctx)
{
- return ctx->save.nr_batch_pfns == MAX_BATCH_SIZE;
+ return ctx->save.nr_batch_pfns == batch_sizes[ctx->save.batch_type];
}
/*
@@ -298,12 +341,29 @@ static bool batch_empty(struct xc_sr_context *ctx)
static int flush_batch(struct xc_sr_context *ctx)
{
int rc = 0;
+ xc_interface *xch = ctx->xch;
+ xen_pfn_t *gfns = NULL, *types = NULL;
+ unsigned int nr_pfns = ctx->save.nr_batch_pfns;
if ( batch_empty(ctx) )
- return rc;
+ goto out;
- rc = write_batch(ctx);
+ gfns = malloc(nr_pfns * sizeof(*gfns));
+ types = malloc(nr_pfns * sizeof(*types));
+ if ( !gfns || !types )
+ {
+ ERROR("Unable to allocate arrays for a batch of %u pages",
+ nr_pfns);
+ rc = -1;
+ goto out;
+ }
+
+ rc = get_batch_info(ctx, gfns, types);
+ if ( rc )
+ goto out;
+
+ rc = write_batch(ctx, gfns, types);
if ( !rc )
{
VALGRIND_MAKE_MEM_UNDEFINED(ctx->save.batch_pfns,
@@ -311,6 +371,10 @@ static int flush_batch(struct xc_sr_context *ctx)
sizeof(*ctx->save.batch_pfns));
}
+ out:
+ free(gfns);
+ free(types);
+
return rc;
}
@@ -319,7 +383,7 @@ static int flush_batch(struct xc_sr_context *ctx)
*/
static void add_to_batch(struct xc_sr_context *ctx, xen_pfn_t pfn)
{
- assert(ctx->save.nr_batch_pfns < MAX_BATCH_SIZE);
+ assert(ctx->save.nr_batch_pfns < batch_sizes[ctx->save.batch_type]);
ctx->save.batch_pfns[ctx->save.nr_batch_pfns++] = pfn;
}
@@ -391,6 +455,7 @@ static int send_dirty_pages(struct xc_sr_context *ctx,
void *data = ctx->save.callbacks->data;
assert(batch_empty(ctx));
+ ctx->save.batch_type = XC_SR_SAVE_BATCH_PRECOPY_PAGE;
while ( p < ctx->save.p2m_size )
{
if ( ctx->save.phase == XC_SAVE_PHASE_PRECOPY )
diff --git a/tools/libxc/xg_save_restore.h b/tools/libxc/xg_save_restore.h
index 303081d..40debf6 100644
--- a/tools/libxc/xg_save_restore.h
+++ b/tools/libxc/xg_save_restore.h
@@ -24,7 +24,7 @@
** We process save/restore/migrate in batches of pages; the below
** determines how many pages we (at maximum) deal with in each batch.
*/
-#define MAX_BATCH_SIZE 1024 /* up to 1024 pages (4MB) at a time */
+#define MAX_PRECOPY_BATCH_SIZE 1024 /* up to 1024 pages (4MB) at a time */
/* When pinning page tables at the end of restore, we also use batching. */
#define MAX_PIN_BATCH 1024
--
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 |