前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >virtio+ovs转发原理和性能分析

virtio+ovs转发原理和性能分析

作者头像
惠伟
发布2021-02-24 11:27:53
3.1K0
发布2021-02-24 11:27:53
举报
文章被收录于专栏:虚拟化笔记虚拟化笔记

virtio和ovs介绍

传统数据中心中硬件服务器上运行linux,linux用硬件网卡收发包,硬件网卡有broadcom的有mellanox的有intel的等各式各样的,硬件网卡连接到硬件交换机上,硬件交换机有H3C的有cisco的,交换机进行包转发实现服务器之间互通。在云计算环境下,对计算资源进行了切分,服务器上运行的是一个个虚拟机,虚拟机也要有网卡实现互连互通,但虚拟机的网卡不是物理的,是虚拟的网卡,虚拟的网卡连接到虚拟的交换机上,虚拟的交换机对同一个服务器上的虚拟机之间流量进行转发,如果虚拟交换机再连接到服务器的硬件网卡,那么虚拟机就可以和服务器外面通信了。

硬件网卡收包时,CPU先分配内存,然后告诉硬件网卡内存的地址,报文从硬件交换机出来后进入硬件网卡的队列,硬件网卡通过DMA功能把包从物理网卡搬运到内存中。然后中断CPU说报文收上来了,CPU处理中断,软中断执行内核协议栈处理,最后通知应用程序收包。

虚拟网卡是CPU模拟出来的,它的队列也是模拟出来的,就是服务器上的一块内存。要模拟DMA功能就得进行一次内存拷贝,这次内存拷贝比较特殊,从服务器上拷贝到虚拟机里,虚拟机运行在服务器上,用的也是服务器上的物理内存,相当于服务器上物理内存之间的拷贝,只是地址转换比较复杂。

应用程序调用发包时,内核协议栈模块分配一块内存,把用户态要发的内容拷贝到内核。然后经过复杂的协议处理,最后地址告诉硬件网卡说我要发包你发完了告诉我一声,硬件网卡的DMA就把要发的数据从内存中搬运到物理网卡的队列中,然后告诉CPU说发完了,你可以回收内存了。

虚拟网卡发包和物理网卡发包类似,包从虚拟机中搬运到物理服务器内存中,然后经过软件交换机,最后从物理网卡出去。虚拟网卡有e1000,virtio等,为什么云计算环境最终选择了virtio?因为virtio首先提供了一种虚拟机和物理服务器数据交换的通用机制,虚拟网卡虚拟硬盘虚拟显卡虚拟串口等都可以用。其次virtio还分frontend和backend,frontend运行在虚拟机中,backend运行在物理服务器上,frontend和backend配合实现具体的网络功能,frontend和backend之间用virtio通用机制交换数据,设计真是十分巧妙。 virtio得到了大多的hypervisor支持,成了虚拟化事实上的标准。

硬件交换机是进行二层转发的,学习MAC进行转发,用STP协议防止环路出现,为了减小arp这样的广播风暴又增加了vlan隔离。软件交换机有linux bridge,vpp,ovs等,为什么云计算环境中大多用了ovs?因为云计算是最自然的SDN应用场景,不再需要复杂的控制面协议,如STP。另外云计算要求灵活,vpp pipeline和linux bridge设计时考虑更多的是二三层转发原理和转发性能,要加入新功能异常艰难,而ovs采用了openflow pipeline,多table和多group灵活跳转。pipeline设计考虑更多的是功能实现,性能方面由datapath支撑,并且datapath upstream到kernel中了,顺手就能用了,所以选择ovs。

vhost-net

这张图来自于redhat的博客,博客地址在参考文献中,非常完美的一张图,就没必要重复造轮子了。操作硬件网卡就是读写硬件网卡的寄存器,现在的外设都是PCIE外设,有配置空间和BAR空间。配置空间是PCIE标准定义,BAR空间硬件自己定义,读写寄存器就会触发硬件相应的操作,比如发送包,会构造一个ring,每个ring指向skb,然后把ring的头写到一个寄存器,再写一下触发发包的寄存器,硬件就读取ring,把ring指向的skb都从内存搬运到网卡上去。

虚拟机也有模拟的主板芯片和PCIE总线,virtio-net是PCIE总线上的一个外设。在这张图中frontend就是guest kernel中的virtio-net driver,而backend是qemu模拟的virtio-net device。virtio-net driver读写寄存器,qemu就要把原来硬件干的活模拟一遍。因为虚拟网卡寄存器是不存在的,kvm就把一块内存做特殊标志当作寄存器,virtio-net driver一写这块内存,cpu就从guest中exit出来,停止执行guest,开始执行qemu代码模拟guest触发的动作。qemu把外设模拟分为两种:控制面模拟和数据面模拟。控制面模拟有feature协商,vring地址交换等;数据面模拟有数据搬运和消息传递,其中消息传递就是guest通知vhost-net数据准备好了,我要发送,vhost-net发送完后告诉guest我帮你发送完了。在这张图中qemu把数据面模拟的工作交给了内核的vhost-net模拟来完成。

上面说虚拟网卡模拟DMA是内存拷贝,既然是内存拷贝那干脆共享内存就不用拷贝了。vhost-net就是这么干的,不管是内存拷贝还是内存共享都要进行内存地址换算。guest中的virtio-net driver设置的都是guest physical address,而vhost-net只知道host virtual address,但别忘记guest的内存是qemu分配的,qemu知道换算关系,qemu就通过vhost protoco告诉vhost-net模块换算关系。剩下就是消息传递了,kvm给qemu提供了api,qemu创建出两个eventfd传递给kvm,qemu通知guest,写一个fd,kvm读这个fd,kvm再把中断注入guest中,这个方向的通知叫做call。guest通知qemu,guest写寄存器,kvm拦截,kvm写另一个fd,qemu读这个fd,这个方向的通知叫做kick,qemu干脆一狠心,把这两个fd仍给了vhost-net,说以后这两个fd的读写就交给你了。

  • guest发包流程

guest在内核中分配skb,把地址写到vring中,kick kvm,kvm再通知vhost。vhost是内核线程,vhost地址换算拿到了skb,复制skb,通知guest发送完成,guest回收sbk,vhost继续给skb找netdev,加入到一个cpu的backlog,触发softirq,这个cpu的softirq发现netdev绑定在ovs桥上,查ovs流表找到出接口,调用物理网卡的驱动发送出去,物理网卡发送完成,把skb释放。

  • guest收包流程

guest中的virtio-net driver分配skb,设置到vring上,物理网卡驱动分配sbk设置给网卡,网卡DMA,中断触发,在softirq发现物理网卡绑在桥上,查ovs流表找到虚拟机的tap口,把skb放入tap的队列中,叫醒vhost worker,vhost worker醒来一看skb到了tap队列中,把skb拷贝到guest分配的skb上,通知kvm包来了,kvm再中断guest。

vhost-user

代码语言:txt
复制
这张图也来自于redhat的博客,我做了一点修改,原图guest中运行的是virtio-net-pmd而且还有vIOMMU。考虑到guest中的难度,我觉得公有云上是不现实的,让用户在guest中搞hugepage和dpdk pmd难度很大,而且目前没有配套的成熟用户态协议栈,修改一下假设guest是最普通的guest,相比vhost-net一点也不变。这种模式ovs不再用内核的datapath,物理网卡绑定了DPDK,物理网卡直接把包DMA到用户态ovs,ovs进程和qemu进程共享内存把包传递到qemu进程中,qemu进程地址换算一下包就到了guest中。virtio-net控制面模拟还在qemu中,原来给vhost-net下配置的vhost protoco变成了vhost-user protoco把配置交给了ovs-dpdk进程,qemu和ovs-dpdk之间建立了unix domain socket。这个socket的神奇之处在于不仅能传递virtio配置信息,还能传递qemu和kvm之间通信的kick/call fd。qemu把virtio-net数据面模拟交给了ovs-dpdk进程,消息通道还是靠kvm。guest中一直poll不现实,但ovs-dpdk从guest中拿包时可以一直poll。

virtio full offload

这张图也来自于redhat博客,根据我自己的理解修改一把,保持guest不变,用户在物理机上怎么部署业务,在虚拟机中也怎么部署业务,不能让用户感觉到不习惯或者不舒服。整体原理就是把virtio backend都由硬件实现了,然后用passthrough功能,和普通的物理网卡passthrough一模一样,只是这块卡实现了virtio标准。 passthrough和dpdk都用了vfio-pci,原理一样,把物理网卡的pcie配置空间映射到qemu进程或者ovs-dpdk进程空间中,ovs-dpdk就直接读写,但qemu还得再地址转换一下给了guest,guest中的virtio-net driver就可以直接读写了,所以virtio kick就好搞了,但call还是经过vfio-pci,vfio-pci通知kvm,kvm再中断注入guest中。数据搬运就是硬件直接DMA到qemu进程或者ovs-dpdk进程中,进程在虚拟地址空间中分配内存,交给硬件的地址都是进程的虚拟地址。然后进程把地址信息给vfio,vfio把进程虚拟地址转换成物理地址给IOMMU,IOMMU在外设给进程虚拟地址搬运数据时把地址转换成这个物理地址。qemu相比ovs-dpdk多了一道手续,guest driver给硬件配置的地址是自己的物理地址,qemu和vfio得把guest的物理地址转换成host的物理地址。

passthrough的问题是虚拟机不能热迁移,热迁移要把网卡pcie空间的数据迁移走,而且还要知道硬件DMA修改了那些guest的内存,要把修改的内存也迁移过去,但硬件都没有提供这样的接口,提供了接口qemu还得有办法获取。既然有其它物理网卡能passthrough了干嘛还要实现virtio物理网卡来passthrough,这样做没什么用。

vdpa

这张图也来自于redhat博客,图太多可能博客作者搞混了图,原图是不对,当时可能想把vpda往vfio/mdev方面靠,首先mdev并不要求必须遵循virtio标准,vdpa实现了数据面的virtio标准,并且把datapath offload到了硬件,控制面没有offload继续由qemu模拟,硬件设备虚拟出来的vdpa未必实现了virtio标准要求的pcie功能,显然用vfio不行,没有vfio就不能利用vfio控制iommu的代码,vdpa需要自己开发,内核中没有upstream,控制面利用vhost协议,vhost再调用vhost-vdpa,vhost-vdpa调用硬件厂商提供的接口把控制信息下放到硬件中。

这种方法硬件网卡直接把包DMA到guest中了,问题是kick和call还是很麻烦,kick到了kvm,kvm通知vhost-vdpa,vhost-vdpa调用硬件驱动通知硬件。硬件call时中断给了硬件驱动,硬件驱动通知vhsot-vdpa,vhost-vdpa通知kvm,kvm把中断注入guest,如果guest用dpdk poll mode driver,kick和call就不成问题了。

搞了这么复杂,第一是为了让硬件网卡实现起来容易,硬件网卡厂商基于SRIOV和Scable IOV实现资源分割,再模拟一下vring操作就可以包装出vdpa。第二是为了实现热迁移,寄希望于硬件厂商的driver能提供接口获取DMA写了那些guest内存。第三guest和host用vdpa统一用virtio-net驱动,qemu只增加一个vhost-vdpa模块,qemu也简单,硬件的不同让硬件厂商的驱动屏蔽,厂商同时提供硬件和驱动,其它模块都不动就能适配不同厂商。

性能分析

guest保持不变,用的都是virtio-net驱动,中断有开销,协议栈有开销,这是避免不了的。定性分析一下三种转发模型的利弊,忽略virtio full offload。首先上限受制于PCIE插槽的物理能力,当然真实场景下很难达到物理上限,只有dpdk l2fwd这些简单模型下才能达到。包处理就是一条流水线,达到无缝配合才能实现性能最高,并行几条流水线处理就更好了。

简单总结一下影响性能的几个因素,分析每种因素在三种模块中的影响。

  • 中断收包有上下文切换,有切换就会有性能开销。

vhost-net在物理网卡收发包用了中断,vhost-user用了dpdk pmd没有中断开销。

  • zero-copy,当然是拷贝越少越好了,最好零拷贝。

vdpa就是zero-copy。vhost-net 和vhost-user rx无法zero-copy,假如rx要实现zero copy,guest alloc skb,backend把skb给了硬件网卡驱动,驱动设置给硬件网卡,硬件哪知道要来的包是给哪个虚拟机,假如硬件网卡一个队列对应一个虚拟机,那guest不提供skb,硬件网卡就丢包了,rx skb只能由硬件网卡驱动分配,分配时能不能从guest内存中分配,好像也不行,没法和guest同步,只能从内核或者ovs空间中分配skb,分配的skb也没法共享给guest,必须拷贝。vhost-net和vhsot-user tx都能实现zero-copy,guest alloc skb,backend直到等物理网卡驱动DMA走再通知guest发送完了,你可以回收skb了,有点麻烦,第一guest可能得等好久才能回收,第二等硬件网卡发送完了通知哪个guest回收难实现。所以vhost-net和vhost-user zero-copy都不现实。

  • hugepage,能减少TBL miss。

vhost-user模型中qemu和ovs-dpdk都用到了hugepage,坏处是内存不能超卖。

  • 多队列。

就是说参与转发的CPU多少,从串行变成并行,当然能提高性能了,但CPU资源是有限的,到底几个队列好呢,物理网卡有多队列,virtio-net也有多队列,按流分队列还是按包分队列,不管怎样进哪个队列由物理网卡来实现,软件实现不划算,物理网卡计算一个hash值写到skb中,到了virtio-net再根据这个值再入virtio-net队列。发送最简单配置就是一个cpu一个队列。

  • 无锁

减少同步的开销,DPDK中有无锁队列,CPU之间传递skb用无锁队列开销就小。另一方面就是查流表和写流表实现无锁,用urcu等等。

  • 接力干还是一口气干到底

pipeline设计问题,接力干就是一个cpu把skb处理一会然后把skb放入队列交由下一下cpu处理,下一个cpu poll还是上个cpu通知下一个cpu开始干活,poll没sbk就白白浪费cpu,通知机制有开销和时延。一口气干到底就是就是一个cpu收skb处理skb最后发送skb,把所有活都干了,多个cpu同时开干,配合多队列好一点。这两种模式的不同涉及到cpu cache/prefech等技术的利用,接力干对cache/prefetch友好。vhost-net是接力干的,vhost-user两个模式都能用,用户态修改代码和调试都容易一点。

vhost-net

vhost-user

vdpa

interrupt

有开销

无开销

无开销

zero-copy

不能用

不能用

没必要用

hugepage

不能用

用了

没必要用

mutilple queue

能用

能用

能用

lock

没有

不存在

pipeline

分工衔接通知复杂CPU不能专心干活

灵活设计,CPU可以独占

不存在

整体来看vdpa是性能最高的,还省cpu,但是价钱贵,性能其次是vhost-user,有点费cpu,vhost性能最差。

总结

vhost-net用的最多,配套完善,稳定成熟。vhost-user据我所知在电信级别云中很常用,电信级别云只追求性能,不考虑超卖,vcpu强绑定,不跨numa,网元数据面passthrough,数据面和控制面通过vhost-user通信。如果数据面用了vhost-user能热迁移能横向扩展,这样更符合NFV的理念,但普通openstack环境vhost-user就不能用内核的功能了,内核qos,netfilter,connection tracking,三层转发和arp都不能用了,意味着openstack安全组,QOS和DVR都没法用了,只能用高性能三层专用网关。vpda个人没用过,没有这样的硬件,可能很多公司已经开始试用了,恐怕是一张很贵的卡,不仅要处理virtio还要处理vxlan,虚拟机能用,裸机也能用。

没有完美的开源技术,只有最佳实践经验,最佳适用场景,用起来,修改起来,回馈开源社区。

参考文献

https://www.redhat.com/en/blog/deep-dive-virtio-networking-and-vhost-net

https://www.redhat.com/en/blog/journey-vhost-users-realm

https://www.redhat.com/en/blog/how-deep-does-vdpa-rabbit-hole-go

https://cloud.tencent.com/developer/article/1792378

https://cloud.tencent.com/developer/article/1792373

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • virtio和ovs介绍
  • vhost-net
  • vhost-user
  • virtio full offload
  • vdpa
  • 性能分析
  • 总结
  • 参考文献
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档