* The downside to this alignment of the IP header is that the DMA is now * unaligned. On some architectures the cost of an unaligned DMA is high * and this cost outweighs the gains made by aligning the IP header. *
* Since this trade off varies between architectures, we allow NET_IP_ALIGN * to be overridden. */
#ifndef NET_IP_ALIGN #define NET_IP_ALIGN #endif /*
* The networking layer reserves some headroom in skb data (via
* dev_alloc_skb). This is used to avoid having to reallocate skb data when * the header has to grow. In the default case, if the header has to grow * 16 bytes or less we avoid the reallocation. *
* Unfortunately this headroom changes the DMA alignment of the resulting * network packet. As for NET_IP_ALIGN, this unaligned DMA is expensive * on some architectures. An architecture can override this value, * perhaps setting it to a cacheline in size (since that will maintain * cacheline alignment of the DMA). It must be a power of 2. *
* Various parts of the networking layer expect at least 16 bytes of * headroom, you should not reduce this. */
#ifndef NET_SKB_PAD #define NET_SKB_PAD 16 #endif ?
netdev_alloc_skb和__netdev_alloc_skb:也是用于网卡驱动分配skb,与前面提到的dev_alloc_skb的区别在于:调用alloc_skb时多了一个numa node的判断,如下: static inline struct sk_buff *netdev_alloc_skb(struct net_device *dev, unsigned int length) { }
struct sk_buff *__netdev_alloc_skb(struct net_device *dev, {
unsigned int length, gfp_t gfp_mask)
return __netdev_alloc_skb(dev, length, GFP_ATOMIC);
2
int node = dev->dev.parent ? dev_to_node(dev->dev.parent) : -1; struct sk_buff *skb;
} ?
skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, 0, node); if (likely(skb)) { skb_reserve(skb, NET_SKB_PAD); }
skb->dev = dev;
return skb;
sock_alloc_send_skb和sock_alloc_send_pskb:sock_alloc_send_skb是
sock_alloc_send_pskb的包装,一般在ip_output,af_unix, af_packet, raw.c,udp等处调用,我们主要关心skb的分配和初始化情况,socket相关的代码不作讨论: 1. 输入参数:
a) size:线性buffer的长度;(header_len)
b) data_len:paged data的长度;kernel里目前都是直接使用
sock_alloc_send_skb来调用sock_alloc_send_pskb,所以data_len = 0
2. skb分配过程:
a) 先调用alloc_skb,传入的size参数为header_len; b) 如果没有data_len,直接返回;否则 i. ii. iii.
根据data_len,获得page数目,赋给skb_shinfo(skb)->nr_frags; skb->truesize += data_len; 通过调用alloc_page分配paged data,除了最后一个frags之外,其他的frags->offset = 0; frags->size = PAGE_SIZE;最后一个的frags->size = data_len % PAGE_SIZE。
?
sk_stream_alloc_skb是sk_stream_alloc_pskb的包装,从名字上看,就是用于stream发送的时候分配skb,一般是在tcp层调用,下面三处调用sk_stream_alloc_pskb ? tso_fragment ?
tcp_sendmsg
? do_tcp_sendpages
下面两个调用sk_stream_alloc_skb ? tcp_fragment ? tcp_mtu_probe 下面分析代码: 1. 输入参数:
a) size:待发送的字节流长度;
b) mem:不知道干啥用的,看起来只是修改了skb->truesize,所有调用的地方
传递的值也都是0;
2. skb分配过程:
a) size + 根据sk->sk_proto算出来的最大头部长度(对于TCP,#define
MAX_TCP_HEADER (128 + MAX_HEADER))作为分配的skb里线性Buffer的长度;
b) 调用alloc_skb_fclone分配skb(因为很快就会被clone?) c) 调用skb_reserve,把头部空出来以备后用。
? sock_wmalloc:如果在发送一个很长的数据包,L4在处理的时候会为IP层的分片提前
做一些工作,比如将数据放置在一系列的skb中,一般调用sock_alloc_send_skb来创建这一系列skb中的第一个(可以参考ip_append_data),调用sock_wmalloc来分配剩下的分片skb。函数体本身很简单,如下:
struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, gfp_t priority) { }
if (force || atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
struct sk_buff * skb = alloc_skb(size, priority); if (skb) { }
skb_set_owner_w(skb, sk); return skb;
}
return NULL;
skb释放
kfree_skb和__kfree_skb
kfree_skb()是__kfree_skb()的一个包装,首先检查skb的引用计数skb->users,为1才继续调用__kfree_skb(),否则只是skb->users - 1(原子操作)。
在kernel的协议栈代码中,很多地方都是先显式的调用atomic_inc(&skb->users);然后处理一堆内容后,调用kfree_skb,而不是调用atomic_dec(&skb->users)。这样做的好处是,可以在合适的地方尽快释放skb,并且可以让处理skb结构的代码逻辑更独立。(调用处理skb的函数之前可能增加过skb->users,也可能没有)
__kfree_skb()是真正释放skb的函数:先将skb结构里指向其他网络子系统的指针release或put,并且调用析构函数skb->destructor(),最后通过kfree_skbmem()来释放内存: ? 首先调用skb_release_data来处理线性buffer区域和paged_data(包括frag_list);
判断skb的Data Buffer区域可以被释放的条件是以下二者之一: ?
!skb->cloned:skb没有被clone(注意被clone的和clone生成的skb->cloned
都被置1,两个skb里的skb_shinfo(skb)->dataref都atomic_inc,见skb_clone的讨论),skb没被clone,说明数据区肯定只有唯一一个skb指向;
!atomic_sub_return(skb->nohdr ? (1 << SKB_DATAREF_SHIFT) + 1 : 1, &skb_shinfo(skb)->dataref) 对skb_shinfo(skb)->dataref进行判断:
? 如果nohdr被设置,代表dataref被分成两部分,前面SKB_DATAREF_SHIFT(16)
bit代表skb->data里的payload部分的引用计数;后面16bits代表整个
skb->data的引用计数。所以需要把dataref-(1 << SKB_DATAREF_SHIFT) + 1),来判断是否可以释放skb->data;
? nohdr没被设置,不需要把skb->data的payload单独考虑引用计数,只需要?
dataref-1来决定是否释放skb->data 释放Data Buffer分为三个部分:
?
? put_page释放“paged data”;
? skb_drop_fraglist,进而调用kfree_skb,来释放frag_list里的所有skb; ? kfree(skb->head),释放“线性Buffer”。
然后根据skb->fclone标志将skb结构本身归还slab cache。见fclone讨论部分
?
skb共享复制相关操作
skb_clone, skb_cloned, skb_header_cloned, skb_shared, skb_share_check, skb_unshare, skb_clone_fraglist, skb_copy, pskb_copy, skb_header_release
内核的网络子系统核心数据结构是sk_buff,同一个skb可能会被很多个子系统共享,同时使用或修改,出于性能的考虑,内核中提供了一系列对skb的共享和拷贝函数,网络子系统根据各自的需求来调用:
skb_share: 只读的共享需求
? ?
需要共享的地方调用:直接调用atomic_inc(skb->users),或者skb_get(); 判断是否被共享:skb_shared()
static inline int skb_shared(const struct sk_buff *skb) { return atomic_read(&skb->users) != 1; }
? ?
取消共享的地方调用:kfree_skb()(见kfree_skb一节的讨论) 对分片队列的共享:skb_clone_fraglist(),(pskb_expand_head()里用了),其实就是把skb队列里每一个skb的引用计数+1;然后在取消共享时,对队列里每一个skb调用kfree_skb()见减小引用计数。
static inline void skb_drop_fraglist(struct sk_buff *skb) { }
skb_drop_list(&skb_shinfo(skb)->frag_list);
static void skb_drop_list(struct sk_buff **listp) { }
struct sk_buff *list = *listp; *listp = NULL;
do { struct sk_buff *this = list;
list = list->next; kfree_skb(this);
} while (list);
static void skb_clone_fraglist(struct sk_buff *skb) { struct sk_buff *list; }
for (list = skb_shinfo(skb)->frag_list; list; list = list->next) skb_get(list);
skb_clone: 只修改sk_buff结构本身
?
需要共享的地方调用:skb_clone() i. 首先根据skb->fclone决定采用快速clone机制还是普通机制;
新分配的skb暂时不用和skb链表,socket关联,都设为NULL;
其他成员的值都copy,需要增加引用计数的也都使用XXX_get()处理;
新skb的users为1(与alloc_skb()一样),n->nohdr = 0, destructor = NULL; 新老skb的skb->cloned = 1;
新老skb指向的数据区是一样的(包括线性buffer和paged data,分片等),则需要把dataref+1:atomic_inc(&(skb_shinfo(skb)->dataref))(整个数据区的引用计数)
ii. iii. iv. v. vi.
?
判断是否被共享: ?
skb_cloned():返回1的条件为skb->cloned = 1并且dataref的低16bit部分(整个数据区的引用计数)不为1;
static inline int skb_cloned(const struct sk_buff *skb) {
return skb->cloned &&
(atomic_read(&skb_shinfo(skb)->dataref) & SKB_DATAREF_MASK) != 1;
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库sk_buff详解(4)在线全文阅读。
相关推荐: