接前一篇文章:中移(苏州)软件技术有限公司面试问题与解答(4)—— virtio所创建的设备1

在上一篇文章中,对于面试所提出的问题“virtio会创建哪些设备?”,有了初步答案,即virtio后端设备分为两大类设备:virtio PCI控制设备和virtio设备。具体来说,就是virtio-pci设备和具体的virtio设备。本回对于细节进行讲解,在初步回答此面试问题的基础上,深入回答此问题。

中移(苏州)软件技术有限公司面试问题与解答(4)—— virtio所创建的设备2-LMLPHP

virtio设备的初始化

virtio设备首先需要创建一个PCI设备,叫作 virtio PCI代理设备(就是上边的virtio PCI控制设备),这个代理设备挂到了PCI总线上;接着,virtio代理设备再创建一条virtio总线,这样virtio设备就可以挂到这条总线上了。

首先来看virtio PCI代理设备类型的定义,在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,
};

其中:

  • TYPE_VIRTIO_PCI

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

#define TYPE_VIRTIO_PCI "virtio-pci"
OBJECT_DECLARE_TYPE(VirtIOPCIProxy, VirtioPCIClass, VIRTIO_PCI)
  • TYPE_PCI_DEVICE

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

#define TYPE_PCI_DEVICE "pci-device"
typedef struct PCIDeviceClass PCIDeviceClass;
DECLARE_OBJ_CHECKERS(PCIDevice, PCIDeviceClass,
                     PCI_DEVICE, TYPE_PCI_DEVICE)
  • VirtIOPCIProxy

VirtIOPCIProxy结构的定义也在include/hw/virtio/virtio-pci.h中,如下:

struct VirtIOPCIProxy {
    PCIDevice pci_dev;
    MemoryRegion bar;
    union {
        struct {
            VirtIOPCIRegion common;
            VirtIOPCIRegion isr;
            VirtIOPCIRegion device;
            VirtIOPCIRegion notify;
            VirtIOPCIRegion notify_pio;
        };
        VirtIOPCIRegion regs[5];
    };
    MemoryRegion modern_bar;
    MemoryRegion io_bar;
    uint32_t legacy_io_bar_idx;
    uint32_t msix_bar_idx;
    uint32_t modern_io_bar_idx;
    uint32_t modern_mem_bar_idx;
    int config_cap;
    uint32_t flags;
    bool disable_modern;
    bool ignore_backend_features;
    OnOffAuto disable_legacy;
    /* Transitional device id */
    uint16_t trans_devid;
    uint32_t class_code;
    uint32_t nvectors;
    uint32_t dfselect;
    uint32_t gfselect;
    uint32_t guest_features[2];
    VirtIOPCIQueue vqs[VIRTIO_QUEUE_MAX];

    VirtIOIRQFD *vector_irqfd;
    int nvqs_with_notifiers;
    VirtioBusState bus;
};
  • VirtioPCIClass

VirtioPCIClass结构的定义也在include/hw/virtio/virtio-pci.h中,如下:

struct VirtioPCIClass {
    PCIDeviceClass parent_class;
    DeviceRealize parent_dc_realize;
    void (*realize)(VirtIOPCIProxy *vpci_dev, Error **errp);
};

再回顾一下virtio PCI代理设备类型的定义:

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,
};

由定义可知,virtio PCI代理设备的父设备是一个PCI设备(.parent        = TYPE_PCI_DEVICE,);virtio PCI代理设备的类型为VirtioPCIClass(.class_size    = sizeof(VirtioPCIClass),);virtio PCI代理设备的实例为VirtIOPCIProxy(.instance_size = sizeof(VirtIOPCIProxy),),注意这是一个抽象设备,所以并不能创建其实例,只能由其子类去创建。

QEMU(源码)中定义了所有virtio设备的PCI代理设备,如virtio balloon PCI设备、virtio scsi PCI设备、virtio crypto 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,
};

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

中移(苏州)软件技术有限公司面试问题与解答(4)—— virtio所创建的设备2-LMLPHP

所有的virtio设备都有一个共同的父类TYPE_VIRTIO_DEVICE。仍以virtio balloon设备为例,virtio balloon设备的定义在hw/virtio/virtio-balloon.c中,如下:

static const TypeInfo virtio_balloon_info = {
    .name = TYPE_VIRTIO_BALLOON,
    .parent = TYPE_VIRTIO_DEVICE,
    .instance_size = sizeof(VirtIOBalloon),
    .instance_init = virtio_balloon_instance_init,
    .class_init = virtio_balloon_class_init,
};

其中:

  • TYPE_VIRTIO_BALLOON

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

#define TYPE_VIRTIO_BALLOON "virtio-balloon-device"
OBJECT_DECLARE_SIMPLE_TYPE(VirtIOBalloon, VIRTIO_BALLOON)
  • TYPE_VIRTIO_DEVICE

TYPE_VIRTIO_DEVICE的定义在include/hw/virtio/virtio.h中,如下:

#define TYPE_VIRTIO_DEVICE "virtio-device"
OBJECT_DECLARE_TYPE(VirtIODevice, VirtioDeviceClass, VIRTIO_DEVICE)
  • VirtIOBalloon

VirtIOBalloon结构的定义在中,如下:

struct VirtIOBalloon {
    VirtIODevice parent_obj;
    VirtQueue *ivq, *dvq, *svq, *free_page_vq, *reporting_vq;
    uint32_t free_page_hint_status;
    uint32_t num_pages;
    uint32_t actual;
    uint32_t free_page_hint_cmd_id;
    uint64_t stats[VIRTIO_BALLOON_S_NR];
    VirtQueueElement *stats_vq_elem;
    size_t stats_vq_offset;
    QEMUTimer *stats_timer;
    IOThread *iothread;
    QEMUBH *free_page_bh;
    /*
     * Lock to synchronize threads to access the free page reporting related
     * fields (e.g. free_page_hint_status).
     */
    QemuMutex free_page_lock;
    QemuCond  free_page_cond;
    /*
     * Set to block iothread to continue reading free page hints as the VM is
     * stopped.
     */
    bool block_iothread;
    NotifierWithReturn free_page_hint_notify;
    int64_t stats_last_update;
    int64_t stats_poll_interval;
    uint32_t host_features;

    bool qemu_4_0_config_size;
    uint32_t poison_val;
};

再来回顾一下virtio balloon设备的定义:

static const TypeInfo virtio_balloon_info = {
    .name = TYPE_VIRTIO_BALLOON,
    .parent = TYPE_VIRTIO_DEVICE,
    .instance_size = sizeof(VirtIOBalloon),
    .instance_init = virtio_balloon_instance_init,
    .class_init = virtio_balloon_class_init,
};

由定义可知,virtio balloon设备的实例对象为VirtIOBalloon。具体的virtio设备、virtio PCI代理设备以及virtio公共设备的关系如下图所示:

中移(苏州)软件技术有限公司面试问题与解答(4)—— virtio所创建的设备2-LMLPHP

到此,就能够完全、完整地回答“virtio会创建哪些设备?”这一面试问题了:

virtio会创建两类设备:

  • virtio PCI控制设备(也可称作virtio PCI代理设备)

virtio PCI代理设备virtio-pci是一个抽象设备,并不能创建实例,只能由其子类去创建实例,如virtio balloon PCI设备virtio-balloon-pci-base、virtio scsi PCI设备virtio-scsi-pci-base、virtio crypto PCI设备virtio-crypto-pci等。

  • virtio设备

virtio 设备virtio-device是一个抽象设备,并不能创建实例,只能由其子类去创建实例,如virtio balloon设备virtio-balloon-device、virtio scsi设备virtio-scsi-device、virtio crypto设备virtio-crypto-device等。

01-26 18:16