接前一篇文章:
上一回以 virtio balloon设备为例开始对于virtio设备的初始化过程进行分析,讲解了“virtio_balloon_pci_instance_init() -> virtio_instance_init_common() -> object_initialize_child_with_props() -> object_initialize_child_with_propsv() -> object_initialize()”的过程。并指出从这一过程中可以看出来,vitrtio设备在实例创建过程中并没有做很多事情,大部分的工作都是在设备的具现化过程中做的。本回就来深入解析这一过程。
在讲解virtio balloon设备的具现化之前,先来回顾一下设备具现化所调用的函数。QEMU在main函数中会对所有的“-device”参数进行具现化(前文书讲过,实际是在main函数中的qemu_init函数中进行的),设备的具现化函数都会调用device_set_realized函数,在该函数中会调用设备类的realize函数。相关代码如下:
- -device选项
与-device选项相对应的代码在softmmu/qdev-monitor.c中,如下所示:
- QemuOptsList qemu_device_opts = {
- .name = "device",
- .implied_opt_name = "driver",
- .head = QTAILQ_HEAD_INITIALIZER(qemu_device_opts.head),
- .desc = {
- /*
- * no elements => accept any
- * sanity checking will happen later
- * when setting device properties
- */
- { /* end of list */ }
- },
- };
- device_set_realized函数
device_set_realized函数在hw/core/qdev.c中,代码如下:
- static void device_set_realized(Object *obj, bool value, Error **errp)
- {
- DeviceState *dev = DEVICE(obj);
- DeviceClass *dc = DEVICE_GET_CLASS(dev);
- HotplugHandler *hotplug_ctrl;
- BusState *bus;
- NamedClockList *ncl;
- Error *local_err = NULL;
- bool unattached_parent = false;
- static int unattached_count;
-
- if (dev->hotplugged && !dc->hotpluggable) {
- error_setg(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj));
- return;
- }
-
- if (value && !dev->realized) {
- if (!check_only_migratable(obj, errp)) {
- goto fail;
- }
-
- if (!obj->parent) {
- gchar *name = g_strdup_printf("device[%d]", unattached_count++);
-
- object_property_add_child(container_get(qdev_get_machine(),
- "/unattached"),
- name, obj);
- unattached_parent = true;
- g_free(name);
- }
-
- hotplug_ctrl = qdev_get_hotplug_handler(dev);
- if (hotplug_ctrl) {
- hotplug_handler_pre_plug(hotplug_ctrl, dev, &local_err);
- if (local_err != NULL) {
- goto fail;
- }
- }
-
- if (dc->realize) {
- dc->realize(dev, &local_err);
- if (local_err != NULL) {
- goto fail;
- }
- }
-
- DEVICE_LISTENER_CALL(realize, Forward, dev);
-
- /*
- * always free/re-initialize here since the value cannot be cleaned up
- * in device_unrealize due to its usage later on in the unplug path
- */
- g_free(dev->canonical_path);
- dev->canonical_path = object_get_canonical_path(OBJECT(dev));
- QLIST_FOREACH(ncl, &dev->clocks, node) {
- if (ncl->alias) {
- continue;
- } else {
- clock_setup_canonical_path(ncl->clock);
- }
- }
-
- if (qdev_get_vmsd(dev)) {
- if (vmstate_register_with_alias_id(VMSTATE_IF(dev),
- VMSTATE_INSTANCE_ID_ANY,
- qdev_get_vmsd(dev), dev,
- dev->instance_id_alias,
- dev->alias_required_for_version,
- &local_err) < 0) {
- goto post_realize_fail;
- }
- }
-
- /*
- * Clear the reset state, in case the object was previously unrealized
- * with a dirty state.
- */
- resettable_state_clear(&dev->reset);
-
- QLIST_FOREACH(bus, &dev->child_bus, sibling) {
- if (!qbus_realize(bus, errp)) {
- goto child_realize_fail;
- }
- }
- if (dev->hotplugged) {
- /*
- * Reset the device, as well as its subtree which, at this point,
- * should be realized too.
- */
- resettable_assert_reset(OBJECT(dev), RESET_TYPE_COLD);
- resettable_change_parent(OBJECT(dev), OBJECT(dev->parent_bus),
- NULL);
- resettable_release_reset(OBJECT(dev), RESET_TYPE_COLD);
- }
- dev->pending_deleted_event = false;
-
- if (hotplug_ctrl) {
- hotplug_handler_plug(hotplug_ctrl, dev, &local_err);
- if (local_err != NULL) {
- goto child_realize_fail;
- }
- }
-
- qatomic_store_release(&dev->realized, value);
-
- } else if (!value && dev->realized) {
-
- /*
- * Change the value so that any concurrent users are aware
- * that the device is going to be unrealized
- *
- * TODO: change .realized property to enum that states
- * each phase of the device realization/unrealization
- */
-
- qatomic_set(&dev->realized, value);
- /*
- * Ensure that concurrent users see this update prior to
- * any other changes done by unrealize.
- */
- smp_wmb();
-
- QLIST_FOREACH(bus, &dev->child_bus, sibling) {
- qbus_unrealize(bus);
- }
- if (qdev_get_vmsd(dev)) {
- vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);
- }
- if (dc->unrealize) {
- dc->unrealize(dev);
- }
- dev->pending_deleted_event = true;
- DEVICE_LISTENER_CALL(unrealize, Reverse, dev);
- }
-
- assert(local_err == NULL);
- return;
-
- child_realize_fail:
- QLIST_FOREACH(bus, &dev->child_bus, sibling) {
- qbus_unrealize(bus);
- }
-
- if (qdev_get_vmsd(dev)) {
- vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);
- }
-
- post_realize_fail:
- g_free(dev->canonical_path);
- dev->canonical_path = NULL;
- if (dc->unrealize) {
- dc->unrealize(dev);
- }
-
- fail:
- error_propagate(errp, local_err);
- if (unattached_parent) {
- /*
- * Beware, this doesn't just revert
- * object_property_add_child(), it also runs bus_remove()!
- */
- object_unparent(OBJECT(dev));
- unattached_count--;
- }
- }
- 设备类的realize函数
相关代码片段:
-
- if (dc->realize) {
- dc->realize(dev, &local_err);
- if (local_err != NULL) {
- goto fail;
- }
- }
设备类的realize函数最开始调用的是DeviceClass的realize函数,这个回调的默认函数是xxx_device_realize()。当然,如果是继承自Device的类,也可以重写这个函数。如PCIDeviceClass类就在其类 初始化 函数pci_device_class_init()中将DeviceClass->realize重写为了pci_qdev_realize。代码在hw/pci/pci.c中,如下所示:
- static void pci_device_class_init(ObjectClass *klass, void *data)
- {
- DeviceClass *k = DEVICE_CLASS(klass);
-
- k->realize = pci_qdev_realize;
- k->unrealize = pci_qdev_unrealize;
- k->bus_type = TYPE_PCI_BUS;
- device_class_set_props(k, pci_props);
- }
对于PCIDeviceClass本身来说,其PCIDeviceClass->realize可以设置为pci_default_realize函数(此处只是为了更好地说明问题,新版QEMU中已无此函数)。后边继承PCIDeviceClass的类可以在自己的类初始化函数中设置其自己的realize函数。
virtio设备类的继承链关系为“DeviceClass -> PCIDeviceClass ->VirtioPCIClass”。
下一回将分析在VirtioPCIClass类还没有初始化时的realize函数。
欲知后事如何,且看下回分解。