qemu/migration-tcp.c
<<
>>
Prefs
   1/*
   2 * QEMU live migration
   3 *
   4 * Copyright IBM, Corp. 2008
   5 *
   6 * Authors:
   7 *  Anthony Liguori   <aliguori@us.ibm.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2.  See
  10 * the COPYING file in the top-level directory.
  11 *
  12 */
  13
  14#include "qemu-common.h"
  15#include "qemu_socket.h"
  16#include "migration.h"
  17#include "qemu-char.h"
  18#include "sysemu.h"
  19#include "buffered_file.h"
  20#include "block.h"
  21
  22//#define DEBUG_MIGRATION_TCP
  23
  24#ifdef DEBUG_MIGRATION_TCP
  25#define dprintf(fmt, ...) \
  26    do { printf("migration-tcp: " fmt, ## __VA_ARGS__); } while (0)
  27#else
  28#define dprintf(fmt, ...) \
  29    do { } while (0)
  30#endif
  31
  32static int socket_errno(FdMigrationState *s)
  33{
  34    return socket_error();
  35}
  36
  37static int socket_write(FdMigrationState *s, const void * buf, size_t size)
  38{
  39    return send(s->fd, buf, size, 0);
  40}
  41
  42static int tcp_close(FdMigrationState *s)
  43{
  44    dprintf("tcp_close\n");
  45    if (s->fd != -1) {
  46        close(s->fd);
  47        s->fd = -1;
  48    }
  49    return 0;
  50}
  51
  52
  53static void tcp_wait_for_connect(void *opaque)
  54{
  55    FdMigrationState *s = opaque;
  56    int val, ret;
  57    socklen_t valsize = sizeof(val);
  58
  59    dprintf("connect completed\n");
  60    do {
  61        ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize);
  62    } while (ret == -1 && (s->get_error(s)) == EINTR);
  63
  64    if (ret < 0) {
  65        migrate_fd_error(s);
  66        return;
  67    }
  68
  69    qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
  70
  71    if (val == 0)
  72        migrate_fd_connect(s);
  73    else {
  74        dprintf("error connecting %d\n", val);
  75        migrate_fd_error(s);
  76    }
  77}
  78
  79MigrationState *tcp_start_outgoing_migration(Monitor *mon,
  80                                             const char *host_port,
  81                                             int64_t bandwidth_limit,
  82                                             int detach,
  83                                             int blk,
  84                                             int inc)
  85{
  86    struct sockaddr_in addr;
  87    FdMigrationState *s;
  88    int ret;
  89
  90    if (parse_host_port(&addr, host_port) < 0)
  91        return NULL;
  92
  93    s = qemu_mallocz(sizeof(*s));
  94
  95    s->get_error = socket_errno;
  96    s->write = socket_write;
  97    s->close = tcp_close;
  98    s->mig_state.cancel = migrate_fd_cancel;
  99    s->mig_state.get_status = migrate_fd_get_status;
 100    s->mig_state.release = migrate_fd_release;
 101
 102    s->mig_state.blk = blk;
 103    s->mig_state.shared = inc;
 104
 105    s->state = MIG_STATE_ACTIVE;
 106    s->mon = NULL;
 107    s->bandwidth_limit = bandwidth_limit;
 108    s->fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
 109    if (s->fd == -1) {
 110        qemu_free(s);
 111        return NULL;
 112    }
 113
 114    socket_set_nonblock(s->fd);
 115
 116    if (!detach) {
 117        migrate_fd_monitor_suspend(s, mon);
 118    }
 119
 120    do {
 121        ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr));
 122        if (ret == -1)
 123            ret = -(s->get_error(s));
 124
 125        if (ret == -EINPROGRESS || ret == -EWOULDBLOCK)
 126            qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s);
 127    } while (ret == -EINTR);
 128
 129    if (ret < 0 && ret != -EINPROGRESS && ret != -EWOULDBLOCK) {
 130        dprintf("connect failed\n");
 131        close(s->fd);
 132        qemu_free(s);
 133        return NULL;
 134    } else if (ret >= 0)
 135        migrate_fd_connect(s);
 136
 137    return &s->mig_state;
 138}
 139
 140static void tcp_accept_incoming_migration(void *opaque)
 141{
 142    struct sockaddr_in addr;
 143    socklen_t addrlen = sizeof(addr);
 144    int s = (unsigned long)opaque;
 145    QEMUFile *f;
 146    int c, ret;
 147
 148    do {
 149        c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen);
 150    } while (c == -1 && socket_error() == EINTR);
 151
 152    dprintf("accepted migration\n");
 153
 154    if (c == -1) {
 155        fprintf(stderr, "could not accept migration connection\n");
 156        return;
 157    }
 158
 159    f = qemu_fopen_socket(c);
 160    if (f == NULL) {
 161        fprintf(stderr, "could not qemu_fopen socket\n");
 162        goto out;
 163    }
 164
 165    ret = qemu_loadvm_state(f);
 166    if (ret < 0) {
 167        fprintf(stderr, "load of migration failed\n");
 168        goto out_fopen;
 169    }
 170    qemu_announce_self();
 171    dprintf("successfully loaded vm state\n");
 172
 173    /* we've successfully migrated, close the server socket */
 174    qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL);
 175    close(s);
 176    if (autostart)
 177        vm_start();
 178
 179out_fopen:
 180    qemu_fclose(f);
 181out:
 182    close(c);
 183}
 184
 185int tcp_start_incoming_migration(const char *host_port)
 186{
 187    struct sockaddr_in addr;
 188    int val;
 189    int s;
 190
 191    if (parse_host_port(&addr, host_port) < 0) {
 192        fprintf(stderr, "invalid host/port combination: %s\n", host_port);
 193        return -EINVAL;
 194    }
 195
 196    s = qemu_socket(PF_INET, SOCK_STREAM, 0);
 197    if (s == -1)
 198        return -socket_error();
 199
 200    val = 1;
 201    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
 202
 203    if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
 204        goto err;
 205
 206    if (listen(s, 1) == -1)
 207        goto err;
 208
 209    qemu_set_fd_handler2(s, NULL, tcp_accept_incoming_migration, NULL,
 210                         (void *)(unsigned long)s);
 211
 212    return 0;
 213
 214err:
 215    close(s);
 216    return -socket_error();
 217}
 218