From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1MaS2z-0005oe-9j for qemu-devel@nongnu.org; Mon, 10 Aug 2009 06:24:09 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1MaS2u-0005k8-3g for qemu-devel@nongnu.org; Mon, 10 Aug 2009 06:24:08 -0400 Received: from [199.232.76.173] (port=44574 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MaS2t-0005jt-Qa for qemu-devel@nongnu.org; Mon, 10 Aug 2009 06:24:03 -0400 Received: from mx2.redhat.com ([66.187.237.31]:55038) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1MaS2t-00077Y-4b for qemu-devel@nongnu.org; Mon, 10 Aug 2009 06:24:03 -0400 Message-ID: <4A7FF53F.2000106@redhat.com> Date: Mon, 10 Aug 2009 12:23:59 +0200 From: Chris Lalancette MIME-Version: 1.0 Subject: Re: [Qemu-devel] [PATCH] Migration via unix sockets. References: <1249485869-23590-1-git-send-email-clalance@redhat.com> In-Reply-To: <1249485869-23590-1-git-send-email-clalance@redhat.com> Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Anthony Liguori Cc: qemu-devel@nongnu.org Chris Lalancette wrote: > Implement migration via unix sockets. While you can fake this using > exec and netcat, this involves forking another process and is > generally not very nice. By doing this directly in qemu, we can avoid > the copy through the external nc command. This is useful for > implementations (such as libvirt) that want to do "secure" migration; > we pipe the data on the sending side into the unix socket, libvirt > picks it up, encrypts it, and transports it, and then on the remote > side libvirt decrypts it, dumps it to another unix socket, and > feeds it into qemu. > > The implementation is straightforward and looks very similar to > migration-exec.c and migration-tcp.c ping? > > Signed-off-by: Chris Lalancette > --- > Makefile | 2 +- > migration-unix.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > migration.c | 4 + > migration.h | 6 ++ > 4 files changed, 227 insertions(+), 1 deletions(-) > create mode 100644 migration-unix.c > > diff --git a/Makefile b/Makefile > index d3f999e..c5763b7 100644 > --- a/Makefile > +++ b/Makefile > @@ -110,7 +110,7 @@ obj-$(CONFIG_BRLAPI) += baum.o > LIBS+=$(BRLAPI_LIBS) > > obj-$(CONFIG_WIN32) += tap-win32.o > -obj-$(CONFIG_POSIX) += migration-exec.o > +obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o > > ifdef CONFIG_COREAUDIO > AUDIO_PT = y > diff --git a/migration-unix.c b/migration-unix.c > new file mode 100644 > index 0000000..a26587a > --- /dev/null > +++ b/migration-unix.c > @@ -0,0 +1,216 @@ > +/* > + * QEMU live migration via Unix Domain Sockets > + * > + * Copyright Red Hat, Inc. 2009 > + * > + * Authors: > + * Chris Lalancette > + * > + * This work is licensed under the terms of the GNU GPL, version 2. See > + * the COPYING file in the top-level directory. > + * > + */ > + > +#include "qemu-common.h" > +#include "qemu_socket.h" > +#include "migration.h" > +#include "qemu-char.h" > +#include "sysemu.h" > +#include "buffered_file.h" > +#include "block.h" > + > +//#define DEBUG_MIGRATION_UNIX > + > +#ifdef DEBUG_MIGRATION_UNIX > +#define dprintf(fmt, ...) \ > + do { printf("migration-unix: " fmt, ## __VA_ARGS__); } while (0) > +#else > +#define dprintf(fmt, ...) \ > + do { } while (0) > +#endif > + > +static int unix_errno(FdMigrationState *s) > +{ > + return errno; > +} > + > +static int unix_write(FdMigrationState *s, const void * buf, size_t size) > +{ > + return write(s->fd, buf, size); > +} > + > +static int unix_close(FdMigrationState *s) > +{ > + dprintf("unix_close\n"); > + if (s->fd != -1) { > + close(s->fd); > + s->fd = -1; > + } > + return 0; > +} > + > +static void unix_wait_for_connect(void *opaque) > +{ > + FdMigrationState *s = opaque; > + int val, ret; > + socklen_t valsize = sizeof(val); > + > + dprintf("connect completed\n"); > + do { > + ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize); > + } while (ret == -1 && (s->get_error(s)) == EINTR); > + > + if (ret < 0) { > + migrate_fd_error(s); > + return; > + } > + > + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); > + > + if (val == 0) > + migrate_fd_connect(s); > + else { > + dprintf("error connecting %d\n", val); > + migrate_fd_error(s); > + } > +} > + > +MigrationState *unix_start_outgoing_migration(const char *path, > + int64_t bandwidth_limit, > + int detach) > +{ > + FdMigrationState *s; > + struct sockaddr_un addr; > + int ret; > + > + addr.sun_family = AF_UNIX; > + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path); > + > + s = qemu_mallocz(sizeof(*s)); > + > + s->get_error = unix_errno; > + s->write = unix_write; > + s->close = unix_close; > + s->mig_state.cancel = migrate_fd_cancel; > + s->mig_state.get_status = migrate_fd_get_status; > + s->mig_state.release = migrate_fd_release; > + > + s->state = MIG_STATE_ACTIVE; > + s->mon_resume = NULL; > + s->bandwidth_limit = bandwidth_limit; > + s->fd = socket(PF_UNIX, SOCK_STREAM, 0); > + if (s->fd < 0) { > + dprintf("Unable to open socket"); > + goto err_after_alloc; > + } > + > + socket_set_nonblock(s->fd); > + > + if (!detach) > + migrate_fd_monitor_suspend(s); > + > + do { > + ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)); > + if (ret == -1) > + ret = -(s->get_error(s)); > + > + if (ret == -EINPROGRESS || ret == -EWOULDBLOCK) > + qemu_set_fd_handler2(s->fd, NULL, NULL, unix_wait_for_connect, s); > + } while (ret == -EINTR); > + > + if (ret < 0 && ret != -EINPROGRESS && ret != -EWOULDBLOCK) { > + dprintf("connect failed\n"); > + goto err_after_open; > + } else if (ret >= 0) > + migrate_fd_connect(s); > + > + return &s->mig_state; > + > +err_after_open: > + close(s->fd); > + > +err_after_alloc: > + qemu_free(s); > + return NULL; > +} > + > +static void unix_accept_incoming_migration(void *opaque) > +{ > + struct sockaddr_un addr; > + socklen_t addrlen = sizeof(addr); > + int s = (unsigned long)opaque; > + QEMUFile *f; > + int c, ret; > + > + do { > + c = accept(s, (struct sockaddr *)&addr, &addrlen); > + } while (c == -1 && socket_error() == EINTR); > + > + dprintf("accepted migration\n"); > + > + if (c == -1) { > + fprintf(stderr, "could not accept migration connection\n"); > + return; > + } > + > + f = qemu_fopen_socket(c); > + if (f == NULL) { > + fprintf(stderr, "could not qemu_fopen socket\n"); > + goto out; > + } > + > + ret = qemu_loadvm_state(f); > + if (ret < 0) { > + fprintf(stderr, "load of migration failed\n"); > + goto out_fopen; > + } > + qemu_announce_self(); > + dprintf("successfully loaded vm state\n"); > + > + /* we've successfully migrated, close the server socket */ > + qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL); > + close(s); > + > +out_fopen: > + qemu_fclose(f); > +out: > + close(c); > +} > + > +int unix_start_incoming_migration(const char *path) > +{ > + struct sockaddr_un un; > + int sock; > + > + dprintf("Attempting to start an incoming migration\n"); > + > + sock = socket(PF_UNIX, SOCK_STREAM, 0); > + if (sock < 0) { > + fprintf(stderr, "Could not open unix socket: %s\n", strerror(errno)); > + return -EINVAL; > + } > + > + memset(&un, 0, sizeof(un)); > + un.sun_family = AF_UNIX; > + snprintf(un.sun_path, sizeof(un.sun_path), "%s", path); > + > + unlink(un.sun_path); > + if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) { > + fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno)); > + goto err; > + } > + if (listen(sock, 1) < 0) { > + fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno)); > + goto err; > + } > + > + qemu_set_fd_handler2(sock, NULL, unix_accept_incoming_migration, NULL, > + (void *)(unsigned long)sock); > + > + return 0; > + > +err: > + close(sock); > + > + return -EINVAL; > +} > diff --git a/migration.c b/migration.c > index ee64d41..34e2bc1 100644 > --- a/migration.c > +++ b/migration.c > @@ -43,6 +43,8 @@ void qemu_start_incoming_migration(const char *uri) > #if !defined(WIN32) > else if (strstart(uri, "exec:", &p)) > exec_start_incoming_migration(p); > + else if (strstart(uri, "unix:", &p)) > + unix_start_incoming_migration(p); > #endif > else > fprintf(stderr, "unknown migration protocol: %s\n", uri); > @@ -58,6 +60,8 @@ void do_migrate(Monitor *mon, int detach, const char *uri) > #if !defined(WIN32) > else if (strstart(uri, "exec:", &p)) > s = exec_start_outgoing_migration(p, max_throttle, detach); > + else if (strstart(uri, "unix:", &p)) > + s = unix_start_outgoing_migration(p, max_throttle, detach); > #endif > else > monitor_printf(mon, "unknown migration protocol: %s\n", uri); > diff --git a/migration.h b/migration.h > index 37c7f8e..0ed1fcb 100644 > --- a/migration.h > +++ b/migration.h > @@ -73,6 +73,12 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port, > int64_t bandwidth_limit, > int detach); > > +int unix_start_incoming_migration(const char *path); > + > +MigrationState *unix_start_outgoing_migration(const char *path, > + int64_t bandwidth_limit, > + int detach); > + > void migrate_fd_monitor_suspend(FdMigrationState *s); > > void migrate_fd_error(FdMigrationState *s); -- Chris Lalancette