VMStateDescription
在迁移的最后阶段,对每个SaveStateEntry的vmsd会跳用函数vmstate_save。在上一节中,我们跳过了这部分的讲解,在这节中我们补上。
VMStateDescription是SaveStateEntry的一部分
VMStateDescription并不是单独存在,而是SaveStateEntry的一部分。但是并非所有的SaveStateEntry结构都有vmsd。比如上一小节看到的“ram”,”block“这几个SaveStateEntry就没有。
正因为如此,vmsd的出现会伴随这SaveStateEntry。通畅我们见到创建两者的函数是vmstate_register_with_alias_id。比如在函数apic_common_realize中,每个apic设备就会有一个SaveStateEntry且它的vmsd就是vmstate_apic_common。
发送流程 vmstate_save
接下来我们了解一下上一小节中我们略过的函数。
vmstate_save(f, se, vmdesc)
if (!se->vmsd) {
vmstate_save_old_style(f, se, vmdesc);
vmstate_save_state(f, se->vmsd, se->opaque, vmdesc)
vmsd->pre_save(se->opaque)
; iterate on fields
; iterate on elements of this field
vmsd_desc_field_start
vmstate_save_state(f, field->vmsd, curr_elem, vmdesc_loop)
vmstate_save_state_v(f, field->vmsd, curr_elem, vmdesc_loop, field->struct_version_id)
field->info->put(f, curr_elem, size, field, vmdesc_loop)
vmsd_desc_field_end
; go into subsection
vmstate_subsection_save()
vmstate_save_state(f, vmsdsub, se->opaque, vmdesc)
vmsd->post_save(se->opaque)
当然这个调用图中我们跳过了没有vmsd的情况。先着重观察有vmsd时的操作。
其实上面的结构不是很清楚,让我再多说两句:
vmsd有多个field
field有多个element
field类型可以嵌套
接收流程 vmstate_load
这个函数是vmstate_save的另一半。
vmstate_load(f, se)
if (!se->vmsd)
se->ops->load_state()
vmstate_load_state(f, se->vmsd, se->opaque, se->load_version_id)
vmsd->pre_load(se->opaque)
; iterate on fields
; iterate on elements of this field
vmstate_load_state(f, field->vmsd, curr_elem, field->vmsd->version_id)
vmstate_load_state(f, field->vmsd, curr_elem, field->struct_version_id)
field->info->get(f, curr_elem, size, field)
; go into subsection
vmstate_subsection_save()
vmstate_save_state(f, vmsdsub, se->opaque, vmdesc)
vmsd->pre_save(se->opaque)
可谓是珠联璧合。
结构体
是时候展示一下结构体的庐山真面目了。
+--------------------------------------+
|vmsd |
| (VMStateDescription *) |
| +--------------------------------+
| |name |
| | (char *) |
| +--------------------------------+
| |version_id |
| |minimum_version_id |
| |minimum_version_id_old |
| | (int) |
| +--------------------------------+
| |unmigratable |
| | (int) |
| +--------------------------------+
| |priority |
| | (MigrationPriority) |
| +--------------------------------+
| |load_state_old |
| |pre_load |
| |post_load |
| |pre_save |
| |post_save |
| |needed |
| | bool (*)() |
| +--------------------------------+
| |fields |
| | (VMStateField *) |
| | +---------------------------+
| | |flags | POINTER, ARRARY, V/STRUCT, VARRAY_INT32, BUFFER, ARRAY_OF_POINTER
| | | (VMStateFlags) | VARRAY_UINT8/16/32, VBUFFER, MULTIPLY/_ELEMENTS, MUST_EXIST, ALLOC
| | | |
| | |name |
| | |offset |
| | |start |
| | | |
| | |size |
| | |size_offset |
| | | |
| | |num |
| | |num_offset |
| | | |
| | |version_id |
| | |struct_version_id |
| | |field_exists |
| | | |
| | |vmsd |
| | | (VMStateDescription*) |
| | | |
| | |info |
| | | (VMStateInfo *) |
| | | +----------------------+
| | | |name |
| | | |get |
| | | |put |
| +----+----+----------------------+
| |subsections |
| | (VMStateDescription **) |
+-----+--------------------------------+
是不是很长,感觉一头雾水? 我也觉得是,那就再来一张凸显其中某些关联的图。
SaveStateEntry
+--------------------------------------+
|opaque |
| (void *) ----|--->+-+----+----+----+----+----+----+----+----+----+------------------+
| | | |elem|elem|elem|elem|elem|elem|elem|elem|elem| |
| | +-+----+----+----+----+----+----+----+----+----+------------------+
| | ^ ^
| | | |
| | | |
| | | field | field
+--------------------------------------+ +----------------+ +----------------+
|vmsd | | |
| (VMStateDescription *) ----|--->+--------------------------------+ +--------------------------------+
+--------------------------------------+ |fields | | |fields | |
|subsections | |(VMStateField *) | | |(VMStateField *) | |
| (VMStateDescription **) | | +---------------------------+ | +---------------------------+
+--------------------------------------+ | |name | | | |name | |
| |offset ----+ | | |offset ----+ |
| |start | | |start |
| | | | | |
| |size | => size | |size | => size
| |size_offset | | |size_offset |
| | | | | |
| |num | => n_elems | |num | => n_elems
| |num_offset | | |num_offset |
| | | | | |
| |version_id | | |version_id |
| |struct_version_id | | |struct_version_id |
| |field_exists | | |field_exists |
| | | | | |
| |vmsd | | |vmsd |
| | (VMStateDescription*) | | | (VMStateDescription*) |
| | | | | |
| |info | | |info |
| | (VMStateInfo *) | | | (VMStateInfo *) |
| | +----------------------+ | | +----------------------+
| | |name | | | |name |
| | |get | | | |get |
| | |put | | | |put |
+----+----+----------------------+ +----+----+----------------------+
这样希望能够突出几点:
vmsd包含了fields的数组,每个元素都是VMStateField结构
每个field又包含了多个elements,其个数由num/num_offset决定,其大小由size/size_offset决定
elements的起始位置由offset决定,在opaque指针指向的空间
如何定义
对结构体有了大致了概念后,我们就可以来看看代码中我们是如何定义的。下面这个是apic设备的vmsd。
static const VMStateDescription vmstate_apic_common = {
.name = "apic",
.version_id = 3,
.minimum_version_id = 3,
.minimum_version_id_old = 1,
.load_state_old = apic_load_old,
.pre_load = apic_pre_load,
.pre_save = apic_dispatch_pre_save,
.post_load = apic_dispatch_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32(apicbase, APICCommonState),
VMSTATE_UINT8(id, APICCommonState),
VMSTATE_UINT8(arb_id, APICCommonState),
VMSTATE_UINT8(tpr, APICCommonState),
VMSTATE_UINT32(spurious_vec, APICCommonState),
VMSTATE_UINT8(log_dest, APICCommonState),
VMSTATE_UINT8(dest_mode, APICCommonState),
VMSTATE_UINT32_ARRAY(isr, APICCommonState, 8),
VMSTATE_UINT32_ARRAY(tmr, APICCommonState, 8),
VMSTATE_UINT32_ARRAY(irr, APICCommonState, 8),
VMSTATE_UINT32_ARRAY(lvt, APICCommonState, APIC_LVT_NB),
VMSTATE_UINT32(esr, APICCommonState),
VMSTATE_UINT32_ARRAY(icr, APICCommonState, 2),
VMSTATE_UINT32(divide_conf, APICCommonState),
VMSTATE_INT32(count_shift, APICCommonState),
VMSTATE_UINT32(initial_count, APICCommonState),
VMSTATE_INT64(initial_count_load_time, APICCommonState),
VMSTATE_INT64(next_time, APICCommonState),
VMSTATE_INT64(timer_expiry,
APICCommonState), /* open-coded timer state */
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription*[]) {
&vmstate_apic_common_sipi,
NULL
}
};
可以看到,它的fields上有多个部分,而且还包含了一个subsection。其他的成员都一眼看穿,唯独fields成员的定义隐藏在VMSTATE_宏定义里。那就让我们打开这个宏定义看看吧。
VMSTATE_UINT8
VMSTATE_UINT8(id, APICCommonState)
#define VMSTATE_UINT8(_f, _s) \
VMSTATE_UINT8_V(_f, _s, 0)
VMSTATE_UINT8_V(id, APICCommonState, 0)
#define VMSTATE_UINT8_V(_f, _s, _v) \
VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint8, uint8_t)
VMSTATE_SINGLE(id, APICCommonState, 0, vmstate_info_uint8, uint8_t)
#define VMSTATE_SINGLE(_field, _state, _version, _info, _type) \
VMSTATE_SINGLE_TEST(_field, _state, NULL, _version, _info, _type)
VMSTATE_SINGLE_TEST(id, APICCommonState, NULL, 0, vmstate_info_uint8, uint8_t)
#define VMSTATE_SINGLE_TEST(_field, _state, _test, _version, _info, _type) { \
.name = (stringify(_field)), \
.version_id = (_version), \
.field_exists = (_test), \
.size = sizeof(_type), \
.info = &(_info), \
.flags = VMS_SINGLE, \
.offset = vmstate_offset_value(_state, _field, _type), \
}
{
.name = "id",
.version_id = 0
.field_exists = NULL,
.size = 1,
.info = &vmstate_info_uint8,
.flags = VMS_SINGLE,
.offset = offsetof(APICCommonState, id),
}
VMSTATE_UINT32_ARRAY
VMSTATE_UINT32_ARRAY(tmr, APICCommonState, 8)
#define VMSTATE_UINT32_ARRAY(_f, _s, _n) \
VMSTATE_UINT32_ARRAY_V(_f, _s, _n, 0)
VMSTATE_UINT32_ARRAY_V(tmr, APICCommonState, 8, 0)
#define VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v) \
VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint32, uint32_t)
VMSTATE_ARRAY(tmr, APICCommonState, 8, 0, vmstate_info_uint32, uint32_t)
#define VMSTATE_ARRAY(_field, _state, _num, _version, _info, _type) {\
.name = (stringify(_field)), \
.version_id = (_version), \
.num = (_num), \
.info = &(_info), \
.size = sizeof(_type), \
.flags = VMS_ARRAY, \
.offset = vmstate_offset_array(_state, _field, _type, _num), \
}
{
.name = "tmr",
.version_id = 0,
.num = 8,
.info = &vmstate_info_uint32,
.size = 4,
.flags = VMS_ARRAY,
.offset = offsetof(APICCommonState, tmr),
}
Last updated