virtio-scsi的类,对象和实例

1.在qemu-kvm中实现一个对象需要三种结构:
1).类型type  将类class和实例instance关联起来
2).类class  定义这一类对象的通用操作
3).实例instance  定义一个对象的数据

注:类型定义中不一定非要包含完整的类Class和实例instance的定义,如果没有新增的成员,则完全可以从父类型继承过来,qemu-kvm初始化的代码会自动找到父类型并使用父类型的定义。

2.初始化
type是一个变量,并且会把自己注册到系统类型表中,如果哪里有使用这个type,则会到系统类型表中找到它。
1).type是一个变量,在定义时已经把静态成员初始化完毕,

2).调用type_initialize函数,动态初始化type的动态对应的class
ti->class_size = type_class_get_size(ti);
ti->instance_size = type_object_get_size(ti); /*指定instance的size*/
ti->class = g_malloc0(ti->class_size);
if (ti->class_init) {
    ti->class_init(ti->class, ti->class_data);
}

3).调用object_new_with_type函数,初始化type对应的instance
obj = g_malloc(type->instance_size);
object_init_with_type(obj, type);
->  if (ti->instance_init) {
        ti->instance_init(obj);
    }

3.继承关系:
1).type的继承关系通过parent指针实现:通过parent指针找到父类型,进而使用父类型提供的资源。

2).Class和instance的继承关系则是通过包含父对象作为子对象第一个成员来实现。

总结:
qemu-kvm根据启动参数初始化一切对象的时候,
type变量的定义是原点,
找到这个对象的type是起点,
调用type_initialize是初始化class,
调用object_new_with_type初始化instance,
然后。。。

4.本文中:
type   Class  instance
virtio-scsi-pci  PCIDeviceClass VirtIOPCIProxy

1).type:virtio-scsi-pci
virtio-scsi-pci -> pci_device_type_info -> device_type_info -> object_info

static TypeInfo virtio_scsi_info = {
    .name          = "virtio-scsi-pci",
    .parent        = TYPE_PCI_DEVICE,
    .instance_size = sizeof(VirtIOPCIProxy),
    .class_init    = virtio_scsi_class_init,
};

static TypeInfo pci_device_type_info = {
    .name = TYPE_PCI_DEVICE,
    .parent = TYPE_DEVICE,
    .instance_size = sizeof(PCIDevice),
    .abstract = true,
    .class_size = sizeof(PCIDeviceClass),
    .class_init = pci_device_class_init,
};

static TypeInfo device_type_info = {
    .name = TYPE_DEVICE,
    .parent = TYPE_OBJECT,
    .instance_size = sizeof(DeviceState),
    .instance_init = device_initfn,
    .instance_finalize = device_finalize,
    .class_base_init = device_class_base_init,
    .abstract = true,
    .class_size = sizeof(DeviceClass),
};

static TypeInfo object_info = {
    .name = TYPE_OBJECT,
    .instance_size = sizeof(Object),
    .instance_init = object_instance_init,
    .abstract = true,
};


2).Class:PCIDeviceClass
继承关系:PCIDeviceClass -> DeviceClass -> ObjectClass

typedef struct PCIDeviceClass {
    DeviceClass parent_class;

    int (*init)(PCIDevice *dev);
    PCIUnregisterFunc *exit;
    PCIConfigReadFunc *config_read;
    PCIConfigWriteFunc *config_write;

    uint16_t vendor_id;
    uint16_t device_id;
    uint8_t revision;
    uint16_t class_id;
    uint16_t subsystem_vendor_id;       /* only for header type = 0 */
    uint16_t subsystem_id;              /* only for header type = 0 */

    /*
     * pci-to-pci bridge or normal device.
     * This doesn't mean pci host switch.
     * When card bus bridge is supported, this would be enhanced.
     */
    int is_bridge;

    /* pcie stuff */
    int is_express;   /* is this device pci express? */

    /* device isn't hot-pluggable */
    int no_hotplug;

    /* rom bar */
    const char *romfile;
} PCIDeviceClass;

typedef struct DeviceClass {
    ObjectClass parent_class;

    const char *fw_name;
    const char *desc;
    Property *props;
    int no_user;

    /* callbacks */
    void (*reset)(DeviceState *dev);

    /* device state */
    const VMStateDescription *vmsd;

    /* Private to qdev / bus.  */
    qdev_initfn init;
    qdev_event unplug;
    qdev_event exit;
    const char *bus_type;
} DeviceClass;

struct ObjectClass
{
    /*< private >*/
    Type type;
};

3).instance:VirtIOPCIProxy
继承关系:VirtIOPCIProxy -> PCIDevice -> DeviceState -> Object
typedef struct {
    PCIDevice pci_dev;
    VirtIODevice *vdev;
    MemoryRegion bar;
    uint32_t flags;
    uint32_t class_code;
    uint32_t nvectors;
    VirtIOBlkConf blk;
    NICConf nic;
    uint32_t host_features;
#ifdef CONFIG_LINUX
    V9fsConf fsconf;
#endif
    virtio_serial_conf serial;
    virtio_net_conf net;
    VirtIOSCSIConf scsi;
    bool ioeventfd_disabled;
    bool ioeventfd_started;
    VirtIOIRQFD *vector_irqfd;
} VirtIOPCIProxy;


struct PCIDevice {
    DeviceState qdev;

    /* PCI config space */
    uint8_t *config;

    /* Used to enable config checks on load. Note that writable bits are
     * never checked even if set in cmask. */
    uint8_t *cmask;

    /* Used to implement R/W bytes */
    uint8_t *wmask;

    /* Used to implement RW1C(Write 1 to Clear) bytes */
    uint8_t *w1cmask;

    /* Used to allocate config space for capabilities. */
    uint8_t *used;

    /* the following fields are read only */
    PCIBus *bus;
    int32_t devfn;
    char name[64];
    PCIIORegion io_regions[PCI_NUM_REGIONS];
    DMAContext *dma;

    /* do not access the following fields */
    PCIConfigReadFunc *config_read;
    PCIConfigWriteFunc *config_write;

    /* IRQ objects for the INTA-INTD pins.  */
    qemu_irq *irq;

    /* Current IRQ levels.  Used internally by the generic PCI code.  */
    uint8_t irq_state;

    /* Capability bits */
    uint32_t cap_present;

    /* Offset of MSI-X capability in config space */
    uint8_t msix_cap;

    /* MSI-X entries */
    int msix_entries_nr;

    /* Space to store MSIX table & pending bit array */
    uint8_t *msix_table;
    uint8_t *msix_pba;
    /* MemoryRegion container for msix exclusive BAR setup */
    MemoryRegion msix_exclusive_bar;
    /* Memory Regions for MSIX table and pending bit entries. */
    MemoryRegion msix_table_mmio;
    MemoryRegion msix_pba_mmio;
    /* Reference-count for entries actually in use by driver. */
    unsigned *msix_entry_used;
    /* MSIX function mask set or MSIX disabled */
    bool msix_function_masked;
    /* Version id needed for VMState */
    int32_t version_id;

    /* Offset of MSI capability in config space */
    uint8_t msi_cap;

    /* PCI Express */
    PCIExpressDevice exp;

    /* SHPC */
    SHPCDevice *shpc;

    /* Location of option rom */
    char *romfile;
    bool has_rom;
    MemoryRegion rom;
    uint32_t rom_bar;

    /* INTx routing notifier */
    PCIINTxRoutingNotifier intx_routing_notifier;

    /* MSI-X notifiers */
    MSIVectorUseNotifier msix_vector_use_notifier;
    MSIVectorReleaseNotifier msix_vector_release_notifier;
};

/* This structure should not be accessed directly.  We declare it here
   so that it can be embedded in individual device state structures.  */
struct DeviceState {
    Object parent_obj;

    const char *id;
    enum DevState state;
    QemuOpts *opts;
    int hotplugged;
    BusState *parent_bus;
    int num_gpio_out;
    qemu_irq *gpio_out;
    int num_gpio_in;
    qemu_irq *gpio_in;
    QLIST_HEAD(, BusState) child_bus;
    int num_child_bus;
    int instance_id_alias;
    int alias_required_for_version;
};

/**
 * Object:
 *
 * The base for all objects.  The first member of this object is a pointer to
 * a #ObjectClass.  Since C guarantees that the first member of a structure
 * always begins at byte 0 of that structure, as long as any sub-object places
 * its parent as the first member, we can cast directly to a #Object.
 *
 * As a result, #Object contains a reference to the objects type as its
 * first member.  This allows identification of the real type of the object at
 * run time.
 *
 * #Object also contains a list of #Interfaces that this object
 * implements.
 */
struct Object
{
    /*< private >*/
    ObjectClass *class;
    GSList *interfaces;
    QTAILQ_HEAD(, ObjectProperty) properties;
    uint32_t ref;
    Object *parent;
};


----------------------------------------------------------------------
1.动态创建virtio-scsi-pci的PCIDeviceClass类型变量
在type_initialize函数,为virtio-scsi-pci的class成员(一个指向ObjectClass的指针)分配空间,空间大小就是PCIDeviceClass:

1).让virtio-scsi-pci的class成员指向PCIDeviceClass的第一个成员DeviceClass的第一个成员ObjectClass。

这样virtio-scsi-pci通过class成员就可以找到PCIDeviceClass。

2).PCIDeviceClass的第一个成员DeviceClass的第一个成员ObjectClass只有一个成员type(一个指向TypeImpl的指针),在type_initialize函数中让type指向virtio-scsi-pci。

这样PCIDeviceClass通过成员DeviceClass的第一个成员ObjectClass的type指针就可以找到virtio-scsi-pci。

3).动态分配和初始化PCIDeviceClass变量过程:
DeviceState *qdev_device_add(QemuOpts *opts)
{   ...
    /* find driver */
    obj = object_class_by_name(driver);
    ...
}

ObjectClass *object_class_by_name(const char *typename)
{
    /* 通过名字“virtio-scsi-pci”返回它的地址 */
    TypeImpl *type = type_get_by_name(typename);

    if (!type) {
        return NULL;
    }
    /* type初始化,即动态创建PCIDeviceClass变量 */
    /* 初始化一个类型,动态为这个类型对应的Class(如果该类型没有对应的Class,则父类型对应的Class)分配空间,并按顺序调用父类型的class_base_init函数,interface初始化函数,最后调用自己类型的class_init函数。 */
    type_initialize(type);

    return type->class;
}

static void type_initialize(TypeImpl *ti)
{
    TypeImpl *parent;
    int i;
    /* the ti here is virtio-scsi-pci which is defined in virtio-pci.c */
    if (ti->class) {
        return;
    }
    /* 从ti->class_size和ti->instance_size,我们可以看出,virtio-scsi类型同其他两件事有关联,一个是Class PCIDeviceClass,一个是Object VirtIOPCIProxy */
    /* for virtio-scsi-pci, it doesn't have defined the class_size
       while his parent "pci" have ".class_size = sizeof(PCIDeviceClass)" */
    ti->class_size = type_class_get_size(ti);
    /* for virtio-scsi, it's instance_size  is ".instance_size = sizeof(VirtIOPCIProxy)"  */
    ti->instance_size = type_object_get_size(ti);
    /* so we alloc the class as  sizeof(PCIDeviceClass) */
    ti->class = g_malloc0(ti->class_size);

    parent = type_get_parent(ti);
    if (parent) {
        type_initialize(parent);

        g_assert(parent->class_size <= ti->class_size);
 /* for the first element of child's struct class is parent's struct class (the first element
      of PCIDeviceClass is DeviceClass), then we copy parent's class's content to
      child's first element to archive the inherit.
  */
        memcpy(ti->class, parent->class, parent->class_size);
    }
    /* ti->class is TypeImpl's element of "ObjectClass *class;" &&
         ObjectClass is the first element of DeviceClass &&
         DeviceClass is the first element of PCIDeviceClass.
        
         The space we allocted in up code is the sizeof(PCIDeviceClass).
         So the assigment of "ti->class->type = ti;" below is to assign to the element
         of ObjectClass which is inseted in DeviceClass which is inseted in PCIDeviceClass.

         ObjectClass only have one element which is just a pointer of TypeImpl *, so here
         the assignment means the PCIDeviceClass 's first elemen DeviceClass's
         first element ObjectClass's element of type is set to point to the
         "TypeImpl virtio-scsi-pci".
     */
    ti->class->type = ti;

    while (parent) {
        if (parent->class_base_init) {
            parent->class_base_init(ti->class, ti->class_data);
        }
        parent = type_get_parent(parent);
    }

    for (i = 0; i < ti->num_interfaces; i++) {
        type_class_interface_init(ti, &ti->interfaces[i]);
    }
    /* 调用 virtio_scsi_class_init 来初始化PCIDeviceClass变量 */
    if (ti->class_init) {
        ti->class_init(ti->class, ti->class_data);
    }
}

static void virtio_scsi_class_init(ObjectClass *klass, void *data)
{
    /* 1. We alloc the space which size is "sizeof(PCIDeviceClass)" during function
             of "type_initialize".
         2. The kclass is a pointer which pointed to a ObjectClass.
         3. Relationship between ObjectClass, DeviceClass and PCIDeviceClass.
             The ObjectClass is the first element of DeviceClass.
             The DeviceClass is the first element of PCIDeviceClass.
         4. So we can get the pointer to DeviceClass by DEVICE_CLASS(klass) and
             the pointer to PCIDeviceClass by PCI_DEVICE_CLASS.
    */
    DeviceClass *dc = DEVICE_CLASS(klass);
    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);

    k->init = virtio_scsi_init_pci;
    k->exit = virtio_scsi_exit_pci;
    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
    k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI;
    k->revision = 0x00;
    k->class_id = PCI_CLASS_STORAGE_SCSI;
    dc->reset = virtio_pci_reset;
    dc->props = virtio_scsi_properties;
}


使用DEVICE_CLASS和PCI_DEVICE_CLASS宏将ObjectClass 指针转化为DeviceClass 或PCIDeviceClass类型指针的实现函数。
原理:ObjectClass *class指针实际就是整个已分配空间的第一个地址,可以转化为DeviceClass,也可以转化为PCIDeviceClass。
ObjectClass *object_class_dynamic_cast(ObjectClass *class,
                                       const char *typename)
{
    TypeImpl *target_type = type_get_by_name(typename);
    TypeImpl *type = class->type; //the addr of virtio-scsi-pci

    while (type) {
        if (type == target_type) {
            return class;
        }
        /* type is virtio-scsi-pci at begin, if it is not equal to
           target_type( type name may be "device" or "pci-device"),
           get the parent of virtio-scsi-pci(his parent is
           "pci-device") circularly until it equal to target_type.
        */
        type = type_get_parent(type);
    }

    return NULL;
}


2.动态创建virtio-scsi-pci的VirtIOPCIProxy类型变量
DeviceState *qdev_device_add(QemuOpts *opts)
{   ...
    /* create device, set properties */
    qdev = DEVICE(object_new(driver));
    ...
}

Object *object_new(const char *typename)
{
    /* typename is virtio-scsi-pci */
    TypeImpl *ti = type_get_by_name(typename);

    return object_new_with_type(ti);
}

/*
The first element of VirtIOPCIProxy is "PCIDevice pci_dev;" &&
The first element of PCIDevice is "DeviceState qdev;" &&
The first element of DeviceState is "Object parent_obj;"

*/
Object *object_new_with_type(Type type)
{
    Object *obj;

    g_assert(type != NULL);
    type_initialize(type);
    /* for virtio-scsi-pci ".instance_size = sizeof(VirtIOPCIProxy)" */
    obj = g_malloc(type->instance_size);
    object_initialize_with_type(obj, type);
    object_ref(obj);

    return obj;
}

void object_initialize_with_type(void *data, TypeImpl *type)
{
    Object *obj = data;

    g_assert(type != NULL);
    type_initialize(type);

    g_assert(type->instance_size >= sizeof(Object));
    g_assert(type->abstract == false);

    memset(obj, 0, type->instance_size);
    /* 对于virtio-scsi-pci, type->class指向PCIDeviceClass类型的变量.
       而这里VirtIOPCIProxy的第一个成员PCIDevice的第一个成员DeviceState的第一个成员Object的成员class指向了virtio-scsi-pci对应的PCIDeviceClass类型的变量。就是说VirtIOPCIProxy可以通过它子成员class可以找到PCIDeviceClass。
    */
    obj->class = type->class;
    QTAILQ_INIT(&obj->properties);
    object_init_with_type(obj, type);
}

/* 通过递归调用实现面向对象中先调用父对象的初始化函数,再调用子对象的
初始化函数的功能,进而实现多层次的初始化,从而使不同对象调用同一个父对象的初始化函数,增加代码重用。
 */
static void object_init_with_type(Object *obj, TypeImpl *ti)
{
    int i;
    /* 利用object_init_with_type函数中的递归,达到按继承关系(virtio-scsi-pci -> pci_device_type_info -> device_type_info -> object_info)先调用父对象中定义的interface和instance_init初始化函数的目的。
       即,从object_info开始,按顺序先后执行object_info、device_type_info、pci_device_type_info和virtio-scsi-pci中定义的interface和instance_init初始化函数。
       通过调用不同层级的instance_init初始化函数,依次初始化父对象中的成员,父对象的instance_init初始化函数初始化父对象的成员,子对象的instance_init初始化函数初始化子对象新增的成员。
     */
    if (type_has_parent(ti)) {
        object_init_with_type(obj, type_get_parent(ti));
    }

    for (i = 0; i < ti->num_interfaces; i++) {
        object_interface_init(obj, &ti->interfaces[i]);
    }

    if (ti->instance_init) {
        ti->instance_init(obj);
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值