mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 18:21:39 +01:00
Bitmaps are used in save/restore, xenpaging and blktap2. Merge the code into a private xc_bitops.h file. All users are single threaded, so locking is not an issue. The array of bits is handled as volatile because the x86 save/restore code passes the bitmap to the hypervisor which in turn modifies the bitmap. blktap2 uses a private bitmap. There was a possible overflow in the bitmap_size() function, the remainder was not considered. ia64 save/restore uses a bitmap to send the number of vcpus to the host. x86 save/restore uses a bitmap to track dirty pages. This bitmap is shared with the hypervisor. An unused function count_bits() was removed and a new bitmap_size() function is now used. xenpaging uses 3 private bitmaps to track the gfns which are in paged-out state. It had a copy of some Linux bitops.h, which is now obsolete. Also the BITS_PER_LONG macro was hardcoded to 64 which made it impossible to run 32bit tools on a 64bit host. Wether this works at all has to be tested, yet. Signed-off-by: Olaf Hering <olaf@aepfle.de> Committed-by: Ian Jackson <ian.jackson.citrix.com>
801 lines
23 KiB
C
801 lines
23 KiB
C
/******************************************************************************
|
|
* xc_ia64_linux_save.c
|
|
*
|
|
* Save the state of a running Linux session.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Copyright (c) 2003, K A Fraser.
|
|
* Rewritten for ia64 by Tristan Gingold <tristan.gingold@bull.net>
|
|
*
|
|
* Copyright (c) 2007 Isaku Yamahata <yamahata@valinux.co.jp>
|
|
* Use foreign p2m exposure.
|
|
* VTi domain support.
|
|
*/
|
|
|
|
#include <inttypes.h>
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/time.h>
|
|
|
|
#include "xg_private.h"
|
|
#include "xc_bitops.h"
|
|
#include "xc_ia64.h"
|
|
#include "xc_ia64_save_restore.h"
|
|
#include "xc_efi.h"
|
|
#include "xen/hvm/params.h"
|
|
|
|
/*
|
|
** Default values for important tuning parameters. Can override by passing
|
|
** non-zero replacement values to xc_linux_save().
|
|
**
|
|
** XXX SMH: should consider if want to be able to override MAX_MBIT_RATE too.
|
|
**
|
|
*/
|
|
#define DEF_MAX_ITERS (4 - 1) /* limit us to 4 times round loop */
|
|
#define DEF_MAX_FACTOR 3 /* never send more than 3x nr_pfns */
|
|
|
|
/*
|
|
** During (live) save/migrate, we maintain a number of bitmaps to track
|
|
** which pages we have to send, and to skip.
|
|
*/
|
|
|
|
static int
|
|
suspend_and_state(int (*suspend)(void*), void* data,
|
|
xc_interface *xch, int io_fd,
|
|
int dom, xc_dominfo_t *info)
|
|
{
|
|
if ( !(*suspend)(data) ) {
|
|
ERROR("Suspend request failed");
|
|
return -1;
|
|
}
|
|
|
|
if ( (xc_domain_getinfo(xch, dom, 1, info) != 1) ||
|
|
!info->shutdown || (info->shutdown_reason != SHUTDOWN_suspend) ) {
|
|
ERROR("Could not get domain info");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
md_is_not_ram(const efi_memory_desc_t *md)
|
|
{
|
|
return ((md->type != EFI_CONVENTIONAL_MEMORY) ||
|
|
(md->attribute != EFI_MEMORY_WB) ||
|
|
(md->num_pages == 0));
|
|
}
|
|
|
|
/*
|
|
* Send through a list of all the PFNs that were not in map at the close.
|
|
* We send pages which was allocated. However balloon driver may
|
|
* decreased after sending page. So we have to check the freed
|
|
* page after pausing the domain.
|
|
*/
|
|
static int
|
|
xc_ia64_send_unallocated_list(xc_interface *xch, int io_fd,
|
|
struct xen_ia64_p2m_table *p2m_table,
|
|
xen_ia64_memmap_info_t *memmap_info,
|
|
void *memmap_desc_start, void *memmap_desc_end)
|
|
{
|
|
void *p;
|
|
efi_memory_desc_t *md;
|
|
|
|
unsigned long N;
|
|
unsigned long pfntab[1024];
|
|
unsigned int j;
|
|
|
|
j = 0;
|
|
for (p = memmap_desc_start;
|
|
p < memmap_desc_end;
|
|
p += memmap_info->efi_memdesc_size) {
|
|
md = p;
|
|
|
|
if (md_is_not_ram(md))
|
|
continue;
|
|
|
|
for (N = md->phys_addr >> PAGE_SHIFT;
|
|
N < (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT)) >>
|
|
PAGE_SHIFT;
|
|
N++) {
|
|
if (!xc_ia64_p2m_allocated(p2m_table, N))
|
|
j++;
|
|
}
|
|
}
|
|
if (write_exact(io_fd, &j, sizeof(unsigned int))) {
|
|
ERROR("Error when writing to state file (6a)");
|
|
return -1;
|
|
}
|
|
|
|
j = 0;
|
|
for (p = memmap_desc_start;
|
|
p < memmap_desc_end;
|
|
p += memmap_info->efi_memdesc_size) {
|
|
md = p;
|
|
|
|
if (md_is_not_ram(md))
|
|
continue;
|
|
|
|
for (N = md->phys_addr >> PAGE_SHIFT;
|
|
N < (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT)) >>
|
|
PAGE_SHIFT;
|
|
N++) {
|
|
if (!xc_ia64_p2m_allocated(p2m_table, N))
|
|
pfntab[j++] = N;
|
|
if (j == sizeof(pfntab)/sizeof(pfntab[0])) {
|
|
if (write_exact(io_fd, &pfntab, sizeof(pfntab[0]) * j)) {
|
|
ERROR("Error when writing to state file (6b)");
|
|
return -1;
|
|
}
|
|
j = 0;
|
|
}
|
|
}
|
|
}
|
|
if (j > 0) {
|
|
if (write_exact(io_fd, &pfntab, sizeof(pfntab[0]) * j)) {
|
|
ERROR("Error when writing to state file (6c)");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
xc_ia64_send_vcpu_context(xc_interface *xch, int io_fd, uint32_t dom,
|
|
uint32_t vcpu, vcpu_guest_context_any_t *ctxt_any)
|
|
{
|
|
vcpu_guest_context_t *ctxt = &ctxt_any->c;
|
|
if (xc_vcpu_getcontext(xch, dom, vcpu, ctxt_any)) {
|
|
ERROR("Could not get vcpu context");
|
|
return -1;
|
|
}
|
|
|
|
if (write_exact(io_fd, ctxt, sizeof(*ctxt))) {
|
|
ERROR("Error when writing to state file (1)");
|
|
return -1;
|
|
}
|
|
|
|
fprintf(stderr, "ip=%016lx, b0=%016lx\n", ctxt->regs.ip, ctxt->regs.b[0]);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
xc_ia64_send_shared_info(xc_interface *xch, int io_fd, shared_info_t *live_shinfo)
|
|
{
|
|
if (write_exact(io_fd, live_shinfo, PAGE_SIZE)) {
|
|
ERROR("Error when writing to state file (1)");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
xc_ia64_send_vcpumap(xc_interface *xch, int io_fd, uint32_t dom,
|
|
const xc_dominfo_t *info, uint64_t max_virt_cpus,
|
|
uint64_t **vcpumapp)
|
|
{
|
|
int rc = -1;
|
|
unsigned int i;
|
|
unsigned long vcpumap_size;
|
|
uint64_t *vcpumap = NULL;
|
|
|
|
vcpumap_size = bitmap_size(max_virt_cpus);
|
|
rc = bitmap_alloc(&vcpumap, max_virt_cpus);
|
|
if (rc < 0) {
|
|
ERROR("memory alloc for vcpumap");
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i <= info->max_vcpu_id; i++) {
|
|
xc_vcpuinfo_t vinfo;
|
|
if ((xc_vcpu_getinfo(xch, dom, i, &vinfo) == 0) && vinfo.online)
|
|
set_bit(i, vcpumap);
|
|
}
|
|
|
|
if (write_exact(io_fd, &max_virt_cpus, sizeof(max_virt_cpus))) {
|
|
ERROR("write max_virt_cpus");
|
|
goto out;
|
|
}
|
|
|
|
if (write_exact(io_fd, vcpumap, vcpumap_size)) {
|
|
ERROR("write vcpumap");
|
|
goto out;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
if (rc != 0 && vcpumap != NULL) {
|
|
free(vcpumap);
|
|
vcpumap = NULL;
|
|
}
|
|
*vcpumapp = vcpumap;
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int
|
|
xc_ia64_pv_send_context(xc_interface *xch, int io_fd, uint32_t dom,
|
|
const xc_dominfo_t *info, shared_info_t *live_shinfo)
|
|
{
|
|
int rc = -1;
|
|
unsigned int i;
|
|
|
|
/* vcpu map */
|
|
uint64_t *vcpumap = NULL;
|
|
if (xc_ia64_send_vcpumap(xch, io_fd, dom, info, XEN_LEGACY_MAX_VCPUS,
|
|
&vcpumap))
|
|
goto out;
|
|
|
|
/* vcpu context */
|
|
for (i = 0; i <= info->max_vcpu_id; i++) {
|
|
/* A copy of the CPU context of the guest. */
|
|
vcpu_guest_context_any_t ctxt_any;
|
|
vcpu_guest_context_t *ctxt = &ctxt_any.c;
|
|
|
|
char *mem;
|
|
|
|
if (!test_bit(i, vcpumap))
|
|
continue;
|
|
|
|
if (xc_ia64_send_vcpu_context(xch, io_fd, dom, i, &ctxt_any))
|
|
goto out;
|
|
|
|
mem = xc_map_foreign_range(xch, dom, PAGE_SIZE,
|
|
PROT_READ|PROT_WRITE, ctxt->privregs_pfn);
|
|
if (mem == NULL) {
|
|
ERROR("cannot map privreg page");
|
|
goto out;
|
|
}
|
|
if (write_exact(io_fd, mem, PAGE_SIZE)) {
|
|
ERROR("Error when writing privreg to state file (5)");
|
|
munmap(mem, PAGE_SIZE);
|
|
goto out;
|
|
}
|
|
munmap(mem, PAGE_SIZE);
|
|
}
|
|
|
|
rc = xc_ia64_send_shared_info(xch, io_fd, live_shinfo);
|
|
|
|
out:
|
|
if (vcpumap != NULL)
|
|
free(vcpumap);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
xc_ia64_hvm_send_context(xc_interface *xch, int io_fd, uint32_t dom,
|
|
const xc_dominfo_t *info, shared_info_t *live_shinfo)
|
|
{
|
|
int rc = -1;
|
|
unsigned int i;
|
|
|
|
/* vcpu map */
|
|
uint64_t *vcpumap = NULL;
|
|
|
|
/* HVM: magic frames for ioreqs and xenstore comms */
|
|
const int hvm_params[] = {
|
|
HVM_PARAM_STORE_PFN,
|
|
HVM_PARAM_IOREQ_PFN,
|
|
HVM_PARAM_BUFIOREQ_PFN,
|
|
HVM_PARAM_BUFPIOREQ_PFN,
|
|
};
|
|
const int NR_PARAMS = sizeof(hvm_params) / sizeof(hvm_params[0]);
|
|
/* ioreq_pfn, bufioreq_pfn, store_pfn */
|
|
uint64_t magic_pfns[NR_PARAMS];
|
|
|
|
/* HVM: a buffer for holding HVM contxt */
|
|
uint64_t rec_size;
|
|
uint64_t hvm_buf_size = 0;
|
|
uint8_t *hvm_buf = NULL;
|
|
|
|
if (xc_ia64_send_shared_info(xch, io_fd, live_shinfo))
|
|
return -1;
|
|
|
|
/* vcpu map */
|
|
if (xc_ia64_send_vcpumap(xch, io_fd, dom, info, XEN_LEGACY_MAX_VCPUS,
|
|
&vcpumap))
|
|
goto out;
|
|
|
|
/* vcpu context */
|
|
for (i = 0; i <= info->max_vcpu_id; i++) {
|
|
/* A copy of the CPU context of the guest. */
|
|
vcpu_guest_context_any_t ctxt_any;
|
|
|
|
if (!test_bit(i, vcpumap))
|
|
continue;
|
|
|
|
if (xc_ia64_send_vcpu_context(xch, io_fd, dom, i, &ctxt_any))
|
|
goto out;
|
|
|
|
/* system context of vcpu is sent as hvm context. */
|
|
}
|
|
|
|
/* Save magic-page locations. */
|
|
memset(magic_pfns, 0, sizeof(magic_pfns));
|
|
for (i = 0; i < NR_PARAMS; i++) {
|
|
if (xc_get_hvm_param(xch, dom, hvm_params[i], &magic_pfns[i])) {
|
|
PERROR("Error when xc_get_hvm_param");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (write_exact(io_fd, magic_pfns, sizeof(magic_pfns))) {
|
|
ERROR("Error when writing to state file (7)");
|
|
goto out;
|
|
}
|
|
|
|
/* Need another buffer for HVM context */
|
|
hvm_buf_size = xc_domain_hvm_getcontext(xch, 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;
|
|
}
|
|
|
|
/* Get HVM context from Xen and save it too */
|
|
rec_size = xc_domain_hvm_getcontext(xch, dom, hvm_buf, hvm_buf_size);
|
|
if (rec_size == -1) {
|
|
ERROR("HVM:Could not get hvm buffer");
|
|
goto out;
|
|
}
|
|
|
|
if (write_exact(io_fd, &rec_size, sizeof(rec_size))) {
|
|
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;
|
|
}
|
|
|
|
rc = 0;
|
|
out:
|
|
if (hvm_buf != NULL)
|
|
free(hvm_buf);
|
|
if (vcpumap != NULL)
|
|
free(vcpumap);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
xc_domain_save(xc_interface *xch, int io_fd, uint32_t dom, uint32_t max_iters,
|
|
uint32_t max_factor, uint32_t flags,
|
|
struct save_callbacks* callbacks, int hvm)
|
|
{
|
|
DECLARE_DOMCTL;
|
|
xc_dominfo_t info;
|
|
|
|
int rc = 1;
|
|
|
|
int debug = (flags & XCFLAGS_DEBUG);
|
|
int live = (flags & XCFLAGS_LIVE);
|
|
|
|
/* The new domain's shared-info frame number. */
|
|
unsigned long shared_info_frame;
|
|
|
|
/* Live mapping of shared info structure */
|
|
shared_info_t *live_shinfo = NULL;
|
|
|
|
/* Iteration number. */
|
|
int iter;
|
|
|
|
/* Number of pages sent in the last iteration (live only). */
|
|
unsigned int sent_last_iter;
|
|
|
|
/* Number of pages sent (live only). */
|
|
unsigned int total_sent = 0;
|
|
|
|
/* total number of pages used by the current guest */
|
|
unsigned long p2m_size;
|
|
|
|
/* Size of the shadow bitmap (live only). */
|
|
unsigned int bitmap_size = 0;
|
|
|
|
/* True if last iteration. */
|
|
int last_iter = 0;
|
|
|
|
/* Bitmap of pages to be sent. */
|
|
DECLARE_HYPERCALL_BUFFER(unsigned long, to_send);
|
|
/* Bitmap of pages not to be sent (because dirtied). */
|
|
DECLARE_HYPERCALL_BUFFER(unsigned long, to_skip);
|
|
|
|
char *mem;
|
|
|
|
/* for foreign p2m exposure */
|
|
unsigned long memmap_info_num_pages;
|
|
/* Unsigned int was used before. To keep file format compatibility. */
|
|
unsigned int memmap_info_num_pages_to_send;
|
|
unsigned long memmap_size = 0;
|
|
xen_ia64_memmap_info_t *memmap_info = NULL;
|
|
void *memmap_desc_start;
|
|
void *memmap_desc_end;
|
|
void *p;
|
|
efi_memory_desc_t *md;
|
|
struct xen_ia64_p2m_table p2m_table;
|
|
|
|
if ( hvm && !callbacks->switch_qemu_logdirty )
|
|
{
|
|
ERROR("No switch_qemu_logdirty callback given.");
|
|
errno = EINVAL;
|
|
return 1;
|
|
}
|
|
|
|
xc_ia64_p2m_init(&p2m_table);
|
|
|
|
if (debug)
|
|
fprintf(stderr, "xc_linux_save (ia64): started dom=%d\n", dom);
|
|
|
|
/* If no explicit control parameters given, use defaults */
|
|
if (!max_iters)
|
|
max_iters = DEF_MAX_ITERS;
|
|
if (!max_factor)
|
|
max_factor = DEF_MAX_FACTOR;
|
|
|
|
//initialize_mbit_rate();
|
|
|
|
if (xc_domain_getinfo(xch, dom, 1, &info) != 1) {
|
|
ERROR("Could not get domain info");
|
|
return 1;
|
|
}
|
|
|
|
shared_info_frame = info.shared_info_frame;
|
|
|
|
#if 0
|
|
/* cheesy sanity check */
|
|
if ((info.max_memkb >> (PAGE_SHIFT - 10)) > max_mfn) {
|
|
ERROR("Invalid state record -- pfn count out of range: %lu",
|
|
(info.max_memkb >> (PAGE_SHIFT - 10)));
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
/* Map the shared info frame */
|
|
live_shinfo = xc_map_foreign_range(xch, dom, PAGE_SIZE,
|
|
PROT_READ, shared_info_frame);
|
|
if (!live_shinfo) {
|
|
ERROR("Couldn't map live_shinfo");
|
|
goto out;
|
|
}
|
|
|
|
p2m_size = xc_domain_maximum_gpfn(xch, dom) + 1;
|
|
|
|
/* This is expected by xm restore. */
|
|
if (write_exact(io_fd, &p2m_size, sizeof(unsigned long))) {
|
|
ERROR("write: p2m_size");
|
|
goto out;
|
|
}
|
|
|
|
/* xc_linux_restore starts to read here. */
|
|
/* Write a version number. This can avoid searching for a stupid bug
|
|
if the format change.
|
|
The version is hard-coded, don't forget to change the restore code
|
|
too! */
|
|
{
|
|
unsigned long version = XC_IA64_SR_FORMAT_VER_CURRENT;
|
|
|
|
if (write_exact(io_fd, &version, sizeof(unsigned long))) {
|
|
ERROR("write: version");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
domctl.cmd = XEN_DOMCTL_arch_setup;
|
|
domctl.domain = (domid_t)dom;
|
|
domctl.u.arch_setup.flags = XEN_DOMAINSETUP_query;
|
|
if (xc_domctl(xch, &domctl) < 0) {
|
|
ERROR("Could not get domain setup");
|
|
goto out;
|
|
}
|
|
if (write_exact(io_fd, &domctl.u.arch_setup,
|
|
sizeof(domctl.u.arch_setup))) {
|
|
ERROR("write: domain setup");
|
|
goto out;
|
|
}
|
|
|
|
/* Domain is still running at this point */
|
|
if (live) {
|
|
|
|
if (xc_shadow_control(xch, dom,
|
|
XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY,
|
|
NULL, 0, NULL, 0, NULL ) < 0) {
|
|
ERROR("Couldn't enable shadow mode");
|
|
goto out;
|
|
}
|
|
|
|
last_iter = 0;
|
|
|
|
bitmap_size = ((p2m_size + BITS_PER_LONG-1) & ~(BITS_PER_LONG-1)) / 8;
|
|
to_send = xc_hypercall_buffer_alloc(xch, to_send, bitmap_size);
|
|
to_skip = xc_hypercall_buffer_alloc(xch, to_skip, bitmap_size);
|
|
|
|
if (!to_send || !to_skip) {
|
|
ERROR("Couldn't allocate bitmap array");
|
|
goto out;
|
|
}
|
|
|
|
/* Initially all the pages must be sent. */
|
|
memset(to_send, 0xff, bitmap_size);
|
|
|
|
/* Enable qemu-dm logging dirty pages to xen */
|
|
if (hvm && callbacks->switch_qemu_logdirty(dom, 1, callbacks->data)) {
|
|
ERROR("Unable to enable qemu log-dirty mode");
|
|
goto out;
|
|
}
|
|
} else {
|
|
|
|
/* This is a non-live suspend. Issue the call back to get the
|
|
domain suspended */
|
|
|
|
last_iter = 1;
|
|
|
|
if (suspend_and_state(callbacks->suspend, callbacks->data, xch,
|
|
io_fd, dom, &info)) {
|
|
ERROR("Domain appears not to have suspended");
|
|
goto out;
|
|
}
|
|
|
|
}
|
|
|
|
/* copy before use in case someone updating them */
|
|
if (xc_ia64_copy_memmap(xch, info.domid, live_shinfo,
|
|
&memmap_info, &memmap_info_num_pages) != 0) {
|
|
PERROR("Could not copy memmap");
|
|
goto out;
|
|
}
|
|
memmap_size = memmap_info_num_pages << PAGE_SHIFT;
|
|
|
|
if (xc_ia64_p2m_map(&p2m_table, xch, dom, memmap_info, 0) < 0) {
|
|
PERROR("xc_ia64_p2m_map");
|
|
goto out;
|
|
}
|
|
memmap_info_num_pages_to_send = memmap_info_num_pages;
|
|
if (write_exact(io_fd, &memmap_info_num_pages_to_send,
|
|
sizeof(memmap_info_num_pages_to_send))) {
|
|
PERROR("write: arch.memmap_info_num_pages");
|
|
goto out;
|
|
}
|
|
if (write_exact(io_fd, memmap_info, memmap_size)) {
|
|
PERROR("write: memmap_info");
|
|
goto out;
|
|
}
|
|
|
|
sent_last_iter = p2m_size;
|
|
|
|
copypages:
|
|
for (iter = 1; ; iter++) {
|
|
unsigned int sent_this_iter, skip_this_iter;
|
|
unsigned long N;
|
|
|
|
sent_this_iter = 0;
|
|
skip_this_iter = 0;
|
|
|
|
/* Dirtied pages won't be saved.
|
|
slightly wasteful to peek the whole array evey time,
|
|
but this is fast enough for the moment. */
|
|
if (!last_iter) {
|
|
if (xc_shadow_control(xch, dom,
|
|
XEN_DOMCTL_SHADOW_OP_PEEK,
|
|
HYPERCALL_BUFFER(to_skip), p2m_size,
|
|
NULL, 0, NULL) != p2m_size) {
|
|
ERROR("Error peeking shadow bitmap");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Start writing out the saved-domain record. */
|
|
memmap_desc_start = &memmap_info->memdesc;
|
|
memmap_desc_end = memmap_desc_start + memmap_info->efi_memmap_size;
|
|
for (p = memmap_desc_start;
|
|
p < memmap_desc_end;
|
|
p += memmap_info->efi_memdesc_size) {
|
|
md = p;
|
|
if (md_is_not_ram(md))
|
|
continue;
|
|
|
|
for (N = md->phys_addr >> PAGE_SHIFT;
|
|
N < (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT)) >>
|
|
PAGE_SHIFT;
|
|
N++) {
|
|
|
|
if (!xc_ia64_p2m_allocated(&p2m_table, N))
|
|
continue;
|
|
|
|
if (!last_iter) {
|
|
if (test_bit(N, to_skip) && test_bit(N, to_send))
|
|
skip_this_iter++;
|
|
if (test_bit(N, to_skip) || !test_bit(N, to_send))
|
|
continue;
|
|
} else if (live) {
|
|
if (!test_bit(N, to_send))
|
|
continue;
|
|
}
|
|
|
|
if (debug)
|
|
fprintf(stderr, "xc_linux_save: page %lx (%lu/%lu)\n",
|
|
xc_ia64_p2m_mfn(&p2m_table, N),
|
|
N, p2m_size);
|
|
|
|
mem = xc_map_foreign_range(xch, dom, PAGE_SIZE,
|
|
PROT_READ|PROT_WRITE, N);
|
|
if (mem == NULL) {
|
|
/* The page may have move.
|
|
It will be remarked dirty.
|
|
FIXME: to be tracked. */
|
|
fprintf(stderr, "cannot map mfn page %lx gpfn %lx: %s\n",
|
|
xc_ia64_p2m_mfn(&p2m_table, N),
|
|
N, xc_strerror(xch, errno));
|
|
goto out;
|
|
}
|
|
|
|
if (write_exact(io_fd, &N, sizeof(N))) {
|
|
ERROR("write: p2m_size");
|
|
munmap(mem, PAGE_SIZE);
|
|
goto out;
|
|
}
|
|
|
|
if (write(io_fd, mem, PAGE_SIZE) != PAGE_SIZE) {
|
|
ERROR("Error when writing to state file (5)");
|
|
munmap(mem, PAGE_SIZE);
|
|
goto out;
|
|
}
|
|
munmap(mem, PAGE_SIZE);
|
|
sent_this_iter++;
|
|
total_sent++;
|
|
}
|
|
}
|
|
|
|
if (last_iter)
|
|
break;
|
|
|
|
DPRINTF(" %d: sent %d, skipped %d\n",
|
|
iter, sent_this_iter, skip_this_iter );
|
|
|
|
if (live) {
|
|
if ( /* ((sent_this_iter > sent_last_iter) && RATE_IS_MAX()) || */
|
|
(iter >= max_iters) || (sent_this_iter+skip_this_iter < 50) ||
|
|
(total_sent > p2m_size*max_factor)) {
|
|
DPRINTF("Start last iteration\n");
|
|
last_iter = 1;
|
|
|
|
if (suspend_and_state(callbacks->suspend, callbacks->data,
|
|
xch, io_fd, dom, &info)) {
|
|
ERROR("Domain appears not to have suspended");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Pages to be sent are pages which were dirty. */
|
|
if (xc_shadow_control(xch, dom,
|
|
XEN_DOMCTL_SHADOW_OP_CLEAN,
|
|
HYPERCALL_BUFFER(to_send), p2m_size,
|
|
NULL, 0, NULL ) != p2m_size) {
|
|
ERROR("Error flushing shadow PT");
|
|
goto out;
|
|
}
|
|
|
|
sent_last_iter = sent_this_iter;
|
|
|
|
//print_stats(xch, dom, sent_this_iter, &stats, 1);
|
|
}
|
|
}
|
|
|
|
fprintf(stderr, "All memory is saved\n");
|
|
|
|
/* terminate */
|
|
{
|
|
unsigned long pfn = INVALID_MFN;
|
|
if (write_exact(io_fd, &pfn, sizeof(pfn))) {
|
|
ERROR("Error when writing to state file (6)");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (xc_ia64_send_unallocated_list(xch, io_fd, &p2m_table,
|
|
memmap_info,
|
|
memmap_desc_start, memmap_desc_end))
|
|
goto out;
|
|
|
|
if (!hvm)
|
|
rc = xc_ia64_pv_send_context(xch, io_fd,
|
|
dom, &info, live_shinfo);
|
|
else
|
|
rc = xc_ia64_hvm_send_context(xch, io_fd,
|
|
dom, &info, live_shinfo);
|
|
if (rc)
|
|
goto out;
|
|
|
|
/* Success! */
|
|
rc = 0;
|
|
|
|
out:
|
|
if ( !rc && callbacks->postcopy )
|
|
callbacks->postcopy(callbacks->data);
|
|
|
|
/* Flush last write and discard cache for file. */
|
|
discard_file_cache(xch, io_fd, 1 /* flush */);
|
|
|
|
/* checkpoint_cb can spend arbitrarily long in between rounds */
|
|
if (!rc && callbacks->checkpoint &&
|
|
callbacks->checkpoint(callbacks->data) > 0)
|
|
{
|
|
/* reset stats timer */
|
|
//print_stats(xch, dom, 0, &stats, 0);
|
|
|
|
rc = 1;
|
|
/* last_iter = 1; */
|
|
if ( suspend_and_state(callbacks->suspend, callbacks->data, xch,
|
|
io_fd, dom, &info) )
|
|
{
|
|
ERROR("Domain appears not to have suspended");
|
|
goto out;
|
|
}
|
|
DPRINTF("SUSPEND shinfo %08lx\n", info.shared_info_frame);
|
|
//print_stats(xch, dom, 0, &stats, 1);
|
|
|
|
if ( xc_shadow_control(xch, dom,
|
|
XEN_DOMCTL_SHADOW_OP_CLEAN, HYPERCALL_BUFFER(to_send),
|
|
p2m_size, NULL, 0, NULL) != p2m_size )
|
|
{
|
|
ERROR("Error flushing shadow PT");
|
|
}
|
|
|
|
goto copypages;
|
|
}
|
|
|
|
if (live) {
|
|
if (xc_shadow_control(xch, dom,
|
|
XEN_DOMCTL_SHADOW_OP_OFF,
|
|
NULL, 0, NULL, 0, NULL ) < 0) {
|
|
DPRINTF("Warning - couldn't disable shadow mode");
|
|
}
|
|
if ( hvm &&
|
|
callbacks->switch_qemu_logdirty(dom, 0, callbacks->data) ) {
|
|
DPRINTF("Warning - couldn't disable qemu log-dirty mode");
|
|
}
|
|
}
|
|
|
|
xc_hypercall_buffer_free(xch, to_send);
|
|
xc_hypercall_buffer_free(xch, to_skip);
|
|
if (live_shinfo)
|
|
munmap(live_shinfo, PAGE_SIZE);
|
|
if (memmap_info)
|
|
free(memmap_info);
|
|
xc_ia64_p2m_unmap(&p2m_table);
|
|
|
|
fprintf(stderr,"Save exit rc=%d\n",rc);
|
|
|
|
return !!rc;
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-set-style: "BSD"
|
|
* c-basic-offset: 4
|
|
* tab-width: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|