当前位置:   article > 正文

virtio的qemu总线与设备模型_qemu pit set channel gate

qemu pit set channel gate

(很多内容是网上找的,+上我个人的一点理解,推荐大家去看 http://mnstory.net/2014/10/qemu-device-simulation 这篇文章)

qemu启动时,如果配置了相应virtio设备,会对guest的pci总线,virtio设备等进行模拟,先来看看qemu的设备模拟,那i8254/PIT为例(PIT的硬件规范略过,有兴趣的话可以参考 http://wiki.osdev.org/Programmable_Interval_Timer)

hw/timer/i8254.c定义了PIT设备的模拟,通过qom来定义设备对象模型,e.g.

  1. static const TypeInfo pit_info = {
  2. .name = TYPE_I8254,
  3. .parent = TYPE_PIT_COMMON,
  4. .instance_size = sizeof(PITCommonState),
  5. .class_init = pit_class_initfn,
  6. .class_size = sizeof(PITClass),
  7. };
  8. static void pit_register_types(void)
  9. {
  10. type_register_static(&pit_info);
  11. }
  12. type_init(pit_register_types)

type_init宏遵循qom的设备定义规范,实际调用的是module_init宏,这个宏被定义为__attribute__((constructor))属性,类似于cpp里面的构建函数,会在main函数之前被调用。

  1. typedef enum {
  2. MODULE_INIT_BLOCK,
  3. MODULE_INIT_MACHINE,
  4. MODULE_INIT_QAPI,
  5. MODULE_INIT_QOM,
  6. MODULE_INIT_MAX
  7. } module_init_type;
  8. #define type_init(function) module_init(function, MODULE_INIT_QOM)
  9. /* This should not be used directly. Use block_init etc. instead. */
  10. #define module_init(function, type) \
  11. static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
  12. { \
  13. register_module_init(function, type); \
  14. }
register_module_init的作用就是初始化type类型的ModuleEntry结构,并插入到init_type_list[type]的QLIST列表中。module_call_init则是对type的所有ModuleEntry,调用注册的初始化函数。
  1. typedef struct ModuleEntry
  2. {
  3. void (*init)(void);
  4. QTAILQ_ENTRY(ModuleEntry) node;
  5. module_init_type type;
  6. } ModuleEntry;
  7. typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList;
  8. static ModuleTypeList init_type_list[MODULE_INIT_MAX];
  9. static ModuleTypeList *find_type(module_init_type type)
  10. {
  11. ModuleTypeList *l;
  12. init_lists();
  13. l = &init_type_list[type];
  14. return l;
  15. }
  16. void module_call_init(module_init_type type)
  17. {
  18. ModuleTypeList *l;
  19. ModuleEntry *e;
  20. module_load(type);
  21. l = find_type(type);
  22. QTAILQ_FOREACH(e, l, node) {
  23. e->init();
  24. }
  25. }
现在回到PIT的设备注册函数pit_register_types,最终是通过传入的TypeInfo生成一个TypeImpl,并把这个TypeImpl插入到一个全局静态的GHashTable type_table中。
  1. static const TypeInfo pit_info = {
  2. .name = TYPE_I8254,
  3. .parent = TYPE_PIT_COMMON,
  4. .instance_size = sizeof(PITCommonState),
  5. .class_init = pit_class_initfn,
  6. .class_size = sizeof(PITClass),
  7. };
  8. static void pit_register_types(void)
  9. {
  10. type_register_static(&pit_info);
  11. }
  12. TypeImpl *type_register_static(const TypeInfo *info)
  13. {
  14. return type_register(info);
  15. }
  16. TypeImpl *type_register(const TypeInfo *info)
  17. {
  18. assert(info->parent);
  19. return type_register_internal(info);
  20. }
  21. static TypeImpl *type_register_internal(const TypeInfo *info)
  22. {
  23. TypeImpl *ti;
  24. ti = type_new(info);
  25. type_table_add(ti);
  26. return ti;
  27. }
  28. static void type_table_add(TypeImpl *ti)
  29. {
  30. assert(!enumerating_types);
  31. g_hash_table_insert(type_table_get(), (void *)ti->name, ti);
  32. }

从PITClass也可以看出,qom实际上把qemu的设备对象模型搞成了类似cpp的对象,通过父子类,继承,虚函数等一系列特性,让qemu设备对象形成了一个树形结构,树根就是object,同时TypeInfo, TypeImpl, Object, ObjectClass都可以支持这种树形结构,e.g. 

  1. static const TypeInfo pit_info = {
  2. .name = TYPE_I8254,
  3. .parent = TYPE_PIT_COMMON,
  4. .instance_size = sizeof(PITCommonState),
  5. .class_init = pit_class_initfn,
  6. .class_size = sizeof(PITClass),
  7. };
  8. static const TypeInfo pit_common_type = {
  9. .name = TYPE_PIT_COMMON,
  10. .parent = TYPE_ISA_DEVICE,
  11. .instance_size = sizeof(PITCommonState),
  12. .class_size = sizeof(PITCommonClass),
  13. .class_init = pit_common_class_init,
  14. .abstract = true,
  15. };
  16. static const TypeInfo isa_device_type_info = {
  17. .name = TYPE_ISA_DEVICE,
  18. .parent = TYPE_DEVICE,
  19. .instance_size = sizeof(ISADevice),
  20. .instance_init = isa_device_init,
  21. .abstract = true,
  22. .class_size = sizeof(ISADeviceClass),
  23. .class_init = isa_device_class_init,
  24. };
  25. static const TypeInfo device_type_info = {
  26. .name = TYPE_DEVICE,
  27. .parent = TYPE_OBJECT,
  28. .instance_size = sizeof(DeviceState),
  29. .instance_init = device_initfn,
  30. .instance_post_init = device_post_init,
  31. .instance_finalize = device_finalize,
  32. .class_base_init = device_class_base_init,
  33. .class_init = device_class_init,
  34. .abstract = true,
  35. .class_size = sizeof(DeviceClass),
  36. };
  37. static TypeInfo object_info = {
  38. .name = TYPE_OBJECT,
  39. .instance_size = sizeof(Object),
  40. .instance_init = object_instance_init,
  41. .abstract = true,
  42. };
ObjectClass这层同样体现了这种继承关系,同时ObjectClass也被用来实现多态,即Object对象的ObjectClass指针实际是一个虚函数的指针,e.g.
  1. typedef struct PITCommonClass {
  2. ISADeviceClass parent_class;
  3. void (*set_channel_gate)(PITCommonState *s, PITChannelState *sc, int val);
  4. void (*get_channel_info)(PITCommonState *s, PITChannelState *sc,
  5. PITChannelInfo *info);
  6. void (*pre_save)(PITCommonState *s);
  7. void (*post_load)(PITCommonState *s);
  8. } PITCommonClass;
  9. typedef struct ISADeviceClass {
  10. DeviceClass parent_class;
  11. } ISADeviceClass;
  12. typedef struct DeviceClass {
  13. /*< private >*/
  14. ObjectClass parent_class;
  15. /*< public >*/
  16. DECLARE_BITMAP(categories, DEVICE_CATEGORY_MAX);
  17. const char *fw_name;
  18. const char *desc;
  19. Property *props;
  20. ...
  21. } DeviceClass
  22. struct ObjectClass
  23. {
  24. /*< private >*/
  25. Type type;
  26. GSList *interfaces;
  27. const char *object_cast_cache[OBJECT_CLASS_CAST_CACHE];
  28. const char *class_cast_cache[OBJECT_CLASS_CAST_CACHE];
  29. ObjectUnparent *unparent;
  30. };
实际的设备对象继承关系如下:
  1. typedef struct PITCommonState {
  2. ISADevice dev;
  3. MemoryRegion ioports;
  4. uint32_t iobase;
  5. PITChannelState channels[3];
  6. } PITCommonState;
  7. struct ISADevice {
  8. /*< private >*/
  9. DeviceState parent_obj;
  10. /*< public >*/
  11. uint32_t isairq[2];
  12. int nirqs;
  13. int ioport_id;
  14. };
  15. struct DeviceState {
  16. /*< private >*/
  17. Object parent_obj;
  18. /*< public >*/
  19. const char *id;
  20. bool realized;
  21. bool pending_deleted_event;
  22. QemuOpts *opts;
  23. int hotplugged;
  24. BusState *parent_bus;
  25. QLIST_HEAD(, NamedGPIOList) gpios;
  26. QLIST_HEAD(, BusState) child_bus;
  27. int num_child_bus;
  28. int instance_id_alias;
  29. int alias_required_for_version;
  30. };
  31. struct Object
  32. {
  33. ObjectClass *class;
  34. ObjectFree *free;
  35. QTAILQ_HEAD(, ObjectProperty) properties;
  36. uint32_t ref;
  37. Object *parent;
  38. };
下图清晰的解释了多态是如何实现的,注意ObjectClass* 指针实际指向的是PITCommonClass


言归正传,现在来看下qemu端的virtio pci设备。在qemu里,virtio pci设备是所有其他virtio设备,e.g. virtio block, virtio net, virtio ballon的父类,其定义如下

  1. static void virtio_device_class_init(ObjectClass *klass, void *data)
  2. {
  3. /* Set the default value here. */
  4. DeviceClass *dc = DEVICE_CLASS(klass);
  5. dc->realize = virtio_device_realize;
  6. dc->unrealize = virtio_device_unrealize;
  7. dc->bus_type = TYPE_VIRTIO_BUS;
  8. }
  9. static const TypeInfo virtio_device_info = {
  10. .name = TYPE_VIRTIO_DEVICE,
  11. .parent = TYPE_DEVICE,
  12. .instance_size = sizeof(VirtIODevice),
  13. .class_init = virtio_device_class_init,
  14. .abstract = true,
  15. .class_size = sizeof(VirtioDeviceClass),
  16. };
  17. static void virtio_register_types(void)
  18. {
  19. type_register_static(&virtio_device_info);
  20. }
  21. type_init(virtio_register_types)

virtio设备的结构如下,其中最关键的是VirtQueue的指针,我理解VirtIODevice主要是封装了VirtQueue

  1. struct VirtIODevice
  2. {
  3. DeviceState parent_obj;
  4. const char *name;
  5. uint8_t status;
  6. uint8_t isr;
  7. uint16_t queue_sel;
  8. uint32_t guest_features;
  9. size_t config_len;
  10. void *config;
  11. uint16_t config_vector;
  12. int nvectors;
  13. VirtQueue *vq;
  14. uint16_t device_id;
  15. bool vm_running;
  16. VMChangeStateEntry *vmstate;
  17. char *bus_name;
  18. uint8_t device_endian;
  19. };
  20. typedef struct VirtioDeviceClass {
  21. /*< private >*/
  22. DeviceClass parent;
  23. /*< public >*/
  24. /* This is what a VirtioDevice must implement */
  25. DeviceRealize realize;
  26. DeviceUnrealize unrealize;
  27. uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features);
  28. uint32_t (*bad_features)(VirtIODevice *vdev);
  29. void (*set_features)(VirtIODevice *vdev, uint32_t val);
  30. void (*get_config)(VirtIODevice *vdev, uint8_t *config);
  31. void (*set_config)(VirtIODevice *vdev, const uint8_t *config);
  32. void (*reset)(VirtIODevice *vdev);
  33. void (*set_status)(VirtIODevice *vdev, uint8_t val);
  34. void (*set_version)(VirtIODevice *vdev, uint32_t val);
  35. /* Test and clear event pending status.
  36. * Should be called after unmask to avoid losing events.
  37. * If backend does not support masking,
  38. * must check in frontend instead.
  39. */
  40. bool (*guest_notifier_pending)(VirtIODevice *vdev, int n);
  41. /* Mask/unmask events from this vq. Any events reported
  42. * while masked will become pending.
  43. * If backend does not support masking,
  44. * must mask in frontend instead.
  45. */
  46. void (*guest_notifier_mask)(VirtIODevice *vdev, int n, bool mask);
  47. void (*save)(VirtIODevice *vdev, QEMUFile *f);
  48. int (*load)(VirtIODevice *vdev, QEMUFile *f, int version_id);
  49. } VirtioDeviceClass;

qemu内部也定义了VirtQueue和VRing的结构,注意这里需要保证前端virtio驱动和qemu的vring两者的ABI一致,即内存结构保证一致性,e.g.

  1. /* The standard layout for the ring is a continuous chunk of memory which looks
  2. * like this. We assume num is a power of 2.
  3. *
  4. * struct vring
  5. * {
  6. * // The actual descriptors (16 bytes each)
  7. * struct vring_desc desc[num];
  8. *
  9. * // A ring of available descriptor heads with free-running index.
  10. * __u16 avail_flags;
  11. * __u16 avail_idx;
  12. * __u16 available[num];
  13. * __u16 used_event_idx;
  14. *
  15. * // Padding to the next align boundary.
  16. * char pad[];
  17. *
  18. * // A ring of used descriptor heads with free-running index.
  19. * __u16 used_flags;
  20. * __u16 used_idx;
  21. * struct vring_used_elem used[num];
  22. * __u16 avail_event_idx;
  23. * };
注意qemu对于VRing的操作一般都只限于VRingUsed这部分以及avail_event_idx的更新
  1. #define VIRTIO_PCI_VRING_ALIGN 4096
  2. typedef struct VRingDesc
  3. {
  4. uint64_t addr;
  5. uint32_t len;
  6. uint16_t flags;
  7. uint16_t next;
  8. } VRingDesc;
  9. typedef struct VRingAvail
  10. {
  11. uint16_t flags;
  12. uint16_t idx;
  13. uint16_t ring[0];
  14. } VRingAvail;
  15. typedef struct VRingUsedElem
  16. {
  17. uint32_t id;
  18. uint32_t len;
  19. } VRingUsedElem;
  20. typedef struct VRingUsed
  21. {
  22. uint16_t flags;
  23. uint16_t idx;
  24. VRingUsedElem ring[0];
  25. } VRingUsed;
  26. typedef struct VRing
  27. {
  28. unsigned int num;
  29. unsigned int align;
  30. hwaddr desc;
  31. hwaddr avail;
  32. hwaddr used;
  33. } VRing;
  34. struct VirtQueue
  35. {
  36. VRing vring;
  37. hwaddr pa;
  38. uint16_t last_avail_idx;
  39. /* Last used index value we have signalled on */
  40. uint16_t signalled_used;
  41. /* Last used index value we have signalled on */
  42. bool signalled_used_valid;
  43. /* Notification enabled? */
  44. bool notification; /* 是否使能notification,只有enable了event_idx才有效 */
  45. uint16_t queue_index;
  46. int inuse;
  47. uint16_t vector;
  48. void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq);
  49. VirtIODevice *vdev;
  50. EventNotifier guest_notifier; /* guest notifier fd,目前只有vhost使用 */
  51. EventNotifier host_notifier; /* host notifier fd, 目前只有vhost使用 */
  52. };
qemu的VRing通过查看avail_event_idx来判断是否有新的avail buffer到达。关于VirtQueue和VRing的部分后面再详细阐述。


除了TYPE_DEVICE -> TYPE_VIRTIO_DEVICE -> TYPE_VIRTIO_XXX_DEVICE这条继承关系之外,qemu的virtio设备同时也是PCI设备,因此还存在有TYPE_DEVICE -> TYPE_PCI_DEVICE -> TYPE_VIRTIO_PCI -> TYPE_VIRTIO_XXX_PCI的继承关系,同时总线上也存在TYPE_BUS -> TYPE_VIRTIO_BUS -> TYPE_VIRTIO_PCI_BUS

virtio bus定义如下

  1. static const TypeInfo virtio_bus_info = {
  2. .name = TYPE_VIRTIO_BUS,
  3. .parent = TYPE_BUS,
  4. .instance_size = sizeof(VirtioBusState),
  5. .abstract = true,
  6. .class_size = sizeof(VirtioBusClass),
  7. .class_init = virtio_bus_class_init
  8. };
  9. static void virtio_register_types(void)
  10. {
  11. type_register_static(&virtio_bus_info);
  12. }
  13. type_init(virtio_register_types)

总线设备virtio-bus,对应类型是VirtioBusClass。设备定义了多个虚函数的接口,具体实现分为TYPE_VIRTIO_MMIO_BUS和TYPE_VIRTIO_PCI_BUS两种,对应设备是virtio-pci-bus和virtio-mmio-bus

  1. static const TypeInfo virtio_pci_bus_info = {
  2. .name = TYPE_VIRTIO_PCI_BUS,
  3. .parent = TYPE_VIRTIO_BUS,
  4. .instance_size = sizeof(VirtioPCIBusState),
  5. .class_init = virtio_pci_bus_class_init,
  6. };
  7. static void virtio_pci_bus_class_init(ObjectClass *klass, void *data)
  8. {
  9. BusClass *bus_class = BUS_CLASS(klass);
  10. VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
  11. bus_class->max_dev = 1;
  12. k->notify = virtio_pci_notify;
  13. k->save_config = virtio_pci_save_config;
  14. k->load_config = virtio_pci_load_config;
  15. k->save_queue = virtio_pci_save_queue;
  16. k->load_queue = virtio_pci_load_queue;
  17. k->get_features = virtio_pci_get_features;
  18. k->query_guest_notifiers = virtio_pci_query_guest_notifiers;
  19. k->set_host_notifier = virtio_pci_set_host_notifier;
  20. k->start_host_notifier = virtio_pci_start_host_notifier;
  21. k->set_guest_notifiers = virtio_pci_set_guest_notifiers;
  22. k->vmstate_change = virtio_pci_vmstate_change;
  23. k->device_plugged = virtio_pci_device_plugged;
  24. k->device_unplugged = virtio_pci_device_unplugged;
  25. }
另一个相关的设备是virtio-pci,代表了挂载virtio pci总线上的pci设备模型,定义如下
  1. static const TypeInfo virtio_pci_info = {
  2. .name = TYPE_VIRTIO_PCI,
  3. .parent = TYPE_PCI_DEVICE,
  4. .instance_size = sizeof(VirtIOPCIProxy),
  5. .class_init = virtio_pci_class_init,
  6. .class_size = sizeof(VirtioPCIClass),
  7. .abstract = true,
  8. };
  9. struct VirtIOPCIProxy {
  10. PCIDevice pci_dev;
  11. MemoryRegion bar;
  12. uint32_t flags;
  13. uint32_t class_code;
  14. uint32_t nvectors;
  15. uint32_t host_features;
  16. bool ioeventfd_disabled;
  17. bool ioeventfd_started;
  18. VirtIOIRQFD *vector_irqfd;
  19. int nvqs_with_notifiers;
  20. VirtioBusState bus;
  21. };
  22. typedef struct VirtioPCIClass {
  23. PCIDeviceClass parent_class;
  24. int (*init)(VirtIOPCIProxy *vpci_dev);
  25. } VirtioPCIClass;

VirtioPCIBusState除了虚函数的实现,其他地方和VirtioBusState完全一致,同样VirtioBusClass和VirtioPCIBusClass也是完全一致。从代码实现上看,和内核的virtio pci设备基本类似。

virtio_pci_notify用于通知guest中断,根据pci设备是否enable msix

  1. static void virtio_pci_notify(DeviceState *d, uint16_t vector)
  2. {
  3. VirtIOPCIProxy *proxy = to_virtio_pci_proxy_fast(d);
  4. if (msix_enabled(&proxy->pci_dev))
  5. msix_notify(&proxy->pci_dev, vector); /* msix_notify通过写pci配置空间来传递中断 */
  6. else {
  7. VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
  8. pci_set_irq(&proxy->pci_dev, vdev->isr & 1); /* 通过irq传递中断 */
  9. }
  10. }
virtio_pci_get_features用于获取pci配置空间的HOST_FEATURES
  1. static unsigned virtio_pci_get_features(DeviceState *d)
  2. {
  3. VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
  4. return proxy->host_features;
  5. }
virtio_pci_save_config/virtio_pci_load_config,virtio_pci_save_queue/virtio_pci_load_queue用于save/load pci的配置信息和队列信息

virtio_pci_device_plugged当有virtio-pci设备被virtio-pci-bus启动时被调用

  1. static void virtio_pci_device_plugged(DeviceState *d)
  2. {
  3. VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
  4. VirtioBusState *bus = &proxy->bus;
  5. uint8_t *config;
  6. uint32_t size;
  7. config = proxy->pci_dev.config;
  8. if (proxy->class_code) {
  9. pci_config_set_class(config, proxy->class_code); /* 设置pci配置空间的class code */
  10. }
  11. pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID,
  12. pci_get_word(config + PCI_VENDOR_ID)); /* 设置pci配置空间的VENDOR_ID */
  13. pci_set_word(config + PCI_SUBSYSTEM_ID, virtio_bus_get_vdev_id(bus)); /* 设置pci配置空间的SUBSYSTEM_ID */
  14. config[PCI_INTERRUPT_PIN] = 1;
  15. if (proxy->nvectors &&
  16. msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors, 1)) { /* 初始化virtio bar的msix配置 */
  17. error_report("unable to init msix vectors to %" PRIu32,
  18. proxy->nvectors);
  19. proxy->nvectors = 0;
  20. }
  21. proxy->pci_dev.config_write = virtio_write_config;
  22. size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev)
  23. + virtio_bus_get_vdev_config_len(bus);
  24. if (size & (size - 1)) {
  25. size = 1 << qemu_fls(size);
  26. }
  27. memory_region_init_io(&proxy->bar, OBJECT(proxy), &virtio_pci_config_ops,
  28. proxy, "virtio-pci", size); /* 初始化bar0的内存为virtio的配置空间 */
  29. pci_register_bar(&proxy->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO,
  30. &proxy->bar); /* 注册virtio pci的bar0 */
  31. if (!kvm_has_many_ioeventfds()) {
  32. proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
  33. }
  34. proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY;
  35. proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE;
  36. proxy->host_features = virtio_bus_get_vdev_features(bus,
  37. proxy->host_features);
  38. }
virtio_pci_device_unplugged当设备停用并从总线上删除时调用,对vhost而言,ioeventfd用于guest通过pci通知host,irqfd用于host把中断注入guest。这里调用了virtio_pci_stop_ioeventfd,该函数最终通过virtio_pci_set_host_notifier_internal来配置ioveventfd
  1. static void virtio_pci_device_unplugged(DeviceState *d)
  2. {
  3. VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
  4. virtio_pci_stop_ioeventfd(proxy);
  5. }
VirtioBusClass需要支持guest notifier和host notifier的相关操作,e.g.
  1. static int virtio_pci_set_host_notifier(DeviceState *d, int n, bool assign)
  2. {
  3. VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
  4. /* Stop using ioeventfd for virtqueue kick if the device starts using host
  5. * notifiers. This makes it easy to avoid stepping on each others' toes.
  6. */
  7. proxy->ioeventfd_disabled = assign; /* assign为true,说明设置host notifier;assign为false,说明清理host notifier */
  8. if (assign) { /* 如果是assign新的fd,需要先清理掉老的fd */
  9. virtio_pci_stop_ioeventfd(proxy);
  10. }
  11. /* We don't need to start here: it's not needed because backend
  12. * currently only stops on status change away from ok,
  13. * reset, vmstop and such. If we do add code to start here,
  14. * need to check vmstate, device state etc. */
  15. return virtio_pci_set_host_notifier_internal(proxy, n, assign, false);
  16. }
  17. static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
  18. int n, bool assign, bool set_handler)
  19. {
  20. VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
  21. VirtQueue *vq = virtio_get_queue(vdev, n);
  22. EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
  23. int r = 0;
  24. if (assign) {
  25. r = event_notifier_init(notifier, 1);
  26. if (r < 0) {
  27. error_report("%s: unable to init event notifier: %d",
  28. __func__, r);
  29. return r;
  30. }
  31. virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler); /* set_host_notifier的时候set_handler设置为false */
  32. /* start_ioeventfd的时候设置为true */
  33. memory_region_add_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2,
  34. true, n, notifier); /* 把ioport/iomem对应的memregion和fd关联起来 */
  35. } else {
  36. memory_region_del_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2,
  37. true, n, notifier);
  38. virtio_queue_set_host_notifier_fd_handler(vq, false, false);
  39. event_notifier_cleanup(notifier);
  40. }
  41. return r;
  42. }
上述的host notifier,实际对应VirtQueue EventNotifier的一个read fd,通常叫做ioevent_fd,可以通过virtio_pci_start_ioeventfd,virtio_pci_stop_ioeventfd来控制;而guest notifier通常对应于VirtIOPCIProxy的vector_irqfd,通常叫做irqfd,e.g.
  1. static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign)
  2. {
  3. VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
  4. VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
  5. VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
  6. int r, n;
  7. bool with_irqfd = msix_enabled(&proxy->pci_dev) &&
  8. kvm_msi_via_irqfd_enabled();
  9. nvqs = MIN(nvqs, VIRTIO_PCI_QUEUE_MAX);
  10. /* When deassigning, pass a consistent nvqs value
  11. * to avoid leaking notifiers.
  12. */
  13. assert(assign || nvqs == proxy->nvqs_with_notifiers);
  14. proxy->nvqs_with_notifiers = nvqs;
  15. /* Must unset vector notifier while guest notifier is still assigned */
  16. if ((proxy->vector_irqfd || k->guest_notifier_mask) && !assign) {
  17. msix_unset_vector_notifiers(&proxy->pci_dev); /* 修改pci配置空间清理msix vector */
  18. if (proxy->vector_irqfd) {
  19. kvm_virtio_pci_vector_release(proxy, nvqs);
  20. g_free(proxy->vector_irqfd);
  21. proxy->vector_irqfd = NULL;
  22. }
  23. }
  24. for (n = 0; n < nvqs; n++) {
  25. if (!virtio_queue_get_num(vdev, n)) {
  26. break;
  27. }
  28. r = virtio_pci_set_guest_notifier(d, n, assign, with_irqfd); /* 对每个VirtQueue设置irqfd */
  29. if (r < 0) {
  30. goto assign_error;
  31. }
  32. }
  33. /* Must set vector notifier after guest notifier has been assigned */
  34. if ((with_irqfd || k->guest_notifier_mask) && assign) {
  35. if (with_irqfd) {
  36. proxy->vector_irqfd =
  37. g_malloc0(sizeof(*proxy->vector_irqfd) *
  38. msix_nr_vectors_allocated(&proxy->pci_dev));
  39. r = kvm_virtio_pci_vector_use(proxy, nvqs);
  40. if (r < 0) {
  41. goto assign_error;
  42. }
  43. }
  44. r = msix_set_vector_notifiers(&proxy->pci_dev,
  45. virtio_pci_vector_unmask,
  46. virtio_pci_vector_mask,
  47. virtio_pci_vector_poll); /* 同样配置pci空间增加msix vector */
  48. if (r < 0) {
  49. goto notifiers_error;
  50. }
  51. }
  52. return 0;
  53. notifiers_error:
  54. if (with_irqfd) {
  55. assert(assign);
  56. kvm_virtio_pci_vector_release(proxy, nvqs);
  57. }
  58. assign_error:
  59. /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */
  60. assert(assign);
  61. while (--n >= 0) {
  62. virtio_pci_set_guest_notifier(d, n, !assign, with_irqfd);
  63. }
  64. return r;
  65. }

virtio的pci配置空间的读写(bar0的IO空间)操作,用于pci配置空间的offset + size的读写操作,并分为单字节,双字节,四字节三种。对于virtio配置空间20字节之前的读写通过io port完成,20字节之后的通过mmio完成。

  1. static const MemoryRegionOps virtio_pci_config_ops = {
  2. .read = virtio_pci_config_read,
  3. .write = virtio_pci_config_write,
  4. .impl = {
  5. .min_access_size = 1,
  6. .max_access_size = 4,
  7. },
  8. .endianness = DEVICE_LITTLE_ENDIAN,
  9. };





声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/885036
推荐阅读
相关标签
  

闽ICP备14008679号