qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Charles Duffy <Charles_Duffy@messageone.com>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PATCH] Re: Live migration - exec: support to be reintroduced?
Date: Thu, 06 Nov 2008 19:54:59 -0600	[thread overview]
Message-ID: <gf075k$ljh$1@ger.gmane.org> (raw)
In-Reply-To: <49113157.3090101@codemonkey.ws>

[-- Attachment #1: Type: text/plain, Size: 660 bytes --]

Anthony Liguori wrote:
> Charles Duffy wrote:
>> KVM's live migration support used to support an exec: protoco, 
>> allowing  either a completely arbitrary transport or a mechanism for 
>> storing system state to a separate file for later resurrection, being 
>> used in the latter by libvirt's qemu driver. I notice that this 
>> support no longer exists in the current codebase.
>> Would a patch reimplementing this support be welcome?
> 
> Absolutely.

Please see attached patch.

I'm new to the codebase (and a bit rusty on my C), so I expect some 
rework to be needed on this one; let me know what needs doing, and I'll 
try to update it as time permits.

[-- Attachment #2: qemu-live_migration_exec_support-r1.patch --]
[-- Type: text/x-patch, Size: 11185 bytes --]

diff --git a/Makefile.target b/Makefile.target
index 031ab45..d844d9c 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -596,7 +596,7 @@ endif
 ifdef CONFIG_WIN32
 OBJS+=block-raw-win32.o
 else
-OBJS+=block-raw-posix.o
+OBJS+=block-raw-posix.o migration-exec.o
 endif
 
 LIBS+=-lz
diff --git a/hw/hw.h b/hw/hw.h
index 99d4b8d..eab7bb4 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -35,6 +35,8 @@ QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer,
                          QEMUFileRateLimit *rate_limit);
 QEMUFile *qemu_fopen(const char *filename, const char *mode);
 QEMUFile *qemu_fopen_socket(int fd);
+QEMUFile *qemu_popen(FILE *popen_file, const char *mode);
+QEMUFile *qemu_popen_cmd(const char *command, const char *mode);
 void qemu_fflush(QEMUFile *f);
 int qemu_fclose(QEMUFile *f);
 void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
diff --git a/migration-exec.c b/migration-exec.c
new file mode 100644
index 0000000..2d04008
--- /dev/null
+++ b/migration-exec.c
@@ -0,0 +1,295 @@
+/*
+ * QEMU live migration
+ *
+ * Copyright IBM, Corp. 2008
+ * Copyright Dell MessageOne 2008
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Charles Duffy     <charles_duffy@messageone.com>
+ *
+ * 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 "console.h"
+#include "buffered_file.h"
+#include "block.h"
+
+//#define DEBUG_MIGRATION_EXEC
+
+typedef struct FdMigrationState
+{
+    MigrationState mig_state;
+    QEMUFile *file, *popen_file;
+    int64_t bandwidth_limit;
+    int fd;
+    int detach;
+    int state;
+} FdMigrationState;
+
+#ifdef DEBUG_MIGRATION_EXEC
+#define dprintf(fmt, ...) \
+    do { printf("migration-exec: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+    do { } while (0)
+#endif
+
+static void exec_cleanup(FdMigrationState *s)
+{
+    qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+
+    if (s->file) {
+        dprintf("closing file\n");
+        qemu_fclose(s->file);
+    }
+
+    if (s->fd != -1)
+        close(s->fd);
+
+    /* Don't resume monitor until we've flushed all of the buffers */
+    if (s->detach == 2) {
+        monitor_resume();
+        s->detach = 0;
+    }
+
+    s->fd = -1;
+}
+
+static void exec_error(FdMigrationState *s)
+{
+    dprintf("setting error state\n");
+    s->state = MIG_STATE_ERROR;
+    exec_cleanup(s);
+}
+
+static void fd_put_notify(void *opaque)
+{
+    FdMigrationState *s = opaque;
+
+    qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+    qemu_file_put_notify(s->file);
+}
+
+static ssize_t fd_put_buffer(void *opaque, const void *data, size_t size)
+{
+    FdMigrationState *s = opaque;
+    ssize_t ret;
+
+    do {
+        ret = write(s->fd, data, size);
+    } while (ret == -1 && (errno == EINTR || errno == EWOULDBLOCK));
+
+    if (ret == -1)
+        ret = -errno;
+
+    if (ret == -EAGAIN)
+        qemu_set_fd_handler2(s->fd, NULL, NULL, fd_put_notify, s);
+
+    return ret;
+}
+
+static int fd_close(void *opaque)
+{
+    FdMigrationState *s = opaque;
+    dprintf("fd_close\n");
+    if (s->popen_file) {
+        qemu_fclose(s->popen_file);
+        s->popen_file = NULL;
+        s->fd = -1;
+    }
+    return 0;
+}
+
+static void fd_wait_for_unfreeze(void *opaque)
+{
+    FdMigrationState *s = opaque;
+    int ret;
+
+    dprintf("wait for unfreeze\n");
+    if (s->state != MIG_STATE_ACTIVE)
+        return;
+
+    do {
+        fd_set wfds;
+
+        FD_ZERO(&wfds);
+        FD_SET(s->fd, &wfds);
+
+        ret = select(s->fd + 1, NULL, &wfds, NULL, NULL);
+    } while (ret == -1 && errno == EINTR);
+}
+
+static void fd_put_ready(void *opaque)
+{
+    FdMigrationState *s = opaque;
+
+    if (s->state != MIG_STATE_ACTIVE) {
+        dprintf("put_ready returning because of non-active state\n");
+        return;
+    }
+
+    dprintf("iterate\n");
+    if (qemu_savevm_state_iterate(s->file) == 1) {
+        dprintf("done iterating\n");
+        vm_stop(0);
+
+        bdrv_flush_all();
+        qemu_savevm_state_complete(s->file);
+        s->state = MIG_STATE_COMPLETED;
+        exec_cleanup(s);
+    }
+}
+
+static void exec_connect_migrate(FdMigrationState *s)
+{
+    int ret;
+
+    s->file = qemu_fopen_ops_buffered(s,
+                                      s->bandwidth_limit,
+                                      fd_put_buffer,
+                                      fd_put_ready,
+                                      fd_wait_for_unfreeze,
+                                      fd_close);
+
+    dprintf("beginning savevm\n");
+    ret = qemu_savevm_state_begin(s->file);
+    if (ret < 0) {
+        dprintf("failed, %d\n", ret);
+        exec_error(s);
+        return;
+    }
+
+    fd_put_ready(s);
+}
+
+static FdMigrationState *to_fms(MigrationState *mig_state)
+{
+    return container_of(mig_state, FdMigrationState, mig_state);
+}
+
+static int exec_get_status(MigrationState *mig_state)
+{
+    FdMigrationState *s = to_fms(mig_state);
+
+    return s->state;
+}
+
+static void exec_cancel(MigrationState *mig_state)
+{
+    FdMigrationState *s = to_fms(mig_state);
+
+    if (s->state != MIG_STATE_ACTIVE)
+        return;
+
+    dprintf("cancelling migration\n");
+
+    s->state = MIG_STATE_CANCELLED;
+
+    exec_cleanup(s);
+}
+
+static void exec_release(MigrationState *mig_state)
+{
+    FdMigrationState *s = to_fms(mig_state);
+
+    dprintf("releasing state\n");
+   
+    if (s->state == MIG_STATE_ACTIVE) {
+        s->state = MIG_STATE_CANCELLED;
+        exec_cleanup(s);
+    }
+    free(s);
+}
+
+MigrationState *exec_start_outgoing_migration(const char *command,
+                                             int64_t bandwidth_limit,
+                                             int async)
+{
+    FdMigrationState *s;
+    FILE *f;
+
+    s = qemu_mallocz(sizeof(*s));
+    if (s == NULL) {
+        dprintf("Unable to allocate FdMigrationState\n");
+        goto err;
+    }
+
+    f = popen(command, "w");
+    if (f == NULL) {
+        dprintf("Unable to popen exec target\n");
+        goto err_after_alloc;
+    }
+
+    s->fd = fileno(f);
+    if (s->fd == -1) {
+        dprintf("Unable to retrieve file descriptor for popen'd handle\n");
+        goto err_after_open;
+    }
+
+    if (fcntl(s->fd, F_SETFD, O_NONBLOCK) == -1) {
+        dprintf("Unable to set nonblocking mode on file descriptor\n");
+        goto err_after_open;
+    }
+
+    s->popen_file = qemu_popen(f, "w");
+
+    s->mig_state.cancel = exec_cancel;
+    s->mig_state.get_status = exec_get_status;
+    s->mig_state.release = exec_release;
+
+    s->state = MIG_STATE_ACTIVE;
+    s->detach = !async;
+    s->bandwidth_limit = bandwidth_limit;
+
+    if (s->detach == 1) {
+        dprintf("detaching from monitor\n");
+        monitor_suspend();
+        s->detach = 2;
+    }
+
+    exec_connect_migrate(s);
+    return &s->mig_state;
+
+err_after_open:
+    pclose(f);
+err_after_alloc:
+    qemu_free(s);
+err:
+    return NULL;
+}
+
+int exec_start_incoming_migration(const char *command)
+{
+    int ret;
+    QEMUFile *f;
+
+    dprintf("Attempting to start an incoming migration\n");
+    f = qemu_popen_cmd(command, "r");
+    if(f == NULL) {
+        dprintf("Unable to apply qemu wrapper to popen file\n");
+        return -errno;
+    }
+    vm_stop(0); /* just in case */
+    ret = qemu_loadvm_state(f);
+    if (ret < 0) {
+        fprintf(stderr, "load of migration failed\n");
+        goto err;
+    }
+    qemu_announce_self();
+    dprintf("successfully loaded vm state\n");
+    vm_start();
+    qemu_fclose(f);
+    return 0;
+
+err:
+    qemu_fclose(f);
+    return -errno;
+}
diff --git a/migration.c b/migration.c
index 9e6c437..fb4d3d3 100644
--- a/migration.c
+++ b/migration.c
@@ -26,6 +26,10 @@ void qemu_start_incoming_migration(const char *uri)
 
     if (strstart(uri, "tcp:", &p))
         tcp_start_incoming_migration(p);
+#if !defined(WIN32)
+    else if (strstart(uri, "exec:", &p))
+        exec_start_incoming_migration(p);
+#endif
     else
         fprintf(stderr, "unknown migration protocol: %s\n", uri);
 }
@@ -37,6 +41,10 @@ void do_migrate(int detach, const char *uri)
 
     if (strstart(uri, "tcp:", &p))
         s = tcp_start_outgoing_migration(p, max_throttle, detach);
+#if !defined(WIN32)
+    else if (strstart(uri, "exec:", &p))
+        s = exec_start_outgoing_migration(p, max_throttle, detach);
+#endif
     else
         term_printf("unknown migration protocol: %s\n", uri);
 
diff --git a/migration.h b/migration.h
index 9947f6a..5008323 100644
--- a/migration.h
+++ b/migration.h
@@ -39,6 +39,12 @@ void do_migrate_set_speed(const char *value);
 
 void do_info_migrate(void);
 
+int exec_start_incoming_migration(const char *host_port);
+
+MigrationState *exec_start_outgoing_migration(const char *host_port,
+					     int64_t bandwidth_limit,
+					     int detach);
+
 int tcp_start_incoming_migration(const char *host_port);
 
 MigrationState *tcp_start_outgoing_migration(const char *host_port,
diff --git a/vl.c b/vl.c
index 31dd3d7..b49e694 100644
--- a/vl.c
+++ b/vl.c
@@ -2816,6 +2816,12 @@ struct QEMUFile {
     int has_error;
 };
 
+typedef struct QEMUFilePopen
+{
+    FILE *popen_file;
+    QEMUFile *file;
+} QEMUFilePopen;
+
 typedef struct QEMUFileSocket
 {
     int fd;
@@ -2844,6 +2850,64 @@ static int socket_close(void *opaque)
     return 0;
 }
 
+static int popen_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
+{
+    QEMUFilePopen *s = opaque;
+    return fwrite(buf, 1, size, s->popen_file);
+}
+
+static int popen_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+    QEMUFilePopen *s = opaque;
+    return fread(buf, 1, size, s->popen_file);
+}
+
+static int popen_close(void *opaque)
+{
+    QEMUFilePopen *s = opaque;
+    pclose(s->popen_file);
+    qemu_free(s);
+    return 0;
+}
+
+QEMUFile *qemu_popen(FILE *popen_file, const char *mode)
+{
+    QEMUFilePopen *s;
+
+    if (popen_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
+        fprintf(stderr, "qemu_popen: Argument validity check failed\n");
+        return NULL;
+    }
+
+    s = qemu_mallocz(sizeof(QEMUFilePopen));
+    if (!s) {
+        fprintf(stderr, "qemu_popen: malloc failed\n");
+        return NULL;
+    }
+
+    s->popen_file = popen_file;
+
+    if(mode[0] == 'r') {
+        s->file = qemu_fopen_ops(s, NULL, popen_get_buffer, popen_close, NULL);
+    } else {
+        s->file = qemu_fopen_ops(s, popen_put_buffer, NULL, popen_close, NULL);
+    }
+    fprintf(stderr, "qemu_popen: returning result of qemu_fopen_ops\n");
+    return s->file;
+}
+
+QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
+{
+    FILE *popen_file;
+
+    popen_file = popen(command, mode);
+    if(popen_file == NULL) {
+        return NULL;
+    }
+
+    return qemu_popen(popen_file, mode);
+}
+
 QEMUFile *qemu_fopen_socket(int fd)
 {
     QEMUFileSocket *s = qemu_mallocz(sizeof(QEMUFileSocket));

  parent reply	other threads:[~2008-11-07  2:27 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-11-05  0:40 [Qemu-devel] Live migration - exec: support to be reintroduced? Charles Duffy
2008-11-05  5:38 ` Anthony Liguori
2008-11-05  7:54   ` Chris Lalancette
2008-11-05 14:09     ` Anthony Liguori
2008-11-05 14:19       ` Daniel P. Berrange
2008-11-05 14:52         ` Jamie Lokier
2008-11-05 15:12         ` Anthony Liguori
2008-11-05 18:10           ` [Qemu-devel] " Charles Duffy
2008-11-05 18:55           ` Charles Duffy
2008-11-05 19:10             ` Anthony Liguori
2008-11-05 10:05   ` [Qemu-devel] " Daniel P. Berrange
2008-11-05 13:03     ` Avi Kivity
2008-11-05 14:14       ` Anthony Liguori
2008-11-05 14:37         ` Daniel P. Berrange
2008-11-05 15:19           ` Anthony Liguori
2008-11-05 17:23             ` Daniel P. Berrange
2008-11-05 17:30               ` Anthony Liguori
2008-11-05 18:13             ` Avi Kivity
2008-11-05 18:10         ` Avi Kivity
2008-11-07  1:54   ` Charles Duffy [this message]
2008-11-07 18:49     ` [Qemu-devel] Re: [PATCH] Re: Live migration - exec: support to be reintroduced? (r2) Charles Duffy
2008-11-11 16:41       ` Charles Duffy
2008-11-11 16:46       ` Anthony Liguori

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='gf075k$ljh$1@ger.gmane.org' \
    --to=charles_duffy@messageone.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).