diff options
author | Avi Kivity <avi@qumranet.com> | 2006-11-28 15:43:58 +0000 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2006-11-28 15:43:58 +0000 |
commit | 8f5d9698ecdfb153e45fafd30154f41fe61320d1 (patch) | |
tree | eae33db8924dd23b01c4f6cbf13bbe4378ee826d | |
parent | 0d628811f1a614222b43e115499abe17ce011941 (diff) |
kvm: release: merge from trunkkvm-5
- qemu migration work
- administratrivia
-rw-r--r-- | Makefile.target | 1 | ||||
-rw-r--r-- | kvm/Makefile | 14 | ||||
-rw-r--r-- | kvm/kernel/Makefile | 13 | ||||
-rwxr-xr-x | kvm/kvm | 16 | ||||
-rw-r--r-- | migration.c | 551 | ||||
-rw-r--r-- | migration.h | 14 | ||||
-rw-r--r-- | monitor.c | 63 | ||||
-rw-r--r-- | vl.c | 137 | ||||
-rw-r--r-- | vl.h | 22 |
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) @@ -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 */ @@ -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(); } @@ -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 */ @@ -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, |