QEMU源码全解析 —— virtio(11)

88 篇文章 19 订阅
本文详细解析QEMU virtio_device_realize函数,这是所有virtio设备初始化时必经的步骤。文章介绍了函数的主要流程,包括获取设备类、调用virtio_balloon_device_realize、设备挂载到virtio总线、注册内存监听器以及设备链表插入操作。
摘要由CSDN通过智能技术生成

接前一篇文章:

上一回对于virtio_balloon_pci_realize函数进行了详细解析。最后讲到在virtio_balloon_pci_realize函数的最后一步调用了object_property_ set函数 ,从而导致了virtio_device_realize函数的执行。本回就来对于virtio_device_realize函数进行解析。

为了便于理解,再次贴出virtio_device_realize函数的源码,在hw/ virtio /virtio.c中,如下:

  1. static void virtio_device_realize(DeviceState *dev, Error **errp)
  2. {
  3. VirtIODevice *vdev = VIRTIO_DEVICE(dev);
  4. VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(dev);
  5. Error *err = NULL;
  6. /* Devices should either use vmsd or the load/save methods */
  7. assert(!vdc->vmsd || !vdc->load);
  8. if (vdc->realize != NULL) {
  9. vdc->realize(dev, &err);
  10. if (err != NULL) {
  11. error_propagate(errp, err);
  12. return;
  13. }
  14. }
  15. virtio_bus_device_plugged(vdev, &err);
  16. if (err != NULL) {
  17. error_propagate(errp, err);
  18. vdc->unrealize(dev);
  19. return;
  20. }
  21. vdev->listener.commit = virtio_memory_listener_commit;
  22. vdev->listener.name = "virtio";
  23. memory_listener_register(&vdev->listener, vdev->dma_as);
  24. QTAILQ_INSERT_TAIL(&virtio_list, vdev, next);
  25. }

virtio_device_realize函数其实也是一个通用函数,是类型为TYPE_VIRTIO_DEVICE抽象设备的具现化函数。所有的virtio设备在初始化的时候都会调用这个函数。

(1)virtio_device_realize函数首先得到virtio设备所属的类。代码片段如下:

  1. VirtIODevice *vdev = VIRTIO_DEVICE(dev);
  2. VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(dev);

(2)然后调用具体类的realize函数,对于virtio balloon设备来说是virtio_balloon_device_realize函数。代码片段如下:

  1. if (vdc->realize != NULL) {
  2. vdc->realize(dev, &err);
  3. if (err != NULL) {
  4. error_propagate(errp, err);
  5. return;
  6. }
  7. }

(3)接下来,调用virtio_bus_device_plugged函数,将virtio设备挂到virtio总线上。代码片段如下:

  1. virtio_bus_device_plugged(vdev, &err);
  2. if (err != NULL) {
  3. error_propagate(errp, err);
  4. vdc->unrealize(dev);
  5. return;
  6. }

(4)其后,调用memory_listener_register函数注册内存监听器。代码片段如下:

    memory_listener_register(&vdev->listener, vdev->dma_as);

memory_listener_register函数在include/exec/memory.h中声明,其原型如下:

  1. /**
  2. * memory_listener_register: register callbacks to be called when memory
  3. * sections are mapped or unmapped into an address
  4. * space
  5. *
  6. * @listener: an object containing the callbacks to be called
  7. * @filter: if non-%NULL, only regions in this address space will be observed
  8. */
  9. 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中):

  1. #define QTAILQ_INSERT_TAIL(head, elm, field) do { \
  2. (elm)->field.tqe_next = NULL; \
  3. (elm)->field.tqe_circ.tql_prev = (head)->tqh_circ.tql_prev; \
  4. (head)->tqh_circ.tql_prev->tql_next = (elm); \
  5. (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \
  6. } while (/*CONSTCOND*/0)

因此,上述代码最终展开为:

  1. do { \
  2. (vdev)->next.tqe_next = NULL; \
  3. (vdev)->next.tqe_circ.tql_prev = (&virtio_list)->tqh_circ.tql_prev; \
  4. (&virtio_list)->tqh_circ.tql_prev->tql_next = (vdev); \
  5. (&virtio_list)->tqh_circ.tql_prev = &(vdev)->next.tqe_circ; \
  6. } while (/*CONSTCOND*/0)

欲知后事如何,且看下回分解。

举报

选择你想要举报的内容(必选)
  • 内容涉黄
  • 政治相关
  • 内容抄袭
  • 涉嫌广告
  • 内容侵权
  • 侮辱谩骂
  • 样式问题
  • 其他