Full analysis of QEMU source code - virtio (11)

Continuing from the previous article:

The virtio_balloon_pci_realize function was analyzed in detail last time. Finally, it is mentioned that the object_property_set function is called in the last step of the virtio_balloon_pci_realize function, which leads to the execution of the virtio_device_realize function. This time we will analyze the virtio_device_realize function.

For ease of understanding, the source code of the virtio_device_realize function is posted again, in hw/virtio/virtio.c, as follows:

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);
}

The virtio_device_realize function is actually a general function, which is the realization function of the abstract device of type TYPE_VIRTIO_DEVICE. All virtio devices will call this function during initialization.

(1) The virtio_device_realize function first gets the class to which the virtio device belongs. The code snippet is as follows:

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

(2) Then call the realize function of the specific class, which is the virtio_balloon_device_realize function for the virtio balloon device. The code snippet is as follows:

    if (vdc->realize != NULL) {
        vdc->realize(dev, &err);
        if (err != NULL) {
            error_propagate(errp, err);
            return;
        }
    }

(3) Next, call the virtio_bus_device_plugged function to connect the virtio device to the virtio bus. The code snippet is as follows:

    virtio_bus_device_plugged(vdev, &err);
    if (err != NULL) {
        error_propagate(errp, err);
        vdc->unrealize(dev);
        return;
    }

(4) Subsequently, call the memory_listener_register function to register the memory listener. The code snippet is as follows:

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

The memory_listener_register function is declared in include/exec/memory.h, and its prototype is as follows:

/**
 * 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);

This function registers the listener (here is vdev->listener) into the filter address space (here is vdev->dma_as). When the topology of the filter address space changes, all the links on its linked list will be called. listener, calls the relevant callback function.

(5) Finally, call QTAILQ_INSERT_TAIL() to insert vdev into the end of virtio_list. The code snippet is as follows:

    QTAILQ_INSERT_TAIL(&virtio_list, vdev, next);

QTAILQ_INSERT_TAIL is a macro used to insert elements into the tail of the queue. QTAIL_INSERT_TAIL is defined as follows (in 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)

Therefore, the above code finally expands to:

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)

If you want to know what happens next, let’s look at the breakdown in the next chapter.

Guess you like

Origin blog.csdn.net/phmatthaus/article/details/135034948