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

2023-12-17 00:02:02

接前一篇文章:

上一回对于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)

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

文章来源:https://blog.csdn.net/phmatthaus/article/details/135034948
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。