QEMU源码全解析 —— virtio(3)

本文详细介绍了QEMU中virtio设备的初始化过程,包括创建virtio PCI代理设备,该设备挂载到PCI总线上,并进一步创建virtio总线,使virtio设备能够挂载。涉及的源码文件包括virtio-pci.c、virtio-pci.h等,同时列举了virtio balloon、scsi、crypto、block、net和serial等PCI设备的定义。
摘要由CSDN通过智能技术生成

接前一篇文章:

本文内容参考:

《趣谈 Linux操作系统 》 —— 刘超, 极客时间

QEMU /KVM》源码解析与应用 —— 李强,机械工业出版社

特此致谢!

上两回对于virtio进行了简介,并说明了其基本原理以及框架。本回开始正式结合代码讲解virtio的实现机制和细节。

virtio设备的初始化

virtio设备 首先需要创建一个PCI设备 ,叫作 virtio PCI代理设备 ,这个代理设备 挂到PCI总线上

接着, virtio代理设备再创建一条virtio总线 ,这样 virtio设备就可以挂到这条virtio总线上了

首先来看virtio PCI代理设备类型的定义,在QEMU源码(qemu-8.1.3)的hw/virtio/virtio-pci.c中,代码如下:

static const TypeInfo virtio_pci_info = {
    .name          = TYPE_VIRTIO_PCI,
    .parent        = TYPE_PCI_DEVICE,
    .instance_size = sizeof(VirtIOPCIProxy),
    .class_init    = virtio_pci_class_init,
    .class_size    = sizeof(VirtioPCIClass),
    .abstract      = true,
};
……
static const TypeInfo virtio_pci_bus_info = {
    .name          = TYPE_VIRTIO_PCI_BUS,
    .parent        = TYPE_VIRTIO_BUS,
    .instance_size = sizeof(VirtioPCIBusState),
    .class_size    = sizeof(VirtioPCIBusClass),
    .class_init    = virtio_pci_bus_class_init,
};

static void virtio_pci_register_types(void)
{
    /* Base types: */
    type_register_static(&virtio_pci_bus_info);
    type_register_static(&virtio_pci_info);
}

type_init(virtio_pci_register_types)

这里给出以上几个宏的定义:

  • TYPE_VIRTIO_PCI

TYPE_VIRTIO_PCI的定义在include/hw/virtio/virtio-pci.h中,如下:

/*
 * virtio-pci: This is the PCIDevice which has a virtio-pci-bus.
 */
#define TYPE_VIRTIO_PCI "virtio-pci"
  • TYPE_PCI_DEVICE

TYPE_PCI_DEVICE的定义在include/hw/pci/pci_device.h中,如下:

#define TYPE_PCI_DEVICE "pci-device"
  • TYPE_VIRTIO_PCI_BUS

TYPE_VIRTIO_PCI_BUS的定义在include/hw/virtio/virtio-pci.h中,如下:

#define TYPE_VIRTIO_PCI_BUS "virtio-pci-bus"
  • TYPE_VIRTIO_BUS

TYPE_VIRTIO_BUS的定义在include/hw/virtio/virtio-bus.h中,如下:

#define TYPE_VIRTIO_BUS "virtio-bus"

从virtio PCI代理设备的定义就可以看出,virtio PCI代理设备的 父设备是一个PCI设备 (.parent        = TYPE_PCI_DEVICE,), 类型为VirtIOPCIClass (.class_size    = sizeof(VirtioPCIClass),), 实例为virtIOPCIProxy (.instance_size = sizeof(VirtIOPCIProxy),),注意这是一个 抽象设备 (.abstract      = true,),因此并 不能创建其实例,只能由其子类去创建

QEMU中定义了所有virtio设备的PCI代理设备,如virtio balloon PCI设备、virtio scsi PCI设备、virtio crypto PCI设备等。各代理设备的定义分别如下所示:

  • virtio balloon PCI设备

virtio balloon PCI设备的定义在hw/virtio/virtio-balloon-pci.c中,代码如下:

static const VirtioPCIDeviceTypeInfo virtio_balloon_pci_info = {
    .base_name             = TYPE_VIRTIO_BALLOON_PCI,
    .generic_name          = "virtio-balloon-pci",
    .transitional_name     = "virtio-balloon-pci-transitional",
    .non_transitional_name = "virtio-balloon-pci-non-transitional",
    .instance_size = sizeof(VirtIOBalloonPCI),
    .instance_init = virtio_balloon_pci_instance_init,
    .class_init    = virtio_balloon_pci_class_init,
};

static void virtio_balloon_pci_register(void)
{
    virtio_pci_types_register(&virtio_balloon_pci_info);
}

type_init(virtio_balloon_pci_register)
  • virtio scsi PCI设备

virtio scsi PCI设备的定义在hw/virtio/virtio-scsi-pci.c中,代码如下:

static const VirtioPCIDeviceTypeInfo virtio_scsi_pci_info = {
    .base_name              = TYPE_VIRTIO_SCSI_PCI,
    .generic_name           = "virtio-scsi-pci",
    .transitional_name      = "virtio-scsi-pci-transitional",
    .non_transitional_name  = "virtio-scsi-pci-non-transitional",
    .instance_size = sizeof(VirtIOSCSIPCI),
    .instance_init = virtio_scsi_pci_instance_init,
    .class_init    = virtio_scsi_pci_class_init,
};

static void virtio_scsi_pci_register(void)
{
    virtio_pci_types_register(&virtio_scsi_pci_info);
}

type_init(virtio_scsi_pci_register)
  • virtio crypto PCI设备

virtio crypto PCI设备的定义在hw/virtio/virtio-crypto-pci.c中,代码如下:

static const VirtioPCIDeviceTypeInfo virtio_crypto_pci_info = {
    .generic_name  = TYPE_VIRTIO_CRYPTO_PCI,
    .instance_size = sizeof(VirtIOCryptoPCI),
    .instance_init = virtio_crypto_initfn,
    .class_init    = virtio_crypto_pci_class_init,
};

static void virtio_crypto_pci_register_types(void)
{
    virtio_pci_types_register(&virtio_crypto_pci_info);
}
type_init(virtio_crypto_pci_register_types)
  • virtio block PCI设备

virtio block PCI设备的定义在hw/virtio/virtio-blk-pci.c中,代码如下:

static const VirtioPCIDeviceTypeInfo virtio_blk_pci_info = {
    .base_name              = TYPE_VIRTIO_BLK_PCI,
    .generic_name           = "virtio-blk-pci",
    .transitional_name      = "virtio-blk-pci-transitional",
    .non_transitional_name  = "virtio-blk-pci-non-transitional",
    .instance_size = sizeof(VirtIOBlkPCI),
    .instance_init = virtio_blk_pci_instance_init,
    .class_init    = virtio_blk_pci_class_init,
};

static void virtio_blk_pci_register(void)
{
    virtio_pci_types_register(&virtio_blk_pci_info);
}

type_init(virtio_blk_pci_register)
  • virtio net PCI设备

virtio net PCI设备的定义在hw/virtio/virtio-net-pci.c中,代码如下:

static const VirtioPCIDeviceTypeInfo virtio_net_pci_info = {
    .base_name             = TYPE_VIRTIO_NET_PCI,
    .generic_name          = "virtio-net-pci",
    .transitional_name     = "virtio-net-pci-transitional",
    .non_transitional_name = "virtio-net-pci-non-transitional",
    .instance_size = sizeof(VirtIONetPCI),
    .instance_init = virtio_net_pci_instance_init,
    .class_init    = virtio_net_pci_class_init,
};

static void virtio_net_pci_register(void)
{
    virtio_pci_types_register(&virtio_net_pci_info);
}

type_init(virtio_net_pci_register)
  • virtio serial PCI设备

virtio serial PCI设备的定义在hw/virtio/virtio-serial-pci.c中,代码如下:

static const VirtioPCIDeviceTypeInfo virtio_serial_pci_info = {
    .base_name             = TYPE_VIRTIO_SERIAL_PCI,
    .generic_name          = "virtio-serial-pci",
    .transitional_name     = "virtio-serial-pci-transitional",
    .non_transitional_name = "virtio-serial-pci-non-transitional",
    .instance_size = sizeof(VirtIOSerialPCI),
    .instance_init = virtio_serial_pci_instance_init,
    .class_init    = virtio_serial_pci_class_init,
};

static void virtio_serial_pci_register(void)
{
    virtio_pci_types_register(&virtio_serial_pci_info);
}

type_init(virtio_serial_pci_register)
  • virtio input PCI设备

(各种)virtio input PCI设备的定义在hw/virtio/virtio-input-pci.c中,代码如下:

static const TypeInfo virtio_input_pci_info = {
    .name          = TYPE_VIRTIO_INPUT_PCI,
    .parent        = TYPE_VIRTIO_PCI,
    .instance_size = sizeof(VirtIOInputPCI),
    .class_init    = virtio_input_pci_class_init,
    .abstract      = true,
};

static const TypeInfo virtio_input_hid_pci_info = {
    .name          = TYPE_VIRTIO_INPUT_HID_PCI,
    .parent        = TYPE_VIRTIO_INPUT_PCI,
    .instance_size = sizeof(VirtIOInputHIDPCI),
    .abstract      = true,
};

static const VirtioPCIDeviceTypeInfo virtio_keyboard_pci_info = {
    .generic_name  = TYPE_VIRTIO_KEYBOARD_PCI,
    .parent        = TYPE_VIRTIO_INPUT_HID_PCI,
    .class_init    = virtio_input_hid_kbd_pci_class_init,
    .instance_size = sizeof(VirtIOInputHIDPCI),
    .instance_init = virtio_keyboard_initfn,
};

static const VirtioPCIDeviceTypeInfo virtio_mouse_pci_info = {
    .generic_name  = TYPE_VIRTIO_MOUSE_PCI,
    .parent        = TYPE_VIRTIO_INPUT_HID_PCI,
    .class_init    = virtio_input_hid_mouse_pci_class_init,
    .instance_size = sizeof(VirtIOInputHIDPCI),
    .instance_init = virtio_mouse_initfn,
};

static const VirtioPCIDeviceTypeInfo virtio_tablet_pci_info = {
    .generic_name  = TYPE_VIRTIO_TABLET_PCI,
    .parent        = TYPE_VIRTIO_INPUT_HID_PCI,
    .instance_size = sizeof(VirtIOInputHIDPCI),
    .instance_init = virtio_tablet_initfn,
};

static const VirtioPCIDeviceTypeInfo virtio_multitouch_pci_info = {
    .generic_name  = TYPE_VIRTIO_MULTITOUCH_PCI,
    .parent        = TYPE_VIRTIO_INPUT_HID_PCI,
    .instance_size = sizeof(VirtIOInputHIDPCI),
    .instance_init = virtio_multitouch_initfn,
};

static void virtio_pci_input_register(void)
{
    /* Base types: */
    type_register_static(&virtio_input_pci_info);
    type_register_static(&virtio_input_hid_pci_info);

    /* Implementations: */
    virtio_pci_types_register(&virtio_keyboard_pci_info);
    virtio_pci_types_register(&virtio_mouse_pci_info);
    virtio_pci_types_register(&virtio_tablet_pci_info);
    virtio_pci_types_register(&virtio_multitouch_pci_info);
}

type_init(virtio_pci_input_register)

上边各virtio代理设备都使用了VirtioPCIDeviceTypeInfo,该结构的定义在include/hw/virtio/virtio-pci.h中,代码如下:

/* Input for virtio_pci_types_register() */
typedef struct VirtioPCIDeviceTypeInfo {
    /*
     * Common base class for the subclasses below.
     *
     * Required only if transitional_name or non_transitional_name is set.
     *
     * We need a separate base type instead of making all types
     * inherit from generic_name for two reasons:
     * 1) generic_name implements INTERFACE_PCIE_DEVICE, but
     *    transitional_name does not.
     * 2) generic_name has the "disable-legacy" and "disable-modern"
     *    properties, transitional_name and non_transitional name don't.
     */
    const char *base_name;
    /*
     * Generic device type.  Optional.
     *
     * Supports both transitional and non-transitional modes,
     * using the disable-legacy and disable-modern properties.
     * If disable-legacy=auto, (non-)transitional mode is selected
     * depending on the bus where the device is plugged.
     *
     * Implements both INTERFACE_PCIE_DEVICE and INTERFACE_CONVENTIONAL_PCI_DEVICE,
     * but PCI Express is supported only in non-transitional mode.
     *
     * The only type implemented by QEMU 3.1 and older.
     */
    const char *generic_name;
    /*
     * The transitional device type.  Optional.
     *
     * Implements both INTERFACE_PCIE_DEVICE and INTERFACE_CONVENTIONAL_PCI_DEVICE.
     */
    const char *transitional_name;
    /*
     * The non-transitional device type.  Optional.
     *
     * Implements INTERFACE_CONVENTIONAL_PCI_DEVICE only.
     */
    const char *non_transitional_name;

    /* Parent type.  If NULL, TYPE_VIRTIO_PCI is used */
    const char *parent;

    /* Same as TypeInfo fields: */
    size_t instance_size;
    size_t class_size;
    void (*instance_init)(Object *obj);
    void (*instance_finalize)(Object *obj);
    void (*class_init)(ObjectClass *klass, void *data);
    InterfaceInfo *interfaces;
} VirtioPCIDeviceTypeInfo;

virtio设备在系统的设备树中的位置如下图所示:

欲知后事如何,请看下回分解。