master
c 410 lines 9.7 KB
Raw
1 /*
2 * Copyright (c) 2021-2024 Oracle and/or its affiliates.
3 *
4 * This work is licensed under the terms of the GNU GPL, version 2 or later.
5 * See the COPYING file in the top-level directory.
6 */
7
8 #include "qemu/osdep.h"
9 #include "qemu/error-report.h"
10 #include "qapi/error.h"
11 #include "qemu/error-report.h"
12 #include "hw/vfio/vfio-cpr.h"
13 #include "migration/cpr.h"
14 #include "migration/misc.h"
15 #include "migration/options.h"
16 #include "migration/qemu-file.h"
17 #include "migration/savevm.h"
18 #include "migration/vmstate.h"
19 #include "monitor/monitor.h"
20 #include "system/runstate.h"
21 #include "trace.h"
22
23 /*************************************************************************/
24 /* cpr state container for all information to be saved. */
25
26 CprState cpr_state;
27 static GHashTable *cpr_fds_hash;
28
29 /****************************************************************************/
30
31 typedef struct CprFd {
32 char *name;
33 unsigned int namelen;
34 int id;
35 int fd;
36 QLIST_ENTRY(CprFd) next;
37 } CprFd;
38
39 static const VMStateDescription vmstate_cpr_fd = {
40 .name = "cpr fd",
41 .version_id = 1,
42 .minimum_version_id = 1,
43 .fields = (VMStateField[]) {
44 VMSTATE_UINT32(namelen, CprFd),
45 VMSTATE_VBUFFER_ALLOC_UINT32(name, CprFd, 0, NULL, namelen),
46 VMSTATE_INT32(id, CprFd),
47 VMSTATE_FD(fd, CprFd),
48 VMSTATE_END_OF_LIST()
49 }
50 };
51
52 static guint cpr_fd_hash(gconstpointer v)
53 {
54 const CprFd *elem = v;
55
56 return g_str_hash(elem->name) ^ elem->id;
57 }
58
59 static gboolean cpr_fd_equal(gconstpointer a, gconstpointer b)
60 {
61 const CprFd *elem_a = a;
62 const CprFd *elem_b = b;
63
64 return !strcmp(elem_a->name, elem_b->name) && elem_a->id == elem_b->id;
65 }
66
67 static void cpr_fd_destroy(gpointer data)
68 {
69 CprFd *elem = data;
70
71 g_free(elem->name);
72 g_free(elem);
73 }
74
75 static GHashTable *get_cpr_fds_hash(void)
76 {
77 if (!cpr_fds_hash) {
78 cpr_fds_hash = g_hash_table_new_full(cpr_fd_hash, cpr_fd_equal,
79 cpr_fd_destroy, NULL);
80 }
81
82 return cpr_fds_hash;
83 }
84
85 static void cpr_fd_hash_insert(CprFd *elem)
86 {
87 /* Use the same CprFd as key and value. */
88 g_hash_table_insert(get_cpr_fds_hash(), elem, elem);
89 }
90
91 static int cpr_fd_pre_save(void *opaque)
92 {
93 CprState *state = (CprState *)opaque;
94 GHashTableIter iter;
95 CprFd *elem;
96
97 QLIST_INIT(&state->fds);
98
99 g_hash_table_iter_init(&iter, get_cpr_fds_hash());
100 while (g_hash_table_iter_next(&iter, (gpointer *)&elem, NULL)) {
101 QLIST_INSERT_HEAD(&state->fds, elem, next);
102 }
103
104 return 0;
105 }
106
107 static int cpr_fd_post_load(void *opaque, int version_id)
108 {
109 CprState *state = (CprState *)opaque;
110 CprFd *elem;
111
112 while ((elem = QLIST_FIRST(&state->fds))) {
113 QLIST_REMOVE(elem, next);
114
115 /*
116 * Preserve legacy QLIST lookup semantics if duplicate keys exist in
117 * the incoming stream: the first matching entry wins.
118 */
119 if (g_hash_table_contains(get_cpr_fds_hash(), elem)) {
120 cpr_fd_destroy(elem);
121 continue;
122 }
123
124 cpr_fd_hash_insert(elem);
125 }
126
127 return 0;
128 }
129
130 void cpr_save_fd(const char *name, int id, int fd)
131 {
132 CprFd *elem = g_new0(CprFd, 1);
133
134 trace_cpr_save_fd(name, id, fd);
135 elem->name = g_strdup(name);
136 elem->namelen = strlen(name) + 1;
137 elem->id = id;
138 elem->fd = fd;
139 cpr_fd_hash_insert(elem);
140 }
141
142 static CprFd *find_fd(const char *name, int id)
143 {
144 CprFd key = {
145 .name = (char *)name,
146 .id = id,
147 };
148
149 return g_hash_table_lookup(get_cpr_fds_hash(), &key);
150 }
151
152 void cpr_delete_fd(const char *name, int id)
153 {
154 CprFd key = {
155 .name = (char *)name,
156 .id = id,
157 };
158
159 g_hash_table_remove(get_cpr_fds_hash(), &key);
160
161 trace_cpr_delete_fd(name, id);
162 }
163
164 int cpr_find_fd(const char *name, int id)
165 {
166 CprFd *elem = find_fd(name, id);
167 int fd = elem ? elem->fd : -1;
168
169 trace_cpr_find_fd(name, id, fd);
170 return fd;
171 }
172
173 void cpr_resave_fd(const char *name, int id, int fd)
174 {
175 CprFd *elem = find_fd(name, id);
176 int old_fd = elem ? elem->fd : -1;
177
178 if (old_fd < 0) {
179 cpr_save_fd(name, id, fd);
180 } else if (old_fd != fd) {
181 error_report("internal error: cpr fd '%s' id %d value %d "
182 "already saved with a different value %d",
183 name, id, fd, old_fd);
184 g_assert_not_reached();
185 }
186 }
187
188 int cpr_open_fd(const char *path, int flags, const char *name, int id,
189 Error **errp)
190 {
191 int fd = cpr_find_fd(name, id);
192
193 if (fd < 0) {
194 fd = qemu_open(path, flags, errp);
195 if (fd >= 0) {
196 cpr_save_fd(name, id, fd);
197 }
198 }
199 return fd;
200 }
201
202 bool cpr_walk_fd(cpr_walk_fd_cb cb)
203 {
204 GHashTableIter iter;
205 CprFd *elem;
206
207 g_hash_table_iter_init(&iter, get_cpr_fds_hash());
208 while (g_hash_table_iter_next(&iter, (gpointer *)&elem, NULL)) {
209 g_assert(elem->fd >= 0);
210 if (!cb(elem->fd)) {
211 return false;
212 }
213 }
214 return true;
215 }
216
217 /*************************************************************************/
218 static const VMStateDescription vmstate_cpr_state = {
219 .name = CPR_STATE,
220 .version_id = 1,
221 .minimum_version_id = 1,
222 .pre_save = cpr_fd_pre_save,
223 .post_load = cpr_fd_post_load,
224 .fields = (VMStateField[]) {
225 VMSTATE_QLIST_V(fds, CprState, 1, vmstate_cpr_fd, CprFd, next),
226 VMSTATE_END_OF_LIST()
227 },
228 .subsections = (const VMStateDescription * const []) {
229 &vmstate_cpr_vfio_devices,
230 NULL
231 }
232 };
233 /*************************************************************************/
234
235 static QEMUFile *cpr_state_file;
236
237 QIOChannel *cpr_state_ioc(void)
238 {
239 return qemu_file_get_ioc(cpr_state_file);
240 }
241
242 static MigMode incoming_mode = MIG_MODE_NONE;
243
244 MigMode cpr_get_incoming_mode(void)
245 {
246 return incoming_mode;
247 }
248
249 void cpr_set_incoming_mode(MigMode mode)
250 {
251 incoming_mode = mode;
252 }
253
254 bool cpr_is_incoming(void)
255 {
256 return incoming_mode != MIG_MODE_NONE;
257 }
258
259 bool cpr_state_save(MigrationChannel *channel, Error **errp)
260 {
261 QEMUFile *f;
262 MigMode mode = migrate_mode();
263
264 trace_cpr_state_save(MigMode_str(mode));
265
266 if (mode == MIG_MODE_CPR_TRANSFER) {
267 g_assert(channel);
268 f = cpr_transfer_output(channel, errp);
269 } else if (mode == MIG_MODE_CPR_EXEC) {
270 f = cpr_exec_output(errp);
271 } else {
272 return true;
273 }
274 if (!f) {
275 return false;
276 }
277
278 qemu_put_be32(f, QEMU_CPR_FILE_MAGIC);
279 qemu_put_be32(f, QEMU_CPR_FILE_VERSION);
280
281 if (!vmstate_save_vmsd(f, &vmstate_cpr_state, &cpr_state, 0, errp)) {
282 qemu_fclose(f);
283 return false;
284 }
285
286 if (migrate_mode() == MIG_MODE_CPR_EXEC) {
287 if (!cpr_exec_persist_state(f, errp)) {
288 qemu_fclose(f);
289 return false;
290 }
291 }
292
293 /*
294 * Close the socket only partially so we can later detect when the other
295 * end closes by getting a HUP event.
296 */
297 qemu_fflush(f);
298 qio_channel_shutdown(qemu_file_get_ioc(f), QIO_CHANNEL_SHUTDOWN_WRITE,
299 NULL);
300 cpr_state_file = f;
301 return true;
302 }
303
304 bool cpr_state_load(MigrationChannel *channel, Error **errp)
305 {
306 uint32_t v;
307 QEMUFile *f;
308 MigMode mode = 0;
309
310 if (cpr_exec_has_state()) {
311 mode = MIG_MODE_CPR_EXEC;
312 f = cpr_exec_input(errp);
313 if (channel) {
314 warn_report("ignoring cpr channel for migration mode cpr-exec");
315 }
316 } else if (channel) {
317 mode = MIG_MODE_CPR_TRANSFER;
318 cpr_set_incoming_mode(mode);
319 f = cpr_transfer_input(channel, errp);
320 } else {
321 return true;
322 }
323 if (!f) {
324 return false;
325 }
326
327 trace_cpr_state_load(MigMode_str(mode));
328 cpr_set_incoming_mode(mode);
329
330 v = qemu_get_be32(f);
331 if (v != QEMU_CPR_FILE_MAGIC) {
332 error_setg(errp, "Not a migration stream (bad magic %x)", v);
333 qemu_fclose(f);
334 return false;
335 }
336 v = qemu_get_be32(f);
337 if (v != QEMU_CPR_FILE_VERSION) {
338 error_setg(errp, "Unsupported migration stream version %d", v);
339 qemu_fclose(f);
340 return false;
341 }
342
343 if (!vmstate_load_vmsd(f, &vmstate_cpr_state, &cpr_state, 1, errp)) {
344 qemu_fclose(f);
345 return false;
346 }
347
348 if (migrate_mode() == MIG_MODE_CPR_EXEC) {
349 /* Set cloexec to prevent fd leaks from fork until the next cpr-exec */
350 cpr_exec_unpreserve_fds();
351 }
352
353 /*
354 * Let the caller decide when to close the socket (and generate a HUP event
355 * for the sending side).
356 */
357 cpr_state_file = f;
358
359 return true;
360 }
361
362 void cpr_state_close(void)
363 {
364 if (cpr_state_file) {
365 qemu_fclose(cpr_state_file);
366 cpr_state_file = NULL;
367 }
368 }
369
370 bool cpr_incoming_needed(void *opaque)
371 {
372 MigMode mode = migrate_mode();
373 return mode == MIG_MODE_CPR_TRANSFER || mode == MIG_MODE_CPR_EXEC;
374 }
375
376 /*
377 * cpr_get_fd_param: find a descriptor and return its value.
378 *
379 * @name: CPR name for the descriptor
380 * @fdname: An integer-valued string, or a name passed to a getfd command
381 * @index: CPR index of the descriptor
382 * @errp: returned error message
383 *
384 * If CPR is not being performed, then use @fdname to find the fd.
385 * If CPR is being performed, then ignore @fdname, and look for @name
386 * and @index in CPR state.
387 *
388 * On success returns the fd value, else returns -1.
389 */
390 int cpr_get_fd_param(const char *name, const char *fdname, int index,
391 Error **errp)
392 {
393 ERRP_GUARD();
394 int fd;
395
396 if (cpr_is_incoming()) {
397 fd = cpr_find_fd(name, index);
398 if (fd < 0) {
399 error_setg(errp, "cannot find saved value for fd %s", fdname);
400 }
401 } else {
402 fd = monitor_fd_param(monitor_cur(), fdname, errp);
403 if (fd >= 0) {
404 cpr_save_fd(name, index, fd);
405 } else {
406 error_prepend(errp, "Could not parse object fd %s:", fdname);
407 }
408 }
409 return fd;
410 }