Skip to contentSkip to conversation

: Implement tcp "nowait" option for migration

PATCH In review Chris Lalancette 1 participant 2 messages Aug 1, 2008 – Aug 6, 2008 KVM

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 : Implement tcp "nowait" option for migration

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.

qemu/migration.c +82 -19
index a64a287..d16e289 100644
@@ -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;
 }
 
#1 lore raw
Quoted 10 lines · Chris Lalancette

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.

Anthony,
Any comments on this patch? I'd like to try to get this in the KVM tree
once Avi gets back, so I would prefer to do any iterations/cleanups now.

Thanks,
Chris Lalancette