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

Linux 6.18.37 · Driver API

Writing Virtio Drivers

Virtio driver skeleton, probe/remove, ID 예약, DRIVER_OK, scatterlist 송수신과 callback 제어를 설명하는 한국어 전문 번역입니다.

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

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

1. 요약·해설

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

요약·해설

writing_virtio_drivers.rst:1-196

Virtio driver는 specification과 일치하는 queue를 probe에서 구성하고 DRIVER_OK 뒤 buffer를 제출합니다. Driver가 scatterlist를 queue에 넣고 kick하면 device가 처리 후 callback으로 알리며, remove에서는 reset, unused-buffer 분리, queue 제거 순서로 정리합니다.

문서 구성
원문 줄핵심 내용
1-17작성 지침의 범위
18-109Driver skeleton, probe, remove와 등록
110-131Device ID와 ready 상태
132-188Buffer 송수신과 callback control
189-196Specification reference

2. 영어 원문 전체

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

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 .. _writing_virtio_drivers:
4
5 ======================
6 Writing Virtio Drivers
7 ======================
8
9 Introduction
10 ============
11
12 This document serves as a basic guideline for driver programmers that
13 need to hack a new virtio driver or understand the essentials of the
14 existing ones. See :ref:`Virtio on Linux <virtio>` for a general
15 overview of virtio.
16
17
18 Driver boilerplate
19 ==================
20
21 As a bare minimum, a virtio driver needs to register in the virtio bus
22 and configure the virtqueues for the device according to its spec, the
23 configuration of the virtqueues in the driver side must match the
24 virtqueue definitions in the device. A basic driver skeleton could look
25 like this::
26
27 #include <linux/virtio.h>
28 #include <linux/virtio_ids.h>
29 #include <linux/virtio_config.h>
30 #include <linux/module.h>
31
32 /* device private data (one per device) */
33 struct virtio_dummy_dev {
34 struct virtqueue *vq;
35 };
36
37 static void virtio_dummy_recv_cb(struct virtqueue *vq)
38 {
39 struct virtio_dummy_dev *dev = vq->vdev->priv;
40 char *buf;
41 unsigned int len;
42
43 while ((buf = virtqueue_get_buf(dev->vq, &len)) != NULL) {
44 /* process the received data */
45 }
46 }
47
48 static int virtio_dummy_probe(struct virtio_device *vdev)
49 {
50 struct virtio_dummy_dev *dev = NULL;
51
52 /* initialize device data */
53 dev = kzalloc(sizeof(struct virtio_dummy_dev), GFP_KERNEL);
54 if (!dev)
55 return -ENOMEM;
56
57 /* the device has a single virtqueue */
58 dev->vq = virtio_find_single_vq(vdev, virtio_dummy_recv_cb, "input");
59 if (IS_ERR(dev->vq)) {
60 kfree(dev);
61 return PTR_ERR(dev->vq);
62
63 }
64 vdev->priv = dev;
65
66 /* from this point on, the device can notify and get callbacks */
67 virtio_device_ready(vdev);
68
69 return 0;
70 }
71
72 static void virtio_dummy_remove(struct virtio_device *vdev)
73 {
74 struct virtio_dummy_dev *dev = vdev->priv;
75
76 /*
77 * disable vq interrupts: equivalent to
78 * vdev->config->reset(vdev)
79 */
80 virtio_reset_device(vdev);
81
82 /* detach unused buffers */
83 while ((buf = virtqueue_detach_unused_buf(dev->vq)) != NULL) {
84 kfree(buf);
85 }
86
87 /* remove virtqueues */
88 vdev->config->del_vqs(vdev);
89
90 kfree(dev);
91 }
92
93 static const struct virtio_device_id id_table[] = {
94 { VIRTIO_ID_DUMMY, VIRTIO_DEV_ANY_ID },
95 { 0 },
96 };
97
98 static struct virtio_driver virtio_dummy_driver = {
99 .driver.name = KBUILD_MODNAME,
100 .id_table = id_table,
101 .probe = virtio_dummy_probe,
102 .remove = virtio_dummy_remove,
103 };
104
105 module_virtio_driver(virtio_dummy_driver);
106 MODULE_DEVICE_TABLE(virtio, id_table);
107 MODULE_DESCRIPTION("Dummy virtio driver");
108 MODULE_LICENSE("GPL");
109
110 The device id ``VIRTIO_ID_DUMMY`` here is a placeholder, virtio drivers
111 should be added only for devices that are defined in the spec, see
112 include/uapi/linux/virtio_ids.h. Device ids need to be at least reserved
113 in the virtio spec before being added to that file.
114
115 If your driver doesn't have to do anything special in its ``init`` and
116 ``exit`` methods, you can use the module_virtio_driver() helper to
117 reduce the amount of boilerplate code.
118
119 The ``probe`` method does the minimum driver setup in this case
120 (memory allocation for the device data) and initializes the
121 virtqueue. virtio_device_ready() is used to enable the virtqueue and to
122 notify the device that the driver is ready to manage the device
123 ("DRIVER_OK"). The virtqueues are anyway enabled automatically by the
124 core after ``probe`` returns.
125
126 .. kernel-doc:: include/linux/virtio_config.h
127 :identifiers: virtio_device_ready
128
129 In any case, the virtqueues need to be enabled before adding buffers to
130 them.
131
132 Sending and receiving data
133 ==========================
134
135 The virtio_dummy_recv_cb() callback in the code above will be triggered
136 when the device notifies the driver after it finishes processing a
137 descriptor or descriptor chain, either for reading or writing. However,
138 that's only the second half of the virtio device-driver communication
139 process, as the communication is always started by the driver regardless
140 of the direction of the data transfer.
141
142 To configure a buffer transfer from the driver to the device, first you
143 have to add the buffers -- packed as `scatterlists` -- to the
144 appropriate virtqueue using any of the virtqueue_add_inbuf(),
145 virtqueue_add_outbuf() or virtqueue_add_sgs(), depending on whether you
146 need to add one input `scatterlist` (for the device to fill in), one
147 output `scatterlist` (for the device to consume) or multiple
148 `scatterlists`, respectively. Then, once the virtqueue is set up, a call
149 to virtqueue_kick() sends a notification that will be serviced by the
150 hypervisor that implements the device::
151
152 struct scatterlist sg[1];
153 sg_init_one(sg, buffer, BUFLEN);
154 virtqueue_add_inbuf(dev->vq, sg, 1, buffer, GFP_ATOMIC);
155 virtqueue_kick(dev->vq);
156
157 .. kernel-doc:: drivers/virtio/virtio_ring.c
158 :identifiers: virtqueue_add_inbuf
159
160 .. kernel-doc:: drivers/virtio/virtio_ring.c
161 :identifiers: virtqueue_add_outbuf
162
163 .. kernel-doc:: drivers/virtio/virtio_ring.c
164 :identifiers: virtqueue_add_sgs
165
166 Then, after the device has read or written the buffers prepared by the
167 driver and notifies it back, the driver can call virtqueue_get_buf() to
168 read the data produced by the device (if the virtqueue was set up with
169 input buffers) or simply to reclaim the buffers if they were already
170 consumed by the device:
171
172 .. kernel-doc:: drivers/virtio/virtio_ring.c
173 :identifiers: virtqueue_get_buf_ctx
174
175 The virtqueue callbacks can be disabled and re-enabled using the
176 virtqueue_disable_cb() and the family of virtqueue_enable_cb() functions
177 respectively. See drivers/virtio/virtio_ring.c for more details:
178
179 .. kernel-doc:: drivers/virtio/virtio_ring.c
180 :identifiers: virtqueue_disable_cb
181
182 .. kernel-doc:: drivers/virtio/virtio_ring.c
183 :identifiers: virtqueue_enable_cb
184
185 But note that some spurious callbacks can still be triggered under
186 certain scenarios. The way to disable callbacks reliably is to reset the
187 device or the virtqueue (virtio_reset_device()).
188
189
190 References
191 ==========
192
193 _`[1]` Virtio Spec v1.2:
194 https://docs.oasis-open.org/virtio/virtio/v1.2/virtio-v1.2.html
195
196 Check for later versions of the spec as well.
197

3. 한국어 전문 번역

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

Virtio driver 작성 지침의 범위

1-17

이 문서는 새 virtio driver를 구현하거나 기존 driver의 핵심을 이해해야 하는 programmer를 위한 기본 지침입니다. Virtio 전체 구조는 `Virtio on Linux` 문서를 먼저 참조합니다.

작성 지침의 목표
독자얻는 내용
새 driver 작성자Bus 등록, queue 구성, data path skeleton
기존 driver 분석자Probe/remove와 callback lifecycle의 핵심
Virtio 입문자Linux virtio overview로 이어지는 참조

.. SPDX-License-Identifier: GPL-2.0

.. _writing_virtio_drivers:

======================
Writing Virtio Drivers
======================

Introduction
============

This document serves as a basic guideline for driver programmers that
need to hack a new virtio driver or understand the essentials of the
existing ones. See :ref:`Virtio on Linux <virtio>` for a general
overview of virtio.

최소 driver skeleton과 probe

18-70

최소한의 virtio driver는 virtio bus에 등록하고 device specification에 맞춰 virtqueue를 구성해야 합니다. Driver 쪽 queue 구성은 device가 정의한 virtqueue와 정확히 일치해야 합니다.

예제의 `struct virtio_dummy_dev`는 device마다 하나씩 존재하는 private data이며 `struct virtqueue *vq`를 보관합니다. Completion callback `virtio_dummy_recv_cb()`는 `vq->vdev->priv`에서 private data를 얻고 `virtqueue_get_buf()`가 반환하는 처리 완료 buffer를 반복해서 회수합니다.

`virtio_dummy_probe()`는 private data를 `kzalloc()`으로 할당하고 `virtio_find_single_vq()`로 이름이 `input`인 단일 queue를 찾습니다. 실패하면 private data를 해제하고 error pointer의 errno를 반환합니다.

성공하면 `vdev->priv`에 private data를 저장하고 `virtio_device_ready()`를 호출합니다. 이 지점부터 device가 notify할 수 있고 driver callback도 실행될 수 있습니다.

Minimal virtio probe
Allocate per-device private stateCall `virtio_find_single_vq()`Install completion callbackStore state in `vdev->priv`Call `virtio_device_ready()`Accept device notifications

Private state를 만든 뒤 specification과 일치하는 queue를 찾고 마지막에 device를 ready 상태로 전환합니다.

Driver boilerplate
==================

As a bare minimum, a virtio driver needs to register in the virtio bus
and configure the virtqueues for the device according to its spec, the
configuration of the virtqueues in the driver side must match the
virtqueue definitions in the device. A basic driver skeleton could look
like this::

        #include <linux/virtio.h>
        #include <linux/virtio_ids.h>
        #include <linux/virtio_config.h>
        #include <linux/module.h>

        /* device private data (one per device) */
        struct virtio_dummy_dev {
                struct virtqueue *vq;
        };

        static void virtio_dummy_recv_cb(struct virtqueue *vq)
        {
                struct virtio_dummy_dev *dev = vq->vdev->priv;
                char *buf;
                unsigned int len;

                while ((buf = virtqueue_get_buf(dev->vq, &len)) != NULL) {
                        /* process the received data */
                }
        }

        static int virtio_dummy_probe(struct virtio_device *vdev)
        {
                struct virtio_dummy_dev *dev = NULL;

                /* initialize device data */
                dev = kzalloc(sizeof(struct virtio_dummy_dev), GFP_KERNEL);
                if (!dev)
                        return -ENOMEM;

                /* the device has a single virtqueue */
                dev->vq = virtio_find_single_vq(vdev, virtio_dummy_recv_cb, "input");
                if (IS_ERR(dev->vq)) {
                        kfree(dev);
                        return PTR_ERR(dev->vq);

                }
                vdev->priv = dev;

                /* from this point on, the device can notify and get callbacks */
                virtio_device_ready(vdev);

                return 0;
        }

Remove 경로와 driver 등록

71-109

`virtio_dummy_remove()`는 먼저 `virtio_reset_device()`로 virtqueue interrupt를 비활성화합니다. 이는 `vdev->config->reset(vdev)`와 동등한 효과를 냅니다.

그 다음 `virtqueue_detach_unused_buf()`로 아직 사용되지 않은 buffer를 모두 분리해 해제하고, `vdev->config->del_vqs(vdev)`로 virtqueue를 제거한 뒤 private data를 해제합니다.

`id_table`은 placeholder `VIRTIO_ID_DUMMY`와 `VIRTIO_DEV_ANY_ID`를 사용합니다. `struct virtio_driver`에는 module name, ID table, `probe`, `remove` callback을 지정합니다.

`module_virtio_driver()`가 module init/exit와 virtio bus 등록 boilerplate를 만들고, `MODULE_DEVICE_TABLE`, description, GPL license metadata를 함께 선언합니다.

Virtio remove와 module registration
`virtio_reset_device()`Detach every unused bufferDelete virtqueues with `del_vqs`Free private stateRegister callbacks through `struct virtio_driver`Use `module_virtio_driver()` boilerplate

Remove는 notification을 먼저 멈춘 뒤 buffer, queue, private state 순서로 정리합니다.


static void virtio_dummy_remove(struct virtio_device *vdev)
{
        struct virtio_dummy_dev *dev = vdev->priv;

        /*
         * disable vq interrupts: equivalent to
         * vdev->config->reset(vdev)
         */
        virtio_reset_device(vdev);

        /* detach unused buffers */
        while ((buf = virtqueue_detach_unused_buf(dev->vq)) != NULL) {
                kfree(buf);
        }

        /* remove virtqueues */
        vdev->config->del_vqs(vdev);

        kfree(dev);
}

static const struct virtio_device_id id_table[] = {
        { VIRTIO_ID_DUMMY, VIRTIO_DEV_ANY_ID },
        { 0 },
};

static struct virtio_driver virtio_dummy_driver = {
        .driver.name =  KBUILD_MODNAME,
        .id_table =     id_table,
        .probe =        virtio_dummy_probe,
        .remove =       virtio_dummy_remove,
};

module_virtio_driver(virtio_dummy_driver);
MODULE_DEVICE_TABLE(virtio, id_table);
MODULE_DESCRIPTION("Dummy virtio driver");
MODULE_LICENSE("GPL");

Device ID 예약과 DRIVER_OK 전환

110-131

예제의 `VIRTIO_ID_DUMMY`는 placeholder입니다. Virtio driver는 specification에 정의된 device에만 추가해야 하며 ID 목록은 `include/uapi/linux/virtio_ids.h`에 있습니다. 이 file에 ID를 넣기 전에 virtio specification에서 최소한 해당 ID를 reserve해야 합니다.

Driver의 `init`과 `exit`에서 특별한 처리가 필요하지 않으면 `module_virtio_driver()` helper를 사용해 boilerplate code를 줄일 수 있습니다.

예제 `probe`는 device private memory를 할당하고 virtqueue를 초기화하는 최소 setup을 수행합니다. `virtio_device_ready()`는 queue를 enable하고 driver가 device를 관리할 준비가 되었다는 `DRIVER_OK` 상태를 device에 알립니다.

Core는 `probe`가 반환된 뒤에도 virtqueue를 자동 enable하지만, 어떠한 경우에도 queue에 buffer를 추가하기 전에 virtqueue가 enable되어 있어야 합니다.

Virtio driver activation 규칙
항목규칙
Device IDSpecification에서 정의 또는 최소 reserve 후 header에 추가
Module helperSpecial init/exit가 없으면 `module_virtio_driver()` 사용
ProbePrivate state와 virtqueue 최소 설정
Ready`virtio_device_ready()`로 `DRIVER_OK` 전달
Buffer submissionVirtqueue enable 이후에만 허용

The device id ``VIRTIO_ID_DUMMY`` here is a placeholder, virtio drivers
should be added only for devices that are defined in the spec, see
include/uapi/linux/virtio_ids.h. Device ids need to be at least reserved
in the virtio spec before being added to that file.

If your driver doesn't have to do anything special in its ``init`` and
``exit`` methods, you can use the module_virtio_driver() helper to
reduce the amount of boilerplate code.

The ``probe`` method does the minimum driver setup in this case
(memory allocation for the device data) and initializes the
virtqueue. virtio_device_ready() is used to enable the virtqueue and to
notify the device that the driver is ready to manage the device
("DRIVER_OK"). The virtqueues are anyway enabled automatically by the
core after ``probe`` returns.

.. kernel-doc:: include/linux/virtio_config.h
    :identifiers: virtio_device_ready

In any case, the virtqueues need to be enabled before adding buffers to
them.

Scatterlist buffer 제출과 kick

132-170

앞선 `virtio_dummy_recv_cb()`는 device가 descriptor 또는 descriptor chain의 read/write 처리를 끝내고 driver에 notify할 때 실행됩니다. 하지만 이것은 통신의 후반부이며 data direction과 관계없이 virtio device-driver communication은 항상 driver가 시작합니다.

Driver는 먼저 buffer를 scatterlist로 묶어 queue에 넣습니다. Device가 채울 input scatterlist 하나에는 `virtqueue_add_inbuf()`, device가 소비할 output scatterlist 하나에는 `virtqueue_add_outbuf()`, 여러 scatterlist에는 `virtqueue_add_sgs()`를 사용합니다.

Queue entry를 준비한 뒤 `virtqueue_kick()`을 호출하면 device를 구현한 hypervisor에 notification이 전달됩니다. 예제는 `sg_init_one()`으로 buffer 하나를 만들고 `virtqueue_add_inbuf()`로 input queue에 넣은 뒤 kick합니다.

Device가 buffer를 읽거나 쓴 뒤 다시 notify하면, input buffer에서는 `virtqueue_get_buf()`로 device가 만든 data를 읽고 output buffer에서는 이미 소비된 buffer를 회수합니다.

Virtio buffer transfer
Prepare buffer and `scatterlist`Choose inbuf, outbuf or multiple sgsAdd descriptor chain to virtqueueCall `virtqueue_kick()`Device reads or writes buffersDevice notifies driver callbackDriver calls `virtqueue_get_buf()`

Driver가 descriptor를 제출하고 kick한 뒤 device completion notification에서 buffer를 회수합니다.

Sending and receiving data
==========================

The virtio_dummy_recv_cb() callback in the code above will be triggered
when the device notifies the driver after it finishes processing a
descriptor or descriptor chain, either for reading or writing. However,
that's only the second half of the virtio device-driver communication
process, as the communication is always started by the driver regardless
of the direction of the data transfer.

To configure a buffer transfer from the driver to the device, first you
have to add the buffers -- packed as `scatterlists` -- to the
appropriate virtqueue using any of the virtqueue_add_inbuf(),
virtqueue_add_outbuf() or virtqueue_add_sgs(), depending on whether you
need to add one input `scatterlist` (for the device to fill in), one
output `scatterlist` (for the device to consume) or multiple
`scatterlists`, respectively. Then, once the virtqueue is set up, a call
to virtqueue_kick() sends a notification that will be serviced by the
hypervisor that implements the device::

        struct scatterlist sg[1];
        sg_init_one(sg, buffer, BUFLEN);
        virtqueue_add_inbuf(dev->vq, sg, 1, buffer, GFP_ATOMIC);
        virtqueue_kick(dev->vq);

.. kernel-doc:: drivers/virtio/virtio_ring.c
    :identifiers: virtqueue_add_inbuf

.. kernel-doc:: drivers/virtio/virtio_ring.c
    :identifiers: virtqueue_add_outbuf

.. kernel-doc:: drivers/virtio/virtio_ring.c
    :identifiers: virtqueue_add_sgs

Then, after the device has read or written the buffers prepared by the
driver and notifies it back, the driver can call virtqueue_get_buf() to
read the data produced by the device (if the virtqueue was set up with
input buffers) or simply to reclaim the buffers if they were already
consumed by the device:

Buffer 회수와 callback 제어

171-188

`virtqueue_get_buf_ctx()` 계열은 완료된 buffer를 queue에서 꺼내며 필요하면 함께 저장한 context도 반환합니다.

Virtqueue callback은 `virtqueue_disable_cb()`로 비활성화하고 `virtqueue_enable_cb()` 계열 함수로 다시 활성화할 수 있습니다. 구체적인 variant와 synchronization 동작은 `drivers/virtio/virtio_ring.c`의 kernel-doc을 확인합니다.

특정 scenario에서는 callback을 disable한 뒤에도 spurious callback이 발생할 수 있습니다. Callback을 확실하게 막는 방법은 `virtio_reset_device()`로 device 또는 virtqueue를 reset하는 것입니다.

Virtqueue callback control
API기능과 주의점
`virtqueue_get_buf_ctx()`완료 buffer와 optional context 회수
`virtqueue_disable_cb()`Queue callback 억제 요청
`virtqueue_enable_cb()` familyCallback 재활성화와 race 확인
`virtio_reset_device()`Spurious callback까지 막는 확실한 reset


.. kernel-doc:: drivers/virtio/virtio_ring.c
    :identifiers: virtqueue_get_buf_ctx

The virtqueue callbacks can be disabled and re-enabled using the
virtqueue_disable_cb() and the family of virtqueue_enable_cb() functions
respectively. See drivers/virtio/virtio_ring.c for more details:

.. kernel-doc:: drivers/virtio/virtio_ring.c
    :identifiers: virtqueue_disable_cb

.. kernel-doc:: drivers/virtio/virtio_ring.c
    :identifiers: virtqueue_enable_cb

But note that some spurious callbacks can still be triggered under
certain scenarios. The way to disable callbacks reliably is to reset the
device or the virtqueue (virtio_reset_device()).

Virtio driver 작성 기준 specification

189-196

Driver 구현의 normative reference는 OASIS Virtio Specification v1.2입니다. 실제 개발 시 v1.2만 고정해서 사용하지 말고 이후에 발표된 최신 version도 확인해야 합니다.

Driver 작성 reference
자료용도
Virtio Spec v1.2Device ID, feature, queue와 transport contract
Later specification versions변경된 요구사항과 새 device type 확인


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.