aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvi Kivity <avi@qumranet.com>2006-11-28 15:43:58 +0000
committerAvi Kivity <avi@qumranet.com>2006-11-28 15:43:58 +0000
commit8f5d9698ecdfb153e45fafd30154f41fe61320d1 (patch)
treeeae33db8924dd23b01c4f6cbf13bbe4378ee826d
parent0d628811f1a614222b43e115499abe17ce011941 (diff)
kvm: release: merge from trunkkvm-5
- qemu migration work - administratrivia
-rw-r--r--Makefile.target1
-rw-r--r--kvm/Makefile14
-rw-r--r--kvm/kernel/Makefile13
-rwxr-xr-xkvm/kvm16
-rw-r--r--migration.c551
-rw-r--r--migration.h14
-rw-r--r--monitor.c63
-rw-r--r--vl.c137
-rw-r--r--vl.h22
9 files changed, 771 insertions, 60 deletions
diff --git a/Makefile.target b/Makefile.target
index 3eccd227b..51fed35f1 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -295,6 +295,7 @@ endif
# must use static linking to avoid leaving stuff in virtual address space
VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o loader.o
VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o
+VL_OBJS+=migration.o
ifdef CONFIG_WIN32
VL_OBJS+=tap-win32.o
endif
diff --git a/kvm/Makefile b/kvm/Makefile
index 07a1111b9..07d555099 100644
--- a/kvm/Makefile
+++ b/kvm/Makefile
@@ -9,16 +9,13 @@ rpmrelease = devel
all: $(if $(WANT_MODULE), kernel) user qemu
+kcmd = $(if $(WANT_MODULE),,@\#)
+
qemu kernel user:
$(MAKE) -C $@
qemu: user
-clean:
- @for d in kernel user qemu; do \
- $(MAKE) -C $$d $@; \
- done
-
bindir = /usr/bin
bin = $(bindir)/kvm
initdir = /etc/init.d
@@ -36,19 +33,23 @@ install-rpm:
cp kvm $(DESTDIR)/$(utilsdir)/kvm
install:
+ $(kcmd)make -C kernel DESTDIR="$(DESTDIR)" install
make -C user DESTDIR="$(DESTDIR)" install
make -C qemu DESTDIR="$(DESTDIR)" install
tmpspec = .tmp.kvm.spec
+RPMDIR=$$(pwd)/RPMS
rpm: user qemu
- mkdir -p BUILD RPMS/$$(uname -i)
+ mkdir -p $(RPMDIR)/$$(uname -i)
sed 's/^Release:.*/Release: $(rpmrelease)/' kvm.spec > $(tmpspec)
rpmbuild --define="kverrel $$(uname -r)" \
--define="objdir $$(pwd)" \
+ --define="_rpmdir $(RPMDIR)" \
--define="_topdir $$(pwd)" \
--define="prebuilt 1" \
-bb $(tmpspec)
+ $(RM) $(tmpspec)
srpm:
mkdir -p SOURCES SRPMS
@@ -59,6 +60,7 @@ srpm:
tar czf SOURCES/scripts.tar.gz scripts
cp Makefile SOURCES
rpmbuild --define="_topdir $$(pwd)" -bs $(tmpspec)
+ $(RM) $(tmpspec)
clean:
for i in $(if $(WANT_MODULE), kernel) user qemu; do \
diff --git a/kvm/kernel/Makefile b/kvm/kernel/Makefile
index 1f2d19296..f40e39761 100644
--- a/kvm/kernel/Makefile
+++ b/kvm/kernel/Makefile
@@ -1,18 +1,27 @@
KERNELDIR := /lib/modules/$(shell uname -r)/build
KVERREL = $(patsubst /lib/modules/%/build,%,$(KERNELDIR))
+DESTDIR=
+
+INSTALLDIR = $(patsubst %/build,%/extra,$(KERNELDIR))
+
rpmrelease = devel
all::
$(MAKE) -C $(KERNELDIR) M=`pwd` "$$@"
-tmpspec = .tmp.kvm-kmod.spec
+install:
+ cp *.ko $(DESTDIR)/$(INSTALLDIR)
+ depmod -a
+tmpspec = .tmp.kvm-kmod.spec
+RPMDIR = $$(pwd)/../RPMS
rpm: all
- mkdir -p ../BUILD ../RPMS/$$(uname -m)
+ mkdir -p ../BUILD $(RPMDIR)/$$(uname -m)
sed 's/^Release:.*/Release: $(rpmrelease)/' kvm-kmod.spec > $(tmpspec)
rpmbuild --define="kverrel $(KVERREL)" \
--define="objdir $$(pwd)" \
+ --define="_rpmdir $(RPMDIR)" \
--define="_topdir $$(pwd)/.." \
-bb $(tmpspec)
diff --git a/kvm/kvm b/kvm/kvm
index 73c351525..b5e315309 100755
--- a/kvm/kvm
+++ b/kvm/kvm
@@ -1,7 +1,7 @@
#!/usr/bin/python
import sys, os, time, re
-import optparse
+import optparse, commands
optparser = optparse.OptionParser()
@@ -140,7 +140,19 @@ if options.debugger:
qemu_args += ('-s',)
if not options.notap:
- qemu_args += ('-net', 'nic' ,'-net', 'tap,script=/etc/kvm/qemu-ifup',)
+ mac = None
+ for line in commands.getoutput('ip link show eth0').splitlines():
+ m = re.match(r'.*link/ether (..:..:..:..:..:..).*', line)
+ if m:
+ mac = m.group(1)
+ if not mac:
+ raise Exception, 'Unable to determine eth0 mac address'
+ mac_components = mac.split(':')
+ mac_components[0] = 'a0'
+ mac = ':'.join(mac_components)
+
+ qemu_args += ('-net', 'nic,macaddr=%s' % (mac,),
+ '-net', 'tap,script=/etc/kvm/qemu-ifup',)
if options.vnc is not None:
qemu_args += ('-vnc', str(options.vnc))
diff --git a/migration.c b/migration.c
new file mode 100644
index 000000000..82c9d670b
--- /dev/null
+++ b/migration.c
@@ -0,0 +1,551 @@
+#include "vl.h"
+#include "qemu_socket.h"
+#include "migration.h"
+
+#define TO_BE_IMPLEMENTED term_printf("%s: TO_BE_IMPLEMENTED\n", __FUNCTION__)
+
+#ifndef CONFIG_USER_ONLY
+
+/* defined in vl.c */
+int parse_host_port(struct sockaddr_in *saddr, const char *str);
+
+#define FD_UNUSED -1
+
+typedef enum {
+ NONE = 0,
+ WRITER = 1,
+ READER = 2
+} migration_role_t;
+
+typedef struct migration_state {
+ int fd;
+#define BUFFSIZE ( 256 * 1024)
+ unsigned char buff[BUFFSIZE]; /* FIXME: allocate dynamically; use mutli/double buffer */
+ unsigned buffsize;
+ unsigned head, tail;
+ migration_role_t role;
+ int64_t head_counter, tail_counter;
+} migration_state_t;
+
+static migration_state_t ms = {
+ .fd = FD_UNUSED,
+ .buff = { 0 },
+ .buffsize = BUFFSIZE,
+ .head = 0,
+ .tail = 0,
+ .head_counter = 0,
+ .tail_counter = 0
+};
+
+static const char *reader_default_addr="localhost:4455";
+static const char *writer_default_addr="localhost:4456";
+
+/* circular buffer functions */
+static int migration_buffer_empty(migration_state_t *pms)
+{
+ return (pms->head == pms->tail);
+}
+
+static int migration_buffer_bytes_filled(migration_state_t *pms)
+{
+ return (pms->head - pms->tail) % pms->buffsize;
+}
+
+static int migration_buffer_bytes_empty(migration_state_t *pms)
+{
+ return (pms->tail - pms->head -1) % pms->buffsize;
+}
+
+static int migration_buffer_bytes_head_end(migration_state_t *pms)
+{
+ return pms->buffsize - pms->head;
+}
+
+static int migration_buffer_bytes_tail_end(migration_state_t *pms)
+{
+ return pms->buffsize - pms->tail;
+}
+
+static void migration_state_inc_head(migration_state_t *pms, int n)
+{
+ pms->head = (pms->head + n) % pms->buffsize;
+ pms->head_counter += n;
+}
+
+static void migration_state_inc_tail(migration_state_t *pms, int n)
+{
+ pms->tail = (pms->tail + n) % pms->buffsize;
+ pms->tail_counter += n;
+}
+
+
+/* create a network address according to arg/default_addr */
+static int parse_host_port_and_message(struct sockaddr_in *saddr,
+ const char *arg,
+ const char *default_addr,
+ const char *name)
+{
+ if (!arg)
+ arg = default_addr;
+ if (parse_host_port(saddr, arg) < 0) {
+ term_printf("%s: invalid argument '%s'", name, arg);
+ return -1;
+ }
+ return 0;
+}
+
+static void migration_cleanup(migration_state_t *pms)
+{
+ if (pms->fd != FD_UNUSED) {
+#ifdef USE_NONBLOCKING_SOCKETS
+ qemu_set_fd_handler(pms->fd, NULL, NULL, NULL);
+#endif
+ close(pms->fd);
+ pms->fd = FD_UNUSED;
+ }
+}
+
+static int migration_read_from_socket(void *opaque)
+{
+ migration_state_t *pms = (migration_state_t *)opaque;
+ int size, toend;
+
+ if (pms->fd == FD_UNUSED) /* not connected */
+ return 0;
+ while (1) { /* breaking if O.K. */
+ size = migration_buffer_bytes_empty(pms); /* available size */
+ toend = migration_buffer_bytes_head_end(pms);
+ if (size > toend) /* read till end of buffer */
+ size = toend;
+ size = read(pms->fd, pms->buff + pms->head, size);
+ if (size < 0) {
+ if (socket_error() == EINTR)
+ continue;
+ term_printf("migration_read_from_socket: read failed (%s)\n", strerror(errno) );
+ return size;
+ }
+ if (size == 0) {
+ /* connection closed */
+ term_printf("migration_read_from_socket: CONNECTION CLOSED\n");
+ migration_cleanup(pms);
+ /* FIXME: call vm_start on A or B according to migration status ? */
+ return size;
+ }
+ else /* we did read something */
+ break;
+ }
+
+ migration_state_inc_head(pms, size);
+ return size;
+}
+
+static int migration_write_into_socket(void *opaque, int len)
+{
+ migration_state_t *pms = (migration_state_t *)opaque;
+ int size, toend;
+
+ if (pms->fd == FD_UNUSED) /* not connected */
+ return 0;
+ while (1) { /* breaking if O.K. */
+ size = migration_buffer_bytes_filled(pms); /* available size */
+ toend = migration_buffer_bytes_tail_end(pms);
+ if (size > toend) /* write till end of buffer */
+ size = toend;
+ if (size > len)
+ size = len;
+ size = write(pms->fd, pms->buff + pms->tail, size);
+ if (size < 0) {
+ if (socket_error() == EINTR)
+ continue;
+ term_printf("migration_write_into_socket: write failed (%s)\n",
+ strerror(socket_error()) );
+ return size;
+ }
+ if (size == 0) {
+ /* connection closed */
+ term_printf("migration_write_into_socket: CONNECTION CLOSED\n");
+ migration_cleanup(pms);
+ return size;
+ }
+ else /* we did write something */
+ break;
+ }
+
+ migration_state_inc_tail(pms, size);
+ return size;
+}
+
+static void migration_accept(void *opaque)
+{
+ migration_state_t *pms = (migration_state_t *)opaque;
+ socklen_t len;
+ struct sockaddr_in sockaddr;
+ int new_fd;
+
+ for(;;) {
+ len = sizeof(sockaddr);
+ new_fd = accept(pms->fd, (struct sockaddr *)&sockaddr, &len);
+ if (new_fd < 0 && errno != EINTR) {
+ term_printf("migration listen: accept failed (%s)\n",
+ strerror(errno));
+ return;
+ } else if (new_fd >= 0) {
+ break;
+ }
+ }
+
+ /* FIXME: Need to be modified if we want to have a control connection
+ * e.g. cancel/abort
+ */
+ migration_cleanup(pms); /* clean old fd */
+ pms->fd = new_fd;
+
+ term_printf("accepted new socket as fd %d\n", pms->fd);
+
+#ifdef USE_NONBLOCKING_SOCKETS
+ /* start handling I/O */
+ qemu_set_fd_handler(pms->fd, migration_read_from_socket, NULL, NULL);
+#else
+ term_printf("waiting for migration to start...\n");
+ do_migration_start("offline");
+#endif
+
+}
+
+
+void do_migration_listen(char *arg1, char *arg2)
+{
+ struct sockaddr_in local, remote;
+ int val;
+
+ if (ms.fd != FD_UNUSED) {
+ term_printf("Already listening or connection established\n");
+ return;
+ }
+
+ ms.role = READER;
+
+ if (parse_host_port_and_message(&local, arg1, reader_default_addr, "migration listen"))
+ return;
+
+ if (parse_host_port_and_message(&remote, arg2, writer_default_addr, "migration listen"))
+ return;
+
+ ms.fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (ms.fd < 0) {
+ term_printf("migration listen: socket() failed (%s)\n",
+ strerror(errno));
+ return;
+ }
+
+ /* fast reuse of address */
+ val = 1;
+ setsockopt(ms.fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
+
+ if (bind(ms.fd, &local, sizeof local) < 0 ) {
+ migration_cleanup(&ms);
+ term_printf("migration listen: bind() failed (%s)\n", strerror(errno));
+ return;
+ }
+
+ if (listen(ms.fd, 1) < 0) { /* allow only one connection */
+ migration_cleanup(&ms);
+ term_printf("migration listen: listen() failed (%s)\n", strerror(errno));
+ return;
+ }
+
+#ifdef USE_NONBLOCKING_SOCKETS
+ /* FIXME: should I allow BLOCKING socket after vm_stop() to get full bandwidth? */
+ socket_set_nonblock(fd); /* do not block and delay the guest */
+
+ qemu_set_fd_handler(fd, migration_accept, NULL, NULL); /* wait for connect() */
+#else
+ migration_accept(&ms);
+#endif
+}
+
+/* reads from the socket if needed
+ * returns 0 if connection closed
+ * >0 if buffer is not empty, number of bytes filled
+ * <0 if error occure
+ */
+static int migration_read_some(void)
+{
+ int size;
+
+ if (migration_buffer_empty(&ms))
+ size = migration_read_from_socket(&ms);
+ else
+ size = migration_buffer_bytes_filled(&ms);
+ return size;
+}
+
+
+/* returns the byte read or 0 on error/connection closed */
+int migration_read_byte(void)
+{
+ int val = 0;
+
+ if (migration_read_some() > 0) {
+ val = ms.buff[ms.tail];
+ migration_state_inc_tail(&ms, 1);
+ }
+ return val;
+}
+
+/* returns >=0 the number of bytes actually read,
+ * or <0 if error occured
+ */
+int migration_read_buffer(char *buff, int len)
+{
+ int size, toend, len_req = len;
+ while (len > 0) {
+ size = migration_read_some();
+ if (size < 0)
+ return size;
+ else if (size==0)
+ break;
+ toend = migration_buffer_bytes_tail_end(&ms);
+ if (size > toend)
+ size = toend;
+ if (size > len)
+ size = len;
+ memcpy(buff, &ms.buff[ms.tail], size);
+ migration_state_inc_tail(&ms, size);
+ len -= size;
+ buff += size;
+ }
+ return len_req - len;
+}
+
+
+
+/*
+ * buffer the bytes, and send when threshold reached
+ * FIXME: bandwidth control can be implemented here
+ * returns 0 on success, <0 on error
+ */
+static int migration_write_some(int force)
+{
+ int size, threshold = 1024;
+
+ if (threshold >= ms.buffsize) /* if buffsize is small */
+ threshold = ms.buffsize / 2;
+ size = migration_buffer_bytes_filled(&ms);
+ while (size && (force || (size > threshold))) {
+ size = migration_write_into_socket(&ms, size);
+ if (size < 0) /* error */
+ return size;
+ if (size == 0) { /* connection closed -- announce ERROR */
+ term_printf("migration: other side closed connection\n");
+ return -1;
+ }
+ size = migration_buffer_bytes_filled(&ms);
+ }
+ return 0;
+}
+
+static int migration_write_byte(int val)
+{
+ int rc;
+
+ rc = migration_write_some(0);
+ if ( rc == 0) {
+ rc = 1;
+ ms.buff[ms.head] = val;
+ migration_state_inc_head(&ms, 1);
+ }
+ return rc;
+}
+
+static int migration_write_buffer(const char *buff, int len)
+{
+ int size, toend, len_req = len;
+
+ while (len > 0) {
+ if (migration_write_some(0) < 0)
+ break;
+ size = migration_buffer_bytes_empty(&ms);
+ toend = migration_buffer_bytes_head_end(&ms);
+ if (size > toend)
+ size = toend;
+ if (size > len)
+ size = len;
+ memcpy(&ms.buff[ms.head], buff, size);
+ migration_state_inc_head(&ms, size);
+ len -= size;
+ buff += size;
+ }
+ return len_req - len;
+}
+
+void do_migration_connect(char *arg1, char *arg2)
+{
+ struct sockaddr_in local, remote;
+
+ if (ms.fd != FD_UNUSED) {
+ term_printf("Already connecting or connection established\n");
+ return;
+ }
+
+ ms.role = WRITER;
+
+ if (parse_host_port_and_message(&local, arg1, writer_default_addr, "migration connect"))
+ return;
+
+ if (parse_host_port_and_message(&remote, arg2, reader_default_addr, "migration connect"))
+ return;
+
+ ms.fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (ms.fd < 0) {
+ term_printf("migration connect: socket() failed (%s)\n",
+ strerror(errno));
+ return;
+ }
+
+ if (connect(ms.fd, (struct sockaddr*)&remote, sizeof remote) < 0) {
+ term_printf("migration connect: connect() failed (%s)\n",
+ strerror(errno));
+ migration_cleanup(&ms);
+ return;
+ }
+
+ term_printf("migration connect: connected through fd %d\n", ms.fd);
+}
+
+
+void do_migration_getfd(int fd) { TO_BE_IMPLEMENTED; }
+void do_migration_start(char *deadoralive)
+{
+ int rc = -1;
+ const char *dummy = "online_migration";
+
+ switch (ms.role) {
+ case WRITER:
+ rc = qemu_savevm(dummy, &qemu_savevm_method_socket);
+ break;
+ case READER:
+ rc = qemu_loadvm(dummy, &qemu_savevm_method_socket);
+ break;
+ default:
+ term_printf("ERROR: unexpected role=%d\n", ms.role);
+ break;
+ }
+ term_printf("migration %s\n", (rc)?"failed":"completed");
+}
+
+void do_migration_cancel(void)
+{
+ migration_cleanup(&ms);
+}
+void do_migration_status(void){ TO_BE_IMPLEMENTED; }
+void do_migration_set(char *fmt, ...){ TO_BE_IMPLEMENTED; }
+void do_migration_show(void){ TO_BE_IMPLEMENTED; }
+
+
+
+/*
+ * =============================================
+ * qemu_savevm_method implementation for sockets
+ * =============================================
+ */
+static int qemu_savevm_method_socket_open(QEMUFile *f, const char *filename,
+ const char *flags)
+{
+ if (ms.fd == FD_UNUSED)
+ return -1;
+ f->opaque = (void*)&ms;
+ return 0;
+}
+
+static void qemu_savevm_method_socket_close(QEMUFile *f)
+{
+ migration_state_t *pms = (migration_state_t*)f->opaque;
+ if (pms->role == WRITER) {
+ migration_write_some(1); /* sync */
+ }
+}
+
+static void qemu_savevm_method_socket_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
+{
+ migration_write_buffer(buf, size);
+}
+
+static void qemu_savevm_method_socket_put_byte(QEMUFile *f, int v)
+{
+ migration_write_byte(v);
+}
+
+static int qemu_savevm_method_socket_get_buffer(QEMUFile *f, uint8_t *buf, int size)
+{
+ return migration_read_buffer(buf, size);
+}
+
+static int qemu_savevm_method_socket_get_byte(QEMUFile *f)
+{
+ return migration_read_byte();
+}
+
+static int64_t qemu_savevm_method_socket_tell(QEMUFile *f)
+{
+ migration_state_t *pms = (migration_state_t*)f->opaque;
+ int64_t cnt=-1;
+ if (pms->role == WRITER)
+ cnt = pms->head_counter;
+ else if (pms->role == READER)
+ cnt = pms->tail_counter;
+ return cnt;
+}
+
+/*
+ * hack alert: written to overcome a weakness of our solution (not yet generic).
+ * READER: read 4 bytes of the actual length (and maybe do something)
+ * WRITER: ignore (or maybe do something)
+ */
+static int64_t qemu_savevm_method_socket_seek(QEMUFile *f, int64_t pos, int whence)
+{
+ migration_state_t *pms = (migration_state_t*)f->opaque;
+ unsigned int record_len;
+
+ if (pms->role == READER) {
+ record_len = qemu_get_be32(f);
+ }
+ return 0;
+}
+
+static int qemu_savevm_method_socket_eof(QEMUFile *f)
+{
+ migration_state_t *pms = (migration_state_t*)f->opaque;
+
+ return (pms->fd == FD_UNUSED);
+}
+
+QEMUFile qemu_savevm_method_socket = {
+ .opaque = NULL,
+ .open = qemu_savevm_method_socket_open,
+ .close = qemu_savevm_method_socket_close,
+ .put_byte = qemu_savevm_method_socket_put_byte,
+ .get_byte = qemu_savevm_method_socket_get_byte,
+ .put_buffer = qemu_savevm_method_socket_put_buffer,
+ .get_buffer = qemu_savevm_method_socket_get_buffer,
+ .tell = qemu_savevm_method_socket_tell,
+ .seek = qemu_savevm_method_socket_seek,
+ .eof = qemu_savevm_method_socket_eof
+};
+
+
+
+
+
+#else /* CONFIG_USER_ONLY is defined */
+
+void do_migration_listen(char *arg1, char *arg2) { TO_BE_IMPLEMENTED; }
+void do_migration_connect(char *arg1, char *arg2) { TO_BE_IMPLEMENTED; }
+void do_migration_getfd(int fd) { TO_BE_IMPLEMENTED; }
+void do_migration_start(char *deadoralive) { TO_BE_IMPLEMENTED; }
+void do_migration_cancel(void){ TO_BE_IMPLEMENTED; }
+void do_migration_status(void){ TO_BE_IMPLEMENTED; }
+void do_migration_set(char *fmt, ...){ TO_BE_IMPLEMENTED; }
+void do_migration_show(void){ TO_BE_IMPLEMENTED; }
+
+#endif /* of CONFIG_USER_ONLY is defined */
diff --git a/migration.h b/migration.h
new file mode 100644
index 000000000..c93662d0a
--- /dev/null
+++ b/migration.h
@@ -0,0 +1,14 @@
+#ifndef QEMU_MIGRATION_H
+#define QEMU_MIGRATION_H
+
+/* migration commands */
+void do_migration_listen(char *arg1, char *arg2);
+void do_migration_connect(char *arg1, char *arg2);
+void do_migration_getfd(int fd);
+void do_migration_start(char *deadoralive);
+void do_migration_cancel(void);
+void do_migration_status(void);
+void do_migration_set(char *fmt, ...);
+void do_migration_show(void);
+
+#endif /* QEMU_MIGRATION_H */
diff --git a/monitor.c b/monitor.c
index 8f4c93a86..285781081 100644
--- a/monitor.c
+++ b/monitor.c
@@ -24,7 +24,7 @@
#include "vl.h"
#include "disas.h"
#include <dirent.h>
-
+#include "migration.h"
//#define DEBUG
//#define DEBUG_COMPLETION
@@ -41,6 +41,7 @@
* 'i' 32 bit integer
* 'l' target long (32 or 64 bit)
* '/' optional gdb-like print format (like "/10x")
+ * 'A' pass the rest of cmdline as one argument (for subcommands).
*
* '?' optional type (for 'F', 's' and 'i')
*
@@ -58,11 +59,15 @@ static CharDriverState *monitor_hd;
static term_cmd_t term_cmds[];
static term_cmd_t info_cmds[];
+static term_cmd_t migration_cmds[];
static char term_outbuf[1024];
static int term_outbuf_index;
static void monitor_start_input(void);
+static void monitor_handle_command(const term_cmd_t *cmds,
+ const char *cmdline);
+
CPUState *mon_cpu = NULL;
@@ -425,13 +430,13 @@ static void do_log(const char *items)
static void do_savevm(const char *filename)
{
- if (qemu_savevm(filename) < 0)
+ if (qemu_savevm(filename, &qemu_savevm_method_file) < 0)
term_printf("I/O error when saving VM to '%s'\n", filename);
}
static void do_loadvm(const char *filename)
{
- if (qemu_loadvm(filename) < 0)
+ if (qemu_loadvm(filename, &qemu_savevm_method_file) < 0)
term_printf("I/O error when loading VM from '%s'\n", filename);
}
@@ -1152,6 +1157,16 @@ static void do_stop_capture (int n)
}
}
+static void do_migration(const char *subcmdline)
+{
+ monitor_handle_command(migration_cmds, subcmdline);
+}
+
+static void do_migration_help(char *name)
+{
+ help_cmd1(migration_cmds, "migration ", name);
+}
+
#ifdef HAS_AUDIO
int wav_start_capture (CaptureState *s, const char *path, int freq,
int bits, int nchannels);
@@ -1246,6 +1261,8 @@ static term_cmd_t term_cmds[] = {
"capture index", "stop capture" },
{ "create_snapshot", "ss?s?s?", do_snapshot,
"hda [hdb] [hdc] [hdd]", "create snapshot of one or more images (VMDK format)" },
+ { "migration", "A", do_migration, "subcommand|help",
+ "start/stop/manage migrations"},
{ NULL, NULL, },
};
@@ -1289,6 +1306,25 @@ static term_cmd_t info_cmds[] = {
{ NULL, NULL, },
};
+
+static term_cmd_t migration_cmds[] = {
+ { "listen", "s?s?", do_migration_listen,
+ "[local_host:port [remote_host:port]]", "listen to a port" },
+ { "connect", "s?s?", do_migration_connect,
+ "[local_host:port [remote_host:port]]", "connect to a port"},
+ { "getfd", "i", do_migration_getfd, "fd (socket)",
+ "get established connection"},
+ { "start", "s", do_migration_start, "online|offline" ,
+ "start the migration proccess"},
+ { "cancel", "", do_migration_cancel, "",
+ "cancel an ongoing migration procces"},
+ { "status", "", do_migration_status, "", "get migration status/progress"},
+ { "set", "A", do_migration_set, "params", "set migration parameters"},
+ { "show", "", do_migration_show, "", "show migration parameters"},
+ { "help", "s?", do_migration_help, "[subcommand]", "show help message"},
+ { NULL, NULL, },
+};
+
/*******************************************************************/
static const char *pch;
@@ -1905,7 +1941,7 @@ static int default_fmt_size = 4;
#define MAX_ARGS 16
-static void monitor_handle_command(const char *cmdline)
+static void monitor_handle_command(const term_cmd_t *cmds, const char *cmdline)
{
const char *p, *pstart, *typestr;
char *q;
@@ -1937,7 +1973,7 @@ static void monitor_handle_command(const char *cmdline)
cmdname[len] = '\0';
/* find the command */
- for(cmd = term_cmds; cmd->name != NULL; cmd++) {
+ for(cmd = cmds; cmd->name != NULL; cmd++) {
if (compare_cmd(cmdname, cmd->name))
goto found;
}
@@ -1952,6 +1988,8 @@ static void monitor_handle_command(const char *cmdline)
typestr = cmd->args_type;
nb_args = 0;
for(;;) {
+ while (isspace(*p)) /* eat whitespaces */
+ p++;
c = *typestr;
if (c == '\0')
break;
@@ -1964,8 +2002,6 @@ static void monitor_handle_command(const char *cmdline)
int ret;
char *str;
- while (isspace(*p))
- p++;
if (*typestr == '?') {
typestr++;
if (*p == '\0') {
@@ -2005,8 +2041,6 @@ static void monitor_handle_command(const char *cmdline)
{
int count, format, size;
- while (isspace(*p))
- p++;
if (*p == '/') {
/* format found */
p++;
@@ -2085,8 +2119,6 @@ static void monitor_handle_command(const char *cmdline)
case 'l':
{
target_long val;
- while (isspace(*p))
- p++;
if (*typestr == '?' || *typestr == '.') {
if (*typestr == '?') {
if (*p == '\0')
@@ -2133,6 +2165,11 @@ static void monitor_handle_command(const char *cmdline)
}
}
break;
+ case 'A':
+ args[nb_args++] = p;
+ while (*p) /* goto end of cmdline */
+ p++;
+ break;
case '-':
{
int has_option;
@@ -2141,8 +2178,6 @@ static void monitor_handle_command(const char *cmdline)
c = *typestr++;
if (c == '\0')
goto bad_type;
- while (isspace(*p))
- p++;
has_option = 0;
if (*p == '-') {
p++;
@@ -2419,7 +2454,7 @@ static void monitor_start_input(void);
static void monitor_handle_command1(void *opaque, const char *cmdline)
{
- monitor_handle_command(cmdline);
+ monitor_handle_command(term_cmds, cmdline);
monitor_start_input();
}
diff --git a/vl.c b/vl.c
index 27dc339c0..5f8bb1de7 100644
--- a/vl.c
+++ b/vl.c
@@ -4093,12 +4093,12 @@ void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque)
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
{
- fwrite(buf, 1, size, f);
+ f->put_buffer(f, buf, size);
}
void qemu_put_byte(QEMUFile *f, int v)
{
- fputc(v, f);
+ f->put_byte(f, v);
}
void qemu_put_be16(QEMUFile *f, unsigned int v)
@@ -4123,17 +4123,12 @@ void qemu_put_be64(QEMUFile *f, uint64_t v)
int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
{
- return fread(buf, 1, size, f);
+ return f->get_buffer(f, buf, size);
}
int qemu_get_byte(QEMUFile *f)
{
- int v;
- v = fgetc(f);
- if (v == EOF)
- return 0;
- else
- return v;
+ return f->get_byte(f);
}
unsigned int qemu_get_be16(QEMUFile *f)
@@ -4164,14 +4159,12 @@ uint64_t qemu_get_be64(QEMUFile *f)
int64_t qemu_ftell(QEMUFile *f)
{
- return ftell(f);
+ return f->tell(f);
}
int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
{
- if (fseek(f, pos, whence) < 0)
- return -1;
- return ftell(f);
+ return f->seek(f, pos, whence);
}
typedef struct SaveStateEntry {
@@ -4217,17 +4210,97 @@ int register_savevm(const char *idstr,
#define QEMU_VM_FILE_MAGIC 0x5145564d
#define QEMU_VM_FILE_VERSION 0x00000001
-int qemu_savevm(const char *filename)
+
+static int qemu_savevm_method_file_open(QEMUFile *f, const char *filename,
+ const char *flags)
+{
+ FILE *fp = fopen(filename, flags);
+ f->opaque = (void*)fp;
+ if (!fp)
+ return -1;
+ return 0;
+}
+
+static void qemu_savevm_method_file_close(QEMUFile *f)
+{
+ FILE *fp = (FILE*)f->opaque;
+ if (fp)
+ fclose(fp);
+}
+
+static void qemu_savevm_method_file_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
+{
+ FILE *fp = (FILE*)f->opaque;
+ fwrite(buf, 1, size, fp);
+}
+
+static void qemu_savevm_method_file_put_byte(QEMUFile *f, int v)
+{
+ FILE *fp = (FILE*)f->opaque;
+ fputc(v, fp);
+}
+
+static int qemu_savevm_method_file_get_buffer(QEMUFile *f, uint8_t *buf, int size)
+{
+ FILE *fp = (FILE*)f->opaque;
+ return fread(buf, 1, size, fp);
+}
+
+static int qemu_savevm_method_file_get_byte(QEMUFile *f)
+{
+ FILE *fp = (FILE*)f->opaque;
+ int v;
+
+ v = fgetc(fp);
+ if (v == EOF)
+ return 0;
+ else
+ return v;
+}
+
+static int64_t qemu_savevm_method_file_tell(QEMUFile *f)
+{
+ FILE *fp = (FILE*)f->opaque;
+ return ftell(fp);
+}
+
+static int64_t qemu_savevm_method_file_seek(QEMUFile *f, int64_t pos, int whence)
+{
+ FILE *fp = (FILE*)f->opaque;
+ if (fseek(fp, pos, whence) < 0)
+ return -1;
+ return ftell(fp);
+}
+
+static int qemu_savevm_method_file_eof(QEMUFile *f)
+{
+ FILE *fp = (FILE*)f->opaque;
+ return feof(fp);
+}
+
+QEMUFile qemu_savevm_method_file = {
+ .opaque = NULL,
+ .open = qemu_savevm_method_file_open,
+ .close = qemu_savevm_method_file_close,
+ .put_byte = qemu_savevm_method_file_put_byte,
+ .get_byte = qemu_savevm_method_file_get_byte,
+ .put_buffer = qemu_savevm_method_file_put_buffer,
+ .get_buffer = qemu_savevm_method_file_get_buffer,
+ .tell = qemu_savevm_method_file_tell,
+ .seek = qemu_savevm_method_file_seek,
+ .eof = qemu_savevm_method_file_eof
+};
+
+
+int qemu_savevm(const char *filename, QEMUFile *f)
{
SaveStateEntry *se;
- QEMUFile *f;
int len, len_pos, cur_pos, saved_vm_running, ret;
saved_vm_running = vm_running;
vm_stop(0);
- f = fopen(filename, "wb");
- if (!f) {
+ if (f->open(f, filename, "wb")) {
ret = -1;
goto the_end;
}
@@ -4245,20 +4318,20 @@ int qemu_savevm(const char *filename)
qemu_put_be32(f, se->version_id);
/* record size: filled later */
- len_pos = ftell(f);
+ len_pos = qemu_ftell(f);
qemu_put_be32(f, 0);
se->save_state(f, se->opaque);
/* fill record size */
- cur_pos = ftell(f);
- len = ftell(f) - len_pos - 4;
- fseek(f, len_pos, SEEK_SET);
+ cur_pos = qemu_ftell(f);
+ len = qemu_ftell(f) - len_pos - 4;
+ qemu_fseek(f, len_pos, SEEK_SET);
qemu_put_be32(f, len);
- fseek(f, cur_pos, SEEK_SET);
+ qemu_fseek(f, cur_pos, SEEK_SET);
}
-
- fclose(f);
+ qemu_put_byte(f, 0); /* len==0 represents end of state */
+ f->close(f);
ret = 0;
the_end:
if (saved_vm_running)
@@ -4278,10 +4351,9 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id)
return NULL;
}
-int qemu_loadvm(const char *filename)
+int qemu_loadvm(const char *filename, QEMUFile *f)
{
SaveStateEntry *se;
- QEMUFile *f;
int len, cur_pos, ret, instance_id, record_len, version_id;
int saved_vm_running;
unsigned int v;
@@ -4290,8 +4362,7 @@ int qemu_loadvm(const char *filename)
saved_vm_running = vm_running;
vm_stop(0);
- f = fopen(filename, "rb");
- if (!f) {
+ if (f->open(f, filename, "rb")) {
ret = -1;
goto the_end;
}
@@ -4302,13 +4373,13 @@ int qemu_loadvm(const char *filename)
v = qemu_get_be32(f);
if (v != QEMU_VM_FILE_VERSION) {
fail:
- fclose(f);
+ f->close(f);
ret = -1;
goto the_end;
}
for(;;) {
len = qemu_get_byte(f);
- if (feof(f))
+ if (f->eof(f) || len==0)
break;
qemu_get_buffer(f, idstr, len);
idstr[len] = '\0';
@@ -4319,7 +4390,7 @@ int qemu_loadvm(const char *filename)
printf("idstr=%s instance=0x%x version=%d len=%d\n",
idstr, instance_id, version_id, record_len);
#endif
- cur_pos = ftell(f);
+ cur_pos = qemu_ftell(f);
se = find_se(idstr, instance_id);
if (!se) {
fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n",
@@ -4334,7 +4405,7 @@ int qemu_loadvm(const char *filename)
/* always seek to exact end of record */
qemu_fseek(f, cur_pos + record_len, SEEK_SET);
}
- fclose(f);
+ f->close(f);
ret = 0;
the_end:
if (saved_vm_running)
@@ -6277,7 +6348,7 @@ int main(int argc, char **argv)
} else
#endif
if (loadvm)
- qemu_loadvm(loadvm);
+ qemu_loadvm(loadvm, &qemu_savevm_method_file);
{
/* XXX: simplify init */
diff --git a/vl.h b/vl.h
index a29aa440c..9f29ac472 100644
--- a/vl.h
+++ b/vl.h
@@ -401,7 +401,23 @@ void cpu_disable_ticks(void);
/* VM Load/Save */
-typedef FILE QEMUFile;
+//typedef FILE QEMUFile;
+typedef struct QEMUFile_s QEMUFile;
+struct QEMUFile_s {
+ void *opaque;
+ int (*open)(QEMUFile *f, const char *filename, const char *flags);
+ void (*close)(QEMUFile *f);
+ void (*put_byte)(QEMUFile *f, int v);
+ int (*get_byte)(QEMUFile *f);
+ void (*put_buffer)(QEMUFile *f, const uint8_t *buf, int size);
+ int (*get_buffer)(QEMUFile *f, uint8_t *buf, int size);
+ int64_t (*tell)(QEMUFile *f);
+ int64_t (*seek)(QEMUFile *f, int64_t pos, int whence);
+ int (*eof)(QEMUFile *f);
+};
+
+extern QEMUFile qemu_savevm_method_file;
+extern QEMUFile qemu_savevm_method_socket;
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
void qemu_put_byte(QEMUFile *f, int v);
@@ -472,8 +488,8 @@ int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence);
typedef void SaveStateHandler(QEMUFile *f, void *opaque);
typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
-int qemu_loadvm(const char *filename);
-int qemu_savevm(const char *filename);
+int qemu_loadvm(const char *filename, QEMUFile *f);
+int qemu_savevm(const char *filename, QEMUFile *f);
int register_savevm(const char *idstr,
int instance_id,
int version_id,