【Linux4.1.12源碼分析】二層報文發送之GSO條件判斷

4.1.12內核中,GSO報文的判斷和分段的入口函數是validate_xmit_skb,其中使用netif_needs_gso用來判斷軟件是否要進行GSO分段,skb_gso_segment實現報文的GSO分段,本篇重點講述GSO分段的判斷條件,即netif_needs_gso相關函數。

1、netif_needs_gso函數

static inline bool netif_needs_gso(struct sk_buff *skb,
				   netdev_features_t features)
{
	return skb_is_gso(skb) && (!skb_gso_ok(skb, features) ||   //skb 爲gso報文,且feature不包含skb->gso_type  或者
                                                                   //skb 爲gso報文,且skb_ipsummed不爲CHECKSUM_PARTIAL和CHECKSUM_UNNECESSARY
		unlikely((skb->ip_summed != CHECKSUM_PARTIAL) &&
			 (skb->ip_summed != CHECKSUM_UNNECESSARY)));
}
2、skb_gso_ok函數

static inline bool skb_gso_ok(struct sk_buff *skb, netdev_features_t features)
{
	return net_gso_ok(features, skb_shinfo(skb)->gso_type) &&	//feature包含gso_type 並且skb沒有frag_list或者feature包含NETIF_F_FRAGLIST
	       (!skb_has_frag_list(skb) || (features & NETIF_F_FRAGLIST));
}
3、net_gso_ok函數

static inline bool net_gso_ok(netdev_features_t features, int gso_type)
{
	netdev_features_t feature = gso_type << NETIF_F_GSO_SHIFT;

	/* check flags correspondence */
	BUILD_BUG_ON(SKB_GSO_TCPV4   != (NETIF_F_TSO >> NETIF_F_GSO_SHIFT));
	BUILD_BUG_ON(SKB_GSO_UDP     != (NETIF_F_UFO >> NETIF_F_GSO_SHIFT));
	BUILD_BUG_ON(SKB_GSO_DODGY   != (NETIF_F_GSO_ROBUST >> NETIF_F_GSO_SHIFT));
	BUILD_BUG_ON(SKB_GSO_TCP_ECN != (NETIF_F_TSO_ECN >> NETIF_F_GSO_SHIFT));
	BUILD_BUG_ON(SKB_GSO_TCPV6   != (NETIF_F_TSO6 >> NETIF_F_GSO_SHIFT));
	BUILD_BUG_ON(SKB_GSO_FCOE    != (NETIF_F_FSO >> NETIF_F_GSO_SHIFT));
	BUILD_BUG_ON(SKB_GSO_GRE     != (NETIF_F_GSO_GRE >> NETIF_F_GSO_SHIFT));
	BUILD_BUG_ON(SKB_GSO_GRE_CSUM != (NETIF_F_GSO_GRE_CSUM >> NETIF_F_GSO_SHIFT));
	BUILD_BUG_ON(SKB_GSO_IPIP    != (NETIF_F_GSO_IPIP >> NETIF_F_GSO_SHIFT));
	BUILD_BUG_ON(SKB_GSO_SIT     != (NETIF_F_GSO_SIT >> NETIF_F_GSO_SHIFT));
	BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL != (NETIF_F_GSO_UDP_TUNNEL >> NETIF_F_GSO_SHIFT));
	BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL_CSUM != (NETIF_F_GSO_UDP_TUNNEL_CSUM >> NETIF_F_GSO_SHIFT));
	BUILD_BUG_ON(SKB_GSO_TUNNEL_REMCSUM != (NETIF_F_GSO_TUNNEL_REMCSUM >> NETIF_F_GSO_SHIFT));

	return (features & feature) == feature;<span style="white-space:pre">		</span>//features 包含feature
}

從上面的函數可以判斷是否需要進行GSO分段,主要 兩個參數features(硬件支持的特性)和skb報文。主要判斷條件是features是否包含skb->gso_type。那麼我們來看下features是怎麼得到的,其實是通過netif_skb_features得到的。

netif_skb_features函數

netdev_features_t netif_skb_features(struct sk_buff *skb)
{
	struct net_device *dev = skb->dev;
	netdev_features_t features = dev->features;	//獲取設備的features
	u16 gso_segs = skb_shinfo(skb)->gso_segs;

	if (gso_segs > dev->gso_max_segs || gso_segs < dev->gso_min_segs)
		features &= ~NETIF_F_GSO_MASK;		//如果gso_segs不在規定範圍內,則去掉NETIF_F_GSO_MASK標記

	/* If encapsulation offload request, verify we are testing
	 * hardware encapsulation features instead of standard
	 * features for the netdev
	 */
	if (skb->encapsulation)
		features &= dev->hw_enc_features;	//如果是封裝報文,則feature需要和hw_enc_features取交集, 主流設備均不支持gso offload能力

	if (skb_vlan_tagged(skb))		//如果是vlan報文,刷新feature值
		features = netdev_intersect_features(features,		//features與vlan_feature取交集
						     dev->vlan_features |
						     NETIF_F_HW_VLAN_CTAG_TX |
						     NETIF_F_HW_VLAN_STAG_TX);

	if (dev->netdev_ops->ndo_features_check)
		features &= dev->netdev_ops->ndo_features_check(skb, dev,
								features);
	else
		features &= dflt_features_check(skb, dev, features);	//刷新vlan feature,最終會調用netdev_intersect_features函數

	return harmonize_features(skb, features);	//更新mpls feature 和csum feature
}
netdev_intersect_features函數

static inline netdev_features_t netdev_intersect_features(netdev_features_t f1,
							  netdev_features_t f2)
{
	if (f1 & NETIF_F_GEN_CSUM)
		f1 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);	//添加NETIF_F_V4_CSUM | NETIF_F_V6_CSUM 標記
	if (f2 & NETIF_F_GEN_CSUM)
		f2 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);	//添加NETIF_F_V4_CSUM | NETIF_F_V6_CSUM 標記

	f1 &= f2;						//取交集
	if (f1 & NETIF_F_GEN_CSUM)
		f1 &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);	//刪除NETIF_F_V4_CSUM | NETIF_F_V6_CSUM 標記

	return f1;
}
harmonize_features函數

static netdev_features_t harmonize_features(struct sk_buff *skb,
	netdev_features_t features)
{
	int tmp;
	__be16 type;

	type = skb_network_protocol(skb, &tmp);
	features = net_mpls_features(skb, features, type);	//mpls feature更新

	if (skb->ip_summed != CHECKSUM_NONE &&
	    !can_checksum_protocol(features, type)) {
		features &= ~NETIF_F_ALL_CSUM;		//去掉NETIF_F_ALL_CSUM標記
	} else if (illegal_highdma(skb->dev, skb)) {
		features &= ~NETIF_F_SG;		//去掉NETIF_F_SG標記
	}

	return features;
}

判斷GSO分段的條件基本搞清楚了,下一篇將分析報文GSO分段是如何實現的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章