1、 virtio_bus结构

struct bus_type是基于总线驱动设备模型的虚拟总线,定义新的bus,就是填充该结构。virtio_bus定义在drivers/virtio/virtio.c中,具体如下,

注册:

补充注册的早不早:

virtio_bus以core_initcall的方式被注册,该方式注册的启动顺序优先级很高(作为对比,module_init最终对应的是device_initcall),在使用中要注意不同组件的启动顺序。

补充2:virtio_dev_match函数

virtio驱动的match涉及到virtio_device_id结构,在virtio_device结构中包含该结构;在virtio_driver中则是包含该驱动支持的virtio_device_id列表

具体的match流程如下,

可见virtio驱动的match函数先匹配device字段,后匹配vendor字段,二者都满足条件时match成功

补充3:从virtio_dev_match的流程可以看出,virtio_driver中的id_table必须以id->device = 0结尾,以便结束循环

2 virtio_device结构

struct virtio_device定义在include/linux/virtio.h中,具体如下,

​

2.1 struct virtio_device_id id

​

其中的device成员标识了当前virtio_device的用途,virt-net是其中的一种,

​

2.2 const struct virtio_config_ops *config

​

virtio_config_ops操作集中的函数主要与virtio_device的配置相关,主要有如下2类操作,

  • 实例化 / 反实例化virtqueue,其中要特别注意find_vqs函数,该函数用于实例化virtio_device所持有的virtqueue
  • . 获取 / 设置virtio_device的属性与状态,相关属性均在虚拟机虚拟出的PCI配置空间

2.3 struct list_head vqs

virtio_device持有的virtqueue链表,virtio-net中建立了2条virtqueue

2.4 u64 features

irtio_driver & virtio_device同时支持的通信特性,也就是前后端最终协商的通信特性

3 virtio_driver结构

struct virtio_driver定义在include/linux/virtio.h中,具体如下,

​

3.1 const struct virtio_device_id *id_table

对应virtio_device结构中的id成员,virtio_device中标识的是当前device的id属性;而virtio_driver中的id_table则是当前driver支持的所有id列表

3.2 const unsigned int *feature_table & unsigned int feature_table_size

feature列表包含了当前driver支持的所有virtio传输属性,feature_table_size则说明属性数组的元素个数

4 virtqueue结构

struct virtqueue定义在include/linux/virtio.h中,具体如下,

vdev 是引用了 virtio_device 的实例。

5 vring结构

struct vring 定义在include/uapi/linux/virtio_ring.h中,具体如下,

一定要结合一个后端驱动进行分析,可以对照rpmsg-lite分析

可以就分析rpmsg-lite对virtqueue的操作部分,不用上升到rpmsg协议的部分

说明1:vring的三个构成区域

  • Destcriptor Table:描述内存buffer,主要包括addr & len等信息
  • Avail Ring:用于前端驱动(Guest)通知后端驱动(Host)有可用的描述符

e.g. 前端驱动有一个报文需要发送,需要将其加入Avail Ring,之后通知后端驱动读取

  • Used Ring:用于后端驱动(Host)通知前端驱动(Guest)有可用的描述符,或者是后端驱动已将前端驱动提供的描述符使用完毕

e.g. 后端驱动有一个报文需要发送,需要将其加入Used Ring,之后通知前端驱动读取

可见avail & used的命名都是站在Host的角度进行的

说明2:vring的存储

vring结构只是用于描述vring在内存中的布局(因此包含的都是指针变量),实际用于通信的vring是存储在内存中

上文提到的vring的三个区域是在内存中连续存储的,而且是存储在Guest & Host共享的一片连续内存中

我们可以通过vring_init函数理解vring存储结构的布局

实际vring的内存布局如下图所示,

在计算used ring的起始地址时,在avail->ring[num]的地址之后又加了sizeof(__virtio16),也就是增加了2B,是为了容纳avail ring末尾的used_event,该机制详见下文(是一种控制中断触发频率的机制)

说明3:实际vring的大小

实际vring的大小可以通过vring_size函数获得

① 计算avail ring时加3,分别为flags、idx和used_event

② 计算used ring时加3,分别为flags、idx和avail_event

③ 计算过程中,包含了为满足对齐要求padding的空间

说明4:used_event与avail_event机制概述

这2个字段均与virtio设备的VIRTIO_RING_F_EVENT_IDX特性有关,由于virtio驱动触发对方中断将导致CPU反复进出虚拟机 & 宿主机模式,从而降低性能,因此需要控制触发中断频率的机制

① avail ring中的used_event

a. 由前端驱动(Geust)设置,标识希望后端驱动(Host)触发中断的阈值

b. 后端驱动(Host)在向Used Ring加入buffer后,检查Used Ring中的idx字段,只有达到阈值才触发中断

② used_ring中的avail_event

a. 由后端驱动(Host)设置,标识希望前端驱动(Guest)触发中断的阈值

b. 前端驱动(Guest)在向Avail Ring加入buffer后,检查Avail Ring的idx字段,只有达到阈值才触发中断

综上所属,vring结构的构成如下图所示,

6 vring_virtqueue结构

vring_virtqueue结构用于描述前端驱动(Guest)中的一条虚拟队列

总结:virtio_device / vring_virtqueue / virtqueue / vring结构之间的关系

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐