刚离开时,总想着四海为家,仗剑行走天涯!一副数风流人物还看今朝!
惊涛拍浪时,偶而有个电话,却又烦唠叨而言它!殊不知以后也没多少时间听父母叨叨了!!!
不哆嗦了!!今天查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; }
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 }
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结构体
上述 使用中都遇到了:
skb_orphan_frags以及__alloc_skb
__alloc_skb:
算了 下次再看吧;