* [PATCH v2 0/2] live migration: optimisations
@ 2022-09-06 9:54 Andrei Semenov
2022-09-06 9:54 ` [PATCH v2 1/2] live migration: do not use deffered bitmap when inappropriate Andrei Semenov
2022-09-06 9:54 ` [PATCH v2 2/2] live migration: use superpages for physmap population on restore when possible Andrei Semenov
0 siblings, 2 replies; 7+ messages in thread
From: Andrei Semenov @ 2022-09-06 9:54 UTC (permalink / raw)
To: andrei.semenov, xen-devel; +Cc: Wei Liu, Anthony PERARD, Juergen Gross
This 2 patches implement some optimisations for guests live migraiton.
Andrei Semenov (2):
live migration: do not use deffered bitmap when inappropriate
live migration: use superpages for physmap population on restore when
possible
tools/include/xen-tools/libs.h | 4 ++
tools/libs/guest/xg_private.h | 3 +
tools/libs/guest/xg_sr_common.h | 44 +++++++++++-
tools/libs/guest/xg_sr_restore.c | 60 +++++++---------
tools/libs/guest/xg_sr_restore_x86_hvm.c | 88 +++++++++++++++++++++++-
tools/libs/guest/xg_sr_restore_x86_pv.c | 22 +++++-
tools/libs/guest/xg_sr_save.c | 23 +++----
tools/libs/guest/xg_sr_save_x86_hvm.c | 21 ++++++
tools/libs/guest/xg_sr_save_x86_pv.c | 39 +++++++++++
9 files changed, 247 insertions(+), 57 deletions(-)
--
2.34.1
Andrei Semenov | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
w: vates.fr | xcp-ng.org | xen-orchestra.com
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/2] live migration: do not use deffered bitmap when inappropriate
2022-09-06 9:54 [PATCH v2 0/2] live migration: optimisations Andrei Semenov
@ 2022-09-06 9:54 ` Andrei Semenov
2022-09-07 7:39 ` Jan Beulich
2022-09-06 9:54 ` [PATCH v2 2/2] live migration: use superpages for physmap population on restore when possible Andrei Semenov
1 sibling, 1 reply; 7+ messages in thread
From: Andrei Semenov @ 2022-09-06 9:54 UTC (permalink / raw)
To: andrei.semenov, xen-devel; +Cc: Wei Liu, Anthony PERARD, Juergen Gross
Use deffered bitmap only in PV guests context as it not used for HVM guests.
This allow to reduce memory pressure on domain0 while migrating very large
(memory wise) HVM guests.
Signed-off-by: Andrei Semenov <andrei.semenov@vates.fr>
---
tools/libs/guest/xg_sr_common.h | 26 ++++++++++++++++--
tools/libs/guest/xg_sr_save.c | 23 +++++++---------
tools/libs/guest/xg_sr_save_x86_hvm.c | 21 +++++++++++++++
tools/libs/guest/xg_sr_save_x86_pv.c | 39 +++++++++++++++++++++++++++
4 files changed, 93 insertions(+), 16 deletions(-)
diff --git a/tools/libs/guest/xg_sr_common.h b/tools/libs/guest/xg_sr_common.h
index 36d45ef56f..941e24d7b7 100644
--- a/tools/libs/guest/xg_sr_common.h
+++ b/tools/libs/guest/xg_sr_common.h
@@ -96,6 +96,24 @@ struct xc_sr_save_ops
*/
int (*check_vm_state)(struct xc_sr_context *ctx);
+ /**
+ * For some reasons the page can't be sent for the moment. Postpone this
+ * send to the later stage when domain is suspended.
+ */
+ int (*defer_page)(struct xc_sr_context *ctx, xen_pfn_t pfn);
+
+ /**
+ * Merge all deferred pages with the dirty pages bitmap (in order to be
+ * sent).
+ */
+ int (*merge_deferred)(const struct xc_sr_context *ctx,
+ unsigned long *bitmap, unsigned long *count);
+
+ /**
+ * Deferred pages was successfully sent. Reset all associated information.
+ */
+ int (*reset_deferred)(struct xc_sr_context *ctx);
+
/**
* Clean up the local environment. Will be called exactly once, either
* after a successful save, or upon encountering an error.
@@ -243,8 +261,6 @@ struct xc_sr_context
xen_pfn_t *batch_pfns;
unsigned int nr_batch_pfns;
- unsigned long *deferred_pages;
- unsigned long nr_deferred_pages;
xc_hypercall_buffer_t dirty_bitmap_hbuf;
} save;
@@ -349,6 +365,12 @@ struct xc_sr_context
union
{
+ struct
+ {
+ unsigned long *deferred_pages;
+ unsigned long nr_deferred_pages;
+ } save;
+
struct
{
/* State machine for the order of received records. */
diff --git a/tools/libs/guest/xg_sr_save.c b/tools/libs/guest/xg_sr_save.c
index 9853d8d846..602b18488d 100644
--- a/tools/libs/guest/xg_sr_save.c
+++ b/tools/libs/guest/xg_sr_save.c
@@ -132,8 +132,7 @@ static int write_batch(struct xc_sr_context *ctx)
/* Likely a ballooned page. */
if ( mfns[i] == INVALID_MFN )
{
- set_bit(ctx->save.batch_pfns[i], ctx->save.deferred_pages);
- ++ctx->save.nr_deferred_pages;
+ ctx->save.ops.defer_page(ctx, ctx->save.batch_pfns[i]);
}
}
@@ -192,8 +191,7 @@ static int write_batch(struct xc_sr_context *ctx)
{
if ( rc == -1 && errno == EAGAIN )
{
- set_bit(ctx->save.batch_pfns[i], ctx->save.deferred_pages);
- ++ctx->save.nr_deferred_pages;
+ ctx->save.ops.defer_page(ctx, ctx->save.batch_pfns[i]);
types[i] = XEN_DOMCTL_PFINFO_XTAB;
--nr_pages;
}
@@ -641,6 +639,7 @@ static int suspend_and_send_dirty(struct xc_sr_context *ctx)
xc_interface *xch = ctx->xch;
xc_shadow_op_stats_t stats = { 0, ctx->save.p2m_size };
char *progress_str = NULL;
+ unsigned long merged;
int rc;
DECLARE_HYPERCALL_BUFFER_SHADOW(unsigned long, dirty_bitmap,
&ctx->save.dirty_bitmap_hbuf);
@@ -669,7 +668,7 @@ static int suspend_and_send_dirty(struct xc_sr_context *ctx)
else
xc_set_progress_prefix(xch, "Checkpointed save");
- bitmap_or(dirty_bitmap, ctx->save.deferred_pages, ctx->save.p2m_size);
+ ctx->save.ops.merge_deferred(ctx, dirty_bitmap, &merged);
if ( !ctx->save.live && ctx->stream_type == XC_STREAM_COLO )
{
@@ -681,12 +680,11 @@ static int suspend_and_send_dirty(struct xc_sr_context *ctx)
}
}
- rc = send_dirty_pages(ctx, stats.dirty_count + ctx->save.nr_deferred_pages);
+ rc = send_dirty_pages(ctx, stats.dirty_count + merged);
if ( rc )
goto out;
- bitmap_clear(ctx->save.deferred_pages, ctx->save.p2m_size);
- ctx->save.nr_deferred_pages = 0;
+ ctx->save.ops.reset_deferred(ctx);
out:
xc_set_progress_prefix(xch, NULL);
@@ -805,18 +803,16 @@ static int setup(struct xc_sr_context *ctx)
xch, dirty_bitmap, NRPAGES(bitmap_size(ctx->save.p2m_size)));
ctx->save.batch_pfns = malloc(MAX_BATCH_SIZE *
sizeof(*ctx->save.batch_pfns));
- ctx->save.deferred_pages = bitmap_alloc(ctx->save.p2m_size);
- if ( !ctx->save.batch_pfns || !dirty_bitmap || !ctx->save.deferred_pages )
+ if ( !ctx->save.batch_pfns || !dirty_bitmap )
{
- ERROR("Unable to allocate memory for dirty bitmaps, batch pfns and"
- " deferred pages");
+ ERROR("Unable to allocate memory for dirty bitmaps, batch pfns");
rc = -1;
errno = ENOMEM;
goto err;
}
- rc = 0;
+ rc = ctx->save.ops.reset_deferred(ctx);
err:
return rc;
@@ -837,7 +833,6 @@ static void cleanup(struct xc_sr_context *ctx)
xc_hypercall_buffer_free_pages(xch, dirty_bitmap,
NRPAGES(bitmap_size(ctx->save.p2m_size)));
- free(ctx->save.deferred_pages);
free(ctx->save.batch_pfns);
}
diff --git a/tools/libs/guest/xg_sr_save_x86_hvm.c b/tools/libs/guest/xg_sr_save_x86_hvm.c
index 1634a7bc43..3c762a0af0 100644
--- a/tools/libs/guest/xg_sr_save_x86_hvm.c
+++ b/tools/libs/guest/xg_sr_save_x86_hvm.c
@@ -211,6 +211,24 @@ static int x86_hvm_end_of_checkpoint(struct xc_sr_context *ctx)
return 0;
}
+static int x86_hvm_defer_page(struct xc_sr_context *ctx, xen_pfn_t pfn)
+{
+ return 0;
+}
+
+static int x86_hvm_merge_deferred(const struct xc_sr_context *ctx,
+ unsigned long *bitmap, unsigned long *count)
+{
+ *count = 0;
+
+ return 0;
+}
+
+static int x86_hvm_reset_deferred(struct xc_sr_context *ctx)
+{
+ return 0;
+}
+
static int x86_hvm_cleanup(struct xc_sr_context *ctx)
{
xc_interface *xch = ctx->xch;
@@ -237,6 +255,9 @@ struct xc_sr_save_ops save_ops_x86_hvm =
.start_of_checkpoint = x86_hvm_start_of_checkpoint,
.end_of_checkpoint = x86_hvm_end_of_checkpoint,
.check_vm_state = x86_hvm_check_vm_state,
+ .defer_page = x86_hvm_defer_page,
+ .merge_deferred = x86_hvm_merge_deferred,
+ .reset_deferred = x86_hvm_reset_deferred,
.cleanup = x86_hvm_cleanup,
};
diff --git a/tools/libs/guest/xg_sr_save_x86_pv.c b/tools/libs/guest/xg_sr_save_x86_pv.c
index 4964f1f7b8..5fdc7e9590 100644
--- a/tools/libs/guest/xg_sr_save_x86_pv.c
+++ b/tools/libs/guest/xg_sr_save_x86_pv.c
@@ -1031,6 +1031,7 @@ static int x86_pv_normalise_page(struct xc_sr_context *ctx, xen_pfn_t type,
*/
static int x86_pv_setup(struct xc_sr_context *ctx)
{
+ xc_interface *xch = ctx->xch;
int rc;
rc = x86_pv_domain_info(ctx);
@@ -1049,6 +1050,15 @@ static int x86_pv_setup(struct xc_sr_context *ctx)
if ( rc )
return rc;
+ ctx->x86.pv.save.deferred_pages = bitmap_alloc(ctx->save.p2m_size);
+
+ if (!ctx->x86.pv.save.deferred_pages)
+ {
+ ERROR("Unable to allocate memory for deferred pages");
+ errno = ENOMEM;
+ return -1;
+ }
+
return 0;
}
@@ -1116,9 +1126,35 @@ static int x86_pv_check_vm_state(struct xc_sr_context *ctx)
return x86_pv_check_vm_state_p2m_list(ctx);
}
+static int x86_pv_defer_page(struct xc_sr_context *ctx, xen_pfn_t pfn)
+{
+ set_bit(pfn, ctx->x86.pv.save.deferred_pages);
+ ++ctx->x86.pv.save.nr_deferred_pages;
+
+ return 0;
+}
+
+static int x86_pv_merge_deferred(const struct xc_sr_context *ctx,
+ unsigned long *bitmap, unsigned long *count)
+{
+ bitmap_or(bitmap, ctx->x86.pv.save.deferred_pages, ctx->save.p2m_size);
+ *count = ctx->x86.pv.save.nr_deferred_pages;
+
+ return 0;
+}
+
+static int x86_pv_reset_deferred(struct xc_sr_context *ctx)
+{
+ bitmap_clear(ctx->x86.pv.save.deferred_pages, ctx->save.p2m_size);
+ ctx->x86.pv.save.nr_deferred_pages = 0;
+
+ return 0;
+}
+
static int x86_pv_cleanup(struct xc_sr_context *ctx)
{
free(ctx->x86.pv.p2m_pfns);
+ free(ctx->x86.pv.save.deferred_pages);
if ( ctx->x86.pv.p2m )
munmap(ctx->x86.pv.p2m, ctx->x86.pv.p2m_frames * PAGE_SIZE);
@@ -1142,6 +1178,9 @@ struct xc_sr_save_ops save_ops_x86_pv =
.start_of_checkpoint = x86_pv_start_of_checkpoint,
.end_of_checkpoint = x86_pv_end_of_checkpoint,
.check_vm_state = x86_pv_check_vm_state,
+ .defer_page = x86_pv_defer_page,
+ .merge_deferred = x86_pv_merge_deferred,
+ .reset_deferred = x86_pv_reset_deferred,
.cleanup = x86_pv_cleanup,
};
--
2.34.1
Andrei Semenov | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
w: vates.fr | xcp-ng.org | xen-orchestra.com
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 2/2] live migration: use superpages for physmap population on restore when possible
2022-09-06 9:54 [PATCH v2 0/2] live migration: optimisations Andrei Semenov
2022-09-06 9:54 ` [PATCH v2 1/2] live migration: do not use deffered bitmap when inappropriate Andrei Semenov
@ 2022-09-06 9:54 ` Andrei Semenov
2022-09-07 7:40 ` Jan Beulich
1 sibling, 1 reply; 7+ messages in thread
From: Andrei Semenov @ 2022-09-06 9:54 UTC (permalink / raw)
To: andrei.semenov, xen-devel; +Cc: Wei Liu, Anthony PERARD, Juergen Gross
Implement an heuristic for X86 HVM guests which tries to use superpages while
populating guest physmap on live migration. This should impove memory accesses
performances for these guests.
Signed-off-by: Andrei Semenov <andrei.semenov@vates.fr>
---
tools/include/xen-tools/libs.h | 4 ++
tools/libs/guest/xg_private.h | 3 +
tools/libs/guest/xg_sr_common.h | 18 ++++-
tools/libs/guest/xg_sr_restore.c | 60 +++++++---------
tools/libs/guest/xg_sr_restore_x86_hvm.c | 88 +++++++++++++++++++++++-
tools/libs/guest/xg_sr_restore_x86_pv.c | 22 +++++-
6 files changed, 154 insertions(+), 41 deletions(-)
diff --git a/tools/include/xen-tools/libs.h b/tools/include/xen-tools/libs.h
index a16e0c3807..bdd903eb7b 100644
--- a/tools/include/xen-tools/libs.h
+++ b/tools/include/xen-tools/libs.h
@@ -63,4 +63,8 @@
#define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1))
#endif
+#ifndef ROUNDDOWN
+#define ROUNDDOWN(_x,_w) ((unsigned long)(_x) & (-1UL << (_w)))
+#endif
+
#endif /* __XEN_TOOLS_LIBS__ */
diff --git a/tools/libs/guest/xg_private.h b/tools/libs/guest/xg_private.h
index 09e24f1227..dcf63b5188 100644
--- a/tools/libs/guest/xg_private.h
+++ b/tools/libs/guest/xg_private.h
@@ -134,6 +134,9 @@ typedef uint64_t x86_pgentry_t;
#define PAGE_SIZE_X86 (1UL << PAGE_SHIFT_X86)
#define PAGE_MASK_X86 (~(PAGE_SIZE_X86-1))
+#define S_PAGE_1GB_ORDER 18
+#define S_PAGE_2MB_ORDER 9
+
#define NRPAGES(x) (ROUNDUP(x, PAGE_SHIFT) >> PAGE_SHIFT)
static inline xen_pfn_t xc_pfn_to_mfn(xen_pfn_t pfn, xen_pfn_t *p2m,
diff --git a/tools/libs/guest/xg_sr_common.h b/tools/libs/guest/xg_sr_common.h
index 941e24d7b7..96365e05a8 100644
--- a/tools/libs/guest/xg_sr_common.h
+++ b/tools/libs/guest/xg_sr_common.h
@@ -137,7 +137,8 @@ struct xc_sr_restore_ops
bool (*pfn_is_valid)(const struct xc_sr_context *ctx, xen_pfn_t pfn);
/* Set the GFN of a PFN. */
- void (*set_gfn)(struct xc_sr_context *ctx, xen_pfn_t pfn, xen_pfn_t gfn);
+ void (*set_gfn)(struct xc_sr_context *ctx, xen_pfn_t pfn, xen_pfn_t gfn,
+ unsigned int order);
/* Set the type of a PFN. */
void (*set_page_type)(struct xc_sr_context *ctx, xen_pfn_t pfn,
@@ -175,6 +176,17 @@ struct xc_sr_restore_ops
#define BROKEN_CHANNEL 2
int (*process_record)(struct xc_sr_context *ctx, struct xc_sr_record *rec);
+ /**
+ * Guest physmap population order is based on heuristic which is family
+ * dependant. X86 HVM heuristic is interested in observing the whole
+ * record (the first) in order to guess how the physmap should be populated.
+ */
+ void (*guess_physmap)(struct xc_sr_context *ctx, unsigned int count,
+ const xen_pfn_t *pfns, const uint32_t *types);
+
+ /* Get the physmap population order for given PFN */
+ int (*get_physmap_order)(const struct xc_sr_context *ctx, xen_pfn_t pfn);
+
/**
* Perform any actions required after the static data has arrived. Called
* when the STATIC_DATA_COMPLETE record has been recieved/inferred.
@@ -404,6 +416,10 @@ struct xc_sr_context
{
/* HVM context blob. */
struct xc_sr_blob context;
+
+ /* Set guest type (based on the first record) */
+ bool set_guest_type;
+ bool pvh_guest;
} restore;
};
} hvm;
diff --git a/tools/libs/guest/xg_sr_restore.c b/tools/libs/guest/xg_sr_restore.c
index 074b56d263..af864bd5ea 100644
--- a/tools/libs/guest/xg_sr_restore.c
+++ b/tools/libs/guest/xg_sr_restore.c
@@ -86,18 +86,21 @@ static bool pfn_is_populated(const struct xc_sr_context *ctx, xen_pfn_t pfn)
* avoid realloc()ing too excessively, the size increased to the nearest power
* of two large enough to contain the required pfn.
*/
-static int pfn_set_populated(struct xc_sr_context *ctx, xen_pfn_t pfn)
+static int pfn_set_populated(struct xc_sr_context *ctx, xen_pfn_t pfn,
+ unsigned int order)
{
xc_interface *xch = ctx->xch;
+ xen_pfn_t start_pfn = ROUNDDOWN(pfn, order),
+ end_pfn = (ROUNDUP(pfn + 1, order) - 1);
- if ( pfn > ctx->restore.max_populated_pfn )
+ if ( end_pfn > ctx->restore.max_populated_pfn )
{
xen_pfn_t new_max;
size_t old_sz, new_sz;
unsigned long *p;
/* Round up to the nearest power of two larger than pfn, less 1. */
- new_max = pfn;
+ new_max = end_pfn;
new_max |= new_max >> 1;
new_max |= new_max >> 2;
new_max |= new_max >> 4;
@@ -123,8 +126,11 @@ static int pfn_set_populated(struct xc_sr_context *ctx, xen_pfn_t pfn)
ctx->restore.max_populated_pfn = new_max;
}
- assert(!test_bit(pfn, ctx->restore.populated_pfns));
- set_bit(pfn, ctx->restore.populated_pfns);
+ for ( pfn = start_pfn; pfn <= end_pfn; ++pfn )
+ {
+ assert(!test_bit(pfn, ctx->restore.populated_pfns));
+ set_bit(pfn, ctx->restore.populated_pfns);
+ }
return 0;
}
@@ -138,60 +144,40 @@ int populate_pfns(struct xc_sr_context *ctx, unsigned int count,
const xen_pfn_t *original_pfns, const uint32_t *types)
{
xc_interface *xch = ctx->xch;
- xen_pfn_t *mfns = malloc(count * sizeof(*mfns)),
- *pfns = malloc(count * sizeof(*pfns));
- unsigned int i, nr_pfns = 0;
+ xen_pfn_t mfn, pfn;
+ unsigned int i, order;
int rc = -1;
- if ( !mfns || !pfns )
- {
- ERROR("Failed to allocate %zu bytes for populating the physmap",
- 2 * count * sizeof(*mfns));
- goto err;
- }
+ /* Feed this record for family dependant heuristic to guess the physmap */
+ ctx->restore.ops.guess_physmap(ctx, count, original_pfns, types);
for ( i = 0; i < count; ++i )
{
if ( (!types || page_type_to_populate(types[i])) &&
!pfn_is_populated(ctx, original_pfns[i]) )
{
- rc = pfn_set_populated(ctx, original_pfns[i]);
+ order = ctx->restore.ops.get_physmap_order(ctx, original_pfns[i]);
+ rc = pfn_set_populated(ctx, original_pfns[i], order);
if ( rc )
goto err;
- pfns[nr_pfns] = mfns[nr_pfns] = original_pfns[i];
- ++nr_pfns;
- }
- }
-
- if ( nr_pfns )
- {
- rc = xc_domain_populate_physmap_exact(
- xch, ctx->domid, nr_pfns, 0, 0, mfns);
- if ( rc )
- {
- PERROR("Failed to populate physmap");
- goto err;
- }
- for ( i = 0; i < nr_pfns; ++i )
- {
- if ( mfns[i] == INVALID_MFN )
+ pfn = mfn = ROUNDDOWN(original_pfns[i], order);
+ rc = xc_domain_populate_physmap_exact(xch, ctx->domid, 1, order, 0,
+ &mfn);
+ if ( rc || (mfn == INVALID_MFN) )
{
- ERROR("Populate physmap failed for pfn %u", i);
+ ERROR("Failed to populate physmap for pfn %lu (%u)", pfn, order);
rc = -1;
goto err;
}
- ctx->restore.ops.set_gfn(ctx, pfns[i], mfns[i]);
+ ctx->restore.ops.set_gfn(ctx, pfn, mfn, order);
}
}
rc = 0;
err:
- free(pfns);
- free(mfns);
-
return rc;
}
diff --git a/tools/libs/guest/xg_sr_restore_x86_hvm.c b/tools/libs/guest/xg_sr_restore_x86_hvm.c
index d6ea6f3012..2e525443ab 100644
--- a/tools/libs/guest/xg_sr_restore_x86_hvm.c
+++ b/tools/libs/guest/xg_sr_restore_x86_hvm.c
@@ -110,7 +110,7 @@ static xen_pfn_t x86_hvm_pfn_to_gfn(const struct xc_sr_context *ctx,
/* restore_ops function. */
static void x86_hvm_set_gfn(struct xc_sr_context *ctx, xen_pfn_t pfn,
- xen_pfn_t gfn)
+ xen_pfn_t gfn, unsigned int order)
{
/* no op */
}
@@ -161,6 +161,8 @@ static int x86_hvm_setup(struct xc_sr_context *ctx)
}
#endif
+ ctx->x86.hvm.restore.set_guest_type = true;
+
return 0;
}
@@ -192,6 +194,88 @@ static int x86_hvm_process_record(struct xc_sr_context *ctx,
}
}
+/*
+ * We consider that PVH guest physmap starts from 0 and coninugiously cover the
+ * pysical memory space for the first GB of memory. HVM guest will have I/0
+ * holes in the first 2MB of memory space (at least for VGA). Therefore we
+ * should observe the very first record (wich comes in physmap order) to find
+ * out how we should map this first GB.
+ * To map the rest of the memory space in both cases (PVH or HVM) we will use
+ * the maximum available order (up to 1GB), except for forth GB wich holds the
+ * low MMIO hole (at least for LAPIC MMIO window and for potential passthroughed
+ * or emulated PCI devices BARs).
+ */
+static void x86_hvm_guess_physmap(struct xc_sr_context *ctx, unsigned int count,
+ const xen_pfn_t *pfns, const uint32_t *types)
+{
+ xen_pfn_t prev;
+ unsigned int i;
+
+
+ if ( !ctx->x86.hvm.restore.set_guest_type )
+ return;
+
+ for ( i = 0, prev = INVALID_PFN; i < count; ++i )
+ {
+ if ( !types || page_type_to_populate(types[i]) )
+ {
+ if ( prev == INVALID_MFN )
+ {
+ if (pfns[i] != 0)
+ break;
+ }
+ else
+ {
+ if ( pfns[i] != (prev + 1) )
+ break;
+ }
+ prev = pfns[i];
+ }
+ }
+
+ ctx->x86.hvm.restore.pvh_guest = (i == count) ? true : false;
+ ctx->x86.hvm.restore.set_guest_type = false;
+}
+
+/*
+ *
+ */
+static int x86_hvm_get_physmap_order(const struct xc_sr_context *ctx,
+ xen_pfn_t pfn)
+{
+ int order;
+
+ if ( pfn >= ctx->restore.p2m_size )
+ return 0;
+
+ switch (pfn >> S_PAGE_1GB_ORDER)
+ {
+ case 3:
+ /* The forth GB of memory is mapped with 2MB superpages */
+ order = S_PAGE_2MB_ORDER;
+ break;
+ case 0:
+ if (!ctx->x86.hvm.restore.pvh_guest)
+ {
+ /* First 2MB are mapped as 4K for HVM guest */
+ order = (pfn > 0x1ff) ? S_PAGE_2MB_ORDER : 0;
+ break;
+ }
+ default:
+ order = S_PAGE_1GB_ORDER;
+ }
+
+ if ( ((ROUNDUP(pfn + 1, S_PAGE_1GB_ORDER) - 1) >= ctx->restore.p2m_size) &&
+ order == S_PAGE_1GB_ORDER )
+ order = S_PAGE_2MB_ORDER;
+
+ if ( ((ROUNDUP(pfn + 1, S_PAGE_2MB_ORDER) - 1) >= ctx->restore.p2m_size) &&
+ order == S_PAGE_2MB_ORDER )
+ order = 0;
+
+ return order;
+}
+
/*
* restore_ops function. Sets extra hvm parameters and seeds the grant table.
*/
@@ -258,6 +342,8 @@ struct xc_sr_restore_ops restore_ops_x86_hvm =
.localise_page = x86_hvm_localise_page,
.setup = x86_hvm_setup,
.process_record = x86_hvm_process_record,
+ .guess_physmap = x86_hvm_guess_physmap,
+ .get_physmap_order = x86_hvm_get_physmap_order,
.static_data_complete = x86_static_data_complete,
.stream_complete = x86_hvm_stream_complete,
.cleanup = x86_hvm_cleanup,
diff --git a/tools/libs/guest/xg_sr_restore_x86_pv.c b/tools/libs/guest/xg_sr_restore_x86_pv.c
index dc50b0f5a8..f8545f941a 100644
--- a/tools/libs/guest/xg_sr_restore_x86_pv.c
+++ b/tools/libs/guest/xg_sr_restore_x86_pv.c
@@ -59,7 +59,7 @@ static int expand_p2m(struct xc_sr_context *ctx, unsigned long max_pfn)
ctx->x86.pv.max_pfn = max_pfn;
for ( i = (old_max ? old_max + 1 : 0); i <= max_pfn; ++i )
{
- ctx->restore.ops.set_gfn(ctx, i, INVALID_MFN);
+ ctx->restore.ops.set_gfn(ctx, i, INVALID_MFN, 0);
ctx->restore.ops.set_page_type(ctx, i, 0);
}
@@ -947,9 +947,10 @@ static void x86_pv_set_page_type(struct xc_sr_context *ctx, xen_pfn_t pfn,
/* restore_ops function. */
static void x86_pv_set_gfn(struct xc_sr_context *ctx, xen_pfn_t pfn,
- xen_pfn_t mfn)
+ xen_pfn_t mfn, unsigned int order)
{
assert(pfn <= ctx->x86.pv.max_pfn);
+ assert(!order);
if ( ctx->x86.pv.width == sizeof(uint64_t) )
/* 64 bit guest. Need to expand INVALID_MFN for 32 bit toolstacks. */
@@ -1113,6 +1114,21 @@ static int x86_pv_process_record(struct xc_sr_context *ctx,
}
}
+/*
+ * There's no reliable heuristic which can predict the PV guest physmap.
+ * Therefore the 0 order always will be used.
+ */
+static void x86_pv_guess_physmap(struct xc_sr_context *ctx, unsigned int count,
+ const xen_pfn_t *pfns, const uint32_t *types)
+{
+}
+
+static int x86_pv_get_physmap_order(const struct xc_sr_context *ctx,
+ xen_pfn_t pfn)
+{
+ return 0;
+}
+
/*
* restore_ops function. Update the vcpu context in Xen, pin the pagetables,
* rewrite the p2m and seed the grant table.
@@ -1194,6 +1210,8 @@ struct xc_sr_restore_ops restore_ops_x86_pv =
.localise_page = x86_pv_localise_page,
.setup = x86_pv_setup,
.process_record = x86_pv_process_record,
+ .guess_physmap = x86_pv_guess_physmap,
+ .get_physmap_order = x86_pv_get_physmap_order,
.static_data_complete = x86_static_data_complete,
.stream_complete = x86_pv_stream_complete,
.cleanup = x86_pv_cleanup,
--
2.34.1
Andrei Semenov | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
w: vates.fr | xcp-ng.org | xen-orchestra.com
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/2] live migration: do not use deffered bitmap when inappropriate
2022-09-06 9:54 ` [PATCH v2 1/2] live migration: do not use deffered bitmap when inappropriate Andrei Semenov
@ 2022-09-07 7:39 ` Jan Beulich
0 siblings, 0 replies; 7+ messages in thread
From: Jan Beulich @ 2022-09-07 7:39 UTC (permalink / raw)
To: Andrei Semenov; +Cc: Wei Liu, Anthony PERARD, Juergen Gross, xen-devel
On 06.09.2022 11:54, Andrei Semenov wrote:
> Use deffered bitmap only in PV guests context as it not used for HVM guests.
> This allow to reduce memory pressure on domain0 while migrating very large
> (memory wise) HVM guests.
>
> Signed-off-by: Andrei Semenov <andrei.semenov@vates.fr>
Did you see https://lists.xen.org/archives/html/xen-devel/2022-04/msg02037.html?
It would seem to me that doing what you want would be less intrusive on top of
that work, by simply suppressing the allocation also for HVM then.
Jan
> ---
> tools/libs/guest/xg_sr_common.h | 26 ++++++++++++++++--
> tools/libs/guest/xg_sr_save.c | 23 +++++++---------
> tools/libs/guest/xg_sr_save_x86_hvm.c | 21 +++++++++++++++
> tools/libs/guest/xg_sr_save_x86_pv.c | 39 +++++++++++++++++++++++++++
> 4 files changed, 93 insertions(+), 16 deletions(-)
>
> diff --git a/tools/libs/guest/xg_sr_common.h b/tools/libs/guest/xg_sr_common.h
> index 36d45ef56f..941e24d7b7 100644
> --- a/tools/libs/guest/xg_sr_common.h
> +++ b/tools/libs/guest/xg_sr_common.h
> @@ -96,6 +96,24 @@ struct xc_sr_save_ops
> */
> int (*check_vm_state)(struct xc_sr_context *ctx);
>
> + /**
> + * For some reasons the page can't be sent for the moment. Postpone this
> + * send to the later stage when domain is suspended.
> + */
> + int (*defer_page)(struct xc_sr_context *ctx, xen_pfn_t pfn);
> +
> + /**
> + * Merge all deferred pages with the dirty pages bitmap (in order to be
> + * sent).
> + */
> + int (*merge_deferred)(const struct xc_sr_context *ctx,
> + unsigned long *bitmap, unsigned long *count);
> +
> + /**
> + * Deferred pages was successfully sent. Reset all associated information.
> + */
> + int (*reset_deferred)(struct xc_sr_context *ctx);
> +
> /**
> * Clean up the local environment. Will be called exactly once, either
> * after a successful save, or upon encountering an error.
> @@ -243,8 +261,6 @@ struct xc_sr_context
>
> xen_pfn_t *batch_pfns;
> unsigned int nr_batch_pfns;
> - unsigned long *deferred_pages;
> - unsigned long nr_deferred_pages;
> xc_hypercall_buffer_t dirty_bitmap_hbuf;
> } save;
>
> @@ -349,6 +365,12 @@ struct xc_sr_context
>
> union
> {
> + struct
> + {
> + unsigned long *deferred_pages;
> + unsigned long nr_deferred_pages;
> + } save;
> +
> struct
> {
> /* State machine for the order of received records. */
> diff --git a/tools/libs/guest/xg_sr_save.c b/tools/libs/guest/xg_sr_save.c
> index 9853d8d846..602b18488d 100644
> --- a/tools/libs/guest/xg_sr_save.c
> +++ b/tools/libs/guest/xg_sr_save.c
> @@ -132,8 +132,7 @@ static int write_batch(struct xc_sr_context *ctx)
> /* Likely a ballooned page. */
> if ( mfns[i] == INVALID_MFN )
> {
> - set_bit(ctx->save.batch_pfns[i], ctx->save.deferred_pages);
> - ++ctx->save.nr_deferred_pages;
> + ctx->save.ops.defer_page(ctx, ctx->save.batch_pfns[i]);
> }
> }
>
> @@ -192,8 +191,7 @@ static int write_batch(struct xc_sr_context *ctx)
> {
> if ( rc == -1 && errno == EAGAIN )
> {
> - set_bit(ctx->save.batch_pfns[i], ctx->save.deferred_pages);
> - ++ctx->save.nr_deferred_pages;
> + ctx->save.ops.defer_page(ctx, ctx->save.batch_pfns[i]);
> types[i] = XEN_DOMCTL_PFINFO_XTAB;
> --nr_pages;
> }
> @@ -641,6 +639,7 @@ static int suspend_and_send_dirty(struct xc_sr_context *ctx)
> xc_interface *xch = ctx->xch;
> xc_shadow_op_stats_t stats = { 0, ctx->save.p2m_size };
> char *progress_str = NULL;
> + unsigned long merged;
> int rc;
> DECLARE_HYPERCALL_BUFFER_SHADOW(unsigned long, dirty_bitmap,
> &ctx->save.dirty_bitmap_hbuf);
> @@ -669,7 +668,7 @@ static int suspend_and_send_dirty(struct xc_sr_context *ctx)
> else
> xc_set_progress_prefix(xch, "Checkpointed save");
>
> - bitmap_or(dirty_bitmap, ctx->save.deferred_pages, ctx->save.p2m_size);
> + ctx->save.ops.merge_deferred(ctx, dirty_bitmap, &merged);
>
> if ( !ctx->save.live && ctx->stream_type == XC_STREAM_COLO )
> {
> @@ -681,12 +680,11 @@ static int suspend_and_send_dirty(struct xc_sr_context *ctx)
> }
> }
>
> - rc = send_dirty_pages(ctx, stats.dirty_count + ctx->save.nr_deferred_pages);
> + rc = send_dirty_pages(ctx, stats.dirty_count + merged);
> if ( rc )
> goto out;
>
> - bitmap_clear(ctx->save.deferred_pages, ctx->save.p2m_size);
> - ctx->save.nr_deferred_pages = 0;
> + ctx->save.ops.reset_deferred(ctx);
>
> out:
> xc_set_progress_prefix(xch, NULL);
> @@ -805,18 +803,16 @@ static int setup(struct xc_sr_context *ctx)
> xch, dirty_bitmap, NRPAGES(bitmap_size(ctx->save.p2m_size)));
> ctx->save.batch_pfns = malloc(MAX_BATCH_SIZE *
> sizeof(*ctx->save.batch_pfns));
> - ctx->save.deferred_pages = bitmap_alloc(ctx->save.p2m_size);
>
> - if ( !ctx->save.batch_pfns || !dirty_bitmap || !ctx->save.deferred_pages )
> + if ( !ctx->save.batch_pfns || !dirty_bitmap )
> {
> - ERROR("Unable to allocate memory for dirty bitmaps, batch pfns and"
> - " deferred pages");
> + ERROR("Unable to allocate memory for dirty bitmaps, batch pfns");
> rc = -1;
> errno = ENOMEM;
> goto err;
> }
>
> - rc = 0;
> + rc = ctx->save.ops.reset_deferred(ctx);
>
> err:
> return rc;
> @@ -837,7 +833,6 @@ static void cleanup(struct xc_sr_context *ctx)
>
> xc_hypercall_buffer_free_pages(xch, dirty_bitmap,
> NRPAGES(bitmap_size(ctx->save.p2m_size)));
> - free(ctx->save.deferred_pages);
> free(ctx->save.batch_pfns);
> }
>
> diff --git a/tools/libs/guest/xg_sr_save_x86_hvm.c b/tools/libs/guest/xg_sr_save_x86_hvm.c
> index 1634a7bc43..3c762a0af0 100644
> --- a/tools/libs/guest/xg_sr_save_x86_hvm.c
> +++ b/tools/libs/guest/xg_sr_save_x86_hvm.c
> @@ -211,6 +211,24 @@ static int x86_hvm_end_of_checkpoint(struct xc_sr_context *ctx)
> return 0;
> }
>
> +static int x86_hvm_defer_page(struct xc_sr_context *ctx, xen_pfn_t pfn)
> +{
> + return 0;
> +}
> +
> +static int x86_hvm_merge_deferred(const struct xc_sr_context *ctx,
> + unsigned long *bitmap, unsigned long *count)
> +{
> + *count = 0;
> +
> + return 0;
> +}
> +
> +static int x86_hvm_reset_deferred(struct xc_sr_context *ctx)
> +{
> + return 0;
> +}
> +
> static int x86_hvm_cleanup(struct xc_sr_context *ctx)
> {
> xc_interface *xch = ctx->xch;
> @@ -237,6 +255,9 @@ struct xc_sr_save_ops save_ops_x86_hvm =
> .start_of_checkpoint = x86_hvm_start_of_checkpoint,
> .end_of_checkpoint = x86_hvm_end_of_checkpoint,
> .check_vm_state = x86_hvm_check_vm_state,
> + .defer_page = x86_hvm_defer_page,
> + .merge_deferred = x86_hvm_merge_deferred,
> + .reset_deferred = x86_hvm_reset_deferred,
> .cleanup = x86_hvm_cleanup,
> };
>
> diff --git a/tools/libs/guest/xg_sr_save_x86_pv.c b/tools/libs/guest/xg_sr_save_x86_pv.c
> index 4964f1f7b8..5fdc7e9590 100644
> --- a/tools/libs/guest/xg_sr_save_x86_pv.c
> +++ b/tools/libs/guest/xg_sr_save_x86_pv.c
> @@ -1031,6 +1031,7 @@ static int x86_pv_normalise_page(struct xc_sr_context *ctx, xen_pfn_t type,
> */
> static int x86_pv_setup(struct xc_sr_context *ctx)
> {
> + xc_interface *xch = ctx->xch;
> int rc;
>
> rc = x86_pv_domain_info(ctx);
> @@ -1049,6 +1050,15 @@ static int x86_pv_setup(struct xc_sr_context *ctx)
> if ( rc )
> return rc;
>
> + ctx->x86.pv.save.deferred_pages = bitmap_alloc(ctx->save.p2m_size);
> +
> + if (!ctx->x86.pv.save.deferred_pages)
> + {
> + ERROR("Unable to allocate memory for deferred pages");
> + errno = ENOMEM;
> + return -1;
> + }
> +
> return 0;
> }
>
> @@ -1116,9 +1126,35 @@ static int x86_pv_check_vm_state(struct xc_sr_context *ctx)
> return x86_pv_check_vm_state_p2m_list(ctx);
> }
>
> +static int x86_pv_defer_page(struct xc_sr_context *ctx, xen_pfn_t pfn)
> +{
> + set_bit(pfn, ctx->x86.pv.save.deferred_pages);
> + ++ctx->x86.pv.save.nr_deferred_pages;
> +
> + return 0;
> +}
> +
> +static int x86_pv_merge_deferred(const struct xc_sr_context *ctx,
> + unsigned long *bitmap, unsigned long *count)
> +{
> + bitmap_or(bitmap, ctx->x86.pv.save.deferred_pages, ctx->save.p2m_size);
> + *count = ctx->x86.pv.save.nr_deferred_pages;
> +
> + return 0;
> +}
> +
> +static int x86_pv_reset_deferred(struct xc_sr_context *ctx)
> +{
> + bitmap_clear(ctx->x86.pv.save.deferred_pages, ctx->save.p2m_size);
> + ctx->x86.pv.save.nr_deferred_pages = 0;
> +
> + return 0;
> +}
> +
> static int x86_pv_cleanup(struct xc_sr_context *ctx)
> {
> free(ctx->x86.pv.p2m_pfns);
> + free(ctx->x86.pv.save.deferred_pages);
>
> if ( ctx->x86.pv.p2m )
> munmap(ctx->x86.pv.p2m, ctx->x86.pv.p2m_frames * PAGE_SIZE);
> @@ -1142,6 +1178,9 @@ struct xc_sr_save_ops save_ops_x86_pv =
> .start_of_checkpoint = x86_pv_start_of_checkpoint,
> .end_of_checkpoint = x86_pv_end_of_checkpoint,
> .check_vm_state = x86_pv_check_vm_state,
> + .defer_page = x86_pv_defer_page,
> + .merge_deferred = x86_pv_merge_deferred,
> + .reset_deferred = x86_pv_reset_deferred,
> .cleanup = x86_pv_cleanup,
> };
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/2] live migration: use superpages for physmap population on restore when possible
2022-09-06 9:54 ` [PATCH v2 2/2] live migration: use superpages for physmap population on restore when possible Andrei Semenov
@ 2022-09-07 7:40 ` Jan Beulich
2022-09-07 7:45 ` Olaf Hering
0 siblings, 1 reply; 7+ messages in thread
From: Jan Beulich @ 2022-09-07 7:40 UTC (permalink / raw)
To: Andrei Semenov, Olaf Hering
Cc: Wei Liu, Anthony PERARD, Juergen Gross, xen-devel
On 06.09.2022 11:54, Andrei Semenov wrote:
> Implement an heuristic for X86 HVM guests which tries to use superpages while
> populating guest physmap on live migration. This should impove memory accesses
> performances for these guests.
>
> Signed-off-by: Andrei Semenov <andrei.semenov@vates.fr>
Olaf - I recall you've done some similar work before. Do you have any
thoughts here, perhaps going as far as merging your and Andrei's work?
Jan
> ---
> tools/include/xen-tools/libs.h | 4 ++
> tools/libs/guest/xg_private.h | 3 +
> tools/libs/guest/xg_sr_common.h | 18 ++++-
> tools/libs/guest/xg_sr_restore.c | 60 +++++++---------
> tools/libs/guest/xg_sr_restore_x86_hvm.c | 88 +++++++++++++++++++++++-
> tools/libs/guest/xg_sr_restore_x86_pv.c | 22 +++++-
> 6 files changed, 154 insertions(+), 41 deletions(-)
>
> diff --git a/tools/include/xen-tools/libs.h b/tools/include/xen-tools/libs.h
> index a16e0c3807..bdd903eb7b 100644
> --- a/tools/include/xen-tools/libs.h
> +++ b/tools/include/xen-tools/libs.h
> @@ -63,4 +63,8 @@
> #define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1))
> #endif
>
> +#ifndef ROUNDDOWN
> +#define ROUNDDOWN(_x,_w) ((unsigned long)(_x) & (-1UL << (_w)))
> +#endif
> +
> #endif /* __XEN_TOOLS_LIBS__ */
> diff --git a/tools/libs/guest/xg_private.h b/tools/libs/guest/xg_private.h
> index 09e24f1227..dcf63b5188 100644
> --- a/tools/libs/guest/xg_private.h
> +++ b/tools/libs/guest/xg_private.h
> @@ -134,6 +134,9 @@ typedef uint64_t x86_pgentry_t;
> #define PAGE_SIZE_X86 (1UL << PAGE_SHIFT_X86)
> #define PAGE_MASK_X86 (~(PAGE_SIZE_X86-1))
>
> +#define S_PAGE_1GB_ORDER 18
> +#define S_PAGE_2MB_ORDER 9
> +
> #define NRPAGES(x) (ROUNDUP(x, PAGE_SHIFT) >> PAGE_SHIFT)
>
> static inline xen_pfn_t xc_pfn_to_mfn(xen_pfn_t pfn, xen_pfn_t *p2m,
> diff --git a/tools/libs/guest/xg_sr_common.h b/tools/libs/guest/xg_sr_common.h
> index 941e24d7b7..96365e05a8 100644
> --- a/tools/libs/guest/xg_sr_common.h
> +++ b/tools/libs/guest/xg_sr_common.h
> @@ -137,7 +137,8 @@ struct xc_sr_restore_ops
> bool (*pfn_is_valid)(const struct xc_sr_context *ctx, xen_pfn_t pfn);
>
> /* Set the GFN of a PFN. */
> - void (*set_gfn)(struct xc_sr_context *ctx, xen_pfn_t pfn, xen_pfn_t gfn);
> + void (*set_gfn)(struct xc_sr_context *ctx, xen_pfn_t pfn, xen_pfn_t gfn,
> + unsigned int order);
>
> /* Set the type of a PFN. */
> void (*set_page_type)(struct xc_sr_context *ctx, xen_pfn_t pfn,
> @@ -175,6 +176,17 @@ struct xc_sr_restore_ops
> #define BROKEN_CHANNEL 2
> int (*process_record)(struct xc_sr_context *ctx, struct xc_sr_record *rec);
>
> + /**
> + * Guest physmap population order is based on heuristic which is family
> + * dependant. X86 HVM heuristic is interested in observing the whole
> + * record (the first) in order to guess how the physmap should be populated.
> + */
> + void (*guess_physmap)(struct xc_sr_context *ctx, unsigned int count,
> + const xen_pfn_t *pfns, const uint32_t *types);
> +
> + /* Get the physmap population order for given PFN */
> + int (*get_physmap_order)(const struct xc_sr_context *ctx, xen_pfn_t pfn);
> +
> /**
> * Perform any actions required after the static data has arrived. Called
> * when the STATIC_DATA_COMPLETE record has been recieved/inferred.
> @@ -404,6 +416,10 @@ struct xc_sr_context
> {
> /* HVM context blob. */
> struct xc_sr_blob context;
> +
> + /* Set guest type (based on the first record) */
> + bool set_guest_type;
> + bool pvh_guest;
> } restore;
> };
> } hvm;
> diff --git a/tools/libs/guest/xg_sr_restore.c b/tools/libs/guest/xg_sr_restore.c
> index 074b56d263..af864bd5ea 100644
> --- a/tools/libs/guest/xg_sr_restore.c
> +++ b/tools/libs/guest/xg_sr_restore.c
> @@ -86,18 +86,21 @@ static bool pfn_is_populated(const struct xc_sr_context *ctx, xen_pfn_t pfn)
> * avoid realloc()ing too excessively, the size increased to the nearest power
> * of two large enough to contain the required pfn.
> */
> -static int pfn_set_populated(struct xc_sr_context *ctx, xen_pfn_t pfn)
> +static int pfn_set_populated(struct xc_sr_context *ctx, xen_pfn_t pfn,
> + unsigned int order)
> {
> xc_interface *xch = ctx->xch;
> + xen_pfn_t start_pfn = ROUNDDOWN(pfn, order),
> + end_pfn = (ROUNDUP(pfn + 1, order) - 1);
>
> - if ( pfn > ctx->restore.max_populated_pfn )
> + if ( end_pfn > ctx->restore.max_populated_pfn )
> {
> xen_pfn_t new_max;
> size_t old_sz, new_sz;
> unsigned long *p;
>
> /* Round up to the nearest power of two larger than pfn, less 1. */
> - new_max = pfn;
> + new_max = end_pfn;
> new_max |= new_max >> 1;
> new_max |= new_max >> 2;
> new_max |= new_max >> 4;
> @@ -123,8 +126,11 @@ static int pfn_set_populated(struct xc_sr_context *ctx, xen_pfn_t pfn)
> ctx->restore.max_populated_pfn = new_max;
> }
>
> - assert(!test_bit(pfn, ctx->restore.populated_pfns));
> - set_bit(pfn, ctx->restore.populated_pfns);
> + for ( pfn = start_pfn; pfn <= end_pfn; ++pfn )
> + {
> + assert(!test_bit(pfn, ctx->restore.populated_pfns));
> + set_bit(pfn, ctx->restore.populated_pfns);
> + }
>
> return 0;
> }
> @@ -138,60 +144,40 @@ int populate_pfns(struct xc_sr_context *ctx, unsigned int count,
> const xen_pfn_t *original_pfns, const uint32_t *types)
> {
> xc_interface *xch = ctx->xch;
> - xen_pfn_t *mfns = malloc(count * sizeof(*mfns)),
> - *pfns = malloc(count * sizeof(*pfns));
> - unsigned int i, nr_pfns = 0;
> + xen_pfn_t mfn, pfn;
> + unsigned int i, order;
> int rc = -1;
>
> - if ( !mfns || !pfns )
> - {
> - ERROR("Failed to allocate %zu bytes for populating the physmap",
> - 2 * count * sizeof(*mfns));
> - goto err;
> - }
> + /* Feed this record for family dependant heuristic to guess the physmap */
> + ctx->restore.ops.guess_physmap(ctx, count, original_pfns, types);
>
> for ( i = 0; i < count; ++i )
> {
> if ( (!types || page_type_to_populate(types[i])) &&
> !pfn_is_populated(ctx, original_pfns[i]) )
> {
> - rc = pfn_set_populated(ctx, original_pfns[i]);
> + order = ctx->restore.ops.get_physmap_order(ctx, original_pfns[i]);
> + rc = pfn_set_populated(ctx, original_pfns[i], order);
> if ( rc )
> goto err;
> - pfns[nr_pfns] = mfns[nr_pfns] = original_pfns[i];
> - ++nr_pfns;
> - }
> - }
> -
> - if ( nr_pfns )
> - {
> - rc = xc_domain_populate_physmap_exact(
> - xch, ctx->domid, nr_pfns, 0, 0, mfns);
> - if ( rc )
> - {
> - PERROR("Failed to populate physmap");
> - goto err;
> - }
>
> - for ( i = 0; i < nr_pfns; ++i )
> - {
> - if ( mfns[i] == INVALID_MFN )
> + pfn = mfn = ROUNDDOWN(original_pfns[i], order);
> + rc = xc_domain_populate_physmap_exact(xch, ctx->domid, 1, order, 0,
> + &mfn);
> + if ( rc || (mfn == INVALID_MFN) )
> {
> - ERROR("Populate physmap failed for pfn %u", i);
> + ERROR("Failed to populate physmap for pfn %lu (%u)", pfn, order);
> rc = -1;
> goto err;
> }
>
> - ctx->restore.ops.set_gfn(ctx, pfns[i], mfns[i]);
> + ctx->restore.ops.set_gfn(ctx, pfn, mfn, order);
> }
> }
>
> rc = 0;
>
> err:
> - free(pfns);
> - free(mfns);
> -
> return rc;
> }
>
> diff --git a/tools/libs/guest/xg_sr_restore_x86_hvm.c b/tools/libs/guest/xg_sr_restore_x86_hvm.c
> index d6ea6f3012..2e525443ab 100644
> --- a/tools/libs/guest/xg_sr_restore_x86_hvm.c
> +++ b/tools/libs/guest/xg_sr_restore_x86_hvm.c
> @@ -110,7 +110,7 @@ static xen_pfn_t x86_hvm_pfn_to_gfn(const struct xc_sr_context *ctx,
>
> /* restore_ops function. */
> static void x86_hvm_set_gfn(struct xc_sr_context *ctx, xen_pfn_t pfn,
> - xen_pfn_t gfn)
> + xen_pfn_t gfn, unsigned int order)
> {
> /* no op */
> }
> @@ -161,6 +161,8 @@ static int x86_hvm_setup(struct xc_sr_context *ctx)
> }
> #endif
>
> + ctx->x86.hvm.restore.set_guest_type = true;
> +
> return 0;
> }
>
> @@ -192,6 +194,88 @@ static int x86_hvm_process_record(struct xc_sr_context *ctx,
> }
> }
>
> +/*
> + * We consider that PVH guest physmap starts from 0 and coninugiously cover the
> + * pysical memory space for the first GB of memory. HVM guest will have I/0
> + * holes in the first 2MB of memory space (at least for VGA). Therefore we
> + * should observe the very first record (wich comes in physmap order) to find
> + * out how we should map this first GB.
> + * To map the rest of the memory space in both cases (PVH or HVM) we will use
> + * the maximum available order (up to 1GB), except for forth GB wich holds the
> + * low MMIO hole (at least for LAPIC MMIO window and for potential passthroughed
> + * or emulated PCI devices BARs).
> + */
> +static void x86_hvm_guess_physmap(struct xc_sr_context *ctx, unsigned int count,
> + const xen_pfn_t *pfns, const uint32_t *types)
> +{
> + xen_pfn_t prev;
> + unsigned int i;
> +
> +
> + if ( !ctx->x86.hvm.restore.set_guest_type )
> + return;
> +
> + for ( i = 0, prev = INVALID_PFN; i < count; ++i )
> + {
> + if ( !types || page_type_to_populate(types[i]) )
> + {
> + if ( prev == INVALID_MFN )
> + {
> + if (pfns[i] != 0)
> + break;
> + }
> + else
> + {
> + if ( pfns[i] != (prev + 1) )
> + break;
> + }
> + prev = pfns[i];
> + }
> + }
> +
> + ctx->x86.hvm.restore.pvh_guest = (i == count) ? true : false;
> + ctx->x86.hvm.restore.set_guest_type = false;
> +}
> +
> +/*
> + *
> + */
> +static int x86_hvm_get_physmap_order(const struct xc_sr_context *ctx,
> + xen_pfn_t pfn)
> +{
> + int order;
> +
> + if ( pfn >= ctx->restore.p2m_size )
> + return 0;
> +
> + switch (pfn >> S_PAGE_1GB_ORDER)
> + {
> + case 3:
> + /* The forth GB of memory is mapped with 2MB superpages */
> + order = S_PAGE_2MB_ORDER;
> + break;
> + case 0:
> + if (!ctx->x86.hvm.restore.pvh_guest)
> + {
> + /* First 2MB are mapped as 4K for HVM guest */
> + order = (pfn > 0x1ff) ? S_PAGE_2MB_ORDER : 0;
> + break;
> + }
> + default:
> + order = S_PAGE_1GB_ORDER;
> + }
> +
> + if ( ((ROUNDUP(pfn + 1, S_PAGE_1GB_ORDER) - 1) >= ctx->restore.p2m_size) &&
> + order == S_PAGE_1GB_ORDER )
> + order = S_PAGE_2MB_ORDER;
> +
> + if ( ((ROUNDUP(pfn + 1, S_PAGE_2MB_ORDER) - 1) >= ctx->restore.p2m_size) &&
> + order == S_PAGE_2MB_ORDER )
> + order = 0;
> +
> + return order;
> +}
> +
> /*
> * restore_ops function. Sets extra hvm parameters and seeds the grant table.
> */
> @@ -258,6 +342,8 @@ struct xc_sr_restore_ops restore_ops_x86_hvm =
> .localise_page = x86_hvm_localise_page,
> .setup = x86_hvm_setup,
> .process_record = x86_hvm_process_record,
> + .guess_physmap = x86_hvm_guess_physmap,
> + .get_physmap_order = x86_hvm_get_physmap_order,
> .static_data_complete = x86_static_data_complete,
> .stream_complete = x86_hvm_stream_complete,
> .cleanup = x86_hvm_cleanup,
> diff --git a/tools/libs/guest/xg_sr_restore_x86_pv.c b/tools/libs/guest/xg_sr_restore_x86_pv.c
> index dc50b0f5a8..f8545f941a 100644
> --- a/tools/libs/guest/xg_sr_restore_x86_pv.c
> +++ b/tools/libs/guest/xg_sr_restore_x86_pv.c
> @@ -59,7 +59,7 @@ static int expand_p2m(struct xc_sr_context *ctx, unsigned long max_pfn)
> ctx->x86.pv.max_pfn = max_pfn;
> for ( i = (old_max ? old_max + 1 : 0); i <= max_pfn; ++i )
> {
> - ctx->restore.ops.set_gfn(ctx, i, INVALID_MFN);
> + ctx->restore.ops.set_gfn(ctx, i, INVALID_MFN, 0);
> ctx->restore.ops.set_page_type(ctx, i, 0);
> }
>
> @@ -947,9 +947,10 @@ static void x86_pv_set_page_type(struct xc_sr_context *ctx, xen_pfn_t pfn,
>
> /* restore_ops function. */
> static void x86_pv_set_gfn(struct xc_sr_context *ctx, xen_pfn_t pfn,
> - xen_pfn_t mfn)
> + xen_pfn_t mfn, unsigned int order)
> {
> assert(pfn <= ctx->x86.pv.max_pfn);
> + assert(!order);
>
> if ( ctx->x86.pv.width == sizeof(uint64_t) )
> /* 64 bit guest. Need to expand INVALID_MFN for 32 bit toolstacks. */
> @@ -1113,6 +1114,21 @@ static int x86_pv_process_record(struct xc_sr_context *ctx,
> }
> }
>
> +/*
> + * There's no reliable heuristic which can predict the PV guest physmap.
> + * Therefore the 0 order always will be used.
> + */
> +static void x86_pv_guess_physmap(struct xc_sr_context *ctx, unsigned int count,
> + const xen_pfn_t *pfns, const uint32_t *types)
> +{
> +}
> +
> +static int x86_pv_get_physmap_order(const struct xc_sr_context *ctx,
> + xen_pfn_t pfn)
> +{
> + return 0;
> +}
> +
> /*
> * restore_ops function. Update the vcpu context in Xen, pin the pagetables,
> * rewrite the p2m and seed the grant table.
> @@ -1194,6 +1210,8 @@ struct xc_sr_restore_ops restore_ops_x86_pv =
> .localise_page = x86_pv_localise_page,
> .setup = x86_pv_setup,
> .process_record = x86_pv_process_record,
> + .guess_physmap = x86_pv_guess_physmap,
> + .get_physmap_order = x86_pv_get_physmap_order,
> .static_data_complete = x86_static_data_complete,
> .stream_complete = x86_pv_stream_complete,
> .cleanup = x86_pv_cleanup,
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/2] live migration: use superpages for physmap population on restore when possible
2022-09-07 7:40 ` Jan Beulich
@ 2022-09-07 7:45 ` Olaf Hering
2022-09-08 9:22 ` Julien Grall
0 siblings, 1 reply; 7+ messages in thread
From: Olaf Hering @ 2022-09-07 7:45 UTC (permalink / raw)
To: Jan Beulich
Cc: Andrei Semenov, Wei Liu, Anthony PERARD, Juergen Gross, xen-devel
[-- Attachment #1: Type: text/plain, Size: 337 bytes --]
Wed, 7 Sep 2022 09:40:41 +0200 Jan Beulich <jbeulich@suse.com>:
> Olaf - I recall you've done some similar work before. Do you have any
> thoughts here, perhaps going as far as merging your and Andrei's work?
I have no hope that any such work will be merged.
My variant is in the archives, for reference.
Thanks,
Olaf
[-- Attachment #2: Digitale Signatur von OpenPGP --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/2] live migration: use superpages for physmap population on restore when possible
2022-09-07 7:45 ` Olaf Hering
@ 2022-09-08 9:22 ` Julien Grall
0 siblings, 0 replies; 7+ messages in thread
From: Julien Grall @ 2022-09-08 9:22 UTC (permalink / raw)
To: Olaf Hering, Jan Beulich
Cc: Andrei Semenov, Wei Liu, Anthony PERARD, Juergen Gross,
xen-devel@lists.xenproject.org
Hi Olaf,
On 07/09/2022 08:45, Olaf Hering wrote:
> Wed, 7 Sep 2022 09:40:41 +0200 Jan Beulich <jbeulich@suse.com>:
>
>> Olaf - I recall you've done some similar work before. Do you have any
>> thoughts here, perhaps going as far as merging your and Andrei's work?
>
> > My variant is in the archives, for reference.
You are referring to [1], is that correct?
> I have no hope that any such work will be merged.
Is this because it was lack of review or are there any technical
difficulties/disagreements?
If the former, this is not unsolvable. Depending on which approach we
go, one of you could review the series of the other.
With my AWS hat on, I would also be interested with the optimization in
the LM code. So I would be willing to help reviewing it. That said, I
can't promise any in-depth review before 4.17 is out.
Cheers,
[1] https://lore.kernel.org/xen-devel/20210713180605.12096-1-olaf@aepfle.de
--
Julien Grall
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2022-09-08 9:23 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-09-06 9:54 [PATCH v2 0/2] live migration: optimisations Andrei Semenov
2022-09-06 9:54 ` [PATCH v2 1/2] live migration: do not use deffered bitmap when inappropriate Andrei Semenov
2022-09-07 7:39 ` Jan Beulich
2022-09-06 9:54 ` [PATCH v2 2/2] live migration: use superpages for physmap population on restore when possible Andrei Semenov
2022-09-07 7:40 ` Jan Beulich
2022-09-07 7:45 ` Olaf Hering
2022-09-08 9:22 ` Julien Grall
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.