skb_clone/pskb_copy/skb_copy

在Linux对网络数据包的处理过程中,会用到对skb的拷贝,skb的拷贝分成了几种拷贝,之所以分成几种拷贝,在于不同情况下,可能需要修改的skb范围不同,核心思想在于尽可能小的重新开辟内存,尽可能的共享内存,共享数据区。
对一个正常的skb来讲,一般要包括sk_buff以及数据区两部分。而数据区又分成普通数据区和skb_shared_info.

  • skb_clone: 只拷贝sk_buff, 换而言之,整个数据区从策略上讲,是没有计划修改的。
  • pskb_copy: 拷贝sk_buff以及普通数据区两部分,对于skb_shared_info,会使用指针指向的方式。
  • skb_copy: 实现全部拷贝

skb_clone

struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask)
{
    struct sk_buff *n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
    #define C(x) n->x = skb->x
    ...
    atomic_set(&n->users, 1);
    C(head);
    C(data);
    C(tail);
    C(end);

    atomic_inc(&(skb_shinfo(skb)->dataref));
    skb->cloned = 1;
    
    return n;
}

skb_clone的两个常见用法

  • br_flood 当中,会skb_clone
  • 当skb向上层协议栈传递时。
    例如在ip_rcv当中:
static __inline__ int deliver_skb(struct sk_buff *skb,
                  struct packet_type *pt_prev)
{                                                                                                                                                     
    atomic_inc(&skb->users);
    return pt_prev->func(skb, skb->dev, pt_prev);
}
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
{
    struct iphdr *iph;

    /* When the interface is in promisc. mode, drop all the crap
     * that it receives, do not try to analyse it.
     */
    if (skb->pkt_type == PACKET_OTHERHOST)
        goto drop;

    IP_INC_STATS_BH(IPSTATS_MIB_INRECEIVES);

    if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
        IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
        goto out;
    }   
    ...
}
static inline int skb_shared(const struct sk_buff *skb)
{                                                                                                                                                     
    return atomic_read(&skb->users) != 1;
}

static inline struct sk_buff *skb_share_check(struct sk_buff *skb, int pri) 
{
    might_sleep_if(pri & __GFP_WAIT);
    if (skb_shared(skb)) {                                                                                                                            
        struct sk_buff *nskb = skb_clone(skb, pri);
        kfree_skb(skb);
        skb = nskb;
    }    
    return skb; 
}

pskb_copy

struct sk_buff *alloc_skb(unsigned int size, int gfp_mask)
{
    struct sk_buff *skb;
    u8 *data;

    /* Get the HEAD */
    skb = kmem_cache_alloc(skbuff_head_cache,
                   gfp_mask & ~__GFP_DMA);                                          // sk_buff memory.
    if (!skb)
        goto out;

    /* Get the DATA. Size must match skb_add_mtu(). */
    size = SKB_DATA_ALIGN(size);
    data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);    // SKB Data Area
    if (!data)
        goto nodata;

    memset(skb, 0, offsetof(struct sk_buff, truesize));
    skb->truesize = size + sizeof(struct sk_buff);
    atomic_set(&skb->users, 1);
    skb->head = data;
    skb->data = data;
    skb->tail = data;
    skb->end  = data + size;

    atomic_set(&(skb_shinfo(skb)->dataref), 1);
    skb_shinfo(skb)->nr_frags  = 0;
    skb_shinfo(skb)->tso_size = 0;
    skb_shinfo(skb)->tso_segs = 0;
    skb_shinfo(skb)->frag_list = NULL;
out:
    return skb;
nodata:
    kmem_cache_free(skbuff_head_cache, skb);
    skb = NULL;
    goto out;
}

struct sk_buff *pskb_copy(struct sk_buff *skb, int gfp_mask)
{
    /*
     *  Allocate the copy buffer
     */
    struct sk_buff *n = alloc_skb(skb->end - skb->head, gfp_mask);

    if (!n)
        goto out;

    /* Set the data pointer */
    skb_reserve(n, skb->data - skb->head);
    /* Set the tail pointer and length */
    skb_put(n, skb_headlen(skb));
    /* Copy the bytes */
    memcpy(n->data, skb->data, n->len);
    n->csum      = skb->csum;
    n->ip_summed = skb->ip_summed;

    n->data_len  = skb->data_len;
    n->len       = skb->len;

    if (skb_shinfo(skb)->nr_frags) {
        int i;

        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
            skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
            get_page(skb_shinfo(n)->frags[i].page);
        }
        skb_shinfo(n)->nr_frags = i;
    }

    if (skb_shinfo(skb)->frag_list) {
        skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list;
        skb_clone_fraglist(n);
    }
    copy_skb_header(n, skb);
out:
    return n;
}

skb_copy

struct sk_buff *skb_copy(const struct sk_buff *skb, int gfp_mask)
{
    int headerlen = skb->data - skb->head;
    /*   
     *  Allocate the copy buffer
     */
    struct sk_buff *n = alloc_skb(skb->end - skb->head + skb->data_len,
                      gfp_mask);
    if (!n) 
        return NULL;

    /* Set the data pointer */
    skb_reserve(n, headerlen);
    /* Set the tail pointer and length */
    skb_put(n, skb->len);
    n->csum      = skb->csum;
    n->ip_summed = skb->ip_summed;

    if (skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len))
        BUG();

    copy_skb_header(n, skb);
    return n;
}

posted @ 2018-05-01 16:47  johnson.c  阅读(1134)  评论(0编辑  收藏  举报