網絡子係統23_skb常用函數
//分配新的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_BYTES size = 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為NULL ninfo->nr_frags = 0; ninfo->frag_list = NULL; offset = data - skb->head; //釋放之前skb的data skb_release_data(skb); //將skb指向新的data skb->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中,拷貝長度為len 1.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; //遍曆frags end = 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這麼多數據量的最後一個page if (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_list if (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) { //找到下一個skb eat -= list->len; //list指向下一個需要的skb list = list->next; //insp指向當前的skb insp = list; } else { //此時insp指向前一個skb //說明當前skb可以滿足需要的數據量 if (skb_shared(list)) {//但是當前skb被共享 clone = skb_clone(list, GFP_ATOMIC);//對最後那個拷貝不完全的skb,進行克隆 if (!clone) return NULL; //list指向當前被克隆的的skb //insp指向下一個skb insp = list->next; list = clone; } else { //list與insp指向當前的skb insp = list; } //修改最後一個skb,移動指針,刪除掉被拷貝的數據 if (!pskb_pull(list, eat)) { if (clone) kfree_skb(clone);//遞減clone的引用計數 return NULL; } break; } } while (eat); //list指向frag_list頭 //直到list遍曆到數據量足夠的最後一個skb while ((list = skb_shinfo(skb)->frag_list) != insp) { skb_shinfo(skb)->frag_list = list->next; //釋放當前的skb kfree_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中的page for (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; }
最後更新:2017-04-03 15:22:11