1.alloc_skb()
alloc_skb()用来分配SKB。数据缓存区和SKB描述符是两个不同的实体,这就意味着,在分配一个SKB时,需要分配两块内存,一块是数据缓存区,一块是SKB描述符。__alloc_skb()调用kmem_cache_alloc_node()从高速缓存中获取一个sk_buff结构的空间,然后调用kmalloc_node_track_caller()分配数据缓存区。参数说明如下:
size,待分配SKB的线性存储区的长度。
gfp_mask,分配内存的方式,见表25-3。
fclone,预测是否会克隆,用于确定从哪个高速缓存中分配。
node,当支持NUMA(非均匀质存储结构)时,用于确定何种区域中分配SKB。
144 struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
145 int fclone, int node)
146 {
147 struct kmem_cache *cache;
148 struct skb_shared_info *shinfo;
149 struct sk_buff *skb;
150 u8 *data;
151
152 cache = fclone ? skbuff_fclone_cache : skbuff_head_cache;
153
154 /* Get the HEAD */
155 skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);
156 if (!skb)
157 goto out;
158
159 /* Get the DATA. Size must match skb_add_mtu(). */
160 size = SKB_DATA_ALIGN(size);
161 data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info),
162 gfp_mask, node);
163 if (!data)
164 goto nodata;
165
166 memset(skb, 0, offsetof(struct sk_buff, truesize));
167 skb->truesize = size + sizeof(struct sk_buff);
168 atomic_set(&skb->users, 1);
169 skb->head = data;
170 skb->datadata = data;
171 skb->tail = data;
172 skb->end = data + size;
173 /* make sure we initialize shinfo sequentially */
174 shinfo = skb_shinfo(skb);
175 atomic_set(&shinfo->dataref, 1);
176 shinfo->nr_frags = 0;
177 shinfo->gso_size = 0;
178 shinfo->gso_segs = 0;
179 shinfo->gso_type = 0;
180 shinfo->ip6_frag_id = 0;
181 shinfo->frag_list = NULL;
182
183 if (fclone) {
184 struct sk_buff *child = skb + 1;
185 atomic_t *fclone_ref = (atomic_t *) (child + 1);
186
187 skb->fclone = SKB_FCLONE_ORIG;
188 atomic_set(fclone_ref, 1);
189
190 child->fclone = SKB_FCLONE_UNAVAILABLE;
191 }
192 out:
193 return skb;
194 nodata:
195 kmem_cache_free(cache, skb);
196 skb = NULL;
197 goto out;
198 }
152 根据参数fclone确定从哪个高速缓存中分配SKB。
155 调用kmem_cache_alloc_node()从选定的高速缓存中分配一个SKB。在此从分配标志中去除GFP_DMA,是为了不从DMA内存区域中分配SKB描述符,因为DMA内存区域比较小且有特定用途,没有必要用来分配SKB描述符。而后面分配数据缓存区时,就不会去掉GFP_DMA标志,因为很有可能数据缓存区就需要在DMA内存区域中分配,这样硬件可以直接进行DMA操作,参见161~162行。
160 在分配数据缓存区之前,强制对给定的数据缓存区大小size作对齐操作。
161-165 调用kmalloc_node_track_caller()分配数据缓存区,其长度为size和sizeof(struct skb_shared_info)之和,因为在缓存区尾部紧跟着一个skb_shared_info结构。
168-181 初始化新分配SKB描述符和skb_shared_info结构。
183-191 如果是skbuff_fclone_cache高速缓存中分配SKB描述符,则还需置父SKB描述符的fclone为SKB_FCLONE_ORIG,表示可以被克隆;同时将子SKB描述符的fclone成员置为SKB_FCLONE_UNAVAILABLE,表示该SKB还没有被创建出来;最后将引用计数置为1。
最后SKB结构如图3-13所示,在图右边所示的内存块中部,可以看到对齐操作所带来的填充区域。需要说明的是,__alloc_skb()一般不被直接调用,而是被封装函数调用,如__netdev_alloc_skb()、alloc_skb()、alloc_skb_fclone()等函数。
2.dev_alloc_skb()
dev_alloc_skb()也是一个缓存区分配函数,通常被设备驱动用在中断上下文中。这是一个alloc_skb()的封装函数,因为是在中断处理函数中被调用的,因此要求原子操作(GFP_ATOMIC)。
- 1124 static inline struct sk_buff *dev_alloc_skb(unsigned int length)
- 1125 {
- 1126 return __dev_alloc_skb(length, GFP_ATOMIC);
- 1127 }
- ... ...
- 1103 static inline struct sk_buff *__dev_alloc_skb(unsigned int length,
- 1104 gfp_t gfp_mask)
- 1105 {
- 1106 struct sk_buff *skb = alloc_skb(length + NET_SKB_PAD, gfp_mask);
- 1107 if (likely(skb))
- 1108 skb_reserve(skb, NET_SKB_PAD);
- 1109 return skb;
- 1110 }
1108 调用skb_reserve()在skb->head与skb->data之间预留NET_SKB_PAD个字节。NET_SKB_PAD的定义在skbuff.h中,其值为 16。这部分空间将被填入硬件帧头,如14B的以太网帧头。
1126 以GFP_ATOMIC为内存分配优先级,表示分配过程为原子操作,不能被中断。