]> git.proxmox.com Git - mirror_qemu.git/blame - migration/vmstate.c
migration: Send requested page directly in rp-return thread
[mirror_qemu.git] / migration / vmstate.c
CommitLineData
576d1abc
JQ
1/*
2 * VMState interpreter
3 *
4 * Copyright (c) 2009-2017 Red Hat Inc
5 *
6 * Authors:
7 * Juan Quintela <quintela@redhat.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
11 */
12
1393a485 13#include "qemu/osdep.h"
6666c96a 14#include "migration.h"
b6fcfa59 15#include "migration/vmstate.h"
53d37d36 16#include "savevm.h"
3ddba9a9 17#include "qapi/qmp/json-writer.h"
08a0aee1 18#include "qemu-file.h"
b6fcfa59 19#include "qemu/bitops.h"
6a64b644 20#include "qemu/error-report.h"
9013dca5 21#include "trace.h"
b6fcfa59 22
f3cadd39 23static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
3ddba9a9 24 void *opaque, JSONWriter *vmdesc);
b6fcfa59
EH
25static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
26 void *opaque);
27
03fee66f 28static int vmstate_n_elems(void *opaque, const VMStateField *field)
35fc1f71
MT
29{
30 int n_elems = 1;
31
32 if (field->flags & VMS_ARRAY) {
33 n_elems = field->num;
34 } else if (field->flags & VMS_VARRAY_INT32) {
395cb450 35 n_elems = *(int32_t *)(opaque + field->num_offset);
35fc1f71 36 } else if (field->flags & VMS_VARRAY_UINT32) {
395cb450 37 n_elems = *(uint32_t *)(opaque + field->num_offset);
35fc1f71 38 } else if (field->flags & VMS_VARRAY_UINT16) {
395cb450 39 n_elems = *(uint16_t *)(opaque + field->num_offset);
35fc1f71 40 } else if (field->flags & VMS_VARRAY_UINT8) {
395cb450 41 n_elems = *(uint8_t *)(opaque + field->num_offset);
35fc1f71
MT
42 }
43
b47d3af7
JQ
44 if (field->flags & VMS_MULTIPLY_ELEMENTS) {
45 n_elems *= field->num;
46 }
47
023ad1a6 48 trace_vmstate_n_elems(field->name, n_elems);
35fc1f71
MT
49 return n_elems;
50}
51
03fee66f 52static int vmstate_size(void *opaque, const VMStateField *field)
35fc1f71
MT
53{
54 int size = field->size;
55
56 if (field->flags & VMS_VBUFFER) {
395cb450 57 size = *(int32_t *)(opaque + field->size_offset);
35fc1f71
MT
58 if (field->flags & VMS_MULTIPLY) {
59 size *= field->size;
60 }
61 }
62
63 return size;
64}
65
03fee66f
MAL
66static void vmstate_handle_alloc(void *ptr, const VMStateField *field,
67 void *opaque)
35fc1f71 68{
cbfda0e6
HP
69 if (field->flags & VMS_POINTER && field->flags & VMS_ALLOC) {
70 gsize size = vmstate_size(opaque, field);
71 size *= vmstate_n_elems(opaque, field);
72 if (size) {
73 *(void **)ptr = g_malloc(size);
f32935ea 74 }
35fc1f71 75 }
35fc1f71
MT
76}
77
b6fcfa59
EH
78int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
79 void *opaque, int version_id)
80{
03fee66f 81 const VMStateField *field = vmsd->fields;
a5df2a02 82 int ret = 0;
b6fcfa59 83
a5df2a02 84 trace_vmstate_load_state(vmsd->name, version_id);
b6fcfa59 85 if (version_id > vmsd->version_id) {
cde7ee59
JD
86 error_report("%s: incoming version_id %d is too new "
87 "for local version_id %d",
88 vmsd->name, version_id, vmsd->version_id);
a5df2a02 89 trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL);
b6fcfa59
EH
90 return -EINVAL;
91 }
b6fcfa59 92 if (version_id < vmsd->minimum_version_id) {
cde7ee59
JD
93 error_report("%s: incoming version_id %d is too old "
94 "for local minimum version_id %d",
95 vmsd->name, version_id, vmsd->minimum_version_id);
a5df2a02 96 trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
767adce2 97 return -EINVAL;
b6fcfa59
EH
98 }
99 if (vmsd->pre_load) {
100 int ret = vmsd->pre_load(opaque);
101 if (ret) {
102 return ret;
103 }
104 }
105 while (field->name) {
a5df2a02 106 trace_vmstate_load_state_field(vmsd->name, field->name);
b6fcfa59
EH
107 if ((field->field_exists &&
108 field->field_exists(opaque, version_id)) ||
109 (!field->field_exists &&
110 field->version_id <= version_id)) {
cbfda0e6 111 void *first_elem = opaque + field->offset;
35fc1f71
MT
112 int i, n_elems = vmstate_n_elems(opaque, field);
113 int size = vmstate_size(opaque, field);
114
cbfda0e6
HP
115 vmstate_handle_alloc(first_elem, field, opaque);
116 if (field->flags & VMS_POINTER) {
117 first_elem = *(void **)first_elem;
e1e686c1 118 assert(first_elem || !n_elems || !size);
cbfda0e6 119 }
b6fcfa59 120 for (i = 0; i < n_elems; i++) {
e84641f7 121 void *curr_elem = first_elem + size * i;
b6fcfa59
EH
122
123 if (field->flags & VMS_ARRAY_OF_POINTER) {
e84641f7 124 curr_elem = *(void **)curr_elem;
b6fcfa59 125 }
e1e686c1 126 if (!curr_elem && size) {
07d4e691
HP
127 /* if null pointer check placeholder and do not follow */
128 assert(field->flags & VMS_ARRAY_OF_POINTER);
129 ret = vmstate_info_nullptr.get(f, curr_elem, size, NULL);
130 } else if (field->flags & VMS_STRUCT) {
e84641f7 131 ret = vmstate_load_state(f, field->vmsd, curr_elem,
b6fcfa59 132 field->vmsd->version_id);
2dc6660b
CM
133 } else if (field->flags & VMS_VSTRUCT) {
134 ret = vmstate_load_state(f, field->vmsd, curr_elem,
135 field->struct_version_id);
b6fcfa59 136 } else {
e84641f7 137 ret = field->info->get(f, curr_elem, size, field);
b6fcfa59 138 }
13cde508
JQ
139 if (ret >= 0) {
140 ret = qemu_file_get_error(f);
141 }
b6fcfa59 142 if (ret < 0) {
13cde508 143 qemu_file_set_error(f, ret);
a1771070
DDAG
144 error_report("Failed to load %s:%s", vmsd->name,
145 field->name);
9013dca5 146 trace_vmstate_load_field_error(field->name, ret);
b6fcfa59
EH
147 return ret;
148 }
149 }
5bf81c8d 150 } else if (field->flags & VMS_MUST_EXIST) {
6a64b644
DDAG
151 error_report("Input validation failed: %s/%s",
152 vmsd->name, field->name);
5bf81c8d 153 return -1;
b6fcfa59
EH
154 }
155 field++;
156 }
157 ret = vmstate_subsection_load(f, vmsd, opaque);
158 if (ret != 0) {
159 return ret;
160 }
161 if (vmsd->post_load) {
a5df2a02 162 ret = vmsd->post_load(opaque, version_id);
b6fcfa59 163 }
a5df2a02
DDAG
164 trace_vmstate_load_state_end(vmsd->name, "end", ret);
165 return ret;
b6fcfa59
EH
166}
167
03fee66f
MAL
168static int vmfield_name_num(const VMStateField *start,
169 const VMStateField *search)
8118f095 170{
03fee66f 171 const VMStateField *field;
8118f095
AG
172 int found = 0;
173
174 for (field = start; field->name; field++) {
175 if (!strcmp(field->name, search->name)) {
176 if (field == search) {
177 return found;
178 }
179 found++;
180 }
181 }
182
183 return -1;
184}
185
03fee66f
MAL
186static bool vmfield_name_is_unique(const VMStateField *start,
187 const VMStateField *search)
8118f095 188{
03fee66f 189 const VMStateField *field;
8118f095
AG
190 int found = 0;
191
192 for (field = start; field->name; field++) {
193 if (!strcmp(field->name, search->name)) {
194 found++;
195 /* name found more than once, so it's not unique */
196 if (found > 1) {
197 return false;
198 }
199 }
200 }
201
202 return true;
203}
204
03fee66f 205static const char *vmfield_get_type_name(const VMStateField *field)
8118f095
AG
206{
207 const char *type = "unknown";
208
209 if (field->flags & VMS_STRUCT) {
210 type = "struct";
2dc6660b
CM
211 } else if (field->flags & VMS_VSTRUCT) {
212 type = "vstruct";
8118f095
AG
213 } else if (field->info->name) {
214 type = field->info->name;
215 }
216
217 return type;
218}
219
03fee66f 220static bool vmsd_can_compress(const VMStateField *field)
8118f095
AG
221{
222 if (field->field_exists) {
223 /* Dynamically existing fields mess up compression */
224 return false;
225 }
226
227 if (field->flags & VMS_STRUCT) {
03fee66f 228 const VMStateField *sfield = field->vmsd->fields;
8118f095
AG
229 while (sfield->name) {
230 if (!vmsd_can_compress(sfield)) {
231 /* Child elements can't compress, so can't we */
232 return false;
233 }
234 sfield++;
235 }
236
237 if (field->vmsd->subsections) {
238 /* Subsections may come and go, better don't compress */
239 return false;
240 }
241 }
242
243 return true;
244}
245
3ddba9a9
MA
246static void vmsd_desc_field_start(const VMStateDescription *vmsd,
247 JSONWriter *vmdesc,
03fee66f 248 const VMStateField *field, int i, int max)
8118f095
AG
249{
250 char *name, *old_name;
251 bool is_array = max > 1;
252 bool can_compress = vmsd_can_compress(field);
253
254 if (!vmdesc) {
255 return;
256 }
257
258 name = g_strdup(field->name);
259
260 /* Field name is not unique, need to make it unique */
261 if (!vmfield_name_is_unique(vmsd->fields, field)) {
262 int num = vmfield_name_num(vmsd->fields, field);
263 old_name = name;
264 name = g_strdup_printf("%s[%d]", name, num);
265 g_free(old_name);
266 }
267
3ddba9a9
MA
268 json_writer_start_object(vmdesc, NULL);
269 json_writer_str(vmdesc, "name", name);
8118f095
AG
270 if (is_array) {
271 if (can_compress) {
3ddba9a9 272 json_writer_int64(vmdesc, "array_len", max);
8118f095 273 } else {
3ddba9a9 274 json_writer_int64(vmdesc, "index", i);
8118f095
AG
275 }
276 }
3ddba9a9 277 json_writer_str(vmdesc, "type", vmfield_get_type_name(field));
8118f095
AG
278
279 if (field->flags & VMS_STRUCT) {
3ddba9a9 280 json_writer_start_object(vmdesc, "struct");
8118f095
AG
281 }
282
283 g_free(name);
284}
285
3ddba9a9
MA
286static void vmsd_desc_field_end(const VMStateDescription *vmsd,
287 JSONWriter *vmdesc,
03fee66f 288 const VMStateField *field, size_t size, int i)
8118f095
AG
289{
290 if (!vmdesc) {
291 return;
292 }
293
294 if (field->flags & VMS_STRUCT) {
295 /* We printed a struct in between, close its child object */
3ddba9a9 296 json_writer_end_object(vmdesc);
8118f095
AG
297 }
298
3ddba9a9
MA
299 json_writer_int64(vmdesc, "size", size);
300 json_writer_end_object(vmdesc);
8118f095
AG
301}
302
df896152
JQ
303
304bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque)
305{
306 if (vmsd->needed && !vmsd->needed(opaque)) {
307 /* optional section not needed */
308 return false;
309 }
310 return true;
311}
312
313
551dbd08 314int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
3ddba9a9 315 void *opaque, JSONWriter *vmdesc_id)
2dc6660b
CM
316{
317 return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id);
318}
319
320int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
3ddba9a9 321 void *opaque, JSONWriter *vmdesc, int version_id)
b6fcfa59 322{
551dbd08 323 int ret = 0;
03fee66f 324 const VMStateField *field = vmsd->fields;
b6fcfa59 325
46a02a74
DDAG
326 trace_vmstate_save_state_top(vmsd->name);
327
b6fcfa59 328 if (vmsd->pre_save) {
551dbd08
DDAG
329 ret = vmsd->pre_save(opaque);
330 trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
331 if (ret) {
332 error_report("pre-save failed: %s", vmsd->name);
333 return ret;
334 }
b6fcfa59 335 }
8118f095
AG
336
337 if (vmdesc) {
3ddba9a9
MA
338 json_writer_str(vmdesc, "vmsd_name", vmsd->name);
339 json_writer_int64(vmdesc, "version", version_id);
340 json_writer_start_array(vmdesc, "fields");
8118f095
AG
341 }
342
b6fcfa59 343 while (field->name) {
2dc6660b
CM
344 if ((field->field_exists &&
345 field->field_exists(opaque, version_id)) ||
346 (!field->field_exists &&
347 field->version_id <= version_id)) {
cbfda0e6 348 void *first_elem = opaque + field->offset;
35fc1f71
MT
349 int i, n_elems = vmstate_n_elems(opaque, field);
350 int size = vmstate_size(opaque, field);
8118f095 351 int64_t old_offset, written_bytes;
3ddba9a9 352 JSONWriter *vmdesc_loop = vmdesc;
35fc1f71 353
46a02a74 354 trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems);
cbfda0e6
HP
355 if (field->flags & VMS_POINTER) {
356 first_elem = *(void **)first_elem;
e1e686c1 357 assert(first_elem || !n_elems || !size);
cbfda0e6 358 }
b6fcfa59 359 for (i = 0; i < n_elems; i++) {
e84641f7 360 void *curr_elem = first_elem + size * i;
b6fcfa59 361
8118f095 362 vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems);
fbfa6404 363 old_offset = qemu_file_total_transferred_fast(f);
b6fcfa59 364 if (field->flags & VMS_ARRAY_OF_POINTER) {
e84641f7
HP
365 assert(curr_elem);
366 curr_elem = *(void **)curr_elem;
b6fcfa59 367 }
e1e686c1 368 if (!curr_elem && size) {
07d4e691
HP
369 /* if null pointer write placeholder and do not follow */
370 assert(field->flags & VMS_ARRAY_OF_POINTER);
88b0faf1
DDAG
371 ret = vmstate_info_nullptr.put(f, curr_elem, size, NULL,
372 NULL);
07d4e691 373 } else if (field->flags & VMS_STRUCT) {
88b0faf1
DDAG
374 ret = vmstate_save_state(f, field->vmsd, curr_elem,
375 vmdesc_loop);
2dc6660b
CM
376 } else if (field->flags & VMS_VSTRUCT) {
377 ret = vmstate_save_state_v(f, field->vmsd, curr_elem,
378 vmdesc_loop,
379 field->struct_version_id);
b6fcfa59 380 } else {
88b0faf1
DDAG
381 ret = field->info->put(f, curr_elem, size, field,
382 vmdesc_loop);
383 }
384 if (ret) {
385 error_report("Save of field %s/%s failed",
386 vmsd->name, field->name);
8c07559f
AL
387 if (vmsd->post_save) {
388 vmsd->post_save(opaque);
389 }
88b0faf1 390 return ret;
b6fcfa59 391 }
8118f095 392
fbfa6404
DB
393 written_bytes = qemu_file_total_transferred_fast(f) -
394 old_offset;
8118f095
AG
395 vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i);
396
397 /* Compressed arrays only care about the first element */
398 if (vmdesc_loop && vmsd_can_compress(field)) {
399 vmdesc_loop = NULL;
400 }
b6fcfa59 401 }
5bf81c8d
MT
402 } else {
403 if (field->flags & VMS_MUST_EXIST) {
6a64b644 404 error_report("Output state validation failed: %s/%s",
5bf81c8d
MT
405 vmsd->name, field->name);
406 assert(!(field->flags & VMS_MUST_EXIST));
407 }
b6fcfa59
EH
408 }
409 field++;
410 }
8118f095
AG
411
412 if (vmdesc) {
3ddba9a9 413 json_writer_end_array(vmdesc);
8118f095
AG
414 }
415
8c07559f
AL
416 ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc);
417
418 if (vmsd->post_save) {
419 int ps_ret = vmsd->post_save(opaque);
420 if (!ret) {
421 ret = ps_ret;
422 }
423 }
424 return ret;
b6fcfa59
EH
425}
426
427static const VMStateDescription *
5cd8cada 428vmstate_get_subsection(const VMStateDescription **sub, char *idstr)
b6fcfa59 429{
6f4923fc 430 while (sub && *sub) {
5cd8cada
JQ
431 if (strcmp(idstr, (*sub)->name) == 0) {
432 return *sub;
b6fcfa59
EH
433 }
434 sub++;
435 }
436 return NULL;
437}
438
439static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
440 void *opaque)
441{
a5df2a02
DDAG
442 trace_vmstate_subsection_load(vmsd->name);
443
b6fcfa59 444 while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
7c1e52ba 445 char idstr[256], *idstr_ret;
b6fcfa59
EH
446 int ret;
447 uint8_t version_id, len, size;
448 const VMStateDescription *sub_vmsd;
449
450 len = qemu_peek_byte(f, 1);
451 if (len < strlen(vmsd->name) + 1) {
452 /* subsection name has be be "section_name/a" */
023ad1a6 453 trace_vmstate_subsection_load_bad(vmsd->name, "(short)", "");
b6fcfa59
EH
454 return 0;
455 }
7c1e52ba 456 size = qemu_peek_buffer(f, (uint8_t **)&idstr_ret, len, 2);
b6fcfa59 457 if (size != len) {
023ad1a6 458 trace_vmstate_subsection_load_bad(vmsd->name, "(peek fail)", "");
b6fcfa59
EH
459 return 0;
460 }
7c1e52ba 461 memcpy(idstr, idstr_ret, size);
b6fcfa59
EH
462 idstr[size] = 0;
463
464 if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
023ad1a6
DDAG
465 trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(prefix)");
466 /* it doesn't have a valid subsection name */
b6fcfa59
EH
467 return 0;
468 }
469 sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
470 if (sub_vmsd == NULL) {
023ad1a6 471 trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(lookup)");
b6fcfa59
EH
472 return -ENOENT;
473 }
474 qemu_file_skip(f, 1); /* subsection */
475 qemu_file_skip(f, 1); /* len */
476 qemu_file_skip(f, len); /* idstr */
477 version_id = qemu_get_be32(f);
478
479 ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
480 if (ret) {
023ad1a6 481 trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(child)");
b6fcfa59
EH
482 return ret;
483 }
484 }
a5df2a02
DDAG
485
486 trace_vmstate_subsection_load_good(vmsd->name);
b6fcfa59
EH
487 return 0;
488}
489
f3cadd39 490static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
3ddba9a9 491 void *opaque, JSONWriter *vmdesc)
b6fcfa59 492{
5cd8cada 493 const VMStateDescription **sub = vmsd->subsections;
f2dd7edd 494 bool vmdesc_has_subsections = false;
f3cadd39 495 int ret = 0;
b6fcfa59 496
46a02a74 497 trace_vmstate_subsection_save_top(vmsd->name);
6f4923fc
PM
498 while (sub && *sub) {
499 if (vmstate_save_needed(*sub, opaque)) {
46a02a74 500 const VMStateDescription *vmsdsub = *sub;
b6fcfa59
EH
501 uint8_t len;
502
46a02a74 503 trace_vmstate_subsection_save_loop(vmsd->name, vmsdsub->name);
8118f095
AG
504 if (vmdesc) {
505 /* Only create subsection array when we have any */
f2dd7edd 506 if (!vmdesc_has_subsections) {
3ddba9a9 507 json_writer_start_array(vmdesc, "subsections");
f2dd7edd 508 vmdesc_has_subsections = true;
8118f095
AG
509 }
510
3ddba9a9 511 json_writer_start_object(vmdesc, NULL);
8118f095
AG
512 }
513
b6fcfa59 514 qemu_put_byte(f, QEMU_VM_SUBSECTION);
46a02a74 515 len = strlen(vmsdsub->name);
b6fcfa59 516 qemu_put_byte(f, len);
46a02a74
DDAG
517 qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len);
518 qemu_put_be32(f, vmsdsub->version_id);
f3cadd39
DDAG
519 ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc);
520 if (ret) {
521 return ret;
522 }
8118f095
AG
523
524 if (vmdesc) {
3ddba9a9 525 json_writer_end_object(vmdesc);
8118f095 526 }
b6fcfa59
EH
527 }
528 sub++;
529 }
8118f095 530
f2dd7edd 531 if (vmdesc_has_subsections) {
3ddba9a9 532 json_writer_end_array(vmdesc);
8118f095 533 }
f3cadd39
DDAG
534
535 return ret;
b6fcfa59 536}