说明
linux kernel version:5.7.1 openvswitch version:2.15.0 source insight version:4.0
概要
本篇文章来介绍ovs内核部分的流表实现原理,这里采用带入式疗法来分析其代码实现,那么首先从内核收包开始说起,以e1000网卡为例开始阅读之旅。
设备收包
这里简单
介绍一下驱动收包流程,当网卡收到数据包之后,会发送硬件中断通知cpu进行处理,网卡在打开的时候会注册对应的中断处理函数,这里e1000网卡在启动时候会调用e1000_open函数,在e1000_open函数中会调度e1000_request_irq函数进行网卡硬中断的注册:
static int e1000_request_irq(struct e1000_adapter *adapter) { struct net_device *netdev = adapter->netdev; irq_handler_t handler = e1000_intr; int irq_flags = IRQF_SHARED; int err; err = request_irq(adapter->pdev->irq, handler, irq_flags, netdev->name, netdev); if (err) { e_err(probe, "Unable to allocate interrupt Error: %d\n", err); } return err; }
这里可以看到e1000的硬中断响应函数为e1000_intr,即当网卡触发中断时os将会调用到这个函数,在napi模式下触发软中断,然后进入e1000的poll函数,e1000的poll函数为e1000_clean,e1000_clean调用e1000_clean_rx_irq进行数据包的处理,即从dma驱动拷贝数据到内核skb,然后使用e1000_receive_skb进入skb的处理,首先进行gro(和tso对应,gro用于收包,tso用于发包)流程napi_gro_receive,gro的处理函数是dev_gro_receive,napi_skb_finish根据dev_gro_receive的返回结果决定是否将skb上送协议栈,如果dev_gro_receive返回GRO_NORMAL则调用gro_normal_one上送协议栈处理,然后在经过一系列的调用之后(netif_receive_skb_list_internal->__netif_receive_skb_list->__netif_receive_skb_list_core->__netif_receive_skb_core
)进入协议栈处理函数__netif_receive_skb_core,从这开始就到了协议栈的部分。
协议栈处理
上面说到数据包来到了__netif_receive_skb_core,这个函数主要完成如下工作:
-
打时间戳
-
重置网络头部
-
xdp处理
-
vlan处理
-
tc处理
-
ptype_all处理(tcpdump、tap\tun等)
-
二层网桥处理(rx_handler)
-
ptype_base处理(ip/arp等)
在二层往网桥处理部分,即rx_handler的处理中,就会到ovs的流程了,目前内核中这个rx_handler的值可能有两个:内核网桥的br_handle_frame
(在将设备设置为混杂模式时就会将此设备的rx_handler设置为br_handle_frame)、ovs网桥的netdev_frame_hook
(在将设备添加到ovs网桥设备时就会将rx_handler赋值为netdev_frame_hook),所以后面的分析就从netdev_frame_hook
函数开始。
ovs设备入口
netdev_frame_hook分析,代码中一些关键点有注释:
/* Called with rcu_read_lock and bottom-halves disabled. */ /* Must be called with rcu_read_lock. */ static void netdev_port_receive(struct sk_buff *skb) { struct vport *vport; //vport信息保存在dev->rx_handler_data中的 vport = ovs_netdev_get_vport(skb->dev); if (unlikely(!vport)) goto error; if (unlikely(skb_warn_if_lro(skb))) goto error; /* Make our own copy of