X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/a67a47d2b559a7733c3f89aeb2d81b19d2c027e4..da71ebd1450fcf6f0c424810a37ec6abee02a22b:/savevm.c diff --git a/savevm.c b/savevm.c index 7801aa7056..80be1ff063 100644 --- a/savevm.c +++ b/savevm.c @@ -81,7 +81,9 @@ #include "migration.h" #include "qemu_socket.h" #include "qemu-queue.h" +#include "qemu-timer.h" #include "cpus.h" +#include "memory.h" #define SELF_ANNOUNCE_ROUNDS 5 @@ -173,7 +175,7 @@ struct QEMUFile { int buf_size; /* 0 when writing */ uint8_t buf[IO_BUF_SIZE]; - int has_error; + int last_error; }; typedef struct QEMUFileStdio @@ -206,7 +208,7 @@ static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) static int socket_close(void *opaque) { QEMUFileSocket *s = opaque; - qemu_free(s); + g_free(s); return 0; } @@ -234,16 +236,22 @@ static int stdio_pclose(void *opaque) QEMUFileStdio *s = opaque; int ret; ret = pclose(s->stdio_file); - qemu_free(s); + if (ret == -1) { + ret = -errno; + } + g_free(s); return ret; } static int stdio_fclose(void *opaque) { QEMUFileStdio *s = opaque; - fclose(s->stdio_file); - qemu_free(s); - return 0; + int ret = 0; + if (fclose(s->stdio_file) == EOF) { + ret = -errno; + } + g_free(s); + return ret; } QEMUFile *qemu_popen(FILE *stdio_file, const char *mode) @@ -255,7 +263,7 @@ QEMUFile *qemu_popen(FILE *stdio_file, const char *mode) return NULL; } - s = qemu_mallocz(sizeof(QEMUFileStdio)); + s = g_malloc0(sizeof(QEMUFileStdio)); s->stdio_file = stdio_file; @@ -303,7 +311,7 @@ QEMUFile *qemu_fdopen(int fd, const char *mode) return NULL; } - s = qemu_mallocz(sizeof(QEMUFileStdio)); + s = g_malloc0(sizeof(QEMUFileStdio)); s->stdio_file = fdopen(fd, mode); if (!s->stdio_file) goto fail; @@ -318,13 +326,13 @@ QEMUFile *qemu_fdopen(int fd, const char *mode) return s->file; fail: - qemu_free(s); + g_free(s); return NULL; } QEMUFile *qemu_fopen_socket(int fd) { - QEMUFileSocket *s = qemu_mallocz(sizeof(QEMUFileSocket)); + QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket)); s->fd = fd; s->file = qemu_fopen_ops(s, NULL, socket_get_buffer, socket_close, @@ -358,7 +366,7 @@ QEMUFile *qemu_fopen(const char *filename, const char *mode) return NULL; } - s = qemu_mallocz(sizeof(QEMUFileStdio)); + s = g_malloc0(sizeof(QEMUFileStdio)); s->stdio_file = fopen(filename, mode); if (!s->stdio_file) @@ -373,7 +381,7 @@ QEMUFile *qemu_fopen(const char *filename, const char *mode) } return s->file; fail: - qemu_free(s); + g_free(s); return NULL; } @@ -411,7 +419,7 @@ QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, { QEMUFile *f; - f = qemu_mallocz(sizeof(QEMUFile)); + f = g_malloc0(sizeof(QEMUFile)); f->opaque = opaque; f->put_buffer = put_buffer; @@ -425,16 +433,32 @@ QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, return f; } -int qemu_file_has_error(QEMUFile *f) +int qemu_file_get_error(QEMUFile *f) { - return f->has_error; + return f->last_error; } -void qemu_file_set_error(QEMUFile *f) +void qemu_file_set_error(QEMUFile *f, int ret) +{ + f->last_error = ret; +} + +/** Sets last_error conditionally + * + * Sets last_error only if ret is negative _and_ no error + * was set before. + */ +static void qemu_file_set_if_error(QEMUFile *f, int ret) { - f->has_error = 1; + if (ret < 0 && !f->last_error) { + qemu_file_set_error(f, ret); + } } +/** Flushes QEMUFile buffer + * + * In case of error, last_error is set. + */ void qemu_fflush(QEMUFile *f) { if (!f->put_buffer) @@ -447,7 +471,7 @@ void qemu_fflush(QEMUFile *f) if (len > 0) f->buf_offset += f->buf_index; else - f->has_error = 1; + qemu_file_set_error(f, -EINVAL); f->buf_index = 0; } } @@ -455,6 +479,7 @@ void qemu_fflush(QEMUFile *f) static void qemu_fill_buffer(QEMUFile *f) { int len; + int pending; if (!f->get_buffer) return; @@ -462,22 +487,60 @@ static void qemu_fill_buffer(QEMUFile *f) if (f->is_write) abort(); - len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE); + pending = f->buf_size - f->buf_index; + if (pending > 0) { + memmove(f->buf, f->buf + f->buf_index, pending); + } + f->buf_index = 0; + f->buf_size = pending; + + len = f->get_buffer(f->opaque, f->buf + pending, f->buf_offset, + IO_BUF_SIZE - pending); if (len > 0) { - f->buf_index = 0; - f->buf_size = len; + f->buf_size += len; f->buf_offset += len; + } else if (len == 0) { + f->last_error = -EIO; } else if (len != -EAGAIN) - f->has_error = 1; + qemu_file_set_error(f, len); } -int qemu_fclose(QEMUFile *f) +/** Calls close function and set last_error if needed + * + * Internal function. qemu_fflush() must be called before this. + * + * Returns f->close() return value, or 0 if close function is not set. + */ +static int qemu_close(QEMUFile *f) { int ret = 0; - qemu_fflush(f); - if (f->close) + if (f->close) { ret = f->close(f->opaque); - qemu_free(f); + qemu_file_set_if_error(f, ret); + } + return ret; +} + +/** Closes the file + * + * Returns negative error value if any error happened on previous operations or + * while closing the file. Returns 0 or positive number on success. + * + * The meaning of return value on success depends on the specific backend + * being used. + */ +int qemu_fclose(QEMUFile *f) +{ + int ret; + qemu_fflush(f); + ret = qemu_close(f); + /* If any error was spotted before closing, we should report it + * instead of the close() return value. + */ + if (f->last_error) { + ret = f->last_error; + } + g_free(f); return ret; } @@ -490,13 +553,13 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) { int l; - if (!f->has_error && f->is_write == 0 && f->buf_index > 0) { + if (!f->last_error && f->is_write == 0 && f->buf_index > 0) { fprintf(stderr, "Attempted to write to buffer while read buffer is not empty\n"); abort(); } - while (!f->has_error && size > 0) { + while (!f->last_error && size > 0) { l = IO_BUF_SIZE - f->buf_index; if (l > size) l = size; @@ -512,7 +575,7 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) void qemu_put_byte(QEMUFile *f, int v) { - if (!f->has_error && f->is_write == 0 && f->buf_index > 0) { + if (!f->last_error && f->is_write == 0 && f->buf_index > 0) { fprintf(stderr, "Attempted to write to buffer while read buffer is not empty\n"); abort(); @@ -524,56 +587,86 @@ void qemu_put_byte(QEMUFile *f, int v) qemu_fflush(f); } -int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1) +static void qemu_file_skip(QEMUFile *f, int size) { - int size, l; + if (f->buf_index + size <= f->buf_size) { + f->buf_index += size; + } +} - if (f->is_write) +static int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset) +{ + int pending; + int index; + + if (f->is_write) { abort(); + } - size = size1; - while (size > 0) { - l = f->buf_size - f->buf_index; - if (l == 0) { - qemu_fill_buffer(f); - l = f->buf_size - f->buf_index; - if (l == 0) - break; + index = f->buf_index + offset; + pending = f->buf_size - index; + if (pending < size) { + qemu_fill_buffer(f); + index = f->buf_index + offset; + pending = f->buf_size - index; + } + + if (pending <= 0) { + return 0; + } + if (size > pending) { + size = pending; + } + + memcpy(buf, f->buf + index, size); + return size; +} + +int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size) +{ + int pending = size; + int done = 0; + + while (pending > 0) { + int res; + + res = qemu_peek_buffer(f, buf, pending, 0); + if (res == 0) { + return done; } - if (l > size) - l = size; - memcpy(buf, f->buf + f->buf_index, l); - f->buf_index += l; - buf += l; - size -= l; + qemu_file_skip(f, res); + buf += res; + pending -= res; + done += res; } - return size1 - size; + return done; } -static int qemu_peek_byte(QEMUFile *f) +static int qemu_peek_byte(QEMUFile *f, int offset) { - if (f->is_write) + int index = f->buf_index + offset; + + if (f->is_write) { abort(); + } - if (f->buf_index >= f->buf_size) { + if (index >= f->buf_size) { qemu_fill_buffer(f); - if (f->buf_index >= f->buf_size) + index = f->buf_index + offset; + if (index >= f->buf_size) { return 0; + } } - return f->buf[f->buf_index]; + return f->buf[index]; } int qemu_get_byte(QEMUFile *f) { - if (f->is_write) - abort(); + int result; - if (f->buf_index >= f->buf_size) { - qemu_fill_buffer(f); - if (f->buf_index >= f->buf_size) - return 0; - } - return f->buf[f->buf_index++]; + result = qemu_peek_byte(f, 0); + qemu_file_skip(f, 1); + return result; } int64_t qemu_ftell(QEMUFile *f) @@ -674,6 +767,30 @@ uint64_t qemu_get_be64(QEMUFile *f) return v; } + +/* timer */ + +void qemu_put_timer(QEMUFile *f, QEMUTimer *ts) +{ + uint64_t expire_time; + + expire_time = qemu_timer_expire_time_ns(ts); + qemu_put_be64(f, expire_time); +} + +void qemu_get_timer(QEMUFile *f, QEMUTimer *ts) +{ + uint64_t expire_time; + + expire_time = qemu_get_be64(f); + if (expire_time != -1) { + qemu_mod_timer_ns(ts, expire_time); + } else { + qemu_del_timer(ts); + } +} + + /* bool */ static int get_bool(QEMUFile *f, void *pv, size_t size) @@ -1114,7 +1231,7 @@ int register_savevm_live(DeviceState *dev, { SaveStateEntry *se; - se = qemu_mallocz(sizeof(SaveStateEntry)); + se = g_malloc0(sizeof(SaveStateEntry)); se->version_id = version_id; se->section_id = global_section_id++; se->set_params = set_params; @@ -1130,9 +1247,9 @@ int register_savevm_live(DeviceState *dev, if (id) { pstrcpy(se->idstr, sizeof(se->idstr), id); pstrcat(se->idstr, sizeof(se->idstr), "/"); - qemu_free(id); + g_free(id); - se->compat = qemu_mallocz(sizeof(CompatEntry)); + se->compat = g_malloc0(sizeof(CompatEntry)); pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), idstr); se->compat->instance_id = instance_id == -1 ? calculate_compat_instance_id(idstr) : instance_id; @@ -1174,7 +1291,7 @@ void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque) if (path) { pstrcpy(id, sizeof(id), path); pstrcat(id, sizeof(id), "/"); - qemu_free(path); + g_free(path); } } pstrcat(id, sizeof(id), idstr); @@ -1183,34 +1300,9 @@ void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque) if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) { QTAILQ_REMOVE(&savevm_handlers, se, entry); if (se->compat) { - qemu_free(se->compat); + g_free(se->compat); } - qemu_free(se); - } - } -} - -/* mark a device as not to be migrated, that is the device should be - unplugged before migration */ -void register_device_unmigratable(DeviceState *dev, const char *idstr, - void *opaque) -{ - SaveStateEntry *se; - char id[256] = ""; - - if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) { - char *path = dev->parent_bus->info->get_dev_path(dev); - if (path) { - pstrcpy(id, sizeof(id), path); - pstrcat(id, sizeof(id), "/"); - qemu_free(path); - } - } - pstrcat(id, sizeof(id), idstr); - - QTAILQ_FOREACH(se, &savevm_handlers, entry) { - if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) { - se->no_migrate = 1; + g_free(se); } } } @@ -1225,7 +1317,7 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, /* If this triggers, alias support can be dropped for the vmsd. */ assert(alias_id == -1 || required_for_version >= vmsd->minimum_version_id); - se = qemu_mallocz(sizeof(SaveStateEntry)); + se = g_malloc0(sizeof(SaveStateEntry)); se->version_id = vmsd->version_id; se->section_id = global_section_id++; se->save_live_state = NULL; @@ -1241,9 +1333,9 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, if (id) { pstrcpy(se->idstr, sizeof(se->idstr), id); pstrcat(se->idstr, sizeof(se->idstr), "/"); - qemu_free(id); + g_free(id); - se->compat = qemu_mallocz(sizeof(CompatEntry)); + se->compat = g_malloc0(sizeof(CompatEntry)); pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), vmsd->name); se->compat->instance_id = instance_id == -1 ? calculate_compat_instance_id(vmsd->name) : instance_id; @@ -1279,9 +1371,9 @@ void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, if (se->vmsd == vmsd && se->opaque == opaque) { QTAILQ_REMOVE(&savevm_handlers, se, entry); if (se->compat) { - qemu_free(se->compat); + g_free(se->compat); } - qemu_free(se); + g_free(se); } } } @@ -1466,6 +1558,7 @@ int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, int shared) { SaveStateEntry *se; + int ret; QTAILQ_FOREACH(se, &savevm_handlers, entry) { if(se->set_params == NULL) { @@ -1495,17 +1588,27 @@ int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, qemu_put_be32(f, se->instance_id); qemu_put_be32(f, se->version_id); - se->save_live_state(mon, f, QEMU_VM_SECTION_START, se->opaque); + ret = se->save_live_state(mon, f, QEMU_VM_SECTION_START, se->opaque); + if (ret < 0) { + qemu_savevm_state_cancel(mon, f); + return ret; + } } - - if (qemu_file_has_error(f)) { + ret = qemu_file_get_error(f); + if (ret != 0) { qemu_savevm_state_cancel(mon, f); - return -EIO; } - return 0; + return ret; + } +/* + * this function has three return values: + * negative: there was one error, and we have -errno. + * 0 : We haven't finished, caller have to go again + * 1 : We have finished, we can go to complete phase + */ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f) { SaveStateEntry *se; @@ -1520,7 +1623,7 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f) qemu_put_be32(f, se->section_id); ret = se->save_live_state(mon, f, QEMU_VM_SECTION_PART, se->opaque); - if (!ret) { + if (ret <= 0) { /* Do not proceed to the next vmstate before this one reported completion of the current stage. This serializes the migration and reduces the probability that a faster changing state is @@ -1528,21 +1631,20 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f) break; } } - - if (ret) - return 1; - - if (qemu_file_has_error(f)) { + if (ret != 0) { + return ret; + } + ret = qemu_file_get_error(f); + if (ret != 0) { qemu_savevm_state_cancel(mon, f); - return -EIO; } - - return 0; + return ret; } int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) { SaveStateEntry *se; + int ret; cpu_synchronize_all_states(); @@ -1554,7 +1656,10 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) qemu_put_byte(f, QEMU_VM_SECTION_END); qemu_put_be32(f, se->section_id); - se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque); + ret = se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque); + if (ret < 0) { + return ret; + } } QTAILQ_FOREACH(se, &savevm_handlers, entry) { @@ -1580,10 +1685,7 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) qemu_put_byte(f, QEMU_VM_EOF); - if (qemu_file_has_error(f)) - return -EIO; - - return 0; + return qemu_file_get_error(f); } void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f) @@ -1599,12 +1701,8 @@ void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f) static int qemu_savevm_state(Monitor *mon, QEMUFile *f) { - int saved_vm_running; int ret; - saved_vm_running = vm_running; - vm_stop(VMSTOP_SAVEVM); - if (qemu_savevm_state_blocked(mon)) { ret = -EINVAL; goto out; @@ -1623,11 +1721,9 @@ static int qemu_savevm_state(Monitor *mon, QEMUFile *f) ret = qemu_savevm_state_complete(mon, f); out: - if (qemu_file_has_error(f)) - ret = -EIO; - - if (!ret && saved_vm_running) - vm_start(); + if (ret == 0) { + ret = qemu_file_get_error(f); + } return ret; } @@ -1666,29 +1762,36 @@ static const VMStateDescription *vmstate_get_subsection(const VMStateSubsection static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, void *opaque) { - const VMStateSubsection *sub = vmsd->subsections; - - if (!sub || !sub->needed) { - return 0; - } - - while (qemu_peek_byte(f) == QEMU_VM_SUBSECTION) { + while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) { char idstr[256]; int ret; - uint8_t version_id, len; + uint8_t version_id, len, size; const VMStateDescription *sub_vmsd; - qemu_get_byte(f); /* subsection */ - len = qemu_get_byte(f); - qemu_get_buffer(f, (uint8_t *)idstr, len); - idstr[len] = 0; - version_id = qemu_get_be32(f); + len = qemu_peek_byte(f, 1); + if (len < strlen(vmsd->name) + 1) { + /* subsection name has be be "section_name/a" */ + return 0; + } + size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2); + if (size != len) { + return 0; + } + idstr[size] = 0; - sub_vmsd = vmstate_get_subsection(sub, idstr); + if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) { + /* it don't have a valid subsection name */ + return 0; + } + sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr); if (sub_vmsd == NULL) { return -ENOENT; } - assert(!sub_vmsd->subsections); + 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) { return ret; @@ -1712,7 +1815,6 @@ static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, qemu_put_byte(f, len); qemu_put_buffer(f, (uint8_t *)vmsd->name, len); qemu_put_be32(f, vmsd->version_id); - assert(!vmsd->subsections); vmstate_save_state(f, vmsd, opaque); } sub++; @@ -1785,7 +1887,7 @@ int qemu_loadvm_state(QEMUFile *f) } /* Add entry */ - le = qemu_mallocz(sizeof(*le)); + le = g_malloc0(sizeof(*le)); le->se = se; le->section_id = section_id; @@ -1835,11 +1937,12 @@ int qemu_loadvm_state(QEMUFile *f) out: QLIST_FOREACH_SAFE(le, &loadvm_handlers, entry, new_le) { QLIST_REMOVE(le, entry); - qemu_free(le); + g_free(le); } - if (qemu_file_has_error(f)) - ret = -EIO; + if (ret == 0) { + ret = qemu_file_get_error(f); + } return ret; } @@ -1862,7 +1965,7 @@ static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, break; } } - qemu_free(sn_tab); + g_free(sn_tab); return ret; } @@ -1900,7 +2003,7 @@ void do_savevm(Monitor *mon, const QDict *qdict) int ret; QEMUFile *f; int saved_vm_running; - uint32_t vm_state_size; + uint64_t vm_state_size; #ifdef _WIN32 struct _timeb tb; struct tm *ptm; @@ -1914,7 +2017,7 @@ void do_savevm(Monitor *mon, const QDict *qdict) bs = NULL; while ((bs = bdrv_next(bs))) { - if (bdrv_is_removable(bs) || bdrv_is_read_only(bs)) { + if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { continue; } @@ -1931,8 +2034,8 @@ void do_savevm(Monitor *mon, const QDict *qdict) return; } - saved_vm_running = vm_running; - vm_stop(VMSTOP_SAVEVM); + saved_vm_running = runstate_is_running(); + vm_stop(RUN_STATE_SAVE_VM); memset(sn, 0, sizeof(*sn)); @@ -2034,7 +2137,7 @@ int load_vmstate(const char *name) bs = NULL; while ((bs = bdrv_next(bs))) { - if (bdrv_is_removable(bs) || bdrv_is_read_only(bs)) { + if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { continue; } @@ -2053,7 +2156,7 @@ int load_vmstate(const char *name) } /* Flush all IO requests so they don't interfere with the new state. */ - qemu_aio_flush(); + bdrv_drain_all(); bs = NULL; while ((bs = bdrv_next(bs))) { @@ -2141,7 +2244,7 @@ void do_info_snapshots(Monitor *mon) return; } - available_snapshots = qemu_mallocz(sizeof(int) * nb_sns); + available_snapshots = g_malloc0(sizeof(int) * nb_sns); total = 0; for (i = 0; i < nb_sns; i++) { sn = &sn_tab[i]; @@ -2174,7 +2277,23 @@ void do_info_snapshots(Monitor *mon) monitor_printf(mon, "There is no suitable snapshot available\n"); } - qemu_free(sn_tab); - qemu_free(available_snapshots); + g_free(sn_tab); + g_free(available_snapshots); } + +void vmstate_register_ram(MemoryRegion *mr, DeviceState *dev) +{ + qemu_ram_set_idstr(memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK, + memory_region_name(mr), dev); +} + +void vmstate_unregister_ram(MemoryRegion *mr, DeviceState *dev) +{ + /* Nothing do to while the implementation is in RAMBlock */ +} + +void vmstate_register_ram_global(MemoryRegion *mr) +{ + vmstate_register_ram(mr, NULL); +}