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