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