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--领域驱动设计的理解 我觉得领域驱动设计概念的提出,是为了更清晰的区分边界.这里的边界包括业务边界和功能的边界,每个边界都包含具体的领域对象,当业务和功能的领域对象一一对应上之后,业务的变 ...
随机推荐
- android系统中有哪些日志
日志目录 android系统中还有很多常用的日志目录.我们可以通过adb命令把这些日志信息提取出来. data/system/dropbox data/system/usagestats data/s ...
- SQL Server之自动创建视图
本方法只适合特定模式的视图创建. 比如,创建需要整张表列名的视图,或者当表和需要的列名统计在一张数据表当中,如图所示: 首先要先获取要创建视图所需要的表,这里我获取的是整个数据库中的表, IF OBJ ...
- 前缀和与差分(Acwing795-798)
一维前缀和 Acwing795.前缀和 #include <iostream> using namespace std; const int N = 100010; int n, m; i ...
- 2 万字 + 30 张图 | 细聊 MySQL undo log、redo log、binlog 有什么用?
作者:小林coding 计算机八股文网站:https://xiaolincoding.com/ 大家好,我是小林. 从这篇「执行一条 SQL 查询语句,期间发生了什么?」中,我们知道了一条查询语句经历 ...
- 封装环形加载进度条(Vue插件版和原生js版)
1.效果预览 2.用到的知识 主要利用SVG的stroke-dasharray和stroke-dashoffset这两个属性. 在看下面文章之前,你需要了解 <!DOCTYPE html> ...
- react的setState到底是同步还是异步
在介绍这个问题之前,我们先来看一下一个例子: state = {number:1};componentDidMount(){this.setState({number:3})console.log(t ...
- 分享|智慧环保-生态文明信息化解决方案(附PDF)
内容摘要: 生态文明建设被提到前所未有的战略高度,我们既要绿水青山,也要金山银山.宁要绿水青山,不要金山银山,而且绿水青山就是金山银山.要正确处理好经济发展同生态环境保护的关系,牢固树立保护生态环境就 ...
- Java获取显示器屏幕大小
public static void main(String[] args) { Dimension screensize = Toolkit.getDefaultToolkit().getScree ...
- 『现学现忘』Git后悔药 — 27、版本回退介绍
目录 1.什么版本回退 2.需要了解两个知识点 (1)HEAD是什么 (2)HEAD指针用法 3.git reflog命令介绍 1.什么版本回退 版本回退也可以叫回滚. 若修改过的文件,不仅添加到了暂 ...
- 数据访问 - EntityFramework集成
前言 Masa提供了基于EntityFramework的数据集成,并提供了数据过滤与软删除的功能,下面我们将介绍如何使用它? MasaDbContext入门 安装.Net 6.0 新建ASP.NET ...