QEMU源码全解析 —— virtio(11)
接前一篇文章:
上一回对于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)
欲知后事如何,且看下回分解。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!