From 6f2b811a61810a7fd9f9a5085de223f66b823342 Mon Sep 17 00:00:00 2001 From: "Zhanghaoyu (A)" Date: Thu, 7 Nov 2013 08:21:23 +0000 Subject: [PATCH 01/49] avoid a bogus COMPLETED->CANCELLED transition Avoid a bogus COMPLETED->CANCELLED transition. There is a period of time from the timing of setting COMPLETED state to that of migration thread exits, so during which it's problematic in COMPLETED->CANCELLED transition. Signed-off-by: Zeng Junliang Signed-off-by: Zhang Haoyu Reviewed-by: Paolo Bonzini Signed-off-by: Juan Quintela --- migration.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/migration.c b/migration.c index 2b1ab20c54..fd73b97bd6 100644 --- a/migration.c +++ b/migration.c @@ -326,9 +326,16 @@ void migrate_fd_error(MigrationState *s) static void migrate_fd_cancel(MigrationState *s) { + int old_state ; DPRINTF("cancelling migration\n"); - migrate_set_state(s, s->state, MIG_STATE_CANCELLED); + do { + old_state = s->state; + if (old_state != MIG_STATE_SETUP && old_state != MIG_STATE_ACTIVE) { + break; + } + migrate_set_state(s, old_state, MIG_STATE_CANCELLED); + } while (s->state != MIG_STATE_CANCELLED); } void add_migration_state_change_notifier(Notifier *notify) From 51cf4c1a99a172679c2949a2d58a2a4ee307b557 Mon Sep 17 00:00:00 2001 From: "Zhanghaoyu (A)" Date: Thu, 7 Nov 2013 11:01:15 +0000 Subject: [PATCH 02/49] introduce MIG_STATE_CANCELLING state Introduce MIG_STATE_CANCELLING state to avoid starting a new migration task while the previous one still exist. Signed-off-by: Zeng Junliang Signed-off-by: Zhang Haoyu Reviewed-by: Paolo Bonzini Signed-off-by: Juan Quintela --- migration.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/migration.c b/migration.c index fd73b97bd6..4ee341b06e 100644 --- a/migration.c +++ b/migration.c @@ -40,6 +40,7 @@ enum { MIG_STATE_ERROR = -1, MIG_STATE_NONE, MIG_STATE_SETUP, + MIG_STATE_CANCELLING, MIG_STATE_CANCELLED, MIG_STATE_ACTIVE, MIG_STATE_COMPLETED, @@ -196,6 +197,7 @@ MigrationInfo *qmp_query_migrate(Error **errp) info->has_total_time = false; break; case MIG_STATE_ACTIVE: + case MIG_STATE_CANCELLING: info->has_status = true; info->status = g_strdup("active"); info->has_total_time = true; @@ -282,6 +284,13 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, /* shared migration helpers */ +static void migrate_set_state(MigrationState *s, int old_state, int new_state) +{ + if (atomic_cmpxchg(&s->state, old_state, new_state) == new_state) { + trace_migrate_set_state(new_state); + } +} + static void migrate_fd_cleanup(void *opaque) { MigrationState *s = opaque; @@ -303,18 +312,14 @@ static void migrate_fd_cleanup(void *opaque) if (s->state != MIG_STATE_COMPLETED) { qemu_savevm_state_cancel(); + if (s->state == MIG_STATE_CANCELLING) { + migrate_set_state(s, MIG_STATE_CANCELLING, MIG_STATE_CANCELLED); + } } notifier_list_notify(&migration_state_notifiers, s); } -static void migrate_set_state(MigrationState *s, int old_state, int new_state) -{ - if (atomic_cmpxchg(&s->state, old_state, new_state) == new_state) { - trace_migrate_set_state(new_state); - } -} - void migrate_fd_error(MigrationState *s) { DPRINTF("setting error state\n"); @@ -334,8 +339,8 @@ static void migrate_fd_cancel(MigrationState *s) if (old_state != MIG_STATE_SETUP && old_state != MIG_STATE_ACTIVE) { break; } - migrate_set_state(s, old_state, MIG_STATE_CANCELLED); - } while (s->state != MIG_STATE_CANCELLED); + migrate_set_state(s, old_state, MIG_STATE_CANCELLING); + } while (s->state != MIG_STATE_CANCELLING); } void add_migration_state_change_notifier(Notifier *notify) @@ -412,7 +417,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, params.blk = has_blk && blk; params.shared = has_inc && inc; - if (s->state == MIG_STATE_ACTIVE || s->state == MIG_STATE_SETUP) { + if (s->state == MIG_STATE_ACTIVE || s->state == MIG_STATE_SETUP || + s->state == MIG_STATE_CANCELLING) { error_set(errp, QERR_MIGRATION_ACTIVE); return; } From 40596834c0d57a223124a956ccbe39dfeadc9f0e Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 25 Nov 2013 14:42:43 -0500 Subject: [PATCH 03/49] migration: Fix rate limit The migration thread appears to want to allow writeout to occur at full speed rather than being rate limited during completion of state saving, but sets the limit to INT_MAX when xfer_limit is INT64_MAX. This causes problems if there's more than 2GB of state left to save at this point. It probably ought to just be INT64_MAX instead. Signed-off-by: Matthew Garrett Reviewed-by: Paolo Bonzini Signed-off-by: Juan Quintela --- migration.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration.c b/migration.c index 4ee341b06e..e5f6b98023 100644 --- a/migration.c +++ b/migration.c @@ -596,7 +596,7 @@ static void *migration_thread(void *opaque) ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); if (ret >= 0) { - qemu_file_set_rate_limit(s->file, INT_MAX); + qemu_file_set_rate_limit(s->file, INT64_MAX); qemu_savevm_state_complete(s->file); } qemu_mutex_unlock_iothread(); From c961514fd9e707b6a39c60f687a711ffe0a33539 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 28 Nov 2013 12:01:10 -0200 Subject: [PATCH 04/49] qemu-file: Make a few functions non-static The QEMUFile code will be moved to qemu-file.c. This will require making the following functions non-static because they are used by the savevm.c code: * qemu_peek_byte() * qemu_peek_buffer() * qemu_file_skip() * qemu_file_set_error() Signed-off-by: Eduardo Habkost Reviewed-by: Orit Wasserman Signed-off-by: Juan Quintela --- include/migration/qemu-file.h | 4 ++++ savevm.c | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h index 0f757fbeb6..a191fb6d8d 100644 --- a/include/migration/qemu-file.h +++ b/include/migration/qemu-file.h @@ -121,8 +121,11 @@ static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v) void qemu_put_be16(QEMUFile *f, unsigned int v); void qemu_put_be32(QEMUFile *f, unsigned int v); void qemu_put_be64(QEMUFile *f, uint64_t v); +int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset); int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size); +int qemu_peek_byte(QEMUFile *f, int offset); int qemu_get_byte(QEMUFile *f); +void qemu_file_skip(QEMUFile *f, int size); void qemu_update_position(QEMUFile *f, size_t size); static inline unsigned int qemu_get_ubyte(QEMUFile *f) @@ -141,6 +144,7 @@ void qemu_file_reset_rate_limit(QEMUFile *f); void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate); int64_t qemu_file_get_rate_limit(QEMUFile *f); int qemu_file_get_error(QEMUFile *f); +void qemu_file_set_error(QEMUFile *f, int ret); void qemu_fflush(QEMUFile *f); static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv) diff --git a/savevm.c b/savevm.c index 3f912ddcf9..8da1d295b1 100644 --- a/savevm.c +++ b/savevm.c @@ -578,7 +578,7 @@ int qemu_file_get_error(QEMUFile *f) return f->last_error; } -static void qemu_file_set_error(QEMUFile *f, int ret) +void qemu_file_set_error(QEMUFile *f, int ret) { if (f->last_error == 0) { f->last_error = ret; @@ -827,14 +827,14 @@ void qemu_put_byte(QEMUFile *f, int v) } } -static void qemu_file_skip(QEMUFile *f, int size) +void qemu_file_skip(QEMUFile *f, int size) { if (f->buf_index + size <= f->buf_size) { f->buf_index += size; } } -static int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset) +int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset) { int pending; int index; @@ -880,7 +880,7 @@ int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size) return done; } -static int qemu_peek_byte(QEMUFile *f, int offset) +int qemu_peek_byte(QEMUFile *f, int offset) { int index = f->buf_index + offset; From b5503338ed1c66310f090cd9164abd244fc82631 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 28 Nov 2013 12:01:11 -0200 Subject: [PATCH 05/49] migration: Move QEMU_VM_* defines to migration/migration.h The VMState code will be moved to vmstate.c and it uses some of the QEMU_VM_* constants, so move it to a header. Signed-off-by: Eduardo Habkost Reviewed-by: Orit Wasserman Signed-off-by: Juan Quintela --- include/migration/migration.h | 11 +++++++++++ savevm.c | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/migration/migration.h b/include/migration/migration.h index 140e6b471c..bfa3951a61 100644 --- a/include/migration/migration.h +++ b/include/migration/migration.h @@ -23,6 +23,17 @@ #include "qapi-types.h" #include "exec/cpu-common.h" +#define QEMU_VM_FILE_MAGIC 0x5145564d +#define QEMU_VM_FILE_VERSION_COMPAT 0x00000002 +#define QEMU_VM_FILE_VERSION 0x00000003 + +#define QEMU_VM_EOF 0x00 +#define QEMU_VM_SECTION_START 0x01 +#define QEMU_VM_SECTION_PART 0x02 +#define QEMU_VM_SECTION_END 0x03 +#define QEMU_VM_SECTION_FULL 0x04 +#define QEMU_VM_SUBSECTION 0x05 + struct MigrationParams { bool blk; bool shared; diff --git a/savevm.c b/savevm.c index 8da1d295b1..46643f1cd8 100644 --- a/savevm.c +++ b/savevm.c @@ -1821,17 +1821,6 @@ static void vmstate_save(QEMUFile *f, SaveStateEntry *se) vmstate_save_state(f,se->vmsd, se->opaque); } -#define QEMU_VM_FILE_MAGIC 0x5145564d -#define QEMU_VM_FILE_VERSION_COMPAT 0x00000002 -#define QEMU_VM_FILE_VERSION 0x00000003 - -#define QEMU_VM_EOF 0x00 -#define QEMU_VM_SECTION_START 0x01 -#define QEMU_VM_SECTION_PART 0x02 -#define QEMU_VM_SECTION_END 0x03 -#define QEMU_VM_SECTION_FULL 0x04 -#define QEMU_VM_SUBSECTION 0x05 - bool qemu_savevm_state_blocked(Error **errp) { SaveStateEntry *se; From 5cecf414945e5ba40da893cbdaed554371d4eca9 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 28 Nov 2013 12:01:12 -0200 Subject: [PATCH 06/49] savevm: Convert all tabs to spaces Signed-off-by: Eduardo Habkost Reviewed-by: Orit Wasserman Signed-off-by: Juan Quintela --- savevm.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/savevm.c b/savevm.c index 46643f1cd8..3cdd5f2fc8 100644 --- a/savevm.c +++ b/savevm.c @@ -53,7 +53,7 @@ #define ARP_OP_REQUEST_REV 0x3 static int announce_self_create(uint8_t *buf, - uint8_t *mac_addr) + uint8_t *mac_addr) { /* Ethernet header. */ memset(buf, 0xff, 6); /* destination MAC addr */ @@ -100,16 +100,16 @@ static void qemu_announce_self_once(void *opaque) timer_mod(timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 50 + (SELF_ANNOUNCE_ROUNDS - count - 1) * 100); } else { - timer_del(timer); - timer_free(timer); + timer_del(timer); + timer_free(timer); } } void qemu_announce_self(void) { - static QEMUTimer *timer; - timer = timer_new_ms(QEMU_CLOCK_REALTIME, qemu_announce_self_once, &timer); - qemu_announce_self_once(&timer); + static QEMUTimer *timer; + timer = timer_new_ms(QEMU_CLOCK_REALTIME, qemu_announce_self_once, &timer); + qemu_announce_self_once(&timer); } /***********************************************************/ @@ -420,8 +420,8 @@ QEMUFile *qemu_fdopen(int fd, const char *mode) QEMUFileSocket *s; if (mode == NULL || - (mode[0] != 'r' && mode[0] != 'w') || - mode[1] != 'b' || mode[2] != 0) { + (mode[0] != 'r' && mode[0] != 'w') || + mode[1] != 'b' || mode[2] != 0) { fprintf(stderr, "qemu_fdopen: Argument validity check failed\n"); return NULL; } @@ -1959,7 +1959,7 @@ void qemu_savevm_state_complete(QEMUFile *f) int len; if ((!se->ops || !se->ops->save_state) && !se->vmsd) { - continue; + continue; } trace_savevm_section_start(); /* Section type */ From 38ff78d363da0d2e0ad27853036b08eaa52ef18d Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 28 Nov 2013 12:01:13 -0200 Subject: [PATCH 07/49] savevm.c: Coding style fixes Signed-off-by: Eduardo Habkost Reviewed-by: Orit Wasserman Signed-off-by: Juan Quintela --- savevm.c | 75 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/savevm.c b/savevm.c index 3cdd5f2fc8..66b8b5ec8b 100644 --- a/savevm.c +++ b/savevm.c @@ -137,14 +137,12 @@ struct QEMUFile { int last_error; }; -typedef struct QEMUFileStdio -{ +typedef struct QEMUFileStdio { FILE *stdio_file; QEMUFile *file; } QEMUFileStdio; -typedef struct QEMUFileSocket -{ +typedef struct QEMUFileSocket { int fd; QEMUFile *file; } QEMUFileSocket; @@ -208,7 +206,8 @@ static int stdio_get_fd(void *opaque) return fileno(s->stdio_file); } -static int stdio_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); @@ -310,7 +309,7 @@ QEMUFile *qemu_popen_cmd(const char *command, const char *mode) s->stdio_file = stdio_file; - if(mode[0] == 'r') { + if (mode[0] == 'r') { s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops); } else { s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops); @@ -429,7 +428,7 @@ QEMUFile *qemu_fdopen(int fd, const char *mode) s = g_malloc0(sizeof(QEMUFileSocket)); s->fd = fd; - if(mode[0] == 'r') { + if (mode[0] == 'r') { s->file = qemu_fopen_ops(s, &unix_read_ops); } else { s->file = qemu_fopen_ops(s, &unix_write_ops); @@ -491,10 +490,11 @@ QEMUFile *qemu_fopen(const char *filename, const char *mode) s = g_malloc0(sizeof(QEMUFileStdio)); s->stdio_file = fopen(filename, mode); - if (!s->stdio_file) + if (!s->stdio_file) { goto fail; - - if(mode[0] == 'w') { + } + + if (mode[0] == 'w') { s->file = qemu_fopen_ops(s, &stdio_file_write_ops); } else { s->file = qemu_fopen_ops(s, &stdio_file_read_ops); @@ -550,8 +550,9 @@ static const QEMUFileOps bdrv_write_ops = { static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable) { - if (is_writable) + if (is_writable) { return qemu_fopen_ops(bs, &bdrv_write_ops); + } return qemu_fopen_ops(bs, &bdrv_read_ops); } @@ -703,8 +704,9 @@ static void qemu_fill_buffer(QEMUFile *f) f->pos += len; } else if (len == 0) { qemu_file_set_error(f, -EIO); - } else if (len != -EAGAIN) + } else if (len != -EAGAIN) { qemu_file_set_error(f, len); + } } int qemu_get_fd(QEMUFile *f) @@ -791,8 +793,9 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) while (size > 0) { l = IO_BUF_SIZE - f->buf_index; - if (l > size) + if (l > size) { l = size; + } memcpy(f->buf + f->buf_index, buf, l); f->bytes_xfer += l; if (f->ops->writev_buffer) { @@ -1100,8 +1103,9 @@ static int get_int32_equal(QEMUFile *f, void *pv, size_t size) int32_t v2; qemu_get_sbe32s(f, &v2); - if (*v == v2) + if (*v == v2) { return 0; + } return -EINVAL; } @@ -1120,8 +1124,9 @@ static int get_int32_le(QEMUFile *f, void *pv, size_t size) int32_t new; qemu_get_sbe32s(f, &new); - if (*old <= new) + if (*old <= new) { return 0; + } return -EINVAL; } @@ -1287,8 +1292,9 @@ static int get_uint8_equal(QEMUFile *f, void *pv, size_t size) uint8_t v2; qemu_get_8s(f, &v2); - if (*v == v2) + if (*v == v2) { return 0; + } return -EINVAL; } @@ -1307,8 +1313,9 @@ static int get_uint16_equal(QEMUFile *f, void *pv, size_t size) uint16_t v2; qemu_get_be16s(f, &v2); - if (*v == v2) + if (*v == v2) { return 0; + } return -EINVAL; } @@ -1502,8 +1509,9 @@ static int calculate_compat_instance_id(const char *idstr) int instance_id = 0; QTAILQ_FOREACH(se, &savevm_handlers, entry) { - if (!se->compat) + if (!se->compat) { continue; + } if (strcmp(idstr, se->compat->idstr) == 0 && instance_id <= se->compat->instance_id) { @@ -1690,10 +1698,11 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, } if (vmsd->pre_load) { int ret = vmsd->pre_load(opaque); - if (ret) + if (ret) { return ret; + } } - while(field->name) { + while (field->name) { if ((field->field_exists && field->field_exists(opaque, version_id)) || (!field->field_exists && @@ -1729,7 +1738,8 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, addr = *(void **)addr; } if (field->flags & VMS_STRUCT) { - ret = vmstate_load_state(f, field->vmsd, addr, field->vmsd->version_id); + ret = vmstate_load_state(f, field->vmsd, addr, + field->vmsd->version_id); } else { ret = field->info->get(f, addr, size); @@ -1759,7 +1769,7 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, if (vmsd->pre_save) { vmsd->pre_save(opaque); } - while(field->name) { + while (field->name) { if (!field->field_exists || field->field_exists(opaque, vmsd->version_id)) { void *base_addr = opaque + field->offset; @@ -1818,7 +1828,7 @@ static void vmstate_save(QEMUFile *f, SaveStateEntry *se) se->ops->save_state(f, se->opaque); return; } - vmstate_save_state(f,se->vmsd, se->opaque); + vmstate_save_state(f, se->vmsd, se->opaque); } bool qemu_savevm_state_blocked(Error **errp) @@ -1846,7 +1856,7 @@ void qemu_savevm_state_begin(QEMUFile *f, } se->ops->set_params(params, se->opaque); } - + qemu_put_be32(f, QEMU_VM_FILE_MAGIC); qemu_put_be32(f, QEMU_VM_FILE_VERSION); @@ -2106,7 +2116,7 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id) static const VMStateDescription *vmstate_get_subsection(const VMStateSubsection *sub, char *idstr) { - while(sub && sub->needed) { + while (sub && sub->needed) { if (strcmp(idstr, sub->vmsd->name) == 0) { return sub->vmsd; } @@ -2198,16 +2208,18 @@ int qemu_loadvm_state(QEMUFile *f) } v = qemu_get_be32(f); - if (v != QEMU_VM_FILE_MAGIC) + if (v != QEMU_VM_FILE_MAGIC) { return -EINVAL; + } v = qemu_get_be32(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) + 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; @@ -2326,8 +2338,7 @@ static int del_existing_snapshots(Monitor *mon, const char *name) bs = NULL; while ((bs = bdrv_next(bs))) { if (bdrv_can_snapshot(bs) && - bdrv_snapshot_find(bs, snapshot, name) >= 0) - { + bdrv_snapshot_find(bs, snapshot, name) >= 0) { bdrv_snapshot_delete_by_id_or_name(bs, name, &err); if (error_is_set(&err)) { monitor_printf(mon, @@ -2437,8 +2448,9 @@ void do_savevm(Monitor *mon, const QDict *qdict) } the_end: - if (saved_vm_running) + if (saved_vm_running) { vm_start(); + } } void qmp_xen_save_devices_state(const char *filename, Error **errp) @@ -2462,8 +2474,9 @@ void qmp_xen_save_devices_state(const char *filename, Error **errp) } the_end: - if (saved_vm_running) + if (saved_vm_running) { vm_start(); + } } int load_vmstate(const char *name) From 51b7fa5b3cf7678263382039a884aa2e74742bb6 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 28 Nov 2013 12:01:14 -0200 Subject: [PATCH 08/49] savevm.c: Coding style fix Signed-off-by: Eduardo Habkost Reviewed-by: Orit Wasserman Signed-off-by: Juan Quintela --- savevm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/savevm.c b/savevm.c index 66b8b5ec8b..bdb90ef09b 100644 --- a/savevm.c +++ b/savevm.c @@ -2114,7 +2114,8 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id) return NULL; } -static const VMStateDescription *vmstate_get_subsection(const VMStateSubsection *sub, char *idstr) +static const VMStateDescription * + vmstate_get_subsection(const VMStateSubsection *sub, char *idstr) { while (sub && sub->needed) { if (strcmp(idstr, sub->vmsd->name) == 0) { From b6fcfa59fa6ad6d61603e701e006d4ec8f4c9d11 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 28 Nov 2013 12:01:15 -0200 Subject: [PATCH 09/49] vmstate: Move VMState code to vmstate.c This will allow unit tests to be written for VMState code without pulling dependencies from the savevm code. Signed-off-by: Eduardo Habkost Reviewed-by: Orit Wasserman Signed-off-by: Juan Quintela --- Makefile.objs | 1 + savevm.c | 645 ------------------------------------------------- vmstate.c | 650 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 651 insertions(+), 645 deletions(-) create mode 100644 vmstate.c diff --git a/Makefile.objs b/Makefile.objs index 2b6c1fe2a8..23eda2b19b 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -51,6 +51,7 @@ common-obj-$(CONFIG_POSIX) += os-posix.o common-obj-$(CONFIG_LINUX) += fsdev/ common-obj-y += migration.o migration-tcp.o +common-obj-y += vmstate.o common-obj-$(CONFIG_RDMA) += migration-rdma.o common-obj-y += qemu-char.o #aio.o common-obj-y += block-migration.o diff --git a/savevm.c b/savevm.c index bdb90ef09b..3f13a6a4d2 100644 --- a/savevm.c +++ b/savevm.c @@ -38,7 +38,6 @@ #include "exec/memory.h" #include "qmp-commands.h" #include "trace.h" -#include "qemu/bitops.h" #include "qemu/iov.h" #include "block/snapshot.h" #include "block/qapi.h" @@ -1010,344 +1009,6 @@ void timer_get(QEMUFile *f, QEMUTimer *ts) } -/* bool */ - -static int get_bool(QEMUFile *f, void *pv, size_t size) -{ - bool *v = pv; - *v = qemu_get_byte(f); - return 0; -} - -static void put_bool(QEMUFile *f, void *pv, size_t size) -{ - bool *v = pv; - qemu_put_byte(f, *v); -} - -const VMStateInfo vmstate_info_bool = { - .name = "bool", - .get = get_bool, - .put = put_bool, -}; - -/* 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, -}; - -/* 32 bit uint. See that the received value is the same than the one - in the field */ - -static int get_uint32_equal(QEMUFile *f, void *pv, size_t size) -{ - uint32_t *v = pv; - uint32_t v2; - qemu_get_be32s(f, &v2); - - if (*v == v2) { - return 0; - } - return -EINVAL; -} - -const VMStateInfo vmstate_info_uint32_equal = { - .name = "uint32 equal", - .get = get_uint32_equal, - .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, -}; - -/* 64 bit unsigned int. See that the received value is the same than the one - in the field */ - -static int get_uint64_equal(QEMUFile *f, void *pv, size_t size) -{ - uint64_t *v = pv; - uint64_t v2; - qemu_get_be64s(f, &v2); - - if (*v == v2) { - return 0; - } - return -EINVAL; -} - -const VMStateInfo vmstate_info_uint64_equal = { - .name = "int64 equal", - .get = get_uint64_equal, - .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 = "uint8 equal", - .get = get_uint8_equal, - .put = put_uint8, -}; - -/* 16 bit unsigned int int. See that the received value is the same than the one - in the field */ - -static int get_uint16_equal(QEMUFile *f, void *pv, size_t size) -{ - uint16_t *v = pv; - uint16_t v2; - qemu_get_be16s(f, &v2); - - if (*v == v2) { - return 0; - } - return -EINVAL; -} - -const VMStateInfo vmstate_info_uint16_equal = { - .name = "uint16 equal", - .get = get_uint16_equal, - .put = put_uint16, -}; - -/* floating point */ - -static int get_float64(QEMUFile *f, void *pv, size_t size) -{ - float64 *v = pv; - - *v = make_float64(qemu_get_be64(f)); - return 0; -} - -static void put_float64(QEMUFile *f, void *pv, size_t size) -{ - uint64_t *v = pv; - - qemu_put_be64(f, float64_val(*v)); -} - -const VMStateInfo vmstate_info_float64 = { - .name = "float64", - .get = get_float64, - .put = put_float64, -}; - /* timers */ static int get_timer(QEMUFile *f, void *pv, size_t size) @@ -1369,100 +1030,6 @@ const VMStateInfo vmstate_info_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, -}; - -/* unused buffers: space that was used for some fields that are - not useful anymore */ - -static int get_unused_buffer(QEMUFile *f, void *pv, size_t size) -{ - uint8_t buf[1024]; - int block_len; - - while (size > 0) { - block_len = MIN(sizeof(buf), size); - size -= block_len; - qemu_get_buffer(f, buf, block_len); - } - return 0; -} - -static void put_unused_buffer(QEMUFile *f, void *pv, size_t size) -{ - static const uint8_t buf[1024]; - int block_len; - - while (size > 0) { - block_len = MIN(sizeof(buf), size); - size -= block_len; - qemu_put_buffer(f, buf, block_len); - } -} - -const VMStateInfo vmstate_info_unused_buffer = { - .name = "unused_buffer", - .get = get_unused_buffer, - .put = put_unused_buffer, -}; - -/* bitmaps (as defined by bitmap.h). Note that size here is the size - * of the bitmap in bits. The on-the-wire format of a bitmap is 64 - * bit words with the bits in big endian order. The in-memory format - * is an array of 'unsigned long', which may be either 32 or 64 bits. - */ -/* This is the number of 64 bit words sent over the wire */ -#define BITS_TO_U64S(nr) DIV_ROUND_UP(nr, 64) -static int get_bitmap(QEMUFile *f, void *pv, size_t size) -{ - unsigned long *bmp = pv; - int i, idx = 0; - for (i = 0; i < BITS_TO_U64S(size); i++) { - uint64_t w = qemu_get_be64(f); - bmp[idx++] = w; - if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) { - bmp[idx++] = w >> 32; - } - } - return 0; -} - -static void put_bitmap(QEMUFile *f, void *pv, size_t size) -{ - unsigned long *bmp = pv; - int i, idx = 0; - for (i = 0; i < BITS_TO_U64S(size); i++) { - uint64_t w = bmp[idx++]; - if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) { - w |= ((uint64_t)bmp[idx++]) << 32; - } - qemu_put_be64(f, w); - } -} - -const VMStateInfo vmstate_info_bitmap = { - .name = "bitmap", - .get = get_bitmap, - .put = put_bitmap, -}; typedef struct CompatEntry { char idstr[256]; @@ -1676,144 +1243,6 @@ void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, } } -static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque); -static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque); - -int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque, int version_id) -{ - VMStateField *field = vmsd->fields; - int ret; - - 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 i, n_elems = 1; - int size = field->size; - - if (field->flags & VMS_VBUFFER) { - size = *(int32_t *)(opaque+field->size_offset); - if (field->flags & VMS_MULTIPLY) { - size *= field->size; - } - } - 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); - } - if (field->flags & VMS_POINTER) { - base_addr = *(void **)base_addr + field->start; - } - for (i = 0; i < n_elems; i++) { - void *addr = base_addr + 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, size); - - } - if (ret < 0) { - return ret; - } - } - } - field++; - } - ret = vmstate_subsection_load(f, vmsd, opaque); - if (ret != 0) { - return ret; - } - 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; - int size = field->size; - - if (field->flags & VMS_VBUFFER) { - size = *(int32_t *)(opaque+field->size_offset); - if (field->flags & VMS_MULTIPLY) { - size *= field->size; - } - } - 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); - } - if (field->flags & VMS_POINTER) { - base_addr = *(void **)base_addr + field->start; - } - for (i = 0; i < n_elems; i++) { - void *addr = base_addr + size * i; - - if (field->flags & VMS_ARRAY_OF_POINTER) { - addr = *(void **)addr; - } - if (field->flags & VMS_STRUCT) { - vmstate_save_state(f, field->vmsd, addr); - } else { - field->info->put(f, addr, size); - } - } - } - field++; - } - vmstate_subsection_save(f, vmsd, opaque); -} - static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id) { if (!se->vmsd) { /* Old style */ @@ -2114,80 +1543,6 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id) return NULL; } -static const VMStateDescription * - vmstate_get_subsection(const VMStateSubsection *sub, char *idstr) -{ - while (sub && sub->needed) { - if (strcmp(idstr, sub->vmsd->name) == 0) { - return sub->vmsd; - } - sub++; - } - return NULL; -} - -static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque) -{ - while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) { - char idstr[256]; - 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" */ - return 0; - } - size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2); - if (size != len) { - return 0; - } - idstr[size] = 0; - - 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; - } - 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; - } - } - return 0; -} - -static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque) -{ - const VMStateSubsection *sub = vmsd->subsections; - - while (sub && sub->needed) { - if (sub->needed(opaque)) { - const VMStateDescription *vmsd = sub->vmsd; - uint8_t len; - - qemu_put_byte(f, QEMU_VM_SUBSECTION); - len = strlen(vmsd->name); - qemu_put_byte(f, len); - qemu_put_buffer(f, (uint8_t *)vmsd->name, len); - qemu_put_be32(f, vmsd->version_id); - vmstate_save_state(f, vmsd, opaque); - } - sub++; - } -} - typedef struct LoadStateEntry { QLIST_ENTRY(LoadStateEntry) entry; SaveStateEntry *se; diff --git a/vmstate.c b/vmstate.c new file mode 100644 index 0000000000..284b080f46 --- /dev/null +++ b/vmstate.c @@ -0,0 +1,650 @@ +#include "qemu-common.h" +#include "migration/migration.h" +#include "migration/qemu-file.h" +#include "migration/vmstate.h" +#include "qemu/bitops.h" + +static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque); +static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque); + +int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque, int version_id) +{ + VMStateField *field = vmsd->fields; + int ret; + + 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 i, n_elems = 1; + int size = field->size; + + if (field->flags & VMS_VBUFFER) { + size = *(int32_t *)(opaque+field->size_offset); + if (field->flags & VMS_MULTIPLY) { + size *= field->size; + } + } + 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); + } + if (field->flags & VMS_POINTER) { + base_addr = *(void **)base_addr + field->start; + } + for (i = 0; i < n_elems; i++) { + void *addr = base_addr + 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, size); + + } + if (ret < 0) { + return ret; + } + } + } + field++; + } + ret = vmstate_subsection_load(f, vmsd, opaque); + if (ret != 0) { + return ret; + } + 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; + int size = field->size; + + if (field->flags & VMS_VBUFFER) { + size = *(int32_t *)(opaque+field->size_offset); + if (field->flags & VMS_MULTIPLY) { + size *= field->size; + } + } + 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); + } + if (field->flags & VMS_POINTER) { + base_addr = *(void **)base_addr + field->start; + } + for (i = 0; i < n_elems; i++) { + void *addr = base_addr + size * i; + + if (field->flags & VMS_ARRAY_OF_POINTER) { + addr = *(void **)addr; + } + if (field->flags & VMS_STRUCT) { + vmstate_save_state(f, field->vmsd, addr); + } else { + field->info->put(f, addr, size); + } + } + } + field++; + } + vmstate_subsection_save(f, vmsd, opaque); +} + +static const VMStateDescription * + vmstate_get_subsection(const VMStateSubsection *sub, char *idstr) +{ + while (sub && sub->needed) { + if (strcmp(idstr, sub->vmsd->name) == 0) { + return sub->vmsd; + } + sub++; + } + return NULL; +} + +static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque) +{ + while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) { + char idstr[256]; + 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" */ + return 0; + } + size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2); + if (size != len) { + return 0; + } + idstr[size] = 0; + + 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; + } + 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; + } + } + return 0; +} + +static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque) +{ + const VMStateSubsection *sub = vmsd->subsections; + + while (sub && sub->needed) { + if (sub->needed(opaque)) { + const VMStateDescription *vmsd = sub->vmsd; + uint8_t len; + + qemu_put_byte(f, QEMU_VM_SUBSECTION); + len = strlen(vmsd->name); + qemu_put_byte(f, len); + qemu_put_buffer(f, (uint8_t *)vmsd->name, len); + qemu_put_be32(f, vmsd->version_id); + vmstate_save_state(f, vmsd, opaque); + } + sub++; + } +} + +/* bool */ + +static int get_bool(QEMUFile *f, void *pv, size_t size) +{ + bool *v = pv; + *v = qemu_get_byte(f); + return 0; +} + +static void put_bool(QEMUFile *f, void *pv, size_t size) +{ + bool *v = pv; + qemu_put_byte(f, *v); +} + +const VMStateInfo vmstate_info_bool = { + .name = "bool", + .get = get_bool, + .put = put_bool, +}; + +/* 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, +}; + +/* 32 bit uint. See that the received value is the same than the one + in the field */ + +static int get_uint32_equal(QEMUFile *f, void *pv, size_t size) +{ + uint32_t *v = pv; + uint32_t v2; + qemu_get_be32s(f, &v2); + + if (*v == v2) { + return 0; + } + return -EINVAL; +} + +const VMStateInfo vmstate_info_uint32_equal = { + .name = "uint32 equal", + .get = get_uint32_equal, + .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, +}; + +/* 64 bit unsigned int. See that the received value is the same than the one + in the field */ + +static int get_uint64_equal(QEMUFile *f, void *pv, size_t size) +{ + uint64_t *v = pv; + uint64_t v2; + qemu_get_be64s(f, &v2); + + if (*v == v2) { + return 0; + } + return -EINVAL; +} + +const VMStateInfo vmstate_info_uint64_equal = { + .name = "int64 equal", + .get = get_uint64_equal, + .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 = "uint8 equal", + .get = get_uint8_equal, + .put = put_uint8, +}; + +/* 16 bit unsigned int int. See that the received value is the same than the one + in the field */ + +static int get_uint16_equal(QEMUFile *f, void *pv, size_t size) +{ + uint16_t *v = pv; + uint16_t v2; + qemu_get_be16s(f, &v2); + + if (*v == v2) { + return 0; + } + return -EINVAL; +} + +const VMStateInfo vmstate_info_uint16_equal = { + .name = "uint16 equal", + .get = get_uint16_equal, + .put = put_uint16, +}; + +/* floating point */ + +static int get_float64(QEMUFile *f, void *pv, size_t size) +{ + float64 *v = pv; + + *v = make_float64(qemu_get_be64(f)); + return 0; +} + +static void put_float64(QEMUFile *f, void *pv, size_t size) +{ + uint64_t *v = pv; + + qemu_put_be64(f, float64_val(*v)); +} + +const VMStateInfo vmstate_info_float64 = { + .name = "float64", + .get = get_float64, + .put = put_float64, +}; + +/* 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, +}; + +/* unused buffers: space that was used for some fields that are + not useful anymore */ + +static int get_unused_buffer(QEMUFile *f, void *pv, size_t size) +{ + uint8_t buf[1024]; + int block_len; + + while (size > 0) { + block_len = MIN(sizeof(buf), size); + size -= block_len; + qemu_get_buffer(f, buf, block_len); + } + return 0; +} + +static void put_unused_buffer(QEMUFile *f, void *pv, size_t size) +{ + static const uint8_t buf[1024]; + int block_len; + + while (size > 0) { + block_len = MIN(sizeof(buf), size); + size -= block_len; + qemu_put_buffer(f, buf, block_len); + } +} + +const VMStateInfo vmstate_info_unused_buffer = { + .name = "unused_buffer", + .get = get_unused_buffer, + .put = put_unused_buffer, +}; + +/* bitmaps (as defined by bitmap.h). Note that size here is the size + * of the bitmap in bits. The on-the-wire format of a bitmap is 64 + * bit words with the bits in big endian order. The in-memory format + * is an array of 'unsigned long', which may be either 32 or 64 bits. + */ +/* This is the number of 64 bit words sent over the wire */ +#define BITS_TO_U64S(nr) DIV_ROUND_UP(nr, 64) +static int get_bitmap(QEMUFile *f, void *pv, size_t size) +{ + unsigned long *bmp = pv; + int i, idx = 0; + for (i = 0; i < BITS_TO_U64S(size); i++) { + uint64_t w = qemu_get_be64(f); + bmp[idx++] = w; + if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) { + bmp[idx++] = w >> 32; + } + } + return 0; +} + +static void put_bitmap(QEMUFile *f, void *pv, size_t size) +{ + unsigned long *bmp = pv; + int i, idx = 0; + for (i = 0; i < BITS_TO_U64S(size); i++) { + uint64_t w = bmp[idx++]; + if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) { + w |= ((uint64_t)bmp[idx++]) << 32; + } + qemu_put_be64(f, w); + } +} + +const VMStateInfo vmstate_info_bitmap = { + .name = "bitmap", + .get = get_bitmap, + .put = put_bitmap, +}; From 093c455a8c6d8f715eabd8c8d346f08f17d686ec Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 28 Nov 2013 12:01:16 -0200 Subject: [PATCH 10/49] qemu-file: Move QEMUFile code to qemu-file.c Signed-off-by: Eduardo Habkost Reviewed-by: Orit Wasserman Signed-off-by: Juan Quintela --- Makefile.objs | 1 + qemu-file.c | 826 ++++++++++++++++++++++++++++++++++++++++++++++++++ savevm.c | 820 ------------------------------------------------- 3 files changed, 827 insertions(+), 820 deletions(-) create mode 100644 qemu-file.c diff --git a/Makefile.objs b/Makefile.objs index 23eda2b19b..857bb53ae4 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -52,6 +52,7 @@ common-obj-$(CONFIG_LINUX) += fsdev/ common-obj-y += migration.o migration-tcp.o common-obj-y += vmstate.o +common-obj-y += qemu-file.o common-obj-$(CONFIG_RDMA) += migration-rdma.o common-obj-y += qemu-char.o #aio.o common-obj-y += block-migration.o diff --git a/qemu-file.c b/qemu-file.c new file mode 100644 index 0000000000..9473b674ba --- /dev/null +++ b/qemu-file.c @@ -0,0 +1,826 @@ +#include "qemu-common.h" +#include "qemu/iov.h" +#include "qemu/sockets.h" +#include "block/coroutine.h" +#include "migration/migration.h" +#include "migration/qemu-file.h" + +#define IO_BUF_SIZE 32768 +#define MAX_IOV_SIZE MIN(IOV_MAX, 64) + +struct QEMUFile { + const QEMUFileOps *ops; + void *opaque; + + int64_t bytes_xfer; + int64_t xfer_limit; + + int64_t pos; /* start of buffer when writing, end of buffer + when reading */ + int buf_index; + int buf_size; /* 0 when writing */ + uint8_t buf[IO_BUF_SIZE]; + + struct iovec iov[MAX_IOV_SIZE]; + unsigned int iovcnt; + + int last_error; +}; + +typedef struct QEMUFileStdio { + FILE *stdio_file; + QEMUFile *file; +} QEMUFileStdio; + +typedef struct QEMUFileSocket { + int fd; + QEMUFile *file; +} QEMUFileSocket; + +static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, + int64_t pos) +{ + QEMUFileSocket *s = opaque; + ssize_t len; + ssize_t size = iov_size(iov, iovcnt); + + len = iov_send(s->fd, iov, iovcnt, 0, size); + if (len < size) { + len = -socket_error(); + } + return len; +} + +static int socket_get_fd(void *opaque) +{ + QEMUFileSocket *s = opaque; + + return s->fd; +} + +static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +{ + QEMUFileSocket *s = opaque; + ssize_t len; + + for (;;) { + len = qemu_recv(s->fd, buf, size, 0); + if (len != -1) { + break; + } + if (socket_error() == EAGAIN) { + yield_until_fd_readable(s->fd); + } else if (socket_error() != EINTR) { + break; + } + } + + if (len == -1) { + len = -socket_error(); + } + return len; +} + +static int socket_close(void *opaque) +{ + QEMUFileSocket *s = opaque; + closesocket(s->fd); + g_free(s); + return 0; +} + +static int stdio_get_fd(void *opaque) +{ + QEMUFileStdio *s = opaque; + + return fileno(s->stdio_file); +} + +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) +{ + QEMUFileStdio *s = opaque; + FILE *fp = s->stdio_file; + int bytes; + + for (;;) { + clearerr(fp); + bytes = fread(buf, 1, size, fp); + if (bytes != 0 || !ferror(fp)) { + break; + } + if (errno == EAGAIN) { + yield_until_fd_readable(fileno(fp)); + } else if (errno != EINTR) { + break; + } + } + return bytes; +} + +static int stdio_pclose(void *opaque) +{ + QEMUFileStdio *s = opaque; + int ret; + ret = pclose(s->stdio_file); + if (ret == -1) { + ret = -errno; + } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) { + /* close succeeded, but non-zero exit code: */ + ret = -EIO; /* fake errno value */ + } + g_free(s); + return ret; +} + +static int stdio_fclose(void *opaque) +{ + QEMUFileStdio *s = opaque; + int ret = 0; + + if (s->file->ops->put_buffer || s->file->ops->writev_buffer) { + int fd = fileno(s->stdio_file); + struct stat st; + + ret = fstat(fd, &st); + if (ret == 0 && S_ISREG(st.st_mode)) { + /* + * If the file handle is a regular file make sure the + * data is flushed to disk before signaling success. + */ + ret = fsync(fd); + if (ret != 0) { + ret = -errno; + return ret; + } + } + } + if (fclose(s->stdio_file) == EOF) { + ret = -errno; + } + g_free(s); + return ret; +} + +static const QEMUFileOps stdio_pipe_read_ops = { + .get_fd = stdio_get_fd, + .get_buffer = stdio_get_buffer, + .close = stdio_pclose +}; + +static const QEMUFileOps stdio_pipe_write_ops = { + .get_fd = stdio_get_fd, + .put_buffer = stdio_put_buffer, + .close = stdio_pclose +}; + +QEMUFile *qemu_popen_cmd(const char *command, const char *mode) +{ + FILE *stdio_file; + QEMUFileStdio *s; + + if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { + fprintf(stderr, "qemu_popen: Argument validity check failed\n"); + return NULL; + } + + stdio_file = popen(command, mode); + if (stdio_file == NULL) { + return NULL; + } + + s = g_malloc0(sizeof(QEMUFileStdio)); + + s->stdio_file = stdio_file; + + if (mode[0] == 'r') { + s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops); + } else { + s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops); + } + return s->file; +} + +static const QEMUFileOps stdio_file_read_ops = { + .get_fd = stdio_get_fd, + .get_buffer = stdio_get_buffer, + .close = stdio_fclose +}; + +static const QEMUFileOps stdio_file_write_ops = { + .get_fd = stdio_get_fd, + .put_buffer = stdio_put_buffer, + .close = stdio_fclose +}; + +static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, + int64_t pos) +{ + QEMUFileSocket *s = opaque; + ssize_t len, offset; + ssize_t size = iov_size(iov, iovcnt); + ssize_t total = 0; + + assert(iovcnt > 0); + offset = 0; + while (size > 0) { + /* Find the next start position; skip all full-sized vector elements */ + while (offset >= iov[0].iov_len) { + offset -= iov[0].iov_len; + iov++, iovcnt--; + } + + /* skip `offset' bytes from the (now) first element, undo it on exit */ + assert(iovcnt > 0); + iov[0].iov_base += offset; + iov[0].iov_len -= offset; + + do { + len = writev(s->fd, iov, iovcnt); + } while (len == -1 && errno == EINTR); + if (len == -1) { + return -errno; + } + + /* Undo the changes above */ + iov[0].iov_base -= offset; + iov[0].iov_len += offset; + + /* Prepare for the next iteration */ + offset += len; + total += len; + size -= len; + } + + return total; +} + +static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +{ + QEMUFileSocket *s = opaque; + ssize_t len; + + for (;;) { + len = read(s->fd, buf, size); + if (len != -1) { + break; + } + if (errno == EAGAIN) { + yield_until_fd_readable(s->fd); + } else if (errno != EINTR) { + break; + } + } + + if (len == -1) { + len = -errno; + } + return len; +} + +static int unix_close(void *opaque) +{ + QEMUFileSocket *s = opaque; + close(s->fd); + g_free(s); + return 0; +} + +static const QEMUFileOps unix_read_ops = { + .get_fd = socket_get_fd, + .get_buffer = unix_get_buffer, + .close = unix_close +}; + +static const QEMUFileOps unix_write_ops = { + .get_fd = socket_get_fd, + .writev_buffer = unix_writev_buffer, + .close = unix_close +}; + +QEMUFile *qemu_fdopen(int fd, const char *mode) +{ + QEMUFileSocket *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 = g_malloc0(sizeof(QEMUFileSocket)); + s->fd = fd; + + if (mode[0] == 'r') { + s->file = qemu_fopen_ops(s, &unix_read_ops); + } else { + s->file = qemu_fopen_ops(s, &unix_write_ops); + } + return s->file; +} + +static const QEMUFileOps socket_read_ops = { + .get_fd = socket_get_fd, + .get_buffer = socket_get_buffer, + .close = socket_close +}; + +static const QEMUFileOps socket_write_ops = { + .get_fd = socket_get_fd, + .writev_buffer = socket_writev_buffer, + .close = socket_close +}; + +bool qemu_file_mode_is_not_valid(const char *mode) +{ + if (mode == NULL || + (mode[0] != 'r' && mode[0] != 'w') || + mode[1] != 'b' || mode[2] != 0) { + fprintf(stderr, "qemu_fopen: Argument validity check failed\n"); + return true; + } + + return false; +} + +QEMUFile *qemu_fopen_socket(int fd, const char *mode) +{ + QEMUFileSocket *s; + + if (qemu_file_mode_is_not_valid(mode)) { + return NULL; + } + + s = g_malloc0(sizeof(QEMUFileSocket)); + s->fd = fd; + if (mode[0] == 'w') { + qemu_set_block(s->fd); + s->file = qemu_fopen_ops(s, &socket_write_ops); + } else { + s->file = qemu_fopen_ops(s, &socket_read_ops); + } + return s->file; +} + +QEMUFile *qemu_fopen(const char *filename, const char *mode) +{ + QEMUFileStdio *s; + + if (qemu_file_mode_is_not_valid(mode)) { + return NULL; + } + + s = g_malloc0(sizeof(QEMUFileStdio)); + + s->stdio_file = fopen(filename, mode); + if (!s->stdio_file) { + goto fail; + } + + if (mode[0] == 'w') { + s->file = qemu_fopen_ops(s, &stdio_file_write_ops); + } else { + s->file = qemu_fopen_ops(s, &stdio_file_read_ops); + } + return s->file; +fail: + g_free(s); + return NULL; +} + +QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops) +{ + QEMUFile *f; + + f = g_malloc0(sizeof(QEMUFile)); + + f->opaque = opaque; + f->ops = ops; + return f; +} + +/* + * Get last error for stream f + * + * Return negative error value if there has been an error on previous + * operations, return 0 if no error happened. + * + */ +int qemu_file_get_error(QEMUFile *f) +{ + return f->last_error; +} + +void qemu_file_set_error(QEMUFile *f, int ret) +{ + if (f->last_error == 0) { + f->last_error = ret; + } +} + +static inline bool qemu_file_is_writable(QEMUFile *f) +{ + return f->ops->writev_buffer || f->ops->put_buffer; +} + +/** + * Flushes QEMUFile buffer + * + * If there is writev_buffer QEMUFileOps it uses it otherwise uses + * put_buffer ops. + */ +void qemu_fflush(QEMUFile *f) +{ + ssize_t ret = 0; + + if (!qemu_file_is_writable(f)) { + return; + } + + if (f->ops->writev_buffer) { + if (f->iovcnt > 0) { + ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos); + } + } else { + if (f->buf_index > 0) { + ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index); + } + } + if (ret >= 0) { + f->pos += ret; + } + f->buf_index = 0; + f->iovcnt = 0; + if (ret < 0) { + qemu_file_set_error(f, ret); + } +} + +void ram_control_before_iterate(QEMUFile *f, uint64_t flags) +{ + int ret = 0; + + if (f->ops->before_ram_iterate) { + ret = f->ops->before_ram_iterate(f, f->opaque, flags); + if (ret < 0) { + qemu_file_set_error(f, ret); + } + } +} + +void ram_control_after_iterate(QEMUFile *f, uint64_t flags) +{ + int ret = 0; + + if (f->ops->after_ram_iterate) { + ret = f->ops->after_ram_iterate(f, f->opaque, flags); + if (ret < 0) { + qemu_file_set_error(f, ret); + } + } +} + +void ram_control_load_hook(QEMUFile *f, uint64_t flags) +{ + int ret = -EINVAL; + + if (f->ops->hook_ram_load) { + ret = f->ops->hook_ram_load(f, f->opaque, flags); + if (ret < 0) { + qemu_file_set_error(f, ret); + } + } else { + qemu_file_set_error(f, ret); + } +} + +size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, + ram_addr_t offset, size_t size, int *bytes_sent) +{ + if (f->ops->save_page) { + int ret = f->ops->save_page(f, f->opaque, block_offset, + offset, size, bytes_sent); + + if (ret != RAM_SAVE_CONTROL_DELAYED) { + if (bytes_sent && *bytes_sent > 0) { + qemu_update_position(f, *bytes_sent); + } else if (ret < 0) { + qemu_file_set_error(f, ret); + } + } + + return ret; + } + + return RAM_SAVE_CONTROL_NOT_SUPP; +} + +static void qemu_fill_buffer(QEMUFile *f) +{ + int len; + int pending; + + assert(!qemu_file_is_writable(f)); + + 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->ops->get_buffer(f->opaque, f->buf + pending, f->pos, + IO_BUF_SIZE - pending); + if (len > 0) { + f->buf_size += len; + f->pos += len; + } else if (len == 0) { + qemu_file_set_error(f, -EIO); + } else if (len != -EAGAIN) { + qemu_file_set_error(f, len); + } +} + +int qemu_get_fd(QEMUFile *f) +{ + if (f->ops->get_fd) { + return f->ops->get_fd(f->opaque); + } + return -1; +} + +void qemu_update_position(QEMUFile *f, size_t size) +{ + f->pos += size; +} + +/** 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_file_get_error(f); + + if (f->ops->close) { + int ret2 = f->ops->close(f->opaque); + if (ret >= 0) { + ret = ret2; + } + } + /* 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; +} + +static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size) +{ + /* check for adjacent buffer and coalesce them */ + if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base + + f->iov[f->iovcnt - 1].iov_len) { + f->iov[f->iovcnt - 1].iov_len += size; + } else { + f->iov[f->iovcnt].iov_base = (uint8_t *)buf; + f->iov[f->iovcnt++].iov_len = size; + } + + if (f->iovcnt >= MAX_IOV_SIZE) { + qemu_fflush(f); + } +} + +void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size) +{ + if (!f->ops->writev_buffer) { + qemu_put_buffer(f, buf, size); + return; + } + + if (f->last_error) { + return; + } + + f->bytes_xfer += size; + add_to_iovec(f, buf, size); +} + +void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) +{ + int l; + + if (f->last_error) { + return; + } + + while (size > 0) { + l = IO_BUF_SIZE - f->buf_index; + if (l > size) { + l = size; + } + memcpy(f->buf + f->buf_index, buf, l); + f->bytes_xfer += l; + if (f->ops->writev_buffer) { + add_to_iovec(f, f->buf + f->buf_index, l); + } + f->buf_index += l; + if (f->buf_index == IO_BUF_SIZE) { + qemu_fflush(f); + } + if (qemu_file_get_error(f)) { + break; + } + buf += l; + size -= l; + } +} + +void qemu_put_byte(QEMUFile *f, int v) +{ + if (f->last_error) { + return; + } + + f->buf[f->buf_index] = v; + f->bytes_xfer++; + if (f->ops->writev_buffer) { + add_to_iovec(f, f->buf + f->buf_index, 1); + } + f->buf_index++; + if (f->buf_index == IO_BUF_SIZE) { + qemu_fflush(f); + } +} + +void qemu_file_skip(QEMUFile *f, int size) +{ + if (f->buf_index + size <= f->buf_size) { + f->buf_index += size; + } +} + +int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset) +{ + int pending; + int index; + + assert(!qemu_file_is_writable(f)); + + 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; + } + qemu_file_skip(f, res); + buf += res; + pending -= res; + done += res; + } + return done; +} + +int qemu_peek_byte(QEMUFile *f, int offset) +{ + int index = f->buf_index + offset; + + assert(!qemu_file_is_writable(f)); + + if (index >= f->buf_size) { + qemu_fill_buffer(f); + index = f->buf_index + offset; + if (index >= f->buf_size) { + return 0; + } + } + return f->buf[index]; +} + +int qemu_get_byte(QEMUFile *f) +{ + int result; + + result = qemu_peek_byte(f, 0); + qemu_file_skip(f, 1); + return result; +} + +int64_t qemu_ftell(QEMUFile *f) +{ + qemu_fflush(f); + return f->pos; +} + +int qemu_file_rate_limit(QEMUFile *f) +{ + if (qemu_file_get_error(f)) { + return 1; + } + if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) { + return 1; + } + return 0; +} + +int64_t qemu_file_get_rate_limit(QEMUFile *f) +{ + return f->xfer_limit; +} + +void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit) +{ + f->xfer_limit = limit; +} + +void qemu_file_reset_rate_limit(QEMUFile *f) +{ + f->bytes_xfer = 0; +} + +void qemu_put_be16(QEMUFile *f, unsigned int v) +{ + qemu_put_byte(f, v >> 8); + qemu_put_byte(f, v); +} + +void qemu_put_be32(QEMUFile *f, unsigned int v) +{ + qemu_put_byte(f, v >> 24); + qemu_put_byte(f, v >> 16); + qemu_put_byte(f, v >> 8); + qemu_put_byte(f, v); +} + +void qemu_put_be64(QEMUFile *f, uint64_t v) +{ + qemu_put_be32(f, v >> 32); + qemu_put_be32(f, v); +} + +unsigned int qemu_get_be16(QEMUFile *f) +{ + unsigned int v; + v = qemu_get_byte(f) << 8; + v |= qemu_get_byte(f); + return v; +} + +unsigned int qemu_get_be32(QEMUFile *f) +{ + unsigned int v; + v = qemu_get_byte(f) << 24; + v |= qemu_get_byte(f) << 16; + v |= qemu_get_byte(f) << 8; + v |= qemu_get_byte(f); + return v; +} + +uint64_t qemu_get_be64(QEMUFile *f) +{ + uint64_t v; + v = (uint64_t)qemu_get_be32(f) << 32; + v |= qemu_get_be32(f); + return v; +} diff --git a/savevm.c b/savevm.c index 3f13a6a4d2..8eeb5ef56e 100644 --- a/savevm.c +++ b/savevm.c @@ -114,396 +114,6 @@ void qemu_announce_self(void) /***********************************************************/ /* savevm/loadvm support */ -#define IO_BUF_SIZE 32768 -#define MAX_IOV_SIZE MIN(IOV_MAX, 64) - -struct QEMUFile { - const QEMUFileOps *ops; - void *opaque; - - int64_t bytes_xfer; - int64_t xfer_limit; - - int64_t pos; /* start of buffer when writing, end of buffer - when reading */ - int buf_index; - int buf_size; /* 0 when writing */ - uint8_t buf[IO_BUF_SIZE]; - - struct iovec iov[MAX_IOV_SIZE]; - unsigned int iovcnt; - - int last_error; -}; - -typedef struct QEMUFileStdio { - FILE *stdio_file; - QEMUFile *file; -} QEMUFileStdio; - -typedef struct QEMUFileSocket { - int fd; - QEMUFile *file; -} QEMUFileSocket; - -static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, - int64_t pos) -{ - QEMUFileSocket *s = opaque; - ssize_t len; - ssize_t size = iov_size(iov, iovcnt); - - len = iov_send(s->fd, iov, iovcnt, 0, size); - if (len < size) { - len = -socket_error(); - } - return len; -} - -static int socket_get_fd(void *opaque) -{ - QEMUFileSocket *s = opaque; - - return s->fd; -} - -static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) -{ - QEMUFileSocket *s = opaque; - ssize_t len; - - for (;;) { - len = qemu_recv(s->fd, buf, size, 0); - if (len != -1) { - break; - } - if (socket_error() == EAGAIN) { - yield_until_fd_readable(s->fd); - } else if (socket_error() != EINTR) { - break; - } - } - - if (len == -1) { - len = -socket_error(); - } - return len; -} - -static int socket_close(void *opaque) -{ - QEMUFileSocket *s = opaque; - closesocket(s->fd); - g_free(s); - return 0; -} - -static int stdio_get_fd(void *opaque) -{ - QEMUFileStdio *s = opaque; - - return fileno(s->stdio_file); -} - -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) -{ - QEMUFileStdio *s = opaque; - FILE *fp = s->stdio_file; - int bytes; - - for (;;) { - clearerr(fp); - bytes = fread(buf, 1, size, fp); - if (bytes != 0 || !ferror(fp)) { - break; - } - if (errno == EAGAIN) { - yield_until_fd_readable(fileno(fp)); - } else if (errno != EINTR) { - break; - } - } - return bytes; -} - -static int stdio_pclose(void *opaque) -{ - QEMUFileStdio *s = opaque; - int ret; - ret = pclose(s->stdio_file); - if (ret == -1) { - ret = -errno; - } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) { - /* close succeeded, but non-zero exit code: */ - ret = -EIO; /* fake errno value */ - } - g_free(s); - return ret; -} - -static int stdio_fclose(void *opaque) -{ - QEMUFileStdio *s = opaque; - int ret = 0; - - if (s->file->ops->put_buffer || s->file->ops->writev_buffer) { - int fd = fileno(s->stdio_file); - struct stat st; - - ret = fstat(fd, &st); - if (ret == 0 && S_ISREG(st.st_mode)) { - /* - * If the file handle is a regular file make sure the - * data is flushed to disk before signaling success. - */ - ret = fsync(fd); - if (ret != 0) { - ret = -errno; - return ret; - } - } - } - if (fclose(s->stdio_file) == EOF) { - ret = -errno; - } - g_free(s); - return ret; -} - -static const QEMUFileOps stdio_pipe_read_ops = { - .get_fd = stdio_get_fd, - .get_buffer = stdio_get_buffer, - .close = stdio_pclose -}; - -static const QEMUFileOps stdio_pipe_write_ops = { - .get_fd = stdio_get_fd, - .put_buffer = stdio_put_buffer, - .close = stdio_pclose -}; - -QEMUFile *qemu_popen_cmd(const char *command, const char *mode) -{ - FILE *stdio_file; - QEMUFileStdio *s; - - if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { - fprintf(stderr, "qemu_popen: Argument validity check failed\n"); - return NULL; - } - - stdio_file = popen(command, mode); - if (stdio_file == NULL) { - return NULL; - } - - s = g_malloc0(sizeof(QEMUFileStdio)); - - s->stdio_file = stdio_file; - - if (mode[0] == 'r') { - s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops); - } else { - s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops); - } - return s->file; -} - -static const QEMUFileOps stdio_file_read_ops = { - .get_fd = stdio_get_fd, - .get_buffer = stdio_get_buffer, - .close = stdio_fclose -}; - -static const QEMUFileOps stdio_file_write_ops = { - .get_fd = stdio_get_fd, - .put_buffer = stdio_put_buffer, - .close = stdio_fclose -}; - -static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, - int64_t pos) -{ - QEMUFileSocket *s = opaque; - ssize_t len, offset; - ssize_t size = iov_size(iov, iovcnt); - ssize_t total = 0; - - assert(iovcnt > 0); - offset = 0; - while (size > 0) { - /* Find the next start position; skip all full-sized vector elements */ - while (offset >= iov[0].iov_len) { - offset -= iov[0].iov_len; - iov++, iovcnt--; - } - - /* skip `offset' bytes from the (now) first element, undo it on exit */ - assert(iovcnt > 0); - iov[0].iov_base += offset; - iov[0].iov_len -= offset; - - do { - len = writev(s->fd, iov, iovcnt); - } while (len == -1 && errno == EINTR); - if (len == -1) { - return -errno; - } - - /* Undo the changes above */ - iov[0].iov_base -= offset; - iov[0].iov_len += offset; - - /* Prepare for the next iteration */ - offset += len; - total += len; - size -= len; - } - - return total; -} - -static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) -{ - QEMUFileSocket *s = opaque; - ssize_t len; - - for (;;) { - len = read(s->fd, buf, size); - if (len != -1) { - break; - } - if (errno == EAGAIN) { - yield_until_fd_readable(s->fd); - } else if (errno != EINTR) { - break; - } - } - - if (len == -1) { - len = -errno; - } - return len; -} - -static int unix_close(void *opaque) -{ - QEMUFileSocket *s = opaque; - close(s->fd); - g_free(s); - return 0; -} - -static const QEMUFileOps unix_read_ops = { - .get_fd = socket_get_fd, - .get_buffer = unix_get_buffer, - .close = unix_close -}; - -static const QEMUFileOps unix_write_ops = { - .get_fd = socket_get_fd, - .writev_buffer = unix_writev_buffer, - .close = unix_close -}; - -QEMUFile *qemu_fdopen(int fd, const char *mode) -{ - QEMUFileSocket *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 = g_malloc0(sizeof(QEMUFileSocket)); - s->fd = fd; - - if (mode[0] == 'r') { - s->file = qemu_fopen_ops(s, &unix_read_ops); - } else { - s->file = qemu_fopen_ops(s, &unix_write_ops); - } - return s->file; -} - -static const QEMUFileOps socket_read_ops = { - .get_fd = socket_get_fd, - .get_buffer = socket_get_buffer, - .close = socket_close -}; - -static const QEMUFileOps socket_write_ops = { - .get_fd = socket_get_fd, - .writev_buffer = socket_writev_buffer, - .close = socket_close -}; - -bool qemu_file_mode_is_not_valid(const char *mode) -{ - if (mode == NULL || - (mode[0] != 'r' && mode[0] != 'w') || - mode[1] != 'b' || mode[2] != 0) { - fprintf(stderr, "qemu_fopen: Argument validity check failed\n"); - return true; - } - - return false; -} - -QEMUFile *qemu_fopen_socket(int fd, const char *mode) -{ - QEMUFileSocket *s; - - if (qemu_file_mode_is_not_valid(mode)) { - return NULL; - } - - s = g_malloc0(sizeof(QEMUFileSocket)); - s->fd = fd; - if (mode[0] == 'w') { - qemu_set_block(s->fd); - s->file = qemu_fopen_ops(s, &socket_write_ops); - } else { - s->file = qemu_fopen_ops(s, &socket_read_ops); - } - return s->file; -} - -QEMUFile *qemu_fopen(const char *filename, const char *mode) -{ - QEMUFileStdio *s; - - if (qemu_file_mode_is_not_valid(mode)) { - return NULL; - } - - s = g_malloc0(sizeof(QEMUFileStdio)); - - s->stdio_file = fopen(filename, mode); - if (!s->stdio_file) { - goto fail; - } - - if (mode[0] == 'w') { - s->file = qemu_fopen_ops(s, &stdio_file_write_ops); - } else { - s->file = qemu_fopen_ops(s, &stdio_file_read_ops); - } - return s->file; -fail: - g_free(s); - return NULL; -} - static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, int64_t pos) { @@ -555,436 +165,6 @@ static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable) return qemu_fopen_ops(bs, &bdrv_read_ops); } -QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops) -{ - QEMUFile *f; - - f = g_malloc0(sizeof(QEMUFile)); - - f->opaque = opaque; - f->ops = ops; - return f; -} - -/* - * Get last error for stream f - * - * Return negative error value if there has been an error on previous - * operations, return 0 if no error happened. - * - */ -int qemu_file_get_error(QEMUFile *f) -{ - return f->last_error; -} - -void qemu_file_set_error(QEMUFile *f, int ret) -{ - if (f->last_error == 0) { - f->last_error = ret; - } -} - -static inline bool qemu_file_is_writable(QEMUFile *f) -{ - return f->ops->writev_buffer || f->ops->put_buffer; -} - -/** - * Flushes QEMUFile buffer - * - * If there is writev_buffer QEMUFileOps it uses it otherwise uses - * put_buffer ops. - */ -void qemu_fflush(QEMUFile *f) -{ - ssize_t ret = 0; - - if (!qemu_file_is_writable(f)) { - return; - } - - if (f->ops->writev_buffer) { - if (f->iovcnt > 0) { - ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos); - } - } else { - if (f->buf_index > 0) { - ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index); - } - } - if (ret >= 0) { - f->pos += ret; - } - f->buf_index = 0; - f->iovcnt = 0; - if (ret < 0) { - qemu_file_set_error(f, ret); - } -} - -void ram_control_before_iterate(QEMUFile *f, uint64_t flags) -{ - int ret = 0; - - if (f->ops->before_ram_iterate) { - ret = f->ops->before_ram_iterate(f, f->opaque, flags); - if (ret < 0) { - qemu_file_set_error(f, ret); - } - } -} - -void ram_control_after_iterate(QEMUFile *f, uint64_t flags) -{ - int ret = 0; - - if (f->ops->after_ram_iterate) { - ret = f->ops->after_ram_iterate(f, f->opaque, flags); - if (ret < 0) { - qemu_file_set_error(f, ret); - } - } -} - -void ram_control_load_hook(QEMUFile *f, uint64_t flags) -{ - int ret = -EINVAL; - - if (f->ops->hook_ram_load) { - ret = f->ops->hook_ram_load(f, f->opaque, flags); - if (ret < 0) { - qemu_file_set_error(f, ret); - } - } else { - qemu_file_set_error(f, ret); - } -} - -size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, - ram_addr_t offset, size_t size, int *bytes_sent) -{ - if (f->ops->save_page) { - int ret = f->ops->save_page(f, f->opaque, block_offset, - offset, size, bytes_sent); - - if (ret != RAM_SAVE_CONTROL_DELAYED) { - if (bytes_sent && *bytes_sent > 0) { - qemu_update_position(f, *bytes_sent); - } else if (ret < 0) { - qemu_file_set_error(f, ret); - } - } - - return ret; - } - - return RAM_SAVE_CONTROL_NOT_SUPP; -} - -static void qemu_fill_buffer(QEMUFile *f) -{ - int len; - int pending; - - assert(!qemu_file_is_writable(f)); - - 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->ops->get_buffer(f->opaque, f->buf + pending, f->pos, - IO_BUF_SIZE - pending); - if (len > 0) { - f->buf_size += len; - f->pos += len; - } else if (len == 0) { - qemu_file_set_error(f, -EIO); - } else if (len != -EAGAIN) { - qemu_file_set_error(f, len); - } -} - -int qemu_get_fd(QEMUFile *f) -{ - if (f->ops->get_fd) { - return f->ops->get_fd(f->opaque); - } - return -1; -} - -void qemu_update_position(QEMUFile *f, size_t size) -{ - f->pos += size; -} - -/** 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_file_get_error(f); - - if (f->ops->close) { - int ret2 = f->ops->close(f->opaque); - if (ret >= 0) { - ret = ret2; - } - } - /* 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; -} - -static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size) -{ - /* check for adjacent buffer and coalesce them */ - if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base + - f->iov[f->iovcnt - 1].iov_len) { - f->iov[f->iovcnt - 1].iov_len += size; - } else { - f->iov[f->iovcnt].iov_base = (uint8_t *)buf; - f->iov[f->iovcnt++].iov_len = size; - } - - if (f->iovcnt >= MAX_IOV_SIZE) { - qemu_fflush(f); - } -} - -void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size) -{ - if (!f->ops->writev_buffer) { - qemu_put_buffer(f, buf, size); - return; - } - - if (f->last_error) { - return; - } - - f->bytes_xfer += size; - add_to_iovec(f, buf, size); -} - -void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) -{ - int l; - - if (f->last_error) { - return; - } - - while (size > 0) { - l = IO_BUF_SIZE - f->buf_index; - if (l > size) { - l = size; - } - memcpy(f->buf + f->buf_index, buf, l); - f->bytes_xfer += l; - if (f->ops->writev_buffer) { - add_to_iovec(f, f->buf + f->buf_index, l); - } - f->buf_index += l; - if (f->buf_index == IO_BUF_SIZE) { - qemu_fflush(f); - } - if (qemu_file_get_error(f)) { - break; - } - buf += l; - size -= l; - } -} - -void qemu_put_byte(QEMUFile *f, int v) -{ - if (f->last_error) { - return; - } - - f->buf[f->buf_index] = v; - f->bytes_xfer++; - if (f->ops->writev_buffer) { - add_to_iovec(f, f->buf + f->buf_index, 1); - } - f->buf_index++; - if (f->buf_index == IO_BUF_SIZE) { - qemu_fflush(f); - } -} - -void qemu_file_skip(QEMUFile *f, int size) -{ - if (f->buf_index + size <= f->buf_size) { - f->buf_index += size; - } -} - -int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset) -{ - int pending; - int index; - - assert(!qemu_file_is_writable(f)); - - 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; - } - qemu_file_skip(f, res); - buf += res; - pending -= res; - done += res; - } - return done; -} - -int qemu_peek_byte(QEMUFile *f, int offset) -{ - int index = f->buf_index + offset; - - assert(!qemu_file_is_writable(f)); - - if (index >= f->buf_size) { - qemu_fill_buffer(f); - index = f->buf_index + offset; - if (index >= f->buf_size) { - return 0; - } - } - return f->buf[index]; -} - -int qemu_get_byte(QEMUFile *f) -{ - int result; - - result = qemu_peek_byte(f, 0); - qemu_file_skip(f, 1); - return result; -} - -int64_t qemu_ftell(QEMUFile *f) -{ - qemu_fflush(f); - return f->pos; -} - -int qemu_file_rate_limit(QEMUFile *f) -{ - if (qemu_file_get_error(f)) { - return 1; - } - if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) { - return 1; - } - return 0; -} - -int64_t qemu_file_get_rate_limit(QEMUFile *f) -{ - return f->xfer_limit; -} - -void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit) -{ - f->xfer_limit = limit; -} - -void qemu_file_reset_rate_limit(QEMUFile *f) -{ - f->bytes_xfer = 0; -} - -void qemu_put_be16(QEMUFile *f, unsigned int v) -{ - qemu_put_byte(f, v >> 8); - qemu_put_byte(f, v); -} - -void qemu_put_be32(QEMUFile *f, unsigned int v) -{ - qemu_put_byte(f, v >> 24); - qemu_put_byte(f, v >> 16); - qemu_put_byte(f, v >> 8); - qemu_put_byte(f, v); -} - -void qemu_put_be64(QEMUFile *f, uint64_t v) -{ - qemu_put_be32(f, v >> 32); - qemu_put_be32(f, v); -} - -unsigned int qemu_get_be16(QEMUFile *f) -{ - unsigned int v; - v = qemu_get_byte(f) << 8; - v |= qemu_get_byte(f); - return v; -} - -unsigned int qemu_get_be32(QEMUFile *f) -{ - unsigned int v; - v = qemu_get_byte(f) << 24; - v |= qemu_get_byte(f) << 16; - v |= qemu_get_byte(f) << 8; - v |= qemu_get_byte(f); - return v; -} - -uint64_t qemu_get_be64(QEMUFile *f) -{ - uint64_t v; - v = (uint64_t)qemu_get_be32(f) << 32; - v |= qemu_get_be32(f); - return v; -} - /* timer */ From bb1a6d8c57217188f33d8e59d71fc62dbee9e4f8 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 29 Nov 2013 12:26:02 -0200 Subject: [PATCH 11/49] savevm: Small comment about why timer QEMUFile/VMState code is in savevm.c Signed-off-by: Eduardo Habkost Reviewed-by: Orit Wasserman Signed-off-by: Juan Quintela --- savevm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/savevm.c b/savevm.c index 8eeb5ef56e..a7dbe18a67 100644 --- a/savevm.c +++ b/savevm.c @@ -166,7 +166,9 @@ static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable) } -/* timer */ +/* QEMUFile timer support. + * Not in qemu-file.c to not add qemu-timer.c as dependency to qemu-file.c + */ void timer_put(QEMUFile *f, QEMUTimer *ts) { @@ -189,7 +191,9 @@ void timer_get(QEMUFile *f, QEMUTimer *ts) } -/* timers */ +/* VMState timer support. + * Not in vmstate.c to not add qemu-timer.c as dependency to vmstate.c + */ static int get_timer(QEMUFile *f, void *pv, size_t size) { From 2668b4bff484a8428947c10a0efb9758658f4b1a Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 28 Nov 2013 12:01:18 -0200 Subject: [PATCH 12/49] tests: Some unit tests for vmstate.c * Basic load/save tests * Tests for loading older versions * Tests for .field_exists() handling Signed-off-by: Eduardo Habkost Signed-off-by: Juan Quintela --- tests/.gitignore | 1 + tests/Makefile | 4 + tests/test-vmstate.c | 357 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 362 insertions(+) create mode 100644 tests/test-vmstate.c diff --git a/tests/.gitignore b/tests/.gitignore index 425757cfe1..1aed2249ff 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -20,6 +20,7 @@ test-qmp-commands test-qmp-input-strict test-qmp-marshal.c test-thread-pool +test-vmstate test-x86-cpuid test-xbzrle *-test diff --git a/tests/Makefile b/tests/Makefile index 0b85a34147..0aaf657be5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -54,6 +54,7 @@ check-unit-y += tests/test-bitops$(EXESUF) check-unit-y += tests/test-qdev-global-props$(EXESUF) check-unit-y += tests/check-qom-interface$(EXESUF) gcov-files-check-qom-interface-y = qom/object.c +check-unit-y += tests/test-vmstate$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh @@ -167,6 +168,9 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \ $(qom-core-obj) \ $(test-qapi-obj-y) \ libqemuutil.a libqemustub.a +tests/test-vmstate$(EXESUF): tests/test-vmstate.o \ + vmstate.o qemu-file.o \ + libqemuutil.a tests/test-qapi-types.c tests/test-qapi-types.h :\ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c new file mode 100644 index 0000000000..75cd1a1fd4 --- /dev/null +++ b/tests/test-vmstate.c @@ -0,0 +1,357 @@ +/* + * Test code for VMState + * + * Copyright (c) 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "qemu-common.h" +#include "migration/migration.h" +#include "migration/vmstate.h" +#include "block/coroutine.h" + +char temp_file[] = "/tmp/vmst.test.XXXXXX"; +int temp_fd; + +/* Fake yield_until_fd_readable() implementation so we don't have to pull the + * coroutine code as dependency. + */ +void yield_until_fd_readable(int fd) +{ + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + select(fd + 1, &fds, NULL, NULL, NULL); +} + +/* Duplicate temp_fd and seek to the beginning of the file */ +static int dup_temp_fd(bool truncate) +{ + int fd = dup(temp_fd); + lseek(fd, 0, SEEK_SET); + if (truncate) { + g_assert_cmpint(ftruncate(fd, 0), ==, 0); + } + return fd; +} + +typedef struct TestSruct { + uint32_t a, b, c, e; + uint64_t d, f; + bool skip_c_e; +} TestStruct; + + +static const VMStateDescription vmstate_simple = { + .name = "test", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(a, TestStruct), + VMSTATE_UINT32(b, TestStruct), + VMSTATE_UINT32(c, TestStruct), + VMSTATE_UINT64(d, TestStruct), + VMSTATE_END_OF_LIST() + } +}; + +static void test_simple_save(void) +{ + QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); + TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4 }; + vmstate_save_state(fsave, &vmstate_simple, &obj); + g_assert(!qemu_file_get_error(fsave)); + qemu_fclose(fsave); + + QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); + uint8_t expected[] = { + 0, 0, 0, 1, /* a */ + 0, 0, 0, 2, /* b */ + 0, 0, 0, 3, /* c */ + 0, 0, 0, 0, 0, 0, 0, 4, /* d */ + }; + uint8_t result[sizeof(expected)]; + g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==, + sizeof(result)); + g_assert(!qemu_file_get_error(loading)); + g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0); + + /* Must reach EOF */ + qemu_get_byte(loading); + g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO); + + qemu_fclose(loading); +} + +static void test_simple_load(void) +{ + QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); + uint8_t buf[] = { + 0, 0, 0, 10, /* a */ + 0, 0, 0, 20, /* b */ + 0, 0, 0, 30, /* c */ + 0, 0, 0, 0, 0, 0, 0, 40, /* d */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ + }; + qemu_put_buffer(fsave, buf, sizeof(buf)); + qemu_fclose(fsave); + + QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); + TestStruct obj; + vmstate_load_state(loading, &vmstate_simple, &obj, 1); + g_assert(!qemu_file_get_error(loading)); + g_assert_cmpint(obj.a, ==, 10); + g_assert_cmpint(obj.b, ==, 20); + g_assert_cmpint(obj.c, ==, 30); + g_assert_cmpint(obj.d, ==, 40); + qemu_fclose(loading); +} + +static const VMStateDescription vmstate_versioned = { + .name = "test", + .version_id = 2, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32(a, TestStruct), + VMSTATE_UINT32_V(b, TestStruct, 2), /* Versioned field in the middle, so + * we catch bugs more easily. + */ + VMSTATE_UINT32(c, TestStruct), + VMSTATE_UINT64(d, TestStruct), + VMSTATE_UINT32_V(e, TestStruct, 2), + VMSTATE_UINT64_V(f, TestStruct, 2), + VMSTATE_END_OF_LIST() + } +}; + +static void test_load_v1(void) +{ + QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); + uint8_t buf[] = { + 0, 0, 0, 10, /* a */ + 0, 0, 0, 30, /* c */ + 0, 0, 0, 0, 0, 0, 0, 40, /* d */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ + }; + qemu_put_buffer(fsave, buf, sizeof(buf)); + qemu_fclose(fsave); + + QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); + TestStruct obj = { .b = 200, .e = 500, .f = 600 }; + vmstate_load_state(loading, &vmstate_versioned, &obj, 1); + g_assert(!qemu_file_get_error(loading)); + g_assert_cmpint(obj.a, ==, 10); + g_assert_cmpint(obj.b, ==, 200); + g_assert_cmpint(obj.c, ==, 30); + g_assert_cmpint(obj.d, ==, 40); + g_assert_cmpint(obj.e, ==, 500); + g_assert_cmpint(obj.f, ==, 600); + qemu_fclose(loading); +} + +static void test_load_v2(void) +{ + QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); + uint8_t buf[] = { + 0, 0, 0, 10, /* a */ + 0, 0, 0, 20, /* b */ + 0, 0, 0, 30, /* c */ + 0, 0, 0, 0, 0, 0, 0, 40, /* d */ + 0, 0, 0, 50, /* e */ + 0, 0, 0, 0, 0, 0, 0, 60, /* f */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ + }; + qemu_put_buffer(fsave, buf, sizeof(buf)); + qemu_fclose(fsave); + + QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); + TestStruct obj; + vmstate_load_state(loading, &vmstate_versioned, &obj, 2); + g_assert_cmpint(obj.a, ==, 10); + g_assert_cmpint(obj.b, ==, 20); + g_assert_cmpint(obj.c, ==, 30); + g_assert_cmpint(obj.d, ==, 40); + g_assert_cmpint(obj.e, ==, 50); + g_assert_cmpint(obj.f, ==, 60); + qemu_fclose(loading); +} + +static bool test_skip(void *opaque, int version_id) +{ + TestStruct *t = (TestStruct *)opaque; + return !t->skip_c_e; +} + +static const VMStateDescription vmstate_skipping = { + .name = "test", + .version_id = 2, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32(a, TestStruct), + VMSTATE_UINT32(b, TestStruct), + VMSTATE_UINT32_TEST(c, TestStruct, test_skip), + VMSTATE_UINT64(d, TestStruct), + VMSTATE_UINT32_TEST(e, TestStruct, test_skip), + VMSTATE_UINT64_V(f, TestStruct, 2), + VMSTATE_END_OF_LIST() + } +}; + + +static void test_save_noskip(void) +{ + QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); + TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, + .skip_c_e = false }; + vmstate_save_state(fsave, &vmstate_skipping, &obj); + g_assert(!qemu_file_get_error(fsave)); + qemu_fclose(fsave); + + QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); + uint8_t expected[] = { + 0, 0, 0, 1, /* a */ + 0, 0, 0, 2, /* b */ + 0, 0, 0, 3, /* c */ + 0, 0, 0, 0, 0, 0, 0, 4, /* d */ + 0, 0, 0, 5, /* e */ + 0, 0, 0, 0, 0, 0, 0, 6, /* f */ + }; + uint8_t result[sizeof(expected)]; + g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==, + sizeof(result)); + g_assert(!qemu_file_get_error(loading)); + g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0); + + /* Must reach EOF */ + qemu_get_byte(loading); + g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO); + + qemu_fclose(loading); +} + +static void test_save_skip(void) +{ + QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); + TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, + .skip_c_e = true }; + vmstate_save_state(fsave, &vmstate_skipping, &obj); + g_assert(!qemu_file_get_error(fsave)); + qemu_fclose(fsave); + + QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); + uint8_t expected[] = { + 0, 0, 0, 1, /* a */ + 0, 0, 0, 2, /* b */ + 0, 0, 0, 0, 0, 0, 0, 4, /* d */ + 0, 0, 0, 0, 0, 0, 0, 6, /* f */ + }; + uint8_t result[sizeof(expected)]; + g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==, + sizeof(result)); + g_assert(!qemu_file_get_error(loading)); + g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0); + + + /* Must reach EOF */ + qemu_get_byte(loading); + g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO); + + qemu_fclose(loading); +} + +static void test_load_noskip(void) +{ + QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); + uint8_t buf[] = { + 0, 0, 0, 10, /* a */ + 0, 0, 0, 20, /* b */ + 0, 0, 0, 30, /* c */ + 0, 0, 0, 0, 0, 0, 0, 40, /* d */ + 0, 0, 0, 50, /* e */ + 0, 0, 0, 0, 0, 0, 0, 60, /* f */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ + }; + qemu_put_buffer(fsave, buf, sizeof(buf)); + qemu_fclose(fsave); + + QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); + TestStruct obj = { .skip_c_e = false }; + vmstate_load_state(loading, &vmstate_skipping, &obj, 2); + g_assert(!qemu_file_get_error(loading)); + g_assert_cmpint(obj.a, ==, 10); + g_assert_cmpint(obj.b, ==, 20); + g_assert_cmpint(obj.c, ==, 30); + g_assert_cmpint(obj.d, ==, 40); + g_assert_cmpint(obj.e, ==, 50); + g_assert_cmpint(obj.f, ==, 60); + qemu_fclose(loading); +} + +static void test_load_skip(void) +{ + QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); + uint8_t buf[] = { + 0, 0, 0, 10, /* a */ + 0, 0, 0, 20, /* b */ + 0, 0, 0, 0, 0, 0, 0, 40, /* d */ + 0, 0, 0, 0, 0, 0, 0, 60, /* f */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ + }; + qemu_put_buffer(fsave, buf, sizeof(buf)); + qemu_fclose(fsave); + + QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); + TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 }; + vmstate_load_state(loading, &vmstate_skipping, &obj, 2); + g_assert(!qemu_file_get_error(loading)); + g_assert_cmpint(obj.a, ==, 10); + g_assert_cmpint(obj.b, ==, 20); + g_assert_cmpint(obj.c, ==, 300); + g_assert_cmpint(obj.d, ==, 40); + g_assert_cmpint(obj.e, ==, 500); + g_assert_cmpint(obj.f, ==, 60); + qemu_fclose(loading); +} + +int main(int argc, char **argv) +{ + temp_fd = mkstemp(temp_file); + + g_test_init(&argc, &argv, NULL); + g_test_add_func("/vmstate/simple/save", test_simple_save); + g_test_add_func("/vmstate/simple/load", test_simple_load); + g_test_add_func("/vmstate/versioned/load/v1", test_load_v1); + g_test_add_func("/vmstate/versioned/load/v2", test_load_v2); + g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip); + g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip); + g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip); + g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip); + g_test_run(); + + close(temp_fd); + unlink(temp_file); + + return 0; +} From 9c22687ea8c1a2644a41a683ff0a291adc9a657c Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 17 Dec 2013 12:12:24 +0100 Subject: [PATCH 13/49] bitmap: use long as index Move index and size fields from int to long. We need that for migration. long is 64 bits on sane architectures, and 32bits should be enough on all the 32bits architectures. Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/qemu/bitmap.h | 77 ++++++++++++++++++++++--------------------- include/qemu/bitops.h | 14 ++++---- util/bitmap.c | 60 ++++++++++++++++----------------- 3 files changed, 76 insertions(+), 75 deletions(-) diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h index 308bbb71e9..afdd257d8f 100644 --- a/include/qemu/bitmap.h +++ b/include/qemu/bitmap.h @@ -31,7 +31,7 @@ * bitmap_andnot(dst, src1, src2, nbits) *dst = *src1 & ~(*src2) * bitmap_complement(dst, src, nbits) *dst = ~(*src) * bitmap_equal(src1, src2, nbits) Are *src1 and *src2 equal? - * bitmap_intersects(src1, src2, nbits) Do *src1 and *src2 overlap? + * bitmap_intersects(src1, src2, nbits) Do *src1 and *src2 overlap? * bitmap_empty(src, nbits) Are all bits zero in *src? * bitmap_full(src, nbits) Are all bits set in *src? * bitmap_set(dst, pos, nbits) Set specified bit area @@ -62,71 +62,71 @@ ) #define DECLARE_BITMAP(name,bits) \ - unsigned long name[BITS_TO_LONGS(bits)] + unsigned long name[BITS_TO_LONGS(bits)] #define small_nbits(nbits) \ - ((nbits) <= BITS_PER_LONG) + ((nbits) <= BITS_PER_LONG) -int slow_bitmap_empty(const unsigned long *bitmap, int bits); -int slow_bitmap_full(const unsigned long *bitmap, int bits); +int slow_bitmap_empty(const unsigned long *bitmap, long bits); +int slow_bitmap_full(const unsigned long *bitmap, long bits); int slow_bitmap_equal(const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits); + const unsigned long *bitmap2, long bits); void slow_bitmap_complement(unsigned long *dst, const unsigned long *src, - int bits); + long bits); void slow_bitmap_shift_right(unsigned long *dst, - const unsigned long *src, int shift, int bits); + const unsigned long *src, int shift, long bits); void slow_bitmap_shift_left(unsigned long *dst, - const unsigned long *src, int shift, int bits); + const unsigned long *src, int shift, long bits); int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits); + const unsigned long *bitmap2, long bits); void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits); + const unsigned long *bitmap2, long bits); void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits); + const unsigned long *bitmap2, long bits); int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits); + const unsigned long *bitmap2, long bits); int slow_bitmap_intersects(const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits); + const unsigned long *bitmap2, long bits); -static inline unsigned long *bitmap_new(int nbits) +static inline unsigned long *bitmap_new(long nbits) { - int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); + long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); return g_malloc0(len); } -static inline void bitmap_zero(unsigned long *dst, int nbits) +static inline void bitmap_zero(unsigned long *dst, long nbits) { if (small_nbits(nbits)) { *dst = 0UL; } else { - int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); + long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); memset(dst, 0, len); } } -static inline void bitmap_fill(unsigned long *dst, int nbits) +static inline void bitmap_fill(unsigned long *dst, long nbits) { size_t nlongs = BITS_TO_LONGS(nbits); if (!small_nbits(nbits)) { - int len = (nlongs - 1) * sizeof(unsigned long); + long len = (nlongs - 1) * sizeof(unsigned long); memset(dst, 0xff, len); } dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits); } static inline void bitmap_copy(unsigned long *dst, const unsigned long *src, - int nbits) + long nbits) { if (small_nbits(nbits)) { *dst = *src; } else { - int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); + long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); memcpy(dst, src, len); } } static inline int bitmap_and(unsigned long *dst, const unsigned long *src1, - const unsigned long *src2, int nbits) + const unsigned long *src2, long nbits) { if (small_nbits(nbits)) { return (*dst = *src1 & *src2) != 0; @@ -135,7 +135,7 @@ static inline int bitmap_and(unsigned long *dst, const unsigned long *src1, } static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, - const unsigned long *src2, int nbits) + const unsigned long *src2, long nbits) { if (small_nbits(nbits)) { *dst = *src1 | *src2; @@ -145,7 +145,7 @@ static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, } static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1, - const unsigned long *src2, int nbits) + const unsigned long *src2, long nbits) { if (small_nbits(nbits)) { *dst = *src1 ^ *src2; @@ -155,7 +155,7 @@ static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1, } static inline int bitmap_andnot(unsigned long *dst, const unsigned long *src1, - const unsigned long *src2, int nbits) + const unsigned long *src2, long nbits) { if (small_nbits(nbits)) { return (*dst = *src1 & ~(*src2)) != 0; @@ -163,8 +163,9 @@ static inline int bitmap_andnot(unsigned long *dst, const unsigned long *src1, return slow_bitmap_andnot(dst, src1, src2, nbits); } -static inline void bitmap_complement(unsigned long *dst, const unsigned long *src, - int nbits) +static inline void bitmap_complement(unsigned long *dst, + const unsigned long *src, + long nbits) { if (small_nbits(nbits)) { *dst = ~(*src) & BITMAP_LAST_WORD_MASK(nbits); @@ -174,7 +175,7 @@ static inline void bitmap_complement(unsigned long *dst, const unsigned long *sr } static inline int bitmap_equal(const unsigned long *src1, - const unsigned long *src2, int nbits) + const unsigned long *src2, long nbits) { if (small_nbits(nbits)) { return ! ((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits)); @@ -183,7 +184,7 @@ static inline int bitmap_equal(const unsigned long *src1, } } -static inline int bitmap_empty(const unsigned long *src, int nbits) +static inline int bitmap_empty(const unsigned long *src, long nbits) { if (small_nbits(nbits)) { return ! (*src & BITMAP_LAST_WORD_MASK(nbits)); @@ -192,7 +193,7 @@ static inline int bitmap_empty(const unsigned long *src, int nbits) } } -static inline int bitmap_full(const unsigned long *src, int nbits) +static inline int bitmap_full(const unsigned long *src, long nbits) { if (small_nbits(nbits)) { return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits)); @@ -202,7 +203,7 @@ static inline int bitmap_full(const unsigned long *src, int nbits) } static inline int bitmap_intersects(const unsigned long *src1, - const unsigned long *src2, int nbits) + const unsigned long *src2, long nbits) { if (small_nbits(nbits)) { return ((*src1 & *src2) & BITMAP_LAST_WORD_MASK(nbits)) != 0; @@ -211,12 +212,12 @@ static inline int bitmap_intersects(const unsigned long *src1, } } -void bitmap_set(unsigned long *map, int i, int len); -void bitmap_clear(unsigned long *map, int start, int nr); +void bitmap_set(unsigned long *map, long i, long len); +void bitmap_clear(unsigned long *map, long start, long nr); unsigned long bitmap_find_next_zero_area(unsigned long *map, - unsigned long size, - unsigned long start, - unsigned int nr, - unsigned long align_mask); + unsigned long size, + unsigned long start, + unsigned long nr, + unsigned long align_mask); #endif /* BITMAP_H */ diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index 304c90c2b4..340b1e73bd 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -28,7 +28,7 @@ * @nr: the bit to set * @addr: the address to start counting from */ -static inline void set_bit(int nr, unsigned long *addr) +static inline void set_bit(long nr, unsigned long *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = addr + BIT_WORD(nr); @@ -41,7 +41,7 @@ static inline void set_bit(int nr, unsigned long *addr) * @nr: Bit to clear * @addr: Address to start counting from */ -static inline void clear_bit(int nr, unsigned long *addr) +static inline void clear_bit(long nr, unsigned long *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = addr + BIT_WORD(nr); @@ -54,7 +54,7 @@ static inline void clear_bit(int nr, unsigned long *addr) * @nr: Bit to change * @addr: Address to start counting from */ -static inline void change_bit(int nr, unsigned long *addr) +static inline void change_bit(long nr, unsigned long *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = addr + BIT_WORD(nr); @@ -67,7 +67,7 @@ static inline void change_bit(int nr, unsigned long *addr) * @nr: Bit to set * @addr: Address to count from */ -static inline int test_and_set_bit(int nr, unsigned long *addr) +static inline int test_and_set_bit(long nr, unsigned long *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = addr + BIT_WORD(nr); @@ -82,7 +82,7 @@ static inline int test_and_set_bit(int nr, unsigned long *addr) * @nr: Bit to clear * @addr: Address to count from */ -static inline int test_and_clear_bit(int nr, unsigned long *addr) +static inline int test_and_clear_bit(long nr, unsigned long *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = addr + BIT_WORD(nr); @@ -97,7 +97,7 @@ static inline int test_and_clear_bit(int nr, unsigned long *addr) * @nr: Bit to change * @addr: Address to count from */ -static inline int test_and_change_bit(int nr, unsigned long *addr) +static inline int test_and_change_bit(long nr, unsigned long *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = addr + BIT_WORD(nr); @@ -112,7 +112,7 @@ static inline int test_and_change_bit(int nr, unsigned long *addr) * @nr: bit number to test * @addr: Address to start counting from */ -static inline int test_bit(int nr, const unsigned long *addr) +static inline int test_bit(long nr, const unsigned long *addr) { return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); } diff --git a/util/bitmap.c b/util/bitmap.c index 687841dcec..9c6bb526f6 100644 --- a/util/bitmap.c +++ b/util/bitmap.c @@ -36,9 +36,9 @@ * endian architectures. */ -int slow_bitmap_empty(const unsigned long *bitmap, int bits) +int slow_bitmap_empty(const unsigned long *bitmap, long bits) { - int k, lim = bits/BITS_PER_LONG; + long k, lim = bits/BITS_PER_LONG; for (k = 0; k < lim; ++k) { if (bitmap[k]) { @@ -54,9 +54,9 @@ int slow_bitmap_empty(const unsigned long *bitmap, int bits) return 1; } -int slow_bitmap_full(const unsigned long *bitmap, int bits) +int slow_bitmap_full(const unsigned long *bitmap, long bits) { - int k, lim = bits/BITS_PER_LONG; + long k, lim = bits/BITS_PER_LONG; for (k = 0; k < lim; ++k) { if (~bitmap[k]) { @@ -74,9 +74,9 @@ int slow_bitmap_full(const unsigned long *bitmap, int bits) } int slow_bitmap_equal(const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits) + const unsigned long *bitmap2, long bits) { - int k, lim = bits/BITS_PER_LONG; + long k, lim = bits/BITS_PER_LONG; for (k = 0; k < lim; ++k) { if (bitmap1[k] != bitmap2[k]) { @@ -94,9 +94,9 @@ int slow_bitmap_equal(const unsigned long *bitmap1, } void slow_bitmap_complement(unsigned long *dst, const unsigned long *src, - int bits) + long bits) { - int k, lim = bits/BITS_PER_LONG; + long k, lim = bits/BITS_PER_LONG; for (k = 0; k < lim; ++k) { dst[k] = ~src[k]; @@ -108,10 +108,10 @@ void slow_bitmap_complement(unsigned long *dst, const unsigned long *src, } int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits) + const unsigned long *bitmap2, long bits) { - int k; - int nr = BITS_TO_LONGS(bits); + long k; + long nr = BITS_TO_LONGS(bits); unsigned long result = 0; for (k = 0; k < nr; k++) { @@ -121,10 +121,10 @@ int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1, } void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits) + const unsigned long *bitmap2, long bits) { - int k; - int nr = BITS_TO_LONGS(bits); + long k; + long nr = BITS_TO_LONGS(bits); for (k = 0; k < nr; k++) { dst[k] = bitmap1[k] | bitmap2[k]; @@ -132,10 +132,10 @@ void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1, } void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits) + const unsigned long *bitmap2, long bits) { - int k; - int nr = BITS_TO_LONGS(bits); + long k; + long nr = BITS_TO_LONGS(bits); for (k = 0; k < nr; k++) { dst[k] = bitmap1[k] ^ bitmap2[k]; @@ -143,10 +143,10 @@ void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1, } int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits) + const unsigned long *bitmap2, long bits) { - int k; - int nr = BITS_TO_LONGS(bits); + long k; + long nr = BITS_TO_LONGS(bits); unsigned long result = 0; for (k = 0; k < nr; k++) { @@ -157,10 +157,10 @@ int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1, #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG)) -void bitmap_set(unsigned long *map, int start, int nr) +void bitmap_set(unsigned long *map, long start, long nr) { unsigned long *p = map + BIT_WORD(start); - const int size = start + nr; + const long size = start + nr; int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG); unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start); @@ -177,10 +177,10 @@ void bitmap_set(unsigned long *map, int start, int nr) } } -void bitmap_clear(unsigned long *map, int start, int nr) +void bitmap_clear(unsigned long *map, long start, long nr) { unsigned long *p = map + BIT_WORD(start); - const int size = start + nr; + const long size = start + nr; int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); @@ -212,10 +212,10 @@ void bitmap_clear(unsigned long *map, int start, int nr) * power of 2. A @align_mask of 0 means no alignment is required. */ unsigned long bitmap_find_next_zero_area(unsigned long *map, - unsigned long size, - unsigned long start, - unsigned int nr, - unsigned long align_mask) + unsigned long size, + unsigned long start, + unsigned long nr, + unsigned long align_mask) { unsigned long index, end, i; again: @@ -237,9 +237,9 @@ again: } int slow_bitmap_intersects(const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits) + const unsigned long *bitmap2, long bits) { - int k, lim = bits/BITS_PER_LONG; + long k, lim = bits/BITS_PER_LONG; for (k = 0; k < lim; ++k) { if (bitmap1[k] & bitmap2[k]) { From e2da99d582300bb8deecb25d26a179ef5b92e066 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 2 Oct 2013 14:04:19 +0200 Subject: [PATCH 14/49] memory: cpu_physical_memory_set_dirty_flags() result is never used So return void. Signed-off-by: Juan Quintela Reviewed-by: Orit Wasserman Reviewed-by: Eric Blake --- include/exec/memory-internal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index d0e063392a..c71a5e62f6 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -70,10 +70,10 @@ static inline int cpu_physical_memory_get_dirty(ram_addr_t start, return ret; } -static inline int cpu_physical_memory_set_dirty_flags(ram_addr_t addr, +static inline void cpu_physical_memory_set_dirty_flags(ram_addr_t addr, int dirty_flags) { - return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags; + ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags; } static inline void cpu_physical_memory_set_dirty(ram_addr_t addr) From 78d0042642a415b56b3c53a6044f30eff47bf69c Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 2 Oct 2013 14:30:02 +0200 Subject: [PATCH 15/49] memory: cpu_physical_memory_set_dirty_range() return void Signed-off-by: Juan Quintela Reviewed-by: Orit Wasserman Reviewed-by: Eric Blake --- memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/memory.c b/memory.c index 776431416f..e497f998cd 100644 --- a/memory.c +++ b/memory.c @@ -1182,7 +1182,7 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size) { assert(mr->terminates); - return cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size, -1); + cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size, -1); } bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, From 06567942e5e92cc649b608205d5d31ff3ac21c58 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 11:50:04 +0200 Subject: [PATCH 16/49] exec: use accessor function to know if memory is dirty Signed-off-by: Juan Quintela Reviewed-by: Orit Wasserman Reviewed-by: Eric Blake --- exec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exec.c b/exec.c index 7e49e8e555..c42a2f9794 100644 --- a/exec.c +++ b/exec.c @@ -1508,7 +1508,7 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags); /* we remove the notdirty callback only if the code has been flushed */ - if (dirty_flags == 0xff) { + if (cpu_physical_memory_is_dirty(ram_addr)) { CPUArchState *env = current_cpu->env_ptr; tlb_set_dirty(env, env->mem_io_vaddr); } From a1390db4df32ad95bf7854944180d2343f7f6368 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 11:53:46 +0200 Subject: [PATCH 17/49] memory: create function to set a single dirty bit Signed-off-by: Juan Quintela Reviewed-by: Orit Wasserman Reviewed-by: Eric Blake --- cputlb.c | 2 +- include/exec/memory-internal.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cputlb.c b/cputlb.c index 9270055b83..c545e4c1cf 100644 --- a/cputlb.c +++ b/cputlb.c @@ -122,7 +122,7 @@ void tlb_protect_code(ram_addr_t ram_addr) void tlb_unprotect_code_phys(CPUArchState *env, ram_addr_t ram_addr, target_ulong vaddr) { - cpu_physical_memory_set_dirty_flags(ram_addr, CODE_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flag(ram_addr, CODE_DIRTY_FLAG); } static bool tlb_is_dirty_ram(CPUTLBEntry *tlbe) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index c71a5e62f6..4ebab8066a 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -76,6 +76,12 @@ static inline void cpu_physical_memory_set_dirty_flags(ram_addr_t addr, ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags; } +static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, + int dirty_flag) +{ + ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flag; +} + static inline void cpu_physical_memory_set_dirty(ram_addr_t addr) { cpu_physical_memory_set_dirty_flags(addr, 0xff); From 7e5609a85e3f35965af5e4c7b1480254642cf2dd Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 11:57:21 +0200 Subject: [PATCH 18/49] exec: create function to get a single dirty bit Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- exec.c | 2 +- include/exec/memory-internal.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/exec.c b/exec.c index c42a2f9794..ea78d82023 100644 --- a/exec.c +++ b/exec.c @@ -1487,7 +1487,7 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, { int dirty_flags; dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); - if (!(dirty_flags & CODE_DIRTY_FLAG)) { + if (!cpu_physical_memory_get_dirty_flag(ram_addr, CODE_DIRTY_FLAG)) { tb_invalidate_phys_page_fast(ram_addr, size); dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); } diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 4ebab8066a..136198c001 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -49,6 +49,12 @@ static inline int cpu_physical_memory_get_dirty_flags(ram_addr_t addr) return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS]; } +static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, + int dirty_flag) +{ + return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] & dirty_flag; +} + /* read dirty bit (return 0 or 1) */ static inline int cpu_physical_memory_is_dirty(ram_addr_t addr) { From 4f08cabe9e6efe8a50abc30cfa59e8470ad434d7 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 12:01:01 +0200 Subject: [PATCH 19/49] memory: make cpu_physical_memory_is_dirty return bool Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- exec.c | 7 ++----- include/exec/memory-internal.h | 8 ++++++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/exec.c b/exec.c index ea78d82023..a8c79e5fda 100644 --- a/exec.c +++ b/exec.c @@ -1485,11 +1485,8 @@ found: static void notdirty_mem_write(void *opaque, hwaddr ram_addr, uint64_t val, unsigned size) { - int dirty_flags; - dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); if (!cpu_physical_memory_get_dirty_flag(ram_addr, CODE_DIRTY_FLAG)) { tb_invalidate_phys_page_fast(ram_addr, size); - dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); } switch (size) { case 1: @@ -1504,8 +1501,8 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, default: abort(); } - dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); - cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags); + cpu_physical_memory_set_dirty_flag(ram_addr, MIGRATION_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flag(ram_addr, VGA_DIRTY_FLAG); /* we remove the notdirty callback only if the code has been flushed */ if (cpu_physical_memory_is_dirty(ram_addr)) { diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 136198c001..0b25c3f9c3 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -56,9 +56,13 @@ static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, } /* read dirty bit (return 0 or 1) */ -static inline int cpu_physical_memory_is_dirty(ram_addr_t addr) +static inline bool cpu_physical_memory_is_dirty(ram_addr_t addr) { - return cpu_physical_memory_get_dirty_flags(addr) == 0xff; + bool vga = cpu_physical_memory_get_dirty_flag(addr, VGA_DIRTY_FLAG); + bool code = cpu_physical_memory_get_dirty_flag(addr, CODE_DIRTY_FLAG); + bool migration = + cpu_physical_memory_get_dirty_flag(addr, MIGRATION_DIRTY_FLAG); + return vga && code && migration; } static inline int cpu_physical_memory_get_dirty(ram_addr_t start, From 36187e2ca0295364dcb9a3f256a6fcd77e983c02 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 12:17:12 +0200 Subject: [PATCH 20/49] memory: all users of cpu_physical_memory_get_dirty used only one flag So cpu_physical_memory_get_dirty_flags is not needed anymore Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/exec/memory-internal.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 0b25c3f9c3..53cfe83d2f 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -44,11 +44,6 @@ void qemu_ram_free_from_ptr(ram_addr_t addr); #define CODE_DIRTY_FLAG 0x02 #define MIGRATION_DIRTY_FLAG 0x08 -static inline int cpu_physical_memory_get_dirty_flags(ram_addr_t addr) -{ - return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS]; -} - static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, int dirty_flag) { @@ -67,7 +62,7 @@ static inline bool cpu_physical_memory_is_dirty(ram_addr_t addr) static inline int cpu_physical_memory_get_dirty(ram_addr_t start, ram_addr_t length, - int dirty_flags) + int dirty_flag) { int ret = 0; ram_addr_t addr, end; @@ -75,7 +70,7 @@ static inline int cpu_physical_memory_get_dirty(ram_addr_t start, end = TARGET_PAGE_ALIGN(start + length); start &= TARGET_PAGE_MASK; for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - ret |= cpu_physical_memory_get_dirty_flags(addr) & dirty_flags; + ret |= cpu_physical_memory_get_dirty_flag(addr, dirty_flag); } return ret; } From 63995cebfaa283586682ea6236c9686b2a49ece7 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 12:25:25 +0200 Subject: [PATCH 21/49] memory: set single dirty flags when possible Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- exec.c | 7 ++++--- include/exec/memory-internal.h | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/exec.c b/exec.c index a8c79e5fda..ffda8be436 100644 --- a/exec.c +++ b/exec.c @@ -1912,7 +1912,8 @@ static void invalidate_and_set_dirty(hwaddr addr, /* invalidate code */ tb_invalidate_phys_page_range(addr, addr + length, 0); /* set dirty bit */ - cpu_physical_memory_set_dirty_flags(addr, (0xff & ~CODE_DIRTY_FLAG)); + cpu_physical_memory_set_dirty_flag(addr, VGA_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flag(addr, MIGRATION_DIRTY_FLAG); } xen_modified_memory(addr, length); } @@ -2527,8 +2528,8 @@ void stl_phys_notdirty(hwaddr addr, uint32_t val) /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); /* set dirty bit */ - cpu_physical_memory_set_dirty_flags( - addr1, (0xff & ~CODE_DIRTY_FLAG)); + cpu_physical_memory_set_dirty_flag(addr1, MIGRATION_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flag(addr1, VGA_DIRTY_FLAG); } } } diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 53cfe83d2f..9f4ad697b3 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -89,7 +89,9 @@ static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, static inline void cpu_physical_memory_set_dirty(ram_addr_t addr) { - cpu_physical_memory_set_dirty_flags(addr, 0xff); + cpu_physical_memory_set_dirty_flag(addr, MIGRATION_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flag(addr, VGA_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flag(addr, CODE_DIRTY_FLAG); } static inline int cpu_physical_memory_clear_dirty_flags(ram_addr_t addr, From 75218e7f2b7f052c6f44489afaf45b3ea4369f45 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 12:31:54 +0200 Subject: [PATCH 22/49] memory: cpu_physical_memory_set_dirty_range() always dirty all flags So remove the flag argument and do it directly. After this change, there is nothing else using cpu_physical_memory_set_dirty_flags() so remove it. Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- exec.c | 2 +- include/exec/memory-internal.h | 11 ++--------- memory.c | 2 +- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/exec.c b/exec.c index ffda8be436..f21596040d 100644 --- a/exec.c +++ b/exec.c @@ -1275,7 +1275,7 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, last_ram_offset() >> TARGET_PAGE_BITS); memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS), 0, size >> TARGET_PAGE_BITS); - cpu_physical_memory_set_dirty_range(new_block->offset, size, 0xff); + cpu_physical_memory_set_dirty_range(new_block->offset, size); qemu_ram_setup_dump(new_block->host, size); qemu_madvise(new_block->host, size, QEMU_MADV_HUGEPAGE); diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 9f4ad697b3..681d63b7ba 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -75,12 +75,6 @@ static inline int cpu_physical_memory_get_dirty(ram_addr_t start, return ret; } -static inline void cpu_physical_memory_set_dirty_flags(ram_addr_t addr, - int dirty_flags) -{ - ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags; -} - static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, int dirty_flag) { @@ -103,15 +97,14 @@ static inline int cpu_physical_memory_clear_dirty_flags(ram_addr_t addr, } static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, - ram_addr_t length, - int dirty_flags) + ram_addr_t length) { ram_addr_t addr, end; end = TARGET_PAGE_ALIGN(start + length); start &= TARGET_PAGE_MASK; for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - cpu_physical_memory_set_dirty_flags(addr, dirty_flags); + cpu_physical_memory_set_dirty(addr); } xen_modified_memory(addr, length); } diff --git a/memory.c b/memory.c index e497f998cd..fb52e1cdb2 100644 --- a/memory.c +++ b/memory.c @@ -1182,7 +1182,7 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size) { assert(mr->terminates); - cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size, -1); + cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size); } bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, From 52159192919469b424b54c167312e53d5a62d233 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 12:44:04 +0200 Subject: [PATCH 23/49] memory: cpu_physical_memory_mask_dirty_range() always clears a single flag Document it Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- cputlb.c | 4 ++-- exec.c | 19 ++++++++-------- include/exec/memory-internal.h | 40 +++++++++++++++------------------- include/exec/memory.h | 3 --- memory.c | 10 ++++----- 5 files changed, 34 insertions(+), 42 deletions(-) diff --git a/cputlb.c b/cputlb.c index c545e4c1cf..ec2140580b 100644 --- a/cputlb.c +++ b/cputlb.c @@ -114,7 +114,7 @@ void tlb_protect_code(ram_addr_t ram_addr) { cpu_physical_memory_reset_dirty(ram_addr, ram_addr + TARGET_PAGE_SIZE, - CODE_DIRTY_FLAG); + DIRTY_MEMORY_CODE); } /* update the TLB so that writes in physical page 'phys_addr' are no longer @@ -122,7 +122,7 @@ void tlb_protect_code(ram_addr_t ram_addr) void tlb_unprotect_code_phys(CPUArchState *env, ram_addr_t ram_addr, target_ulong vaddr) { - cpu_physical_memory_set_dirty_flag(ram_addr, CODE_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE); } static bool tlb_is_dirty_ram(CPUTLBEntry *tlbe) diff --git a/exec.c b/exec.c index f21596040d..82aca4ad37 100644 --- a/exec.c +++ b/exec.c @@ -738,7 +738,7 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t end, /* Note: start and end must be within the same ram block. */ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, - int dirty_flags) + unsigned client) { uintptr_t length; @@ -748,7 +748,7 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, length = end - start; if (length == 0) return; - cpu_physical_memory_mask_dirty_range(start, length, dirty_flags); + cpu_physical_memory_mask_dirty_range(start, length, client); if (tcg_enabled()) { tlb_reset_dirty_range_all(start, end, length); @@ -1485,7 +1485,7 @@ found: static void notdirty_mem_write(void *opaque, hwaddr ram_addr, uint64_t val, unsigned size) { - if (!cpu_physical_memory_get_dirty_flag(ram_addr, CODE_DIRTY_FLAG)) { + if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) { tb_invalidate_phys_page_fast(ram_addr, size); } switch (size) { @@ -1501,8 +1501,8 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, default: abort(); } - cpu_physical_memory_set_dirty_flag(ram_addr, MIGRATION_DIRTY_FLAG); - cpu_physical_memory_set_dirty_flag(ram_addr, VGA_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_MIGRATION); + cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_VGA); /* we remove the notdirty callback only if the code has been flushed */ if (cpu_physical_memory_is_dirty(ram_addr)) { @@ -1912,8 +1912,8 @@ static void invalidate_and_set_dirty(hwaddr addr, /* invalidate code */ tb_invalidate_phys_page_range(addr, addr + length, 0); /* set dirty bit */ - cpu_physical_memory_set_dirty_flag(addr, VGA_DIRTY_FLAG); - cpu_physical_memory_set_dirty_flag(addr, MIGRATION_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_VGA); + cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_MIGRATION); } xen_modified_memory(addr, length); } @@ -2528,8 +2528,9 @@ void stl_phys_notdirty(hwaddr addr, uint32_t val) /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); /* set dirty bit */ - cpu_physical_memory_set_dirty_flag(addr1, MIGRATION_DIRTY_FLAG); - cpu_physical_memory_set_dirty_flag(addr1, VGA_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flag(addr1, + DIRTY_MEMORY_MIGRATION); + cpu_physical_memory_set_dirty_flag(addr1, DIRTY_MEMORY_VGA); } } } diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 681d63b7ba..b58010f1d0 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -40,29 +40,25 @@ void *qemu_get_ram_ptr(ram_addr_t addr); void qemu_ram_free(ram_addr_t addr); void qemu_ram_free_from_ptr(ram_addr_t addr); -#define VGA_DIRTY_FLAG 0x01 -#define CODE_DIRTY_FLAG 0x02 -#define MIGRATION_DIRTY_FLAG 0x08 - static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, - int dirty_flag) + unsigned client) { - return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] & dirty_flag; + return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] & (1 << client); } /* read dirty bit (return 0 or 1) */ static inline bool cpu_physical_memory_is_dirty(ram_addr_t addr) { - bool vga = cpu_physical_memory_get_dirty_flag(addr, VGA_DIRTY_FLAG); - bool code = cpu_physical_memory_get_dirty_flag(addr, CODE_DIRTY_FLAG); + bool vga = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_VGA); + bool code = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_CODE); bool migration = - cpu_physical_memory_get_dirty_flag(addr, MIGRATION_DIRTY_FLAG); + cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_MIGRATION); return vga && code && migration; } static inline int cpu_physical_memory_get_dirty(ram_addr_t start, ram_addr_t length, - int dirty_flag) + unsigned client) { int ret = 0; ram_addr_t addr, end; @@ -70,28 +66,28 @@ static inline int cpu_physical_memory_get_dirty(ram_addr_t start, end = TARGET_PAGE_ALIGN(start + length); start &= TARGET_PAGE_MASK; for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - ret |= cpu_physical_memory_get_dirty_flag(addr, dirty_flag); + ret |= cpu_physical_memory_get_dirty_flag(addr, client); } return ret; } static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, - int dirty_flag) + unsigned client) { - ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flag; + ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= (1 << client); } static inline void cpu_physical_memory_set_dirty(ram_addr_t addr) { - cpu_physical_memory_set_dirty_flag(addr, MIGRATION_DIRTY_FLAG); - cpu_physical_memory_set_dirty_flag(addr, VGA_DIRTY_FLAG); - cpu_physical_memory_set_dirty_flag(addr, CODE_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_MIGRATION); + cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_VGA); + cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_CODE); } -static inline int cpu_physical_memory_clear_dirty_flags(ram_addr_t addr, - int dirty_flags) +static inline int cpu_physical_memory_clear_dirty_flag(ram_addr_t addr, + unsigned client) { - int mask = ~dirty_flags; + int mask = ~(1 << client); return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] &= mask; } @@ -111,19 +107,19 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, static inline void cpu_physical_memory_mask_dirty_range(ram_addr_t start, ram_addr_t length, - int dirty_flags) + unsigned client) { ram_addr_t addr, end; end = TARGET_PAGE_ALIGN(start + length); start &= TARGET_PAGE_MASK; for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - cpu_physical_memory_clear_dirty_flags(addr, dirty_flags); + cpu_physical_memory_clear_dirty_flag(addr, client); } } void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, - int dirty_flags); + unsigned client); #endif diff --git a/include/exec/memory.h b/include/exec/memory.h index 480dfbf9da..b8e76f44b3 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -33,9 +33,6 @@ typedef struct MemoryRegionOps MemoryRegionOps; typedef struct MemoryRegionMmio MemoryRegionMmio; -/* Must match *_DIRTY_FLAGS in cpu-all.h. To be replaced with dynamic - * registration. - */ #define DIRTY_MEMORY_VGA 0 #define DIRTY_MEMORY_CODE 1 #define DIRTY_MEMORY_MIGRATION 3 diff --git a/memory.c b/memory.c index fb52e1cdb2..a490cbd667 100644 --- a/memory.c +++ b/memory.c @@ -1174,8 +1174,7 @@ bool memory_region_get_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client) { assert(mr->terminates); - return cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, - 1 << client); + return cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, client); } void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, @@ -1190,12 +1189,11 @@ bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, { bool ret; assert(mr->terminates); - ret = cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, - 1 << client); + ret = cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, client); if (ret) { cpu_physical_memory_reset_dirty(mr->ram_addr + addr, mr->ram_addr + addr + size, - 1 << client); + client); } return ret; } @@ -1243,7 +1241,7 @@ void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr, assert(mr->terminates); cpu_physical_memory_reset_dirty(mr->ram_addr + addr, mr->ram_addr + addr + size, - 1 << client); + client); } void *memory_region_get_ram_ptr(MemoryRegion *mr) From 5adca7ace9b80bf594e4c52c0d5b23573cba3639 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 13:26:29 +0200 Subject: [PATCH 24/49] memory: use bit 2 for migration For historical reasons it was bit 3. Once there, create a constant to know the number of clients. Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/exec/memory.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index b8e76f44b3..d5e9d58102 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -35,7 +35,8 @@ typedef struct MemoryRegionMmio MemoryRegionMmio; #define DIRTY_MEMORY_VGA 0 #define DIRTY_MEMORY_CODE 1 -#define DIRTY_MEMORY_MIGRATION 3 +#define DIRTY_MEMORY_MIGRATION 2 +#define DIRTY_MEMORY_NUM 3 /* num of dirty bits */ struct MemoryRegionMmio { CPUReadMemoryFunc *read[3]; From 7a5b558c9d061814f34d9b95d70d17ef75037937 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 13:29:11 +0200 Subject: [PATCH 25/49] memory: make sure that client is always inside range Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/exec/memory-internal.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index b58010f1d0..d09d6d8355 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -43,6 +43,7 @@ void qemu_ram_free_from_ptr(ram_addr_t addr); static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client) { + assert(client < DIRTY_MEMORY_NUM); return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] & (1 << client); } @@ -74,6 +75,7 @@ static inline int cpu_physical_memory_get_dirty(ram_addr_t start, static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client) { + assert(client < DIRTY_MEMORY_NUM); ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= (1 << client); } @@ -89,6 +91,8 @@ static inline int cpu_physical_memory_clear_dirty_flag(ram_addr_t addr, { int mask = ~(1 << client); + assert(client < DIRTY_MEMORY_NUM); + return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] &= mask; } From 2152f5ca782e51a4d05a1f7d9bfa83286323cbc9 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 13:52:02 +0200 Subject: [PATCH 26/49] memory: only resize dirty bitmap when memory size increases Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- exec.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/exec.c b/exec.c index 82aca4ad37..db369dc42f 100644 --- a/exec.c +++ b/exec.c @@ -1211,6 +1211,9 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, MemoryRegion *mr) { RAMBlock *block, *new_block; + ram_addr_t old_ram_size, new_ram_size; + + old_ram_size = last_ram_offset() >> TARGET_PAGE_BITS; size = TARGET_PAGE_ALIGN(size); new_block = g_malloc0(sizeof(*new_block)); @@ -1271,10 +1274,13 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, ram_list.version++; qemu_mutex_unlock_ramlist(); - ram_list.phys_dirty = g_realloc(ram_list.phys_dirty, - last_ram_offset() >> TARGET_PAGE_BITS); - memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS), + new_ram_size = last_ram_offset() >> TARGET_PAGE_BITS; + + if (new_ram_size > old_ram_size) { + ram_list.phys_dirty = g_realloc(ram_list.phys_dirty, new_ram_size); + memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS), 0, size >> TARGET_PAGE_BITS); + } cpu_physical_memory_set_dirty_range(new_block->offset, size); qemu_ram_setup_dump(new_block->host, size); From e8a97cafc4108fa964807eb9cbce7eda698f99bb Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 15:50:55 +0200 Subject: [PATCH 27/49] memory: cpu_physical_memory_clear_dirty_flag() result is never used Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/exec/memory-internal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index d09d6d8355..666490cc1c 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -86,14 +86,14 @@ static inline void cpu_physical_memory_set_dirty(ram_addr_t addr) cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_CODE); } -static inline int cpu_physical_memory_clear_dirty_flag(ram_addr_t addr, +static inline void cpu_physical_memory_clear_dirty_flag(ram_addr_t addr, unsigned client) { int mask = ~(1 << client); assert(client < DIRTY_MEMORY_NUM); - return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] &= mask; + ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] &= mask; } static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, From 164590a60fd685399da259ac41b338d9a0b9d6c0 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 16:12:17 +0200 Subject: [PATCH 28/49] bitmap: Add bitmap_zero_extend operation Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/qemu/bitmap.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h index afdd257d8f..1babd5d812 100644 --- a/include/qemu/bitmap.h +++ b/include/qemu/bitmap.h @@ -220,4 +220,13 @@ unsigned long bitmap_find_next_zero_area(unsigned long *map, unsigned long nr, unsigned long align_mask); +static inline unsigned long *bitmap_zero_extend(unsigned long *old, + long old_nbits, long new_nbits) +{ + long new_len = BITS_TO_LONGS(new_nbits) * sizeof(unsigned long); + unsigned long *new = g_realloc(old, new_len); + bitmap_clear(new, old_nbits, new_nbits - old_nbits); + return new; +} + #endif /* BITMAP_H */ From 1ab4c8ceaa5ec55af9bb25e88e46d461a8550280 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 16:14:39 +0200 Subject: [PATCH 29/49] memory: split dirty bitmap into three After all the previous patches, spliting the bitmap gets direct. Note: For some reason, I have to move DIRTY_MEMORY_* definitions to the beginning of memory.h to make compilation work. Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- exec.c | 9 ++++++--- include/exec/cpu-all.h | 3 ++- include/exec/memory-internal.h | 9 +++------ include/exec/memory.h | 10 +++++----- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/exec.c b/exec.c index db369dc42f..028eee4f59 100644 --- a/exec.c +++ b/exec.c @@ -1277,9 +1277,12 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, new_ram_size = last_ram_offset() >> TARGET_PAGE_BITS; if (new_ram_size > old_ram_size) { - ram_list.phys_dirty = g_realloc(ram_list.phys_dirty, new_ram_size); - memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS), - 0, size >> TARGET_PAGE_BITS); + int i; + for (i = 0; i < DIRTY_MEMORY_NUM; i++) { + ram_list.dirty_memory[i] = + bitmap_zero_extend(ram_list.dirty_memory[i], + old_ram_size, new_ram_size); + } } cpu_physical_memory_set_dirty_range(new_block->offset, size); diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index b6998f055a..4cb4b4a53a 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -21,6 +21,7 @@ #include "qemu-common.h" #include "exec/cpu-common.h" +#include "exec/memory.h" #include "qemu/thread.h" #include "qom/cpu.h" @@ -459,7 +460,7 @@ typedef struct RAMBlock { typedef struct RAMList { QemuMutex mutex; /* Protected by the iothread lock. */ - uint8_t *phys_dirty; + unsigned long *dirty_memory[DIRTY_MEMORY_NUM]; RAMBlock *mru_block; /* Protected by the ramlist lock. */ QTAILQ_HEAD(, RAMBlock) blocks; diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 666490cc1c..6fb1b64410 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -44,7 +44,7 @@ static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client) { assert(client < DIRTY_MEMORY_NUM); - return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] & (1 << client); + return test_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); } /* read dirty bit (return 0 or 1) */ @@ -76,7 +76,7 @@ static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client) { assert(client < DIRTY_MEMORY_NUM); - ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= (1 << client); + set_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); } static inline void cpu_physical_memory_set_dirty(ram_addr_t addr) @@ -89,11 +89,8 @@ static inline void cpu_physical_memory_set_dirty(ram_addr_t addr) static inline void cpu_physical_memory_clear_dirty_flag(ram_addr_t addr, unsigned client) { - int mask = ~(1 << client); - assert(client < DIRTY_MEMORY_NUM); - - ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] &= mask; + clear_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); } static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, diff --git a/include/exec/memory.h b/include/exec/memory.h index d5e9d58102..296d6ab2f4 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -16,6 +16,11 @@ #ifndef CONFIG_USER_ONLY +#define DIRTY_MEMORY_VGA 0 +#define DIRTY_MEMORY_CODE 1 +#define DIRTY_MEMORY_MIGRATION 2 +#define DIRTY_MEMORY_NUM 3 /* num of dirty bits */ + #include #include #include "qemu-common.h" @@ -33,11 +38,6 @@ typedef struct MemoryRegionOps MemoryRegionOps; typedef struct MemoryRegionMmio MemoryRegionMmio; -#define DIRTY_MEMORY_VGA 0 -#define DIRTY_MEMORY_CODE 1 -#define DIRTY_MEMORY_MIGRATION 2 -#define DIRTY_MEMORY_NUM 3 /* num of dirty bits */ - struct MemoryRegionMmio { CPUReadMemoryFunc *read[3]; CPUWriteMemoryFunc *write[3]; From 86a49582dbe66d59a9366d2f002f0d6af965ecb8 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 20:21:10 +0200 Subject: [PATCH 30/49] memory: unfold cpu_physical_memory_clear_dirty_flag() in its only user Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/exec/memory-internal.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 6fb1b64410..2f6610a464 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -86,13 +86,6 @@ static inline void cpu_physical_memory_set_dirty(ram_addr_t addr) cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_CODE); } -static inline void cpu_physical_memory_clear_dirty_flag(ram_addr_t addr, - unsigned client) -{ - assert(client < DIRTY_MEMORY_NUM); - clear_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); -} - static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length) { @@ -112,10 +105,11 @@ static inline void cpu_physical_memory_mask_dirty_range(ram_addr_t start, { ram_addr_t addr, end; + assert(client < DIRTY_MEMORY_NUM); end = TARGET_PAGE_ALIGN(start + length); start &= TARGET_PAGE_MASK; for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - cpu_physical_memory_clear_dirty_flag(addr, client); + clear_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); } } From 4f13bb80a2dc1bb851b43da6dd9117473772b197 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 20:25:16 +0200 Subject: [PATCH 31/49] memory: unfold cpu_physical_memory_set_dirty() in its only user Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/exec/memory-internal.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 2f6610a464..9d344342e4 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -79,13 +79,6 @@ static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, set_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); } -static inline void cpu_physical_memory_set_dirty(ram_addr_t addr) -{ - cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_MIGRATION); - cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_VGA); - cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_CODE); -} - static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length) { @@ -94,7 +87,9 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, end = TARGET_PAGE_ALIGN(start + length); start &= TARGET_PAGE_MASK; for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - cpu_physical_memory_set_dirty(addr); + cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_MIGRATION); + cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_VGA); + cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_CODE); } xen_modified_memory(addr, length); } From c1427a3f84c42d5159d10927700eafc9b653d68f Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 20:27:58 +0200 Subject: [PATCH 32/49] memory: unfold cpu_physical_memory_set_dirty_flag() Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/exec/memory-internal.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 9d344342e4..b99617a500 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -87,9 +87,12 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, end = TARGET_PAGE_ALIGN(start + length); start &= TARGET_PAGE_MASK; for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_MIGRATION); - cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_VGA); - cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_CODE); + set_bit(addr >> TARGET_PAGE_BITS, + ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION]); + set_bit(addr >> TARGET_PAGE_BITS, + ram_list.dirty_memory[DIRTY_MEMORY_VGA]); + set_bit(addr >> TARGET_PAGE_BITS, + ram_list.dirty_memory[DIRTY_MEMORY_CODE]); } xen_modified_memory(addr, length); } From 9f2c43e41a13605d3ab1e37ba433aa669652fb6b Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 20:35:39 +0200 Subject: [PATCH 33/49] memory: make cpu_physical_memory_get_dirty() the main function And make cpu_physical_memory_get_dirty_flag() to use it. It used to be the other way around. Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/exec/memory-internal.h | 36 ++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index b99617a500..edca8a87ce 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -40,11 +40,28 @@ void *qemu_get_ram_ptr(ram_addr_t addr); void qemu_ram_free(ram_addr_t addr); void qemu_ram_free_from_ptr(ram_addr_t addr); +static inline int cpu_physical_memory_get_dirty(ram_addr_t start, + ram_addr_t length, + unsigned client) +{ + int ret = 0; + ram_addr_t addr, end; + + assert(client < DIRTY_MEMORY_NUM); + + end = TARGET_PAGE_ALIGN(start + length); + start &= TARGET_PAGE_MASK; + for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { + ret |= test_bit(addr >> TARGET_PAGE_BITS, + ram_list.dirty_memory[client]); + } + return ret; +} + static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, unsigned client) { - assert(client < DIRTY_MEMORY_NUM); - return test_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); + return cpu_physical_memory_get_dirty(addr, 1, client); } /* read dirty bit (return 0 or 1) */ @@ -57,21 +74,6 @@ static inline bool cpu_physical_memory_is_dirty(ram_addr_t addr) return vga && code && migration; } -static inline int cpu_physical_memory_get_dirty(ram_addr_t start, - ram_addr_t length, - unsigned client) -{ - int ret = 0; - ram_addr_t addr, end; - - end = TARGET_PAGE_ALIGN(start + length); - start &= TARGET_PAGE_MASK; - for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - ret |= cpu_physical_memory_get_dirty_flag(addr, client); - } - return ret; -} - static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client) { From 94833c896d2d339571a66542f2715b1897d70fa7 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 8 Oct 2013 20:40:55 +0200 Subject: [PATCH 34/49] memory: cpu_physical_memory_get_dirty() is used as returning a bool Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/exec/memory-internal.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index edca8a87ce..fa28fc677d 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -40,11 +40,10 @@ void *qemu_get_ram_ptr(ram_addr_t addr); void qemu_ram_free(ram_addr_t addr); void qemu_ram_free_from_ptr(ram_addr_t addr); -static inline int cpu_physical_memory_get_dirty(ram_addr_t start, - ram_addr_t length, - unsigned client) +static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, + ram_addr_t length, + unsigned client) { - int ret = 0; ram_addr_t addr, end; assert(client < DIRTY_MEMORY_NUM); @@ -52,10 +51,12 @@ static inline int cpu_physical_memory_get_dirty(ram_addr_t start, end = TARGET_PAGE_ALIGN(start + length); start &= TARGET_PAGE_MASK; for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - ret |= test_bit(addr >> TARGET_PAGE_BITS, - ram_list.dirty_memory[client]); + if (test_bit(addr >> TARGET_PAGE_BITS, + ram_list.dirty_memory[client])) { + return true; + } } - return ret; + return false; } static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, From ace694cccccf343852d9f0b34171ad475e248bbf Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 9 Oct 2013 10:36:56 +0200 Subject: [PATCH 35/49] memory: s/mask/clear/ cpu_physical_memory_mask_dirty_range Now all functions use the same wording that bitops/bitmap operations Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- exec.c | 2 +- include/exec/memory-internal.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exec.c b/exec.c index 028eee4f59..9267596c05 100644 --- a/exec.c +++ b/exec.c @@ -748,7 +748,7 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, length = end - start; if (length == 0) return; - cpu_physical_memory_mask_dirty_range(start, length, client); + cpu_physical_memory_clear_dirty_range(start, length, client); if (tcg_enabled()) { tlb_reset_dirty_range_all(start, end, length); diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index fa28fc677d..c04a92a2d9 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -100,9 +100,9 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, xen_modified_memory(addr, length); } -static inline void cpu_physical_memory_mask_dirty_range(ram_addr_t start, - ram_addr_t length, - unsigned client) +static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, + ram_addr_t length, + unsigned client) { ram_addr_t addr, end; From 1bafff0c7cb99972fd243464632eca0780c6a8f1 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 9 Oct 2013 12:15:06 +0200 Subject: [PATCH 36/49] memory: use find_next_bit() to find dirty bits This operation is way faster than doing it bit by bit. Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/exec/memory-internal.h | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index c04a92a2d9..b017c2ec98 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -44,19 +44,15 @@ static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, ram_addr_t length, unsigned client) { - ram_addr_t addr, end; + unsigned long end, page, next; assert(client < DIRTY_MEMORY_NUM); - end = TARGET_PAGE_ALIGN(start + length); - start &= TARGET_PAGE_MASK; - for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - if (test_bit(addr >> TARGET_PAGE_BITS, - ram_list.dirty_memory[client])) { - return true; - } - } - return false; + end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; + page = start >> TARGET_PAGE_BITS; + next = find_next_bit(ram_list.dirty_memory[client], end, page); + + return next < end; } static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, From 5b9a3a5f77e3458af6c1bb0654ee0f32936a5594 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 9 Oct 2013 12:26:23 +0200 Subject: [PATCH 37/49] memory: cpu_physical_memory_set_dirty_range() now uses bitmap operations We were setting a range of bits, so use bitmap_set(). Note: xen has always been wrong, and should have used start instead of addr from the beginning. Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/exec/memory-internal.h | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index b017c2ec98..4906cdf0ea 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -81,19 +81,14 @@ static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length) { - ram_addr_t addr, end; + unsigned long end, page; - end = TARGET_PAGE_ALIGN(start + length); - start &= TARGET_PAGE_MASK; - for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - set_bit(addr >> TARGET_PAGE_BITS, - ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION]); - set_bit(addr >> TARGET_PAGE_BITS, - ram_list.dirty_memory[DIRTY_MEMORY_VGA]); - set_bit(addr >> TARGET_PAGE_BITS, - ram_list.dirty_memory[DIRTY_MEMORY_CODE]); - } - xen_modified_memory(addr, length); + end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; + page = start >> TARGET_PAGE_BITS; + bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION], page, end - page); + bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_VGA], page, end - page); + bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_CODE], page, end - page); + xen_modified_memory(start, length); } static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, From a461e389f489e72cdc770ff887512c2c9109bc43 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 9 Oct 2013 12:30:35 +0200 Subject: [PATCH 38/49] memory: cpu_physical_memory_clear_dirty_range() now uses bitmap operations We were clearing a range of bits, so use bitmap_clear(). Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- include/exec/memory-internal.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 4906cdf0ea..e2f55eaeb1 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -95,14 +95,12 @@ static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, ram_addr_t length, unsigned client) { - ram_addr_t addr, end; + unsigned long end, page; assert(client < DIRTY_MEMORY_NUM); - end = TARGET_PAGE_ALIGN(start + length); - start &= TARGET_PAGE_MASK; - for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - clear_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); - } + end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; + page = start >> TARGET_PAGE_BITS; + bitmap_clear(ram_list.dirty_memory[client], page, end - page); } void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, From a2cd8c852d2d8c2a084b68b2470f214d6726f6d2 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 10 Oct 2013 11:20:22 +0200 Subject: [PATCH 39/49] memory: s/dirty/clean/ in cpu_physical_memory_is_dirty() All uses except one really want the other meaning. Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- cputlb.c | 3 ++- exec.c | 6 +++--- include/exec/memory-internal.h | 5 ++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cputlb.c b/cputlb.c index ec2140580b..eab91d7b5e 100644 --- a/cputlb.c +++ b/cputlb.c @@ -284,7 +284,8 @@ void tlb_set_page(CPUArchState *env, target_ulong vaddr, /* Write access calls the I/O callback. */ te->addr_write = address | TLB_MMIO; } else if (memory_region_is_ram(section->mr) - && !cpu_physical_memory_is_dirty(section->mr->ram_addr + xlat)) { + && cpu_physical_memory_is_clean(section->mr->ram_addr + + xlat)) { te->addr_write = address | TLB_NOTDIRTY; } else { te->addr_write = address; diff --git a/exec.c b/exec.c index 9267596c05..47add21303 100644 --- a/exec.c +++ b/exec.c @@ -1514,7 +1514,7 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_VGA); /* we remove the notdirty callback only if the code has been flushed */ - if (cpu_physical_memory_is_dirty(ram_addr)) { + if (!cpu_physical_memory_is_clean(ram_addr)) { CPUArchState *env = current_cpu->env_ptr; tlb_set_dirty(env, env->mem_io_vaddr); } @@ -1917,7 +1917,7 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, static void invalidate_and_set_dirty(hwaddr addr, hwaddr length) { - if (!cpu_physical_memory_is_dirty(addr)) { + if (cpu_physical_memory_is_clean(addr)) { /* invalidate code */ tb_invalidate_phys_page_range(addr, addr + length, 0); /* set dirty bit */ @@ -2533,7 +2533,7 @@ void stl_phys_notdirty(hwaddr addr, uint32_t val) stl_p(ptr, val); if (unlikely(in_migration)) { - if (!cpu_physical_memory_is_dirty(addr1)) { + if (cpu_physical_memory_is_clean(addr1)) { /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); /* set dirty bit */ diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index e2f55eaeb1..771b23f321 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -61,14 +61,13 @@ static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, return cpu_physical_memory_get_dirty(addr, 1, client); } -/* read dirty bit (return 0 or 1) */ -static inline bool cpu_physical_memory_is_dirty(ram_addr_t addr) +static inline bool cpu_physical_memory_is_clean(ram_addr_t addr) { bool vga = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_VGA); bool code = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_CODE); bool migration = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_MIGRATION); - return vga && code && migration; + return !(vga && code && migration); } static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, From a2f4d5bef2cfde557d76fc45a40d2c89b6bed4e4 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 10 Oct 2013 11:49:53 +0200 Subject: [PATCH 40/49] memory: make cpu_physical_memory_reset_dirty() take a length parameter We have an end parameter in all the callers, and this make it coherent with the rest of cpu_physical_memory_* functions, that also take a length parameter. Once here, move the start/end calculation to tlb_reset_dirty_range_all() as we don't need it here anymore. Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- cputlb.c | 3 +-- exec.c | 19 ++++++++----------- include/exec/memory-internal.h | 2 +- memory.c | 8 ++------ 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/cputlb.c b/cputlb.c index eab91d7b5e..0ac8d90a48 100644 --- a/cputlb.c +++ b/cputlb.c @@ -112,8 +112,7 @@ void tlb_flush_page(CPUArchState *env, target_ulong addr) can be detected */ void tlb_protect_code(ram_addr_t ram_addr) { - cpu_physical_memory_reset_dirty(ram_addr, - ram_addr + TARGET_PAGE_SIZE, + cpu_physical_memory_reset_dirty(ram_addr, TARGET_PAGE_SIZE, DIRTY_MEMORY_CODE); } diff --git a/exec.c b/exec.c index 47add21303..c71f2d558a 100644 --- a/exec.c +++ b/exec.c @@ -724,11 +724,14 @@ found: return block; } -static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t end, - uintptr_t length) +static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) { - RAMBlock *block; ram_addr_t start1; + RAMBlock *block; + ram_addr_t end; + + end = TARGET_PAGE_ALIGN(start + length); + start &= TARGET_PAGE_MASK; block = qemu_get_ram_block(start); assert(block == qemu_get_ram_block(end - 1)); @@ -737,21 +740,15 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t end, } /* Note: start and end must be within the same ram block. */ -void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, +void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length, unsigned client) { - uintptr_t length; - - start &= TARGET_PAGE_MASK; - end = TARGET_PAGE_ALIGN(end); - - length = end - start; if (length == 0) return; cpu_physical_memory_clear_dirty_range(start, length, client); if (tcg_enabled()) { - tlb_reset_dirty_range_all(start, end, length); + tlb_reset_dirty_range_all(start, length); } } diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 771b23f321..cb2249fd0b 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -102,7 +102,7 @@ static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, bitmap_clear(ram_list.dirty_memory[client], page, end - page); } -void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, +void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length, unsigned client); #endif diff --git a/memory.c b/memory.c index a490cbd667..c01029633e 100644 --- a/memory.c +++ b/memory.c @@ -1191,9 +1191,7 @@ bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, assert(mr->terminates); ret = cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, client); if (ret) { - cpu_physical_memory_reset_dirty(mr->ram_addr + addr, - mr->ram_addr + addr + size, - client); + cpu_physical_memory_reset_dirty(mr->ram_addr + addr, size, client); } return ret; } @@ -1239,9 +1237,7 @@ void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client) { assert(mr->terminates); - cpu_physical_memory_reset_dirty(mr->ram_addr + addr, - mr->ram_addr + addr + size, - client); + cpu_physical_memory_reset_dirty(mr->ram_addr + addr, size, client); } void *memory_region_get_ram_ptr(MemoryRegion *mr) From 981fdf2353d5e708adbe5227260d5e46eececc05 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 10 Oct 2013 11:54:09 +0200 Subject: [PATCH 41/49] memory: cpu_physical_memory_set_dirty_tracking() should return void Result was always 0, and not used anywhere. Once there, use bool type for the parameter. Signed-off-by: Juan Quintela Reviewed-by: Eric Blake Reviewed-by: Orit Wasserman --- exec.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/exec.c b/exec.c index c71f2d558a..514d6a08cc 100644 --- a/exec.c +++ b/exec.c @@ -57,7 +57,7 @@ //#define DEBUG_SUBPAGE #if !defined(CONFIG_USER_ONLY) -static int in_migration; +static bool in_migration; RAMList ram_list = { .blocks = QTAILQ_HEAD_INITIALIZER(ram_list.blocks) }; @@ -752,11 +752,9 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length, } } -static int cpu_physical_memory_set_dirty_tracking(int enable) +static void cpu_physical_memory_set_dirty_tracking(bool enable) { - int ret = 0; in_migration = enable; - return ret; } hwaddr memory_region_section_get_iotlb(CPUArchState *env, @@ -1798,12 +1796,12 @@ static void tcg_commit(MemoryListener *listener) static void core_log_global_start(MemoryListener *listener) { - cpu_physical_memory_set_dirty_tracking(1); + cpu_physical_memory_set_dirty_tracking(true); } static void core_log_global_stop(MemoryListener *listener) { - cpu_physical_memory_set_dirty_tracking(0); + cpu_physical_memory_set_dirty_tracking(false); } static MemoryListener core_memory_listener = { From 220c3ebddbd1ac289ae7fc64733c9501b3921d94 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 14 Oct 2013 17:13:59 +0200 Subject: [PATCH 42/49] memory: split cpu_physical_memory_* functions to its own include All the functions that use ram_addr_t should be here. Signed-off-by: Juan Quintela Reviewed-by: Orit Wasserman --- cputlb.c | 1 + exec.c | 1 + include/exec/memory-internal.h | 76 -------------------------- include/exec/ram_addr.h | 98 ++++++++++++++++++++++++++++++++++ memory.c | 1 + 5 files changed, 101 insertions(+), 76 deletions(-) create mode 100644 include/exec/ram_addr.h diff --git a/cputlb.c b/cputlb.c index 0ac8d90a48..b533f3f372 100644 --- a/cputlb.c +++ b/cputlb.c @@ -26,6 +26,7 @@ #include "exec/cputlb.h" #include "exec/memory-internal.h" +#include "exec/ram_addr.h" //#define DEBUG_TLB //#define DEBUG_TLB_CHECK diff --git a/exec.c b/exec.c index 514d6a08cc..b387d2856d 100644 --- a/exec.c +++ b/exec.c @@ -50,6 +50,7 @@ #include "translate-all.h" #include "exec/memory-internal.h" +#include "exec/ram_addr.h" #include "qemu/cache-utils.h" #include "qemu/range.h" diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index cb2249fd0b..25c43c06e9 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -20,9 +20,6 @@ #define MEMORY_INTERNAL_H #ifndef CONFIG_USER_ONLY -#include "hw/xen/xen.h" - - typedef struct AddressSpaceDispatch AddressSpaceDispatch; void address_space_init_dispatch(AddressSpace *as); @@ -33,78 +30,5 @@ extern const MemoryRegionOps unassigned_mem_ops; bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, unsigned size, bool is_write); -ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, - MemoryRegion *mr); -ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr); -void *qemu_get_ram_ptr(ram_addr_t addr); -void qemu_ram_free(ram_addr_t addr); -void qemu_ram_free_from_ptr(ram_addr_t addr); - -static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, - ram_addr_t length, - unsigned client) -{ - unsigned long end, page, next; - - assert(client < DIRTY_MEMORY_NUM); - - end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; - page = start >> TARGET_PAGE_BITS; - next = find_next_bit(ram_list.dirty_memory[client], end, page); - - return next < end; -} - -static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, - unsigned client) -{ - return cpu_physical_memory_get_dirty(addr, 1, client); -} - -static inline bool cpu_physical_memory_is_clean(ram_addr_t addr) -{ - bool vga = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_VGA); - bool code = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_CODE); - bool migration = - cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_MIGRATION); - return !(vga && code && migration); -} - -static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, - unsigned client) -{ - assert(client < DIRTY_MEMORY_NUM); - set_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); -} - -static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, - ram_addr_t length) -{ - unsigned long end, page; - - end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; - page = start >> TARGET_PAGE_BITS; - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION], page, end - page); - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_VGA], page, end - page); - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_CODE], page, end - page); - xen_modified_memory(start, length); -} - -static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, - ram_addr_t length, - unsigned client) -{ - unsigned long end, page; - - assert(client < DIRTY_MEMORY_NUM); - end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; - page = start >> TARGET_PAGE_BITS; - bitmap_clear(ram_list.dirty_memory[client], page, end - page); -} - -void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length, - unsigned client); - #endif - #endif diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h new file mode 100644 index 0000000000..db977fbdae --- /dev/null +++ b/include/exec/ram_addr.h @@ -0,0 +1,98 @@ +/* + * Declarations for cpu physical memory functions + * + * Copyright 2011 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Avi Kivity + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + */ + +/* + * This header is for use by exec.c and memory.c ONLY. Do not include it. + * The functions declared here will be removed soon. + */ + +#ifndef RAM_ADDR_H +#define RAM_ADDR_H + +#ifndef CONFIG_USER_ONLY +#include "hw/xen/xen.h" + +ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, + MemoryRegion *mr); +ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr); +void *qemu_get_ram_ptr(ram_addr_t addr); +void qemu_ram_free(ram_addr_t addr); +void qemu_ram_free_from_ptr(ram_addr_t addr); + +static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, + ram_addr_t length, + unsigned client) +{ + unsigned long end, page, next; + + assert(client < DIRTY_MEMORY_NUM); + + end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; + page = start >> TARGET_PAGE_BITS; + next = find_next_bit(ram_list.dirty_memory[client], end, page); + + return next < end; +} + +static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, + unsigned client) +{ + return cpu_physical_memory_get_dirty(addr, 1, client); +} + +static inline bool cpu_physical_memory_is_clean(ram_addr_t addr) +{ + bool vga = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_VGA); + bool code = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_CODE); + bool migration = + cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_MIGRATION); + return !(vga && code && migration); +} + +static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, + unsigned client) +{ + assert(client < DIRTY_MEMORY_NUM); + set_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); +} + +static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, + ram_addr_t length) +{ + unsigned long end, page; + + end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; + page = start >> TARGET_PAGE_BITS; + bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION], page, end - page); + bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_VGA], page, end - page); + bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_CODE], page, end - page); + xen_modified_memory(start, length); +} + +static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, + ram_addr_t length, + unsigned client) +{ + unsigned long end, page; + + assert(client < DIRTY_MEMORY_NUM); + end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; + page = start >> TARGET_PAGE_BITS; + bitmap_clear(ram_list.dirty_memory[client], page, end - page); +} + +void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length, + unsigned client); + +#endif +#endif diff --git a/memory.c b/memory.c index c01029633e..59ecc28401 100644 --- a/memory.c +++ b/memory.c @@ -22,6 +22,7 @@ #include #include "exec/memory-internal.h" +#include "exec/ram_addr.h" //#define DEBUG_UNASSIGNED From 12291ec18fdce3c1973c172f5a942a1bd26b9a5f Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 14 Oct 2013 17:14:47 +0200 Subject: [PATCH 43/49] memory: unfold memory_region_test_and_clear() We are going to update the bitmap directly Signed-off-by: Juan Quintela Reviewed-by: Orit Wasserman --- arch_init.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/arch_init.c b/arch_init.c index e0acbc5661..0e8c8b5fc1 100644 --- a/arch_init.c +++ b/arch_init.c @@ -48,6 +48,7 @@ #include "qmp-commands.h" #include "trace.h" #include "exec/cpu-all.h" +#include "exec/ram_addr.h" #include "hw/acpi/acpi.h" #ifdef DEBUG_ARCH_INIT @@ -400,9 +401,12 @@ static void migration_bitmap_sync(void) QTAILQ_FOREACH(block, &ram_list.blocks, next) { for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) { - if (memory_region_test_and_clear_dirty(block->mr, - addr, TARGET_PAGE_SIZE, - DIRTY_MEMORY_MIGRATION)) { + if (cpu_physical_memory_get_dirty(block->mr->ram_addr + addr, + TARGET_PAGE_SIZE, + DIRTY_MEMORY_MIGRATION)) { + cpu_physical_memory_reset_dirty(block->mr->ram_addr + addr, + TARGET_PAGE_SIZE, + DIRTY_MEMORY_MIGRATION); migration_bitmap_set_dirty(block->mr, addr); } } From 747afd5bcdc90f608c75d95f8a629a9cfc838c57 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 4 Nov 2013 12:59:02 +0100 Subject: [PATCH 44/49] kvm: use directly cpu_physical_memory_* api for tracking dirty pages Performance is important in this function, and we want to optimize even further. Signed-off-by: Juan Quintela Reviewed-by: Orit Wasserman --- kvm-all.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/kvm-all.c b/kvm-all.c index 393775459d..308dfba4a6 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -31,6 +31,7 @@ #include "sysemu/kvm.h" #include "qemu/bswap.h" #include "exec/memory.h" +#include "exec/ram_addr.h" #include "exec/address-spaces.h" #include "qemu/event_notifier.h" #include "trace.h" @@ -382,6 +383,7 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section, unsigned int i, j; unsigned long page_number, c; hwaddr addr, addr1; + ram_addr_t ram_addr; unsigned int pages = int128_get64(section->size) / getpagesize(); unsigned int len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS; unsigned long hpratio = getpagesize() / TARGET_PAGE_SIZE; @@ -399,8 +401,9 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section, page_number = (i * HOST_LONG_BITS + j) * hpratio; addr1 = page_number * TARGET_PAGE_SIZE; addr = section->offset_within_region + addr1; - memory_region_set_dirty(section->mr, addr, - TARGET_PAGE_SIZE * hpratio); + ram_addr = section->mr->ram_addr + addr; + cpu_physical_memory_set_dirty_range(ram_addr, + TARGET_PAGE_SIZE * hpratio); } while (c != 0); } } From c9dd46fc0d64d9f314aa3c220d4aff9d01ab778e Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 5 Nov 2013 15:45:46 +0100 Subject: [PATCH 45/49] kvm: refactor start address calculation Signed-off-by: Juan Quintela Reviewed-by: Orit Wasserman --- kvm-all.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kvm-all.c b/kvm-all.c index 308dfba4a6..cb62ba4333 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -382,7 +382,8 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section, { unsigned int i, j; unsigned long page_number, c; - hwaddr addr, addr1; + hwaddr addr; + ram_addr_t start = section->offset_within_region + section->mr->ram_addr; ram_addr_t ram_addr; unsigned int pages = int128_get64(section->size) / getpagesize(); unsigned int len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS; @@ -399,9 +400,8 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section, j = ffsl(c) - 1; c &= ~(1ul << j); page_number = (i * HOST_LONG_BITS + j) * hpratio; - addr1 = page_number * TARGET_PAGE_SIZE; - addr = section->offset_within_region + addr1; - ram_addr = section->mr->ram_addr + addr; + addr = page_number * TARGET_PAGE_SIZE; + ram_addr = start + addr; cpu_physical_memory_set_dirty_range(ram_addr, TARGET_PAGE_SIZE * hpratio); } while (c != 0); From 5ff7fb77b3cee8e26648e4fdccb23a77c2a6d3c6 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 5 Nov 2013 15:52:54 +0100 Subject: [PATCH 46/49] memory: move bitmap synchronization to its own function We want to have all the functions that handle directly the dirty bitmap near. We will change it later. Signed-off-by: Juan Quintela Reviewed-by: Orit Wasserman --- include/exec/ram_addr.h | 31 +++++++++++++++++++++++++++++++ kvm-all.c | 27 ++------------------------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index db977fbdae..c6736ed04f 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -79,6 +79,37 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, xen_modified_memory(start, length); } +static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, + ram_addr_t start, + ram_addr_t pages) +{ + unsigned int i, j; + unsigned long page_number, c; + hwaddr addr; + ram_addr_t ram_addr; + unsigned int len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS; + unsigned long hpratio = getpagesize() / TARGET_PAGE_SIZE; + + /* + * bitmap-traveling is faster than memory-traveling (for addr...) + * especially when most of the memory is not dirty. + */ + for (i = 0; i < len; i++) { + if (bitmap[i] != 0) { + c = leul_to_cpu(bitmap[i]); + do { + j = ffsl(c) - 1; + c &= ~(1ul << j); + page_number = (i * HOST_LONG_BITS + j) * hpratio; + addr = page_number * TARGET_PAGE_SIZE; + ram_addr = start + addr; + cpu_physical_memory_set_dirty_range(ram_addr, + TARGET_PAGE_SIZE * hpratio); + } while (c != 0); + } + } +} + static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, ram_addr_t length, unsigned client) diff --git a/kvm-all.c b/kvm-all.c index cb62ba4333..0bfb060fa7 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -380,33 +380,10 @@ static int kvm_set_migration_log(int enable) static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section, unsigned long *bitmap) { - unsigned int i, j; - unsigned long page_number, c; - hwaddr addr; ram_addr_t start = section->offset_within_region + section->mr->ram_addr; - ram_addr_t ram_addr; - unsigned int pages = int128_get64(section->size) / getpagesize(); - unsigned int len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS; - unsigned long hpratio = getpagesize() / TARGET_PAGE_SIZE; + ram_addr_t pages = int128_get64(section->size) / getpagesize(); - /* - * bitmap-traveling is faster than memory-traveling (for addr...) - * especially when most of the memory is not dirty. - */ - for (i = 0; i < len; i++) { - if (bitmap[i] != 0) { - c = leul_to_cpu(bitmap[i]); - do { - j = ffsl(c) - 1; - c &= ~(1ul << j); - page_number = (i * HOST_LONG_BITS + j) * hpratio; - addr = page_number * TARGET_PAGE_SIZE; - ram_addr = start + addr; - cpu_physical_memory_set_dirty_range(ram_addr, - TARGET_PAGE_SIZE * hpratio); - } while (c != 0); - } - } + cpu_physical_memory_set_dirty_lebitmap(bitmap, start, pages); return 0; } From ae2810c4bb3b383176e8e1b33931b16c01483aab Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 5 Nov 2013 16:46:26 +0100 Subject: [PATCH 47/49] memory: syncronize kvm bitmap using bitmaps operations If bitmaps are aligned properly, use bitmap operations. If they are not, just use old bit at a time code. Signed-off-by: Juan Quintela Reviewed-by: Orit Wasserman --- include/exec/ram_addr.h | 54 +++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index c6736ed04f..33c8acc02e 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -83,29 +83,47 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, ram_addr_t start, ram_addr_t pages) { - unsigned int i, j; + unsigned long i, j; unsigned long page_number, c; hwaddr addr; ram_addr_t ram_addr; - unsigned int len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS; + unsigned long len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS; unsigned long hpratio = getpagesize() / TARGET_PAGE_SIZE; + unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); - /* - * bitmap-traveling is faster than memory-traveling (for addr...) - * especially when most of the memory is not dirty. - */ - for (i = 0; i < len; i++) { - if (bitmap[i] != 0) { - c = leul_to_cpu(bitmap[i]); - do { - j = ffsl(c) - 1; - c &= ~(1ul << j); - page_number = (i * HOST_LONG_BITS + j) * hpratio; - addr = page_number * TARGET_PAGE_SIZE; - ram_addr = start + addr; - cpu_physical_memory_set_dirty_range(ram_addr, - TARGET_PAGE_SIZE * hpratio); - } while (c != 0); + /* start address is aligned at the start of a word? */ + if (((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) { + long k; + long nr = BITS_TO_LONGS(pages); + + for (k = 0; k < nr; k++) { + if (bitmap[k]) { + unsigned long temp = leul_to_cpu(bitmap[k]); + + ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION][page + k] |= temp; + ram_list.dirty_memory[DIRTY_MEMORY_VGA][page + k] |= temp; + ram_list.dirty_memory[DIRTY_MEMORY_CODE][page + k] |= temp; + } + } + xen_modified_memory(start, pages); + } else { + /* + * bitmap-traveling is faster than memory-traveling (for addr...) + * especially when most of the memory is not dirty. + */ + for (i = 0; i < len; i++) { + if (bitmap[i] != 0) { + c = leul_to_cpu(bitmap[i]); + do { + j = ffsl(c) - 1; + c &= ~(1ul << j); + page_number = (i * HOST_LONG_BITS + j) * hpratio; + addr = page_number * TARGET_PAGE_SIZE; + ram_addr = start + addr; + cpu_physical_memory_set_dirty_range(ram_addr, + TARGET_PAGE_SIZE * hpratio); + } while (c != 0); + } } } } From 791fa2a2451799232d6bc0c29c0fbb13b5293eeb Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 5 Nov 2013 16:47:20 +0100 Subject: [PATCH 48/49] ram: split function that synchronizes a range This function is the only bit where we care about speed. Signed-off-by: Juan Quintela Reviewed-by: Orit Wasserman --- arch_init.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/arch_init.c b/arch_init.c index 0e8c8b5fc1..2cd3d00460 100644 --- a/arch_init.c +++ b/arch_init.c @@ -360,11 +360,10 @@ ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr, return (next - base) << TARGET_PAGE_BITS; } -static inline bool migration_bitmap_set_dirty(MemoryRegion *mr, - ram_addr_t offset) +static inline bool migration_bitmap_set_dirty(ram_addr_t addr) { bool ret; - int nr = (mr->ram_addr + offset) >> TARGET_PAGE_BITS; + int nr = addr >> TARGET_PAGE_BITS; ret = test_and_set_bit(nr, migration_bitmap); @@ -374,12 +373,28 @@ static inline bool migration_bitmap_set_dirty(MemoryRegion *mr, return ret; } +static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length) +{ + ram_addr_t addr; + + for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { + if (cpu_physical_memory_get_dirty(start + addr, + TARGET_PAGE_SIZE, + DIRTY_MEMORY_MIGRATION)) { + cpu_physical_memory_reset_dirty(start + addr, + TARGET_PAGE_SIZE, + DIRTY_MEMORY_MIGRATION); + migration_bitmap_set_dirty(start + addr); + } + } +} + + /* Needs iothread lock! */ static void migration_bitmap_sync(void) { RAMBlock *block; - ram_addr_t addr; uint64_t num_dirty_pages_init = migration_dirty_pages; MigrationState *s = migrate_get_current(); static int64_t start_time; @@ -400,16 +415,7 @@ static void migration_bitmap_sync(void) address_space_sync_dirty_bitmap(&address_space_memory); QTAILQ_FOREACH(block, &ram_list.blocks, next) { - for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) { - if (cpu_physical_memory_get_dirty(block->mr->ram_addr + addr, - TARGET_PAGE_SIZE, - DIRTY_MEMORY_MIGRATION)) { - cpu_physical_memory_reset_dirty(block->mr->ram_addr + addr, - TARGET_PAGE_SIZE, - DIRTY_MEMORY_MIGRATION); - migration_bitmap_set_dirty(block->mr, addr); - } - } + migration_bitmap_sync_range(block->mr->ram_addr, block->length); } trace_migration_bitmap_sync_end(migration_dirty_pages - num_dirty_pages_init); From aa8dc044772ba156cbcf2174b5673cfa11f566a7 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 6 Nov 2013 11:33:05 +0100 Subject: [PATCH 49/49] migration: synchronize memory bitmap 64bits at a time We use the old code if the bitmaps are not aligned Signed-off-by: Juan Quintela Reviewed-by: Orit Wasserman --- arch_init.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/arch_init.c b/arch_init.c index 2cd3d00460..77912e7a7d 100644 --- a/arch_init.c +++ b/arch_init.c @@ -50,6 +50,7 @@ #include "exec/cpu-all.h" #include "exec/ram_addr.h" #include "hw/acpi/acpi.h" +#include "qemu/host-utils.h" #ifdef DEBUG_ARCH_INIT #define DPRINTF(fmt, ...) \ @@ -376,15 +377,34 @@ static inline bool migration_bitmap_set_dirty(ram_addr_t addr) static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length) { ram_addr_t addr; + unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); - for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { - if (cpu_physical_memory_get_dirty(start + addr, - TARGET_PAGE_SIZE, - DIRTY_MEMORY_MIGRATION)) { - cpu_physical_memory_reset_dirty(start + addr, - TARGET_PAGE_SIZE, - DIRTY_MEMORY_MIGRATION); - migration_bitmap_set_dirty(start + addr); + /* start address is aligned at the start of a word? */ + if (((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) { + int k; + int nr = BITS_TO_LONGS(length >> TARGET_PAGE_BITS); + unsigned long *src = ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION]; + + for (k = page; k < page + nr; k++) { + if (src[k]) { + unsigned long new_dirty; + new_dirty = ~migration_bitmap[k]; + migration_bitmap[k] |= src[k]; + new_dirty &= src[k]; + migration_dirty_pages += ctpopl(new_dirty); + src[k] = 0; + } + } + } else { + for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { + if (cpu_physical_memory_get_dirty(start + addr, + TARGET_PAGE_SIZE, + DIRTY_MEMORY_MIGRATION)) { + cpu_physical_memory_reset_dirty(start + addr, + TARGET_PAGE_SIZE, + DIRTY_MEMORY_MIGRATION); + migration_bitmap_set_dirty(start + addr); + } } } }