最近在研究ipq5018的有线驱动,发现它支持S/G (scatter gather)。
驱动首先得告诉内核,我支持S/G,然后内核才会产生对应得skb。在set_features接口中:
static void syn_dp_if_set_features(struct nss_dp_data_plane_ctx *dpc)
{
struct net_device *netdev = dpc->dev;
netdev->features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM | NETIF_F_FRAGLIST | NETIF_F_SG;
netdev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM | NETIF_F_FRAGLIST | NETIF_F_SG;
netdev->vlan_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM | NETIF_F_FRAGLIST | NETIF_F_SG;
netdev->wanted_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM | NETIF_F_FRAGLIST | NETIF_F_SG;
}
有两个重要的feature,就是:NETIF_F_FRAGLIST 和 NETIF_F_SG。
前者对应 frag_list,表示有多个skb,链在一起。
后者对应nr_frags,表示一个skb,有多个数据片。
这也是非线性skb的两种形式。
判断一个skb是否是非线性skb
static inline bool skb_is_nonlinear(const struct sk_buff *skb)
{
return skb->data_len;
}
data_len是fragments中数据的大小。
非线性信息都存储在skb_shared_info结构体中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28  | /* This data is invariant across clones and lives at * the end of the header data, ie. at skb->end. */struct skb_shared_info {        __u8            flags;        __u8            meta_len;        __u8            nr_frags;        __u8            tx_flags;        unsigned short  gso_size;        /* Warning: this field is not always filled in (UFO)! */        unsigned short  gso_segs;        struct sk_buff  *frag_list;        struct skb_shared_hwtstamps hwtstamps;        unsigned int    gso_type;        u32             tskey;        /*         * Warning : all fields before dataref are cleared in __alloc_skb()         */        atomic_t        dataref;        /* Intermediate layers must ensure that destructor_arg         * remains valid until skb destructor */        void *          destructor_arg;        /* must be last field, see pskb_expand_head() */        skb_frag_t      frags[MAX_SKB_FRAGS];}; | 
使用
#define skb_shinfo(SKB) ((struct skb_shared_info *)(skb_end_pointer(SKB)))
可以获得一个skb的 skb_shared_info,这个结构体存储在head数据区的结尾。
frag_list
单向链表,指向下一个skb碎片,使用宏skb_walk_frags可以遍历各个skb:
#define skb_walk_frags(skb, iter) \
for (iter = skb_shinfo(skb)->frag_list; iter; iter = iter->next)
frags
这个是一个数组,nr_frags表示当前有多少个碎片,最多有MAX_SKB_FRAGS个碎片。
1 2 3 4 5 6 7 8 9 10 11 12 13  | /* To allow 64K frame to be packed as single skb without frag_list we * require 64K/PAGE_SIZE pages plus 1 additional page to allow for * buffers which do not start on a page boundary. * * Since GRO uses frags we allocate at least 16 regardless of page * size. */#if (65536/PAGE_SIZE + 1) < 16#define MAX_SKB_FRAGS 16UL#else#define MAX_SKB_FRAGS (65536/PAGE_SIZE + 1)#endifextern int sysctl_max_skb_frags; | 
根据内核注释,frags最多可以容纳64K的数据。如果数据超过64K,那就需要是用frag_list,来链接额外的skb。
碎片数据结构体是:
typedef struct bio_vec skb_frag_t;
它是一个bio_vec结构体,定义在linux/bvec.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17  | /** * struct bio_vec - a contiguous range of physical memory addresses * @bv_page:   First page associated with the address range. * @bv_len:    Number of bytes in the address range. * @bv_offset: Start of the address range relative to the start of @bv_page. * * The following holds for a bvec if n * PAGE_SIZE < bv_offset + bv_len: * *   nth_page(@bv_page, n) == @bv_page + n * * This holds because page_is_mergeable() checks the above property. */struct bio_vec {        struct page     *bv_page;        unsigned int    bv_len;        unsigned int    bv_offset;}; | 
一个碎片就是一个page。因为一个page,不是所有的内容都可以用来存储数据,所以 MAX_SKB_FRAGS 增多了一个,满足64K
获取碎片的长度
1 2 3 4 5 6 7 8  | /** * skb_frag_size() - Returns the size of a skb fragment * @frag: skb fragment */static inline unsigned int skb_frag_size(const skb_frag_t *frag){        return frag->bv_len;} | 
所以,对于tx一个skb的数据流程:
1 先tx head数据区,数据 skb->data,长度 skb_headlen(skb) skb->len - skb->data_len
2 在tx frags 碎片区,个数nr_frags,每个碎片的数据skb_frag_address(frag),长度 skb_frag_size(frag)
3 遍历frag list: skb_walk_frags(skb, iter_skb) ,一个就是一个skb,执行 1和2 发送。
疑问:
skb->data_len,是不是包括frag list中所有skb的数据长度?