qemu/migration-tcp.c
aliguori c1d3666532 Live migration for Win32 (Hervé Poussineau)
This patch fixes migration so that it works on Win32.  This requires using
socket specific calls since sockets cannot be treated like file descriptors
on win32.

Signed-off-by: Hervé Poussineau <hpoussin@reactos.org>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>



git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5525 c046a42c-6fe2-441c-8c8c-71466251a162
2008-10-24 21:55:17 +00:00

372 lines
7.8 KiB
C

/*
* QEMU live migration
*
* Copyright IBM, Corp. 2008
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.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_TCP
typedef struct FdMigrationState
{
MigrationState mig_state;
QEMUFile *file;
int64_t bandwidth_limit;
int fd;
int detach;
int state;
} FdMigrationState;
#ifdef DEBUG_MIGRATION_TCP
#define dprintf(fmt, ...) \
do { printf("migration-tcp: " fmt, ## __VA_ARGS__); } while (0)
#else
#define dprintf(fmt, ...) \
do { } while (0)
#endif
int debug_me = 0;
static void tcp_cleanup(FdMigrationState *s)
{
if (s->detach == 2) {
monitor_resume();
s->detach = 0;
}
qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
if (s->file) {
debug_me = 1;
dprintf("closing file\n");
qemu_fclose(s->file);
}
if (s->fd != -1)
close(s->fd);
s->fd = -1;
}
static void tcp_error(FdMigrationState *s)
{
dprintf("setting error state\n");
s->state = MIG_STATE_ERROR;
tcp_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 = send(s->fd, data, size, 0);
} while (ret == -1 && (socket_error() == EINTR || socket_error() == EWOULDBLOCK));
if (ret == -1)
ret = -socket_error();
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->fd != -1) {
close(s->fd);
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 && socket_error() == 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;
tcp_cleanup(s);
}
}
static void tcp_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);
tcp_error(s);
return;
}
fd_put_ready(s);
}
static void tcp_wait_for_connect(void *opaque)
{
FdMigrationState *s = opaque;
int val, ret;
int valsize = sizeof(val);
dprintf("connect completed\n");
do {
ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &val, &valsize);
} while (ret == -1 && socket_error() == EINTR);
if (ret < 0) {
tcp_error(s);
return;
}
qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
if (val == 0)
tcp_connect_migrate(s);
else {
dprintf("error connecting %d\n", val);
tcp_error(s);
}
}
static FdMigrationState *to_fms(MigrationState *mig_state)
{
return container_of(mig_state, FdMigrationState, mig_state);
}
static int tcp_get_status(MigrationState *mig_state)
{
FdMigrationState *s = to_fms(mig_state);
return s->state;
}
static void tcp_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;
tcp_cleanup(s);
}
static void tcp_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;
tcp_cleanup(s);
}
free(s);
}
MigrationState *tcp_start_outgoing_migration(const char *host_port,
int64_t bandwidth_limit,
int async)
{
struct sockaddr_in addr;
FdMigrationState *s;
int ret;
if (parse_host_port(&addr, host_port) < 0)
return NULL;
s = qemu_mallocz(sizeof(*s));
if (s == NULL)
return NULL;
s->mig_state.cancel = tcp_cancel;
s->mig_state.get_status = tcp_get_status;
s->mig_state.release = tcp_release;
s->state = MIG_STATE_ACTIVE;
s->detach = !async;
s->bandwidth_limit = bandwidth_limit;
s->fd = socket(PF_INET, SOCK_STREAM, 0);
if (s->fd == -1) {
qemu_free(s);
return NULL;
}
socket_set_nonblock(s->fd);
if (s->detach == 1) {
dprintf("detaching from monitor\n");
monitor_suspend();
s->detach = 2;
}
do {
ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr));
if (ret == -1)
ret = -socket_error();
if (ret == -EINPROGRESS || ret == -EWOULDBLOCK)
qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s);
} while (ret == -EINTR);
if (ret < 0 && ret != -EINPROGRESS && ret != -EWOULDBLOCK) {
dprintf("connect failed\n");
close(s->fd);
qemu_free(s);
s = NULL;
} else if (ret >= 0)
tcp_connect_migrate(s);
return &s->mig_state;
}
static void tcp_accept_incoming_migration(void *opaque)
{
struct sockaddr_in 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;
}
vm_stop(0); /* just in case */
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);
vm_start();
out_fopen:
qemu_fclose(f);
out:
close(c);
}
int tcp_start_incoming_migration(const char *host_port)
{
struct sockaddr_in addr;
int val;
int s;
if (parse_host_port(&addr, host_port) < 0) {
fprintf(stderr, "invalid host/port combination: %s\n", host_port);
return -EINVAL;
}
s = socket(PF_INET, SOCK_STREAM, 0);
if (s == -1)
return -socket_error();
val = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
goto err;
if (listen(s, 1) == -1)
goto err;
qemu_set_fd_handler2(s, NULL, tcp_accept_incoming_migration, NULL,
(void *)(unsigned long)s);
return 0;
err:
close(s);
return -socket_error();
}