qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

virtio-backends.rst (7471B)


      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.