virtio 的架构 从总体上看,virtio 可以分为四层,包括前端 guest 中各种驱动程序模块,后端 Hypervisor (实现在Qemu上)上的处理程序模块,中间用于前后端通信的 virtio 层和 virtio-ring 层,virtio 这一层实现的是虚拟队列接口,算是前后端通信的桥梁,而 virtio-ring 则是该桥梁的具体实现,它实现了两个环形缓冲区,分别用于保存前端驱动程序和后端处理程序执行的信息 严格来说,virtio 和 virtio-ring 可以看做是一层,virtio-ring 实现了 virtio 的具体通信机制和数据流程。 Virtio 使用 virtqueue 来实现 I/O 机制,每个 virtqueue 就是一个承载大量数据的队列,具体使用多少个队列取决于需求,例如,virtio 网络驱动程序(virtio-net) 使用两个队列(一个用于接受,另一个用于发送),而 virtio 块驱动程序(virtio-blk)仅使用一个队列。
作为一个开放的标准接口,virtio一直在云计算与虚拟化中扮演着重要的角色。而virtio网络接口,作为virtio标准支持下最复杂的接口之一,在虚拟机/容器网络加速、混合云加速中一直扮演着重要角色。 本文将在读者对virtio标准与虚拟化有一定了解的前提下,介绍virtio网络架构从创造之初到如今的演化之路。 1.virtio-net驱动与设备: 最原始的virtio网络 Virtio网络设备是一种虚拟的以太网卡,支持多队列的网络包收发。熟悉virtio的读者应该知道,在virtio的架构中有前后端之分。 在virtio 网络中,所谓的前端即是虚拟机中的virtio-net网卡驱动。而后端的实现多种多样,后端的变化往往标志着virtio网络的演化。 更重要的是vDPA框架保有virtio这套标准的接口,使云服务提供商在不改变virtio接口的前提下,得到更高的性能。
virtio驱动的Provider一般是Red Hat和云厂商,比如Tencent,下面powershell代码用来卸载virtio驱动,分别指定了Provider为Red Hat和Tencent,如果你有其他厂商代号 delete_virtio.ps1代码如下,不保证所有系统都能正常执行,有些系统环境执行dism.exe /online /get-drivers会报87,这种就不行。
virtio-user 是 DPDK 针对特定场景提出的一种解决方案,它主要有两种场景的用途,一种是用于 DPDK 应用容器对 virtio 的支持,这是 DPDK v16.07 开始支持的;另一种是用于和内核通信 virtio_user 用于容器网络 我们知道,对于虚拟机,有 virtio 这套半虚拟化的标准协议来指导虚拟机和宿主机之间的通信,但对于容器的环境,直接沿用 virtio 是不行的,原因是虚拟机是通过 所以,virtio_user 其实就是在 virtio PMD 的基础上进行了少量修改形成的,简单来说,就是添加大页共享的部分逻辑,并精简了整块共享内存部分的逻辑。 有兴趣可以对照 /driver/net/virtio 中的代码和 DPDK virtio_user 代码,其实大部分是相同的。 =virtio_user0,path=/tmp/vhost_user0 virtio_user 作为 exception path 用于与内核通信 virtio_user 的一个用途就是作为 exception
/archive-virtio/virtio-win-0.1.173-5/virtio-win-gt-x64.msi -Outfile virtio-win-0.1.173-5.virtio-win-gt-x64 /archive-virtio/virtio-win-0.1.173-8/virtio-win-gt-x64.msi -Outfile virtio-win-0.1.173-8.virtio-win-gt-x64 /archive-virtio/virtio-win-0.1.185-2/virtio-win-gt-x64.msi -Outfile virtio-win-0.1.185-2.virtio-win-gt-x64 /archive-virtio/virtio-win-0.1.190-1/virtio-win-gt-x64.msi -Outfile virtio-win-0.1.190-1.virtio-win-gt-x64 /archive-virtio/virtio-win-0.1.208-1/virtio-win-gt-x64.msi -Outfile virtio-win-0.1.208-1.virtio-win-gt-x64
(复用iov sample)基本流程:驱动已经加载;之前创建的vf已经被绑定到其他驱动;执行pci_enable_sriov函数;static int virtio_pci_sriov_configure (struct pci_dev *pci_dev, int num_vfs){struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);struct virtio_device *vdev = &vp_dev->vdev;int ret;if (! (vdev->config->get_status(vdev) & VIRTIO_CONFIG_S_DRIVER_OK))return -EBUSY;if (! __virtio_test_bit(vdev, VIRTIO_F_SR_IOV))return -EINVAL;/* pci设备是否有vf被其他驱动使用?
现正在招兵买马,看完请点击左下角阅读原文查看福利哦~ 背景 Virtio 来源于 virtio: towards a de-facto standard for virtual I/O devices 例如 virtio-blk 只有一个 virtqueue,而 virtio-net/virtio-console 有两个 virtqueue,一个用于输入,一个用于输出。 下面看一下 virtio 如何处理 block 的虚拟化的呢。 Virtio-blk 后端 我们先从后端讲起,因为后端相当于一个虚拟的硬件,后端提供什么功能,前端才能使用什么功能。 其中配置空间比较重要,通过 PCI 提供了Guest 访问 virtio 虚拟硬件的一些参数。对于 virtio-blk,包括基本的磁盘布局信息。 本文对部分细节只进行简单的说明,后续会在以下几个话题开展: Qemu 和 virtio 的内存映射 中断注入的实现 Virtio 流控以及性能优化 Vhost-user 当前,距离 virtio 问世已经十年有余
2、Virtio 与 Vhost 协议介绍 Virtio 目前被用作虚拟机(VM)访问块设备(virtio-blk)和网络设备(virtio-net)的标准开放接口。 Virtio-net 作为一种虚拟网卡,是 virtio 迄今为止支持的最复杂的设备。 Part I:Virtio 是如何被构建出来的? Part III:Vhost-net/Virtio-net 架构 virtio 接口有一个前端组件和一个后端组件: 前端组件是 virtio 接口的 guest 端。 内核提供一个通用的 virtio-pci 驱动程序,供实际的 Virtio 传输设备(例如 virtio-net 或 virtio-scsi)使用。 下图显示了 virtio-net 设备使用 virtio-net 驱动程序进行配置和发送数据包的过程,该驱动程序通过 PCI 与 virtio-net 设备通信。
,只有真正看懂了代码才能理解virtio。 以qemu和linux中的virtio-net举例分析代码,这儿只分析qemu部分virtio代码,在qemu中创建一个virtio-net设备,tap作为backend,有2个queue,那么qemu 的初始化 virtio_device_realize ├─virtio_net_pci_realize ├─virtio_bus_device_plugged | ├─virtio_pci_device_plugged ├─virtio_init ├─virtio_net_add_queue | └─virtio_add_queue//rx和tx的handle_output分别是virtio_net_handle_rx 和virtio_net_handle_tx_bh ├─qemu_bh_new//生成一个qemu bh virtio_net_tx_bh └─virtio_add_queue//控制qemu是virtio_net_handle_ctrl
在qemu代码中virtio-pci.c文件中有函数virtio_ioport_write专门处理前端驱动的IO写操作,看 ? 在virtio-net,c中的virtio_net_init,可以看到这里给接收队列绑定的是virtio_net_handle_rx,而给发送队列绑定的是virtio_net_handle_tx_bh或者 virtio_net_handle_tx_timer。 size的问题,然后把这块物理页框号(GPA>>VIRTIO_PCI_QUEUE_ADDR_SHIFT)写入到VIRTIO_PCI_QUEUE_PFN,这样后端就会得到这块内存区的信息。 下面在通过VIRTIO_PCI_QUEUE_PFN传递地址的时候,调用virtio_queue_set_addr设置后端相关队列的vring该函数实现较简单 ?
default: break; } } 设备分类 virtio分为很多设备类型virtio-net/virtio-blk/virtio-scsi等等,virtqueue实现通用部分 实现情况 linux 4.18 virtio-net driver已经能支持virtio 1.1了,但vhost-net不支持virtio 1.1。 qemu master实现了virtio 1.1。 dpdk virtio pmd和vhost-user都支持virtio 1.1。 总结 virtio标准还会继续发展,功能会越来越多,设备类型会越来越多,如virtio GPU和virtio vIOMMU,GPU最难虚拟化,目前用的是mdev,没有IOMMU,virtio设备可以修改任意 总结virtio的目标就是统一IO设备,虚拟机看到的所有的外设都是virtio类型,只需要安装virtio类型的驱动即可,如果硬件也能实现virtio,那么裸金属也一样了,虚拟机和裸金属互相热迁移,一个镜像走天下
default: break; } } 设备分类 virtio分为很多设备类型virtio-net/virtio-blk/virtio-scsi等等,virtqueue实现通用部分 实现情况 linux 4.18 virtio-net driver已经能支持virtio 1.1了,但vhost-net不支持virtio 1.1。 qemu master实现了virtio 1.1。 dpdk virtio pmd和vhost-user都支持virtio 1.1。 总结 virtio标准还会继续发展,功能会越来越多,设备类型会越来越多,如virtio GPU和virtio vIOMMU,GPU最难虚拟化,目前用的是mdev,没有IOMMU,virtio设备可以修改任意 总结virtio的目标就是统一IO设备,虚拟机看到的所有的外设都是virtio类型,只需要安装virtio类型的驱动即可,如果硬件也能实现virtio,那么裸金属也一样了,虚拟机和裸金属互相热迁移,一个镜像走天下
目的本文档分析vrtio-net内核驱动中三个收包方式(recv_small, recf_big, recv_mergeable)实现细节,根据其实现区别,确定三种收包方式下硬件处理包时需要实现的逻辑.virtio-net 驱动收包路径分析:virtio-net驱动收包流程中,这里以napi收包为例(virtio-net驱动不支持直接中断收包),函数调用为:1).填充收包buf(根据feature 选择不同的add_recvbuf 提交到rq中去:其申请的内存布局如下:2)receive_small:small的receive函数使用build_skb函数直接复用add_recv_buf_small中申请的buf创建skb,并将virtio_net_hdr 硬件视角下的big路径:对于硬件而言.加入rq的缓冲区为17个链接描述符,总大小为65536+ 4096个字节,其中前16个字节放virtio_hdr_padded结构体:剩余65536+4080字节放整个数据包
虚拟网卡有e1000,virtio等,为什么云计算环境最终选择了virtio?因为virtio首先提供了一种虚拟机和物理服务器数据交换的通用机制,虚拟网卡虚拟硬盘虚拟显卡虚拟串口等都可以用。 之间用virtio通用机制交换数据,设计真是十分巧妙。 在这张图中frontend就是guest kernel中的virtio-net driver,而backend是qemu模拟的virtio-net device。 virtio full offload ? ,软件实现不划算,物理网卡计算一个hash值写到skb中,到了virtio-net再根据这个值再入virtio-net队列。
惠伟:virtio代码分析(一)-qemu部分zhuanlan.zhihu.com 假如我们加个参数vhost=on,vhost定义了一堆api,qemu把virtio收发包和用于通知收发包的功能offload 还有一点就把把内核vring的三个地址设置和用户态qemu一样,而qemu又来于guest中virtio-net driver写pci配置,所以最终host kernel vhost net就和guest vhost_net_start ├─virtio_pci_set_guest_notifiers | └─virtio_pci_set_guest_notifier ├─vhost_net_start_one | ├─vhost_net_start_one | | └─vhost_dev_enable_notifiers | | └─virtio_bus_set_host_notifier | | ├─event_notifier_init | | └─virtio_pci_ioeventfd_assign | └─vhost_dev_start
但是virtio family的设备这种越来越多,virtiofs、virtio gpu、virtio console等设备却缺少相应的工具。 基于以上原因,作者开发了virtiostat工具,作为bcc工具集的一部分,提供了virtio设备的stat监控能力。 分析 原理 在Linux上,virtio设备进行IO的时候,会先生成scatterlist这样的数据结构,然后使用如下几个API,把数据加入到virt queue中: virtqueue_add_sgs 但是,blk layer的一次IO操作,把请求下发到virtio层的时候,除了IO request和数据之外,在virtio blk中还要加入额外的协议的部分,也就导致了virtiostat看到的SG( 几个重要的场景 在virtio blk、virtio scsi以及virtio net场景下,已经有足够的工具来支持了。
测试结果 bps 多线程大包下virtio,sriov,host都能达到满带宽 因为只有一个vf,sriov和host的性能基本一到。 只有两个VM之间有流量,bps virtio,sriov和host基本一样 ? latency ? pps ? ? 唯一不同的就是pps,因为virtio处理路径较长性能比sriov和host差很多,尤其是在linux bridge上测试pps时丢包特别多,ovs bridge也会丢少部分包。
xml文件配置 这里只写disk相关的块,qcow2硬盘的bus设置为virtio,引导镜像和virtio驱动的ISO的bus设置为sata,第一次安装时引导选择cdrom启动 <disk type 网卡驱动 默认安装完之后是没有virtio 网卡驱动的,需要手动安装驱动,首先打开设备管理器 windows server系统是在计算机管理打开设备管理器 找到以太网控制器,点击右击更新驱动程序 浏览virtio 驱动的文件夹 选择对应操作系统版本安装 安装完成后就有virtio驱动了 安装virtio驱动 打开virtio的驱动目录,最下面有一个virtio-win-guest-tools程序,会安装用到的virtio 驱动,这个驱动也会包含virtio的网卡驱动;这样xml中定义 <input type='tablet' bus='<em>virtio</em>'> 也可以用鼠标跟随了, windows原生支持usb的驱动,所以虚拟机镜像如果没装 virtio驱动,可以修改为 bus='usb',不过性能不如virtio。
例如,virtio 网络驱动使用两个虚拟队列(一个用于接收,一个用于发送),而 virtio 块驱动只使用一个队列。 virtio_config_ops 结构体定义了对 virtio 设备进行配置的操作。 图 4 :virtio 前端对象层次结构 该过程从创建 virtio_driver 开始,随后通过 register_virtio_driver 进行注册。 /driver/net/virtio_net.c 中找到,virtio 块驱动可以在 ./driver/block/virtio_blk.c 中找到。 . /driver/virtio 子目录下提供了 virtio 接口实现(virtio 设备、驱动程序、virtqueue 和 ring)。
什么是 vhost vhost 是 virtio 的一种后端实现方案,在 virtio 简介中,我们已经提到 virtio 是一种半虚拟化的实现方案,需要虚拟机端和主机端都提供驱动才能完成通信,通常,virtio 为什么要用 vhost 在 virtio 的机制中,guest 与 用户空间的 Hypervisor 通信,会造成多次的数据拷贝和 CPU 特权级的上下文切换。 其中,qemu 还是需要负责 virtio 设备的适配模拟,负责用户空间某些管理控制事件的处理,而 vhost 实现较为纯净,以一个独立的模块完成 guest 和 host kernel 的数据交换过程 vhost 与 virtio 前端的通信主要采用一种事件驱动 eventfd 的机制来实现,guest 通知 vhost 的事件要借助 kvm.ko 模块来完成,vhost 初始化期间,会启动一个工作线程