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. Cent OS8.0 及以上版本安装禅道教程

    Cent OS8系统下安装禅道需要搭建环境如下:httpd ,mariadb , php7.2 再运行禅道 一,环境说明: 运行环境推荐使用 Apache + PHP(7.0/7.1/7.2版本) + ...

  2. 开发工具-Redis Desktop Manager下载地址

    更新记录 2022年6月10日 完善标题. 官方: https://github.com/uglide/RedisDesktopManager 免费打包版: https://github.com/le ...

  3. 谷歌浏览器Chrome官方下载地址

    经常看到朋友的电脑上安装是魔改的谷歌浏览器.这里将谷歌浏览器官方的下载地址放在这里.有需要的朋友可以自己去下载. 下载地址 Chrome最新稳定在线安装版:https://www.google.cn/ ...

  4. Citus 11 for Postgres 完全开源,可从任何节点查询(Citus 官方博客)

    Citus 11.0 来了! Citus 是一个 PostgreSQL 扩展,它为 PostgreSQL 添加了分布式数据库的超能力. 使用 Citus,您可以创建跨 PostgreSQL 节点集群透 ...

  5. ShardingSphere-proxy-5.0.0容量范围分片的实现(五)

    一.修改配置文件config-sharding.yaml,并重启服务 # # Licensed to the Apache Software Foundation (ASF) under one or ...

  6. ABAP CDS-基础语法规则

    The general syntax rules for the DDL and the DCL in ABAP CDS are: Keywords Keywords must be all uppe ...

  7. 一条update语句到底加了多少锁?带你深入理解底层原理

    迎面走来了你的面试官,身穿格子衫,挺着啤酒肚,发际线严重后移的中年男子. 手拿泡着枸杞的保温杯,胳膊夹着MacBook,MacBook上还贴着公司标语:"我爱加班". 面试开始,直 ...

  8. GaussDB(for MySQL) :Partial Result Cache,通过缓存中间结果对算子进行加速

    摘要:华为云数据库高级内核技术专家详解GaussDB(for MySQL)Partial Result Cache特性,如何通过缓存中间结果对算子进行加速? 本文分享自华为云社区<GaussDB ...

  9. RocketMQ 集群的搭建部署 以及rocketmq-console-ng仪表台的安装部署

    在 RocketMQ 主要的组件如下. NameServerNameServer 集群,Topic 的路由注册中心,为客户端根据 Topic 提供路由服务,从而引导客户端向 Broker 发送消息.N ...

  10. Neural Networks

    神经网络能够使用torch.nn包构建神经网络. 现在你已经对autogard有了初步的了解,nn基于autograd来定义模型并进行微分.一个nn.Module包含层,和一个forward(inpu ...