Documentation/driver-api/virtio/virtio.rst GitHub 원문 ↗

Linux 6.18.37 · Driver API

Virtio on Linux

Virtio transport, shared-memory virtqueue, completion callback, PCI discovery와 driver probing을 설명하는 한국어 전문 번역입니다.

Source pathDocumentation/driver-api/virtio/virtio.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

virtio.rst:1-145

Virtio는 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가 구성됩니다.

문서 구성
원문 줄핵심 내용
1-23Virtio open standard와 적용 범위
24-68Transport, shared memory와 callback
69-131PCI discovery, bus registration과 queue setup
132-145Specification과 ring 참고자료

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 .. _virtio:
4
5 ===============
6 Virtio on Linux
7 ===============
8
9 Introduction
10 ============
11
12 Virtio is an open standard that defines a protocol for communication
13 between drivers and devices of different types, see Chapter 5 ("Device
14 Types") of the virtio spec (`[1]`_). Originally developed as a standard
15 for paravirtualized devices implemented by a hypervisor, it can be used
16 to interface any compliant device (real or emulated) with a driver.
17
18 For illustrative purposes, this document will focus on the common case
19 of a Linux kernel running in a virtual machine and using paravirtualized
20 devices provided by the hypervisor, which exposes them as virtio devices
21 via standard mechanisms such as PCI.
22
23
24 Device - Driver communication: virtqueues
25 =========================================
26
27 Although the virtio devices are really an abstraction layer in the
28 hypervisor, they're exposed to the guest as if they are physical devices
29 using a specific transport method -- PCI, MMIO or CCW -- that is
30 orthogonal to the device itself. The virtio spec defines these transport
31 methods in detail, including device discovery, capabilities and
32 interrupt handling.
33
34 The communication between the driver in the guest OS and the device in
35 the hypervisor is done through shared memory (that's what makes virtio
36 devices so efficient) using specialized data structures called
37 virtqueues, which are actually ring buffers [#f1]_ of buffer descriptors
38 similar to the ones used in a network device:
39
40 .. kernel-doc:: include/uapi/linux/virtio_ring.h
41 :identifiers: struct vring_desc
42
43 All the buffers the descriptors point to are allocated by the guest and
44 used by the host either for reading or for writing but not for both.
45
46 Refer to Chapter 2.5 ("Virtqueues") of the virtio spec (`[1]`_) for the
47 reference definitions of virtqueues and "Virtqueues and virtio ring: How
48 the data travels" blog post (`[2]`_) for an illustrated overview of how
49 the host device and the guest driver communicate.
50
51 The :c:type:`vring_virtqueue` struct models a virtqueue, including the
52 ring buffers and management data. Embedded in this struct is the
53 :c:type:`virtqueue` struct, which is the data structure that's
54 ultimately used by virtio drivers:
55
56 .. kernel-doc:: include/linux/virtio.h
57 :identifiers: struct virtqueue
58
59 The callback function pointed by this struct is triggered when the
60 device has consumed the buffers provided by the driver. More
61 specifically, the trigger will be an interrupt issued by the hypervisor
62 (see vring_interrupt()). Interrupt request handlers are registered for
63 a virtqueue during the virtqueue setup process (transport-specific).
64
65 .. kernel-doc:: drivers/virtio/virtio_ring.c
66 :identifiers: vring_interrupt
67
68
69 Device discovery and probing
70 ============================
71
72 In the kernel, the virtio core contains the virtio bus driver and
73 transport-specific drivers like `virtio-pci` and `virtio-mmio`. Then
74 there are individual virtio drivers for specific device types that are
75 registered to the virtio bus driver.
76
77 How a virtio device is found and configured by the kernel depends on how
78 the hypervisor defines it. Taking the `QEMU virtio-console
79 <https://gitlab.com/qemu-project/qemu/-/blob/master/hw/char/virtio-console.c>`__
80 device as an example. When using PCI as a transport method, the device
81 will present itself on the PCI bus with vendor 0x1af4 (Red Hat, Inc.)
82 and device id 0x1003 (virtio console), as defined in the spec, so the
83 kernel will detect it as it would do with any other PCI device.
84
85 During the PCI enumeration process, if a device is found to match the
86 virtio-pci driver (according to the virtio-pci device table, any PCI
87 device with vendor id = 0x1af4)::
88
89 /* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */
90 static const struct pci_device_id virtio_pci_id_table[] = {
91 { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_ANY_ID) },
92 { 0 }
93 };
94
95 then the virtio-pci driver is probed and, if the probing goes well, the
96 device is registered to the virtio bus::
97
98 static int virtio_pci_probe(struct pci_dev *pci_dev,
99 const struct pci_device_id *id)
100 {
101 ...
102
103 if (force_legacy) {
104 rc = virtio_pci_legacy_probe(vp_dev);
105 /* Also try modern mode if we can't map BAR0 (no IO space). */
106 if (rc == -ENODEV || rc == -ENOMEM)
107 rc = virtio_pci_modern_probe(vp_dev);
108 if (rc)
109 goto err_probe;
110 } else {
111 rc = virtio_pci_modern_probe(vp_dev);
112 if (rc == -ENODEV)
113 rc = virtio_pci_legacy_probe(vp_dev);
114 if (rc)
115 goto err_probe;
116 }
117
118 ...
119
120 rc = register_virtio_device(&vp_dev->vdev);
121
122 When the device is registered to the virtio bus the kernel will look
123 for a driver in the bus that can handle the device and call that
124 driver's ``probe`` method.
125
126 At this point, the virtqueues will be allocated and configured by
127 calling the appropriate ``virtio_find`` helper function, such as
128 virtio_find_single_vq() or virtio_find_vqs(), which will end up calling
129 a transport-specific ``find_vqs`` method.
130
131
132 References
133 ==========
134
135 _`[1]` Virtio Spec v1.2:
136 https://docs.oasis-open.org/virtio/virtio/v1.2/virtio-v1.2.html
137
138 .. Check for later versions of the spec as well.
139
140 _`[2]` Virtqueues and virtio ring: How the data travels
141 https://www.redhat.com/en/blog/virtqueues-and-virtio-ring-how-data-travels
142
143 .. rubric:: Footnotes
144
145 .. [#f1] that's why they may be also referred to as virtrings.
146

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

Linux에서 Virtio가 맡는 역할

1-23

Virtio는 서로 다른 종류의 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로 노출합니다.

Virtio 적용 범위
구성 요소역할
Virtio specificationDriver-device protocol과 device type 정의
HypervisorParavirtualized 또는 emulated device 구현
TransportPCI, MMIO, CCW를 통해 guest에 device 노출
Linux virtio driver공통 protocol로 compliant 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-50

Virtio 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]`의 설명을 참조합니다.

Virtqueue shared-memory data path
Guest allocates data buffersGuest writes `vring_desc` entriesVirtqueue ring exposes descriptors in shared memoryHost reads or writes each buffer in one directionTransport delivers completion interrupt

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`에 있습니다.

Virtqueue completion callback
Driver submits guest-owned buffersHost device consumes descriptor chainHypervisor raises transport interrupt`vring_interrupt()` handles queue interrupt`virtqueue` callback runs in driver

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-94

Kernel의 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를 기증했다는 코드 주석도 보존됩니다.

Virtio PCI 식별 계층
계층예제 값 또는 driver
PCI vendor`0x1af4` / `PCI_VENDOR_ID_REDHAT_QUMRANET`
PCI device`0x1003` / virtio console
Transport driver`virtio-pci`
Virtio busDevice type별 virtio driver와 matching

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를 호출합니다.

Virtio PCI probing sequence
PCI enumeration matches vendor `0x1af4`Probe legacy or modern virtio-pci modeCall `register_virtio_device()`Virtio bus matches device-type driverCall device driver's `probe``virtio_find_*vq*()` invokes transport `find_vqs`

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`이라고도 불린다는 점을 설명합니다.

Virtio 참고자료
참조내용
Virtio Spec v1.2Protocol, transport와 device-type normative definition
Virtqueues and virtio ringShared-memory data movement illustrated overview
`virtring`Ring-buffer 기반 virtqueue의 다른 이름

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.