ia64/xen-unstable
changeset 14436:8e76e1b95b12
[HVM][QEMU] Save/restore: enable HVM live migration
by getting page-dirtying bitmaps from qemu-dm as well as from xen.
Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com>
by getting page-dirtying bitmaps from qemu-dm as well as from xen.
Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com>
author | Tim Deegan <Tim.Deegan@xensource.com> |
---|---|
date | Fri Mar 16 11:39:50 2007 +0000 (2007-03-16) |
parents | 422a61ebac54 |
children | a20e3ad50ae8 |
files | tools/ioemu/target-i386-dm/exec-dm.c tools/ioemu/xenstore.c tools/libxc/Makefile tools/libxc/xc_hvm_save.c tools/libxc/xenguest.h tools/libxc/xg_private.c tools/xcutils/Makefile tools/xcutils/xc_save.c |
line diff
1.1 --- a/tools/ioemu/target-i386-dm/exec-dm.c Fri Mar 16 10:42:25 2007 +0000 1.2 +++ b/tools/ioemu/target-i386-dm/exec-dm.c Fri Mar 16 11:39:50 2007 +0000 1.3 @@ -450,6 +450,9 @@ static inline int paddr_is_ram(target_ph 1.4 #define phys_ram_addr(x) (phys_ram_base + (x)) 1.5 #endif 1.6 1.7 +extern unsigned long *logdirty_bitmap; 1.8 +extern unsigned long logdirty_bitmap_size; 1.9 + 1.10 void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, 1.11 int len, int is_write) 1.12 { 1.13 @@ -485,9 +488,20 @@ void cpu_physical_memory_rw(target_phys_ 1.14 l = 1; 1.15 } 1.16 } else if (paddr_is_ram(addr)) { 1.17 - /* Reading from RAM */ 1.18 + /* Writing to RAM */ 1.19 ptr = phys_ram_addr(addr); 1.20 memcpy(ptr, buf, l); 1.21 + if (logdirty_bitmap != NULL) { 1.22 + /* Record that we have dirtied this frame */ 1.23 + unsigned long pfn = addr >> TARGET_PAGE_BITS; 1.24 + if (pfn / 8 >= logdirty_bitmap_size) { 1.25 + fprintf(logfile, "dirtying pfn %x >= bitmap size %x\n", 1.26 + pfn, logdirty_bitmap_size * 8); 1.27 + } else { 1.28 + logdirty_bitmap[pfn / HOST_LONG_BITS] 1.29 + |= 1UL << pfn % HOST_LONG_BITS; 1.30 + } 1.31 + } 1.32 #ifdef __ia64__ 1.33 sync_icache(ptr, l); 1.34 #endif
2.1 --- a/tools/ioemu/xenstore.c Fri Mar 16 10:42:25 2007 +0000 2.2 +++ b/tools/ioemu/xenstore.c Fri Mar 16 11:39:50 2007 +0000 2.3 @@ -11,6 +11,11 @@ 2.4 #include "vl.h" 2.5 #include "block_int.h" 2.6 #include <unistd.h> 2.7 +#include <sys/ipc.h> 2.8 +#include <sys/shm.h> 2.9 +#include <sys/types.h> 2.10 +#include <sys/stat.h> 2.11 +#include <fcntl.h> 2.12 2.13 static struct xs_handle *xsh = NULL; 2.14 static char *hd_filename[MAX_DISKS]; 2.15 @@ -183,6 +188,13 @@ void xenstore_parse_domain_config(int do 2.16 } 2.17 } 2.18 2.19 + /* Set a watch for log-dirty requests from the migration tools */ 2.20 + if (pasprintf(&buf, "%s/logdirty/next-active", path) != -1) { 2.21 + xs_watch(xsh, buf, "logdirty"); 2.22 + fprintf(logfile, "Watching %s\n", buf); 2.23 + } 2.24 + 2.25 + 2.26 out: 2.27 free(type); 2.28 free(params); 2.29 @@ -201,6 +213,116 @@ int xenstore_fd(void) 2.30 return -1; 2.31 } 2.32 2.33 +unsigned long *logdirty_bitmap = NULL; 2.34 +unsigned long logdirty_bitmap_size; 2.35 +extern int vga_ram_size, bios_size; 2.36 + 2.37 +void xenstore_process_logdirty_event(void) 2.38 +{ 2.39 + char *act; 2.40 + static char *active_path = NULL; 2.41 + static char *next_active_path = NULL; 2.42 + static char *seg = NULL; 2.43 + unsigned int len; 2.44 + int i; 2.45 + 2.46 + fprintf(logfile, "Triggered log-dirty buffer switch\n"); 2.47 + 2.48 + if (!seg) { 2.49 + char *path, *p, *key_ascii, *key_terminated[17] = {0,}; 2.50 + key_t key; 2.51 + int shmid; 2.52 + 2.53 + /* Find and map the shared memory segment for log-dirty bitmaps */ 2.54 + if (!(path = xs_get_domain_path(xsh, domid))) { 2.55 + fprintf(logfile, "Log-dirty: can't get domain path in store\n"); 2.56 + exit(1); 2.57 + } 2.58 + if (!(path = realloc(path, strlen(path) 2.59 + + strlen("/logdirty/next-active") + 1))) { 2.60 + fprintf(logfile, "Log-dirty: out of memory\n"); 2.61 + exit(1); 2.62 + } 2.63 + strcat(path, "/logdirty/"); 2.64 + p = path + strlen(path); 2.65 + strcpy(p, "key"); 2.66 + 2.67 + key_ascii = xs_read(xsh, XBT_NULL, path, &len); 2.68 + if (!key_ascii) { 2.69 + /* No key yet: wait for the next watch */ 2.70 + free(path); 2.71 + return; 2.72 + } 2.73 + strncpy(key_terminated, key_ascii, 16); 2.74 + free(key_ascii); 2.75 + key = (key_t) strtoull(key_terminated, NULL, 16); 2.76 + 2.77 + /* Figure out how bit the log-dirty bitmaps are */ 2.78 + logdirty_bitmap_size = ((phys_ram_size + 0x20 2.79 + - (vga_ram_size + bios_size)) 2.80 + >> (TARGET_PAGE_BITS)); /* nr of bits in map*/ 2.81 + if (logdirty_bitmap_size > HVM_BELOW_4G_MMIO_START >> TARGET_PAGE_BITS) 2.82 + logdirty_bitmap_size += 2.83 + HVM_BELOW_4G_MMIO_LENGTH >> TARGET_PAGE_BITS; /* still bits */ 2.84 + logdirty_bitmap_size = ((logdirty_bitmap_size + HOST_LONG_BITS - 1) 2.85 + / HOST_LONG_BITS); /* longs */ 2.86 + logdirty_bitmap_size *= sizeof (unsigned long); /* bytes */ 2.87 + 2.88 + /* Map the shared-memory segment */ 2.89 + if ((shmid = shmget(key, 2.90 + 2 * logdirty_bitmap_size, 2.91 + S_IRUSR|S_IWUSR)) == -1 2.92 + || (seg = shmat(shmid, NULL, 0)) == (void *)-1) { 2.93 + fprintf(logfile, "Log-dirty: can't map segment %16.16llx (%s)\n", 2.94 + (unsigned long long) key, strerror(errno)); 2.95 + exit(1); 2.96 + } 2.97 + 2.98 + fprintf(logfile, "Log-dirty: mapped segment at %p\n", seg); 2.99 + 2.100 + /* Double-check that the bitmaps are the size we expect */ 2.101 + if (logdirty_bitmap_size != *(uint32_t *)seg) { 2.102 + fprintf(logfile, "Log-dirty: got %lu, calc %lu\n", 2.103 + *(uint32_t *)seg, logdirty_bitmap_size); 2.104 + return; 2.105 + } 2.106 + 2.107 + /* Remember the paths for the next-active and active entries */ 2.108 + strcpy(p, "active"); 2.109 + if (!(active_path = strdup(path))) { 2.110 + fprintf(logfile, "Log-dirty: out of memory\n"); 2.111 + exit(1); 2.112 + } 2.113 + strcpy(p, "next-active"); 2.114 + if (!(next_active_path = strdup(path))) { 2.115 + fprintf(logfile, "Log-dirty: out of memory\n"); 2.116 + exit(1); 2.117 + } 2.118 + free(path); 2.119 + } 2.120 + 2.121 + /* Read the required active buffer from the store */ 2.122 + act = xs_read(xsh, XBT_NULL, next_active_path, &len); 2.123 + if (!act) { 2.124 + fprintf(logfile, "Log-dirty: can't read next-active\n"); 2.125 + exit(1); 2.126 + } 2.127 + 2.128 + /* Switch buffers */ 2.129 + i = act[0] - '0'; 2.130 + if (i != 0 && i != 1) { 2.131 + fprintf(logfile, "Log-dirty: bad next-active entry: %s\n", act); 2.132 + exit(1); 2.133 + } 2.134 + logdirty_bitmap = seg + i * logdirty_bitmap_size; 2.135 + 2.136 + /* Ack that we've switched */ 2.137 + xs_write(xsh, XBT_NULL, active_path, act, len); 2.138 + free(act); 2.139 +} 2.140 + 2.141 + 2.142 + 2.143 void xenstore_process_event(void *opaque) 2.144 { 2.145 char **vec, *image = NULL; 2.146 @@ -210,6 +332,11 @@ void xenstore_process_event(void *opaque 2.147 if (!vec) 2.148 return; 2.149 2.150 + if (!strcmp(vec[XS_WATCH_TOKEN], "logdirty")) { 2.151 + xenstore_process_logdirty_event(); 2.152 + goto out; 2.153 + } 2.154 + 2.155 if (strncmp(vec[XS_WATCH_TOKEN], "hd", 2) || 2.156 strlen(vec[XS_WATCH_TOKEN]) != 3) 2.157 goto out;
3.1 --- a/tools/libxc/Makefile Fri Mar 16 10:42:25 2007 +0000 3.2 +++ b/tools/libxc/Makefile Fri Mar 16 11:39:50 2007 +0000 3.3 @@ -57,7 +57,7 @@ GUEST_SRCS-$(CONFIG_IA64) += xc_dom_c 3.4 3.5 CFLAGS += -Werror -Wmissing-prototypes 3.6 CFLAGS += -fno-strict-aliasing 3.7 -CFLAGS += $(INCLUDES) -I. 3.8 +CFLAGS += $(INCLUDES) -I. -I../xenstore 3.9 3.10 # Needed for posix_fadvise64() in xc_linux.c 3.11 CFLAGS-$(CONFIG_Linux) += -D_GNU_SOURCE
4.1 --- a/tools/libxc/xc_hvm_save.c Fri Mar 16 10:42:25 2007 +0000 4.2 +++ b/tools/libxc/xc_hvm_save.c Fri Mar 16 11:39:50 2007 +0000 4.3 @@ -54,6 +54,11 @@ static unsigned long hvirt_start; 4.4 /* #levels of page tables used by the current guest */ 4.5 static unsigned int pt_levels; 4.6 4.7 +/* Shared-memory bitmaps for getting log-dirty bits from qemu */ 4.8 +static unsigned long *qemu_bitmaps[2]; 4.9 +static int qemu_active; 4.10 +static int qemu_non_active; 4.11 + 4.12 int xc_hvm_drain_io(int handle, domid_t dom) 4.13 { 4.14 DECLARE_HYPERCALL; 4.15 @@ -77,7 +82,8 @@ int xc_hvm_drain_io(int handle, domid_t 4.16 */ 4.17 4.18 #define BITS_PER_LONG (sizeof(unsigned long) * 8) 4.19 -#define BITMAP_SIZE ((pfn_array_size + BITS_PER_LONG - 1) / 8) 4.20 +#define BITS_TO_LONGS(bits) (((bits)+BITS_PER_LONG-1)/BITS_PER_LONG) 4.21 +#define BITMAP_SIZE (BITS_TO_LONGS(pfn_array_size) * sizeof(unsigned long)) 4.22 4.23 #define BITMAP_ENTRY(_nr,_bmap) \ 4.24 ((unsigned long *)(_bmap))[(_nr)/BITS_PER_LONG] 4.25 @@ -124,6 +130,7 @@ static inline int permute( int i, int nr 4.26 return i; 4.27 } 4.28 4.29 + 4.30 static uint64_t tv_to_us(struct timeval *new) 4.31 { 4.32 return (new->tv_sec * 1000000) + new->tv_usec; 4.33 @@ -277,7 +284,9 @@ static int suspend_and_state(int (*suspe 4.34 } 4.35 4.36 int xc_hvm_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, 4.37 - uint32_t max_factor, uint32_t flags, int (*suspend)(int)) 4.38 + uint32_t max_factor, uint32_t flags, int (*suspend)(int), 4.39 + void *(*init_qemu_maps)(int, unsigned), 4.40 + void (*qemu_flip_buffer)(int, int)) 4.41 { 4.42 xc_dominfo_t info; 4.43 4.44 @@ -392,8 +401,6 @@ int xc_hvm_save(int xc_handle, int io_fd 4.45 "nr_pages=0x%lx\n", info.max_memkb, max_mfn, info.nr_pages); 4.46 4.47 if (live) { 4.48 - ERROR("hvm domain doesn't support live migration now.\n"); 4.49 - goto out; 4.50 4.51 if (xc_shadow_control(xc_handle, dom, 4.52 XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY, 4.53 @@ -453,6 +460,15 @@ int xc_hvm_save(int xc_handle, int io_fd 4.54 to_skip = malloc(BITMAP_SIZE); 4.55 4.56 4.57 + if (live) { 4.58 + /* Get qemu-dm logging dirty pages too */ 4.59 + void *seg = init_qemu_maps(dom, BITMAP_SIZE); 4.60 + qemu_bitmaps[0] = seg; 4.61 + qemu_bitmaps[1] = seg + BITMAP_SIZE; 4.62 + qemu_active = 0; 4.63 + qemu_non_active = 1; 4.64 + } 4.65 + 4.66 hvm_buf_size = xc_domain_hvm_getcontext(xc_handle, dom, 0, 0); 4.67 if ( hvm_buf_size == -1 ) 4.68 { 4.69 @@ -677,10 +693,23 @@ int xc_hvm_save(int xc_handle, int io_fd 4.70 goto out; 4.71 } 4.72 4.73 + /* Pull in the dirty bits from qemu too */ 4.74 + if (!last_iter) { 4.75 + qemu_active = qemu_non_active; 4.76 + qemu_non_active = qemu_active ? 0 : 1; 4.77 + qemu_flip_buffer(dom, qemu_active); 4.78 + for (j = 0; j < BITMAP_SIZE / sizeof(unsigned long); j++) { 4.79 + to_send[j] |= qemu_bitmaps[qemu_non_active][j]; 4.80 + qemu_bitmaps[qemu_non_active][j] = 0; 4.81 + } 4.82 + } else { 4.83 + for (j = 0; j < BITMAP_SIZE / sizeof(unsigned long); j++) 4.84 + to_send[j] |= qemu_bitmaps[qemu_active][j]; 4.85 + } 4.86 + 4.87 sent_last_iter = sent_this_iter; 4.88 4.89 print_stats(xc_handle, dom, sent_this_iter, &stats, 1); 4.90 - 4.91 } 4.92 4.93
5.1 --- a/tools/libxc/xenguest.h Fri Mar 16 10:42:25 2007 +0000 5.2 +++ b/tools/libxc/xenguest.h Fri Mar 16 11:39:50 2007 +0000 5.3 @@ -32,8 +32,10 @@ int xc_linux_save(int xc_handle, int io_ 5.4 * @return 0 on success, -1 on failure 5.5 */ 5.6 int xc_hvm_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, 5.7 - uint32_t max_factor, uint32_t flags /* XCFLAGS_xxx */, 5.8 - int (*suspend)(int domid)); 5.9 + uint32_t max_factor, uint32_t flags /* XCFLAGS_xxx */, 5.10 + int (*suspend)(int domid), 5.11 + void *(*init_qemu_maps)(int, unsigned), 5.12 + void (*qemu_flip_buffer)(int, int)); 5.13 5.14 /** 5.15 * This function will restore a saved domain running Linux.
6.1 --- a/tools/libxc/xg_private.c Fri Mar 16 10:42:25 2007 +0000 6.2 +++ b/tools/libxc/xg_private.c Fri Mar 16 11:39:50 2007 +0000 6.3 @@ -201,7 +201,9 @@ unsigned long csum_page(void *page) 6.4 __attribute__((weak)) 6.5 int xc_hvm_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, 6.6 uint32_t max_factor, uint32_t flags, 6.7 - int (*suspend)(int domid)) 6.8 + int (*suspend)(int domid), 6.9 + void *(*init_qemu_maps)(int, unsigned), 6.10 + void (*qemu_flip_buffer)(int, int)) 6.11 { 6.12 errno = ENOSYS; 6.13 return -1;
7.1 --- a/tools/xcutils/Makefile Fri Mar 16 10:42:25 2007 +0000 7.2 +++ b/tools/xcutils/Makefile Fri Mar 16 11:39:50 2007 +0000 7.3 @@ -13,7 +13,7 @@ include $(XEN_ROOT)/tools/Rules.mk 7.4 7.5 PROGRAMS_INSTALL_DIR = /usr/$(LIBDIR)/xen/bin 7.6 7.7 -INCLUDES += -I $(XEN_LIBXC) 7.8 +INCLUDES += -I $(XEN_LIBXC) -I $(XEN_XENSTORE) 7.9 7.10 CFLAGS += -Werror -fno-strict-aliasing 7.11 CFLAGS += $(INCLUDES) 7.12 @@ -22,9 +22,9 @@ CFLAGS += $(INCLUDES) 7.13 CFLAGS += -Wp,-MD,.$(@F).d 7.14 PROG_DEP = .*.d 7.15 7.16 -PROGRAMS = xc_restore xc_save readnotes 7.17 +PROGRAMS = xc_restore xc_save readnotes 7.18 7.19 -LDLIBS = -L$(XEN_LIBXC) -lxenguest -lxenctrl 7.20 +LDLIBS = -L$(XEN_LIBXC) -L$(XEN_XENSTORE) -lxenguest -lxenctrl -lxenstore 7.21 7.22 .PHONY: all 7.23 all: build
8.1 --- a/tools/xcutils/xc_save.c Fri Mar 16 10:42:25 2007 +0000 8.2 +++ b/tools/xcutils/xc_save.c Fri Mar 16 11:39:50 2007 +0000 8.3 @@ -12,7 +12,13 @@ 8.4 #include <stdint.h> 8.5 #include <string.h> 8.6 #include <stdio.h> 8.7 +#include <sys/ipc.h> 8.8 +#include <sys/shm.h> 8.9 +#include <sys/types.h> 8.10 +#include <sys/stat.h> 8.11 +#include <fcntl.h> 8.12 8.13 +#include <xs.h> 8.14 #include <xenctrl.h> 8.15 #include <xenguest.h> 8.16 8.17 @@ -31,6 +37,123 @@ static int suspend(int domid) 8.18 !strncmp(ans, "done\n", 5)); 8.19 } 8.20 8.21 +/* For HVM guests, there are two sources of dirty pages: the Xen shadow 8.22 + * log-dirty bitmap, which we get with a hypercall, and qemu's version. 8.23 + * The protocol for getting page-dirtying data from qemu uses a 8.24 + * double-buffered shared memory interface directly between xc_save and 8.25 + * qemu-dm. 8.26 + * 8.27 + * xc_save calculates the size of the bitmaps and notifies qemu-dm 8.28 + * through the store that it wants to share the bitmaps. qemu-dm then 8.29 + * starts filling in the 'active' buffer. 8.30 + * 8.31 + * To change the buffers over, xc_save writes the other buffer number to 8.32 + * the store and waits for qemu to acknowledge that it is now writing to 8.33 + * the new active buffer. xc_save can then process and clear the old 8.34 + * active buffer. */ 8.35 + 8.36 +static char *qemu_active_path; 8.37 +static char *qemu_next_active_path; 8.38 +static struct xs_handle *xs; 8.39 + 8.40 +/* Get qemu to change buffers. */ 8.41 +static void qemu_flip_buffer(int domid, int next_active) 8.42 +{ 8.43 + char digit = '0' + next_active; 8.44 + unsigned int len; 8.45 + char *active_str, **watch; 8.46 + struct timeval tv; 8.47 + fd_set fdset; 8.48 + 8.49 + /* Tell qemu that we want it to start writing log-dirty bits to the 8.50 + * other buffer */ 8.51 + if (!xs_write(xs, XBT_NULL, qemu_next_active_path, &digit, 1)) { 8.52 + errx(1, "can't write next-active to store path (%s)\n", 8.53 + qemu_next_active_path); 8.54 + exit(1); 8.55 + } 8.56 + 8.57 + /* Wait a while for qemu to signal that it has switched to the new 8.58 + * active buffer */ 8.59 + read_again: 8.60 + tv.tv_sec = 5; 8.61 + tv.tv_usec = 0; 8.62 + FD_ZERO(&fdset); 8.63 + FD_SET(xs_fileno(xs), &fdset); 8.64 + if ((select(xs_fileno(xs) + 1, &fdset, NULL, NULL, &tv)) != 1) { 8.65 + errx(1, "timed out waiting for qemu to switch buffers\n"); 8.66 + exit(1); 8.67 + } 8.68 + watch = xs_read_watch(xs, &len); 8.69 + free(watch); 8.70 + 8.71 + active_str = xs_read(xs, XBT_NULL, qemu_active_path, &len); 8.72 + if (active_str == NULL || active_str[0] - '0' != next_active) 8.73 + /* Watch fired but value is not yet right */ 8.74 + goto read_again; 8.75 +} 8.76 + 8.77 +static void * init_qemu_maps(int domid, unsigned int bitmap_size) 8.78 +{ 8.79 + key_t key; 8.80 + char key_ascii[17] = {0,}; 8.81 + int shmid = -1; 8.82 + void *seg; 8.83 + char *path, *p; 8.84 + 8.85 + /* Make a shared-memory segment */ 8.86 + while (shmid == -1) 8.87 + { 8.88 + key = rand(); /* No security, just a sequence of numbers */ 8.89 + shmid = shmget(key, 2 * bitmap_size, 8.90 + IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR); 8.91 + if (shmid == -1 && errno != EEXIST) 8.92 + errx(1, "can't get shmem to talk to qemu-dm"); 8.93 + } 8.94 + 8.95 + /* Map it into our address space */ 8.96 + seg = shmat(shmid, NULL, 0); 8.97 + if (seg == (void *) -1) 8.98 + errx(1, "can't map shmem to talk to qemu-dm"); 8.99 + memset(seg, 0, 2 * bitmap_size); 8.100 + 8.101 + /* Write the size of it into the first 32 bits */ 8.102 + *(uint32_t *)seg = bitmap_size; 8.103 + 8.104 + /* Tell qemu about it */ 8.105 + if ((xs = xs_daemon_open()) == NULL) 8.106 + errx(1, "Couldn't contact xenstore"); 8.107 + if (!(path = xs_get_domain_path(xs, domid))) 8.108 + errx(1, "can't get domain path in store"); 8.109 + if (!(path = realloc(path, strlen(path) 8.110 + + strlen("/logdirty/next-active") + 1))) 8.111 + errx(1, "no memory for constructing xenstore path"); 8.112 + strcat(path, "/logdirty/"); 8.113 + p = path + strlen(path); 8.114 + 8.115 + strcpy(p, "key"); 8.116 + snprintf(key_ascii, 17, "%16.16llx", (unsigned long long) key); 8.117 + if (!xs_write(xs, XBT_NULL, path, key_ascii, 16)) 8.118 + errx(1, "can't write key (%s) to store path (%s)\n", key_ascii, path); 8.119 + 8.120 + /* Watch for qemu's indication of the active buffer, and request it 8.121 + * to start writing to buffer 0 */ 8.122 + strcpy(p, "active"); 8.123 + if (!xs_watch(xs, path, "qemu-active-buffer")) 8.124 + errx(1, "can't set watch in store (%s)\n", path); 8.125 + if (!(qemu_active_path = strdup(path))) 8.126 + errx(1, "no memory for copying xenstore path"); 8.127 + 8.128 + strcpy(p, "next-active"); 8.129 + if (!(qemu_next_active_path = strdup(path))) 8.130 + errx(1, "no memory for copying xenstore path"); 8.131 + 8.132 + qemu_flip_buffer(domid, 0); 8.133 + 8.134 + free(path); 8.135 + return seg; 8.136 +} 8.137 + 8.138 8.139 int 8.140 main(int argc, char **argv) 8.141 @@ -52,9 +175,11 @@ main(int argc, char **argv) 8.142 flags = atoi(argv[5]); 8.143 8.144 if (flags & XCFLAGS_HVM) 8.145 - ret = xc_hvm_save(xc_fd, io_fd, domid, maxit, max_f, flags, &suspend); 8.146 + ret = xc_hvm_save(xc_fd, io_fd, domid, maxit, max_f, flags, 8.147 + &suspend, &init_qemu_maps, &qemu_flip_buffer); 8.148 else 8.149 - ret = xc_linux_save(xc_fd, io_fd, domid, maxit, max_f, flags, &suspend); 8.150 + ret = xc_linux_save(xc_fd, io_fd, domid, maxit, max_f, flags, 8.151 + &suspend); 8.152 8.153 xc_interface_close(xc_fd); 8.154