作者:[email protected]部落格:linuxfocus.blog.chinaunix.net
繼續ip_append_data,
if (copy > length)
copy = length;
if (!(rt->dst.dev->features&NETIF_F_SG)) {
/* 如果網卡不支援Scatter/gather特性,直接拷貝資料 */
unsigned int off;
off = skb->len;
if (getfrag(from, skb_put(skb, copy),
offset, copy, off, skb) 0) {
__skb_trim(skb, off);
err = -EFAULT;
goto error;
}
} else {
/* 如果支援Scatter/gather特性 */
/* 得到已儲存的buffer個數 */
int i = skb_shinfo(skb)->nr_frags;
skb_frag_t *frag = &skb_shinfo(skb)->frags[i-1];
/* 得到實體頁面的位址 */
struct page *page = sk->sk_sndmsg_page;
int off = sk->sk_sndmsg_off;
unsigned int left;
if (page && (left = PAGE_SIZE - off) > 0) {
/* 該頁還有空餘空間,可以填充一部分資料 */
/* 如果要拷貝的長度大于剩餘空間,調整拷貝的長度 */
if (copy >= left)
copy = left;
if (page != frag->page) {
/* 此次資料與sock上次緩沖資料不是位于同一實體頁面 */
if (i == MAX_SKB_FRAGS) {
/* 超過了緩存資料最大個數 */
err = -EMSGSIZE;
goto error;
}
get_page(page);
/* 将資料填充到實體頁 */
skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
frag = &skb_shinfo(skb)->frags[i];
}
} else if (i MAX_SKB_FRAGS) {
/* 将拷貝長度調整為實體頁最大值 */
if (copy > PAGE_SIZE)
copy = PAGE_SIZE;
/* 配置設定一個新的實體頁 */
page = alloc_pages(sk->sk_allocation, 0);
if (page == NULL) {
err = -ENOMEM;
goto error;
/* 儲存實體頁位址 */
sk->sk_sndmsg_page = page;
sk->sk_sndmsg_off = 0;
/* 填充實體頁 */
skb_fill_page_desc(skb, i, page, 0, 0);
frag = &skb_shinfo(skb)->frags[i];
} else {
err = -EMSGSIZE;
/* 調用getfrag填充資料 */
if (getfrag(from, page_address(frag->page)+frag->page_offset+frag->size, offset, copy, skb->len, skb) 0) {
/* 調整各個偏移以及長度 */
sk->sk_sndmsg_off += copy;
frag->size += copy;
skb->len += copy;
skb->data_len += copy;
skb->truesize += copy;
atomic_add(copy, &sk->sk_wmem_alloc);
}
/* 繼續下一個 */
offset += copy;
length -= copy;
}
return 0;
error:
inet->cork.length -= length;
IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS);
return err;
看到這裡,這個函數就已經結束了。在之前的學習過程中,有些不明白的問題,或者幹脆了解錯了的東西,在完全看完這個函數之後,已經有了新的認識。
明天,我會對這些問題進行整理,并總結一下這個函數。