接前一篇文章:
上一回对于virtio_balloon_pci_realize函数进行了详细解析。最后讲到在virtio_balloon_pci_realize函数的最后一步调用了object_property_set函数,从而导致了virtio_device_realize函数的执行。本回就来对于virtio_device_realize函数进行解析。
为了便于理解,再次贴出virtio_device_realize函数的源码,在hw/virtio/virtio.c中,如下:
static void virtio_device_realize(DeviceState *dev, Error **errp)
{VirtIODevice *vdev = VIRTIO_DEVICE(dev);VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(dev);Error *err = NULL;/* Devices should either use vmsd or the load/save methods */assert(!vdc->vmsd || !vdc->load);if (vdc->realize != NULL) {vdc->realize(dev, &err);if (err != NULL) {error_propagate(errp, err);return;}}virtio_bus_device_plugged(vdev, &err);if (err != NULL) {error_propagate(errp, err);vdc->unrealize(dev);return;}vdev->listener.commit = virtio_memory_listener_commit;vdev->listener.name = "virtio";memory_listener_register(&vdev->listener, vdev->dma_as);QTAILQ_INSERT_TAIL(&virtio_list, vdev, next);
}
virtio_device_realize函数其实也是一个通用函数,是类型为TYPE_VIRTIO_DEVICE抽象设备的具现化函数。所有的virtio设备在初始化的时候都会调用这个函数。
(1)virtio_device_realize函数首先得到virtio设备所属的类。代码片段如下:
VirtIODevice *vdev = VIRTIO_DEVICE(dev);VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(dev);
(2)然后调用具体类的realize函数,对于virtio balloon设备来说是virtio_balloon_device_realize函数。代码片段如下:
if (vdc->realize != NULL) {vdc->realize(dev, &err);if (err != NULL) {error_propagate(errp, err);return;}}
(3)接下来,调用virtio_bus_device_plugged函数,将virtio设备挂到virtio总线上。代码片段如下:
virtio_bus_device_plugged(vdev, &err);if (err != NULL) {error_propagate(errp, err);vdc->unrealize(dev);return;}
(4)其后,调用memory_listener_register函数注册内存监听器。代码片段如下:
memory_listener_register(&vdev->listener, vdev->dma_as);
memory_listener_register函数在include/exec/memory.h中声明,其原型如下:
/*** memory_listener_register: register callbacks to be called when memory* sections are mapped or unmapped into an address* space** @listener: an object containing the callbacks to be called* @filter: if non-%NULL, only regions in this address space will be observed*/
void memory_listener_register(MemoryListener *listener, AddressSpace *filter);
该函数将lister(此处是vdev->listener)注册到filter地址空间(此处是vdev->dma_as)中,当filter地址空间的拓扑结构发生变化时,就会调用其链表上的所有listener,调用相关回调函数。
(5)最后,调用QTAILQ_INSERT_TAIL()将vdev插入virtio_list尾部。代码片段如下:
QTAILQ_INSERT_TAIL(&virtio_list, vdev, next);
QTAILQ_INSERT_TAIL是一个宏,用于将元素插入到队列的尾部。QTAIL_INSERT_TAIL定义如下(include/qemu/queue.h中):
#define QTAILQ_INSERT_TAIL(head, elm, field) do { \(elm)->field.tqe_next = NULL; \(elm)->field.tqe_circ.tql_prev = (head)->tqh_circ.tql_prev; \(head)->tqh_circ.tql_prev->tql_next = (elm); \(head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \
} while (/*CONSTCOND*/0)
因此,上述代码最终展开为:
do { \(vdev)->next.tqe_next = NULL; \(vdev)->next.tqe_circ.tql_prev = (&virtio_list)->tqh_circ.tql_prev; \(&virtio_list)->tqh_circ.tql_prev->tql_next = (vdev); \(&virtio_list)->tqh_circ.tql_prev = &(vdev)->next.tqe_circ; \
} while (/*CONSTCOND*/0)
欲知后事如何,且看下回分解。