前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ovs vxlan实现代码分析

ovs vxlan实现代码分析

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

惠伟:ovs vxlan 时延和吞吐​zhuanlan.zhihu.com

接前面这个写起,比较过linux kernel vxlan device和ovs vxlan的性能,很好奇ovs vxlan是怎么实现的,linux kernel vxlan device是用如下命令创建的。

代码语言:javascript
复制
ip link add vxlan0 type vxlan id 1111 dstport 5799 remote 10.145.69.49 local 10.145.69.26 dev eth4
[root@openstack607 huiwei]#  ip -d link show vxlan0
375: vxlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT qlen 1000
    link/ether ba:6f:38:6f:bf:9a brd ff:ff:ff:ff:ff:ff promiscuity 0
    vxlan id 1111 remote 10.145.69.49 local 10.145.69.26 dev eth4 srcport 0 0 dstport 5799 ageing 300

发现ovs也创建了一个vxlan device

代码语言:javascript
复制
[root@openstack607 huiwei]# ip -d link show vxlan_sys_4789
241: vxlan_sys_4789: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65000 qdisc noqueue master ovs-system state UNKNOWN mode DEFAULT qlen 1000
    link/ether 56:bc:a6:9b:c2:fd brd ff:ff:ff:ff:ff:ff promiscuity 1
    vxlan id 0 srcport 0 0 dstport 4789 nolearning ageing 300 udp6zerocsumrx
    openvswitch_slave

ovsdb-server存储数据,ovs-vsctl通过ovsdb协议读写ovsdb-server,ovs-vswitchd进行报文转发,ovs-ofctl通过openflow信息给ovs-vswitchd添加/删除flow,那ovs-vsctl添加一个vxlan port,ovs-vswitchd怎么知道的,答案就是ovs-vswitch和ovsdb-server之间也有连接,ovs-vswitch通过IDL感知ovsdb-server的变化。

ovs-vswitchd main线程一直while处理ovsdb-server变化和ovs-appctl命令,ovs-vsctl创建一个ovs vxlan port,最终调用到了dpif_netlink_port_add。

代码语言:javascript
复制
main
 ├─unixctl_server_create
 ├─bridge_init
 └─while//特别注意这个while循环
    ├─bridge_run
    |  ├─ovsdb_idl_run
    |  └─bridge_reconfigure
    |     └─bridge_add_ports
    |        └─iface_create
    |           └─ofproto_port_add
    |              └─(ofproto_class->port_add)port_add
    |                 └─dpif_port_add
    |                     └─(dpif_class->port_add)dpif_netlink_port_add
    ├─unixctl_server_run
    |   └─run_connection
    |      └─process_command
    |         └─(command->cb)
    ├─netdev_run
    ├─memory_wait
    ├─bridge_wait
    ├─unixctl_server_wait
    ├─netdev_wait
    └─pool_block

dpif_netlink_port_add用netlink发送给内核,是不是和ip link add道理一样。

代码语言:javascript
复制
dpif_netlink_port_add
  └─dpif_netlink_rtnl_port_create_and_add
      └─dpif_netlink_rtnl_port_create
          ├─dpif_netlink_rtnl_create
          |   └─dpif_netlink_rtnl_create//注意这个RTM_NEWLINK和IFF_UP
          └─dpif_netlink_port_add__//通知内核ovs添加vxlan_vport

内核ovs模块初始化,内核处理ovs route netlink发送来的添加vxlan口的消息

代码语言:javascript
复制
vxlan_init_module
  └─rtnl_link_register(&vxlan_link_ops)
rtnl_newlink
  ├─rtnl_create_link
  |   ├─dev=kzalloc()
  |   └─alloc_netdev_mqs
  |       └─vxlan_setup
  ├─(ops->newlink)vxlan_newlink
  └─rtnl_configure_link
      └─__dev_change_flags
          └─__dev_open
              └─(ops->ndo_open)vxlan_open

内核除了创建一个vxlan device还要创建一个vport device,vport device里嵌套 vxlan device,内核openvswitch模块处理添加vport, 如果内核不支持RTM_NEWLINK,这儿还可以创建出vxlan device并且UP它。

代码语言:javascript
复制
ovs_vport_cmd_new
  └─new_vport
      └─ovs_vport_add
           └─vxlan_create
               ├─vxlan_tnl_create
               |   ├─vxlan_dev_create//等价于ip link add vxlan0 type vxlan
               |   └─dev_change_flags
               |       └─__dev_change_flags//UP这个口
               |           └─__dev_open
               |               ├─(ops->ndo_open)也就是vxlan_open
               |               └─netpoll_rx_enable
               └─ovs_netdev_link
                   ├─vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), name);
                   ├─netdev_rx_handler_register(vport->dev, netdev_frame_hook,vport);
                   ├─dev_disable_lro
                   └─dev_set_promiscuity

最终都调用到这个三个函数vxlan_netlink/vxlan_setup/vxlan_open

代码语言:javascript
复制
vxlan_newlink
  └─vxlan_dev_configure
      ├─vxlan_ether_setup
      ├─__vxlan_change_mtu
      └─register_netdevice

vxlan_setup
  └─gro_cells_init
      ├─netif_napi_add
      └─napi_enable

vxlan_open
  └─vxlan_sock_add
     └─__vxlan_sock_add
          ├─vxlan_socket_create
          |   └─vxlan_create_sock
          |      └─udp_sock_create                                                     
          ├─udp_tunnel_notify_add_rx_port
          ├─setup_udp_tunnel_sock
          └─udp_tunnel_encap_enable

现在一个报文来到了物理网卡,收包,vxlan decap,查流表,然后交给虚拟机。

看这儿有多少次softirq就知道性能怎样了,一个物理网卡pps大时softirq就承受不住了。

代码语言:javascript
复制
#物理网卡处理中断,触发softirq
i40e_intr
  └─napi_schedule_irqoff

#i40e softirq把包放到per cpu backlog,触发softirq
i40e_napi_poll
  └─i40e_clean_rx_irq
      ├─i40e_fetch_rx_buffer
      |   └─__napi_alloc_skb
      |       └─skb->dev = napi->dev;
      ├─i40e_process_skb_fields
      └─i40e_receive_skb
          └─napi_gro_receive
              └─napi_skb_finish
                  └─netif_receive_skb_internal
                      └─__netif_receive_skb

#rx softirq处理,一起捅到udp层,把skb上的dev切换成vxlan device,再入backlog,触发softirq
process_backlog
  └─__netif_receive_skb
      └─__netif_receive_skb_core
         └─deliver_skb
            └─(pt_prev->func)ip_rcv
              └─ip_rcv_finish
                  └─ip_route_input_noref
                      └─ip_route_input_slow
                         └─ip_local_deliver
                             └─ip_local_deliver_finish
                                └─(ipprot->handler)udp_rcv
                                    └─__udp4_lib_rcv
                                       └─udp_queue_rcv_skb
                                           └─vxlan_rcv
                                              ├─skb->dev = vxlan->dev;//这儿skb的切换到vxlan device
                                              └─gro_cells_receive
                                                 └─netif_rx
                                                     └─netif_rx_internal
                                                         └─enqueue_to_backlog
                                                            └─____napi_schedule

#softirq处理,终于走到查流表了,查完流表output到qvo口,走veth pair,再到qbr,
#qbr再wakeup vhost worker,到此这个softirq的活终于干完了。
#接下来就是vhost worker把skb copy到virtio,再通知kvm irq inject。
process_backlog
  └─__netif_receive_skb
    └─__netif_receive_skb_core
       └─(rx_handler)netdev_frame_hook
           └─netdev_port_receive
               └─ovs_vport_receive
                   ├─ovs_flow_key_extract
                   └─ovs_dp_process_packet

再看发包,包从虚拟机来,进入ovs查流表,然后从vxlan port出去。

代码语言:javascript
复制
#不管怎样给datapath安装流表时需要把vxlan的local_ip和remote_ip下发给datapath   
ovs_flow_cmd_set
   └─sw_flow_actions─┐
      ovs_flow_cmd_new─┤
ovs_packet_cmd_execute─┤
                        └─ovs_nla_copy_actions
                            └─__ovs_nla_copy_actions
                               └─validate_set
                                   └─case OVS_KEY_ATTR_TUNNEL:validate_and_copy_set_tun//填好ovs_tunnel_info
                                       └─__add_action(OVS_KEY_ATTR_TUNNEL_INFO)

把tunnel信息加入skb,按道理dst_entry在ip lookup后找到,难道这儿类似于dst_entry的缓存???

代码语言:javascript
复制
do_execute_actions
  └─case OVS_ACTION_ATTR_SET:execute_set_action
      └─skb_dst_set//把ovs_tunnel_info加到skb上

datapath执行output,vport device中嵌套的vxlan device在这儿就发挥作用了。

代码语言:javascript
复制
do_execute_actions
  └─do_output
      └─ovs_vport_send
          ├─skb->dev = vport->dev;//没有这就不知道用哪个ndo_start_xmit
          └─dev_queue_xmit
              └─__dev_queue_xmit
                 ├─validate_xmit_skb
                 ├─dev_hard_start_xmit
                 |   └─xmit_one
                 |       └─netdev_start_xmit
                 |           └─ndo_start_xmit就是vxlan_xmit
                 └─dev_xmit_complete

vxlan encap,然后一口气把包从物理网卡发送出去。

代码语言:javascript
复制
vxlan_xmit
  └─vxlan_xmit_one
      ├─vxlan_build_skb
      └─udp_tunnel_xmit_skb
          └─iptunnel_xmit
             └─ip_local_out_sk
                └─dst_output_sk
                    └─ip_output
                        └─ip_finish_output
                            └─dst_neigh_output
                                └─neigh_direct_output
                                    └─dev_queue_xmit
                                         └─dev_hard_start_xmit
                                             └─netdev_start_xmit
                                                 └─i40e_lan_xmit_frame
                                                      └─i40e_xmit_frame_ring
                                                           └─i40e_tx_map
                                                              ├─dma_map_single
                                                              └─netdev_tx_sent_queue

经过代码分析,发现ovs kernel datapath vxlan完全利用了kernel vxlan,那ovs dpdk datapath vxlan怎么实现的呢,vxlan encap ip lookup和arp怎么处理的呢,vpp的vxlan怎么实现的,相比ovs实现有什么区别,请听后面分解。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档