blob: 1e2935f926b4b6f16bb124d0d032629b0f2330c9 [file] [log] [blame]
/*
* QEMU live migration channel operations
*
* Copyright Red Hat, Inc. 2016
*
* Authors:
* Daniel P. Berrange <berrange@redhat.com>
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "channel.h"
#include "exec.h"
#include "fd.h"
#include "file.h"
#include "io/channel-socket.h"
#include "io/channel-tls.h"
#include "migration.h"
#include "multifd.h"
#include "options.h"
#include "qapi/clone-visitor.h"
#include "qapi/qapi-types-migration.h"
#include "qapi/qapi-visit-migration.h"
#include "qapi/error.h"
#include "qemu-file.h"
#include "qemu/yank.h"
#include "rdma.h"
#include "savevm.h"
#include "socket.h"
#include "tls.h"
#include "trace.h"
#include "yank_functions.h"
void migration_connect_outgoing(MigrationState *s, MigrationAddress *addr,
Error **errp)
{
g_autoptr(QIOChannel) ioc = NULL;
if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) {
SocketAddress *saddr = &addr->u.socket;
if (saddr->type == SOCKET_ADDRESS_TYPE_INET ||
saddr->type == SOCKET_ADDRESS_TYPE_UNIX ||
saddr->type == SOCKET_ADDRESS_TYPE_VSOCK) {
socket_connect_outgoing(s, saddr, errp);
/*
* async: after the socket is connected, calls
* migration_channel_connect_outgoing() directly.
*/
return;
} else if (saddr->type == SOCKET_ADDRESS_TYPE_FD) {
ioc = fd_connect_outgoing(s, saddr->u.fd.str, errp);
}
#ifdef CONFIG_RDMA
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) {
ioc = rdma_connect_outgoing(s, &addr->u.rdma, errp);
#endif
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_EXEC) {
ioc = exec_connect_outgoing(s, addr->u.exec.args, errp);
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) {
ioc = file_connect_outgoing(s, &addr->u.file, errp);
} else {
error_setg(errp, "uri is not a valid migration protocol");
}
if (ioc) {
migration_channel_connect_outgoing(s, ioc);
}
return;
}
void migration_connect_incoming(MigrationAddress *addr, Error **errp)
{
if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) {
SocketAddress *saddr = &addr->u.socket;
if (saddr->type == SOCKET_ADDRESS_TYPE_INET ||
saddr->type == SOCKET_ADDRESS_TYPE_UNIX ||
saddr->type == SOCKET_ADDRESS_TYPE_VSOCK) {
socket_connect_incoming(saddr, errp);
} else if (saddr->type == SOCKET_ADDRESS_TYPE_FD) {
fd_connect_incoming(saddr->u.fd.str, errp);
}
#ifdef CONFIG_RDMA
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) {
rdma_connect_incoming(&addr->u.rdma, errp);
#endif
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_EXEC) {
exec_connect_incoming(addr->u.exec.args, errp);
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) {
file_connect_incoming(&addr->u.file, errp);
} else {
error_setg(errp, "unknown migration protocol");
}
/*
* async: the above routines all wait for the incoming connection
* and call back to migration_channel_process_incoming() to start
* the migration.
*/
}
bool migration_has_main_and_multifd_channels(void)
{
MigrationIncomingState *mis = migration_incoming_get_current();
if (!mis->from_src_file) {
/* main channel not established */
return false;
}
if (migrate_multifd() && !multifd_recv_all_channels_created()) {
return false;
}
/* main and all multifd channels are established */
return true;
}
/**
* @migration_has_all_channels: We have received all channels that we need
*
* Returns true when we have got connections to all the channels that
* we need for migration.
*/
bool migration_has_all_channels(void)
{
if (!migration_has_main_and_multifd_channels()) {
return false;
}
MigrationIncomingState *mis = migration_incoming_get_current();
if (migrate_postcopy_preempt() && !mis->postcopy_qemufile_dst) {
return false;
}
return true;
}
static MigChannelType migration_channel_identify(MigrationIncomingState *mis,
QIOChannel *ioc, Error **errp)
{
MigChannelType channel = CH_NONE;
uint32_t channel_magic = 0;
int ret = 0;
if (!migration_has_main_and_multifd_channels()) {
if (qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_READ_MSG_PEEK)) {
/*
* With multiple channels, it is possible that we receive channels
* out of order on destination side, causing incorrect mapping of
* source channels on destination side. Check channel MAGIC to
* decide type of channel. Please note this is best effort,
* postcopy preempt channel does not send any magic number so
* avoid it for postcopy live migration. Also tls live migration
* already does tls handshake while initializing main channel so
* with tls this issue is not possible.
*/
ret = migration_channel_read_peek(ioc, (void *)&channel_magic,
sizeof(channel_magic), errp);
if (ret != 0) {
goto out;
}
channel_magic = be32_to_cpu(channel_magic);
if (channel_magic == QEMU_VM_FILE_MAGIC) {
channel = CH_MAIN;
} else if (channel_magic == MULTIFD_MAGIC) {
assert(migrate_multifd());
channel = CH_MULTIFD;
} else if (!mis->from_src_file &&
mis->state == MIGRATION_STATUS_POSTCOPY_PAUSED) {
/* reconnect main channel for postcopy recovery */
channel = CH_MAIN;
} else {
error_setg(errp, "unknown channel magic: %u", channel_magic);
}
} else if (mis->from_src_file && migrate_multifd()) {
/*
* Non-peekable channels like tls/file are processed as
* multifd channels when multifd is enabled.
*/
channel = CH_MULTIFD;
} else if (!mis->from_src_file) {
channel = CH_MAIN;
} else {
error_setg(errp, "non-peekable channel used without multifd");
}
} else {
assert(migrate_postcopy_preempt());
channel = CH_POSTCOPY;
}
out:
return channel;
}
/**
* @migration_channel_process_incoming - Create new incoming migration channel
*
* Notice that TLS is special. For it we listen in a listener socket,
* and then create a new client socket from the TLS library.
*
* @ioc: Channel to which we are connecting
*/
void migration_channel_process_incoming(QIOChannel *ioc)
{
MigrationIncomingState *mis = migration_incoming_get_current();
Error *local_err = NULL;
MigChannelType ch;
trace_migration_set_incoming_channel(
ioc, object_get_typename(OBJECT(ioc)));
if (migrate_channel_requires_tls_upgrade(ioc)) {
migration_tls_channel_process_incoming(ioc, &local_err);
} else {
migration_ioc_register_yank(ioc);
ch = migration_channel_identify(mis, ioc, &local_err);
if (!ch) {
goto out;
}
if (migration_incoming_setup(ioc, ch, &local_err)) {
migration_start_incoming();
}
}
out:
if (local_err) {
error_report_err(local_err);
migrate_set_state(&mis->state, mis->state, MIGRATION_STATUS_FAILED);
if (mis->exit_on_error) {
exit(EXIT_FAILURE);
}
}
}
void migration_channel_connect_outgoing(MigrationState *s, QIOChannel *ioc)
{
trace_migration_set_outgoing_channel(ioc, object_get_typename(OBJECT(ioc)));
if (migrate_channel_requires_tls_upgrade(ioc)) {
Error *local_err = NULL;
migration_tls_channel_connect(s, ioc, &local_err);
if (local_err) {
migration_connect_error_propagate(s, local_err);
}
/*
* async: the above will call back to this function after
* the TLS handshake is successfully completed.
*/
return;
}
migration_ioc_register_yank(ioc);
migration_outgoing_setup(ioc);
migration_start_outgoing(s);
}
/**
* @migration_channel_read_peek - Peek at migration channel, without
* actually removing it from channel buffer.
*
* @ioc: the channel object
* @buf: the memory region to read data into
* @buflen: the number of bytes to read in @buf
* @errp: pointer to a NULL-initialized error object
*
* Returns 0 if successful, returns -1 and sets @errp if fails.
*/
int migration_channel_read_peek(QIOChannel *ioc,
const char *buf,
const size_t buflen,
Error **errp)
{
ssize_t len = 0;
struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen };
while (true) {
len = qio_channel_readv_full(ioc, &iov, 1, NULL, NULL,
QIO_CHANNEL_READ_FLAG_MSG_PEEK, errp);
if (len < 0 && len != QIO_CHANNEL_ERR_BLOCK) {
return -1;
}
if (len == 0) {
error_setg(errp, "Failed to peek at channel");
return -1;
}
if (len == buflen) {
break;
}
qio_channel_wait_cond(ioc, G_IO_IN);
}
return 0;
}
static bool migrate_channels_parse(MigrationChannelList *channels,
MigrationChannel **main_channelp,
MigrationChannel **cpr_channelp,
Error **errp)
{
MigrationChannel *channelv[MIGRATION_CHANNEL_TYPE__MAX] = { NULL };
if (!cpr_channelp && channels->next) {
error_setg(errp, "Channel list must have only one entry, "
"for type 'main'");
return false;
}
for ( ; channels; channels = channels->next) {
MigrationChannelType type;
type = channels->value->channel_type;
if (channelv[type]) {
error_setg(errp, "Channel list has more than one %s entry",
MigrationChannelType_str(type));
return false;
}
channelv[type] = channels->value;
}
if (cpr_channelp) {
*cpr_channelp = QAPI_CLONE(MigrationChannel,
channelv[MIGRATION_CHANNEL_TYPE_CPR]);
if (migrate_mode() == MIG_MODE_CPR_TRANSFER && !*cpr_channelp) {
error_setg(errp, "missing 'cpr' migration channel");
return false;
}
}
*main_channelp = QAPI_CLONE(MigrationChannel,
channelv[MIGRATION_CHANNEL_TYPE_MAIN]);
if (!(*main_channelp)->addr) {
error_setg(errp, "Channel list has no main entry");
return false;
}
return true;
}
bool migrate_uri_parse(const char *uri, MigrationChannel **channel,
Error **errp)
{
g_autoptr(MigrationChannel) val = g_new0(MigrationChannel, 1);
g_autoptr(MigrationAddress) addr = g_new0(MigrationAddress, 1);
InetSocketAddress *isock = &addr->u.rdma;
strList **tail = &addr->u.exec.args;
if (strstart(uri, "exec:", NULL)) {
addr->transport = MIGRATION_ADDRESS_TYPE_EXEC;
#ifdef WIN32
QAPI_LIST_APPEND(tail, g_strdup(exec_get_cmd_path()));
QAPI_LIST_APPEND(tail, g_strdup("/c"));
#else
QAPI_LIST_APPEND(tail, g_strdup("/bin/sh"));
QAPI_LIST_APPEND(tail, g_strdup("-c"));
#endif
QAPI_LIST_APPEND(tail, g_strdup(uri + strlen("exec:")));
} else if (strstart(uri, "rdma:", NULL)) {
if (inet_parse(isock, uri + strlen("rdma:"), errp)) {
qapi_free_InetSocketAddress(isock);
return false;
}
addr->transport = MIGRATION_ADDRESS_TYPE_RDMA;
} else if (strstart(uri, "tcp:", NULL) ||
strstart(uri, "unix:", NULL) ||
strstart(uri, "vsock:", NULL) ||
strstart(uri, "fd:", NULL)) {
addr->transport = MIGRATION_ADDRESS_TYPE_SOCKET;
SocketAddress *saddr = socket_parse(uri, errp);
if (!saddr) {
return false;
}
addr->u.socket.type = saddr->type;
addr->u.socket.u = saddr->u;
/* Don't free the objects inside; their ownership moved to "addr" */
g_free(saddr);
} else if (strstart(uri, "file:", NULL)) {
addr->transport = MIGRATION_ADDRESS_TYPE_FILE;
addr->u.file.filename = g_strdup(uri + strlen("file:"));
if (file_parse_offset(addr->u.file.filename, &addr->u.file.offset,
errp)) {
return false;
}
} else {
error_setg(errp, "unknown migration protocol: %s", uri);
return false;
}
val->channel_type = MIGRATION_CHANNEL_TYPE_MAIN;
val->addr = g_steal_pointer(&addr);
*channel = g_steal_pointer(&val);
return true;
}
bool migration_channel_parse_input(const char *uri,
MigrationChannelList *channels,
MigrationChannel **main_channelp,
MigrationChannel **cpr_channelp,
Error **errp)
{
if (!uri == !channels) {
error_setg(errp, "need either 'uri' or 'channels' argument");
return false;
}
if (channels) {
return migrate_channels_parse(channels, main_channelp, cpr_channelp,
errp);
} else {
return migrate_uri_parse(uri, main_channelp, errp);
}
}