|
1 /* |
|
2 * QEMU live migration |
|
3 * |
|
4 * Copyright IBM, Corp. 2008 |
|
5 * |
|
6 * Authors: |
|
7 * Anthony Liguori <aliguori@us.ibm.com> |
|
8 * |
|
9 * This work is licensed under the terms of the GNU GPL, version 2. See |
|
10 * the COPYING file in the top-level directory. |
|
11 * |
|
12 */ |
|
13 |
|
14 #include "qemu-common.h" |
|
15 #include "qemu_socket.h" |
|
16 #include "migration.h" |
|
17 #include "qemu-char.h" |
|
18 #include "sysemu.h" |
|
19 #include "console.h" |
|
20 #include "buffered_file.h" |
|
21 #include "block.h" |
|
22 |
|
23 //#define DEBUG_MIGRATION_TCP |
|
24 |
|
25 #ifdef DEBUG_MIGRATION_TCP |
|
26 #define dprintf(fmt, ...) \ |
|
27 do { printf("migration-tcp: " fmt, ## __VA_ARGS__); } while (0) |
|
28 #else |
|
29 #define dprintf(fmt, ...) \ |
|
30 do { } while (0) |
|
31 #endif |
|
32 |
|
33 static int socket_errno(FdMigrationState *s) |
|
34 { |
|
35 return socket_error(); |
|
36 } |
|
37 |
|
38 static int socket_write(FdMigrationState *s, const void * buf, size_t size) |
|
39 { |
|
40 return send(s->fd, buf, size, 0); |
|
41 } |
|
42 |
|
43 static int tcp_close(FdMigrationState *s) |
|
44 { |
|
45 dprintf("tcp_close\n"); |
|
46 if (s->fd != -1) { |
|
47 close(s->fd); |
|
48 s->fd = -1; |
|
49 } |
|
50 return 0; |
|
51 } |
|
52 |
|
53 |
|
54 static void tcp_wait_for_connect(void *opaque) |
|
55 { |
|
56 FdMigrationState *s = opaque; |
|
57 int val, ret; |
|
58 socklen_t valsize = sizeof(val); |
|
59 |
|
60 dprintf("connect completed\n"); |
|
61 do { |
|
62 ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &val, &valsize); |
|
63 } while (ret == -1 && (s->get_error(s)) == EINTR); |
|
64 |
|
65 if (ret < 0) { |
|
66 migrate_fd_error(s); |
|
67 return; |
|
68 } |
|
69 |
|
70 qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); |
|
71 |
|
72 if (val == 0) |
|
73 migrate_fd_connect(s); |
|
74 else { |
|
75 dprintf("error connecting %d\n", val); |
|
76 migrate_fd_error(s); |
|
77 } |
|
78 } |
|
79 |
|
80 MigrationState *tcp_start_outgoing_migration(const char *host_port, |
|
81 int64_t bandwidth_limit, |
|
82 int async) |
|
83 { |
|
84 struct sockaddr_in addr; |
|
85 FdMigrationState *s; |
|
86 int ret; |
|
87 |
|
88 if (parse_host_port(&addr, host_port) < 0) |
|
89 return NULL; |
|
90 |
|
91 s = qemu_mallocz(sizeof(*s)); |
|
92 if (s == NULL) |
|
93 return NULL; |
|
94 |
|
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; |
|
101 |
|
102 s->state = MIG_STATE_ACTIVE; |
|
103 s->detach = !async; |
|
104 s->bandwidth_limit = bandwidth_limit; |
|
105 s->fd = socket(PF_INET, SOCK_STREAM, 0); |
|
106 if (s->fd == -1) { |
|
107 qemu_free(s); |
|
108 return NULL; |
|
109 } |
|
110 |
|
111 socket_set_nonblock(s->fd); |
|
112 |
|
113 if (s->detach == 1) { |
|
114 dprintf("detaching from monitor\n"); |
|
115 monitor_suspend(); |
|
116 s->detach = 2; |
|
117 } |
|
118 |
|
119 do { |
|
120 ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)); |
|
121 if (ret == -1) |
|
122 ret = -(s->get_error(s)); |
|
123 |
|
124 if (ret == -EINPROGRESS || ret == -EWOULDBLOCK) |
|
125 qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s); |
|
126 } while (ret == -EINTR); |
|
127 |
|
128 if (ret < 0 && ret != -EINPROGRESS && ret != -EWOULDBLOCK) { |
|
129 dprintf("connect failed\n"); |
|
130 close(s->fd); |
|
131 qemu_free(s); |
|
132 return NULL; |
|
133 } else if (ret >= 0) |
|
134 migrate_fd_connect(s); |
|
135 |
|
136 return &s->mig_state; |
|
137 } |
|
138 |
|
139 static void tcp_accept_incoming_migration(void *opaque) |
|
140 { |
|
141 struct sockaddr_in addr; |
|
142 socklen_t addrlen = sizeof(addr); |
|
143 int s = (unsigned long)opaque; |
|
144 QEMUFile *f; |
|
145 int c, ret; |
|
146 |
|
147 do { |
|
148 c = accept(s, (struct sockaddr *)&addr, &addrlen); |
|
149 } while (c == -1 && socket_error() == EINTR); |
|
150 |
|
151 dprintf("accepted migration\n"); |
|
152 |
|
153 if (c == -1) { |
|
154 fprintf(stderr, "could not accept migration connection\n"); |
|
155 return; |
|
156 } |
|
157 |
|
158 f = qemu_fopen_socket(c); |
|
159 if (f == NULL) { |
|
160 fprintf(stderr, "could not qemu_fopen socket\n"); |
|
161 goto out; |
|
162 } |
|
163 |
|
164 vm_stop(0); /* just in case */ |
|
165 ret = qemu_loadvm_state(f); |
|
166 if (ret < 0) { |
|
167 fprintf(stderr, "load of migration failed\n"); |
|
168 goto out_fopen; |
|
169 } |
|
170 qemu_announce_self(); |
|
171 dprintf("successfully loaded vm state\n"); |
|
172 |
|
173 /* we've successfully migrated, close the server socket */ |
|
174 qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL); |
|
175 close(s); |
|
176 |
|
177 vm_start(); |
|
178 |
|
179 out_fopen: |
|
180 qemu_fclose(f); |
|
181 out: |
|
182 close(c); |
|
183 } |
|
184 |
|
185 int tcp_start_incoming_migration(const char *host_port) |
|
186 { |
|
187 struct sockaddr_in addr; |
|
188 int val; |
|
189 int s; |
|
190 |
|
191 if (parse_host_port(&addr, host_port) < 0) { |
|
192 fprintf(stderr, "invalid host/port combination: %s\n", host_port); |
|
193 return -EINVAL; |
|
194 } |
|
195 |
|
196 s = socket(PF_INET, SOCK_STREAM, 0); |
|
197 if (s == -1) |
|
198 return -socket_error(); |
|
199 |
|
200 val = 1; |
|
201 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)); |
|
202 |
|
203 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) |
|
204 goto err; |
|
205 |
|
206 if (listen(s, 1) == -1) |
|
207 goto err; |
|
208 |
|
209 qemu_set_fd_handler2(s, NULL, tcp_accept_incoming_migration, NULL, |
|
210 (void *)(unsigned long)s); |
|
211 |
|
212 return 0; |
|
213 |
|
214 err: |
|
215 close(s); |
|
216 return -socket_error(); |
|
217 } |