閱讀56 返回首頁    go 阿裏雲 go 技術社區[雲棲]


網絡子係統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

  上一篇:go 網絡子係統31_網絡設備的注冊與注銷
  下一篇:go 編譯原理詞法分析