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分段是如何實現的。