mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 18:21:39 +01:00
[HVM] Save/restore: merge xc_linux_save and xc_hvm_save
into xc_domain_save, like we did for xc_domain_restore Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com> --HG-- rename : tools/libxc/xc_linux_save.c => tools/libxc/xc_domain_save.c
This commit is contained in:
1 parent
30fbfa471f
commit
d11bec8a19
7 files changed
+367
-946
No files matched your search
@@ -26,8 +26,8 @@ CTRL_SRCS-$(CONFIG_X86_Linux) += xc_ptrace.c xc_ptrace_core.c
|
||||
|
||||
GUEST_SRCS-y :=
|
||||
GUEST_SRCS-y += xg_private.c
|
||||
GUEST_SRCS-$(CONFIG_MIGRATE) += xc_domain_restore.c xc_linux_save.c
|
||||
GUEST_SRCS-$(CONFIG_HVM) += xc_hvm_build.c xc_hvm_save.c
|
||||
GUEST_SRCS-$(CONFIG_MIGRATE) += xc_domain_restore.c xc_domain_save.c
|
||||
GUEST_SRCS-$(CONFIG_HVM) += xc_hvm_build.c
|
||||
|
||||
# symlink libelf from xen/common/libelf/
|
||||
LIBELF_SRCS := libelf-tools.c libelf-loader.c
|
||||
|
||||
@@ -134,8 +134,10 @@ retry:
|
||||
}
|
||||
|
||||
int
|
||||
xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
uint32_t max_factor, uint32_t flags, int (*suspend)(int))
|
||||
xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
uint32_t max_factor, uint32_t flags, int (*suspend)(int),
|
||||
int hvm, void *(*init_qemu_maps)(int, unsigned),
|
||||
void (*qemu_flip_buffer)(int, int))
|
||||
{
|
||||
DECLARE_DOMCTL;
|
||||
xc_dominfo_t info;
|
||||
|
||||
@@ -17,9 +17,12 @@
|
||||
#include "xg_private.h"
|
||||
#include "xg_save_restore.h"
|
||||
|
||||
#include <xen/hvm/params.h>
|
||||
#include <xen/hvm/e820.h>
|
||||
|
||||
/*
|
||||
** Default values for important tuning parameters. Can override by passing
|
||||
** non-zero replacement values to xc_linux_save().
|
||||
** non-zero replacement values to xc_domain_save().
|
||||
**
|
||||
** XXX SMH: should consider if want to be able to override MAX_MBIT_RATE too.
|
||||
**
|
||||
@@ -36,6 +39,11 @@ static unsigned long hvirt_start;
|
||||
/* #levels of page tables used by the current guest */
|
||||
static unsigned int pt_levels;
|
||||
|
||||
/* HVM: shared-memory bitmaps for getting log-dirty bits from qemu-dm */
|
||||
static unsigned long *qemu_bitmaps[2];
|
||||
static int qemu_active;
|
||||
static int qemu_non_active;
|
||||
|
||||
/* number of pfns this guest has (i.e. number of entries in the P2M) */
|
||||
static unsigned long p2m_size;
|
||||
|
||||
@@ -77,7 +85,8 @@ static unsigned long m2p_mfn0;
|
||||
*/
|
||||
|
||||
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
|
||||
#define BITMAP_SIZE ((p2m_size + BITS_PER_LONG - 1) / 8)
|
||||
#define BITS_TO_LONGS(bits) (((bits)+BITS_PER_LONG-1)/BITS_PER_LONG)
|
||||
#define BITMAP_SIZE (BITS_TO_LONGS(p2m_size) * sizeof(unsigned long))
|
||||
|
||||
#define BITMAP_ENTRY(_nr,_bmap) \
|
||||
((volatile unsigned long *)(_bmap))[(_nr)/BITS_PER_LONG]
|
||||
@@ -650,8 +659,131 @@ static xen_pfn_t *xc_map_m2p(int xc_handle,
|
||||
return m2p;
|
||||
}
|
||||
|
||||
int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
uint32_t max_factor, uint32_t flags, int (*suspend)(int))
|
||||
|
||||
static xen_pfn_t *map_and_save_p2m_table(int xc_handle,
|
||||
int io_fd,
|
||||
uint32_t dom,
|
||||
vcpu_guest_context_t *ctxt,
|
||||
unsigned long p2m_size,
|
||||
shared_info_t *live_shinfo)
|
||||
{
|
||||
/* Double and single indirect references to the live P2M table */
|
||||
xen_pfn_t *live_p2m_frame_list_list = NULL;
|
||||
xen_pfn_t *live_p2m_frame_list = NULL;
|
||||
|
||||
/* A copy of the pfn-to-mfn table frame list. */
|
||||
xen_pfn_t *p2m_frame_list = NULL;
|
||||
|
||||
/* The mapping of the live p2m table itself */
|
||||
xen_pfn_t *p2m = NULL;
|
||||
|
||||
int i, success = 0;
|
||||
|
||||
live_p2m_frame_list_list = map_frame_list_list(xc_handle, dom,
|
||||
live_shinfo);
|
||||
if ( !live_p2m_frame_list_list )
|
||||
goto out;
|
||||
|
||||
live_p2m_frame_list =
|
||||
xc_map_foreign_batch(xc_handle, dom, PROT_READ,
|
||||
live_p2m_frame_list_list,
|
||||
P2M_FLL_ENTRIES);
|
||||
if ( !live_p2m_frame_list )
|
||||
{
|
||||
ERROR("Couldn't map p2m_frame_list");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
/* Map all the frames of the pfn->mfn table. For migrate to succeed,
|
||||
the guest must not change which frames are used for this purpose.
|
||||
(its not clear why it would want to change them, and we'll be OK
|
||||
from a safety POV anyhow. */
|
||||
|
||||
p2m = xc_map_foreign_batch(xc_handle, dom, PROT_READ,
|
||||
live_p2m_frame_list,
|
||||
P2M_FL_ENTRIES);
|
||||
if ( !p2m )
|
||||
{
|
||||
ERROR("Couldn't map p2m table");
|
||||
goto out;
|
||||
}
|
||||
live_p2m = p2m; /* So that translation macros will work */
|
||||
|
||||
/* Get a local copy of the live_P2M_frame_list */
|
||||
if ( !(p2m_frame_list = malloc(P2M_FL_SIZE)) )
|
||||
{
|
||||
ERROR("Couldn't allocate p2m_frame_list array");
|
||||
goto out;
|
||||
}
|
||||
memcpy(p2m_frame_list, live_p2m_frame_list, P2M_FL_SIZE);
|
||||
|
||||
/* Canonicalise the pfn-to-mfn table frame-number list. */
|
||||
for ( i = 0; i < p2m_size; i += fpp )
|
||||
{
|
||||
if ( !translate_mfn_to_pfn(&p2m_frame_list[i/fpp]) )
|
||||
{
|
||||
ERROR("Frame# in pfn-to-mfn frame list is not in pseudophys");
|
||||
ERROR("entry %d: p2m_frame_list[%ld] is 0x%"PRIx64, i, i/fpp,
|
||||
(uint64_t)p2m_frame_list[i/fpp]);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write an extended-info structure to inform the restore code that
|
||||
* a PAE guest understands extended CR3 (PDPTs above 4GB). Turns off
|
||||
* slow paths in the restore code.
|
||||
*/
|
||||
if ( (pt_levels == 3) &&
|
||||
(ctxt->vm_assist & (1UL << VMASST_TYPE_pae_extended_cr3)) )
|
||||
{
|
||||
unsigned long signature = ~0UL;
|
||||
uint32_t tot_sz = sizeof(struct vcpu_guest_context) + 8;
|
||||
uint32_t chunk_sz = sizeof(struct vcpu_guest_context);
|
||||
char chunk_sig[] = "vcpu";
|
||||
if ( !write_exact(io_fd, &signature, sizeof(signature)) ||
|
||||
!write_exact(io_fd, &tot_sz, sizeof(tot_sz)) ||
|
||||
!write_exact(io_fd, &chunk_sig, 4) ||
|
||||
!write_exact(io_fd, &chunk_sz, sizeof(chunk_sz)) ||
|
||||
!write_exact(io_fd, ctxt, sizeof(*ctxt)) )
|
||||
{
|
||||
ERROR("write: extended info");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !write_exact(io_fd, p2m_frame_list, P2M_FL_SIZE) )
|
||||
{
|
||||
ERROR("write: p2m_frame_list");
|
||||
goto out;
|
||||
}
|
||||
|
||||
success = 1;
|
||||
|
||||
out:
|
||||
|
||||
if ( !success && p2m )
|
||||
munmap(p2m, ROUNDUP(p2m_size * sizeof(xen_pfn_t), PAGE_SHIFT));
|
||||
|
||||
if ( live_p2m_frame_list_list )
|
||||
munmap(live_p2m_frame_list_list, PAGE_SIZE);
|
||||
|
||||
if ( live_p2m_frame_list )
|
||||
munmap(live_p2m_frame_list, P2M_FLL_ENTRIES * PAGE_SIZE);
|
||||
|
||||
if ( p2m_frame_list )
|
||||
free(p2m_frame_list);
|
||||
|
||||
return success ? p2m : NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
uint32_t max_factor, uint32_t flags, int (*suspend)(int),
|
||||
int hvm, void *(*init_qemu_maps)(int, unsigned),
|
||||
void (*qemu_flip_buffer)(int, int))
|
||||
{
|
||||
xc_dominfo_t info;
|
||||
|
||||
@@ -666,20 +798,13 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
/* A copy of the CPU context of the guest. */
|
||||
vcpu_guest_context_t ctxt;
|
||||
|
||||
/* A table containg the type of each PFN (/not/ MFN!). */
|
||||
/* A table containing the type of each PFN (/not/ MFN!). */
|
||||
unsigned long *pfn_type = NULL;
|
||||
unsigned long *pfn_batch = NULL;
|
||||
|
||||
/* A temporary mapping, and a copy, of one frame of guest memory. */
|
||||
/* A copy of one frame of guest memory. */
|
||||
char page[PAGE_SIZE];
|
||||
|
||||
/* Double and single indirect references to the live P2M table */
|
||||
xen_pfn_t *live_p2m_frame_list_list = NULL;
|
||||
xen_pfn_t *live_p2m_frame_list = NULL;
|
||||
|
||||
/* A copy of the pfn-to-mfn table frame list. */
|
||||
xen_pfn_t *p2m_frame_list = NULL;
|
||||
|
||||
/* Live mapping of shared info structure */
|
||||
shared_info_t *live_shinfo = NULL;
|
||||
|
||||
@@ -702,6 +827,13 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
|
||||
uint64_t vcpumap = 1ULL;
|
||||
|
||||
/* HVM: a buffer for holding HVM context */
|
||||
uint32_t hvm_buf_size = 0;
|
||||
uint8_t *hvm_buf = NULL;
|
||||
|
||||
/* HVM: magic frames for ioreqs and xenstore comms. */
|
||||
uint64_t magic_pfns[3]; /* ioreq_pfn, bufioreq_pfn, store_pfn */
|
||||
|
||||
/* If no explicit control parameters given, use defaults */
|
||||
max_iters = max_iters ? : DEF_MAX_ITERS;
|
||||
max_factor = max_factor ? : DEF_MAX_FACTOR;
|
||||
@@ -729,72 +861,20 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
shared_info_frame = info.shared_info_frame;
|
||||
|
||||
/* Map the shared info frame */
|
||||
if ( !(live_shinfo = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
|
||||
PROT_READ, shared_info_frame)) )
|
||||
if ( !hvm )
|
||||
{
|
||||
ERROR("Couldn't map live_shinfo");
|
||||
goto out;
|
||||
}
|
||||
|
||||
p2m_size = live_shinfo->arch.max_pfn;
|
||||
|
||||
live_p2m_frame_list_list = map_frame_list_list(xc_handle, dom,
|
||||
live_shinfo);
|
||||
if ( !live_p2m_frame_list_list )
|
||||
goto out;
|
||||
|
||||
live_p2m_frame_list =
|
||||
xc_map_foreign_batch(xc_handle, dom, PROT_READ,
|
||||
live_p2m_frame_list_list,
|
||||
P2M_FLL_ENTRIES);
|
||||
if ( !live_p2m_frame_list )
|
||||
{
|
||||
ERROR("Couldn't map p2m_frame_list");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Map all the frames of the pfn->mfn table. For migrate to succeed,
|
||||
the guest must not change which frames are used for this purpose.
|
||||
(its not clear why it would want to change them, and we'll be OK
|
||||
from a safety POV anyhow. */
|
||||
|
||||
live_p2m = xc_map_foreign_batch(xc_handle, dom, PROT_READ,
|
||||
live_p2m_frame_list,
|
||||
P2M_FL_ENTRIES);
|
||||
if ( !live_p2m )
|
||||
{
|
||||
ERROR("Couldn't map p2m table");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Setup the mfn_to_pfn table mapping */
|
||||
if ( !(live_m2p = xc_map_m2p(xc_handle, max_mfn, PROT_READ)) )
|
||||
{
|
||||
ERROR("Failed to map live M2P table");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
/* Get a local copy of the live_P2M_frame_list */
|
||||
if ( !(p2m_frame_list = malloc(P2M_FL_SIZE)) )
|
||||
{
|
||||
ERROR("Couldn't allocate p2m_frame_list array");
|
||||
goto out;
|
||||
}
|
||||
memcpy(p2m_frame_list, live_p2m_frame_list, P2M_FL_SIZE);
|
||||
|
||||
/* Canonicalise the pfn-to-mfn table frame-number list. */
|
||||
for ( i = 0; i < p2m_size; i += fpp )
|
||||
{
|
||||
if ( !translate_mfn_to_pfn(&p2m_frame_list[i/fpp]) )
|
||||
live_shinfo = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
|
||||
PROT_READ, shared_info_frame);
|
||||
if ( !live_shinfo )
|
||||
{
|
||||
ERROR("Frame# in pfn-to-mfn frame list is not in pseudophys");
|
||||
ERROR("entry %d: p2m_frame_list[%ld] is 0x%"PRIx64, i, i/fpp,
|
||||
(uint64_t)p2m_frame_list[i/fpp]);
|
||||
ERROR("Couldn't map live_shinfo");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the size of the P2M table */
|
||||
p2m_size = xc_memory_op(xc_handle, XENMEM_maximum_gpfn, &dom);
|
||||
|
||||
/* Domain is still running at this point */
|
||||
if ( live )
|
||||
{
|
||||
@@ -806,6 +886,16 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
ERROR("Couldn't enable shadow mode");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( hvm )
|
||||
{
|
||||
/* Get qemu-dm logging dirty pages too */
|
||||
void *seg = init_qemu_maps(dom, BITMAP_SIZE);
|
||||
qemu_bitmaps[0] = seg;
|
||||
qemu_bitmaps[1] = seg + BITMAP_SIZE;
|
||||
qemu_active = 0;
|
||||
qemu_non_active = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -853,6 +943,23 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( hvm )
|
||||
{
|
||||
/* Need another buffer for HVM context */
|
||||
hvm_buf_size = xc_domain_hvm_getcontext(xc_handle, dom, 0, 0);
|
||||
if ( hvm_buf_size == -1 )
|
||||
{
|
||||
ERROR("Couldn't get HVM context size from Xen");
|
||||
goto out;
|
||||
}
|
||||
hvm_buf = malloc(hvm_buf_size);
|
||||
if ( !hvm_buf )
|
||||
{
|
||||
ERROR("Couldn't allocate memory");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
analysis_phase(xc_handle, dom, p2m_size, to_skip, 0);
|
||||
|
||||
/* We want zeroed memory so use calloc rather than malloc. */
|
||||
@@ -871,12 +978,38 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Quick belt and braces sanity check.
|
||||
*/
|
||||
/* Setup the mfn_to_pfn table mapping */
|
||||
if ( !(live_m2p = xc_map_m2p(xc_handle, max_mfn, PROT_READ)) )
|
||||
{
|
||||
int err=0;
|
||||
ERROR("Failed to map live M2P table");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Start writing out the saved-domain record. */
|
||||
if ( !write_exact(io_fd, &p2m_size, sizeof(unsigned long)) )
|
||||
{
|
||||
ERROR("write: p2m_size");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( !hvm )
|
||||
{
|
||||
int err = 0;
|
||||
unsigned long mfn;
|
||||
|
||||
/* Map the P2M table, and write the list of P2M frames */
|
||||
live_p2m = map_and_save_p2m_table(xc_handle, io_fd, dom,
|
||||
&ctxt, p2m_size, live_shinfo);
|
||||
if ( live_p2m == NULL )
|
||||
{
|
||||
ERROR("Failed to map/save the p2m frame list");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Quick belt and braces sanity check.
|
||||
*/
|
||||
|
||||
for ( i = 0; i < p2m_size; i++ )
|
||||
{
|
||||
mfn = live_p2m[i];
|
||||
@@ -890,42 +1023,6 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
DPRINTF("Had %d unexplained entries in p2m table\n", err);
|
||||
}
|
||||
|
||||
/* Start writing out the saved-domain record. */
|
||||
if ( !write_exact(io_fd, &p2m_size, sizeof(unsigned long)) )
|
||||
{
|
||||
ERROR("write: p2m_size");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write an extended-info structure to inform the restore code that
|
||||
* a PAE guest understands extended CR3 (PDPTs above 4GB). Turns off
|
||||
* slow paths in the restore code.
|
||||
*/
|
||||
if ( (pt_levels == 3) &&
|
||||
(ctxt.vm_assist & (1UL << VMASST_TYPE_pae_extended_cr3)) )
|
||||
{
|
||||
unsigned long signature = ~0UL;
|
||||
uint32_t tot_sz = sizeof(struct vcpu_guest_context) + 8;
|
||||
uint32_t chunk_sz = sizeof(struct vcpu_guest_context);
|
||||
char chunk_sig[] = "vcpu";
|
||||
if ( !write_exact(io_fd, &signature, sizeof(signature)) ||
|
||||
!write_exact(io_fd, &tot_sz, sizeof(tot_sz)) ||
|
||||
!write_exact(io_fd, &chunk_sig, 4) ||
|
||||
!write_exact(io_fd, &chunk_sz, sizeof(chunk_sz)) ||
|
||||
!write_exact(io_fd, &ctxt, sizeof(ctxt)) )
|
||||
{
|
||||
ERROR("write: extended info");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !write_exact(io_fd, p2m_frame_list, P2M_FL_SIZE) )
|
||||
{
|
||||
ERROR("write: p2m_frame_list");
|
||||
goto out;
|
||||
}
|
||||
|
||||
print_stats(xc_handle, dom, 0, &stats, 0);
|
||||
|
||||
/* Now write out each data page, canonicalising page tables as we go... */
|
||||
@@ -976,9 +1073,9 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
|
||||
if ( debug )
|
||||
DPRINTF("%d pfn= %08lx mfn= %08lx %d [mfn]= %08lx\n",
|
||||
iter, (unsigned long)n, live_p2m[n],
|
||||
iter, (unsigned long)n, hvm ? 0 : live_p2m[n],
|
||||
test_bit(n, to_send),
|
||||
mfn_to_pfn(live_p2m[n]&0xFFFFF));
|
||||
hvm ? 0 : mfn_to_pfn(live_p2m[n]&0xFFFFF));
|
||||
|
||||
if ( !last_iter &&
|
||||
test_bit(n, to_send) &&
|
||||
@@ -990,6 +1087,12 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
(test_bit(n, to_fix) && last_iter)) )
|
||||
continue;
|
||||
|
||||
/* Skip PFNs that aren't really there */
|
||||
if ( hvm && ((n >= 0xa0 && n < 0xc0) /* VGA hole */
|
||||
|| (n >= (HVM_BELOW_4G_MMIO_START >> PAGE_SHIFT)
|
||||
&& n < (1ULL<<32) >> PAGE_SHIFT)) /* MMIO */ )
|
||||
continue;
|
||||
|
||||
/*
|
||||
** we get here if:
|
||||
** 1. page is marked to_send & hasn't already been re-dirtied
|
||||
@@ -998,15 +1101,21 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
*/
|
||||
|
||||
pfn_batch[batch] = n;
|
||||
pfn_type[batch] = live_p2m[n];
|
||||
|
||||
/* Hypercall interfaces operate in PFNs for HVM guests
|
||||
* and MFNs for PV guests */
|
||||
if ( hvm )
|
||||
pfn_type[batch] = n;
|
||||
else
|
||||
pfn_type[batch] = live_p2m[n];
|
||||
|
||||
if ( !is_mapped(pfn_type[batch]) )
|
||||
{
|
||||
/*
|
||||
** not currently in psuedo-physical map -- set bit
|
||||
** in to_fix since we must send this page in last_iter
|
||||
** unless its sent sooner anyhow, or it never enters
|
||||
** pseudo-physical map (e.g. for ballooned down domains)
|
||||
** pseudo-physical map (e.g. for ballooned down doms)
|
||||
*/
|
||||
set_bit(n, to_fix);
|
||||
continue;
|
||||
@@ -1020,9 +1129,9 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
DPRINTF("Fix! iter %d, pfn %x. mfn %lx\n",
|
||||
iter, n, pfn_type[batch]);
|
||||
}
|
||||
|
||||
|
||||
clear_bit(n, to_fix);
|
||||
|
||||
|
||||
batch++;
|
||||
}
|
||||
|
||||
@@ -1037,41 +1146,46 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
goto out;
|
||||
}
|
||||
|
||||
for ( j = 0; j < batch; j++ )
|
||||
((uint32_t *)pfn_type)[j] = pfn_type[j];
|
||||
if ( xc_get_pfn_type_batch(xc_handle, dom, batch,
|
||||
(uint32_t *)pfn_type) )
|
||||
if ( !hvm )
|
||||
{
|
||||
ERROR("get_pfn_type_batch failed");
|
||||
goto out;
|
||||
}
|
||||
for ( j = batch-1; j >= 0; j-- )
|
||||
pfn_type[j] = ((uint32_t *)pfn_type)[j];
|
||||
|
||||
for ( j = 0; j < batch; j++ )
|
||||
{
|
||||
|
||||
if ( (pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) ==
|
||||
XEN_DOMCTL_PFINFO_XTAB )
|
||||
/* Get page types */
|
||||
for ( j = 0; j < batch; j++ )
|
||||
((uint32_t *)pfn_type)[j] = pfn_type[j];
|
||||
if ( xc_get_pfn_type_batch(xc_handle, dom, batch,
|
||||
(uint32_t *)pfn_type) )
|
||||
{
|
||||
DPRINTF("type fail: page %i mfn %08lx\n", j, pfn_type[j]);
|
||||
continue;
|
||||
ERROR("get_pfn_type_batch failed");
|
||||
goto out;
|
||||
}
|
||||
for ( j = batch-1; j >= 0; j-- )
|
||||
pfn_type[j] = ((uint32_t *)pfn_type)[j];
|
||||
|
||||
if ( debug )
|
||||
DPRINTF("%d pfn= %08lx mfn= %08lx [mfn]= %08lx"
|
||||
" sum= %08lx\n",
|
||||
iter,
|
||||
(pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) |
|
||||
pfn_batch[j],
|
||||
pfn_type[j],
|
||||
mfn_to_pfn(pfn_type[j] &
|
||||
~XEN_DOMCTL_PFINFO_LTAB_MASK),
|
||||
csum_page(region_base + (PAGE_SIZE*j)));
|
||||
|
||||
/* canonicalise mfn->pfn */
|
||||
pfn_type[j] = (pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) |
|
||||
pfn_batch[j];
|
||||
for ( j = 0; j < batch; j++ )
|
||||
{
|
||||
|
||||
if ( (pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) ==
|
||||
XEN_DOMCTL_PFINFO_XTAB )
|
||||
{
|
||||
DPRINTF("type fail: page %i mfn %08lx\n",
|
||||
j, pfn_type[j]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( debug )
|
||||
DPRINTF("%d pfn= %08lx mfn= %08lx [mfn]= %08lx"
|
||||
" sum= %08lx\n",
|
||||
iter,
|
||||
(pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) |
|
||||
pfn_batch[j],
|
||||
pfn_type[j],
|
||||
mfn_to_pfn(pfn_type[j] &
|
||||
~XEN_DOMCTL_PFINFO_LTAB_MASK),
|
||||
csum_page(region_base + (PAGE_SIZE*j)));
|
||||
|
||||
/* canonicalise mfn->pfn */
|
||||
pfn_type[j] = (pfn_type[j] & XEN_DOMCTL_PFINFO_LTAB_MASK) |
|
||||
pfn_batch[j];
|
||||
}
|
||||
}
|
||||
|
||||
if ( !write_exact(io_fd, &batch, sizeof(unsigned int)) )
|
||||
@@ -1081,7 +1195,7 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( !write_exact(io_fd, pfn_type, sizeof(unsigned long)*j) )
|
||||
if ( !write_exact(io_fd, pfn_type, sizeof(unsigned long)*batch) )
|
||||
{
|
||||
ERROR("Error when writing to state file (3) (errno %d)",
|
||||
errno);
|
||||
@@ -1211,6 +1325,27 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( hvm )
|
||||
{
|
||||
/* Pull in the dirty bits from qemu-dm too */
|
||||
if ( !last_iter )
|
||||
{
|
||||
qemu_active = qemu_non_active;
|
||||
qemu_non_active = qemu_active ? 0 : 1;
|
||||
qemu_flip_buffer(dom, qemu_active);
|
||||
for ( j = 0; j < BITMAP_SIZE / sizeof(unsigned long); j++ )
|
||||
{
|
||||
to_send[j] |= qemu_bitmaps[qemu_non_active][j];
|
||||
qemu_bitmaps[qemu_non_active][j] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( j = 0; j < BITMAP_SIZE / sizeof(unsigned long); j++ )
|
||||
to_send[j] |= qemu_bitmaps[qemu_active][j];
|
||||
}
|
||||
}
|
||||
|
||||
sent_last_iter = sent_this_iter;
|
||||
|
||||
print_stats(xc_handle, dom, sent_this_iter, &stats, 1);
|
||||
@@ -1257,6 +1392,72 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( hvm )
|
||||
{
|
||||
uint32_t rec_size;
|
||||
|
||||
/* Save magic-page locations. */
|
||||
memset(magic_pfns, 0, sizeof(magic_pfns));
|
||||
xc_get_hvm_param(xc_handle, dom, HVM_PARAM_IOREQ_PFN,
|
||||
(unsigned long *)&magic_pfns[0]);
|
||||
xc_get_hvm_param(xc_handle, dom, HVM_PARAM_BUFIOREQ_PFN,
|
||||
(unsigned long *)&magic_pfns[1]);
|
||||
xc_get_hvm_param(xc_handle, dom, HVM_PARAM_STORE_PFN,
|
||||
(unsigned long *)&magic_pfns[2]);
|
||||
if ( !write_exact(io_fd, magic_pfns, sizeof(magic_pfns)) )
|
||||
{
|
||||
ERROR("Error when writing to state file (7)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Save vcpu contexts */
|
||||
|
||||
for ( i = 0; i <= info.max_vcpu_id; i++ )
|
||||
{
|
||||
if ( !(vcpumap & (1ULL << i)) )
|
||||
continue;
|
||||
|
||||
if ( xc_vcpu_getcontext(xc_handle, dom, i, &ctxt) )
|
||||
{
|
||||
ERROR("HVM:Could not get vcpu context");
|
||||
goto out;
|
||||
}
|
||||
|
||||
DPRINTF("write vcpu %d context.\n", i);
|
||||
if ( !write_exact(io_fd, &(ctxt), sizeof(ctxt)) )
|
||||
{
|
||||
ERROR("write vcpu context failed!\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get HVM context from Xen and save it too */
|
||||
if ( (rec_size = xc_domain_hvm_getcontext(xc_handle, dom, hvm_buf,
|
||||
hvm_buf_size)) == -1 )
|
||||
{
|
||||
ERROR("HVM:Could not get hvm buffer");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( !write_exact(io_fd, &rec_size, sizeof(uint32_t)) )
|
||||
{
|
||||
ERROR("error write hvm buffer size");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( !write_exact(io_fd, hvm_buf, rec_size) )
|
||||
{
|
||||
ERROR("write HVM info failed!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* HVM guests are done now */
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* PV guests only from now on */
|
||||
|
||||
/* Send through a list of all the PFNs that were not in map at the close */
|
||||
{
|
||||
unsigned int i,j;
|
||||
@@ -1380,12 +1581,6 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
if ( live_shinfo )
|
||||
munmap(live_shinfo, PAGE_SIZE);
|
||||
|
||||
if ( live_p2m_frame_list_list )
|
||||
munmap(live_p2m_frame_list_list, PAGE_SIZE);
|
||||
|
||||
if ( live_p2m_frame_list )
|
||||
munmap(live_p2m_frame_list, P2M_FLL_ENTRIES * PAGE_SIZE);
|
||||
|
||||
if ( live_p2m )
|
||||
munmap(live_p2m, ROUNDUP(p2m_size * sizeof(xen_pfn_t), PAGE_SHIFT));
|
||||
|
||||
@@ -1,755 +0,0 @@
|
||||
/******************************************************************************
|
||||
* xc_hvm_save.c
|
||||
*
|
||||
* Save the state of a running HVM guest.
|
||||
*
|
||||
* Copyright (c) 2003, K A Fraser.
|
||||
* Copyright (c) 2006 Intel Corperation
|
||||
* rewriten for hvm guest by Zhai Edwin <edwin.zhai@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "xc_private.h"
|
||||
#include "xg_private.h"
|
||||
#include "xg_save_restore.h"
|
||||
|
||||
#include <xen/hvm/e820.h>
|
||||
#include <xen/hvm/params.h>
|
||||
|
||||
/*
|
||||
** Default values for important tuning parameters. Can override by passing
|
||||
** non-zero replacement values to xc_hvm_save().
|
||||
**
|
||||
** XXX SMH: should consider if want to be able to override MAX_MBIT_RATE too.
|
||||
**
|
||||
*/
|
||||
#define DEF_MAX_ITERS 29 /* limit us to 30 times round loop */
|
||||
#define DEF_MAX_FACTOR 3 /* never send more than 3x nr_pfns */
|
||||
|
||||
/* Shared-memory bitmaps for getting log-dirty bits from qemu */
|
||||
static unsigned long *qemu_bitmaps[2];
|
||||
static int qemu_active;
|
||||
static int qemu_non_active;
|
||||
|
||||
/*
|
||||
** During (live) save/migrate, we maintain a number of bitmaps to track
|
||||
** which pages we have to send, to fixup, and to skip.
|
||||
*/
|
||||
|
||||
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
|
||||
#define BITS_TO_LONGS(bits) (((bits)+BITS_PER_LONG-1)/BITS_PER_LONG)
|
||||
#define BITMAP_SIZE (BITS_TO_LONGS(pfn_array_size) * sizeof(unsigned long))
|
||||
|
||||
#define BITMAP_ENTRY(_nr,_bmap) \
|
||||
((unsigned long *)(_bmap))[(_nr)/BITS_PER_LONG]
|
||||
|
||||
#define BITMAP_SHIFT(_nr) ((_nr) % BITS_PER_LONG)
|
||||
|
||||
static inline int test_bit (int nr, volatile void * addr)
|
||||
{
|
||||
return (BITMAP_ENTRY(nr, addr) >> BITMAP_SHIFT(nr)) & 1;
|
||||
}
|
||||
|
||||
static inline void clear_bit (int nr, volatile void * addr)
|
||||
{
|
||||
BITMAP_ENTRY(nr, addr) &= ~(1UL << BITMAP_SHIFT(nr));
|
||||
}
|
||||
|
||||
static inline int permute( int i, int nr, int order_nr )
|
||||
{
|
||||
/* Need a simple permutation function so that we scan pages in a
|
||||
pseudo random order, enabling us to get a better estimate of
|
||||
the domain's page dirtying rate as we go (there are often
|
||||
contiguous ranges of pfns that have similar behaviour, and we
|
||||
want to mix them up. */
|
||||
|
||||
/* e.g. nr->oder 15->4 16->4 17->5 */
|
||||
/* 512MB domain, 128k pages, order 17 */
|
||||
|
||||
/*
|
||||
QPONMLKJIHGFEDCBA
|
||||
QPONMLKJIH
|
||||
GFEDCBA
|
||||
*/
|
||||
|
||||
/*
|
||||
QPONMLKJIHGFEDCBA
|
||||
EDCBA
|
||||
QPONM
|
||||
LKJIHGF
|
||||
*/
|
||||
|
||||
do { i = ((i>>(order_nr-10)) | ( i<<10 ) ) & ((1<<order_nr)-1); }
|
||||
while ( i >= nr ); /* this won't ever loop if nr is a power of 2 */
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
static uint64_t tv_to_us(struct timeval *new)
|
||||
{
|
||||
return (new->tv_sec * 1000000) + new->tv_usec;
|
||||
}
|
||||
|
||||
static uint64_t llgettimeofday(void)
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
return tv_to_us(&now);
|
||||
}
|
||||
|
||||
static uint64_t tv_delta(struct timeval *new, struct timeval *old)
|
||||
{
|
||||
return (((new->tv_sec - old->tv_sec)*1000000) +
|
||||
(new->tv_usec - old->tv_usec));
|
||||
}
|
||||
|
||||
|
||||
#define RATE_IS_MAX() (0)
|
||||
#define ratewrite(_io_fd, _buf, _n) write((_io_fd), (_buf), (_n))
|
||||
#define initialize_mbit_rate()
|
||||
|
||||
static inline ssize_t write_exact(int fd, void *buf, size_t count)
|
||||
{
|
||||
return (write(fd, buf, count) == count);
|
||||
}
|
||||
|
||||
static int print_stats(int xc_handle, uint32_t domid, int pages_sent,
|
||||
xc_shadow_op_stats_t *stats, int print)
|
||||
{
|
||||
static struct timeval wall_last;
|
||||
static long long d0_cpu_last;
|
||||
static long long d1_cpu_last;
|
||||
|
||||
struct timeval wall_now;
|
||||
long long wall_delta;
|
||||
long long d0_cpu_now, d0_cpu_delta;
|
||||
long long d1_cpu_now, d1_cpu_delta;
|
||||
|
||||
gettimeofday(&wall_now, NULL);
|
||||
|
||||
d0_cpu_now = xc_domain_get_cpu_usage(xc_handle, 0, /* FIXME */ 0)/1000;
|
||||
d1_cpu_now = xc_domain_get_cpu_usage(xc_handle, domid, /* FIXME */ 0)/1000;
|
||||
|
||||
if ( (d0_cpu_now == -1) || (d1_cpu_now == -1) )
|
||||
DPRINTF("ARRHHH!!\n");
|
||||
|
||||
wall_delta = tv_delta(&wall_now,&wall_last)/1000;
|
||||
if ( wall_delta == 0 )
|
||||
wall_delta = 1;
|
||||
|
||||
d0_cpu_delta = (d0_cpu_now - d0_cpu_last)/1000;
|
||||
d1_cpu_delta = (d1_cpu_now - d1_cpu_last)/1000;
|
||||
|
||||
if ( print )
|
||||
DPRINTF("delta %lldms, dom0 %d%%, target %d%%, sent %dMb/s, "
|
||||
"dirtied %dMb/s %" PRId32 " pages\n",
|
||||
wall_delta,
|
||||
(int)((d0_cpu_delta*100)/wall_delta),
|
||||
(int)((d1_cpu_delta*100)/wall_delta),
|
||||
(int)((pages_sent*PAGE_SIZE)/(wall_delta*(1000/8))),
|
||||
(int)((stats->dirty_count*PAGE_SIZE)/(wall_delta*(1000/8))),
|
||||
stats->dirty_count);
|
||||
|
||||
d0_cpu_last = d0_cpu_now;
|
||||
d1_cpu_last = d1_cpu_now;
|
||||
wall_last = wall_now;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int analysis_phase(int xc_handle, uint32_t domid, int pfn_array_size,
|
||||
unsigned long *arr, int runs)
|
||||
{
|
||||
long long start, now;
|
||||
xc_shadow_op_stats_t stats;
|
||||
int j;
|
||||
|
||||
start = llgettimeofday();
|
||||
|
||||
for ( j = 0; j < runs; j++ )
|
||||
{
|
||||
int i;
|
||||
|
||||
xc_shadow_control(xc_handle, domid, XEN_DOMCTL_SHADOW_OP_CLEAN,
|
||||
arr, pfn_array_size, NULL, 0, NULL);
|
||||
DPRINTF("#Flush\n");
|
||||
for ( i = 0; i < 40; i++ )
|
||||
{
|
||||
usleep(50000);
|
||||
now = llgettimeofday();
|
||||
xc_shadow_control(xc_handle, domid, XEN_DOMCTL_SHADOW_OP_PEEK,
|
||||
NULL, 0, NULL, 0, &stats);
|
||||
DPRINTF("now= %lld faults= %"PRId32" dirty= %"PRId32"\n",
|
||||
((now-start)+500)/1000,
|
||||
stats.fault_count, stats.dirty_count);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int suspend_and_state(int (*suspend)(int), int xc_handle, int io_fd,
|
||||
int dom, xc_dominfo_t *info,
|
||||
vcpu_guest_context_t *ctxt)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if ( !(*suspend)(dom) )
|
||||
{
|
||||
ERROR("Suspend request failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
retry:
|
||||
|
||||
if ( xc_domain_getinfo(xc_handle, dom, 1, info) != 1 )
|
||||
{
|
||||
ERROR("Could not get domain info");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( xc_vcpu_getcontext(xc_handle, dom, 0, ctxt) )
|
||||
ERROR("Could not get vcpu context");
|
||||
|
||||
if ( info->shutdown && (info->shutdown_reason == SHUTDOWN_suspend) )
|
||||
return 0; /* success */
|
||||
|
||||
if ( info->paused )
|
||||
{
|
||||
/* Try unpausing domain, wait, and retest. */
|
||||
xc_domain_unpause( xc_handle, dom );
|
||||
ERROR("Domain was paused. Wait and re-test.");
|
||||
usleep(10000); /* 10ms */
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if ( ++i < 100 )
|
||||
{
|
||||
ERROR("Retry suspend domain.");
|
||||
usleep(10000); /* 10ms */
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ERROR("Unable to suspend domain.");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int xc_hvm_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
uint32_t max_factor, uint32_t flags, int (*suspend)(int),
|
||||
void *(*init_qemu_maps)(int, unsigned),
|
||||
void (*qemu_flip_buffer)(int, int))
|
||||
{
|
||||
xc_dominfo_t info;
|
||||
|
||||
int rc = 1, i, j, last_iter, iter = 0;
|
||||
int live = !!(flags & XCFLAGS_LIVE);
|
||||
int debug = !!(flags & XCFLAGS_DEBUG);
|
||||
int sent_last_iter, skip_this_iter;
|
||||
|
||||
/* The highest guest-physical frame number used by the current guest */
|
||||
unsigned long max_pfn;
|
||||
|
||||
/* The size of an array big enough to contain all guest pfns */
|
||||
unsigned long pfn_array_size;
|
||||
|
||||
/* Magic frames: ioreqs and xenstore comms. */
|
||||
uint64_t magic_pfns[3]; /* ioreq_pfn, bufioreq_pfn, store_pfn */
|
||||
|
||||
/* A copy of the CPU context of the guest. */
|
||||
vcpu_guest_context_t ctxt;
|
||||
|
||||
/* A table containg the PFNs (/not/ MFN!) to map. */
|
||||
xen_pfn_t *pfn_batch = NULL;
|
||||
|
||||
/* A copy of hvm domain context buffer*/
|
||||
uint32_t hvm_buf_size;
|
||||
uint8_t *hvm_buf = NULL;
|
||||
|
||||
/* base of the region in which domain memory is mapped */
|
||||
unsigned char *region_base = NULL;
|
||||
|
||||
uint32_t rec_size, nr_vcpus;
|
||||
|
||||
/* power of 2 order of pfn_array_size */
|
||||
int order_nr;
|
||||
|
||||
/* bitmap of pages:
|
||||
- that should be sent this iteration (unless later marked as skip);
|
||||
- to skip this iteration because already dirty; */
|
||||
unsigned long *to_send = NULL, *to_skip = NULL;
|
||||
|
||||
xc_shadow_op_stats_t stats;
|
||||
|
||||
unsigned long total_sent = 0;
|
||||
|
||||
uint64_t vcpumap = 1ULL;
|
||||
|
||||
DPRINTF("xc_hvm_save: dom=%d, max_iters=%d, max_factor=%d, flags=0x%x, "
|
||||
"live=%d, debug=%d.\n", dom, max_iters, max_factor, flags,
|
||||
live, debug);
|
||||
|
||||
/* If no explicit control parameters given, use defaults */
|
||||
max_iters = max_iters ? : DEF_MAX_ITERS;
|
||||
max_factor = max_factor ? : DEF_MAX_FACTOR;
|
||||
|
||||
initialize_mbit_rate();
|
||||
|
||||
if ( xc_domain_getinfo(xc_handle, dom, 1, &info) != 1 )
|
||||
{
|
||||
ERROR("HVM: Could not get domain info");
|
||||
return 1;
|
||||
}
|
||||
nr_vcpus = info.nr_online_vcpus;
|
||||
|
||||
if ( mlock(&ctxt, sizeof(ctxt)) )
|
||||
{
|
||||
ERROR("HVM: Unable to mlock ctxt");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Only have to worry about vcpu 0 even for SMP */
|
||||
if ( xc_vcpu_getcontext(xc_handle, dom, 0, &ctxt) )
|
||||
{
|
||||
ERROR("HVM: Could not get vcpu context");
|
||||
goto out;
|
||||
}
|
||||
|
||||
DPRINTF("saved hvm domain info: max_memkb=0x%lx, nr_pages=0x%lx\n",
|
||||
info.max_memkb, info.nr_pages);
|
||||
|
||||
if ( live )
|
||||
{
|
||||
/* Live suspend. Enable log-dirty mode. */
|
||||
if ( xc_shadow_control(xc_handle, dom,
|
||||
XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY,
|
||||
NULL, 0, NULL, 0, NULL) < 0 )
|
||||
{
|
||||
ERROR("Couldn't enable shadow mode");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This is a non-live suspend. Suspend the domain .*/
|
||||
if ( suspend_and_state(suspend, xc_handle, io_fd, dom, &info, &ctxt) )
|
||||
{
|
||||
ERROR("HVM Domain appears not to have suspended");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
last_iter = !live;
|
||||
|
||||
max_pfn = xc_memory_op(xc_handle, XENMEM_maximum_gpfn, &dom);
|
||||
|
||||
DPRINTF("after 1st handle hvm domain max_pfn=0x%lx, "
|
||||
"max_memkb=0x%lx, live=%d.\n",
|
||||
max_pfn, info.max_memkb, live);
|
||||
|
||||
/* Size of any array that covers 0 ... max_pfn */
|
||||
pfn_array_size = max_pfn + 1;
|
||||
if ( !write_exact(io_fd, &pfn_array_size, sizeof(unsigned long)) )
|
||||
{
|
||||
ERROR("Error when writing to state file (1)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* pretend we sent all the pages last iteration */
|
||||
sent_last_iter = pfn_array_size;
|
||||
|
||||
/* calculate the power of 2 order of pfn_array_size, e.g.
|
||||
15->4 16->4 17->5 */
|
||||
for ( i = pfn_array_size-1, order_nr = 0; i ; i >>= 1, order_nr++ )
|
||||
continue;
|
||||
|
||||
/* Setup to_send / to_fix and to_skip bitmaps */
|
||||
to_send = malloc(BITMAP_SIZE);
|
||||
to_skip = malloc(BITMAP_SIZE);
|
||||
|
||||
if ( live )
|
||||
{
|
||||
/* Get qemu-dm logging dirty pages too */
|
||||
void *seg = init_qemu_maps(dom, BITMAP_SIZE);
|
||||
qemu_bitmaps[0] = seg;
|
||||
qemu_bitmaps[1] = seg + BITMAP_SIZE;
|
||||
qemu_active = 0;
|
||||
qemu_non_active = 1;
|
||||
}
|
||||
|
||||
hvm_buf_size = xc_domain_hvm_getcontext(xc_handle, dom, 0, 0);
|
||||
if ( hvm_buf_size == -1 )
|
||||
{
|
||||
ERROR("Couldn't get HVM context size from Xen");
|
||||
goto out;
|
||||
}
|
||||
hvm_buf = malloc(hvm_buf_size);
|
||||
|
||||
if ( !to_send || !to_skip || !hvm_buf )
|
||||
{
|
||||
ERROR("Couldn't allocate memory");
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(to_send, 0xff, BITMAP_SIZE);
|
||||
|
||||
if ( lock_pages(to_send, BITMAP_SIZE) )
|
||||
{
|
||||
ERROR("Unable to lock to_send");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* (to fix is local only) */
|
||||
if ( lock_pages(to_skip, BITMAP_SIZE) )
|
||||
{
|
||||
ERROR("Unable to lock to_skip");
|
||||
return 1;
|
||||
}
|
||||
|
||||
analysis_phase(xc_handle, dom, pfn_array_size, to_skip, 0);
|
||||
|
||||
/* We want zeroed memory so use calloc rather than malloc. */
|
||||
pfn_batch = calloc(MAX_BATCH_SIZE, sizeof(*pfn_batch));
|
||||
if ( pfn_batch == NULL )
|
||||
{
|
||||
ERROR("failed to alloc memory for pfn_batch array");
|
||||
errno = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
unsigned int prev_pc, sent_this_iter, N, batch;
|
||||
|
||||
iter++;
|
||||
sent_this_iter = 0;
|
||||
skip_this_iter = 0;
|
||||
prev_pc = 0;
|
||||
N=0;
|
||||
|
||||
DPRINTF("Saving memory pages: iter %d 0%%", iter);
|
||||
|
||||
while ( N < pfn_array_size )
|
||||
{
|
||||
unsigned int this_pc = (N * 100) / pfn_array_size;
|
||||
int rc;
|
||||
|
||||
if ( (this_pc - prev_pc) >= 5 )
|
||||
{
|
||||
DPRINTF("\b\b\b\b%3d%%", this_pc);
|
||||
prev_pc = this_pc;
|
||||
}
|
||||
|
||||
if ( !last_iter )
|
||||
{
|
||||
/* Slightly wasteful to peek the whole array evey time,
|
||||
but this is fast enough for the moment. */
|
||||
rc = xc_shadow_control(
|
||||
xc_handle, dom, XEN_DOMCTL_SHADOW_OP_PEEK, to_skip,
|
||||
pfn_array_size, NULL, 0, NULL);
|
||||
if ( rc != pfn_array_size )
|
||||
{
|
||||
ERROR("Error peeking shadow bitmap");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* load pfn_batch[] with the mfn of all the pages we're doing in
|
||||
this batch. */
|
||||
for ( batch = 0;
|
||||
(batch < MAX_BATCH_SIZE) && (N < pfn_array_size);
|
||||
N++ )
|
||||
{
|
||||
int n = permute(N, pfn_array_size, order_nr);
|
||||
|
||||
if ( 0 && debug )
|
||||
DPRINTF("%d pfn= %08lx %d \n",
|
||||
iter, (unsigned long)n, test_bit(n, to_send));
|
||||
|
||||
if ( !last_iter &&
|
||||
test_bit(n, to_send) &&
|
||||
test_bit(n, to_skip) )
|
||||
skip_this_iter++; /* stats keeping */
|
||||
|
||||
if ( !((test_bit(n, to_send) && !test_bit(n, to_skip)) ||
|
||||
(test_bit(n, to_send) && last_iter)) )
|
||||
continue;
|
||||
|
||||
/* Skip PFNs that aren't really there */
|
||||
if ( (n >= 0xa0 && n < 0xc0) /* VGA hole */
|
||||
|| (n >= (HVM_BELOW_4G_MMIO_START >> PAGE_SHIFT) &&
|
||||
n < (1ULL << 32) >> PAGE_SHIFT) /* 4G MMIO hole */ )
|
||||
continue;
|
||||
|
||||
/*
|
||||
** we get here if:
|
||||
** 1. page is marked to_send & hasn't already been re-dirtied
|
||||
** 2. (ignore to_skip in last iteration)
|
||||
*/
|
||||
|
||||
pfn_batch[batch] = n;
|
||||
|
||||
batch++;
|
||||
}
|
||||
|
||||
if ( batch == 0 )
|
||||
goto skip; /* vanishingly unlikely... */
|
||||
|
||||
region_base = xc_map_foreign_batch(
|
||||
xc_handle, dom, PROT_READ, pfn_batch, batch);
|
||||
if ( region_base == 0 )
|
||||
{
|
||||
ERROR("map batch failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* write num of pfns */
|
||||
if ( !write_exact(io_fd, &batch, sizeof(unsigned int)) )
|
||||
{
|
||||
ERROR("Error when writing to state file (2)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* write all the pfns */
|
||||
if ( !write_exact(io_fd, pfn_batch, sizeof(unsigned long)*batch) )
|
||||
{
|
||||
ERROR("Error when writing to state file (3)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for ( j = 0; j < batch; j++ )
|
||||
{
|
||||
if ( pfn_batch[j] & XEN_DOMCTL_PFINFO_LTAB_MASK )
|
||||
continue;
|
||||
if ( ratewrite(io_fd, region_base + j*PAGE_SIZE,
|
||||
PAGE_SIZE) != PAGE_SIZE )
|
||||
{
|
||||
ERROR("ERROR when writing to state file (4)");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
sent_this_iter += batch;
|
||||
|
||||
munmap(region_base, batch*PAGE_SIZE);
|
||||
|
||||
} /* end of this while loop for this iteration */
|
||||
|
||||
skip:
|
||||
|
||||
total_sent += sent_this_iter;
|
||||
|
||||
DPRINTF("\r %d: sent %d, skipped %d, ",
|
||||
iter, sent_this_iter, skip_this_iter );
|
||||
|
||||
if ( last_iter )
|
||||
{
|
||||
print_stats( xc_handle, dom, sent_this_iter, &stats, 1);
|
||||
DPRINTF("Total pages sent= %ld (%.2fx)\n",
|
||||
total_sent, ((float)total_sent)/pfn_array_size );
|
||||
}
|
||||
|
||||
if ( last_iter && debug )
|
||||
{
|
||||
int minusone = -1;
|
||||
memset(to_send, 0xff, BITMAP_SIZE);
|
||||
debug = 0;
|
||||
DPRINTF("Entering debug resend-all mode\n");
|
||||
|
||||
/* send "-1" to put receiver into debug mode */
|
||||
if ( !write_exact(io_fd, &minusone, sizeof(int)) )
|
||||
{
|
||||
ERROR("Error when writing to state file (6)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( last_iter )
|
||||
break;
|
||||
|
||||
if ( live )
|
||||
{
|
||||
if ( ((sent_this_iter > sent_last_iter) && RATE_IS_MAX()) ||
|
||||
(iter >= max_iters) ||
|
||||
(sent_this_iter+skip_this_iter < 50) ||
|
||||
(total_sent > pfn_array_size*max_factor) )
|
||||
{
|
||||
DPRINTF("Start last iteration for HVM domain\n");
|
||||
last_iter = 1;
|
||||
|
||||
if ( suspend_and_state(suspend, xc_handle, io_fd, dom, &info,
|
||||
&ctxt))
|
||||
{
|
||||
ERROR("Domain appears not to have suspended");
|
||||
goto out;
|
||||
}
|
||||
|
||||
DPRINTF("SUSPEND eip %08lx edx %08lx\n",
|
||||
(unsigned long)ctxt.user_regs.eip,
|
||||
(unsigned long)ctxt.user_regs.edx);
|
||||
}
|
||||
|
||||
if ( xc_shadow_control(xc_handle, dom,
|
||||
XEN_DOMCTL_SHADOW_OP_CLEAN, to_send,
|
||||
pfn_array_size, NULL,
|
||||
0, &stats) != pfn_array_size )
|
||||
{
|
||||
ERROR("Error flushing shadow PT");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Pull in the dirty bits from qemu too */
|
||||
if ( !last_iter )
|
||||
{
|
||||
qemu_active = qemu_non_active;
|
||||
qemu_non_active = qemu_active ? 0 : 1;
|
||||
qemu_flip_buffer(dom, qemu_active);
|
||||
for ( j = 0; j < BITMAP_SIZE / sizeof(unsigned long); j++ )
|
||||
{
|
||||
to_send[j] |= qemu_bitmaps[qemu_non_active][j];
|
||||
qemu_bitmaps[qemu_non_active][j] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( j = 0; j < BITMAP_SIZE / sizeof(unsigned long); j++ )
|
||||
to_send[j] |= qemu_bitmaps[qemu_active][j];
|
||||
}
|
||||
|
||||
sent_last_iter = sent_this_iter;
|
||||
|
||||
print_stats(xc_handle, dom, sent_this_iter, &stats, 1);
|
||||
}
|
||||
} /* end of while 1 */
|
||||
|
||||
|
||||
DPRINTF("All HVM memory is saved\n");
|
||||
|
||||
{
|
||||
struct {
|
||||
int minustwo;
|
||||
int max_vcpu_id;
|
||||
uint64_t vcpumap;
|
||||
} chunk = { -2, info.max_vcpu_id };
|
||||
|
||||
if (info.max_vcpu_id >= 64) {
|
||||
ERROR("Too many VCPUS in guest!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 1; i <= info.max_vcpu_id; i++) {
|
||||
xc_vcpuinfo_t vinfo;
|
||||
if ((xc_vcpu_getinfo(xc_handle, dom, i, &vinfo) == 0) &&
|
||||
vinfo.online)
|
||||
vcpumap |= 1ULL << i;
|
||||
}
|
||||
|
||||
chunk.vcpumap = vcpumap;
|
||||
if(!write_exact(io_fd, &chunk, sizeof(chunk))) {
|
||||
ERROR("Error when writing to state file (errno %d)", errno);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Zero terminate */
|
||||
i = 0;
|
||||
if ( !write_exact(io_fd, &i, sizeof(int)) )
|
||||
{
|
||||
ERROR("Error when writing to state file (6)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Save magic-page locations. */
|
||||
memset(magic_pfns, 0, sizeof(magic_pfns));
|
||||
xc_get_hvm_param(xc_handle, dom, HVM_PARAM_IOREQ_PFN,
|
||||
(unsigned long *)&magic_pfns[0]);
|
||||
xc_get_hvm_param(xc_handle, dom, HVM_PARAM_BUFIOREQ_PFN,
|
||||
(unsigned long *)&magic_pfns[1]);
|
||||
xc_get_hvm_param(xc_handle, dom, HVM_PARAM_STORE_PFN,
|
||||
(unsigned long *)&magic_pfns[2]);
|
||||
if ( !write_exact(io_fd, magic_pfns, sizeof(magic_pfns)) )
|
||||
{
|
||||
ERROR("Error when writing to state file (7)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* save vcpu/vmcs contexts */
|
||||
for ( i = 0; i < nr_vcpus; i++ )
|
||||
{
|
||||
if ( !(vcpumap & (1ULL << i)) )
|
||||
continue;
|
||||
|
||||
if ( xc_vcpu_getcontext(xc_handle, dom, i, &ctxt) )
|
||||
{
|
||||
ERROR("HVM:Could not get vcpu context");
|
||||
goto out;
|
||||
}
|
||||
|
||||
DPRINTF("write vcpu %d context.\n", i);
|
||||
if ( !write_exact(io_fd, &(ctxt), sizeof(ctxt)) )
|
||||
{
|
||||
ERROR("write vcpu context failed!\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (rec_size = xc_domain_hvm_getcontext(xc_handle, dom, hvm_buf,
|
||||
hvm_buf_size)) == -1 )
|
||||
{
|
||||
ERROR("HVM:Could not get hvm buffer");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( !write_exact(io_fd, &rec_size, sizeof(uint32_t)) )
|
||||
{
|
||||
ERROR("error write hvm buffer size");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( !write_exact(io_fd, hvm_buf, rec_size) )
|
||||
{
|
||||
ERROR("write HVM info failed!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Success! */
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
|
||||
if ( live )
|
||||
{
|
||||
if ( xc_shadow_control(xc_handle, dom, XEN_DOMCTL_SHADOW_OP_OFF,
|
||||
NULL, 0, NULL, 0, NULL) < 0 )
|
||||
DPRINTF("Warning - couldn't disable shadow mode");
|
||||
}
|
||||
|
||||
free(hvm_buf);
|
||||
free(pfn_batch);
|
||||
free(to_send);
|
||||
free(to_skip);
|
||||
|
||||
return !!rc;
|
||||
}
|
||||
+6
-13
@@ -16,26 +16,19 @@
|
||||
|
||||
|
||||
/**
|
||||
* This function will save a domain running Linux.
|
||||
* This function will save a running domain.
|
||||
*
|
||||
* @parm xc_handle a handle to an open hypervisor interface
|
||||
* @parm fd the file descriptor to save a domain to
|
||||
* @parm dom the id of the domain
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
uint32_t max_factor, uint32_t flags /* XCFLAGS_xxx */,
|
||||
int (*suspend)(int domid));
|
||||
int xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
uint32_t max_factor, uint32_t flags /* XCFLAGS_xxx */,
|
||||
int (*suspend)(int domid), int hvm,
|
||||
void *(*init_qemu_maps)(int, unsigned), /* HVM only */
|
||||
void (*qemu_flip_buffer)(int, int)); /* HVM only */
|
||||
|
||||
/**
|
||||
* This function will save a hvm domain running unmodified guest.
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
int xc_hvm_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
uint32_t max_factor, uint32_t flags /* XCFLAGS_xxx */,
|
||||
int (*suspend)(int domid),
|
||||
void *(*init_qemu_maps)(int, unsigned),
|
||||
void (*qemu_flip_buffer)(int, int));
|
||||
|
||||
/**
|
||||
* This function will restore a saved domain.
|
||||
|
||||
@@ -198,17 +198,6 @@ __attribute__((weak))
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
int xc_hvm_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
|
||||
uint32_t max_factor, uint32_t flags,
|
||||
int (*suspend)(int domid),
|
||||
void *(*init_qemu_maps)(int, unsigned),
|
||||
void (*qemu_flip_buffer)(int, int))
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int xc_get_hvm_param(
|
||||
int handle, domid_t dom, int param, unsigned long *value)
|
||||
{
|
||||
|
||||
@@ -174,12 +174,9 @@ main(int argc, char **argv)
|
||||
max_f = atoi(argv[4]);
|
||||
flags = atoi(argv[5]);
|
||||
|
||||
if (flags & XCFLAGS_HVM)
|
||||
ret = xc_hvm_save(xc_fd, io_fd, domid, maxit, max_f, flags,
|
||||
&suspend, &init_qemu_maps, &qemu_flip_buffer);
|
||||
else
|
||||
ret = xc_linux_save(xc_fd, io_fd, domid, maxit, max_f, flags,
|
||||
&suspend);
|
||||
ret = xc_domain_save(xc_fd, io_fd, domid, maxit, max_f, flags,
|
||||
&suspend, !!(flags & XCFLAGS_HVM),
|
||||
&init_qemu_maps, &qemu_flip_buffer);
|
||||
|
||||
xc_interface_close(xc_fd);
|
||||
|
||||
|
||||