您的位置:首页 > 运维架构 > Linux

【Linux4.1.12源码分析】二层报文发送之GSO条件判断

2016-09-29 23:45 741 查看
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分段是如何实现的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息