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
hw/hw.h
... | ... | @@ -35,6 +35,8 @@ QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, |
35 | 35 | QEMUFileRateLimit *rate_limit); |
36 | 36 | QEMUFile *qemu_fopen(const char *filename, const char *mode); |
37 | 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 | 40 | void qemu_fflush(QEMUFile *f); |
39 | 41 | int qemu_fclose(QEMUFile *f); |
40 | 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 | 22 | |
23 | 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 | 25 | #ifdef DEBUG_MIGRATION_TCP |
36 | 26 | #define dprintf(fmt, ...) \ |
37 | 27 | do { printf("migration-tcp: " fmt, ## __VA_ARGS__); } while (0) |
... | ... | @@ -40,64 +30,19 @@ typedef struct FdMigrationState |
40 | 30 | do { } while (0) |
41 | 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 | 46 | if (s->fd != -1) { |
102 | 47 | close(s->fd); |
103 | 48 | s->fd = -1; |
... | ... | @@ -105,67 +50,6 @@ static int fd_close(void *opaque) |
105 | 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 | 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 | dprintf("connect completed\n"); |
177 | 61 | do { |
178 | 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 | 65 | if (ret < 0) { |
182 | - tcp_error(s); | |
66 | + migrate_fd_error(s); | |
183 | 67 | return; |
184 | 68 | } |
185 | 69 | |
186 | 70 | qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); |
187 | 71 | |
188 | 72 | if (val == 0) |
189 | - tcp_connect_migrate(s); | |
73 | + migrate_fd_connect(s); | |
190 | 74 | else { |
191 | 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 | 80 | MigrationState *tcp_start_outgoing_migration(const char *host_port, |
... | ... | @@ -247,9 +92,12 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port, |
247 | 92 | if (s == NULL) |
248 | 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 | 102 | s->state = MIG_STATE_ACTIVE; |
255 | 103 | s->detach = !async; |
... | ... | @@ -271,7 +119,7 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port, |
271 | 119 | do { |
272 | 120 | ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)); |
273 | 121 | if (ret == -1) |
274 | - ret = -socket_error(); | |
122 | + ret = -(s->get_error(s)); | |
275 | 123 | |
276 | 124 | if (ret == -EINPROGRESS || ret == -EWOULDBLOCK) |
277 | 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 | 131 | qemu_free(s); |
284 | 132 | return NULL; |
285 | 133 | } else if (ret >= 0) |
286 | - tcp_connect_migrate(s); | |
134 | + migrate_fd_connect(s); | |
287 | 135 | |
288 | 136 | return &s->mig_state; |
289 | 137 | } | ... | ... |
migration.c
... | ... | @@ -14,6 +14,20 @@ |
14 | 14 | #include "qemu-common.h" |
15 | 15 | #include "migration.h" |
16 | 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 | 32 | /* Migration speed throttling */ |
19 | 33 | static uint32_t max_throttle = (32 << 20); |
... | ... | @@ -26,6 +40,10 @@ void qemu_start_incoming_migration(const char *uri) |
26 | 40 | |
27 | 41 | if (strstart(uri, "tcp:", &p)) |
28 | 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 | 47 | else |
30 | 48 | fprintf(stderr, "unknown migration protocol: %s\n", uri); |
31 | 49 | } |
... | ... | @@ -37,6 +55,10 @@ void do_migrate(int detach, const char *uri) |
37 | 55 | |
38 | 56 | if (strstart(uri, "tcp:", &p)) |
39 | 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 | 62 | else |
41 | 63 | term_printf("unknown migration protocol: %s\n", uri); |
42 | 64 | |
... | ... | @@ -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 | 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 | 48 | void qemu_start_incoming_migration(const char *uri); |
33 | 49 | |
34 | 50 | void do_migrate(int detach, const char *uri); |
... | ... | @@ -39,11 +55,44 @@ void do_migrate_set_speed(const char *value); |
39 | 55 | |
40 | 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 | 64 | int tcp_start_incoming_migration(const char *host_port); |
43 | 65 | |
44 | 66 | MigrationState *tcp_start_outgoing_migration(const char *host_port, |
45 | 67 | int64_t bandwidth_limit, |
46 | 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 | 97 | #endif |
49 | 98 | ... | ... |
vl.c
... | ... | @@ -2964,6 +2964,12 @@ struct QEMUFile { |
2964 | 2964 | int has_error; |
2965 | 2965 | }; |
2966 | 2966 | |
2967 | +typedef struct QEMUFilePopen | |
2968 | +{ | |
2969 | + FILE *popen_file; | |
2970 | + QEMUFile *file; | |
2971 | +} QEMUFilePopen; | |
2972 | + | |
2967 | 2973 | typedef struct QEMUFileSocket |
2968 | 2974 | { |
2969 | 2975 | int fd; |
... | ... | @@ -2992,6 +2998,64 @@ static int socket_close(void *opaque) |
2992 | 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 | 3059 | QEMUFile *qemu_fopen_socket(int fd) |
2996 | 3060 | { |
2997 | 3061 | QEMUFileSocket *s = qemu_mallocz(sizeof(QEMUFileSocket)); | ... | ... |