| .. SPDX-License-Identifier: GPL-2.0 | |
| .. _virtio: | |
| =============== | |
| Virtio on Linux | |
| =============== | |
| Introduction | |
| ============ | |
| Virtio is an open standard that defines a protocol for communication | |
| between drivers and devices of different types, see Chapter 5 ("Device | |
| Types") of the virtio spec (`[1]`_). Originally developed as a standard | |
| for paravirtualized devices implemented by a hypervisor, it can be used | |
| to interface any compliant device (real or emulated) with a driver. | |
| For illustrative purposes, this document will focus on the common case | |
| of a Linux kernel running in a virtual machine and using paravirtualized | |
| devices provided by the hypervisor, which exposes them as virtio devices | |
| via standard mechanisms such as PCI. | |
| Device - Driver communication: virtqueues | |
| ========================================= | |
| Although the virtio devices are really an abstraction layer in the | |
| hypervisor, they're exposed to the guest as if they are physical devices | |
| using a specific transport method -- PCI, MMIO or CCW -- that is | |
| orthogonal to the device itself. The virtio spec defines these transport | |
| methods in detail, including device discovery, capabilities and | |
| interrupt handling. | |
| The communication between the driver in the guest OS and the device in | |
| the hypervisor is done through shared memory (that's what makes virtio | |
| devices so efficient) using specialized data structures called | |
| virtqueues, which are actually ring buffers [#f1]_ of buffer descriptors | |
| similar to the ones used in a network device: | |
| .. kernel-doc:: include/uapi/linux/virtio_ring.h | |
| :identifiers: struct vring_desc | |
| All the buffers the descriptors point to are allocated by the guest and | |
| used by the host either for reading or for writing but not for both. | |
| Refer to Chapter 2.5 ("Virtqueues") of the virtio spec (`[1]`_) for the | |
| reference definitions of virtqueues and "Virtqueues and virtio ring: How | |
| the data travels" blog post (`[2]`_) for an illustrated overview of how | |
| the host device and the guest driver communicate. | |
| The :c:type:`vring_virtqueue` struct models a virtqueue, including the | |
| ring buffers and management data. Embedded in this struct is the | |
| :c:type:`virtqueue` struct, which is the data structure that's | |
| ultimately used by virtio drivers: | |
| .. kernel-doc:: include/linux/virtio.h | |
| :identifiers: struct virtqueue | |
| The callback function pointed by this struct is triggered when the | |
| device has consumed the buffers provided by the driver. More | |
| specifically, the trigger will be an interrupt issued by the hypervisor | |
| (see vring_interrupt()). Interrupt request handlers are registered for | |
| a virtqueue during the virtqueue setup process (transport-specific). | |
| .. kernel-doc:: drivers/virtio/virtio_ring.c | |
| :identifiers: vring_interrupt | |
| Device discovery and probing | |
| ============================ | |
| In the kernel, the virtio core contains the virtio bus driver and | |
| transport-specific drivers like `virtio-pci` and `virtio-mmio`. Then | |
| there are individual virtio drivers for specific device types that are | |
| registered to the virtio bus driver. | |
| How a virtio device is found and configured by the kernel depends on how | |
| the hypervisor defines it. Taking the `QEMU virtio-console | |
| <https://gitlab.com/qemu-project/qemu/-/blob/master/hw/char/virtio-console.c>`__ | |
| device as an example. When using PCI as a transport method, the device | |
| will present itself on the PCI bus with vendor 0x1af4 (Red Hat, Inc.) | |
| and device id 0x1003 (virtio console), as defined in the spec, so the | |
| kernel will detect it as it would do with any other PCI device. | |
| During the PCI enumeration process, if a device is found to match the | |
| virtio-pci driver (according to the virtio-pci device table, any PCI | |
| device with vendor id = 0x1af4):: | |
| /* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */ | |
| static const struct pci_device_id virtio_pci_id_table[] = { | |
| { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_ANY_ID) }, | |
| { 0 } | |
| }; | |
| then the virtio-pci driver is probed and, if the probing goes well, the | |
| device is registered to the virtio bus:: | |
| static int virtio_pci_probe(struct pci_dev *pci_dev, | |
| const struct pci_device_id *id) | |
| { | |
| ... | |
| if (force_legacy) { | |
| rc = virtio_pci_legacy_probe(vp_dev); | |
| /* Also try modern mode if we can't map BAR0 (no IO space). */ | |
| if (rc == -ENODEV || rc == -ENOMEM) | |
| rc = virtio_pci_modern_probe(vp_dev); | |
| if (rc) | |
| goto err_probe; | |
| } else { | |
| rc = virtio_pci_modern_probe(vp_dev); | |
| if (rc == -ENODEV) | |
| rc = virtio_pci_legacy_probe(vp_dev); | |
| if (rc) | |
| goto err_probe; | |
| } | |
| ... | |
| rc = register_virtio_device(&vp_dev->vdev); | |
| When the device is registered to the virtio bus the kernel will look | |
| for a driver in the bus that can handle the device and call that | |
| driver's ``probe`` method. | |
| At this point, the virtqueues will be allocated and configured by | |
| calling the appropriate ``virtio_find`` helper function, such as | |
| virtio_find_single_vq() or virtio_find_vqs(), which will end up calling | |
| a transport-specific ``find_vqs`` method. | |
| References | |
| ========== | |
| _`[1]` Virtio Spec v1.2: | |
| https://docs.oasis-open.org/virtio/virtio/v1.2/virtio-v1.2.html | |
| .. Check for later versions of the spec as well. | |
| _`[2]` Virtqueues and virtio ring: How the data travels | |
| https://www.redhat.com/en/blog/virtqueues-and-virtio-ring-how-data-travels | |
| .. rubric:: Footnotes | |
| .. [#f1] that's why they may be also referred to as virtrings. |