qemu/migration/socket.c
<<
>>
Prefs
   1/*
   2 * QEMU live migration via socket
   3 *
   4 * Copyright Red Hat, Inc. 2009-2016
   5 *
   6 * Authors:
   7 *  Chris Lalancette <clalance@redhat.com>
   8 *  Daniel P. Berrange <berrange@redhat.com>
   9 *
  10 * This work is licensed under the terms of the GNU GPL, version 2.  See
  11 * the COPYING file in the top-level directory.
  12 *
  13 * Contributions after 2012-01-13 are licensed under the terms of the
  14 * GNU GPL, version 2 or (at your option) any later version.
  15 */
  16
  17#include "qemu/osdep.h"
  18#include "qemu/cutils.h"
  19
  20#include "qemu/error-report.h"
  21#include "qapi/error.h"
  22#include "channel.h"
  23#include "socket.h"
  24#include "migration.h"
  25#include "qemu-file.h"
  26#include "io/channel-socket.h"
  27#include "io/net-listener.h"
  28#include "trace.h"
  29#include "postcopy-ram.h"
  30#include "options.h"
  31#include "qapi/clone-visitor.h"
  32#include "qapi/qapi-visit-sockets.h"
  33
  34struct SocketOutgoingArgs {
  35    SocketAddress *saddr;
  36} outgoing_args;
  37
  38void socket_send_channel_create(QIOTaskFunc f, void *data)
  39{
  40    QIOChannelSocket *sioc = qio_channel_socket_new();
  41    qio_channel_socket_connect_async(sioc, outgoing_args.saddr,
  42                                     f, data, NULL, NULL);
  43}
  44
  45struct SocketConnectData {
  46    MigrationState *s;
  47    char *hostname;
  48};
  49
  50static void socket_connect_data_free(void *opaque)
  51{
  52    struct SocketConnectData *data = opaque;
  53    if (!data) {
  54        return;
  55    }
  56    g_free(data->hostname);
  57    g_free(data);
  58}
  59
  60static void socket_outgoing_migration(QIOTask *task,
  61                                      gpointer opaque)
  62{
  63    struct SocketConnectData *data = opaque;
  64    QIOChannel *sioc = QIO_CHANNEL(qio_task_get_source(task));
  65    Error *err = NULL;
  66
  67    if (qio_task_propagate_error(task, &err)) {
  68        trace_migration_socket_outgoing_error(error_get_pretty(err));
  69           goto out;
  70    }
  71
  72    trace_migration_socket_outgoing_connected(data->hostname);
  73
  74    if (migrate_zero_copy_send() &&
  75        !qio_channel_has_feature(sioc, QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY)) {
  76        error_setg(&err, "Zero copy send feature not detected in host kernel");
  77    }
  78
  79out:
  80    migration_channel_connect(data->s, sioc, data->hostname, err);
  81    object_unref(OBJECT(sioc));
  82}
  83
  84void socket_start_outgoing_migration(MigrationState *s,
  85                                     SocketAddress *saddr,
  86                                     Error **errp)
  87{
  88    QIOChannelSocket *sioc = qio_channel_socket_new();
  89    struct SocketConnectData *data = g_new0(struct SocketConnectData, 1);
  90    SocketAddress *addr = QAPI_CLONE(SocketAddress, saddr);
  91
  92    data->s = s;
  93
  94    /* in case previous migration leaked it */
  95    qapi_free_SocketAddress(outgoing_args.saddr);
  96    outgoing_args.saddr = addr;
  97
  98    if (saddr->type == SOCKET_ADDRESS_TYPE_INET) {
  99        data->hostname = g_strdup(saddr->u.inet.host);
 100    }
 101
 102    qio_channel_set_name(QIO_CHANNEL(sioc), "migration-socket-outgoing");
 103    qio_channel_socket_connect_async(sioc,
 104                                     saddr,
 105                                     socket_outgoing_migration,
 106                                     data,
 107                                     socket_connect_data_free,
 108                                     NULL);
 109}
 110
 111void socket_cleanup_outgoing_migration(void)
 112{
 113    if (outgoing_args.saddr) {
 114        qapi_free_SocketAddress(outgoing_args.saddr);
 115        outgoing_args.saddr = NULL;
 116    }
 117}
 118
 119static void socket_accept_incoming_migration(QIONetListener *listener,
 120                                             QIOChannelSocket *cioc,
 121                                             gpointer opaque)
 122{
 123    trace_migration_socket_incoming_accepted();
 124
 125    if (migration_has_all_channels()) {
 126        error_report("%s: Extra incoming migration connection; ignoring",
 127                     __func__);
 128        return;
 129    }
 130
 131    qio_channel_set_name(QIO_CHANNEL(cioc), "migration-socket-incoming");
 132    migration_channel_process_incoming(QIO_CHANNEL(cioc));
 133}
 134
 135static void
 136socket_incoming_migration_end(void *opaque)
 137{
 138    QIONetListener *listener = opaque;
 139
 140    qio_net_listener_disconnect(listener);
 141    object_unref(OBJECT(listener));
 142}
 143
 144void socket_start_incoming_migration(SocketAddress *saddr,
 145                                     Error **errp)
 146{
 147    QIONetListener *listener = qio_net_listener_new();
 148    MigrationIncomingState *mis = migration_incoming_get_current();
 149    size_t i;
 150    int num = 1;
 151
 152    qio_net_listener_set_name(listener, "migration-socket-listener");
 153
 154    if (migrate_multifd()) {
 155        num = migrate_multifd_channels();
 156    } else if (migrate_postcopy_preempt()) {
 157        num = RAM_CHANNEL_MAX;
 158    }
 159
 160    if (qio_net_listener_open_sync(listener, saddr, num, errp) < 0) {
 161        object_unref(OBJECT(listener));
 162        return;
 163    }
 164
 165    mis->transport_data = listener;
 166    mis->transport_cleanup = socket_incoming_migration_end;
 167
 168    qio_net_listener_set_client_func_full(listener,
 169                                          socket_accept_incoming_migration,
 170                                          NULL, NULL,
 171                                          g_main_context_get_thread_default());
 172
 173    for (i = 0; i < listener->nsioc; i++)  {
 174        SocketAddress *address =
 175            qio_channel_socket_get_local_address(listener->sioc[i], errp);
 176        if (!address) {
 177            return;
 178        }
 179        migrate_add_address(address);
 180        qapi_free_SocketAddress(address);
 181    }
 182}
 183
 184