最近在研究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) #endif extern 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的数据长度?