virtio 驱动的数据结构理解
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 驱动的数据结构理解的更多相关文章
- 【原创】Linux虚拟化KVM-Qemu分析(十)之virtio驱动
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: KVM版本:5.9 ...
- IDDD 实现领域驱动设计-理解限界上下文
上一篇:<IDDD 实现领域驱动设计-理解领域和子域> <实现领域驱动设计>前两章内容,基本上读完了,和<领域驱动设计>不同的是,它把很多的概念都放在前面进行讲述了 ...
- DDD领域驱动设计的理解
DDD领域驱动设计的理解 从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能 ...
- KVM下windows虚拟机使用virtio驱动
KVM下windows虚拟机默认disk使用的是Qemu IDE硬盘,网卡默认是rtl8139网卡.为了使kvm主机在相同的配置下,有更好的效率,可以将网卡和磁盘替换成virtio的驱动. windo ...
- 阿里云安装 virtio 驱动
为避免部分服务器.虚拟机或者云主机的操作系统在阿里云控制台 导入镜像 后,使用该自定义镜像创建的 ECS 实例无法启动,您需要在导入镜像前检查是否需要在源服务器中安装 Xen(pv)或 virtio ...
- 已集成 VirtIO驱动windows server 2012, 2008, 2003的ISO镜像下载
已集成 VirtIO驱动简体中文windows server 2012, 2008, 2003系统ISO镜像下载地址. 适用于上传自定义ISO并且使用 VirtIO驱动的kvm架构vps,vultr家 ...
- 【转贴】Windows virtio 驱动
Windows virtio 驱动 https://blog.51cto.com/dangzhiqiang/1833615 去年去中建总部的时候用过. 发现很多搞openstack的人都不清楚这一块的 ...
- 通过virt-manager给Windowsxp系统配置virtio驱动
在虚拟机的detail上添加一个硬件设备. 下载virtio.iso文件,我使用的版本126,具体的virtio驱动放到了10.2上的itest的目录中,使用可以找. 在配置中,添加virtio硬盘. ...
- 浅析DDD——领域驱动设计的理解
浅析DDD--领域驱动设计的理解 我觉得领域驱动设计概念的提出,是为了更清晰的区分边界.这里的边界包括业务边界和功能的边界,每个边界都包含具体的领域对象,当业务和功能的领域对象一一对应上之后,业务的变 ...
随机推荐
- Cent OS8.0 及以上版本安装禅道教程
Cent OS8系统下安装禅道需要搭建环境如下:httpd ,mariadb , php7.2 再运行禅道 一,环境说明: 运行环境推荐使用 Apache + PHP(7.0/7.1/7.2版本) + ...
- 开发工具-Redis Desktop Manager下载地址
更新记录 2022年6月10日 完善标题. 官方: https://github.com/uglide/RedisDesktopManager 免费打包版: https://github.com/le ...
- 谷歌浏览器Chrome官方下载地址
经常看到朋友的电脑上安装是魔改的谷歌浏览器.这里将谷歌浏览器官方的下载地址放在这里.有需要的朋友可以自己去下载. 下载地址 Chrome最新稳定在线安装版:https://www.google.cn/ ...
- Citus 11 for Postgres 完全开源,可从任何节点查询(Citus 官方博客)
Citus 11.0 来了! Citus 是一个 PostgreSQL 扩展,它为 PostgreSQL 添加了分布式数据库的超能力. 使用 Citus,您可以创建跨 PostgreSQL 节点集群透 ...
- ShardingSphere-proxy-5.0.0容量范围分片的实现(五)
一.修改配置文件config-sharding.yaml,并重启服务 # # Licensed to the Apache Software Foundation (ASF) under one or ...
- ABAP CDS-基础语法规则
The general syntax rules for the DDL and the DCL in ABAP CDS are: Keywords Keywords must be all uppe ...
- 一条update语句到底加了多少锁?带你深入理解底层原理
迎面走来了你的面试官,身穿格子衫,挺着啤酒肚,发际线严重后移的中年男子. 手拿泡着枸杞的保温杯,胳膊夹着MacBook,MacBook上还贴着公司标语:"我爱加班". 面试开始,直 ...
- GaussDB(for MySQL) :Partial Result Cache,通过缓存中间结果对算子进行加速
摘要:华为云数据库高级内核技术专家详解GaussDB(for MySQL)Partial Result Cache特性,如何通过缓存中间结果对算子进行加速? 本文分享自华为云社区<GaussDB ...
- RocketMQ 集群的搭建部署 以及rocketmq-console-ng仪表台的安装部署
在 RocketMQ 主要的组件如下. NameServerNameServer 集群,Topic 的路由注册中心,为客户端根据 Topic 提供路由服务,从而引导客户端向 Broker 发送消息.N ...
- Neural Networks
神经网络能够使用torch.nn包构建神经网络. 现在你已经对autogard有了初步的了解,nn基于autograd来定义模型并进行微分.一个nn.Module包含层,和一个forward(inpu ...