All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.