ps:本文基于4.19.204内核

Q:vqueue的结构成员解释:

A:结构如下,解析附后:

struct virtqueue {

struct list_head list;//caq:一个virtio设备所有的vq串接

void (*callback)(struct virtqueue *vq);//caq:buf 被消费之后的回调,比如 virtblk_done,

const char *name;//caq:vq的名字,比如virtio_blk的vq名字叫req.x,x为编号,而net一般是 input.x,output.x,成对

struct virtio_device *vdev;//caq:关联该vq的virtio 设备,一个virtio 设备可能有多个vqueue

unsigned int index;//caq:vq的编号,比如0,1,2....

unsigned int num_free;//caq:环内空闲的个数

void *priv;

};

Q:vq被封装的类型

A:普通的vq可能会被封装,比如 pci对它的封装是:

struct virtio_pci_vq_info {//对vq简单封装

/* the actual virtqueue */

struct virtqueue *vq;

/* the list node for the virtqueues list */
struct list_head node;//串接头使用,操作时需要持有 virtio_pci_device 的lock, /* MSI-X vector (or none) */
unsigned msix_vector;//caq:msix中断,如果该设备最终是INTx中断,则为0

};

Q:vring 和vq是怎么关联的?

A:在这个内核版本中,vq和vring是一起申请内存,存放在 vring_virtqueue 中,

struct vring_virtqueue {//caq:vq 与vring 的联系结构,相当于pair,一个vq关联一个vring,一个设备可能具备多个vq,假设映射为blk的话,就是mq的多队列模式。
struct virtqueue vq;//caq:pair 中的vq /* Actual memory layout for this queue */
struct vring vring;//caq:关联的vring /* Can we use weak barriers? */
bool weak_barriers; /* Other side has made a mess, don't try any more. */
bool broken;//caq:标记vq是否已经broken /* Host supports indirect buffers */
bool indirect;//caq:host端是否支持非直接的buffer /* Host publishes avail event idx */
bool event; /* Head of free buffer list. */
unsigned int free_head;//caq:free buffer 的头
/* Number we've added since last sync. */
unsigned int num_added;//caq:最近一次操作向队列中添加报文的数量 /* Last used index we've seen. */
u16 last_used_idx;//caq:通过比较 last_used_idx 与当前vring的used_idx确认中间的数据 /* Last written value to avail->flags */
u16 avail_flags_shadow; /* Last written value to avail->idx in guest byte order */
u16 avail_idx_shadow; /* How to notify other side. FIXME: commonalize hcalls! */
bool (*notify)(struct virtqueue *vq);//caq:通知后端的方法 /* DMA, allocation, and size information */
bool we_own_ring;
size_t queue_size_in_bytes;
dma_addr_t queue_dma_addr; #ifdef DEBUG
/* They're supposed to lock for us. */
unsigned int in_use; /* Figure out if their kicks are too delayed. */
bool last_add_time_valid;
ktime_t last_add_time;
#endif /* Per-descriptor state. */
struct vring_desc_state desc_state[];//caq:个数为 vq.num_free
};

Q:vring的内存布局是怎么样的?

A:要深刻理解vring,就得知道vring的内存布局:

 * struct vring
* {
* // The actual descriptors (16 bytes each)
* struct vring_desc desc[num];
*
* // A ring of available descriptor heads with free-running index.
* __virtio16 avail_flags;
* __virtio16 avail_idx;
* __virtio16 available[num];
* __virtio16 used_event_idx;------------------这个是有内存分布,但无结构
*
* // Padding to the next align boundary.
* char pad[];
*
* // A ring of used descriptor heads with free-running index.
* __virtio16 used_flags;
* __virtio16 used_idx;
* struct vring_used_elem used[num];
* __virtio16 avail_event_idx;----------------这个是有内存分布,但无结构
* };

所以就能解释下面的3的由来

static inline unsigned vring_size(unsigned int num, unsigned long align)
{
return ((sizeof(struct vring_desc) * num + sizeof(__virtio16) * (3 + num)
+ align - 1) & ~(align - 1))
+ sizeof(__virtio16) * 3 + sizeof(struct vring_used_elem) * num;//caq:内存排列是vring_desc*num
}//caq:然后后面vring_avail,在vring_avail结尾有个16字位的used_event,因此是是(3+num)。
//caq:之后放得是vring_used,同样,后面有个16位的avail_event,因此也是sizeof(u16) * 3。

Q:为什么会有virtio_pci_modern.c与virtio_pci_legacy.c?

A:virtio的pci实现,有不同的版本,0.9.5的版本是 virtio_pci_legacy.c,而1.0及以上的版本,是virtio_pci_modern.c, 之后在2021年,

virtio_pci_modern.c进一步拆分为virtio_pci_modern_dev.c ,以及virtio_pci_modern.c 两个文件。

而 virtio_pci_common.c 实现了类似的基类,来调用 不同时期的版本实现。

从协议的发展来看,可能会进一步迭代。

Q:virtio设备中断申请是如何实现的?

A:virtio设备目前如果是pci来实现的话,有两种中断模式,对应的函数为 vp_find_vqs

一种是INTx模式,一种是 MSIx模式,优先尝试MSIX模式。

而msix下会尝试两个情况,一种是 每个vq一个中断号,另外一种是各个vq共用一个中断号,当使用每个vq一个中断号的时候需要注意,对于没有设置callback的vq,是不申请中断号的,

比如 virtio_net的ctrl vq便是如此。

再申请不到,则回退到 INTx模式。

在MSIx 模式下,每个 vitio_pci 设备还有个 配置通道的中断,其中断处理函数为: vp_config_changed。

在MSIx模式下,如果单vq 单中断申请成功,则它占用的总中断数为 :vq个数+1,其中1 为config 配置变化后的中断,中断处理函数分别为:vp_config_changed,vring_interrupt

                                   vring_interrupt 的中断处理对象为一个指定的vq,效率最高。 

                    如果单vq单中断申请失败,则总中断数为2,一个为配置中断,一个为数据的中断,数据中断的处理函数为 :vp_config_changed,vp_vring_interrupt,

                                  vp_vring_interrupt的中断处理对象为一个 virtio_pci_device,各个vq都需要callback一遍,效率居中。

                   这种模式下,每个中断都有自己的cpu亲核性默认设置。

在INTx模式下,中断个数为1,vp_interrupt 是其中断处理函数。显而易见的是,这种中断处理,既包含config变化的处理,又包含vq的数据中断处理,效率最低。

我们热插拔一个pci设备之后,一定要关注对应的中断处理配置成了那种模式,多队列的情况下,回退到INTx有可能就形成瓶颈。

常见的两个vq的virtio设备的中断申请情况如下:

# cat /proc/interrupts |grep virtio
1319: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ITS-MSI 14680064 Edge virtio0-config
1320: 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ITS-MSI 14680065 Edge virtio0-req.0
1321: 0 0 0 0 0 0 0 0 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ITS-MSI 14680066 Edge virtio0-req.1

其中virtio0是设备名称,req.0是第一个vq的名称,req.1是第二个vq的名称,config是配置变更的中断名称,但凡是中断名称中看到req.0字样的,都是非共享中断,vq共享中断的名称为virtio$id-virtqueues

中断申请的堆栈大概如下:

[    2.918238]  [<ffffffffa008c6a8>] vp_request_msix_vectors+0x68/0x260 [virtio_pci]
[ 2.921753] [<ffffffffa008cbf5>] vp_try_to_find_vqs+0x95/0x3b0 [virtio_pci]
[ 2.925126] [<ffffffffa008cf47>] vp_find_vqs+0x37/0xb0 [virtio_pci]
[ 2.928359] [<ffffffffa00bdefd>] init_vq+0x14d/0x260 [virtio_blk]
[ 2.937883] [<ffffffffa00be178>] virtblk_probe+0xe8/0x840 [virtio_blk]
[ 2.941176] [<ffffffffa002485f>] virtio_dev_probe+0x14f/0x2a0 [virtio]
[ 2.944460] [<ffffffff813f6497>] driver_probe_device+0x87/0x390
[ 2.947612] [<ffffffff813f6873>] __driver_attach+0x93/0xa0
[ 2.950671] [<ffffffff813f67e0>] ? __device_attach+0x40/0x40
[ 2.953783] [<ffffffff813f4203>] bus_for_each_dev+0x73/0xc0
[ 2.956867] [<ffffffff813f5eee>] driver_attach+0x1e/0x20
[ 2.959915] [<ffffffff813f5a40>] bus_add_driver+0x200/0x2d0
[ 2.963031] [<ffffffffa00c3000>] ? 0xffffffffa00c2fff
[ 2.966021] [<ffffffff813f6ef4>] driver_register+0x64/0xf0
[ 2.969093] [<ffffffffa00244d0>] register_virtio_driver+0x20/0x30 [virtio]
[ 2.972502] [<ffffffffa00c304e>] init+0x4e/0x1000 [virtio_blk]
[ 2.979927] [<ffffffff810020e8>] do_one_initcall+0xb8/0x230
[ 2.983036] [<ffffffff810ed4ae>] load_module+0x134e/0x1b50
[ 2.986132] [<ffffffff81316880>] ? ddebug_proc_write+0xf0/0xf0
[ 2.989283] [<ffffffff810e9743>] ? copy_module_from_fd.isra.42+0x53/0x150
[ 2.992652] [<ffffffff810ede66>] SyS_finit_module+0xa6/0xd0
[ 2.995715] [<ffffffff81645909>] system_call_fastpath+0x16/0x1b

Q:virtio设备的协商,主要的工作有哪些?

A:协商就是发生在前端驱动与后端设备之间,前端驱动要根据自己的版本支持哪些特性,后端设备要将自己的feature通过配置空间

 暴露出来,然后前端驱动读取后,取两者的子集。

另外,feature是一个方面,status是一个方面,feature可以认为是静态的,不怎么变化的,而status则是动态的,运行中改变

概率较大的,相当于docker镜像与docker容器之间的关系。

Q:按照virtio 的spec来看,

Vendor ID:0x1AF4

Device ID: 0x1000- 0x107f;

其中0x1000- 0x1040是legacy, 0x1040- 0x107f是modern,其中driver识别device ID, id - 0x1040就是传统的virtio device id. 例如网卡可以是0x1000也可以是0x1041。

那是不是linux驱动也是这么实现的呢?

A:不是的,linux驱动实现过程中,如果配置了force_legacy,则直接按照legacy来来probe,如果没有配置,则先按照modern来probe,

并且,同时都能驱动0x1000- 0x107f范围内的device,只不过针对0x1040及以上的,会减去0x1040来作为deviceid的区分,到底是net还是block之类的。

virtio 驱动的数据结构理解的更多相关文章

  1. 【原创】Linux虚拟化KVM-Qemu分析(十)之virtio驱动

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: KVM版本:5.9 ...

  2. IDDD 实现领域驱动设计-理解限界上下文

    上一篇:<IDDD 实现领域驱动设计-理解领域和子域> <实现领域驱动设计>前两章内容,基本上读完了,和<领域驱动设计>不同的是,它把很多的概念都放在前面进行讲述了 ...

  3. DDD领域驱动设计的理解

    DDD领域驱动设计的理解 从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能 ...

  4. KVM下windows虚拟机使用virtio驱动

    KVM下windows虚拟机默认disk使用的是Qemu IDE硬盘,网卡默认是rtl8139网卡.为了使kvm主机在相同的配置下,有更好的效率,可以将网卡和磁盘替换成virtio的驱动. windo ...

  5. 阿里云安装 virtio 驱动

    为避免部分服务器.虚拟机或者云主机的操作系统在阿里云控制台 导入镜像 后,使用该自定义镜像创建的 ECS 实例无法启动,您需要在导入镜像前检查是否需要在源服务器中安装 Xen(pv)或 virtio ...

  6. 已集成 VirtIO驱动windows server 2012, 2008, 2003的ISO镜像下载

    已集成 VirtIO驱动简体中文windows server 2012, 2008, 2003系统ISO镜像下载地址. 适用于上传自定义ISO并且使用 VirtIO驱动的kvm架构vps,vultr家 ...

  7. 【转贴】Windows virtio 驱动

    Windows virtio 驱动 https://blog.51cto.com/dangzhiqiang/1833615 去年去中建总部的时候用过. 发现很多搞openstack的人都不清楚这一块的 ...

  8. 通过virt-manager给Windowsxp系统配置virtio驱动

    在虚拟机的detail上添加一个硬件设备. 下载virtio.iso文件,我使用的版本126,具体的virtio驱动放到了10.2上的itest的目录中,使用可以找. 在配置中,添加virtio硬盘. ...

  9. 浅析DDD——领域驱动设计的理解

    浅析DDD--领域驱动设计的理解 我觉得领域驱动设计概念的提出,是为了更清晰的区分边界.这里的边界包括业务边界和功能的边界,每个边界都包含具体的领域对象,当业务和功能的领域对象一一对应上之后,业务的变 ...

随机推荐

  1. 图文详解MapReduce工作机制

    job提交阶段 1.准备好待处理文本. 2.客户端submit()前,获取待处理数据的信息,然后根据参数配置,形成一个任务分配的规划. 3.客户端向Yarn请求创建MrAppMaster并提交切片等相 ...

  2. 命令行传参——JavaSE基础

    命令行传参 可以在运行一个程序时再传递给它消息,可以依靠命令行传参给mian()函数实现 public class CommandLine(){ public static void main(Str ...

  3. python求最大公约数和最小公倍数

    1 def gcd(x,y): 2 while(y): 3 t=x%y 4 x=y 5 y=t 6 #print("最小公倍数是:",x*y/x)#最小公倍数是两数之积除以最大公约 ...

  4. GDOI 2021 普及组溺水记

    Day 1 T1 一看样例:答案不就是 \(\dfrac{\max_{i=1}^n a_i +1}{2}\) 吗? 于是自信打上,拍都不拍.然后就,,对了? 插曲:自己出了一个极端数据,发现 scan ...

  5. ExtJS 布局-Accordion布局(Accordion layout)

    更新记录: 2022年6月2日 开始. 2022年6月3日 发布. 1.说明 accordion(手风琴)布局一次仅显示一个子组件,内置支持 折叠 和 展开.当需要堆叠多个子组件,并每次只显示一次时, ...

  6. ESXI系列问题整理以及记录——使用SSH为设备打VIB驱动包,同时提供一种对于ESXI不兼容螃蟹网卡(Realtek 瑞昱)的问题解决思路

    对于ESXI不兼容螃蟹网卡的问题,这里建议购买一张博通的低端单口千兆网卡,先使用博通网卡完成系统部署,再按照下文方法添加螃蟹网卡的VIB驱动,最后拆除博通网卡. 螃蟹网卡VIB驱动包下载地址:http ...

  7. nginx配置的server_name无法访问

    问题: 我的nginx.conf配置文件中的server_name是这样子的,然后无法访问. 但是如果说server_name后面改成服务器的IP地址却是可以访问的. 解决方案: 在本机上(不是服务器 ...

  8. Oracle数据库控制文件多路复用

    Oracle数据库控制文件多路复用多路复用控制文件,指的是在系统不同的位置上同时存放多个控制文件的副本,此时如果某个路径对应的磁盘发送物理损坏导致该控制文件损坏,就可以通过另一个磁盘上的控制文件进行恢 ...

  9. Object类和Dome的新媒体类型

    Object类 所有的类都是继承自Object的 Java Object 类是所有类的父类,也就是说 Java 的所有类都继承了 Object,子类可以使用 Object 的所有方法 Object 类 ...

  10. IO多路复用epoll

    0 why: 问题来源 0.1 网络编程流程 //创建socket int s = socket(AF_INET, SOCK_STREAM, 0); //绑定IP地址和端口号port bind(s, ...