From patchwork Wed Aug 19 14:34:12 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Juan Quintela X-Patchwork-Id: 31648 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by bilbo.ozlabs.org (Postfix) with ESMTPS id 12793B70B3 for ; Thu, 20 Aug 2009 00:39:04 +1000 (EST) Received: from localhost ([127.0.0.1]:45622 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MdmJV-00079C-ME for incoming@patchwork.ozlabs.org; Wed, 19 Aug 2009 10:38:57 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1MdmHM-0006MA-M2 for qemu-devel@nongnu.org; Wed, 19 Aug 2009 10:36:44 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1MdmHJ-0006Jt-BW for qemu-devel@nongnu.org; Wed, 19 Aug 2009 10:36:44 -0400 Received: from [199.232.76.173] (port=46912 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MdmHI-0006Jq-Pj for qemu-devel@nongnu.org; Wed, 19 Aug 2009 10:36:40 -0400 Received: from [66.187.237.31] (port=37246 helo=mx2.redhat.com) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1MdmHH-0003Lh-SN for qemu-devel@nongnu.org; Wed, 19 Aug 2009 10:36:40 -0400 Received: from int-mx2.corp.redhat.com (int-mx2.corp.redhat.com [172.16.27.26]) by mx2.redhat.com (8.13.8/8.13.8) with ESMTP id n7JEaVYX020114 for ; Wed, 19 Aug 2009 10:36:31 -0400 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx2.corp.redhat.com (8.13.1/8.13.1) with ESMTP id n7JEaTPv004527; Wed, 19 Aug 2009 10:36:29 -0400 Received: from localhost.localdomain (vpn1-4-115.ams2.redhat.com [10.36.4.115]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id n7JEaPMu005041; Wed, 19 Aug 2009 10:36:27 -0400 From: Juan Quintela To: qemu-devel@nongnu.org Date: Wed, 19 Aug 2009 16:34:12 +0200 Message-Id: In-Reply-To: References: In-Reply-To: References: X-Scanned-By: MIMEDefang 2.58 on 172.16.27.26 X-detected-operating-system: by monty-python.gnu.org: GNU/Linux 2.6 (newer, 3) Subject: [Qemu-devel] [PATCH 1/3] New VMstate save/load infrastructure X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch introduces VMState infrastructure, to convert the save/load functions of devices to a table approach. This new approach has the advantages: - it is type-safe - you can't have load/save functions out of sync - will allows us to have new interesting commands, like dump , that shows all its internal state. - Just now, the only added type is arrays, but we can add structures. Add support for loading old state using old foo_load() functions Signed-off-by: Juan Quintela --- hw/hw.h | 153 +++++++++++++++++++++++++++++ savevm.c | 325 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 472 insertions(+), 6 deletions(-) diff --git a/hw/hw.h b/hw/hw.h index 1e5783d..5df7708 100644 --- a/hw/hw.h +++ b/hw/hw.h @@ -269,4 +269,157 @@ typedef int QEMUBootSetHandler(void *opaque, const char *boot_devices); void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque); int qemu_boot_set(const char *boot_devices); +typedef struct VMStateInfo VMStateInfo; +typedef struct VMStateDescription VMStateDescription; + +struct VMStateInfo { + const char *name; + const size_t size; + const VMStateDescription *vmsd; + void (*get)(QEMUFile *f, void *pv); + void (*put)(QEMUFile *f, const void *pv); +}; + +typedef struct { + const char *name; + size_t num; + size_t offset; + VMStateInfo *info; + int version_id; +} VMStateField; + +struct VMStateDescription { + const char *name; + int version_id; + int minimum_version_id; + int minimum_version_id_old; + LoadStateHandler *load_state_old; + VMStateField *fields; +}; + +extern VMStateInfo vmstate_info_int8; +extern VMStateInfo vmstate_info_int16; +extern VMStateInfo vmstate_info_int32; +extern VMStateInfo vmstate_info_int64; + +extern VMStateInfo vmstate_info_uint8; +extern VMStateInfo vmstate_info_uint16; +extern VMStateInfo vmstate_info_uint32; +extern VMStateInfo vmstate_info_uint64; + +extern VMStateInfo vmstate_info_timer; + +#define typeof_field2(type, field) typeof(((type *)0)->field) +#define type_check2(t1,t2) ((t1*)0 - (t2*)0) +#define type_check_array(t1,t2,n) ((t1(*)[n])0 - (t2*)0) + +#define VMSTATE_FIELD(_field, _state, _version, _info, _type) { \ + .name = (stringify(_field)), \ + .num = 1, \ + .version_id = (_version), \ + .info = &(_info), \ + .offset = offsetof(_state, _field) \ + + type_check2(_type,typeof_field2(_state, _field)) \ +} + +#define VMSTATE_FIELD_A(_field, _state, _num, _version, _info, _type) { \ + .name = (stringify(_field)), \ + .num = (_num), \ + .version_id = (_version), \ + .info = &(_info), \ + .offset = offsetof(_state, _field) \ + + type_check_array(_type,typeof_field2(_state, _field),_num) \ +} + +/* _f : field name + _s : struct state name + _n : num of elements + _v : version +*/ + +#define VMSTATE_INT8_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_FIELD_A(_f, _s, _n, _v, vmstate_info_int8, int8_t) +#define VMSTATE_INT16_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_FIELD_A(_f, _s, _n, _v, vmstate_info_int16, int16_t) +#define VMSTATE_INT32_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_FIELD_A(_f, _s, _n, _v, vmstate_info_int32, int32_t) +#define VMSTATE_INT64_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_FIELD_A(_f, _s, _n, _v, vmstate_info_int64, int64_t) + +#define VMSTATE_UINT8_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_FIELD_A(_f, _s, _n, _v, vmstate_info_uint8, uint8_t) +#define VMSTATE_UINT16_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_FIELD_A(_f, _s, _n, _v, vmstate_info_uint16, uint16_t) +#define VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_FIELD_A(_f, _s, _n, _v, vmstate_info_uint32, uint32_t) +#define VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_FIELD_A(_f, _s, _n, _v, vmstate_info_uint64, uint64_t) + +#define VMSTATE_INT8_ARRAY(_f, _s, _n) \ + VMSTATE_INT8_ARRAY_V(_f, _s, _n, 0) +#define VMSTATE_INT16_ARRAY(_f, _s, _n) \ + VMSTATE_INT16_ARRAY_V(_f, _s, _n, 0) +#define VMSTATE_INT32_ARRAY(_f, _s, _n) \ + VMSTATE_INT32_ARRAY_V(_f, _s, _n, 0) +#define VMSTATE_INT64_ARRAY(_f, _s, _n) \ + VMSTATE_INT64_ARRAY_V(_f, _s, _n, 0) + +#define VMSTATE_UINT8_ARRAY(_f, _s, _n) \ + VMSTATE_UINT8_ARRAY_V(_f, _s, _n, 0) +#define VMSTATE_UINT16_ARRAY(_f, _s, _n) \ + VMSTATE_UINT16_ARRAY_V(_f, _s, _n, 0) +#define VMSTATE_UINT32_ARRAY(_f, _s, _n) \ + VMSTATE_UINT32_ARRAY_V(_f, _s, _n, 0) +#define VMSTATE_UINT64_ARRAY(_f, _s, _n) \ + VMSTATE_UINT64_ARRAY_V(_f, _s, _n, 0) + +#define VMSTATE_INT8_V(_f, _s, _v) \ + VMSTATE_FIELD(_f, _s, _v, vmstate_info_int8, int8_t) +#define VMSTATE_INT16_V(_f, _s, _v) \ + VMSTATE_FIELD(_f, _s, _v, vmstate_info_int16, int16_t) +#define VMSTATE_INT32_V(_f, _s, _v) \ + VMSTATE_FIELD(_f, _s, _v, vmstate_info_int32, int32_t) +#define VMSTATE_INT64_V(_f, _s, _v) \ + VMSTATE_FIELD(_f, _s, _v, vmstate_info_int64, int64_t) + +#define VMSTATE_UINT8_V(_f, _s, _v) \ + VMSTATE_FIELD(_f, _s, _v, vmstate_info_uint8, uint8_t) +#define VMSTATE_UINT16_V(_f, _s, _v) \ + VMSTATE_FIELD(_f, _s, _v, vmstate_info_uint16, uint16_t) +#define VMSTATE_UINT32_V(_f, _s, _v) \ + VMSTATE_FIELD(_f, _s, _v, vmstate_info_uint32, uint32_t) +#define VMSTATE_UINT64_V(_f, _s, _v) \ + VMSTATE_FIELD(_f, _s, _v, vmstate_info_uint64, uint64_t) + +#define VMSTATE_INT8(_f, _s) \ + VMSTATE_INT8_V(_f, _s, 0) +#define VMSTATE_INT16(_f, _s) \ + VMSTATE_INT16_V(_f, _s, 0) +#define VMSTATE_INT32(_f, _s) \ + VMSTATE_INT32_V(_f, _s, 0) +#define VMSTATE_INT64(_f, _s) \ + VMSTATE_INT64_V(_f, _s, 0) + +#define VMSTATE_UINT8(_f, _s) \ + VMSTATE_UINT8_V(_f, _s, 0) +#define VMSTATE_UINT16(_f, _s) \ + VMSTATE_UINT16_V(_f, _s, 0) +#define VMSTATE_UINT32(_f, _s) \ + VMSTATE_UINT32_V(_f, _s, 0) +#define VMSTATE_UINT64(_f, _s) \ + VMSTATE_UINT64_V(_f, _s, 0) + + +#define VMSTATE_TIMER_V(_f, _s, _v) \ + VMSTATE_FIELD(_f, _s, _v, vmstate_info_timer, QEMUTimer *) + +#define VMSTATE_TIMER(_f, _s) \ + VMSTATE_TIMER_V(_f, _s, 0) + +#define VMSTATE_END_OF_LIST() \ + {} + +extern int vmstate_register(int instance_id, const VMStateDescription *vmsd, + void *base); +extern void vmstate_unregister(const char *idstr, void *opaque); #endif diff --git a/savevm.c b/savevm.c index 570377f..3501605 100644 --- a/savevm.c +++ b/savevm.c @@ -610,6 +610,195 @@ uint64_t qemu_get_be64(QEMUFile *f) return v; } +/* 8 bit int */ + +static void get_int8(QEMUFile *f, void *pv) +{ + int8_t *v = pv; + qemu_get_s8s(f, v); +} + +static void put_int8(QEMUFile *f, const void *pv) +{ + const int8_t *v = pv; + qemu_put_s8s(f, v); +} + +VMStateInfo vmstate_info_int8 = { + .name = "int8", + .size = sizeof(int8_t), + .get = get_int8, + .put = put_int8, +}; + +/* 16 bit int */ + +static void get_int16(QEMUFile *f, void *pv) +{ + int16_t *v = pv; + qemu_get_sbe16s(f, v); +} + +static void put_int16(QEMUFile *f, const void *pv) +{ + const int16_t *v = pv; + qemu_put_sbe16s(f, v); +} + +VMStateInfo vmstate_info_int16 = { + .name = "int16", + .size = sizeof(int16_t), + .get = get_int16, + .put = put_int16, +}; + +/* 32 bit int */ + +static void get_int32(QEMUFile *f, void *pv) +{ + int32_t *v = pv; + qemu_get_sbe32s(f, v); +} + +static void put_int32(QEMUFile *f, const void *pv) +{ + const int32_t *v = pv; + qemu_put_sbe32s(f, v); +} + +VMStateInfo vmstate_info_int32 = { + .name = "int32", + .size = sizeof(int32_t), + .get = get_int32, + .put = put_int32, +}; + +/* 64 bit int */ + +static void get_int64(QEMUFile *f, void *pv) +{ + int64_t *v = pv; + qemu_get_sbe64s(f, v); +} + +static void put_int64(QEMUFile *f, const void *pv) +{ + const int64_t *v = pv; + qemu_put_sbe64s(f, v); +} + +VMStateInfo vmstate_info_int64 = { + .name = "int64", + .size = sizeof(int64_t), + .get = get_int64, + .put = put_int64, +}; + +/* 8 bit unsigned int */ + +static void get_uint8(QEMUFile *f, void *pv) +{ + uint8_t *v = pv; + qemu_get_8s(f, v); +} + +static void put_uint8(QEMUFile *f, const void *pv) +{ + const uint8_t *v = pv; + qemu_put_8s(f, v); +} + +VMStateInfo vmstate_info_uint8 = { + .name = "uint8", + .size = sizeof(uint8_t), + .get = get_uint8, + .put = put_uint8, +}; + +/* 16 bit unsigned int */ + +static void get_uint16(QEMUFile *f, void *pv) +{ + uint16_t *v = pv; + qemu_get_be16s(f, v); +} + +static void put_uint16(QEMUFile *f, const void *pv) +{ + const uint16_t *v = pv; + qemu_put_be16s(f, v); +} + +VMStateInfo vmstate_info_uint16 = { + .name = "uint16", + .size = sizeof(uint16_t), + .get = get_uint16, + .put = put_uint16, +}; + +/* 32 bit unsigned int */ + +static void get_uint32(QEMUFile *f, void *pv) +{ + uint32_t *v = pv; + qemu_get_be32s(f, v); +} + +static void put_uint32(QEMUFile *f, const void *pv) +{ + const uint32_t *v = pv; + qemu_put_be32s(f, v); +} + +VMStateInfo vmstate_info_uint32 = { + .name = "uint32", + .size = sizeof(uint32_t), + .get = get_uint32, + .put = put_uint32, +}; + +/* 64 bit unsigned int */ + +static void get_uint64(QEMUFile *f, void *pv) +{ + uint64_t *v = pv; + qemu_get_be64s(f, v); +} + +static void put_uint64(QEMUFile *f, const void *pv) +{ + const uint64_t *v = pv; + qemu_put_be64s(f, v); +} + +VMStateInfo vmstate_info_uint64 = { + .name = "uint64", + .size = sizeof(uint64_t), + .get = get_uint64, + .put = put_uint64, +}; + +/* timers */ + +static void get_timer(QEMUFile *f, void *pv) +{ + QEMUTimer **v = pv; + qemu_get_timer(f, *v); +} + +static void put_timer(QEMUFile *f, const void *pv) +{ + QEMUTimer **v = (void *)pv; + qemu_put_timer(f, *v); +} + +VMStateInfo vmstate_info_timer = { + .name = "timer", + .size = sizeof(uint64_t), + .get = get_timer, + .put = put_timer, +}; + typedef struct SaveStateEntry { char idstr[256]; int instance_id; @@ -618,11 +807,13 @@ typedef struct SaveStateEntry { SaveLiveStateHandler *save_live_state; SaveStateHandler *save_state; LoadStateHandler *load_state; + const VMStateDescription *vmsd; void *opaque; struct SaveStateEntry *next; } SaveStateEntry; static SaveStateEntry *first_se; +static int global_section_id; /* TODO: Individual devices generally have very little idea about the rest of the system, so instance_id should be removed/replaced. @@ -637,7 +828,6 @@ int register_savevm_live(const char *idstr, void *opaque) { SaveStateEntry *se, **pse; - static int global_section_id; se = qemu_malloc(sizeof(SaveStateEntry)); pstrcpy(se->idstr, sizeof(se->idstr), idstr); @@ -648,6 +838,7 @@ int register_savevm_live(const char *idstr, se->save_state = save_state; se->load_state = load_state; se->opaque = opaque; + se->vmsd = NULL; se->next = NULL; /* add at the end of list */ @@ -690,6 +881,101 @@ void unregister_savevm(const char *idstr, void *opaque) } } +int vmstate_register(int instance_id, const VMStateDescription *vmsd, + void *opaque) +{ + SaveStateEntry *se, **pse; + + se = qemu_malloc(sizeof(SaveStateEntry)); + pstrcpy(se->idstr, sizeof(se->idstr), vmsd->name); + se->instance_id = (instance_id == -1) ? 0 : instance_id; + se->version_id = vmsd->version_id; + se->section_id = global_section_id++; + se->save_live_state = NULL; + se->save_state = NULL; + se->load_state = NULL; + se->opaque = opaque; + se->vmsd = vmsd; + se->next = NULL; + + /* add at the end of list */ + pse = &first_se; + while (*pse != NULL) { + if (instance_id == -1 + && strcmp(se->idstr, (*pse)->idstr) == 0 + && se->instance_id <= (*pse)->instance_id) + se->instance_id = (*pse)->instance_id + 1; + pse = &(*pse)->next; + } + *pse = se; + return 0; +} + +void vmstate_unregister(const char *idstr, void *opaque) +{ + SaveStateEntry **pse; + + pse = &first_se; + while (*pse != NULL) { + if (strcmp((*pse)->idstr, idstr) == 0 && (*pse)->opaque == opaque) { + SaveStateEntry *next = (*pse)->next; + qemu_free(*pse); + *pse = next; + continue; + } + pse = &(*pse)->next; + } +} + +static void vmstate_save(QEMUFile *f, const VMStateDescription *vmsd, const void *base) +{ + VMStateField *field = vmsd->fields; + int i; + + while(field->name) { + for (i = 0; i < field->num; i++) { + const void * addr = base + field->offset + field->info->size * i; + if (field->info->vmsd) { + vmstate_save(f, field->info->vmsd, addr); + } else { + field->info->put(f, addr); + } + } + field++; + } +} + +static int vmstate_load(QEMUFile *f, const VMStateDescription *vmsd, void *base, + int version_id) +{ + VMStateField *field = vmsd->fields; + int i; + + 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, base, version_id); + + while(field->name) { + for (i = 0; i < field->num; i++) { + if (field->version_id <= version_id) { + void * addr = base + field->offset + field->info->size * i; + if (field->info->vmsd) { + vmstate_load(f, field->info->vmsd, addr, version_id); + } else { + field->info->get(f, addr); + } + } + } + field++; + } + return 0; +} + #define QEMU_VM_FILE_MAGIC 0x5145564d #define QEMU_VM_FILE_VERSION_COMPAT 0x00000002 #define QEMU_VM_FILE_VERSION 0x00000003 @@ -777,7 +1063,7 @@ int qemu_savevm_state_complete(QEMUFile *f) for(se = first_se; se != NULL; se = se->next) { int len; - if (se->save_state == NULL) + if (se->save_state == NULL && se->vmsd == NULL) continue; /* Section type */ @@ -792,7 +1078,10 @@ int qemu_savevm_state_complete(QEMUFile *f) qemu_put_be32(f, se->instance_id); qemu_put_be32(f, se->version_id); - se->save_state(f, se->opaque); + if (se->vmsd != NULL) + vmstate_save(f, se->vmsd, se->opaque); + else + se->save_state(f, se->opaque); } qemu_put_byte(f, QEMU_VM_EOF); @@ -878,7 +1167,11 @@ static int qemu_loadvm_state_v2(QEMUFile *f) fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n", instance_id, idstr); } else { - ret = se->load_state(f, se->opaque, version_id); + if (se->vmsd) { + ret = vmstate_load(f, se->vmsd, se->opaque, version_id); + } else { + ret = se->load_state(f, se->opaque, version_id); + } if (ret < 0) { fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n", instance_id, idstr); @@ -955,7 +1248,17 @@ int qemu_loadvm_state(QEMUFile *f) le->next = first_le; first_le = le; - le->se->load_state(f, le->se->opaque, le->version_id); + if (le->se->vmsd) { + ret = vmstate_load(f, le->se->vmsd, le->se->opaque, le->version_id); + } else { + ret = le->se->load_state(f, le->se->opaque, le->version_id); + } + if (ret < 0) { + fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n", + instance_id, idstr); + ret = -EINVAL; + goto out; + } break; case QEMU_VM_SECTION_PART: case QEMU_VM_SECTION_END: @@ -968,7 +1271,17 @@ int qemu_loadvm_state(QEMUFile *f) goto out; } - le->se->load_state(f, le->se->opaque, le->version_id); + if (le->se->vmsd) { + ret = vmstate_load(f, le->se->vmsd, le->se->opaque, le->version_id); + } else { + ret = le->se->load_state(f, le->se->opaque, le->version_id); + } + if (ret != 0) { + fprintf(stderr, "qemu: warning: error while loading state section '%d'\n", + section_id); + ret = -EINVAL; + goto out; + } break; default: fprintf(stderr, "Unknown savevm section type %d\n", section_type);