最近遇到了分片导致的一系列问题,所以在这里盘点一下OVS中都有哪些已经做了分片和重组的地方,以及还有哪些地方需要做。版本还是基于2.7.0。并且datapath的时候会分别分析OVS和OVS-DPDK两块的处理。
up到userspace的时候
OVS部分
netdev_frame_hook-->netdev_port_receive-->ovs_vport_receive-->ovs_dp_process_packet-->ovs_dp_upcall
主要是判定是否是带了GSO标志,如果带了,则需要将报文进行分片上报。
OVS-DPDK部分
pmd_thread_main-->dp_netdev_process_rxq_port-->dp_netdev_input-->dp_netdev_input__-->fast_path_processing
这里不会对报文进行判定,因为目前OVS-DPDK还不支持GSO。其实个人觉得这里也没必要进行分片,因为此处报文不涉及到从kernel上报到userspace的流程。
出端口之前
OVS部分
此处的分片分为两种,一种是之前因为过conntrack的时候进行了重组,那么在出口之前就要进行分片。
netdev_frame_hook-->netdev_port_receive-->ovs_vport_receive-->ovs_dp_process_packet-->ovs_execute_actions-->do_execute_actions-->do_output
这里会根据dev的mtu和报文的mru比较需要不需要分片,通常情况下报文的mru是0,不需要分片,但是因为重组函数handle_fragments
重组时设置了报文的mru,所以分片时也根据此项来判定。调用ovs_fragment
分片完成之后会调用ovs_vport_send-->dev_queue_xmit-->dev_hard_start_xmit-->ops->ndo_start_xmit-->bond_start_xmit
进行报文发送。
另一种的报文分片是VM中设置了TSO、GSO、UFO等features,导致报文比较大,这个时候需要根据标志判定是否需要分片。
netdev_frame_hook-->netdev_port_receive-->ovs_vport_receive-->ovs_dp_process_packet-->ovs_execute_actions-->do_execute_actions-->do_output-->ovs_vport_send-->dev_queue_xmit-->dev_hard_start_xmit
这里会根据gso进行分片,分片完成后继续调用ops->ndo_start_xmit-->bond_start_xmit
进行报文发送。其实此处也会根据其他的一些特性来判定,一般比如带了TSO之类的需要网卡去分片。
OVS-DPDK部分
pmd_thread_main-->dp_netdev_process_rxq_port-->dp_netdev_input-->dp_netdev_input__-->packet_batch_per_flow_execute-->dp_netdev_execute_actions-->dp_execute_cb-->netdev_send-->netdev_dpdk_eth_send
一般会根据网卡的特性来判定是不是分片。
封装之前
OVS部分
此处的分片也分为两种,一种是之前因为过conntrack的时候进行了重组,那么在出口之前就要进行分片。
netdev_frame_hook-->netdev_port_receive-->ovs_vport_receive-->ovs_dp_process_packet-->ovs_execute_actions-->do_execute_actions-->do_output
这里会根据dev的mtu和报文的mru比较需要不需要分片,通常情况下报文的mru是0,不需要分片,但是因为重组函数handle_fragments
重组时设置了报文的mru,所以分片时也根据此项来判定。调用ovs_fragment
分片完成之后调用ovs_vport_send-->vxlan_xmit-->dev_queue_xmit-->dev_hard_start_xmit-->ops->ndo_start_xmit-->vxlan_xmit
进行报文封装。
另一种的报文分片是VM中设置了TSO、GSO、UFO等features,导致报文比较大,这个时候需要根据标志判定是否需要分片。
netdev_frame_hook-->netdev_port_receive-->ovs_vport_receive-->ovs_dp_process_packet-->ovs_execute_actions-->do_execute_actions-->do_output-->ovs_vport_send-->vxlan_xmit-->dev_queue_xmit-->dev_hard_start_xmit
这里会根据gso进行分片,分片完成后继续调用ops->ndo_start_xmit-->vxlan_xmit
进行封装。
OVS-DPDK部分
pmd_thread_main-->dp_netdev_process_rxq_port-->dp_netdev_input-->dp_netdev_input__-->packet_batch_per_flow_execute-->dp_netdev_execute_actions-->dp_execute_cb
- 在
OVS_ACTION_ATTR_TUNNEL_PUSH
的情况下会进行报文封装,调用接口push_tnl_action-->netdev_push_header-->netdev_tnl_push_udp_header
进行封装,需要分片的话需要在该接口之前。 - 调用
dp_netdev_recirculate
重新查表。
重组的处理
conntrack的时候
OVS部分
netdev_frame_hook-->netdev_port_receive-->ovs_vport_receive-->ovs_dp_process_packet-->ovs_execute_actions-->do_execute_actions-->ovs_ct_execute
中会判定报文是否分片报文,是的话调用函数handle_fragments
进行重组,如果没有分片直接进行conntrack处理。
OVS-DPDK部分
pmd_thread_main-->dp_netdev_process_rxq_port-->dp_netdev_input-->dp_netdev_input__-->packet_batch_per_flow_execute-->dp_netdev_execute_actions-->odp_execute_actions-->conntrack_execute
没有对报文分片的判定,直接进行conntrack的处理。
解封装之后
OVS部分
首先是Linux Kernel接收报文,然后最终调用udp_rcv-->__udp4_lib_rcv-->udp_queue_rcv_skb-->encap_rcv
会调用vxlan_rcv
进行报文解封装,最终调用netdev_port_receive-->ovs_vport_receive-->ovs_dp_process_packet
进行查表和转发,需要注意的是这块根本没有分片和重组。
OVS-DPDK部分
pmd_thread_main-->dp_netdev_process_rxq_port-->dp_netdev_input-->dp_netdev_input__-->packet_batch_per_flow_execute-->dp_netdev_execute_actions-->dp_execute_cb
- 在
OVS_ACTION_ATTR_TUNNEL_POP
的情况下会进行报文解封装,调用接口netdev_pop_header-->netdev_vxlan_pop_header
进行解封装,需要重组的话需要在该接口之后进行。 - 调用
dp_netdev_recirculate
重新查表。
匹配流表的之前
bridge_run-->bridge_reconfigure-->ofproto_create
会设置默认的分片配置。一般是normal,表示分片的源、目的端口为0。bridge_run-->bridge_run__-->ofproto_run-->handle_openflow-->handle_openflow__-->handle_set_config-->set_frag_handling
主要是设置分片相关的配置,目前有normal、drop、reassemable、nx_match等,但是目前reassemable功能还不支持,需要我们来完成。rule_dpif_lookup_from_table
根据不同的模式下发不同的流表。
由以上信息可以得知,支持reassemable的话首先是不是所有的报文都进行重组,但是我们并不是所有的报文都需要重组,只有需要匹配L4的匹配项时才需要,都进行重组的话相当消耗性能,第二就是查找流表的时候还要保证分片的首包先进行流表下拉,如果不是首个分片先到,那这个报文要先缓存等待重组才行。