天天看點

核心中的UDP socket流程(11)——ip_append_data

作者:[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;

看到這裡,這個函數就已經結束了。在之前的學習過程中,有些不明白的問題,或者幹脆了解錯了的東西,在完全看完這個函數之後,已經有了新的認識。

明天,我會對這些問題進行整理,并總結一下這個函數。

繼續閱讀