X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/cda9046ba7dbba45f3016e5d60caffa2d72960fa..5330de099adf22a41586525c7afed7d470df11cd:/savevm.c diff --git a/savevm.c b/savevm.c index 6da5e73a96..7a363b64d5 100644 --- a/savevm.c +++ b/savevm.c @@ -29,7 +29,7 @@ #include #include -/* Needed early for HOST_BSD etc. */ +/* Needed early for CONFIG_BSD etc. */ #include "config-host.h" #ifndef _WIN32 @@ -52,7 +52,7 @@ #include #include #include -#ifdef HOST_BSD +#ifdef CONFIG_BSD #include #if defined(__FreeBSD__) || defined(__DragonFly__) #include @@ -90,6 +90,7 @@ #include "audio/audio.h" #include "migration.h" #include "qemu_socket.h" +#include "qemu-queue.h" /* point to the block driver where the snapshots are managed */ static BlockDriverState *bs_snapshots; @@ -131,7 +132,7 @@ static void qemu_announce_self_once(void *opaque) len = announce_self_create(buf, nd_table[i].macaddr); vlan = nd_table[i].vlan; for(vc = vlan->first_client; vc != NULL; vc = vc->next) { - vc->receive(vc->opaque, buf, len); + vc->receive(vc, buf, len); } } if (count--) { @@ -172,11 +173,11 @@ struct QEMUFile { int has_error; }; -typedef struct QEMUFilePopen +typedef struct QEMUFileStdio { - FILE *popen_file; + FILE *stdio_file; QEMUFile *file; -} QEMUFilePopen; +} QEMUFileStdio; typedef struct QEMUFileSocket { @@ -190,7 +191,7 @@ static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) ssize_t len; do { - len = recv(s->fd, buf, size, 0); + len = recv(s->fd, (void *)buf, size, 0); } while (len == -1 && socket_error() == EINTR); if (len == -1) @@ -206,43 +207,58 @@ static int socket_close(void *opaque) return 0; } -static int popen_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) +static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) +{ + QEMUFileStdio *s = opaque; + return fwrite(buf, 1, size, s->stdio_file); +} + +static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) { - QEMUFilePopen *s = opaque; - return fwrite(buf, 1, size, s->popen_file); + QEMUFileStdio *s = opaque; + FILE *fp = s->stdio_file; + int bytes; + + do { + clearerr(fp); + bytes = fread(buf, 1, size, fp); + } while ((bytes == 0) && ferror(fp) && (errno == EINTR)); + return bytes; } -static int popen_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +static int stdio_pclose(void *opaque) { - QEMUFilePopen *s = opaque; - return fread(buf, 1, size, s->popen_file); + QEMUFileStdio *s = opaque; + pclose(s->stdio_file); + qemu_free(s); + return 0; } -static int popen_close(void *opaque) +static int stdio_fclose(void *opaque) { - QEMUFilePopen *s = opaque; - pclose(s->popen_file); + QEMUFileStdio *s = opaque; + fclose(s->stdio_file); qemu_free(s); return 0; } -QEMUFile *qemu_popen(FILE *popen_file, const char *mode) +QEMUFile *qemu_popen(FILE *stdio_file, const char *mode) { - QEMUFilePopen *s; + QEMUFileStdio *s; - if (popen_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { + if (stdio_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { fprintf(stderr, "qemu_popen: Argument validity check failed\n"); return NULL; } - s = qemu_mallocz(sizeof(QEMUFilePopen)); + s = qemu_mallocz(sizeof(QEMUFileStdio)); - s->popen_file = popen_file; + s->stdio_file = stdio_file; if(mode[0] == 'r') { - s->file = qemu_fopen_ops(s, NULL, popen_get_buffer, popen_close, NULL, NULL); + s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_pclose, NULL, NULL); } else { - s->file = qemu_fopen_ops(s, popen_put_buffer, NULL, popen_close, NULL, NULL); + s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_pclose, NULL, NULL); } return s->file; } @@ -259,17 +275,45 @@ QEMUFile *qemu_popen_cmd(const char *command, const char *mode) return qemu_popen(popen_file, mode); } -int qemu_popen_fd(QEMUFile *f) +int qemu_stdio_fd(QEMUFile *f) { - QEMUFilePopen *p; + QEMUFileStdio *p; int fd; - p = (QEMUFilePopen *)f->opaque; - fd = fileno(p->popen_file); + p = (QEMUFileStdio *)f->opaque; + fd = fileno(p->stdio_file); return fd; } +QEMUFile *qemu_fdopen(int fd, const char *mode) +{ + QEMUFileStdio *s; + + if (mode == NULL || + (mode[0] != 'r' && mode[0] != 'w') || + mode[1] != 'b' || mode[2] != 0) { + fprintf(stderr, "qemu_fdopen: Argument validity check failed\n"); + return NULL; + } + + s = qemu_mallocz(sizeof(QEMUFileStdio)); + s->stdio_file = fdopen(fd, mode); + if (!s->stdio_file) + goto fail; + + if(mode[0] == 'r') { + s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_fclose, NULL, NULL); + } else { + s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_fclose, NULL, NULL); + } + return s->file; + +fail: + qemu_free(s); + return NULL; +} + QEMUFile *qemu_fopen_socket(int fd) { QEMUFileSocket *s = qemu_mallocz(sizeof(QEMUFileSocket)); @@ -279,97 +323,72 @@ QEMUFile *qemu_fopen_socket(int fd) return s->file; } -typedef struct QEMUFileStdio -{ - FILE *outfile; -} QEMUFileStdio; - static int file_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) { QEMUFileStdio *s = opaque; - fseek(s->outfile, pos, SEEK_SET); - fwrite(buf, 1, size, s->outfile); + fseek(s->stdio_file, pos, SEEK_SET); + fwrite(buf, 1, size, s->stdio_file); return size; } static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) { QEMUFileStdio *s = opaque; - fseek(s->outfile, pos, SEEK_SET); - return fread(buf, 1, size, s->outfile); -} - -static int file_close(void *opaque) -{ - QEMUFileStdio *s = opaque; - fclose(s->outfile); - qemu_free(s); - return 0; + fseek(s->stdio_file, pos, SEEK_SET); + return fread(buf, 1, size, s->stdio_file); } QEMUFile *qemu_fopen(const char *filename, const char *mode) { QEMUFileStdio *s; + if (mode == NULL || + (mode[0] != 'r' && mode[0] != 'w') || + mode[1] != 'b' || mode[2] != 0) { + fprintf(stderr, "qemu_fdopen: Argument validity check failed\n"); + return NULL; + } + s = qemu_mallocz(sizeof(QEMUFileStdio)); - s->outfile = fopen(filename, mode); - if (!s->outfile) + s->stdio_file = fopen(filename, mode); + if (!s->stdio_file) goto fail; - if (!strcmp(mode, "wb")) - return qemu_fopen_ops(s, file_put_buffer, NULL, file_close, NULL, NULL); - else if (!strcmp(mode, "rb")) - return qemu_fopen_ops(s, NULL, file_get_buffer, file_close, NULL, NULL); - + if(mode[0] == 'w') { + s->file = qemu_fopen_ops(s, file_put_buffer, NULL, stdio_fclose, NULL, NULL); + } else { + s->file = qemu_fopen_ops(s, NULL, file_get_buffer, stdio_fclose, NULL, NULL); + } + return s->file; fail: - if (s->outfile) - fclose(s->outfile); qemu_free(s); return NULL; } -typedef struct QEMUFileBdrv -{ - BlockDriverState *bs; - int64_t base_offset; -} QEMUFileBdrv; - static int block_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) { - QEMUFileBdrv *s = opaque; - bdrv_put_buffer(s->bs, buf, s->base_offset + pos, size); + bdrv_save_vmstate(opaque, buf, pos, size); return size; } static int block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) { - QEMUFileBdrv *s = opaque; - return bdrv_get_buffer(s->bs, buf, s->base_offset + pos, size); + return bdrv_load_vmstate(opaque, buf, pos, size); } static int bdrv_fclose(void *opaque) { - QEMUFileBdrv *s = opaque; - qemu_free(s); return 0; } -static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable) +static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable) { - QEMUFileBdrv *s; - - s = qemu_mallocz(sizeof(QEMUFileBdrv)); - - s->bs = bs; - s->base_offset = offset; - if (is_writable) - return qemu_fopen_ops(s, block_put_buffer, NULL, bdrv_fclose, NULL, NULL); - - return qemu_fopen_ops(s, NULL, block_get_buffer, bdrv_fclose, NULL, NULL); + return qemu_fopen_ops(bs, block_put_buffer, NULL, bdrv_fclose, NULL, NULL); + return qemu_fopen_ops(bs, NULL, block_get_buffer, bdrv_fclose, NULL, NULL); } QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, @@ -567,7 +586,9 @@ int qemu_file_rate_limit(QEMUFile *f) size_t qemu_file_set_rate_limit(QEMUFile *f, size_t new_rate) { - if (f->set_rate_limit) + /* any failed or completed migration keeps its state to allow probing of + * migration data, but has no associated file anymore */ + if (f && f->set_rate_limit) return f->set_rate_limit(f->opaque, new_rate); return 0; @@ -619,7 +640,278 @@ uint64_t qemu_get_be64(QEMUFile *f) return v; } +/* 8 bit int */ + +static int get_int8(QEMUFile *f, void *pv, size_t size) +{ + int8_t *v = pv; + qemu_get_s8s(f, v); + return 0; +} + +static void put_int8(QEMUFile *f, void *pv, size_t size) +{ + int8_t *v = pv; + qemu_put_s8s(f, v); +} + +const VMStateInfo vmstate_info_int8 = { + .name = "int8", + .get = get_int8, + .put = put_int8, +}; + +/* 16 bit int */ + +static int get_int16(QEMUFile *f, void *pv, size_t size) +{ + int16_t *v = pv; + qemu_get_sbe16s(f, v); + return 0; +} + +static void put_int16(QEMUFile *f, void *pv, size_t size) +{ + int16_t *v = pv; + qemu_put_sbe16s(f, v); +} + +const VMStateInfo vmstate_info_int16 = { + .name = "int16", + .get = get_int16, + .put = put_int16, +}; + +/* 32 bit int */ + +static int get_int32(QEMUFile *f, void *pv, size_t size) +{ + int32_t *v = pv; + qemu_get_sbe32s(f, v); + return 0; +} + +static void put_int32(QEMUFile *f, void *pv, size_t size) +{ + int32_t *v = pv; + qemu_put_sbe32s(f, v); +} + +const VMStateInfo vmstate_info_int32 = { + .name = "int32", + .get = get_int32, + .put = put_int32, +}; + +/* 32 bit int. See that the received value is the same than the one + in the field */ + +static int get_int32_equal(QEMUFile *f, void *pv, size_t size) +{ + int32_t *v = pv; + int32_t v2; + qemu_get_sbe32s(f, &v2); + + if (*v == v2) + return 0; + return -EINVAL; +} + +const VMStateInfo vmstate_info_int32_equal = { + .name = "int32 equal", + .get = get_int32_equal, + .put = put_int32, +}; + +/* 32 bit int. See that the received value is the less or the same + than the one in the field */ + +static int get_int32_le(QEMUFile *f, void *pv, size_t size) +{ + int32_t *old = pv; + int32_t new; + qemu_get_sbe32s(f, &new); + + if (*old <= new) + return 0; + return -EINVAL; +} + +const VMStateInfo vmstate_info_int32_le = { + .name = "int32 equal", + .get = get_int32_le, + .put = put_int32, +}; + +/* 64 bit int */ + +static int get_int64(QEMUFile *f, void *pv, size_t size) +{ + int64_t *v = pv; + qemu_get_sbe64s(f, v); + return 0; +} + +static void put_int64(QEMUFile *f, void *pv, size_t size) +{ + int64_t *v = pv; + qemu_put_sbe64s(f, v); +} + +const VMStateInfo vmstate_info_int64 = { + .name = "int64", + .get = get_int64, + .put = put_int64, +}; + +/* 8 bit unsigned int */ + +static int get_uint8(QEMUFile *f, void *pv, size_t size) +{ + uint8_t *v = pv; + qemu_get_8s(f, v); + return 0; +} + +static void put_uint8(QEMUFile *f, void *pv, size_t size) +{ + uint8_t *v = pv; + qemu_put_8s(f, v); +} + +const VMStateInfo vmstate_info_uint8 = { + .name = "uint8", + .get = get_uint8, + .put = put_uint8, +}; + +/* 16 bit unsigned int */ + +static int get_uint16(QEMUFile *f, void *pv, size_t size) +{ + uint16_t *v = pv; + qemu_get_be16s(f, v); + return 0; +} + +static void put_uint16(QEMUFile *f, void *pv, size_t size) +{ + uint16_t *v = pv; + qemu_put_be16s(f, v); +} + +const VMStateInfo vmstate_info_uint16 = { + .name = "uint16", + .get = get_uint16, + .put = put_uint16, +}; + +/* 32 bit unsigned int */ + +static int get_uint32(QEMUFile *f, void *pv, size_t size) +{ + uint32_t *v = pv; + qemu_get_be32s(f, v); + return 0; +} + +static void put_uint32(QEMUFile *f, void *pv, size_t size) +{ + uint32_t *v = pv; + qemu_put_be32s(f, v); +} + +const VMStateInfo vmstate_info_uint32 = { + .name = "uint32", + .get = get_uint32, + .put = put_uint32, +}; + +/* 64 bit unsigned int */ + +static int get_uint64(QEMUFile *f, void *pv, size_t size) +{ + uint64_t *v = pv; + qemu_get_be64s(f, v); + return 0; +} + +static void put_uint64(QEMUFile *f, void *pv, size_t size) +{ + uint64_t *v = pv; + qemu_put_be64s(f, v); +} + +const VMStateInfo vmstate_info_uint64 = { + .name = "uint64", + .get = get_uint64, + .put = put_uint64, +}; + +/* 8 bit int. See that the received value is the same than the one + in the field */ + +static int get_uint8_equal(QEMUFile *f, void *pv, size_t size) +{ + uint8_t *v = pv; + uint8_t v2; + qemu_get_8s(f, &v2); + + if (*v == v2) + return 0; + return -EINVAL; +} + +const VMStateInfo vmstate_info_uint8_equal = { + .name = "int32 equal", + .get = get_uint8_equal, + .put = put_uint8, +}; + +/* timers */ + +static int get_timer(QEMUFile *f, void *pv, size_t size) +{ + QEMUTimer *v = pv; + qemu_get_timer(f, v); + return 0; +} + +static void put_timer(QEMUFile *f, void *pv, size_t size) +{ + QEMUTimer *v = pv; + qemu_put_timer(f, v); +} + +const VMStateInfo vmstate_info_timer = { + .name = "timer", + .get = get_timer, + .put = put_timer, +}; + +/* uint8_t buffers */ + +static int get_buffer(QEMUFile *f, void *pv, size_t size) +{ + uint8_t *v = pv; + qemu_get_buffer(f, v, size); + return 0; +} + +static void put_buffer(QEMUFile *f, void *pv, size_t size) +{ + uint8_t *v = pv; + qemu_put_buffer(f, v, size); +} + +const VMStateInfo vmstate_info_buffer = { + .name = "buffer", + .get = get_buffer, + .put = put_buffer, +}; + typedef struct SaveStateEntry { + QTAILQ_ENTRY(SaveStateEntry) entry; char idstr[256]; int instance_id; int version_id; @@ -627,11 +919,27 @@ typedef struct SaveStateEntry { SaveLiveStateHandler *save_live_state; SaveStateHandler *save_state; LoadStateHandler *load_state; + const VMStateDescription *vmsd; void *opaque; - struct SaveStateEntry *next; } SaveStateEntry; -static SaveStateEntry *first_se; +static QTAILQ_HEAD(savevm_handlers, SaveStateEntry) savevm_handlers = + QTAILQ_HEAD_INITIALIZER(savevm_handlers); +static int global_section_id; + +static int calculate_new_instance_id(const char *idstr) +{ + SaveStateEntry *se; + int instance_id = 0; + + QTAILQ_FOREACH(se, &savevm_handlers, entry) { + if (strcmp(idstr, se->idstr) == 0 + && instance_id <= se->instance_id) { + instance_id = se->instance_id + 1; + } + } + return instance_id; +} /* TODO: Individual devices generally have very little idea about the rest of the system, so instance_id should be removed/replaced. @@ -645,30 +953,25 @@ int register_savevm_live(const char *idstr, LoadStateHandler *load_state, void *opaque) { - SaveStateEntry *se, **pse; - static int global_section_id; + SaveStateEntry *se; se = qemu_malloc(sizeof(SaveStateEntry)); pstrcpy(se->idstr, sizeof(se->idstr), idstr); - se->instance_id = (instance_id == -1) ? 0 : instance_id; se->version_id = version_id; se->section_id = global_section_id++; se->save_live_state = save_live_state; se->save_state = save_state; se->load_state = load_state; se->opaque = opaque; - se->next = NULL; + se->vmsd = NULL; - /* add at the end of list */ - pse = &first_se; - while (*pse != NULL) { - if (instance_id == -1 - && strcmp(se->idstr, (*pse)->idstr) == 0 - && se->instance_id <= (*pse)->instance_id) - se->instance_id = (*pse)->instance_id + 1; - pse = &(*pse)->next; + if (instance_id == -1) { + se->instance_id = calculate_new_instance_id(idstr); + } else { + se->instance_id = instance_id; } - *pse = se; + /* add at the end of list */ + QTAILQ_INSERT_TAIL(&savevm_handlers, se, entry); return 0; } @@ -685,18 +988,167 @@ int register_savevm(const char *idstr, void unregister_savevm(const char *idstr, void *opaque) { - SaveStateEntry **pse; + SaveStateEntry *se, *new_se; - pse = &first_se; - while (*pse != NULL) { - if (strcmp((*pse)->idstr, idstr) == 0 && (*pse)->opaque == opaque) { - SaveStateEntry *next = (*pse)->next; - qemu_free(*pse); - *pse = next; - continue; + QTAILQ_FOREACH_SAFE(se, &savevm_handlers, entry, new_se) { + if (strcmp(se->idstr, idstr) == 0 && se->opaque == opaque) { + QTAILQ_REMOVE(&savevm_handlers, se, entry); + qemu_free(se); + } + } +} + +int vmstate_register(int instance_id, const VMStateDescription *vmsd, + void *opaque) +{ + SaveStateEntry *se; + + se = qemu_malloc(sizeof(SaveStateEntry)); + pstrcpy(se->idstr, sizeof(se->idstr), vmsd->name); + se->version_id = vmsd->version_id; + se->section_id = global_section_id++; + se->save_live_state = NULL; + se->save_state = NULL; + se->load_state = NULL; + se->opaque = opaque; + se->vmsd = vmsd; + + if (instance_id == -1) { + se->instance_id = calculate_new_instance_id(vmsd->name); + } else { + se->instance_id = instance_id; + } + /* add at the end of list */ + QTAILQ_INSERT_TAIL(&savevm_handlers, se, entry); + return 0; +} + +void vmstate_unregister(const VMStateDescription *vmsd, void *opaque) +{ + SaveStateEntry *se, *new_se; + + QTAILQ_FOREACH_SAFE(se, &savevm_handlers, entry, new_se) { + if (se->vmsd == vmsd && se->opaque == opaque) { + QTAILQ_REMOVE(&savevm_handlers, se, entry); + qemu_free(se); + } + } +} + +int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque, int version_id) +{ + VMStateField *field = vmsd->fields; + + if (version_id > vmsd->version_id) { + return -EINVAL; + } + if (version_id < vmsd->minimum_version_id_old) { + return -EINVAL; + } + if (version_id < vmsd->minimum_version_id) { + return vmsd->load_state_old(f, opaque, version_id); + } + if (vmsd->pre_load) { + int ret = vmsd->pre_load(opaque); + if (ret) + return ret; + } + while(field->name) { + if ((field->field_exists && + field->field_exists(opaque, version_id)) || + (!field->field_exists && + field->version_id <= version_id)) { + void *base_addr = opaque + field->offset; + int ret, i, n_elems = 1; + + if (field->flags & VMS_ARRAY) { + n_elems = field->num; + } else if (field->flags & VMS_VARRAY) { + n_elems = *(size_t *)(opaque+field->num_offset); + } + if (field->flags & VMS_POINTER) { + base_addr = *(void **)base_addr; + } + for (i = 0; i < n_elems; i++) { + void *addr = base_addr + field->size * i; + + if (field->flags & VMS_ARRAY_OF_POINTER) { + addr = *(void **)addr; + } + if (field->flags & VMS_STRUCT) { + ret = vmstate_load_state(f, field->vmsd, addr, field->vmsd->version_id); + } else { + ret = field->info->get(f, addr, field->size); + + } + if (ret < 0) { + return ret; + } + } + } + field++; + } + if (vmsd->post_load) { + return vmsd->post_load(opaque, version_id); + } + return 0; +} + +void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque) +{ + VMStateField *field = vmsd->fields; + + if (vmsd->pre_save) { + vmsd->pre_save(opaque); + } + while(field->name) { + if (!field->field_exists || + field->field_exists(opaque, vmsd->version_id)) { + void *base_addr = opaque + field->offset; + int i, n_elems = 1; + + if (field->flags & VMS_ARRAY) { + n_elems = field->num; + } else if (field->flags & VMS_VARRAY) { + n_elems = *(size_t *)(opaque+field->num_offset); + } + if (field->flags & VMS_POINTER) { + base_addr = *(void **)base_addr; + } + for (i = 0; i < n_elems; i++) { + void *addr = base_addr + field->size * i; + + if (field->flags & VMS_STRUCT) { + vmstate_save_state(f, field->vmsd, addr); + } else { + field->info->put(f, addr, field->size); + } + } } - pse = &(*pse)->next; + field++; + } + if (vmsd->post_save) { + vmsd->post_save(opaque); + } +} + +static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id) +{ + if (!se->vmsd) { /* Old style */ + return se->load_state(f, se->opaque, version_id); } + return vmstate_load_state(f, se->vmsd, se->opaque, version_id); +} + +static void vmstate_save(QEMUFile *f, SaveStateEntry *se) +{ + if (!se->vmsd) { /* Old style */ + se->save_state(f, se->opaque); + return; + } + vmstate_save_state(f,se->vmsd, se->opaque); } #define QEMU_VM_FILE_MAGIC 0x5145564d @@ -716,7 +1168,7 @@ int qemu_savevm_state_begin(QEMUFile *f) qemu_put_be32(f, QEMU_VM_FILE_MAGIC); qemu_put_be32(f, QEMU_VM_FILE_VERSION); - for (se = first_se; se != NULL; se = se->next) { + QTAILQ_FOREACH(se, &savevm_handlers, entry) { int len; if (se->save_live_state == NULL) @@ -748,7 +1200,7 @@ int qemu_savevm_state_iterate(QEMUFile *f) SaveStateEntry *se; int ret = 1; - for (se = first_se; se != NULL; se = se->next) { + QTAILQ_FOREACH(se, &savevm_handlers, entry) { if (se->save_live_state == NULL) continue; @@ -772,7 +1224,7 @@ int qemu_savevm_state_complete(QEMUFile *f) { SaveStateEntry *se; - for (se = first_se; se != NULL; se = se->next) { + QTAILQ_FOREACH(se, &savevm_handlers, entry) { if (se->save_live_state == NULL) continue; @@ -783,10 +1235,10 @@ int qemu_savevm_state_complete(QEMUFile *f) se->save_live_state(f, QEMU_VM_SECTION_END, se->opaque); } - for(se = first_se; se != NULL; se = se->next) { + QTAILQ_FOREACH(se, &savevm_handlers, entry) { int len; - if (se->save_state == NULL) + if (se->save_state == NULL && se->vmsd == NULL) continue; /* Section type */ @@ -801,7 +1253,7 @@ int qemu_savevm_state_complete(QEMUFile *f) qemu_put_be32(f, se->instance_id); qemu_put_be32(f, se->version_id); - se->save_state(f, se->opaque); + vmstate_save(f, se); } qemu_put_byte(f, QEMU_VM_EOF); @@ -848,7 +1300,7 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id) { SaveStateEntry *se; - for(se = first_se; se != NULL; se = se->next) { + QTAILQ_FOREACH(se, &savevm_handlers, entry) { if (!strcmp(se->idstr, idstr) && instance_id == se->instance_id) return se; @@ -857,56 +1309,17 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id) } typedef struct LoadStateEntry { + QLIST_ENTRY(LoadStateEntry) entry; SaveStateEntry *se; int section_id; int version_id; - struct LoadStateEntry *next; } LoadStateEntry; -static int qemu_loadvm_state_v2(QEMUFile *f) -{ - SaveStateEntry *se; - int len, ret, instance_id, record_len, version_id; - int64_t total_len, end_pos, cur_pos; - char idstr[256]; - - total_len = qemu_get_be64(f); - end_pos = total_len + qemu_ftell(f); - for(;;) { - if (qemu_ftell(f) >= end_pos) - break; - len = qemu_get_byte(f); - qemu_get_buffer(f, (uint8_t *)idstr, len); - idstr[len] = '\0'; - instance_id = qemu_get_be32(f); - version_id = qemu_get_be32(f); - record_len = qemu_get_be32(f); - cur_pos = qemu_ftell(f); - se = find_se(idstr, instance_id); - if (!se) { - fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n", - instance_id, idstr); - } else { - ret = se->load_state(f, se->opaque, version_id); - if (ret < 0) { - fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n", - instance_id, idstr); - return ret; - } - } - /* always seek to exact end of record */ - qemu_fseek(f, cur_pos + record_len, SEEK_SET); - } - - if (qemu_file_has_error(f)) - return -EIO; - - return 0; -} - int qemu_loadvm_state(QEMUFile *f) { - LoadStateEntry *first_le = NULL; + QLIST_HEAD(, LoadStateEntry) loadvm_handlers = + QLIST_HEAD_INITIALIZER(loadvm_handlers); + LoadStateEntry *le, *new_le; uint8_t section_type; unsigned int v; int ret; @@ -916,14 +1329,15 @@ int qemu_loadvm_state(QEMUFile *f) return -EINVAL; v = qemu_get_be32(f); - if (v == QEMU_VM_FILE_VERSION_COMPAT) - return qemu_loadvm_state_v2(f); + if (v == QEMU_VM_FILE_VERSION_COMPAT) { + fprintf(stderr, "SaveVM v2 format is obsolete and don't work anymore\n"); + return -ENOTSUP; + } if (v != QEMU_VM_FILE_VERSION) return -ENOTSUP; while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) { uint32_t instance_id, version_id, section_id; - LoadStateEntry *le; SaveStateEntry *se; char idstr[257]; int len; @@ -961,23 +1375,36 @@ int qemu_loadvm_state(QEMUFile *f) le->se = se; le->section_id = section_id; le->version_id = version_id; - le->next = first_le; - first_le = le; + QLIST_INSERT_HEAD(&loadvm_handlers, le, entry); - le->se->load_state(f, le->se->opaque, le->version_id); + ret = vmstate_load(f, le->se, le->version_id); + if (ret < 0) { + fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n", + instance_id, idstr); + goto out; + } break; case QEMU_VM_SECTION_PART: case QEMU_VM_SECTION_END: section_id = qemu_get_be32(f); - for (le = first_le; le && le->section_id != section_id; le = le->next); + QLIST_FOREACH(le, &loadvm_handlers, entry) { + if (le->section_id == section_id) { + break; + } + } if (le == NULL) { fprintf(stderr, "Unknown savevm section %d\n", section_id); ret = -EINVAL; goto out; } - le->se->load_state(f, le->se->opaque, le->version_id); + ret = vmstate_load(f, le->se, le->version_id); + if (ret < 0) { + fprintf(stderr, "qemu: warning: error while loading state section id %d\n", + section_id); + goto out; + } break; default: fprintf(stderr, "Unknown savevm section type %d\n", section_type); @@ -989,9 +1416,8 @@ int qemu_loadvm_state(QEMUFile *f) ret = 0; out: - while (first_le) { - LoadStateEntry *le = first_le; - first_le = first_le->next; + QLIST_FOREACH_SAFE(le, &loadvm_handlers, entry, new_le) { + QLIST_REMOVE(le, entry); qemu_free(le); } @@ -1020,12 +1446,12 @@ static int bdrv_has_snapshot(BlockDriverState *bs) static BlockDriverState *get_bs_snapshots(void) { BlockDriverState *bs; - int i; + DriveInfo *dinfo; if (bs_snapshots) return bs_snapshots; - for(i = 0; i <= nb_drives; i++) { - bs = drives_table[i].bdrv; + QTAILQ_FOREACH(dinfo, &drives, next) { + bs = dinfo->bdrv; if (bdrv_can_snapshot(bs)) goto ok; } @@ -1057,12 +1483,12 @@ static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, return ret; } -void do_savevm(Monitor *mon, const char *name) +void do_savevm(Monitor *mon, const QDict *qdict) { + DriveInfo *dinfo; BlockDriverState *bs, *bs1; QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1; - int must_delete, ret, i; - BlockDriverInfo bdi1, *bdi = &bdi1; + int must_delete, ret; QEMUFile *f; int saved_vm_running; uint32_t vm_state_size; @@ -1071,6 +1497,7 @@ void do_savevm(Monitor *mon, const char *name) #else struct timeval tv; #endif + const char *name = qdict_get_try_str(qdict, "name"); bs = get_bs_snapshots(); if (!bs) { @@ -1112,14 +1539,8 @@ void do_savevm(Monitor *mon, const char *name) #endif sn->vm_clock_nsec = qemu_get_clock(vm_clock); - if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) { - monitor_printf(mon, "Device %s does not support VM state snapshots\n", - bdrv_get_device_name(bs)); - goto the_end; - } - /* save the VM state */ - f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 1); + f = qemu_fopen_bdrv(bs, 1); if (!f) { monitor_printf(mon, "Could not open VM state file\n"); goto the_end; @@ -1134,8 +1555,8 @@ void do_savevm(Monitor *mon, const char *name) /* create the snapshots */ - for(i = 0; i < nb_drives; i++) { - bs1 = drives_table[i].bdrv; + QTAILQ_FOREACH(dinfo, &drives, next) { + bs1 = dinfo->bdrv; if (bdrv_has_snapshot(bs1)) { if (must_delete) { ret = bdrv_snapshot_delete(bs1, old_sn->id_str); @@ -1160,29 +1581,25 @@ void do_savevm(Monitor *mon, const char *name) vm_start(); } -void do_loadvm(Monitor *mon, const char *name) +int load_vmstate(Monitor *mon, const char *name) { + DriveInfo *dinfo; BlockDriverState *bs, *bs1; - BlockDriverInfo bdi1, *bdi = &bdi1; QEMUSnapshotInfo sn; QEMUFile *f; - int i, ret; - int saved_vm_running; + int ret; bs = get_bs_snapshots(); if (!bs) { monitor_printf(mon, "No block device supports snapshots\n"); - return; + return -EINVAL; } /* Flush all IO requests so they don't interfere with the new state. */ qemu_aio_flush(); - saved_vm_running = vm_running; - vm_stop(0); - - for(i = 0; i <= nb_drives; i++) { - bs1 = drives_table[i].bdrv; + QTAILQ_FOREACH(dinfo, &drives, next) { + bs1 = dinfo->bdrv; if (bdrv_has_snapshot(bs1)) { ret = bdrv_snapshot_goto(bs1, name); if (ret < 0) { @@ -1206,42 +1623,37 @@ void do_loadvm(Monitor *mon, const char *name) } /* fatal on snapshot block device */ if (bs == bs1) - goto the_end; + return 0; } } } - if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) { - monitor_printf(mon, "Device %s does not support VM state snapshots\n", - bdrv_get_device_name(bs)); - return; - } - /* Don't even try to load empty VM states */ ret = bdrv_snapshot_find(bs, &sn, name); if ((ret >= 0) && (sn.vm_state_size == 0)) - goto the_end; + return -EINVAL; /* restore the VM state */ - f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 0); + f = qemu_fopen_bdrv(bs, 0); if (!f) { monitor_printf(mon, "Could not open VM state file\n"); - goto the_end; + return -EINVAL; } ret = qemu_loadvm_state(f); qemu_fclose(f); if (ret < 0) { monitor_printf(mon, "Error %d while loading VM state\n", ret); + return ret; } - the_end: - if (saved_vm_running) - vm_start(); + return 0; } -void do_delvm(Monitor *mon, const char *name) +void do_delvm(Monitor *mon, const QDict *qdict) { + DriveInfo *dinfo; BlockDriverState *bs, *bs1; - int i, ret; + int ret; + const char *name = qdict_get_str(qdict, "name"); bs = get_bs_snapshots(); if (!bs) { @@ -1249,8 +1661,8 @@ void do_delvm(Monitor *mon, const char *name) return; } - for(i = 0; i <= nb_drives; i++) { - bs1 = drives_table[i].bdrv; + QTAILQ_FOREACH(dinfo, &drives, next) { + bs1 = dinfo->bdrv; if (bdrv_has_snapshot(bs1)) { ret = bdrv_snapshot_delete(bs1, name); if (ret < 0) { @@ -1268,6 +1680,7 @@ void do_delvm(Monitor *mon, const char *name) void do_info_snapshots(Monitor *mon) { + DriveInfo *dinfo; BlockDriverState *bs, *bs1; QEMUSnapshotInfo *sn_tab, *sn; int nb_sns, i; @@ -1279,8 +1692,8 @@ void do_info_snapshots(Monitor *mon) return; } monitor_printf(mon, "Snapshot devices:"); - for(i = 0; i <= nb_drives; i++) { - bs1 = drives_table[i].bdrv; + QTAILQ_FOREACH(dinfo, &drives, next) { + bs1 = dinfo->bdrv; if (bdrv_has_snapshot(bs1)) { if (bs == bs1) monitor_printf(mon, " %s", bdrv_get_device_name(bs1));