Qemu内存热插拔
条评论内存热插流程
1. qemu内存参数
qemu中内存参数列表(如下)中增加两个参数解析slots和maxmem,这两个分别代码了内存的槽和最大内存,而size就变成了当前内存。maxmem是内存的上限,而slots则代码可用于热插内存时需要的槽的上限。
1 | static QemuOptsList qemu_mem_opts = { |
之后在main函数中对memory命令行参数解析的时候,解析slot和maxmem参数,并设置到current_machine中
1 | int main(int argc, char **argv, char **envp) |
2. 热插内存接口
- qemu热插内存时需要两步
- “object_add”: creates a memory backend object
- “device_add”: creates a front-end pc-dimm device and inserts it into the first empty slot
- 举例
- (qemu) object_add memory-backend-ram,id=mem1,size=1G
- (qemu) device_add pc-dimm,id=dimm1,memdev=mem1
- object_add接口分析
object_add命令用于创建新的object,示例中创建的object是memory-backend-ram(参考hostmem-ram.c),在monitor_init_qmp_commands中注册了object_add命令对应的执行函数qmp_object_add(如下),qmp_object_add接口会先获取qom-type,在上面示例中为memory-bachend-ram,之后获取id(mem1),然后获取属性(size=1G)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp)
{
QObject *props;
QDict *pdict;
Visitor *v;
Object *obj;
g_autofree char *type = NULL;
g_autofree char *id = NULL;
/*qom-type = memory-bachend-ram*/
type = g_strdup(qdict_get_try_str(qdict, "qom-type"));
if (!type) {
error_setg(errp, QERR_MISSING_PARAMETER, "qom-type");
return;
}
qdict_del(qdict, "qom-type");
id = g_strdup(qdict_get_try_str(qdict, "id"));
if (!id) {
error_setg(errp, QERR_MISSING_PARAMETER, "id");
return;
}
qdict_del(qdict, "id");
props = qdict_get(qdict, "props");
if (props) {
pdict = qobject_to(QDict, props);
if (!pdict) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict");
return;
}
qobject_ref(pdict);
qdict_del(qdict, "props");
qdict_join(qdict, pdict, false);
if (qdict_size(pdict) != 0) {
error_setg(errp, "Option in 'props' conflicts with top level");
qobject_unref(pdict);
return;
}
qobject_unref(pdict);
}
v = qobject_input_visitor_new(QOBJECT(qdict));
obj = user_creatable_add_type(type, id, qdict, v, errp);
visit_free(v);
if (obj) {
object_unref(obj);
}
*ret_data = QOBJECT(qdict_new());
}
qobject_input_visitor_new用于封装属性参数,以便object对象设置属性的时候可以转换属性值的类型,user_creatable_add_type是真正创建对象的地方:
1 | Object *user_creatable_add_type(const char *type, const char *id, |
memory-bachend-ram的父object为memory-bachend,所以memory-bachend-ram继承了父object的UserCreatableClass,所以当上面user_creatable_complete中complete最终调用的是memory-bachend的class_init中注册的complete回调函数host_memory_backend_memory_complete,此函数负载内存申请以及为申请的内存创建numa策略:
1 | static void |
- device_add接口分析
device_add命令是热插设备的接口,内存热插时指定设备类型为pc-dimm即为热插一个内存条,热插设备调用的接口是qmp_device_add:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp)
{
Error *local_err = NULL;
QemuOpts *opts;
DeviceState *dev;
opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
if (!monitor_cur_is_qmp() && qdev_device_help(opts)) {
qemu_opts_del(opts);
return;
}
/*调用qdev_device_add热插设备*/
dev = qdev_device_add(opts, &local_err);
if (!dev) {
error_propagate(errp, local_err);
qemu_opts_del(opts);
return;
}
object_unref(OBJECT(dev));
}
qdev_device_add是执行创建设备的函数:
1 | DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) |
device_set_realized是真正进行内存热插拔的函数:
1 | if (value && !dev->realized) { |