Sometimes you want to be able to start up the receiving side of a live migration
and actually be able to run monitor commands before you do the migration.
Libvirt, in particular, wants to do this for setting up the migration. This
patch implements a "nowait" option to the receiving side so that you start up
the receiving side similar to:
qemu-kvm -M pc -S blah blah -incoming tcp://0:4444,nowait
Then you are able to interact with the monitor before the live migration takes
place.
Signed-off-by: Chris Lalancette <[EMAIL PROTECTED]>
diff --git a/qemu/migration.c b/qemu/migration.c
index a64a287..d16e289 100644
--- a/qemu/migration.c
+++ b/qemu/migration.c
@@ -886,13 +886,10 @@ static int migrate_incoming_fd(int fd)
return ret;
}
-static int migrate_incoming_tcp(const char *host)
+static int migrate_listen_tcp(const char *host, int *outfd)
{
struct sockaddr_in addr;
- socklen_t addrlen = sizeof(addr);
- int fd, sfd;
- ssize_t len;
- uint8_t status = 0;
+ int fd = -1;
int reuse = 1;
int rc;
@@ -928,19 +925,43 @@ static int migrate_incoming_tcp(const char *host)
goto error_socket;
}
+ *outfd = fd;
+
+ return 0;
+
+error_socket:
+ close(fd);
+error:
+ return rc;
+}
+
+struct migrate_tcp_data {
+ int listen_fd;
+ int rc;
+};
+
+static void migrate_incoming_tcp(void *opaque)
+{
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ struct migrate_tcp_data *data = (struct migrate_tcp_data *)opaque;
+ int sfd;
+ ssize_t len;
+ uint8_t status = 0;
+
again:
- sfd = accept(fd, (struct sockaddr *)&addr, &addrlen);
+ sfd = accept(data->listen_fd, (struct sockaddr *)&addr, &addrlen);
if (sfd == -1) {
if (errno == EINTR)
goto again;
perror("accept() failed");
- rc = MIG_STAT_DST_ACCEPT_FAILED;
+ data->rc = MIG_STAT_DST_ACCEPT_FAILED;
goto error_socket;
}
- rc = migrate_incoming_fd(sfd);
- if (rc != 0) {
- fprintf(stderr, "migrate_incoming_fd failed (rc=%d)\n", rc);
+ data->rc = migrate_incoming_fd(sfd);
+ if (data->rc != 0) {
+ fprintf(stderr, "migrate_incoming_fd failed (rc=%d)\n", data->rc);
goto error_accept;
}
@@ -951,13 +972,13 @@ send_ack:
if (len != 1) {
fprintf(stderr, "migration: send_ack: write error len=%zu (%s)\n",
len, strerror(errno));
- rc = MIG_STAT_DST_WRITE_FAILED;
+ data->rc = MIG_STAT_DST_WRITE_FAILED;
goto error_accept;
}
- rc = wait_for_message("WAIT FOR GO", sfd, wait_for_message_timeout);
- if (rc) {
- rc += 200;
+ data->rc = wait_for_message("WAIT FOR GO", sfd, wait_for_message_timeout);
+ if (data->rc) {
+ data->rc += 200;
goto error_accept;
}
@@ -966,7 +987,7 @@ wait_for_go:
if (len == -1 && errno == EAGAIN)
goto wait_for_go;
if (len != 1) {
- rc = MIG_STAT_DST_READ_FAILED;
+ data->rc = MIG_STAT_DST_READ_FAILED;
fprintf(stderr, "migration: wait_for_go: read error len=%zu (%s)\n",
len, strerror(errno));
}
@@ -974,9 +995,10 @@ wait_for_go:
error_accept:
close(sfd);
error_socket:
- close(fd);
-error:
- return rc;
+ qemu_set_fd_handler(data->listen_fd, NULL, NULL, NULL);
+ close(data->listen_fd);
+
+ qemu_free(data);
}
int migrate_incoming(const char *device)
@@ -996,16 +1018,57 @@ int migrate_incoming(const char *device)
}
} else if (strstart(device, "tcp://", &ptr)) {
char *host, *end;
+ struct migrate_tcp_data *data;
+ int is_waitconnect = 1;
+
host = strdup(ptr);
+ if (!host)
+ goto fail;
end = strchr(host, '/');
if (end) *end = 0;
- ret = migrate_incoming_tcp(host);
+
+ data = qemu_mallocz(sizeof(struct migrate_tcp_data));
+ if (!data) {
+ qemu_free(host);
+ goto fail;
+ }
+
+ ptr = host;
+ while((ptr = strchr(ptr,','))) {
+ ptr++;
+ if (!strncmp(ptr,"nowait",6)) {
+ is_waitconnect = 0;
+ } else {
+ printf("Unknown option: %s\n", ptr);
+ qemu_free(host);
+ goto fail;
+ }
+ }
+
+ ret = migrate_listen_tcp(host, &(data->listen_fd));
qemu_free(host);
+ if (ret != 0)
+ goto fail;
+
+ /*
+ * if we made it here, then migrate_incoming_tcp is responsible for
+ * freeing the "data" structure
+ */
+ if (!is_waitconnect) {
+ socket_set_nonblock(data->listen_fd);
+ qemu_set_fd_handler(data->listen_fd, migrate_incoming_tcp, NULL, data);
+ }
+ else {
+ migrate_incoming_tcp(data);
+ ret = data->rc;
+ }
+
} else {
errno = EINVAL;
ret = MIG_STAT_DST_INVALID_PARAMS;
}
+ fail:
return ret;
}