]>
Commit | Line | Data |
---|---|---|
bcf317f8 AB |
1 | .. |
2 | Copyright (c) 2022, Linaro Limited | |
3 | Written by Alex Bennée | |
4 | ||
5 | Writing VirtIO backends for QEMU | |
6 | ================================ | |
7 | ||
8 | This document attempts to outline the information a developer needs to | |
9 | know to write device emulations in QEMU. It is specifically focused on | |
10 | implementing VirtIO devices. For VirtIO the frontend is the driver | |
11 | running on the guest. The backend is the everything that QEMU needs to | |
12 | do to handle the emulation of the VirtIO device. This can be done | |
13 | entirely in QEMU, divided between QEMU and the kernel (vhost) or | |
14 | handled by a separate process which is configured by QEMU | |
15 | (vhost-user). | |
16 | ||
17 | VirtIO Transports | |
18 | ----------------- | |
19 | ||
20 | VirtIO supports a number of different transports. While the details of | |
21 | the configuration and operation of the device will generally be the | |
22 | same QEMU represents them as different devices depending on the | |
23 | transport they use. For example -device virtio-foo represents the foo | |
24 | device using mmio and -device virtio-foo-pci is the same class of | |
25 | device using the PCI transport. | |
26 | ||
27 | Using the QEMU Object Model (QOM) | |
28 | --------------------------------- | |
29 | ||
30 | Generally all devices in QEMU are super classes of ``TYPE_DEVICE`` | |
31 | however VirtIO devices should be based on ``TYPE_VIRTIO_DEVICE`` which | |
32 | itself is derived from the base class. For example: | |
33 | ||
34 | .. code:: c | |
35 | ||
36 | static const TypeInfo virtio_blk_info = { | |
37 | .name = TYPE_VIRTIO_BLK, | |
38 | .parent = TYPE_VIRTIO_DEVICE, | |
39 | .instance_size = sizeof(VirtIOBlock), | |
40 | .instance_init = virtio_blk_instance_init, | |
41 | .class_init = virtio_blk_class_init, | |
42 | }; | |
43 | ||
44 | The author may decide to have a more expansive class hierarchy to | |
45 | support multiple device types. For example the Virtio GPU device: | |
46 | ||
47 | .. code:: c | |
48 | ||
49 | static const TypeInfo virtio_gpu_base_info = { | |
50 | .name = TYPE_VIRTIO_GPU_BASE, | |
51 | .parent = TYPE_VIRTIO_DEVICE, | |
52 | .instance_size = sizeof(VirtIOGPUBase), | |
53 | .class_size = sizeof(VirtIOGPUBaseClass), | |
54 | .class_init = virtio_gpu_base_class_init, | |
55 | .abstract = true | |
56 | }; | |
57 | ||
58 | static const TypeInfo vhost_user_gpu_info = { | |
59 | .name = TYPE_VHOST_USER_GPU, | |
60 | .parent = TYPE_VIRTIO_GPU_BASE, | |
61 | .instance_size = sizeof(VhostUserGPU), | |
62 | .instance_init = vhost_user_gpu_instance_init, | |
63 | .instance_finalize = vhost_user_gpu_instance_finalize, | |
64 | .class_init = vhost_user_gpu_class_init, | |
65 | }; | |
66 | ||
67 | static const TypeInfo virtio_gpu_info = { | |
68 | .name = TYPE_VIRTIO_GPU, | |
69 | .parent = TYPE_VIRTIO_GPU_BASE, | |
70 | .instance_size = sizeof(VirtIOGPU), | |
71 | .class_size = sizeof(VirtIOGPUClass), | |
72 | .class_init = virtio_gpu_class_init, | |
73 | }; | |
74 | ||
75 | defines a base class for the VirtIO GPU and then specialises two | |
76 | versions, one for the internal implementation and the other for the | |
77 | vhost-user version. | |
78 | ||
79 | VirtIOPCIProxy | |
80 | ^^^^^^^^^^^^^^ | |
81 | ||
82 | [AJB: the following is supposition and welcomes more informed | |
83 | opinions] | |
84 | ||
85 | Probably due to legacy from the pre-QOM days PCI VirtIO devices don't | |
86 | follow the normal hierarchy. Instead the a standalone object is based | |
87 | on the VirtIOPCIProxy class and the specific VirtIO instance is | |
88 | manually instantiated: | |
89 | ||
90 | .. code:: c | |
91 | ||
92 | /* | |
93 | * virtio-blk-pci: This extends VirtioPCIProxy. | |
94 | */ | |
95 | #define TYPE_VIRTIO_BLK_PCI "virtio-blk-pci-base" | |
96 | DECLARE_INSTANCE_CHECKER(VirtIOBlkPCI, VIRTIO_BLK_PCI, | |
97 | TYPE_VIRTIO_BLK_PCI) | |
98 | ||
99 | struct VirtIOBlkPCI { | |
100 | VirtIOPCIProxy parent_obj; | |
101 | VirtIOBlock vdev; | |
102 | }; | |
103 | ||
104 | static Property virtio_blk_pci_properties[] = { | |
105 | DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), | |
106 | DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, | |
107 | VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), | |
108 | DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, | |
109 | DEV_NVECTORS_UNSPECIFIED), | |
110 | DEFINE_PROP_END_OF_LIST(), | |
111 | }; | |
112 | ||
113 | static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) | |
114 | { | |
115 | VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(vpci_dev); | |
116 | DeviceState *vdev = DEVICE(&dev->vdev); | |
117 | ||
118 | ... | |
119 | ||
120 | qdev_realize(vdev, BUS(&vpci_dev->bus), errp); | |
121 | } | |
122 | ||
123 | static void virtio_blk_pci_class_init(ObjectClass *klass, void *data) | |
124 | { | |
125 | DeviceClass *dc = DEVICE_CLASS(klass); | |
126 | VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); | |
127 | PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); | |
128 | ||
129 | set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); | |
130 | device_class_set_props(dc, virtio_blk_pci_properties); | |
131 | k->realize = virtio_blk_pci_realize; | |
132 | pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; | |
133 | pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK; | |
134 | pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; | |
135 | pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; | |
136 | } | |
137 | ||
138 | static void virtio_blk_pci_instance_init(Object *obj) | |
139 | { | |
140 | VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(obj); | |
141 | ||
142 | virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), | |
143 | TYPE_VIRTIO_BLK); | |
144 | object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), | |
145 | "bootindex"); | |
146 | } | |
147 | ||
148 | static const VirtioPCIDeviceTypeInfo virtio_blk_pci_info = { | |
149 | .base_name = TYPE_VIRTIO_BLK_PCI, | |
150 | .generic_name = "virtio-blk-pci", | |
151 | .transitional_name = "virtio-blk-pci-transitional", | |
152 | .non_transitional_name = "virtio-blk-pci-non-transitional", | |
153 | .instance_size = sizeof(VirtIOBlkPCI), | |
154 | .instance_init = virtio_blk_pci_instance_init, | |
155 | .class_init = virtio_blk_pci_class_init, | |
156 | }; | |
157 | ||
158 | Here you can see the instance_init has to manually instantiate the | |
159 | underlying ``TYPE_VIRTIO_BLOCK`` object and link an alias for one of | |
160 | it's properties to the PCI device. | |
161 | ||
162 | ||
163 | Back End Implementations | |
164 | ------------------------ | |
165 | ||
166 | There are a number of places where the implementation of the backend | |
167 | can be done: | |
168 | ||
169 | * in QEMU itself | |
170 | * in the host kernel (a.k.a vhost) | |
171 | * in a separate process (a.k.a. vhost-user) | |
172 | ||
173 | vhost_ops vs TYPE_VHOST_USER_BACKEND | |
174 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
175 | ||
176 | There are two choices to how to implement vhost code. Most of the code | |
177 | which has to work with either vhost or vhost-user uses | |
178 | ``vhost_dev_init()`` to instantiate the appropriate backend. This | |
179 | means including a ``struct vhost_dev`` in the main object structure. | |
180 | ||
181 | For vhost-user devices you also need to add code to track the | |
182 | initialisation of the ``chardev`` device used for the control socket | |
183 | between QEMU and the external vhost-user process. | |
184 | ||
185 | If you only need to implement a vhost-user backed the other option is | |
186 | a use a QOM-ified version of vhost-user. | |
187 | ||
188 | .. code:: c | |
189 | ||
190 | static void | |
191 | vhost_user_gpu_instance_init(Object *obj) | |
192 | { | |
193 | VhostUserGPU *g = VHOST_USER_GPU(obj); | |
194 | ||
195 | g->vhost = VHOST_USER_BACKEND(object_new(TYPE_VHOST_USER_BACKEND)); | |
196 | object_property_add_alias(obj, "chardev", | |
197 | OBJECT(g->vhost), "chardev"); | |
198 | } | |
199 | ||
200 | static const TypeInfo vhost_user_gpu_info = { | |
201 | .name = TYPE_VHOST_USER_GPU, | |
202 | .parent = TYPE_VIRTIO_GPU_BASE, | |
203 | .instance_size = sizeof(VhostUserGPU), | |
204 | .instance_init = vhost_user_gpu_instance_init, | |
205 | .instance_finalize = vhost_user_gpu_instance_finalize, | |
206 | .class_init = vhost_user_gpu_class_init, | |
207 | }; | |
208 | ||
209 | Using it this way entails adding a ``struct VhostUserBackend`` to your | |
210 | core object structure and manually instantiating the backend. This | |
211 | sub-structure tracks both the ``vhost_dev`` and ``CharDev`` types | |
212 | needed for the connection. Instead of calling ``vhost_dev_init`` you | |
213 | would call ``vhost_user_backend_dev_init`` which does what is needed | |
214 | on your behalf. |