网络子系统23_skb常用函数

来源:互联网 发布:山东冶金设计院知乎 编辑:程序博客网 时间:2024/06/11 05:38
//分配新的skb->data,将旧的skb->data、skb_shinfo(skb)->frags、skb_shinfo(skb)->frag_list中的内容拷贝到新skb->data的连续内存空间中,释放frags或frag_list//其中frags用于支持分散聚集IO,frags_list用于支持数据分片1.1 int __skb_linearize(struct sk_buff *skb, int gfp_mask){unsigned int size;u8 *data;long offset;struct skb_shared_info *ninfo;int headerlen = skb->data - skb->head;int expand = (skb->tail + skb->data_len) - skb->end;//如果此skb被共享if (skb_shared(skb))BUG();//产生BUG oops//还需要的内存大小if (expand <= 0)expand = 0;//新申请的skb的大小size = skb->end - skb->head + expand;//将size对齐到SMP_CACHE_BYTESsize = SKB_DATA_ALIGN(size);//分配物理上联系的内存data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);if (!data)return -ENOMEM;//拷贝if (skb_copy_bits(skb, -headerlen, data, headerlen + skb->len))BUG();//初始化skb的skb_shared_info结构ninfo = (struct skb_shared_info*)(data + size);atomic_set(&ninfo->dataref, 1);ninfo->tso_size = skb_shinfo(skb)->tso_size;ninfo->tso_segs = skb_shinfo(skb)->tso_segs;//fraglist为NULLninfo->nr_frags = 0;ninfo->frag_list = NULL;offset = data - skb->head;//释放之前skb的dataskb_release_data(skb);//将skb指向新的dataskb->head = data;skb->end  = data + size;//重新初始化新skb的各个报头指针skb->h.raw   += offset;skb->nh.raw  += offset;skb->mac.raw += offset;skb->tail    += offset;skb->data    += offset;skb->cloned    = 0;skb->tail     += skb->data_len;skb->data_len  = 0;return 0;}1.2 SKB_DATA_ALIGN(X)(((X) + (SMP_CACHE_BYTES - 1)) & \ ~(SMP_CACHE_BYTES - 1))//将skb中起始offset的内容拷贝到to中,拷贝长度为len1.3 int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len){int i, copy;//skb->len-skb->data_len,得到skb->head到skb->end之间的数据量int start = skb_headlen(skb);//偏移量+len > skb->len,说明可供拷贝的数据量不够if (offset > (int)skb->len - len)goto fault;//计算需要拷贝的数据量if ((copy = start - offset) > 0) {if (copy > len)copy = len;//拷贝memcpy(to, skb->data + offset, copy);if ((len -= copy) == 0)//拷贝量=需要拷贝的长度return 0;offset += copy;//更新偏移量to     += copy;}//接下来的数据从skb_shinfo的frags数组中进行拷贝for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {int end;//遍历fragsend = start + skb_shinfo(skb)->frags[i].size;if ((copy = end - offset) > 0) {u8 *vaddr;if (copy > len)copy = len;//映射skb的frag到内核地址空间vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]);//拷贝memcpy(to,       vaddr + skb_shinfo(skb)->frags[i].page_offset+       offset - start, copy);//解除映射kunmap_skb_frag(vaddr);if ((len -= copy) == 0)return 0;offset += copy;to     += copy;}start = end;}//从skb的frag_list中拷贝if (skb_shinfo(skb)->frag_list) {struct sk_buff *list = skb_shinfo(skb)->frag_list;for (; list; list = list->next) {int end;BUG_TRAP(start <= offset + len);end = start + list->len;if ((copy = end - offset) > 0) {if (copy > len)copy = len;//递归调用if (skb_copy_bits(list, offset - start,  to, copy))goto fault;if ((len -= copy) == 0)return 0;offset += copy;to     += copy;}start = end;}}if (!len)return 0;fault:return -EFAULT;}



//保证skb->data 到 skb->tail之间有len长度的数据2.1 static inline int pskb_may_pull(struct sk_buff *skb, unsigned int len){//skb->data 到 skb->tail之间的数据足够len长度if (likely(len <= skb_headlen(skb)))return 1;//len长度超过skb总长度if (unlikely(len > skb->len))return 0;//移动后边的数据到skb->data中return __pskb_pull_tail(skb, len-skb_headlen(skb)) != NULL;}//调用流程pskb_may_pull->__pskb_pull_tail//delta为需要从frags或者frag_list向前移动的数据量2.2 unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta){//eat为去除当前skb可用内存,还需要多少内存int i, k, eat = (skb->tail + delta) - skb->end;//判断当前skb是否被克隆if (eat > 0 || skb_cloned(skb)) {//对sk_buff重新分配头if (pskb_expand_head(skb, 0, eat > 0 ? eat + 128 : 0,     GFP_ATOMIC))return NULL;}//从skb的offset(skb->tail),拷贝delta个字节到skb->tail之后if (skb_copy_bits(skb, skb_headlen(skb), skb->tail, delta))BUG();//没有分段if (!skb_shinfo(skb)->frag_list)goto pull_pages;//由于数据已经拷贝到了skb->data中,因此需要释放frags,frag_list中被拷贝过的数据//计算从frags数组中拷贝的数据量eat = delta;for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {//寻找到满足eat这么多数据量的最后一个pageif (skb_shinfo(skb)->frags[i].size >= eat)//在frags数组中的数据量可以满足delta时,则只释放frags即可goto pull_pages;eat -= skb_shinfo(skb)->frags[i].size;}//eat仍不为0,说明从frag_list中进行了拷贝,释放frag_listif (eat) {struct sk_buff *list = skb_shinfo(skb)->frag_list;struct sk_buff *clone = NULL;struct sk_buff *insp = NULL;do {//list为null,说明数据量不够if (!list)BUG();//当前skb的长度小于需要的长度if (list->len <= eat) {//找到下一个skbeat -= list->len;//list指向下一个需要的skblist = list->next;//insp指向当前的skbinsp = list;} else {//此时insp指向前一个skb//说明当前skb可以满足需要的数据量if (skb_shared(list)) {//但是当前skb被共享clone = skb_clone(list, GFP_ATOMIC);//对最后那个拷贝不完全的skb,进行克隆if (!clone)return NULL;//list指向当前被克隆的的skb//insp指向下一个skbinsp = list->next;list = clone;} else {//list与insp指向当前的skbinsp = list;}//修改最后一个skb,移动指针,删除掉被拷贝的数据if (!pskb_pull(list, eat)) {if (clone)kfree_skb(clone);//递减clone的引用计数return NULL;}break;}} while (eat);//list指向frag_list头//直到list遍历到数据量足够的最后一个skbwhile ((list = skb_shinfo(skb)->frag_list) != insp) {skb_shinfo(skb)->frag_list = list->next;//释放当前的skbkfree_skb(list);//递减当前skb的引用技术,如果引用计数=0,则释放list}//说明最后一个skb只被拷贝了一部分,将此skb挂到frag_list头if (clone) {clone->next = list;skb_shinfo(skb)->frag_list = clone;}}pull_pages:eat = delta;k = 0;//释放frags中的pagefor (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {if (skb_shinfo(skb)->frags[i].size <= eat) {put_page(skb_shinfo(skb)->frags[i].page);eat -= skb_shinfo(skb)->frags[i].size;} else {skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];if (eat) {skb_shinfo(skb)->frags[k].page_offset += eat;skb_shinfo(skb)->frags[k].size -= eat;eat = 0;}k++;}}skb_shinfo(skb)->nr_frags = k;skb->tail     += delta;skb->data_len -= delta;return skb->tail;}

//skb->users指定skb被引用的个数3.1 static inline int skb_shared(const struct sk_buff *skb){return atomic_read(&skb->users) != 1;}


原创粉丝点击