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. ruoyi前后端分离版:添加新页面,不需登录也可访问

    添加QRcode页面代码 <template> <div class="register"> <el-form ref="registerF ...

  2. 看看CabloyJS是如何实现编辑页面脏标记的

    应用场景 我们在使用Word.Excel时,当修改了内容之后在标题栏会显示脏标记,从而可以明确的告知用户内容有变动.此外,如果在没有保存的情况下关闭窗口,系统会弹出提示框,让用户选择是否放弃修改 那么 ...

  3. Centos7 中安装Elasticsearch

    1.下载安装包 1.1 下载elasticsearch 7.13.3 curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/e ...

  4. 五分钟搞懂POM设计模式

    转载请注明出处️ 作者:IT小学生蔡坨坨 原文链接:五分钟搞懂POM设计模式 大家好,我是IT小学生蔡坨坨. 今天,我们来聊聊Web UI自动化测试中的POM设计模式. 为什么要用POM设计模式 前期 ...

  5. 入坑KeePass(一)安全桌面输入管理密钥后,不能输入中文

    坑一:设置了在安全桌面输入管理密钥后,重启电脑后今日keepass软件中不能输入中文: 解决方式:进入keepass软件中取消勾选,在重启软件就可以输入中文了.

  6. 一些好用的javascript/typescript方法封装分享

    1.数字格式化 JS版-直接写到原型链上 /** * @author: silencetea * @name: * @description: 数字格式化,默认每三位用英文逗号分隔 * @param ...

  7. 在海思芯片上使用GDB远程调试

    1 前言 使用海思平台上(编译工具链:arm-himix200-linux)交叉编译 GDB 工具(使用版本8.2,之前用过10.2的版本,在编译 gdbserver 遇到编译出错的问题,因为关联了其 ...

  8. UiPath Level3讲解

    匠厂出品,必属精品   Uipath中文社区qq交流群:465630324 uipath中文交流社区:https://uipathbbs.com RPA之家qq群:465620839 第一课--UiP ...

  9. nextInt和nextLine以及next方法的区别

    1.nextInt() 只读取整型的数据,输入读取完之后,光标仍在当前行. 2.nextLine() 扫描到一行内容,当遇见换行符时,结束扫描.一旦输入读取完毕,该方法会将光标移到下一行开始的位置. ...

  10. Lua5.4源码剖析:二. 详解String数据结构及操作算法

    概述 lua字符串通过操作算法和内存管理,有以下优点: 节省内存. 字符串比较效率高.(比较哈希值) 问题: 相同的字符串共享同一份内存么? 相同的长字符串一定不共享同一份内存么? lua字符串如何管 ...