migration-tcp.c 4.74 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
 * 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 "buffered_file.h"
#include "block.h"

//#define DEBUG_MIGRATION_TCP

#ifdef DEBUG_MIGRATION_TCP
#define dprintf(fmt, ...) \
    do { printf("migration-tcp: " fmt, ## __VA_ARGS__); } while (0)
#else
#define dprintf(fmt, ...) \
    do { } while (0)
#endif

32
static int socket_errno(FdMigrationState *s)
33
{
34
    return socket_error();
35 36
}

37
static int socket_write(FdMigrationState *s, const void * buf, size_t size)
38
{
39
    return send(s->fd, buf, size, 0);
40 41
}

42
static int tcp_close(FdMigrationState *s)
43
{
44
    dprintf("tcp_close\n");
45
    if (s->fd != -1) {
aliguori's avatar
aliguori committed
46 47
        close(s->fd);
        s->fd = -1;
48 49 50 51 52 53 54 55 56
    }
    return 0;
}


static void tcp_wait_for_connect(void *opaque)
{
    FdMigrationState *s = opaque;
    int val, ret;
blueswir1's avatar
blueswir1 committed
57
    socklen_t valsize = sizeof(val);
58 59 60 61

    dprintf("connect completed\n");
    do {
        ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &val, &valsize);
62
    } while (ret == -1 && (s->get_error(s)) == EINTR);
63 64

    if (ret < 0) {
65
        migrate_fd_error(s);
66 67 68 69 70 71
        return;
    }

    qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);

    if (val == 0)
72
        migrate_fd_connect(s);
73 74
    else {
        dprintf("error connecting %d\n", val);
75
        migrate_fd_error(s);
76 77 78 79
    }
}

MigrationState *tcp_start_outgoing_migration(const char *host_port,
aliguori's avatar
aliguori committed
80
                                             int64_t bandwidth_limit,
81
                                             int detach)
82 83 84 85 86 87 88 89 90 91
{
    struct sockaddr_in addr;
    FdMigrationState *s;
    int ret;

    if (parse_host_port(&addr, host_port) < 0)
        return NULL;

    s = qemu_mallocz(sizeof(*s));

92 93 94 95 96 97
    s->get_error = socket_errno;
    s->write = socket_write;
    s->close = tcp_close;
    s->mig_state.cancel = migrate_fd_cancel;
    s->mig_state.get_status = migrate_fd_get_status;
    s->mig_state.release = migrate_fd_release;
98 99

    s->state = MIG_STATE_ACTIVE;
100
    s->mon_resume = NULL;
101 102 103 104
    s->bandwidth_limit = bandwidth_limit;
    s->fd = socket(PF_INET, SOCK_STREAM, 0);
    if (s->fd == -1) {
        qemu_free(s);
aliguori's avatar
aliguori committed
105
        return NULL;
106 107
    }

108
    socket_set_nonblock(s->fd);
109

110 111
    if (!detach)
        migrate_fd_monitor_suspend(s);
112 113 114 115

    do {
        ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr));
        if (ret == -1)
116
            ret = -(s->get_error(s));
117

118
        if (ret == -EINPROGRESS || ret == -EWOULDBLOCK)
119 120 121
            qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s);
    } while (ret == -EINTR);

122
    if (ret < 0 && ret != -EINPROGRESS && ret != -EWOULDBLOCK) {
123 124 125
        dprintf("connect failed\n");
        close(s->fd);
        qemu_free(s);
126
        return NULL;
127
    } else if (ret >= 0)
128
        migrate_fd_connect(s);
129 130 131 132 133 134 135 136 137 138 139 140 141 142

    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);
143
    } while (c == -1 && socket_error() == EINTR);
144 145 146 147 148 149 150 151

    dprintf("accepted migration\n");

    if (c == -1) {
        fprintf(stderr, "could not accept migration connection\n");
        return;
    }

152
    f = qemu_fopen_socket(c);
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
    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)
192
        return -socket_error();
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209

    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);
210
    return -socket_error();
211
}