Commit 065e281356282f59386272aa50590b566a1809d9
1 parent
a74b4d2c
Reintroduce migrate-to-exec: support (Charles Duffy)
KVM's live migration support included support for exec: URLs, allowing system state to be written or received via an arbitrary popen()ed subprocess. This provides a convenient way to pipe state through a compression algorithm or an arbitrary network transport on its way to its destination, and a convenient way to write state to disk; libvirt's qemu driver currently uses migration to exec: targets for this latter purpose. This version of the patch refactors now-common code from migrate-tcp.c into migrate.c. Signed-off-by: Charles Duffy <Charles_Duffy@messageone.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5694 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
7 changed files
with
453 additions
and
171 deletions
Makefile.target
@@ -596,7 +596,7 @@ endif | @@ -596,7 +596,7 @@ endif | ||
596 | ifdef CONFIG_WIN32 | 596 | ifdef CONFIG_WIN32 |
597 | OBJS+=block-raw-win32.o | 597 | OBJS+=block-raw-win32.o |
598 | else | 598 | else |
599 | -OBJS+=block-raw-posix.o | 599 | +OBJS+=block-raw-posix.o migration-exec.o |
600 | endif | 600 | endif |
601 | 601 | ||
602 | LIBS+=-lz | 602 | LIBS+=-lz |
hw/hw.h
@@ -35,6 +35,8 @@ QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, | @@ -35,6 +35,8 @@ QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, | ||
35 | QEMUFileRateLimit *rate_limit); | 35 | QEMUFileRateLimit *rate_limit); |
36 | QEMUFile *qemu_fopen(const char *filename, const char *mode); | 36 | QEMUFile *qemu_fopen(const char *filename, const char *mode); |
37 | QEMUFile *qemu_fopen_socket(int fd); | 37 | QEMUFile *qemu_fopen_socket(int fd); |
38 | +QEMUFile *qemu_popen(FILE *popen_file, const char *mode); | ||
39 | +QEMUFile *qemu_popen_cmd(const char *command, const char *mode); | ||
38 | void qemu_fflush(QEMUFile *f); | 40 | void qemu_fflush(QEMUFile *f); |
39 | int qemu_fclose(QEMUFile *f); | 41 | int qemu_fclose(QEMUFile *f); |
40 | void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); | 42 | void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); |
migration-exec.c
0 → 100644
1 | +/* | ||
2 | + * QEMU live migration | ||
3 | + * | ||
4 | + * Copyright IBM, Corp. 2008 | ||
5 | + * Copyright Dell MessageOne 2008 | ||
6 | + * | ||
7 | + * Authors: | ||
8 | + * Anthony Liguori <aliguori@us.ibm.com> | ||
9 | + * Charles Duffy <charles_duffy@messageone.com> | ||
10 | + * | ||
11 | + * This work is licensed under the terms of the GNU GPL, version 2. See | ||
12 | + * the COPYING file in the top-level directory. | ||
13 | + * | ||
14 | + */ | ||
15 | + | ||
16 | +#include "qemu-common.h" | ||
17 | +#include "qemu_socket.h" | ||
18 | +#include "migration.h" | ||
19 | +#include "qemu-char.h" | ||
20 | +#include "sysemu.h" | ||
21 | +#include "console.h" | ||
22 | +#include "buffered_file.h" | ||
23 | +#include "block.h" | ||
24 | + | ||
25 | +//#define DEBUG_MIGRATION_EXEC | ||
26 | + | ||
27 | +#ifdef DEBUG_MIGRATION_EXEC | ||
28 | +#define dprintf(fmt, ...) \ | ||
29 | + do { printf("migration-exec: " fmt, ## __VA_ARGS__); } while (0) | ||
30 | +#else | ||
31 | +#define dprintf(fmt, ...) \ | ||
32 | + do { } while (0) | ||
33 | +#endif | ||
34 | + | ||
35 | +static int file_errno(FdMigrationState *s) | ||
36 | +{ | ||
37 | + return errno; | ||
38 | +} | ||
39 | + | ||
40 | +static int file_write(FdMigrationState *s, const void * buf, size_t size) | ||
41 | +{ | ||
42 | + return write(s->fd, buf, size); | ||
43 | +} | ||
44 | + | ||
45 | +static int exec_close(FdMigrationState *s) | ||
46 | +{ | ||
47 | + dprintf("exec_close\n"); | ||
48 | + if (s->opaque) { | ||
49 | + qemu_fclose(s->opaque); | ||
50 | + s->opaque = NULL; | ||
51 | + s->fd = -1; | ||
52 | + } | ||
53 | + return 0; | ||
54 | +} | ||
55 | + | ||
56 | +MigrationState *exec_start_outgoing_migration(const char *command, | ||
57 | + int64_t bandwidth_limit, | ||
58 | + int async) | ||
59 | +{ | ||
60 | + FdMigrationState *s; | ||
61 | + FILE *f; | ||
62 | + | ||
63 | + s = qemu_mallocz(sizeof(*s)); | ||
64 | + if (s == NULL) { | ||
65 | + dprintf("Unable to allocate FdMigrationState\n"); | ||
66 | + goto err; | ||
67 | + } | ||
68 | + | ||
69 | + f = popen(command, "w"); | ||
70 | + if (f == NULL) { | ||
71 | + dprintf("Unable to popen exec target\n"); | ||
72 | + goto err_after_alloc; | ||
73 | + } | ||
74 | + | ||
75 | + s->fd = fileno(f); | ||
76 | + if (s->fd == -1) { | ||
77 | + dprintf("Unable to retrieve file descriptor for popen'd handle\n"); | ||
78 | + goto err_after_open; | ||
79 | + } | ||
80 | + | ||
81 | + if (fcntl(s->fd, F_SETFD, O_NONBLOCK) == -1) { | ||
82 | + dprintf("Unable to set nonblocking mode on file descriptor\n"); | ||
83 | + goto err_after_open; | ||
84 | + } | ||
85 | + | ||
86 | + s->opaque = qemu_popen(f, "w"); | ||
87 | + | ||
88 | + s->get_error = file_errno; | ||
89 | + s->write = file_write; | ||
90 | + s->mig_state.cancel = migrate_fd_cancel; | ||
91 | + s->mig_state.get_status = migrate_fd_get_status; | ||
92 | + s->mig_state.release = migrate_fd_release; | ||
93 | + | ||
94 | + s->state = MIG_STATE_ACTIVE; | ||
95 | + s->detach = !async; | ||
96 | + s->bandwidth_limit = bandwidth_limit; | ||
97 | + | ||
98 | + if (s->detach == 1) { | ||
99 | + dprintf("detaching from monitor\n"); | ||
100 | + monitor_suspend(); | ||
101 | + s->detach = 2; | ||
102 | + } | ||
103 | + | ||
104 | + migrate_fd_connect(s); | ||
105 | + return &s->mig_state; | ||
106 | + | ||
107 | +err_after_open: | ||
108 | + pclose(f); | ||
109 | +err_after_alloc: | ||
110 | + qemu_free(s); | ||
111 | +err: | ||
112 | + return NULL; | ||
113 | +} | ||
114 | + | ||
115 | +int exec_start_incoming_migration(const char *command) | ||
116 | +{ | ||
117 | + int ret; | ||
118 | + QEMUFile *f; | ||
119 | + | ||
120 | + dprintf("Attempting to start an incoming migration\n"); | ||
121 | + f = qemu_popen_cmd(command, "r"); | ||
122 | + if(f == NULL) { | ||
123 | + dprintf("Unable to apply qemu wrapper to popen file\n"); | ||
124 | + return -errno; | ||
125 | + } | ||
126 | + vm_stop(0); /* just in case */ | ||
127 | + ret = qemu_loadvm_state(f); | ||
128 | + if (ret < 0) { | ||
129 | + fprintf(stderr, "load of migration failed\n"); | ||
130 | + goto err; | ||
131 | + } | ||
132 | + qemu_announce_self(); | ||
133 | + dprintf("successfully loaded vm state\n"); | ||
134 | + vm_start(); | ||
135 | + qemu_fclose(f); | ||
136 | + return 0; | ||
137 | + | ||
138 | +err: | ||
139 | + qemu_fclose(f); | ||
140 | + return -errno; | ||
141 | +} |
migration-tcp.c
@@ -22,16 +22,6 @@ | @@ -22,16 +22,6 @@ | ||
22 | 22 | ||
23 | //#define DEBUG_MIGRATION_TCP | 23 | //#define DEBUG_MIGRATION_TCP |
24 | 24 | ||
25 | -typedef struct FdMigrationState | ||
26 | -{ | ||
27 | - MigrationState mig_state; | ||
28 | - QEMUFile *file; | ||
29 | - int64_t bandwidth_limit; | ||
30 | - int fd; | ||
31 | - int detach; | ||
32 | - int state; | ||
33 | -} FdMigrationState; | ||
34 | - | ||
35 | #ifdef DEBUG_MIGRATION_TCP | 25 | #ifdef DEBUG_MIGRATION_TCP |
36 | #define dprintf(fmt, ...) \ | 26 | #define dprintf(fmt, ...) \ |
37 | do { printf("migration-tcp: " fmt, ## __VA_ARGS__); } while (0) | 27 | do { printf("migration-tcp: " fmt, ## __VA_ARGS__); } while (0) |
@@ -40,64 +30,19 @@ typedef struct FdMigrationState | @@ -40,64 +30,19 @@ typedef struct FdMigrationState | ||
40 | do { } while (0) | 30 | do { } while (0) |
41 | #endif | 31 | #endif |
42 | 32 | ||
43 | -static void tcp_cleanup(FdMigrationState *s) | 33 | +static int socket_errno(FdMigrationState *s) |
44 | { | 34 | { |
45 | - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); | ||
46 | - | ||
47 | - if (s->file) { | ||
48 | - dprintf("closing file\n"); | ||
49 | - qemu_fclose(s->file); | ||
50 | - } | ||
51 | - | ||
52 | - if (s->fd != -1) | ||
53 | - close(s->fd); | ||
54 | - | ||
55 | - /* Don't resume monitor until we've flushed all of the buffers */ | ||
56 | - if (s->detach == 2) { | ||
57 | - monitor_resume(); | ||
58 | - s->detach = 0; | ||
59 | - } | ||
60 | - | ||
61 | - s->fd = -1; | ||
62 | -} | ||
63 | - | ||
64 | -static void tcp_error(FdMigrationState *s) | ||
65 | -{ | ||
66 | - dprintf("setting error state\n"); | ||
67 | - s->state = MIG_STATE_ERROR; | ||
68 | - tcp_cleanup(s); | ||
69 | -} | ||
70 | - | ||
71 | -static void fd_put_notify(void *opaque) | ||
72 | -{ | ||
73 | - FdMigrationState *s = opaque; | ||
74 | - | ||
75 | - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); | ||
76 | - qemu_file_put_notify(s->file); | 35 | + return (s->get_error(s)); |
77 | } | 36 | } |
78 | 37 | ||
79 | -static ssize_t fd_put_buffer(void *opaque, const void *data, size_t size) | 38 | +static int socket_write(FdMigrationState *s, const void * buf, size_t size) |
80 | { | 39 | { |
81 | - FdMigrationState *s = opaque; | ||
82 | - ssize_t ret; | ||
83 | - | ||
84 | - do { | ||
85 | - ret = send(s->fd, data, size, 0); | ||
86 | - } while (ret == -1 && (socket_error() == EINTR || socket_error() == EWOULDBLOCK)); | ||
87 | - | ||
88 | - if (ret == -1) | ||
89 | - ret = -socket_error(); | ||
90 | - | ||
91 | - if (ret == -EAGAIN) | ||
92 | - qemu_set_fd_handler2(s->fd, NULL, NULL, fd_put_notify, s); | ||
93 | - | ||
94 | - return ret; | 40 | + return send(s->fd, buf, size, 0); |
95 | } | 41 | } |
96 | 42 | ||
97 | -static int fd_close(void *opaque) | 43 | +static int tcp_close(FdMigrationState *s) |
98 | { | 44 | { |
99 | - FdMigrationState *s = opaque; | ||
100 | - dprintf("fd_close\n"); | 45 | + dprintf("tcp_close\n"); |
101 | if (s->fd != -1) { | 46 | if (s->fd != -1) { |
102 | close(s->fd); | 47 | close(s->fd); |
103 | s->fd = -1; | 48 | s->fd = -1; |
@@ -105,67 +50,6 @@ static int fd_close(void *opaque) | @@ -105,67 +50,6 @@ static int fd_close(void *opaque) | ||
105 | return 0; | 50 | return 0; |
106 | } | 51 | } |
107 | 52 | ||
108 | -static void fd_wait_for_unfreeze(void *opaque) | ||
109 | -{ | ||
110 | - FdMigrationState *s = opaque; | ||
111 | - int ret; | ||
112 | - | ||
113 | - dprintf("wait for unfreeze\n"); | ||
114 | - if (s->state != MIG_STATE_ACTIVE) | ||
115 | - return; | ||
116 | - | ||
117 | - do { | ||
118 | - fd_set wfds; | ||
119 | - | ||
120 | - FD_ZERO(&wfds); | ||
121 | - FD_SET(s->fd, &wfds); | ||
122 | - | ||
123 | - ret = select(s->fd + 1, NULL, &wfds, NULL, NULL); | ||
124 | - } while (ret == -1 && socket_error() == EINTR); | ||
125 | -} | ||
126 | - | ||
127 | -static void fd_put_ready(void *opaque) | ||
128 | -{ | ||
129 | - FdMigrationState *s = opaque; | ||
130 | - | ||
131 | - if (s->state != MIG_STATE_ACTIVE) { | ||
132 | - dprintf("put_ready returning because of non-active state\n"); | ||
133 | - return; | ||
134 | - } | ||
135 | - | ||
136 | - dprintf("iterate\n"); | ||
137 | - if (qemu_savevm_state_iterate(s->file) == 1) { | ||
138 | - dprintf("done iterating\n"); | ||
139 | - vm_stop(0); | ||
140 | - | ||
141 | - bdrv_flush_all(); | ||
142 | - qemu_savevm_state_complete(s->file); | ||
143 | - s->state = MIG_STATE_COMPLETED; | ||
144 | - tcp_cleanup(s); | ||
145 | - } | ||
146 | -} | ||
147 | - | ||
148 | -static void tcp_connect_migrate(FdMigrationState *s) | ||
149 | -{ | ||
150 | - int ret; | ||
151 | - | ||
152 | - s->file = qemu_fopen_ops_buffered(s, | ||
153 | - s->bandwidth_limit, | ||
154 | - fd_put_buffer, | ||
155 | - fd_put_ready, | ||
156 | - fd_wait_for_unfreeze, | ||
157 | - fd_close); | ||
158 | - | ||
159 | - dprintf("beginning savevm\n"); | ||
160 | - ret = qemu_savevm_state_begin(s->file); | ||
161 | - if (ret < 0) { | ||
162 | - dprintf("failed, %d\n", ret); | ||
163 | - tcp_error(s); | ||
164 | - return; | ||
165 | - } | ||
166 | - | ||
167 | - fd_put_ready(s); | ||
168 | -} | ||
169 | 53 | ||
170 | static void tcp_wait_for_connect(void *opaque) | 54 | static void tcp_wait_for_connect(void *opaque) |
171 | { | 55 | { |
@@ -176,60 +60,21 @@ static void tcp_wait_for_connect(void *opaque) | @@ -176,60 +60,21 @@ static void tcp_wait_for_connect(void *opaque) | ||
176 | dprintf("connect completed\n"); | 60 | dprintf("connect completed\n"); |
177 | do { | 61 | do { |
178 | ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &val, &valsize); | 62 | ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &val, &valsize); |
179 | - } while (ret == -1 && socket_error() == EINTR); | 63 | + } while (ret == -1 && (s->get_error(s)) == EINTR); |
180 | 64 | ||
181 | if (ret < 0) { | 65 | if (ret < 0) { |
182 | - tcp_error(s); | 66 | + migrate_fd_error(s); |
183 | return; | 67 | return; |
184 | } | 68 | } |
185 | 69 | ||
186 | qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); | 70 | qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); |
187 | 71 | ||
188 | if (val == 0) | 72 | if (val == 0) |
189 | - tcp_connect_migrate(s); | 73 | + migrate_fd_connect(s); |
190 | else { | 74 | else { |
191 | dprintf("error connecting %d\n", val); | 75 | dprintf("error connecting %d\n", val); |
192 | - tcp_error(s); | ||
193 | - } | ||
194 | -} | ||
195 | - | ||
196 | -static FdMigrationState *to_fms(MigrationState *mig_state) | ||
197 | -{ | ||
198 | - return container_of(mig_state, FdMigrationState, mig_state); | ||
199 | -} | ||
200 | - | ||
201 | -static int tcp_get_status(MigrationState *mig_state) | ||
202 | -{ | ||
203 | - FdMigrationState *s = to_fms(mig_state); | ||
204 | - | ||
205 | - return s->state; | ||
206 | -} | ||
207 | - | ||
208 | -static void tcp_cancel(MigrationState *mig_state) | ||
209 | -{ | ||
210 | - FdMigrationState *s = to_fms(mig_state); | ||
211 | - | ||
212 | - if (s->state != MIG_STATE_ACTIVE) | ||
213 | - return; | ||
214 | - | ||
215 | - dprintf("cancelling migration\n"); | ||
216 | - | ||
217 | - s->state = MIG_STATE_CANCELLED; | ||
218 | - | ||
219 | - tcp_cleanup(s); | ||
220 | -} | ||
221 | - | ||
222 | -static void tcp_release(MigrationState *mig_state) | ||
223 | -{ | ||
224 | - FdMigrationState *s = to_fms(mig_state); | ||
225 | - | ||
226 | - dprintf("releasing state\n"); | ||
227 | - | ||
228 | - if (s->state == MIG_STATE_ACTIVE) { | ||
229 | - s->state = MIG_STATE_CANCELLED; | ||
230 | - tcp_cleanup(s); | 76 | + migrate_fd_error(s); |
231 | } | 77 | } |
232 | - free(s); | ||
233 | } | 78 | } |
234 | 79 | ||
235 | MigrationState *tcp_start_outgoing_migration(const char *host_port, | 80 | MigrationState *tcp_start_outgoing_migration(const char *host_port, |
@@ -247,9 +92,12 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port, | @@ -247,9 +92,12 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port, | ||
247 | if (s == NULL) | 92 | if (s == NULL) |
248 | return NULL; | 93 | return NULL; |
249 | 94 | ||
250 | - s->mig_state.cancel = tcp_cancel; | ||
251 | - s->mig_state.get_status = tcp_get_status; | ||
252 | - s->mig_state.release = tcp_release; | 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; | ||
253 | 101 | ||
254 | s->state = MIG_STATE_ACTIVE; | 102 | s->state = MIG_STATE_ACTIVE; |
255 | s->detach = !async; | 103 | s->detach = !async; |
@@ -271,7 +119,7 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port, | @@ -271,7 +119,7 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port, | ||
271 | do { | 119 | do { |
272 | ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)); | 120 | ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)); |
273 | if (ret == -1) | 121 | if (ret == -1) |
274 | - ret = -socket_error(); | 122 | + ret = -(s->get_error(s)); |
275 | 123 | ||
276 | if (ret == -EINPROGRESS || ret == -EWOULDBLOCK) | 124 | if (ret == -EINPROGRESS || ret == -EWOULDBLOCK) |
277 | qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s); | 125 | qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s); |
@@ -283,7 +131,7 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port, | @@ -283,7 +131,7 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port, | ||
283 | qemu_free(s); | 131 | qemu_free(s); |
284 | return NULL; | 132 | return NULL; |
285 | } else if (ret >= 0) | 133 | } else if (ret >= 0) |
286 | - tcp_connect_migrate(s); | 134 | + migrate_fd_connect(s); |
287 | 135 | ||
288 | return &s->mig_state; | 136 | return &s->mig_state; |
289 | } | 137 | } |
migration.c
@@ -14,6 +14,20 @@ | @@ -14,6 +14,20 @@ | ||
14 | #include "qemu-common.h" | 14 | #include "qemu-common.h" |
15 | #include "migration.h" | 15 | #include "migration.h" |
16 | #include "console.h" | 16 | #include "console.h" |
17 | +#include "buffered_file.h" | ||
18 | +#include "sysemu.h" | ||
19 | +#include "block.h" | ||
20 | +#include "qemu_socket.h" | ||
21 | + | ||
22 | +//#define DEBUG_MIGRATION | ||
23 | + | ||
24 | +#ifdef DEBUG_MIGRATION | ||
25 | +#define dprintf(fmt, ...) \ | ||
26 | + do { printf("migration: " fmt, ## __VA_ARGS__); } while (0) | ||
27 | +#else | ||
28 | +#define dprintf(fmt, ...) \ | ||
29 | + do { } while (0) | ||
30 | +#endif | ||
17 | 31 | ||
18 | /* Migration speed throttling */ | 32 | /* Migration speed throttling */ |
19 | static uint32_t max_throttle = (32 << 20); | 33 | static uint32_t max_throttle = (32 << 20); |
@@ -26,6 +40,10 @@ void qemu_start_incoming_migration(const char *uri) | @@ -26,6 +40,10 @@ void qemu_start_incoming_migration(const char *uri) | ||
26 | 40 | ||
27 | if (strstart(uri, "tcp:", &p)) | 41 | if (strstart(uri, "tcp:", &p)) |
28 | tcp_start_incoming_migration(p); | 42 | tcp_start_incoming_migration(p); |
43 | +#if !defined(WIN32) | ||
44 | + else if (strstart(uri, "exec:", &p)) | ||
45 | + exec_start_incoming_migration(p); | ||
46 | +#endif | ||
29 | else | 47 | else |
30 | fprintf(stderr, "unknown migration protocol: %s\n", uri); | 48 | fprintf(stderr, "unknown migration protocol: %s\n", uri); |
31 | } | 49 | } |
@@ -37,6 +55,10 @@ void do_migrate(int detach, const char *uri) | @@ -37,6 +55,10 @@ void do_migrate(int detach, const char *uri) | ||
37 | 55 | ||
38 | if (strstart(uri, "tcp:", &p)) | 56 | if (strstart(uri, "tcp:", &p)) |
39 | s = tcp_start_outgoing_migration(p, max_throttle, detach); | 57 | s = tcp_start_outgoing_migration(p, max_throttle, detach); |
58 | +#if !defined(WIN32) | ||
59 | + else if (strstart(uri, "exec:", &p)) | ||
60 | + s = exec_start_outgoing_migration(p, max_throttle, detach); | ||
61 | +#endif | ||
40 | else | 62 | else |
41 | term_printf("unknown migration protocol: %s\n", uri); | 63 | term_printf("unknown migration protocol: %s\n", uri); |
42 | 64 | ||
@@ -101,3 +123,159 @@ void do_info_migrate(void) | @@ -101,3 +123,159 @@ void do_info_migrate(void) | ||
101 | } | 123 | } |
102 | } | 124 | } |
103 | 125 | ||
126 | +/* shared migration helpers */ | ||
127 | + | ||
128 | +void migrate_fd_error(FdMigrationState *s) | ||
129 | +{ | ||
130 | + dprintf("setting error state\n"); | ||
131 | + s->state = MIG_STATE_ERROR; | ||
132 | + migrate_fd_cleanup(s); | ||
133 | +} | ||
134 | + | ||
135 | +void migrate_fd_cleanup(FdMigrationState *s) | ||
136 | +{ | ||
137 | + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); | ||
138 | + | ||
139 | + if (s->file) { | ||
140 | + dprintf("closing file\n"); | ||
141 | + qemu_fclose(s->file); | ||
142 | + } | ||
143 | + | ||
144 | + if (s->fd != -1) | ||
145 | + close(s->fd); | ||
146 | + | ||
147 | + /* Don't resume monitor until we've flushed all of the buffers */ | ||
148 | + if (s->detach == 2) { | ||
149 | + monitor_resume(); | ||
150 | + s->detach = 0; | ||
151 | + } | ||
152 | + | ||
153 | + s->fd = -1; | ||
154 | +} | ||
155 | + | ||
156 | +void migrate_fd_put_notify(void *opaque) | ||
157 | +{ | ||
158 | + FdMigrationState *s = opaque; | ||
159 | + | ||
160 | + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); | ||
161 | + qemu_file_put_notify(s->file); | ||
162 | +} | ||
163 | + | ||
164 | +ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size) | ||
165 | +{ | ||
166 | + FdMigrationState *s = opaque; | ||
167 | + ssize_t ret; | ||
168 | + | ||
169 | + do { | ||
170 | + ret = s->write(s, data, size); | ||
171 | + } while (ret == -1 && ((s->get_error(s)) == EINTR || (s->get_error(s)) == EWOULDBLOCK)); | ||
172 | + | ||
173 | + if (ret == -1) | ||
174 | + ret = -(s->get_error(s)); | ||
175 | + | ||
176 | + if (ret == -EAGAIN) | ||
177 | + qemu_set_fd_handler2(s->fd, NULL, NULL, migrate_fd_put_notify, s); | ||
178 | + | ||
179 | + return ret; | ||
180 | +} | ||
181 | + | ||
182 | +void migrate_fd_connect(FdMigrationState *s) | ||
183 | +{ | ||
184 | + int ret; | ||
185 | + | ||
186 | + s->file = qemu_fopen_ops_buffered(s, | ||
187 | + s->bandwidth_limit, | ||
188 | + migrate_fd_put_buffer, | ||
189 | + migrate_fd_put_ready, | ||
190 | + migrate_fd_wait_for_unfreeze, | ||
191 | + migrate_fd_close); | ||
192 | + | ||
193 | + dprintf("beginning savevm\n"); | ||
194 | + ret = qemu_savevm_state_begin(s->file); | ||
195 | + if (ret < 0) { | ||
196 | + dprintf("failed, %d\n", ret); | ||
197 | + migrate_fd_error(s); | ||
198 | + return; | ||
199 | + } | ||
200 | + | ||
201 | + migrate_fd_put_ready(s); | ||
202 | +} | ||
203 | + | ||
204 | +void migrate_fd_put_ready(void *opaque) | ||
205 | +{ | ||
206 | + FdMigrationState *s = opaque; | ||
207 | + | ||
208 | + if (s->state != MIG_STATE_ACTIVE) { | ||
209 | + dprintf("put_ready returning because of non-active state\n"); | ||
210 | + return; | ||
211 | + } | ||
212 | + | ||
213 | + dprintf("iterate\n"); | ||
214 | + if (qemu_savevm_state_iterate(s->file) == 1) { | ||
215 | + dprintf("done iterating\n"); | ||
216 | + vm_stop(0); | ||
217 | + | ||
218 | + bdrv_flush_all(); | ||
219 | + qemu_savevm_state_complete(s->file); | ||
220 | + s->state = MIG_STATE_COMPLETED; | ||
221 | + migrate_fd_cleanup(s); | ||
222 | + } | ||
223 | +} | ||
224 | + | ||
225 | +int migrate_fd_get_status(MigrationState *mig_state) | ||
226 | +{ | ||
227 | + FdMigrationState *s = migrate_to_fms(mig_state); | ||
228 | + return s->state; | ||
229 | +} | ||
230 | + | ||
231 | +void migrate_fd_cancel(MigrationState *mig_state) | ||
232 | +{ | ||
233 | + FdMigrationState *s = migrate_to_fms(mig_state); | ||
234 | + | ||
235 | + if (s->state != MIG_STATE_ACTIVE) | ||
236 | + return; | ||
237 | + | ||
238 | + dprintf("cancelling migration\n"); | ||
239 | + | ||
240 | + s->state = MIG_STATE_CANCELLED; | ||
241 | + | ||
242 | + migrate_fd_cleanup(s); | ||
243 | +} | ||
244 | + | ||
245 | +void migrate_fd_release(MigrationState *mig_state) | ||
246 | +{ | ||
247 | + FdMigrationState *s = migrate_to_fms(mig_state); | ||
248 | + | ||
249 | + dprintf("releasing state\n"); | ||
250 | + | ||
251 | + if (s->state == MIG_STATE_ACTIVE) { | ||
252 | + s->state = MIG_STATE_CANCELLED; | ||
253 | + migrate_fd_cleanup(s); | ||
254 | + } | ||
255 | + free(s); | ||
256 | +} | ||
257 | + | ||
258 | +void migrate_fd_wait_for_unfreeze(void *opaque) | ||
259 | +{ | ||
260 | + FdMigrationState *s = opaque; | ||
261 | + int ret; | ||
262 | + | ||
263 | + dprintf("wait for unfreeze\n"); | ||
264 | + if (s->state != MIG_STATE_ACTIVE) | ||
265 | + return; | ||
266 | + | ||
267 | + do { | ||
268 | + fd_set wfds; | ||
269 | + | ||
270 | + FD_ZERO(&wfds); | ||
271 | + FD_SET(s->fd, &wfds); | ||
272 | + | ||
273 | + ret = select(s->fd + 1, NULL, &wfds, NULL, NULL); | ||
274 | + } while (ret == -1 && (s->get_error(s)) == EINTR); | ||
275 | +} | ||
276 | + | ||
277 | +int migrate_fd_close(void *opaque) | ||
278 | +{ | ||
279 | + FdMigrationState *s = opaque; | ||
280 | + return s->close(s); | ||
281 | +} |
migration.h
@@ -29,6 +29,22 @@ struct MigrationState | @@ -29,6 +29,22 @@ struct MigrationState | ||
29 | void (*release)(MigrationState *s); | 29 | void (*release)(MigrationState *s); |
30 | }; | 30 | }; |
31 | 31 | ||
32 | +typedef struct FdMigrationState FdMigrationState; | ||
33 | + | ||
34 | +struct FdMigrationState | ||
35 | +{ | ||
36 | + MigrationState mig_state; | ||
37 | + int64_t bandwidth_limit; | ||
38 | + QEMUFile *file; | ||
39 | + int fd; | ||
40 | + int detach; | ||
41 | + int state; | ||
42 | + int (*get_error)(struct FdMigrationState*); | ||
43 | + int (*close)(struct FdMigrationState*); | ||
44 | + int (*write)(struct FdMigrationState*, const void *, size_t); | ||
45 | + void *opaque; | ||
46 | +}; | ||
47 | + | ||
32 | void qemu_start_incoming_migration(const char *uri); | 48 | void qemu_start_incoming_migration(const char *uri); |
33 | 49 | ||
34 | void do_migrate(int detach, const char *uri); | 50 | void do_migrate(int detach, const char *uri); |
@@ -39,11 +55,44 @@ void do_migrate_set_speed(const char *value); | @@ -39,11 +55,44 @@ void do_migrate_set_speed(const char *value); | ||
39 | 55 | ||
40 | void do_info_migrate(void); | 56 | void do_info_migrate(void); |
41 | 57 | ||
58 | +int exec_start_incoming_migration(const char *host_port); | ||
59 | + | ||
60 | +MigrationState *exec_start_outgoing_migration(const char *host_port, | ||
61 | + int64_t bandwidth_limit, | ||
62 | + int detach); | ||
63 | + | ||
42 | int tcp_start_incoming_migration(const char *host_port); | 64 | int tcp_start_incoming_migration(const char *host_port); |
43 | 65 | ||
44 | MigrationState *tcp_start_outgoing_migration(const char *host_port, | 66 | MigrationState *tcp_start_outgoing_migration(const char *host_port, |
45 | int64_t bandwidth_limit, | 67 | int64_t bandwidth_limit, |
46 | int detach); | 68 | int detach); |
47 | 69 | ||
70 | +void migrate_fd_error(FdMigrationState *s); | ||
71 | + | ||
72 | +void migrate_fd_cleanup(FdMigrationState *s); | ||
73 | + | ||
74 | +void migrate_fd_put_notify(void *opaque); | ||
75 | + | ||
76 | +ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size); | ||
77 | + | ||
78 | +void migrate_fd_connect(FdMigrationState *s); | ||
79 | + | ||
80 | +void migrate_fd_put_ready(void *opaque); | ||
81 | + | ||
82 | +int migrate_fd_get_status(MigrationState *mig_state); | ||
83 | + | ||
84 | +void migrate_fd_cancel(MigrationState *mig_state); | ||
85 | + | ||
86 | +void migrate_fd_release(MigrationState *mig_state); | ||
87 | + | ||
88 | +void migrate_fd_wait_for_unfreeze(void *opaque); | ||
89 | + | ||
90 | +int migrate_fd_close(void *opaque); | ||
91 | + | ||
92 | +static inline FdMigrationState *migrate_to_fms(MigrationState *mig_state) | ||
93 | +{ | ||
94 | + return container_of(mig_state, FdMigrationState, mig_state); | ||
95 | +} | ||
96 | + | ||
48 | #endif | 97 | #endif |
49 | 98 |
vl.c
@@ -2964,6 +2964,12 @@ struct QEMUFile { | @@ -2964,6 +2964,12 @@ struct QEMUFile { | ||
2964 | int has_error; | 2964 | int has_error; |
2965 | }; | 2965 | }; |
2966 | 2966 | ||
2967 | +typedef struct QEMUFilePopen | ||
2968 | +{ | ||
2969 | + FILE *popen_file; | ||
2970 | + QEMUFile *file; | ||
2971 | +} QEMUFilePopen; | ||
2972 | + | ||
2967 | typedef struct QEMUFileSocket | 2973 | typedef struct QEMUFileSocket |
2968 | { | 2974 | { |
2969 | int fd; | 2975 | int fd; |
@@ -2992,6 +2998,64 @@ static int socket_close(void *opaque) | @@ -2992,6 +2998,64 @@ static int socket_close(void *opaque) | ||
2992 | return 0; | 2998 | return 0; |
2993 | } | 2999 | } |
2994 | 3000 | ||
3001 | +static int popen_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) | ||
3002 | +{ | ||
3003 | + QEMUFilePopen *s = opaque; | ||
3004 | + return fwrite(buf, 1, size, s->popen_file); | ||
3005 | +} | ||
3006 | + | ||
3007 | +static int popen_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) | ||
3008 | +{ | ||
3009 | + QEMUFilePopen *s = opaque; | ||
3010 | + return fread(buf, 1, size, s->popen_file); | ||
3011 | +} | ||
3012 | + | ||
3013 | +static int popen_close(void *opaque) | ||
3014 | +{ | ||
3015 | + QEMUFilePopen *s = opaque; | ||
3016 | + pclose(s->popen_file); | ||
3017 | + qemu_free(s); | ||
3018 | + return 0; | ||
3019 | +} | ||
3020 | + | ||
3021 | +QEMUFile *qemu_popen(FILE *popen_file, const char *mode) | ||
3022 | +{ | ||
3023 | + QEMUFilePopen *s; | ||
3024 | + | ||
3025 | + if (popen_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { | ||
3026 | + fprintf(stderr, "qemu_popen: Argument validity check failed\n"); | ||
3027 | + return NULL; | ||
3028 | + } | ||
3029 | + | ||
3030 | + s = qemu_mallocz(sizeof(QEMUFilePopen)); | ||
3031 | + if (!s) { | ||
3032 | + fprintf(stderr, "qemu_popen: malloc failed\n"); | ||
3033 | + return NULL; | ||
3034 | + } | ||
3035 | + | ||
3036 | + s->popen_file = popen_file; | ||
3037 | + | ||
3038 | + if(mode[0] == 'r') { | ||
3039 | + s->file = qemu_fopen_ops(s, NULL, popen_get_buffer, popen_close, NULL); | ||
3040 | + } else { | ||
3041 | + s->file = qemu_fopen_ops(s, popen_put_buffer, NULL, popen_close, NULL); | ||
3042 | + } | ||
3043 | + fprintf(stderr, "qemu_popen: returning result of qemu_fopen_ops\n"); | ||
3044 | + return s->file; | ||
3045 | +} | ||
3046 | + | ||
3047 | +QEMUFile *qemu_popen_cmd(const char *command, const char *mode) | ||
3048 | +{ | ||
3049 | + FILE *popen_file; | ||
3050 | + | ||
3051 | + popen_file = popen(command, mode); | ||
3052 | + if(popen_file == NULL) { | ||
3053 | + return NULL; | ||
3054 | + } | ||
3055 | + | ||
3056 | + return qemu_popen(popen_file, mode); | ||
3057 | +} | ||
3058 | + | ||
2995 | QEMUFile *qemu_fopen_socket(int fd) | 3059 | QEMUFile *qemu_fopen_socket(int fd) |
2996 | { | 3060 | { |
2997 | QEMUFileSocket *s = qemu_mallocz(sizeof(QEMUFileSocket)); | 3061 | QEMUFileSocket *s = qemu_mallocz(sizeof(QEMUFileSocket)); |