vmstate.c 15.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * VMState interpreter
 *
 * Copyright (c) 2009-2017 Red Hat Inc
 *
 * Authors:
 *  Juan Quintela <quintela@redhat.com>
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 */

P
Peter Maydell 已提交
13
#include "qemu/osdep.h"
14
#include "qemu-common.h"
15
#include "migration.h"
16
#include "migration/vmstate.h"
17
#include "migration/savevm.h"
J
Juan Quintela 已提交
18
#include "qemu-file.h"
19
#include "qemu/bitops.h"
20
#include "qemu/error-report.h"
21
#include "trace.h"
22
#include "qjson.h"
23

24 25
static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
                                   void *opaque, QJSON *vmdesc);
26 27 28
static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
                                   void *opaque);

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
static int vmstate_n_elems(void *opaque, VMStateField *field)
{
    int n_elems = 1;

    if (field->flags & VMS_ARRAY) {
        n_elems = field->num;
    } else if (field->flags & VMS_VARRAY_INT32) {
        n_elems = *(int32_t *)(opaque+field->num_offset);
    } else if (field->flags & VMS_VARRAY_UINT32) {
        n_elems = *(uint32_t *)(opaque+field->num_offset);
    } else if (field->flags & VMS_VARRAY_UINT16) {
        n_elems = *(uint16_t *)(opaque+field->num_offset);
    } else if (field->flags & VMS_VARRAY_UINT8) {
        n_elems = *(uint8_t *)(opaque+field->num_offset);
    }

45 46 47 48
    if (field->flags & VMS_MULTIPLY_ELEMENTS) {
        n_elems *= field->num;
    }

49
    trace_vmstate_n_elems(field->name, n_elems);
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
    return n_elems;
}

static int vmstate_size(void *opaque, VMStateField *field)
{
    int size = field->size;

    if (field->flags & VMS_VBUFFER) {
        size = *(int32_t *)(opaque+field->size_offset);
        if (field->flags & VMS_MULTIPLY) {
            size *= field->size;
        }
    }

    return size;
}

67
static void vmstate_handle_alloc(void *ptr, VMStateField *field, void *opaque)
68
{
69 70 71 72 73
    if (field->flags & VMS_POINTER && field->flags & VMS_ALLOC) {
        gsize size = vmstate_size(opaque, field);
        size *= vmstate_n_elems(opaque, field);
        if (size) {
            *(void **)ptr = g_malloc(size);
74
        }
75 76 77
    }
}

78 79 80 81
int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
                       void *opaque, int version_id)
{
    VMStateField *field = vmsd->fields;
82
    int ret = 0;
83

84
    trace_vmstate_load_state(vmsd->name, version_id);
85
    if (version_id > vmsd->version_id) {
J
Jianjun Duan 已提交
86 87 88
        error_report("%s: incoming version_id %d is too new "
                     "for local version_id %d",
                     vmsd->name, version_id, vmsd->version_id);
89
        trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL);
90 91 92
        return -EINVAL;
    }
    if  (version_id < vmsd->minimum_version_id) {
93 94
        if (vmsd->load_state_old &&
            version_id >= vmsd->minimum_version_id_old) {
95 96 97
            ret = vmsd->load_state_old(f, opaque, version_id);
            trace_vmstate_load_state_end(vmsd->name, "old path", ret);
            return ret;
98
        }
J
Jianjun Duan 已提交
99 100 101
        error_report("%s: incoming version_id %d is too old "
                     "for local minimum version_id  %d",
                     vmsd->name, version_id, vmsd->minimum_version_id);
102
        trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
103
        return -EINVAL;
104 105 106 107 108 109 110 111
    }
    if (vmsd->pre_load) {
        int ret = vmsd->pre_load(opaque);
        if (ret) {
            return ret;
        }
    }
    while (field->name) {
112
        trace_vmstate_load_state_field(vmsd->name, field->name);
113 114 115 116
        if ((field->field_exists &&
             field->field_exists(opaque, version_id)) ||
            (!field->field_exists &&
             field->version_id <= version_id)) {
117
            void *first_elem = opaque + field->offset;
118 119 120
            int i, n_elems = vmstate_n_elems(opaque, field);
            int size = vmstate_size(opaque, field);

121 122 123
            vmstate_handle_alloc(first_elem, field, opaque);
            if (field->flags & VMS_POINTER) {
                first_elem = *(void **)first_elem;
124
                assert(first_elem || !n_elems || !size);
125
            }
126
            for (i = 0; i < n_elems; i++) {
127
                void *curr_elem = first_elem + size * i;
128 129

                if (field->flags & VMS_ARRAY_OF_POINTER) {
130
                    curr_elem = *(void **)curr_elem;
131
                }
132
                if (!curr_elem && size) {
133 134 135 136
                    /* if null pointer check placeholder and do not follow */
                    assert(field->flags & VMS_ARRAY_OF_POINTER);
                    ret = vmstate_info_nullptr.get(f, curr_elem, size, NULL);
                } else if (field->flags & VMS_STRUCT) {
137
                    ret = vmstate_load_state(f, field->vmsd, curr_elem,
138 139
                                             field->vmsd->version_id);
                } else {
140
                    ret = field->info->get(f, curr_elem, size, field);
141
                }
142 143 144
                if (ret >= 0) {
                    ret = qemu_file_get_error(f);
                }
145
                if (ret < 0) {
146
                    qemu_file_set_error(f, ret);
147 148
                    error_report("Failed to load %s:%s", vmsd->name,
                                 field->name);
149
                    trace_vmstate_load_field_error(field->name, ret);
150 151 152
                    return ret;
                }
            }
M
Michael S. Tsirkin 已提交
153
        } else if (field->flags & VMS_MUST_EXIST) {
154 155
            error_report("Input validation failed: %s/%s",
                         vmsd->name, field->name);
M
Michael S. Tsirkin 已提交
156
            return -1;
157 158 159 160 161 162 163 164
        }
        field++;
    }
    ret = vmstate_subsection_load(f, vmsd, opaque);
    if (ret != 0) {
        return ret;
    }
    if (vmsd->post_load) {
165
        ret = vmsd->post_load(opaque, version_id);
166
    }
167 168
    trace_vmstate_load_state_end(vmsd->name, "end", ret);
    return ret;
169 170
}

171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
static int vmfield_name_num(VMStateField *start, VMStateField *search)
{
    VMStateField *field;
    int found = 0;

    for (field = start; field->name; field++) {
        if (!strcmp(field->name, search->name)) {
            if (field == search) {
                return found;
            }
            found++;
        }
    }

    return -1;
}

static bool vmfield_name_is_unique(VMStateField *start, VMStateField *search)
{
    VMStateField *field;
    int found = 0;

    for (field = start; field->name; field++) {
        if (!strcmp(field->name, search->name)) {
            found++;
            /* name found more than once, so it's not unique */
            if (found > 1) {
                return false;
            }
        }
    }

    return true;
}

static const char *vmfield_get_type_name(VMStateField *field)
{
    const char *type = "unknown";

    if (field->flags & VMS_STRUCT) {
        type = "struct";
    } else if (field->info->name) {
        type = field->info->name;
    }

    return type;
}

static bool vmsd_can_compress(VMStateField *field)
{
    if (field->field_exists) {
        /* Dynamically existing fields mess up compression */
        return false;
    }

    if (field->flags & VMS_STRUCT) {
        VMStateField *sfield = field->vmsd->fields;
        while (sfield->name) {
            if (!vmsd_can_compress(sfield)) {
                /* Child elements can't compress, so can't we */
                return false;
            }
            sfield++;
        }

        if (field->vmsd->subsections) {
            /* Subsections may come and go, better don't compress */
            return false;
        }
    }

    return true;
}

static void vmsd_desc_field_start(const VMStateDescription *vmsd, QJSON *vmdesc,
                                  VMStateField *field, int i, int max)
{
    char *name, *old_name;
    bool is_array = max > 1;
    bool can_compress = vmsd_can_compress(field);

    if (!vmdesc) {
        return;
    }

    name = g_strdup(field->name);

    /* Field name is not unique, need to make it unique */
    if (!vmfield_name_is_unique(vmsd->fields, field)) {
        int num = vmfield_name_num(vmsd->fields, field);
        old_name = name;
        name = g_strdup_printf("%s[%d]", name, num);
        g_free(old_name);
    }

    json_start_object(vmdesc, NULL);
    json_prop_str(vmdesc, "name", name);
    if (is_array) {
        if (can_compress) {
            json_prop_int(vmdesc, "array_len", max);
        } else {
            json_prop_int(vmdesc, "index", i);
        }
    }
    json_prop_str(vmdesc, "type", vmfield_get_type_name(field));

    if (field->flags & VMS_STRUCT) {
        json_start_object(vmdesc, "struct");
    }

    g_free(name);
}

static void vmsd_desc_field_end(const VMStateDescription *vmsd, QJSON *vmdesc,
                                VMStateField *field, size_t size, int i)
{
    if (!vmdesc) {
        return;
    }

    if (field->flags & VMS_STRUCT) {
        /* We printed a struct in between, close its child object */
        json_end_object(vmdesc);
    }

    json_prop_int(vmdesc, "size", size);
    json_end_object(vmdesc);
}

300 301 302 303 304 305 306 307 308 309 310

bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque)
{
    if (vmsd->needed && !vmsd->needed(opaque)) {
        /* optional section not needed */
        return false;
    }
    return true;
}


311
int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
312
                        void *opaque, QJSON *vmdesc)
313
{
314
    int ret = 0;
315 316
    VMStateField *field = vmsd->fields;

317 318
    trace_vmstate_save_state_top(vmsd->name);

319
    if (vmsd->pre_save) {
320 321 322 323 324 325
        ret = vmsd->pre_save(opaque);
        trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
        if (ret) {
            error_report("pre-save failed: %s", vmsd->name);
            return ret;
        }
326
    }
327 328 329 330 331 332 333

    if (vmdesc) {
        json_prop_str(vmdesc, "vmsd_name", vmsd->name);
        json_prop_int(vmdesc, "version", vmsd->version_id);
        json_start_array(vmdesc, "fields");
    }

334 335 336
    while (field->name) {
        if (!field->field_exists ||
            field->field_exists(opaque, vmsd->version_id)) {
337
            void *first_elem = opaque + field->offset;
338 339
            int i, n_elems = vmstate_n_elems(opaque, field);
            int size = vmstate_size(opaque, field);
340 341
            int64_t old_offset, written_bytes;
            QJSON *vmdesc_loop = vmdesc;
342

343
            trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems);
344 345
            if (field->flags & VMS_POINTER) {
                first_elem = *(void **)first_elem;
346
                assert(first_elem || !n_elems || !size);
347
            }
348
            for (i = 0; i < n_elems; i++) {
349
                void *curr_elem = first_elem + size * i;
350
                ret = 0;
351

352 353
                vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems);
                old_offset = qemu_ftell_fast(f);
354
                if (field->flags & VMS_ARRAY_OF_POINTER) {
355 356
                    assert(curr_elem);
                    curr_elem = *(void **)curr_elem;
357
                }
358
                if (!curr_elem && size) {
359 360
                    /* if null pointer write placeholder and do not follow */
                    assert(field->flags & VMS_ARRAY_OF_POINTER);
361 362
                    ret = vmstate_info_nullptr.put(f, curr_elem, size, NULL,
                                                   NULL);
363
                } else if (field->flags & VMS_STRUCT) {
364 365
                    ret = vmstate_save_state(f, field->vmsd, curr_elem,
                                             vmdesc_loop);
366
                } else {
367 368 369 370 371 372 373
                    ret = field->info->put(f, curr_elem, size, field,
                                     vmdesc_loop);
                }
                if (ret) {
                    error_report("Save of field %s/%s failed",
                                 vmsd->name, field->name);
                    return ret;
374
                }
375 376 377 378 379 380 381 382

                written_bytes = qemu_ftell_fast(f) - old_offset;
                vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i);

                /* Compressed arrays only care about the first element */
                if (vmdesc_loop && vmsd_can_compress(field)) {
                    vmdesc_loop = NULL;
                }
383
            }
M
Michael S. Tsirkin 已提交
384 385
        } else {
            if (field->flags & VMS_MUST_EXIST) {
386
                error_report("Output state validation failed: %s/%s",
M
Michael S. Tsirkin 已提交
387 388 389
                        vmsd->name, field->name);
                assert(!(field->flags & VMS_MUST_EXIST));
            }
390 391 392
        }
        field++;
    }
393 394 395 396 397

    if (vmdesc) {
        json_end_array(vmdesc);
    }

398
    return vmstate_subsection_save(f, vmsd, opaque, vmdesc);
399 400 401
}

static const VMStateDescription *
402
vmstate_get_subsection(const VMStateDescription **sub, char *idstr)
403
{
404 405 406
    while (sub && *sub && (*sub)->needed) {
        if (strcmp(idstr, (*sub)->name) == 0) {
            return *sub;
407 408 409 410 411 412 413 414 415
        }
        sub++;
    }
    return NULL;
}

static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
                                   void *opaque)
{
416 417
    trace_vmstate_subsection_load(vmsd->name);

418
    while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
419
        char idstr[256], *idstr_ret;
420 421 422 423 424 425 426
        int ret;
        uint8_t version_id, len, size;
        const VMStateDescription *sub_vmsd;

        len = qemu_peek_byte(f, 1);
        if (len < strlen(vmsd->name) + 1) {
            /* subsection name has be be "section_name/a" */
427
            trace_vmstate_subsection_load_bad(vmsd->name, "(short)", "");
428 429
            return 0;
        }
430
        size = qemu_peek_buffer(f, (uint8_t **)&idstr_ret, len, 2);
431
        if (size != len) {
432
            trace_vmstate_subsection_load_bad(vmsd->name, "(peek fail)", "");
433 434
            return 0;
        }
435
        memcpy(idstr, idstr_ret, size);
436 437 438
        idstr[size] = 0;

        if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
439 440
            trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(prefix)");
            /* it doesn't have a valid subsection name */
441 442 443 444
            return 0;
        }
        sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
        if (sub_vmsd == NULL) {
445
            trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(lookup)");
446 447 448 449 450 451 452 453 454
            return -ENOENT;
        }
        qemu_file_skip(f, 1); /* subsection */
        qemu_file_skip(f, 1); /* len */
        qemu_file_skip(f, len); /* idstr */
        version_id = qemu_get_be32(f);

        ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
        if (ret) {
455
            trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(child)");
456 457 458
            return ret;
        }
    }
459 460

    trace_vmstate_subsection_load_good(vmsd->name);
461 462 463
    return 0;
}

464 465
static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
                                   void *opaque, QJSON *vmdesc)
466
{
467
    const VMStateDescription **sub = vmsd->subsections;
468
    bool subsection_found = false;
469
    int ret = 0;
470

471
    trace_vmstate_subsection_save_top(vmsd->name);
472 473
    while (sub && *sub && (*sub)->needed) {
        if ((*sub)->needed(opaque)) {
474
            const VMStateDescription *vmsdsub = *sub;
475 476
            uint8_t len;

477
            trace_vmstate_subsection_save_loop(vmsd->name, vmsdsub->name);
478 479 480 481 482 483 484 485 486 487
            if (vmdesc) {
                /* Only create subsection array when we have any */
                if (!subsection_found) {
                    json_start_array(vmdesc, "subsections");
                    subsection_found = true;
                }

                json_start_object(vmdesc, NULL);
            }

488
            qemu_put_byte(f, QEMU_VM_SUBSECTION);
489
            len = strlen(vmsdsub->name);
490
            qemu_put_byte(f, len);
491 492
            qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len);
            qemu_put_be32(f, vmsdsub->version_id);
493 494 495 496
            ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc);
            if (ret) {
                return ret;
            }
497 498 499 500

            if (vmdesc) {
                json_end_object(vmdesc);
            }
501 502 503
        }
        sub++;
    }
504 505 506 507

    if (vmdesc && subsection_found) {
        json_end_array(vmdesc);
    }
508 509

    return ret;
510
}