QEMU VCPU热插特性
最近学习QEMU中VCPU热插特性,需要了解QEMU中VCPU热插的整个流程,VCPU热插是QEMU主板的一个feature。
1:这里先分析一下QEMU的主板模拟,主板在QEMU的设备模型中对应的是一个MachineClass的结构体,其内容如下:
struct MachineClass { /*< private >*/ ObjectClass parent_class; /*< public >*/ const char *family; /* NULL iff @name identifies a standalone machtype */ const char *name; const char *alias; const char *desc; void (*init)(MachineState *state); /* 主板的初始化入口函数 */ void (*reset)(void); void (*hot_add_cpu)(const int64_t id, Error **errp); /* VCPU 热插的调用入口 */ int (*kvm_type)(const char *arg); BlockInterfaceType block_default_type; int units_per_default_bus; int max_cpus; unsigned int no_serial:1, no_parallel:1, use_virtcon:1, use_sclp:1, no_floppy:1, no_cdrom:1, no_sdcard:1, has_dynamic_sysbus:1, pci_allow_0_address:1, legacy_fw_cfg_order:1; int is_default; const char *default_machine_opts; const char *default_boot_order; const char *default_display; GlobalProperty *compat_props; const char *hw_version; ram_addr_t default_ram_size; bool option_rom_has_mr; bool rom_file_has_mr; HotplugHandler *(*get_hotplug_handler)(MachineState *machine, DeviceState *dev); unsigned (*cpu_index_to_socket_id)(unsigned cpu_index); CPUArchIdList *(*possible_cpu_arch_ids)(MachineState *machine); };
在qemu源码vl.c中,做完参数解析之后调用select_machine()获取默认的主板。
static MachineClass *select_machine(void) { MachineClass *machine_class = find_default_machine(); /* 寻找默认的主板(qemu有一个支持的主板列表) */ const char *optarg; QemuOpts *opts; Location loc; loc_push_none(&loc); opts = qemu_get_machine_opts(); qemu_opts_loc_restore(opts); optarg = qemu_opt_get(opts, "type"); if (optarg) { machine_class = machine_parse(optarg); /* 解析命令行传入的machine类型,如果命令行传入的machine解析错误,使用默认的主板类型 */ } if (!machine_class) { error_report("No machine specified, and there is no default"); error_printf("Use -machine help to list supported machines\n"); exit(1); } loc_pop(&loc); return machine_class; }
不管是find_default_machine还是machine_parse中,都有一个函数叫做 object_class_get_list(),这个函数的作用查找实现了某个Class的object列表,下面对其进行分析。
MachineClass *find_default_machine(void) { GSList *el, *machines = object_class_get_list(TYPE_MACHINE, false); /* 查找 machine 对象的列表 */ MachineClass *mc = NULL; for (el = machines; el; el = el->next) { /* 遍历列表,找到is_default=true的object对象 */ MachineClass *temp = el->data; if (temp->is_default) { mc = temp; break; } } g_slist_free(machines); return mc; }
重点分析object_class_get_list,其入参是指向字符串"machine"的指针 和 false (不查找抽象类的实例)。
GSList *object_class_get_list(const char *implements_type, bool include_abstract) { GSList *list = NULL; object_class_foreach(object_class_get_list_tramp, implements_type, include_abstract, &list); /* 调用了object_class_foreach方法, 第一个入参是一个函数指针object_class_get_list_tramp,最后一个参数是一个GList指针(默认为NULL)*/
return list;
}
函数object_class_get_list_tramp的作用是将ObjectClass对象指针klass添加到list当中,并将list的头指针放到opaque里面。用来实现将找到的ObjectClass对象指针串起来。
static void object_class_get_list_tramp(ObjectClass *klass, void *opaque) { GSList **list = opaque; *list = g_slist_prepend(*list, klass); /* add a new element to the start of the list */ }
下面是object_class_foreach的实现。
typedef struct OCFData { void (*fn)(ObjectClass *klass, void *opaque); const char *implements_type; bool include_abstract; void *opaque; } OCFData; static void object_class_foreach_tramp(gpointer key, gpointer value, gpointer opaque) { OCFData *data = opaque; TypeImpl *type = value; ObjectClass *k; type_initialize(type); k = type->class; if (!data->include_abstract && type->abstract) { /* 抽象类不是查对象 */ return; } if (data->implements_type && /* 不能动态cast的类对象也不考虑 */ !object_class_dynamic_cast(k, data->implements_type)) { return; } data->fn(k, data->opaque); /* 调用object_class_get_list_tramp */ } void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque), const char *implements_type, bool include_abstract, void *opaque) { OCFData data = { fn, implements_type, include_abstract, opaque }; enumerating_types = true; g_hash_table_foreach(type_table_get(), object_class_foreach_tramp, &data); enumerating_types = false; }