网络驱动移植之sk_buff结构体及其相关操作函数(下)
2013-09-11 18:54
609 查看
2、结构体相关操作函数
(1)、dev_alloc_skb
实际上,函数dev_alloc_skb最终是调用__alloc_skb函数来分配数据缓冲区和sk_buff结构体的,如下图:
从dev_alloc_skb到__alloc_skb所涉及的源代码如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */
struct sk_buff *dev_alloc_skb(unsigned int length)
{
/*
* There is more code here than it seems:
* __dev_alloc_skb is an inline
*/
return __dev_alloc_skb(length, GFP_ATOMIC);
}
/* linux-2.6.38.8/include/linux/skbuff.h */
static inline struct sk_buff *__dev_alloc_skb(unsigned int length,
gfp_t gfp_mask)
{
struct sk_buff *skb = alloc_skb(length + NET_SKB_PAD, gfp_mask);
if (likely(skb))
skb_reserve(skb, NET_SKB_PAD);
return skb;
}
/* linux-2.6.38.8/include/linux/skbuff.h */
static inline struct sk_buff *alloc_skb(unsigned int size,
gfp_t priority)
{
return __alloc_skb(size, priority, 0, NUMA_NO_NODE);
}
其中,NET_SKB_PAD的值在ARM体系架构上为32。
接下来,在__alloc_skb函数中,首先通过kmem_cache_alloc_node函数(在未配置CONFIG_NUMA和CONFIG_SLOB的情况下,它的实现就是直接调用kmem_cache_alloc函数)从skbuff_head_cache高速缓存中申请一个sk_buff结构体对象。创建skbuff_head_cache高速缓存的源代码如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/socket.c */
static int __init sock_init(void)
{
...
/* Initialize skbuff SLAB cache */
skb_init();
...
}
core_initcall(sock_init); /* early initcall */
/* linux-2.6.38.8/net/core/skbuff.c */
void __init skb_init(void)
{
skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
sizeof(struct sk_buff),
0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL);
skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache",
(2*sizeof(struct sk_buff)) +
sizeof(atomic_t),
0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL);
}
申请sk_buff结构体对象的代码如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */ skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node); if (!skb) goto out; prefetchw(skb);
对于S3C2410,prefetchw函数的实现是使用GCC的内置函数__builtin_prefetch,定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/prefetch.h */
#ifndef ARCH_HAS_PREFETCHW
#define prefetchw(x) __builtin_prefetch(x,1)
#endif
__builtin_prefetch的函数原型为void __builtin_prefetch (const void *addr, ...),常用于最小化数据的存取时间。参数addr的值为将要预取的内存地址,另外,它还有两个可选的参数rw 和 locality,rw的值只能为常量0或者1,1用于写的预取,默认值0用于读的预取。关于它的详细使用说明请参考网址http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/Other-Builtins.html#Other-Builtins。
对于ARMv5,prefetchw函数使用另一种实现,而S3C2410是无法支持的。另外,对于S3C2410,__LINUX_ARM_ARCH__的值为4,在linux-2.6.38.8/arch/arm/Makefile文件中被声明。
__alloc_skb函数的另一个重要功能就是分配数据缓冲区,包括skb_shared_info结构体。先使用SKB_DATA_ALIGN宏以SMP_CACHE_BYTES(对于ARM体系架构,它的值为32)位对齐数据缓冲区(这里不包括skb_shared_info结构体)的大小,然后调用kmalloc_node_track_caller函数分配内存,代码如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */
size = SKB_DATA_ALIGN(size);
data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info),
gfp_mask, node);
if (!data)
goto nodata;
prefetchw(data + size);
其中两个主要函数的实现如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */
#define SKB_DATA_ALIGN(X) (((X) + (SMP_CACHE_BYTES - 1)) & \
~(SMP_CACHE_BYTES - 1))
/* linux-2.6.38.8/include/linux/slab.h */
#define kmalloc_node_track_caller(size, flags, node) \
kmalloc_track_caller(size, flags)
#define kmalloc_track_caller(size, flags) \
__kmalloc(size, flags)
/* linux-2.6.38.8/mm/slab.c */
void *__kmalloc(size_t size, gfp_t flags)
{
return __do_kmalloc(size, flags, NULL);
}
最后,__alloc_skb函数会完成对sk_buff和skb_shared_info两个结构体变量部分成员的初始化。
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */
memset(skb, 0, offsetof(struct sk_buff, tail));
skb->truesize = size + sizeof(struct sk_buff);
atomic_set(&skb->users, 1);
skb->head = data;
skb->data = data;
skb_reset_tail_pointer(skb);
skb->end = skb->tail + size;
#ifdef NET_SKBUFF_DATA_USES_OFFSET
skb->mac_header = ~0U;
#endif
其中,当NET_SKBUFF_DATA_USES_OFFSET未定义时,skb_reset_tail_pointer函数的定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */ static inline void skb_reset_tail_pointer(struct sk_buff *skb) { skb->tail = skb->data; }
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */ shinfo = skb_shinfo(skb); memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); atomic_set(&shinfo->dataref, 1); kmemcheck_annotate_variable(shinfo->destructor_arg);
其中,skb_shinfo函数的定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */
#define skb_shinfo(SKB) ((struct skb_shared_info *)(skb_end_pointer(SKB)))
当NET_SKBUFF_DATA_USES_OFFSET未定义时,skb_end_pointer函数的定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */ static inline unsigned char *skb_end_pointer(const struct sk_buff *skb) { return skb->end; }
__alloc_skb函数完成的工作大致如下图(图片来自《Understanding Linux Network Internals》):
另外,当NET_SKBUFF_DATA_USES_OFFSET未定义时,sk_buff_data_t的声明如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */ typedef unsigned char *sk_buff_data_t;
(2)、skb_reserve
skb_reserve函数用于在缓冲区的头部预留一些空间,其定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */
static inline void skb_reserve(struct sk_buff *skb, int len)
{
skb->data += len;
skb->tail += len;
}
skb_reserve函数只是简单地更新data和tail两个指针而已,如下图(图片来自《Understanding LinuxNetwork Internals》):
(3)、skb_put
skb_put函数会把一个数据块添加到缓冲区的尾端。
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */
unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
{
unsigned char *tmp = skb_tail_pointer(skb);
SKB_LINEAR_ASSERT(skb);
skb->tail += len;
skb->len += len;
if (unlikely(skb->tail > skb->end))
skb_over_panic(skb, len, __builtin_return_address(0));
return tmp;
}
其中,skb_tail_pointer函数在NET_SKBUFF_DATA_USES_OFFSET未定义时,其定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */ static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) { return skb->tail; }
对于ARM体系结构,在CONFIG_BUG和CONFIG_DEBUG_BUGVERBOSE都配置的情况下,SKB_LINEAR_ASSERT的定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */
#define SKB_LINEAR_ASSERT(skb) BUG_ON(skb_is_nonlinear(skb))
/* linux-2.6.38.8/include/asm-generic/bug.h */
#ifndef HAVE_ARCH_BUG_ON
#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while(0)
#endif
/* linux-2.6.38.8/arch/arm/include/asm/bug.h */
extern void __bug(const char *file, int line) __attribute__((noreturn));
#define BUG() __bug(__FILE__, __LINE__) /* give file/line information */
/* linux-2.6.38.8/arch/arm/kernel/traps.c */
void __attribute__((noreturn)) __bug(const char *file, int line)
{
printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line);
*(int *)0 = 0;
/* Avoid "noreturn function does return" */
for (;;);
}
当函数skb_is_nonlinear返回非零值(也就是skb->data_len的值不为0)时,SKB_LINEAR_ASSERT将产生一个oops消息。skb_is_nonlinear的定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */ static inline int skb_is_nonlinear(const struct sk_buff *skb) { return skb->data_len; }
skb_put函数其实也没有真的把数据添加到缓冲区中,而只是简单地更新了skb->tail和skb->len的值,如下图(图片来自《Understanding Linux Network Internals》):
(4)、skb_push
skb_push函数会把一个数据块添加到缓冲区的开端。
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */
unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
{
skb->data -= len;
skb->len += len;
if (unlikely(skb->data<skb->head))
skb_under_panic(skb, len, __builtin_return_address(0));
return skb->data;
}
skb_push函数其实也没有真的把数据添加到缓冲区中,而只是简单地更新了skb->data和skb->len的值,如下图(图片来自《Understanding Linux Network Internals》):
(5)、skb_pull
skb_pull函数会把一个数据块从缓冲区中的顶端删除。
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */
unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)
{
return skb_pull_inline(skb, len);
}
/* linux-2.6.38.8/include/linux/skbuff.h */
static inline unsigned char *skb_pull_inline(struct sk_buff *skb, unsigned int len)
{
return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len);
}
static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len)
{
skb->len -= len;
BUG_ON(skb->len < skb->data_len);
return skb->data += len;
}
skb_pull函数其实也没有真的把数据从缓冲区中删除,而只是简单地更新了skb->data和skb->len的值,如下图(图片来自《Understanding Linux Network Internals》):
(1)、dev_alloc_skb
实际上,函数dev_alloc_skb最终是调用__alloc_skb函数来分配数据缓冲区和sk_buff结构体的,如下图:
从dev_alloc_skb到__alloc_skb所涉及的源代码如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */
struct sk_buff *dev_alloc_skb(unsigned int length)
{
/*
* There is more code here than it seems:
* __dev_alloc_skb is an inline
*/
return __dev_alloc_skb(length, GFP_ATOMIC);
}
/* linux-2.6.38.8/include/linux/skbuff.h */
static inline struct sk_buff *__dev_alloc_skb(unsigned int length,
gfp_t gfp_mask)
{
struct sk_buff *skb = alloc_skb(length + NET_SKB_PAD, gfp_mask);
if (likely(skb))
skb_reserve(skb, NET_SKB_PAD);
return skb;
}
/* linux-2.6.38.8/include/linux/skbuff.h */
static inline struct sk_buff *alloc_skb(unsigned int size,
gfp_t priority)
{
return __alloc_skb(size, priority, 0, NUMA_NO_NODE);
}
/* linux-2.6.38.8/net/core/skbuff.c */ struct sk_buff *dev_alloc_skb(unsigned int length) { /* * There is more code here than it seems: * __dev_alloc_skb is an inline */ return __dev_alloc_skb(length, GFP_ATOMIC); } /* linux-2.6.38.8/include/linux/skbuff.h */ static inline struct sk_buff *__dev_alloc_skb(unsigned int length, gfp_t gfp_mask) { struct sk_buff *skb = alloc_skb(length + NET_SKB_PAD, gfp_mask); if (likely(skb)) skb_reserve(skb, NET_SKB_PAD); return skb; } /* linux-2.6.38.8/include/linux/skbuff.h */ static inline struct sk_buff *alloc_skb(unsigned int size, gfp_t priority) { return __alloc_skb(size, priority, 0, NUMA_NO_NODE); }
其中,NET_SKB_PAD的值在ARM体系架构上为32。
接下来,在__alloc_skb函数中,首先通过kmem_cache_alloc_node函数(在未配置CONFIG_NUMA和CONFIG_SLOB的情况下,它的实现就是直接调用kmem_cache_alloc函数)从skbuff_head_cache高速缓存中申请一个sk_buff结构体对象。创建skbuff_head_cache高速缓存的源代码如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/socket.c */
static int __init sock_init(void)
{
...
/* Initialize skbuff SLAB cache */
skb_init();
...
}
core_initcall(sock_init); /* early initcall */
/* linux-2.6.38.8/net/core/skbuff.c */
void __init skb_init(void)
{
skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
sizeof(struct sk_buff),
0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL);
skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache",
(2*sizeof(struct sk_buff)) +
sizeof(atomic_t),
0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL);
}
/* linux-2.6.38.8/net/socket.c */ static int __init sock_init(void) { ... /* Initialize skbuff SLAB cache */ skb_init(); ... } core_initcall(sock_init); /* early initcall */ /* linux-2.6.38.8/net/core/skbuff.c */ void __init skb_init(void) { skbuff_head_cache = kmem_cache_create("skbuff_head_cache", sizeof(struct sk_buff), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache", (2*sizeof(struct sk_buff)) + sizeof(atomic_t), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); }
申请sk_buff结构体对象的代码如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */ skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node); if (!skb) goto out; prefetchw(skb);
/* linux-2.6.38.8/net/core/skbuff.c */ skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node); if (!skb) goto out; prefetchw(skb);
对于S3C2410,prefetchw函数的实现是使用GCC的内置函数__builtin_prefetch,定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/prefetch.h */
#ifndef ARCH_HAS_PREFETCHW
#define prefetchw(x) __builtin_prefetch(x,1)
#endif
/* linux-2.6.38.8/include/linux/prefetch.h */ #ifndef ARCH_HAS_PREFETCHW #define prefetchw(x) __builtin_prefetch(x,1) #endif
__builtin_prefetch的函数原型为void __builtin_prefetch (const void *addr, ...),常用于最小化数据的存取时间。参数addr的值为将要预取的内存地址,另外,它还有两个可选的参数rw 和 locality,rw的值只能为常量0或者1,1用于写的预取,默认值0用于读的预取。关于它的详细使用说明请参考网址http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/Other-Builtins.html#Other-Builtins。
对于ARMv5,prefetchw函数使用另一种实现,而S3C2410是无法支持的。另外,对于S3C2410,__LINUX_ARM_ARCH__的值为4,在linux-2.6.38.8/arch/arm/Makefile文件中被声明。
__alloc_skb函数的另一个重要功能就是分配数据缓冲区,包括skb_shared_info结构体。先使用SKB_DATA_ALIGN宏以SMP_CACHE_BYTES(对于ARM体系架构,它的值为32)位对齐数据缓冲区(这里不包括skb_shared_info结构体)的大小,然后调用kmalloc_node_track_caller函数分配内存,代码如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */
size = SKB_DATA_ALIGN(size);
data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info),
gfp_mask, node);
if (!data)
goto nodata;
prefetchw(data + size);
/* linux-2.6.38.8/net/core/skbuff.c */ size = SKB_DATA_ALIGN(size); data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info), gfp_mask, node); if (!data) goto nodata; prefetchw(data + size);
其中两个主要函数的实现如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */
#define SKB_DATA_ALIGN(X) (((X) + (SMP_CACHE_BYTES - 1)) & \
~(SMP_CACHE_BYTES - 1))
/* linux-2.6.38.8/include/linux/slab.h */
#define kmalloc_node_track_caller(size, flags, node) \
kmalloc_track_caller(size, flags)
#define kmalloc_track_caller(size, flags) \
__kmalloc(size, flags)
/* linux-2.6.38.8/mm/slab.c */
void *__kmalloc(size_t size, gfp_t flags)
{
return __do_kmalloc(size, flags, NULL);
}
/* linux-2.6.38.8/include/linux/skbuff.h */ #define SKB_DATA_ALIGN(X) (((X) + (SMP_CACHE_BYTES - 1)) & \ ~(SMP_CACHE_BYTES - 1)) /* linux-2.6.38.8/include/linux/slab.h */ #define kmalloc_node_track_caller(size, flags, node) \ kmalloc_track_caller(size, flags) #define kmalloc_track_caller(size, flags) \ __kmalloc(size, flags) /* linux-2.6.38.8/mm/slab.c */ void *__kmalloc(size_t size, gfp_t flags) { return __do_kmalloc(size, flags, NULL); }
最后,__alloc_skb函数会完成对sk_buff和skb_shared_info两个结构体变量部分成员的初始化。
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */
memset(skb, 0, offsetof(struct sk_buff, tail));
skb->truesize = size + sizeof(struct sk_buff);
atomic_set(&skb->users, 1);
skb->head = data;
skb->data = data;
skb_reset_tail_pointer(skb);
skb->end = skb->tail + size;
#ifdef NET_SKBUFF_DATA_USES_OFFSET
skb->mac_header = ~0U;
#endif
/* linux-2.6.38.8/net/core/skbuff.c */ memset(skb, 0, offsetof(struct sk_buff, tail)); skb->truesize = size + sizeof(struct sk_buff); atomic_set(&skb->users, 1); skb->head = data; skb->data = data; skb_reset_tail_pointer(skb); skb->end = skb->tail + size; #ifdef NET_SKBUFF_DATA_USES_OFFSET skb->mac_header = ~0U; #endif
其中,当NET_SKBUFF_DATA_USES_OFFSET未定义时,skb_reset_tail_pointer函数的定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */ static inline void skb_reset_tail_pointer(struct sk_buff *skb) { skb->tail = skb->data; }
/* linux-2.6.38.8/include/linux/skbuff.h */ static inline void skb_reset_tail_pointer(struct sk_buff *skb) { skb->tail = skb->data; }
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */ shinfo = skb_shinfo(skb); memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); atomic_set(&shinfo->dataref, 1); kmemcheck_annotate_variable(shinfo->destructor_arg);
/* linux-2.6.38.8/net/core/skbuff.c */ shinfo = skb_shinfo(skb); memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); atomic_set(&shinfo->dataref, 1); kmemcheck_annotate_variable(shinfo->destructor_arg);
其中,skb_shinfo函数的定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */
#define skb_shinfo(SKB) ((struct skb_shared_info *)(skb_end_pointer(SKB)))
/* linux-2.6.38.8/include/linux/skbuff.h */ #define skb_shinfo(SKB) ((struct skb_shared_info *)(skb_end_pointer(SKB)))
当NET_SKBUFF_DATA_USES_OFFSET未定义时,skb_end_pointer函数的定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */ static inline unsigned char *skb_end_pointer(const struct sk_buff *skb) { return skb->end; }
/* linux-2.6.38.8/include/linux/skbuff.h */ static inline unsigned char *skb_end_pointer(const struct sk_buff *skb) { return skb->end; }
__alloc_skb函数完成的工作大致如下图(图片来自《Understanding Linux Network Internals》):
另外,当NET_SKBUFF_DATA_USES_OFFSET未定义时,sk_buff_data_t的声明如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */ typedef unsigned char *sk_buff_data_t;
/* linux-2.6.38.8/include/linux/skbuff.h */ typedef unsigned char *sk_buff_data_t;
(2)、skb_reserve
skb_reserve函数用于在缓冲区的头部预留一些空间,其定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */
static inline void skb_reserve(struct sk_buff *skb, int len)
{
skb->data += len;
skb->tail += len;
}
/* linux-2.6.38.8/include/linux/skbuff.h */ static inline void skb_reserve(struct sk_buff *skb, int len) { skb->data += len; skb->tail += len; }
skb_reserve函数只是简单地更新data和tail两个指针而已,如下图(图片来自《Understanding LinuxNetwork Internals》):
(3)、skb_put
skb_put函数会把一个数据块添加到缓冲区的尾端。
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */
unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
{
unsigned char *tmp = skb_tail_pointer(skb);
SKB_LINEAR_ASSERT(skb);
skb->tail += len;
skb->len += len;
if (unlikely(skb->tail > skb->end))
skb_over_panic(skb, len, __builtin_return_address(0));
return tmp;
}
/* linux-2.6.38.8/net/core/skbuff.c */ unsigned char *skb_put(struct sk_buff *skb, unsigned int len) { unsigned char *tmp = skb_tail_pointer(skb); SKB_LINEAR_ASSERT(skb); skb->tail += len; skb->len += len; if (unlikely(skb->tail > skb->end)) skb_over_panic(skb, len, __builtin_return_address(0)); return tmp; }
其中,skb_tail_pointer函数在NET_SKBUFF_DATA_USES_OFFSET未定义时,其定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */ static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) { return skb->tail; }
/* linux-2.6.38.8/include/linux/skbuff.h */ static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) { return skb->tail; }
对于ARM体系结构,在CONFIG_BUG和CONFIG_DEBUG_BUGVERBOSE都配置的情况下,SKB_LINEAR_ASSERT的定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */
#define SKB_LINEAR_ASSERT(skb) BUG_ON(skb_is_nonlinear(skb))
/* linux-2.6.38.8/include/asm-generic/bug.h */
#ifndef HAVE_ARCH_BUG_ON
#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while(0)
#endif
/* linux-2.6.38.8/arch/arm/include/asm/bug.h */
extern void __bug(const char *file, int line) __attribute__((noreturn));
#define BUG() __bug(__FILE__, __LINE__) /* give file/line information */
/* linux-2.6.38.8/arch/arm/kernel/traps.c */
void __attribute__((noreturn)) __bug(const char *file, int line)
{
printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line);
*(int *)0 = 0;
/* Avoid "noreturn function does return" */
for (;;);
}
/* linux-2.6.38.8/include/linux/skbuff.h */ #define SKB_LINEAR_ASSERT(skb) BUG_ON(skb_is_nonlinear(skb)) /* linux-2.6.38.8/include/asm-generic/bug.h */ #ifndef HAVE_ARCH_BUG_ON #define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while(0) #endif /* linux-2.6.38.8/arch/arm/include/asm/bug.h */ extern void __bug(const char *file, int line) __attribute__((noreturn)); #define BUG() __bug(__FILE__, __LINE__) /* give file/line information */ /* linux-2.6.38.8/arch/arm/kernel/traps.c */ void __attribute__((noreturn)) __bug(const char *file, int line) { printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line); *(int *)0 = 0; /* Avoid "noreturn function does return" */ for (;;); }
当函数skb_is_nonlinear返回非零值(也就是skb->data_len的值不为0)时,SKB_LINEAR_ASSERT将产生一个oops消息。skb_is_nonlinear的定义如下:
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/include/linux/skbuff.h */ static inline int skb_is_nonlinear(const struct sk_buff *skb) { return skb->data_len; }
/* linux-2.6.38.8/include/linux/skbuff.h */ static inline int skb_is_nonlinear(const struct sk_buff *skb) { return skb->data_len; }
skb_put函数其实也没有真的把数据添加到缓冲区中,而只是简单地更新了skb->tail和skb->len的值,如下图(图片来自《Understanding Linux Network Internals》):
(4)、skb_push
skb_push函数会把一个数据块添加到缓冲区的开端。
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */
unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
{
skb->data -= len;
skb->len += len;
if (unlikely(skb->data<skb->head))
skb_under_panic(skb, len, __builtin_return_address(0));
return skb->data;
}
/* linux-2.6.38.8/net/core/skbuff.c */ unsigned char *skb_push(struct sk_buff *skb, unsigned int len) { skb->data -= len; skb->len += len; if (unlikely(skb->data<skb->head)) skb_under_panic(skb, len, __builtin_return_address(0)); return skb->data; }
skb_push函数其实也没有真的把数据添加到缓冲区中,而只是简单地更新了skb->data和skb->len的值,如下图(图片来自《Understanding Linux Network Internals》):
(5)、skb_pull
skb_pull函数会把一个数据块从缓冲区中的顶端删除。
[cpp]
view plaincopyprint?
/* linux-2.6.38.8/net/core/skbuff.c */
unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)
{
return skb_pull_inline(skb, len);
}
/* linux-2.6.38.8/include/linux/skbuff.h */
static inline unsigned char *skb_pull_inline(struct sk_buff *skb, unsigned int len)
{
return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len);
}
static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len)
{
skb->len -= len;
BUG_ON(skb->len < skb->data_len);
return skb->data += len;
}
/* linux-2.6.38.8/net/core/skbuff.c */ unsigned char *skb_pull(struct sk_buff *skb, unsigned int len) { return skb_pull_inline(skb, len); } /* linux-2.6.38.8/include/linux/skbuff.h */ static inline unsigned char *skb_pull_inline(struct sk_buff *skb, unsigned int len) { return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len); } static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len) { skb->len -= len; BUG_ON(skb->len < skb->data_len); return skb->data += len; }
skb_pull函数其实也没有真的把数据从缓冲区中删除,而只是简单地更新了skb->data和skb->len的值,如下图(图片来自《Understanding Linux Network Internals》):
相关文章推荐
- 网络驱动移植之sk_buff结构体及其相关操作函数
- 网络驱动移植之sk_buff结构体及其相关操作函数(上)
- 网络驱动移植之sk_buff结构体及其相关操作函数(上)
- 网络驱动移植之sk_buff结构体及其相关操作函数
- 网络驱动移植之sk_buff结构体及其相关操作函数(下)
- 网络驱动移植之net_device结构体及其相关的操作函数
- 网络驱动移植之net_device结构体及其相关的操作函数
- 网络驱动移植之net_device结构体及其相关的操作函数
- Linux内核--网络协议栈深入分析(二)--sk_buff的操作函数
- 网络协议栈深入分析(二)--sk_buff的操作函数
- Linux内核--网络协议栈深入分析(二)--sk_buff的操作函数
- Linux块设备驱动(1)---块驱动中相关的结构体及其操作
- Linux内核--网络协议栈深入分析(二)--sk_buff的操作函数
- Linux内核--网络协议栈深入分析(二)--sk_buff的操作函数
- Linux内核--网络协议栈深入分析(二)--sk_buff的操作函数
- Linux内核--网络协议栈深入分析(二)--sk_buff的操作函数
- net_device结构体及其相关的操作函数
- FFMPeg代码分析:AVFrame结构体及其相关的函数
- FFMPeg代码分析:AVFrame结构体及其相关的函数
- sk_buff的操作函数