요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. 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.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Linux에서 Virtio가 맡는 역할
1-23Virtio는 서로 다른 종류의 driver와 device가 통신하는 protocol을 정의하는 open standard입니다. 지원 device type은 virtio specification Chapter 5 `Device Types`에 정의됩니다.
처음에는 hypervisor가 구현하는 paravirtualized device의 표준으로 개발되었지만, 현재는 specification을 따르는 실제 device나 emulated device 모두를 driver와 연결하는 데 사용할 수 있습니다.
이 문서는 설명을 위해 Linux kernel이 virtual machine 안에서 동작하고 hypervisor가 제공하는 paravirtualized device를 사용하는 일반적인 경우에 집중합니다. Hypervisor는 PCI 같은 표준 mechanism으로 이 device를 virtio device로 노출합니다.
.. 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.
Transport와 shared-memory virtqueue
24-50Virtio device가 실제로는 hypervisor의 abstraction layer여도 guest에는 physical device처럼 보입니다. Device 자체와 독립적인 transport method인 PCI, MMIO 또는 CCW를 사용하며, virtio specification은 device discovery, capability, interrupt handling을 포함해 각 transport를 정의합니다.
Guest OS의 driver와 hypervisor의 device는 shared memory로 통신합니다. 이 구조가 virtio의 높은 효율을 만들며, 통신에는 network device의 buffer descriptor와 비슷한 ring buffer인 `virtqueue`를 사용합니다. `include/uapi/linux/virtio_ring.h`의 `struct vring_desc`가 descriptor 형식의 기준입니다.
Descriptor가 가리키는 buffer는 모두 guest가 할당합니다. Host는 각 buffer를 읽기 또는 쓰기 한 방향으로만 사용하며 같은 buffer에 두 방향을 동시에 적용하지 않습니다.
정확한 virtqueue 정의는 virtio specification Chapter 2.5 `Virtqueues`를, host device와 guest driver 사이의 data 이동 그림은 참고문헌 `[2]`의 설명을 참조합니다.
Transport는 device 발견과 interrupt를 담당하고, 실제 payload는 guest가 제공한 shared-memory descriptor ring으로 이동합니다.
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.
virtqueue 구조체와 completion callback
51-68`struct vring_virtqueue`는 ring buffer와 management data를 포함한 virtqueue 전체를 모델링합니다. 그 안에 `struct virtqueue`가 embed되며, virtio driver가 최종적으로 직접 사용하는 data structure는 이 `virtqueue`입니다.
Structure가 가리키는 callback은 device가 driver가 제공한 buffer를 소비했을 때 실행됩니다. 구체적인 trigger는 hypervisor가 발행하는 interrupt이며 `vring_interrupt()`가 처리합니다.
Interrupt request handler는 virtqueue setup 과정에서 queue마다 등록되며, 실제 등록 방식은 PCI, MMIO, CCW 같은 transport에 따라 달라집니다. 관련 kernel-doc은 `include/linux/virtio.h`와 `drivers/virtio/virtio_ring.c`에 있습니다.
Device가 descriptor를 처리한 뒤 transport interrupt가 vring handler와 driver callback을 연결합니다.
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
Virtio core, transport driver와 PCI 발견
69-94Kernel의 virtio core에는 virtio bus driver와 `virtio-pci`, `virtio-mmio` 같은 transport-specific driver가 있습니다. Device type별 virtio driver는 virtio bus driver에 등록됩니다.
Kernel이 virtio device를 찾고 구성하는 방식은 hypervisor가 device를 어떻게 정의했는지에 달려 있습니다. QEMU `virtio-console`을 PCI transport로 사용하면 specification에 따라 PCI vendor ID `0x1af4`(Red Hat, Inc.)와 device ID `0x1003`(virtio console)으로 나타나므로 일반 PCI device와 같은 enumeration 경로에서 발견됩니다.
PCI enumeration 중 vendor ID `0x1af4`인 device는 `virtio_pci_id_table`과 일치하여 `virtio-pci` driver의 probe 대상이 됩니다. Qumranet이 device `0x1000`부터 `0x10FF`까지 사용할 vendor ID를 기증했다는 코드 주석도 보존됩니다.
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 }
};
virtio-pci probe와 virtqueue 검색
95-131`virtio-pci` probe가 성공하면 `register_virtio_device(&vp_dev->vdev)`로 device를 virtio bus에 등록합니다. 예제는 `force_legacy` 여부에 따라 legacy mode와 modern mode를 우선 시도하고, `-ENODEV` 또는 일부 경우 `-ENOMEM` 결과에 따라 다른 mode로 fallback합니다.
Virtio bus에 device가 등록되면 kernel은 해당 device를 처리할 수 있는 bus driver를 찾아 그 driver의 `probe` method를 호출합니다.
이 시점에 driver는 `virtio_find_single_vq()` 또는 `virtio_find_vqs()` 같은 적절한 `virtio_find` helper를 호출해 virtqueue를 할당하고 구성합니다. Helper는 최종적으로 transport-specific `find_vqs` method를 호출합니다.
PCI transport probe가 device를 virtio bus로 승격하고 device-type driver probe가 queue를 구성합니다.
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.
Virtio specification과 ring 참고자료
132-145참고문헌 `[1]`은 OASIS Virtio Specification v1.2이며, 문서 사용 시 더 최신 specification version도 확인해야 합니다.
참고문헌 `[2]`는 `Virtqueues and virtio ring: How the data travels` 글로 host device와 guest driver 사이의 descriptor-ring data path를 그림으로 설명합니다.
각주 `[#f1]`은 virtqueue가 ring buffer이기 때문에 `virtring`이라고도 불린다는 점을 설명합니다.
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.
요약·해설
virtio.rst:1-145Virtio는 PCI, MMIO, CCW transport와 독립적인 protocol이며 guest-owned shared-memory ring으로 host device와 통신합니다. Transport probe가 device를 virtio bus에 등록하면 device-type driver가 matching되고 transport-specific find_vqs를 통해 queue가 구성됩니다.