commit 3cda44f7bae5c9feddc11630ba6eecb2e3bed425
Author: Jens Freimann
Date: Mon Mar 2 17:44:24 2015 +0100
s390x/kvm: migrate vcpu interrupt state
This patch adds support to migrate vcpu interrupts.
We use ioctl KVM_S390_GET_IRQ_STATE and _SET_IRQ_STATE
to get/set the complete interrupt state for a vcpu.
Reviewed-by: David Hildenbrand
Signed-off-by: Jens Freimann
Signed-off-by: Cornelia Huck
diff --git a/target-s390x/cpu-qom.h b/target-s390x/cpu-qom.h
index 8b376df..936ae21 100644
--- a/target-s390x/cpu-qom.h
+++ b/target-s390x/cpu-qom.h
@@ -66,6 +66,9 @@ typedef struct S390CPU {
/*< public >*/
CPUS390XState env;
+ /* needed for live migration */
+ void *irqstate;
+ uint32_t irqstate_saved_size;
} S390CPU;
const VMStateDescription vmstate_fpu = {
.name = "cpu/fpu",
@@ -67,7 +76,8 @@ static inline bool fpu_needed(void *opaque)
const VMStateDescription vmstate_s390_cpu = {
.name = "cpu",
.post_load = cpu_post_load,
- .version_id = 3,
+ .pre_save = cpu_pre_save,
+ .version_id = 4,
.minimum_version_id = 3,
.fields = (VMStateField[]) {
VMSTATE_UINT64_ARRAY(env.regs, S390CPU, 16),
@@ -86,6 +96,9 @@ const VMStateDescription vmstate_s390_cpu = {
VMSTATE_UINT64_ARRAY(env.cregs, S390CPU, 16),
VMSTATE_UINT8(env.cpu_state, S390CPU),
VMSTATE_UINT8(env.sigp_order, S390CPU),
+ VMSTATE_UINT32_V(irqstate_saved_size, S390CPU, 4),
+ VMSTATE_VBUFFER_UINT32(irqstate, S390CPU, 4, NULL, 0,
+ irqstate_saved_size),
VMSTATE_END_OF_LIST()
},
上面的commit在S390CPU设备状态信息中增加了两个字段,irqstate和irqstate_saved_size,两个字段都需要迁移。S390CPU是个设备,它的基类是DeviceState。增加这两个字段后,不仅设备S390CPU的版本ID增大到4,两个字段的版本也声明为4,说明这是在设备版本号为4时引入的字段。
commit c3a86b35f2bae29278b2ebb3018c51ba69697db7
Author: Wei Huang
Date: Thu Feb 18 14:16:17 2016 +0000
ARM: PL061: Cleaning field of PL061 device state
This patch removes the float_high field of PL061State, which doesn't
seem to be used anywhere. Because this changes the device state, the
version ID is also bumped up for the reason of compatiblity.
Signed-off-by: Wei Huang
Reviewed-by: Peter Maydell
Message-id: [email protected]
Signed-off-by: Peter Maydell
diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c
index f9773b8..5ece8b0 100644
--- a/hw/gpio/pl061.c
+++ b/hw/gpio/pl061.c
@@ -56,7 +56,6 @@ typedef struct PL061State {
uint32_t slr;
uint32_t den;
uint32_t cr;
- uint32_t float_high;
uint32_t amsel;
qemu_irq irq;
qemu_irq out[8];
@@ -65,8 +64,8 @@ typedef struct PL061State {
static const VMStateDescription vmstate_pl061 = {
.name = "pl061",
- .version_id = 3,
- .minimum_version_id = 3,
+ .version_id = 4,
+ .minimum_version_id = 4,
.fields = (VMStateField[]) {
VMSTATE_UINT32(locked, PL061State),
VMSTATE_UINT32(data, PL061State),
@@ -88,7 +87,6 @@ static const VMStateDescription vmstate_pl061 = {
VMSTATE_UINT32(slr, PL061State),
VMSTATE_UINT32(den, PL061State),
VMSTATE_UINT32(cr, PL061State),
- VMSTATE_UINT32(float_high, PL061State),
VMSTATE_UINT32_V(amsel, PL061State, 2),
VMSTATE_END_OF_LIST()
}
这个commit想删除PL061State设备的float_high字段,它做了两个工作,一是增加设备状态的版本号为4,二是设置目的端要求的源端设备状态最小版本号为4
commit 59811a320d6b2a6db2646f908bb016dd8553df27
Author: Peter Maydell
Date: Mon Oct 24 16:26:50 2016 +0100
migration/savevm.c: migrate non-default page size
Add a subsection to vmstate_configuration which is present
only if the guest is using a target page size which is
different from the default. This allows us to helpfully
diagnose attempts to migrate between machines which
are using different target page sizes.
diff --git a/migration/savevm.c b/migration/savevm.c
index a831ec2..cfcbbd0 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -265,6 +265,7 @@ typedef struct SaveState {
bool skip_configuration;
uint32_t len;
const char *name;
+ uint32_t target_page_bits;
} SaveState;
static const VMStateDescription vmstate_configuration = {
.name = "configuration",
.version_id = 1,
+ .pre_load = configuration_pre_load,
.post_load = configuration_post_load,
.pre_save = configuration_pre_save,
.fields = (VMStateField[]) {
@@ -311,6 +356,10 @@ static const VMStateDescription vmstate_configuration = {
VMSTATE_VBUFFER_ALLOC_UINT32(name, SaveState, 0, NULL, 0, len),
VMSTATE_END_OF_LIST()
},
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_target_page_bits,
+ NULL
+ }
};
该commit的修改引入了target_page_bits结构体,当target_page_bits大小被虚机修改,和默认值不一样时,需要发送该结构体到目的端。
VMStateDescription
要提供的内容,VMStateDescription
包含了一个设备要迁移所需的全部信息struct VMStateDescription {
const char *name; /* 设备状态名 */
int unmigratable;
/* 版本ID,用于解决设备状态增删字段后的传输问题
* 只有当源端的VMState版本ID小于等于目的端时
* 设备状态信息才能传输成功
*/
int version_id;
/* 目的端允许源端发送的VMState的最低版本
* 如果源端发送的版本小于minimum_version_id
* 目的端报错
* */
int minimum_version_id;
int minimum_version_id_old;
MigrationPriority priority;
LoadStateHandler *load_state_old;
int (*pre_load)(void *opaque); /* 接收VMState前的回调函数,非必须字段 */
int (*post_load)(void *opaque, int version_id); /* 接收VMState后的回调函数,非必须字段 */
int (*pre_save)(void *opaque); /* 发送VMState前的回调函数,非必须字段 */
int (*post_save)(void *opaque); /* 发送VMState后的回调函数,非必须字段 */
bool (*needed)(void *opaque); /* 当VMState为subsection时,用于判断哪些字段需要被发送,非必须字段 */
const VMStateField *fields; /* 必须发送的VMState的字段*/
const VMStateDescription **subsections; /* 可选的发送字段,发送subsections前需要先执行needed函数判断 */
};
struct VMStateField {
const char *name; /* 字段名字 */
const char *err_hint;
size_t offset; /* 字段在VMState结构体中的偏移 */
size_t size; /* 字段长度 */
size_t start;
int num;
size_t num_offset;
size_t size_offset;
const VMStateInfo *info; /* 收发该字段使用的函数 */
enum VMStateFlags flags; /* 描述设备状态字段的类型,包括指针,数组,结构体,缓存等等*/
const VMStateDescription *vmsd; /* 指向包含的子设备状态,非必须*/
int version_id; /* 当该字段的版本大于源端设备状态的版本时,不会被传输 */
int struct_version_id;
bool (*field_exists)(void *opaque, int version_id);
};
static const VMStateDescription vmstate_pl061 = {
.name = "pl061",
.version_id = 4,
.minimum_version_id = 4,
.fields = (VMStateField[]) {
VMSTATE_UINT32(locked, PL061State),
VMSTATE_UINT32(data, PL061State),
......
VMSTATE_UINT32_V(amsel, PL061State, 2),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_configuration = {
.name = "configuration",
.version_id = 1,
.pre_load = configuration_pre_load, /* 目的端接收前的回调*/
.post_load = configuration_post_load, /* 目的端接收后的回调*/
.pre_save = configuration_pre_save, /* 发送端发送前的回调 */
.fields = (VMStateField[]) {
VMSTATE_UINT32(len, SaveState),
VMSTATE_VBUFFER_ALLOC_UINT32(name, SaveState, 0, NULL, len),
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription*[]) {
&vmstate_target_page_bits,
&vmstate_capabilites,
NULL
}
};
qemu_savevm_state_complete_precopy
vmstate_save(f, se, vmdesc)
vmstate_save_state(f, se->vmsd, se->opaque, vmdesc)
vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id)
int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
void *opaque, QJSON *vmdesc, int version_id)
{
int ret = 0;
const VMStateField *field = vmsd->fields;
trace_vmstate_save_state_top(vmsd->name);
if (vmsd->pre_save) {
ret = vmsd->pre_save(opaque);
trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
if (ret) {
error_report("pre-save failed: %s", vmsd->name);
return ret;
}
}
if (vmdesc) {
json_prop_str(vmdesc, "vmsd_name", vmsd->name);
json_prop_int(vmdesc, "version", version_id);
json_start_array(vmdesc, "fields");
}
while (field->name) {
if ((field->field_exists &&
field->field_exists(opaque, version_id)) ||
(!field->field_exists &&
field->version_id <= version_id)) {
void *first_elem = opaque + field->offset;
int i, n_elems = vmstate_n_elems(opaque, field);
int size = vmstate_size(opaque, field);
int64_t old_offset, written_bytes;
QJSON *vmdesc_loop = vmdesc;
trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems);
if (field->flags & VMS_POINTER) {
first_elem = *(void **)first_elem;
assert(first_elem || !n_elems || !size);
}
vmsd->fields
,参数opaque
是设备状态的结构体,如果有prev_save,首先执行prev_save,搜集设备状态信息。参数vmdesc
用于记录发送的设备字段名字。进入while循环,它的结束条件是field->name不为NULL,field数组都以VMSTATE_END_OF_LIST
为结束标志,当field->name为NULL时,一定是遍历到了VMSTATE_END_OF_LIST
。接下来的判断决定这设备的字段是否可以被发送,只有当字段版本不大于设备状态版本时(field->version_id <= version_id),该字段才能发送。first_elem = opaque + field->offset
从设备状态信息中取对应的字段,逐个发送。 for (i = 0; i < n_elems; i++) {
void *curr_elem = first_elem + size * i;
ret = 0;
vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems);
old_offset = qemu_ftell_fast(f);
if (field->flags & VMS_ARRAY_OF_POINTER) {
assert(curr_elem);
curr_elem = *(void **)curr_elem;
}
if (!curr_elem && size) {
/* if null pointer write placeholder and do not follow */
assert(field->flags & VMS_ARRAY_OF_POINTER);
ret = vmstate_info_nullptr.put(f, curr_elem, size, NULL,
NULL);
} else if (field->flags & VMS_STRUCT) {
ret = vmstate_save_state(f, field->vmsd, curr_elem,
vmdesc_loop);
} else if (field->flags & VMS_VSTRUCT) {
ret = vmstate_save_state_v(f, field->vmsd, curr_elem,
vmdesc_loop,
field->struct_version_id);
} else {
ret = field->info->put(f, curr_elem, size, field,
vmdesc_loop);
}
if (ret) {
error_report("Save of field %s/%s failed",
vmsd->name, field->name);
if (vmsd->post_save) {
vmsd->post_save(opaque);
}
return ret;
}
written_bytes = qemu_ftell_fast(f) - old_offset;
vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i);
/* Compressed arrays only care about the first element */
if (vmdesc_loop && vmsd_can_compress(field)) {
vmdesc_loop = NULL;
}
}
if (vmdesc) {
json_end_array(vmdesc);
}
ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc);
if (vmsd->post_save) {
int ps_ret = vmsd->post_save(opaque);
if (!ret) {
ret = ps_ret;
}
}
return ret;
static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
void *opaque, QJSON *vmdesc)
{
const VMStateDescription **sub = vmsd->subsections;
bool subsection_found = false;
int ret = 0;
trace_vmstate_subsection_save_top(vmsd->name);
while (sub && *sub) {
if (vmstate_save_needed(*sub, opaque)) {
const VMStateDescription *vmsdsub = *sub;
uint8_t len;
trace_vmstate_subsection_save_loop(vmsd->name, vmsdsub->name);
if (vmdesc) {
/* Only create subsection array when we have any */
if (!subsection_found) {
json_start_array(vmdesc, "subsections");
subsection_found = true;
}
json_start_object(vmdesc, NULL);
}
qemu_put_byte(f, QEMU_VM_SUBSECTION);
len = strlen(vmsdsub->name);
qemu_put_byte(f, len);
qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len);
qemu_put_be32(f, vmsdsub->version_id);
ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc);
if (ret) {
return ret;
}
if (vmdesc) {
json_end_object(vmdesc);
}
}
sub++;
}
if (vmdesc && subsection_found) {
json_end_array(vmdesc);
}
return ret;
}
vmstate_save_needed
判断该subsection是否需要发送,如果需要才继续,否则跳过。然后是subsection元数据的发送,包括版本,名字长度,名字等,最终vmstate_save_state
才是subsection包含的VMState的发送qemu_loadvm_section_part_end
vmstate_load(f, se)
vmstate_load_state(f, se->vmsd, se->opaque, se->load_version_id);
int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
void *opaque, int version_id)
{
const VMStateField *field = vmsd->fields;
int ret = 0;
trace_vmstate_load_state(vmsd->name, version_id);
if (version_id > vmsd->version_id) {
error_report("%s: incoming version_id %d is too new "
"for local version_id %d",
vmsd->name, version_id, vmsd->version_id);
trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL);
return -EINVAL;
}
if (version_id < vmsd->minimum_version_id) {
if (vmsd->load_state_old &&
version_id >= vmsd->minimum_version_id_old) {
ret = vmsd->load_state_old(f, opaque, version_id);
trace_vmstate_load_state_end(vmsd->name, "old path", ret);
return ret;
}
error_report("%s: incoming version_id %d is too old "
"for local minimum version_id %d",
vmsd->name, version_id, vmsd->minimum_version_id);
trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
return -EINVAL;
}
if (vmsd->pre_load) {
int ret = vmsd->pre_load(opaque);
if (ret) {
return ret;
}
}
while (field->name) {
trace_vmstate_load_state_field(vmsd->name, field->name);
if ((field->field_exists &&
field->field_exists(opaque, version_id)) ||
(!field->field_exists &&
field->version_id <= version_id)) {
field->version_id <= version_id
条件的字段,这个判断实际就是对本地设备字段中,版本号大于对端设备版本的,跳过接收,之后的流程,和发送端类似。