赵占旭的博客

OpenvSwitch 数据层面瓶颈分析

ovs情况下


avatar

virtio和vhost通信

虚拟机到物理机的网络通信,即virtio和vhost的通信。基本方式是virtio创建队列,用于和vhost共享。

  • 虚拟机发送报文的时候,将报文入队,然后通知kvm,kvm通知vhost的线程进行报文处理。
  • 虚拟机收包的流程为,vhost的线程将报文放入队列,通知kvm,触发virtio注册的中断。

此处一般都会是瓶颈点,pktgen测试为500k pps。

目前主要的优化点在于虚拟网卡的features,features有两块决定,一块是virtio驱动,目前基本上支持所有features,另一块就是vhost的features。两块都支持的features最后是我们能看到的features。

  • TSO、GSO、tx csum,开启关闭的方式为ethtool -K eth0 tx on/off
    如果虚拟机的网卡显示支持以上的features,那么TCP报文发送的时候将不做分片和校验,即可以发送64k的报文。这个相对于mtu长度的报文来说,可以极大的提高传输速率。最简单的例子是同物理机的两个虚拟机,如果支持这些features,我们使用iperf3测试能到20Gbps,而不支持则只有2Gbps。至于在其他的场景中会有怎样的表现我们后面会分析。
  • 多队列,即每个virtio的队列对应着一个vhost线程,我们可以看到,我们流量之所以打不上去,主要是因为vhost线程占用cpu到100%,如果多个vhost可以打更多的流量,经过测试,开多队列比不开至少性能提升50%,但是我们不建议开启,因为这样的话,会占用更多的非虚拟机CPU。xml需要添加配置<driver name='vhost' queues='4'/>,虚拟机中ethtool -L eth0 combined 4

TODO:
待补充。

vhost报文处理

主要是将报文发送给tap设备。vhost主要是拿到报文之后,会将报文放入某个CPU的softnet_data队列input_pkt_queue,然后触发软中断。

  • vhost_net模块加载的时候有个参数experimental_zcopytx,默认是0,主要是发送报文时是否判定可以0拷贝的参数,这个应该只是判断是否0拷贝的条件之一,经过测试,开关对流量没有明显的差别。
  • TSO、GSO特性在vhost模块基本上不做什么操作,只是简单地标志设置之类的,报文不会分片。

TODO:
网上有zero copy的patch。

ovs报文转发

vhost触发中断之后,会调用net_rx_action接收到报文,最终调用ovs_flow_tbl_lookup匹配流表,调用ovs_execute_actions执行对应的actions。
这里处理方式是将报文的2-4层的头信息都提取出来存储到key中,然后计算hash匹配流表,此处涉及到classifer模块,对流表的更细化处理。所以此处跟传统的交换路由相比将会更灵活,可以选择2-4层的任何字段进行匹配,也可以修改,但是带来的问题就是性能会差。

  • 此处没有太多优化的点,如果我们目前的交换都是2层的,没有其他的需求的话,使用ovs是大材小用。
  • TSO、GSO需要查看的地方比较多,不同的actions可能不同,但就output的action来说的话,不同类型的口也可能不同,列举一种常见情况。
    • 同宿主机的两个虚拟机通信直接通信,使用的是tap口,这时虚拟机支持gso,所以报文大小支持64k,ovs接收报文ovs_vport_receive会将报文的mru设置为0,执行actions的output接口do_output的时候会判定mru,如果mru是0,就直接发送不分片。然后不分片调用到dev_queue_xmit,然后调用到dev_hard_start_xmit会判定gso并且进行分片,最终调用到tap的tun_net_xmit,然后继续通知vhost接收报文。

TODO:
normal对性能有多少影响。

ovs和主机外的通信

ovs和主机外进行通信,目前主要使用vxlan(也可以geneve或者stt),工作方式为确定输出端口为vxlan端口之后,会将报文进行封装,封装格式根据vxlan端口的配置进行,vxlan_xmit最终调用ip_local_out进入协议栈,此时localout和postrouting两处的策略还是对vxlan报文有影响的。

  • 此处的报文封装和解封装也没有太多的优化空间,对性能影响大小也需要验证,因为目前主要的瓶颈是vhost,所以这里目前并不是瓶颈。
  • vxlan的校验,有些网卡可以支持,但是这块有没有优化的必要还需要在关注,目前我们使用的x540网卡不支持,x550网卡支持。
  • TSO、GSO也列举一种常见的情况。
    • 不同宿主机之间的通信,如果走的是vxlan的时候,虚拟机使用的是tap口,这时虚拟机支持gso,所以报文大小支持64k,ovs接收报文ovs_vport_receive会将报文的mru设置为0,执行actions的output接口do_output的时候会判定mru,如果mru是0,就直接发送不分片。然后不分片调用dev_queue_xmit,然后调用到dev_hard_start_xmit会判定gso并且进行分片,最终调用到vxlan模块的vxlan_xmit进行封装然后调用ip_local_out进入协议栈。

Linux模块datapath,同宿主机的两个虚拟机直接转发。

1
2
3
4
5
6
7
tso on
iperf3 -c host -t 60 -M 88 28Gbps
iperf3 -c host -t 60 32Gbps

tso off
iperf3 -c host -t 60 -M 88 189Mbps
iperf3 -c host -t 60 4.68Gbps

Linux模块datapath,不同宿主机的两个虚拟机通过vxlan转发。

1
2
3
4
5
6
7
tso on
iperf3 -c host -t 60 -M 88 168Mbps
iperf3 -c host -t 60 2.94Gbps

tso off
iperf3 -c host -t 60 -M 88 137Mbps
iperf3 -c host -t 60 2.76Gbps

ovs自带datapath,不同宿主机的两个虚拟机通过vxlan转发。

1
2
3
tso on
iperf3 -c host -t 60 -M 88 170Mbps
iperf3 -c host -t 60 3.2Gbps

ovs-dpdk情况下


avatar

首先说一下ovs-dpdk和ovs的区别。

  • ovs整套方案是virtio+vhost+kvm+ovs+vxlan(geneve,暂不支持stt),而这其中virtio是虚拟机的内核模块,属于单独的线程,vhost、kvm、ovs的datapath、vxlan都是单独的主机内核模块,ovs的datapath和vxlan是一个线程,剩下的vhost、virtio和kvm都是单独线程,总共4个线程。所以一个报文的传输过程涉及到多次的线程间切换。但是报文量大的时候稍微的优势就是每个线程有点类似pipeline,cpu的instructions cache miss能少点。而ovs-dpdk的整套方案是virtio+vhost-user+kvm+ovs-dpdk,这里面virtio和kvm是内核的单独线程,剩下的vhost-user和ovs-dpdk是同一个线程,并且绑定CPU,不会进行线程切换,总共2个线程会切换。所以每个报文的传输涉及的进程切换稍微少一些。这块影响不大。
  • vhost和vhost-user,前者是单独线程没绑定cpu,可以被切换出去,需要信号唤醒的,类似中断。后者是ovs-dpdk datapath线程轮询的,需要绑定CPU。节省了线程来回切换消耗的性能。vhost需要接受kvm模块的激活,vhost-user因为是轮询,所以禁用了kvm的通知。个人认为这块影响最大。
  • ovs和ovs-dpdk,两个datapath一个运行在kernel,一个运行在userspace,当datapath没有匹配流表的时候,ovs会通过netlink将报文上传到用户态匹配流表,而ovs-dpdk因为都在用户态,所以只有一个cache来存datapath的刘表,cache中查不到,继续在所有的流表中查找,存入cache中即可,省去netlink的通信。另外就是报文发送给虚拟机的时候,ovs会调用tap虚拟设备的发送接口,然后通过通知vhost线程做接下来的操作,而ovs-dpdk直接调用vhost-user的接口发送给virtio即可,流程略简单一些。

virtio和vhost-user通信

虚拟机到物理机的网络通信,即virtio和vhost的通信。 基本方式是virtio创建队列,用于和vhost-user共享。他们之间的通信靠的是unix socket。

  • 虚拟机发送报文的时候,将报文入队,vhost-user轮询接收报文处理。
  • 虚拟机收包的流程为,vhost-user的线程将报文放入队列,通知kvm,触发virtio注册的中断。

此处相对于vhost会有一些提升,vhost时pktgen测试为500k pps,vhost-user相同流表时800k pps,如果单虚拟机的最简单流表能到1.6M pps,只是单纯的dpdk的vhost-user,能到2.7M pps。
而使用iperf3测试的时候,如果开了tso的话,速率能到7.9Gbps和5.8Gbps,并且很不稳定,因为重传多所以波动大,关闭tso之后,速率能到2.5Gbps和183Mbps,也是不稳定。

目前的版本对于features支持不如vhost。

  • TSO、GSO、tx csum目前还不支持,但是经过修改代码,能够支持这些特性,修改完成之后,因为受限虚拟机流量的限制,只跑到10Gbps,理论上应该强于vhost。

TODO:
待补充。

ovs-dpdk报文转发

vhost-user和ovs-dpdk报文处理,这里主要接收报文然后进行流表的查询和actions的执行。

  • ovs-dpdk调用vhost-user的轮询接口拿到报文之后,会直接调用miniflow_extract进行报文的解析,然后调用emc_lookup进行流表匹配,最终调用ovs_execute_actions执行对应的actions。
  • 此处也会涉及到拷贝的问题,因为virtio给的报文是skb,而dpdk支持的mbuf结构,但是数据部分应该变化不大,但是接收和发送各有一次拷贝发生。

TODO:
TSO、GSO等特性的影响

ovs-dpdk和主机外的通信

ovs-dpdk和主机外进行通信,目前主要使用vxlan(也可以geneve),工作方式为确定输出端口为vxlan端口之后,会执行push_tnl_action将报文进行封装,封装格式根据vxlan端口的配置进行,最终调用dp_netdev_recirculate重新进行报文的key解析和流表查询,然后匹配到输出端口。

  • vxlan的校验,有些网卡可以支持,但是这块有没有优化的必要还需要在关注,目前我们使用的x540网卡不支持,x550网卡支持。

TODO:
TSO、GSO等特性的影响。

注意:所有文章非特别说明皆为原创。为保证信息与源同步,转载时请务必注明文章出处!谢谢合作 :-)

原始链接:http://zhaozhanxu.com/2016/09/27/SDN/OVS/2016-09-27-ovs-datapath-bottleneck/

许可协议: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。