目录
1. 概述
2. 数据结构
3. 流程分析
3.1 发送
3.2 接收
3.3 代码分析
3.3.1 virtqueue创建
3.3.2 virtio-net驱动发送
3.3.3 Qemu virtio-net设备接收
参考
相关阅读
https://www.cnblogs.com/LoyenWang/
前边系列将Virtio Device和Virtio Driver都已经讲完,本文将分析virtqueue;
virtqueue用于前后端之间的数据交换,一看到这种数据队列,首先想到的就是ring-buffer,实际的实现会是怎么样的呢?
先看一下核心的数据结构:
通常Virtio设备操作Virtqueue时,都是通过struct virtqueue
结构体,这个可以理解成对外的一个接口,而Virtqueue
机制的实现依赖于struct vring_virtqueue
结构体;
Virtqueue
有三个核心的数据结构,由struct vring
负责组织:
struct vring_desc
:描述符表,每一项描述符指向一片内存,内存类型可以分为out类型和in类型,分别代表输出和输入,而内存的管理都由驱动来负责。该结构体中的next字段,可用于将多个描述符构成一个描述符链,而flag字段用于描述属性,比如只读只写等;
struct vring_avail
:可用描述符区域,用于记录设备可用的描述符ID,它的主体是数组ring,实际就是一个环形缓冲区;
struct vring_used
:已用描述符区域,用于记录设备已经处理完的描述符ID,同样,它的ring数组也是环形缓冲区,与struct vring_avail
不同的是,它还记录了设备写回的数据长度;
这么看,当然是有点不太直观,所以,下图来了:
简单来说,驱动会分配好内存(scatterlist
),并通过virtqueue_add
添加到描述表中,这样描述符表中的条目就都能对应到具体的物理地址了,其实可以把它理解成一个资源池子;
驱动可以将可用的资源更新到struct vring_avail
中,也就是将可用的描述符ID添加到ring数组中,熟悉环形缓冲区的同学应该清楚它的机制,通过维护头尾两个指针来进行管理,Driver负责更新头指针(idx),Device负责更新尾指针(Qemu中的Device负责维护一个last_avail_idx),头尾指针,你追我赶,生生不息;
当设备使用完了后,将已用的描述符ID更新到struct vring_used
中,vring_virtqueue
自身维护了last_used_idx,机制与struct vring_avail
一致;
当驱动需要把数据发送给设备时,流程如上图所示:
当驱动从设备接收数据时,流程如上图所示:
代码的分析将围绕下边这个图来展开(Virtio-Net
),偷个懒,只分析单向数据发送了:
virtnet-probe
和virtio_pci_probe
中完成所有的初始化;virtnet_probe
函数入口中,通过init_vqs
完成Virtqueue的初始化,这个逐级调用关系如图所示,最终会调用到vring_create_virtqueue
来创建Virtqueue;vring_virtqueue
数据结构的初始化展开,其中vring数据结构的内存分配也都是在驱动中完成,整个结构体都由驱动来管理与维护;
start_xmit
函数来实现;xmit_skb
函数中,sg_init_table
初始化sg列表,sg_set_buf
将sg指向特定的buffer,skb_to_sgvec
将socket buffer中的数据填充sg;virtqueue_add_outbuf
将sg添加到Virtqueue中,并更新Avail队列中描述符的索引值;virtqueue_notify
通知Device,可以过来取数据了;
kvm_handle_io
;virtio_pci_config_write
;virtio_pci_config_write
函数中,对Guest的写操作进行判断并处理,比如在VIRTIO_PCI_QUEUE_NOTIFY
时,调用virtio_queue_notify
,用于处理Guest驱动的通知,并最终回调handle_output
函数;virtio_net_handle_tx_bh
,并在virtio_net_flush_tx
中完成操作;virtqueue_pop
从Avail队列中获取地址,将数据进行处理,通过virtqueue_push
将处理完后的描述符索引更新到Used队列中,通过virtio_notify
通知Guest驱动;Virtqueue这种设计思想比较巧妙,不仅用在virtio中,在AMP系统中处理器之间的通信也能看到它的身影。
草草收场了,下回见。
https://www.redhat.com/en/blog/virtqueues-and-virtio-ring-how-data-travels
Virtual I/O Device Version 1.1
《virtio数据结构总览 | 图》
《Linux PCI驱动框架分析:(Peripheral Component Interconnect,外部设备互联)》
《Linux虚拟化KVM-Qemu分析(一)》
《Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化》
《Linux虚拟化KVM-Qemu分析(三)之KVM源码(1)》
《Linux虚拟化KVM-Qemu分析(四)之CPU虚拟化(2)》
《Linux虚拟化KVM-Qemu分析(五)之内存虚拟化》
《Linux虚拟化KVM-Qemu分析(六)之中断虚拟化》
《Linux虚拟化KVM-Qemu分析(七)之timer虚拟化》
《Linux虚拟化KVM-Qemu分析(八)之virtio初探》
《Linux虚拟化KVM-Qemu分析(九)之virtio设备》
《Linux虚拟化KVM-Qemu分析(十)之virtio驱动》
《Linux虚拟化KVM-Qemu分析(十一)之virtqueue》
《virtio 网络的演化》
《virtio 网络的演化:原始virtio > vhost-net(内核态) > vhost-user(DPDK) > vDPA》
《vhost(vhost-user)网络I/O半虚拟化详解:一种 virtio 高性能的后端驱动实现》https://rtoax.blog.csdn.net/article/details/114175631
《在CentOS上进行虚拟化:QEMU、Xen、KVM、LibVirt、oVirt》
《ARM SMMU原理与IOMMU技术(“VT-d” DMA、I/O虚拟化、内存虚拟化)》
《OpenVZ,Xen,KVM等:虚拟化解决方案》
《KVM Virtio: An I/O virtualization framework for Linux(Linux虚拟IO框架)》
《ARM SMMU原理与IOMMU技术(“VT-d” DMA、I/O虚拟化、内存虚拟化)》
《用QEMU构建嵌入式LINUX系统》
《疫情下的远程办公:理解Linux虚拟网络设备之tun/tap》
《Linux虚拟网络设备之tun/tap》
《Tun/Tap接口教材-[翻译:Tun/Tap interface tutorial]》
《Linux的TUN/TAP编程》