pskb_copy skb_clone skb_copy

  刚离开时,总想着四海为家,仗剑行走天涯!一副数风流人物还看今朝!
  惊涛拍浪时,偶而有个电话,却又烦唠叨而言它!殊不知以后也没多少时间听父母叨叨了!!!

不哆嗦了!!今天查bug 又涉及到了skb_clone 以及skb_copy ;那就来看看吧!顺便做做笔记,免得总是忘了,好记性不如乱博客

对skb拷贝无非就是 skb的描述符填充字段;线性数据区 linear  非线性区frags  以及frags_list

 skb_copy:拷贝skb描述符+线性缓冲区+非线性缓冲区 

/**
 *    skb_copy    -    create private copy of an sk_buff
 *    @skb: buffer to copy
 *    @gfp_mask: allocation priority
 *
 *    Make a copy of both an &sk_buff and its data. This is used when the
 *    caller wishes to modify the data and needs a private copy of the
 *    data to alter. Returns %NULL on failure or the pointer to the buffer
 *    on success. The returned buffer has a reference count of 1.
 *
 *    As by-product this function converts non-linear &sk_buff to linear
 *    one, so that &sk_buff becomes completely private and caller is allowed
 *    to modify all the data of returned buffer. This means that this
 *    function is not recommended for use in circumstances when only
 *    header is going to be modified. Use pskb_copy() instead.
 */
    /* 拷贝skb描述符+线性缓冲区+非线性缓冲区 */
struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)
{
    int headerlen = skb_headroom(skb); /* 头部空间长度   head---->data之间长度*/
    // skb空间+ skb以外的数据空间
    unsigned int size = skb_end_offset(skb) + skb->data_len;  // head----end + frag/frag_list的数据长度 
    struct sk_buff *n = __alloc_skb(size, gfp_mask,
                    skb_alloc_rx_flag(skb), NUMA_NO_NODE);

    if (!n)
        return NULL;

    /* Set the data pointer  //保留头部空间*/
    skb_reserve(n, headerlen);
    /* Set the tail pointer and length 修正偏移尾部指针修改总长度 */
    skb_put(n, skb->len);
    /* 拷贝数据  从skb的data 偏移 -headerlen 开始  copy 长度为 headerlen+skb->len */
    if (skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len))
        BUG();
    //copy header
    copy_skb_header(n, skb);
    return n;
}
View Code

pskb:拷贝skb描述符+ 线性数据缓冲区, 线性缓冲区外---非线性区(frags/frag_list)数据共享

 1 /**
 2  *    __pskb_copy_fclone    -  create copy of an sk_buff with private head.
 3  *    @skb: buffer to copy
 4  *    @headroom: headroom of new skb
 5  *    @gfp_mask: allocation priority
 6  *    @fclone: if true allocate the copy of the skb from the fclone
 7  *    cache instead of the head cache; it is recommended to set this
 8  *    to true for the cases where the copy will likely be cloned
 9  *
10  *    Make a copy of both an &sk_buff and part of its data, located
11  *    in header. Fragmented data remain shared. This is used when
12  *    the caller wishes to modify only header of &sk_buff and needs
13  *    private copy of the header to alter. Returns %NULL on failure
14  *    or the pointer to the buffer on success.
15  *    The returned buffer has a reference count of 1.
16  */
17 
18 struct sk_buff *__pskb_copy_fclone(struct sk_buff *skb, int headroom,
19                    gfp_t gfp_mask, bool fclone)
20 {
21     unsigned int size = skb_headlen(skb) + headroom;
22     int flags = skb_alloc_rx_flag(skb) | (fclone ? SKB_ALLOC_FCLONE : 0);
23     struct sk_buff *n = __alloc_skb(size, gfp_mask, flags, NUMA_NO_NODE);
24 
25     if (!n)
26         goto out;
27 
28     /* Set the data pointer  保留头部空间  */
29     skb_reserve(n, headroom);
30     /* Set the tail pointer and length 修正尾指针和数据长度*/
31     skb_put(n, skb_headlen(skb));
32     /* Copy the bytes  拷贝线性缓冲区 从 skb->data 开始 长度为n->len 拷贝到 n->data*/
33     skb_copy_from_linear_data(skb, n->data, n->len);
34 //SKB_TRUESIZE//truesize 大小为: 线性区数据+非线性区数据+sizeof(sk_buff) + sizeof(skb_shared_info)
35     n->truesize += skb->data_len;
36     n->data_len  = skb->data_len;
37     n->len         = skb->len;
38 
39     if (skb_shinfo(skb)->nr_frags) {/* 若有片段 */
40         int i;
41 
42         if (skb_orphan_frags(skb, gfp_mask)) {
43             kfree_skb(n);
44             n = NULL;
45             goto out;
46         }
47         for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
48             skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];//n->frags 执行skb的frags  --- 共享 数据
49             skb_frag_ref(skb, i);//对page增加引用计数
50         }
51         skb_shinfo(n)->nr_frags = i;
52     }
53 
54     if (skb_has_frag_list(skb)) {
55         skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list;
56         skb_clone_fraglist(n);// 增加每个skb的users计数
57     }
58 //COPY HEAD FILED字段
59     copy_skb_header(n, skb);
60 out:
61     return n;
62 }
View Code

skb_clone:由skb_clone()函数克隆一个skb,然后共享其他数据。虽然可以提高效率,但是存在一个很大的缺陷,就是当有克隆skb指向共享数据区是,那么共享数据区的数据就不能被修改了。所以说如果只是让多个skb查看共享数据区内容,则可以用skb_clone()函数来克隆这几个skb出来,提高效率。但如果涉及到某个skb要修改sk_buff结构的数据区,则必须要用pskb_copy  skb_copy这几个函数来克隆拷贝出skb

/**
 *    skb_clone    -    duplicate an sk_buff
 *    @skb: buffer to clone
 *    @gfp_mask: allocation priority
 *
 *    Duplicate an &sk_buff. The new one is not owned by a socket. Both
 *    copies share the same packet data but not structure. The new
 *    buffer has a reference count of 1. If the allocation fails the
 *    function returns %NULL otherwise the new buffer is returned.
 *
 *    If this function is called from an interrupt gfp_mask() must be
 *    %GFP_ATOMIC.
 */
/*/ 用于修改skb描述符中的某些字段;克隆skb,该函数只克隆sk_buff部分
     其数据区域公用(需要递增引用计数)*/
struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
{ /* 获取到支持克隆的skb */
    struct sk_buff_fclones *fclones = container_of(skb,
                               struct sk_buff_fclones,
                               skb1);
    struct sk_buff *n;
    //        若发送标记有零拷贝,则拷贝用户空间的片段缓存到内核空间
    if (skb_orphan_frags(skb, gfp_mask))
        return NULL;
    /* 如果skb可以被克隆,fclone---SKB_FCLONE_ORIG标志在allock_skb时 设置 通过 flag 是否 允许 SKB_ALLOC_FCLONE 来实现;
    并且克隆引用为1          */
    if (skb->fclone == SKB_FCLONE_ORIG &&
        atomic_read(&fclones->fclone_ref) == 1) {
        n = &fclones->skb2;
        atomic_set(&fclones->fclone_ref, 2);
    } else {
        if (skb_pfmemalloc(skb))
            gfp_mask |= __GFP_MEMALLOC;

        n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
        if (!n)//为新克隆的skb分配内存
            return NULL;

        kmemcheck_annotate_bitfield(n, flags1);
        n->fclone = SKB_FCLONE_UNAVAILABLE;
    }

    return __skb_clone(n, skb);
}
/*
 * You should not add any new code to this function.  Add it to
 * __copy_skb_header above instead.
 */
static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
{
#define C(x) n->x = skb->x

    n->next = n->prev = NULL;
    n->sk = NULL;
    __copy_skb_header(n, skb);

    C(len);
    C(data_len);
    C(mac_len);
    n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len;
    n->cloned = 1;
    n->nohdr = 0;
    n->destructor = NULL;
    C(tail);
    C(end);
    C(head);
    C(head_frag);
    C(data);
    C(truesize);
    atomic_set(&n->users, 1);

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

    return n;
#undef C
}
/*
atomic_inc(&(skb_shinfo(skb)->dataref));这个简单的说就是,因为sk_buff的数据区和分片结构是一体的,连内存申请和释放都是一起的。而dataref是分片结构skb_shared_info中的一个 表示sk_buff的数据区和分片结构被多少skb共享的 成员字段。这里调用atomic_inc()函数让该引用计数器自增,表明克隆skb对sk_buff数据区和分片结构的共享引用。*/
skb->cloned = 1;表明这是个克隆的skb结构体
View Code

上述 使用中都遇到了:

skb_orphan_frags以及__alloc_skb

 __alloc_skb:

算了 下次再看吧;

http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子
原文地址:https://www.cnblogs.com/codestack/p/14284277.html