2. Structure related operation function
(1), DEV_ALLOC_SKB
In fact, the function DEV_ALLOC_SKB ultimately calls the __ALLOC_SKB function to allocate data buffers and sk_buff structures, as shown in the following figure:
The source code involved from DEV_ALLOC_SKB to __ALLOC_SKB is as follows:
/* 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 are an inline
/return __dev_alloc_skb (length, Gfp_at omic);
}
/* 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);
}
Among them, the value of Net_skb_pad is 32 on the ARM architecture.
Next, in the __ALLOC_SKB function, first through the Kmem_cache_alloc_node function (in the case where Config_numa and Config_slob are not configured, its implementation is called directly kmem_cache_ Alloc function) To request a Sk_buff structure object from the Skbuff_head_cache cache. The source code for creating the Skbuff_head_cache cache is as follows:
/* 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);
The code for the Sk_buff structure object is as follows:
/* 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);
The implementation of the S3C2410,PREFETCHW function is __builtin_prefetch with the built-in function of GCC, defined as follows:
/* LINUX-2.6.38.8/INCLUDE/LINUX/PREFETCH.H
/* #ifndef ARCH_HAS_PREFETCHW
#define PREFETCHW (x) __builtin_ Prefetch (x,1)
#endif
The __builtin_prefetch function prototype is void __builtin_prefetch (const void *ADDR, ...), which is often used to minimize the access time of the data. The value of the parameter addr is the memory address to prefetch, and it has two optional parameters rw and LOCALITY,RW values can only be constants 0 or 1,1 for write prefetching, and the default value of 0 for read prefetching. For detailed instructions on its use, please refer to the website http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/Other-Builtins.html#Other-Builtins.
Another implementation is used for the ARMV5,PREFETCHW function, and s3c2410 is not supported. In addition, the value for s3c2410,__linux_arm_arch__ is 4 and is declared in the Linux-2.6.38.8/arch/arm/makefile file.
Another important function of the __ALLOC_SKB function is to allocate the data buffer, including the skb_shared_info structure. First use the Skb_data_align macro to Smp_cache_bytes (for the ARM architecture, its value is 32) bits to align the size of the data buffer (not including the skb_shared_info structure), and then call the Kmalloc_node_ The Track_caller function allocates memory with the following code:
/* 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);
The implementation of the two main functions is as follows:
/* 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);
Finally, the __ALLOC_SKB function completes initialization of some members of the Sk_buff and skb_shared_info two structural variables.
/* 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
Wherein, when Net_skbuff_data_uses_offset is undefined, the Skb_reset_tail_pointer function is defined as follows:
/* 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/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);
Where the Skb_shinfo function is defined as follows:
/* linux-2.6.38.8/include/linux/skbuff.h/*
#define SKB_SHINFO (SKB) ( struct skb_shared_info *) (Skb_end_ Pointer (SKB))
When Net_skbuff_data_uses_offset is undefined, the Skb_end_pointer function is defined as follows:
/* linux-2.6.38.8/include/linux/skbuff.h
/static inline unsigned char *skb_end_pointer (const struct Sk_buff)
{return
skb->end;
}
The __ALLOC_SKB function completes the work roughly as follows (the picture comes from "Understanding Linux Network Internals"):
In addition, when Net_skbuff_data_uses_offset is undefined, the sk_buff_data_t declaration is as follows:
/* linux-2.6.38.8/include/linux/skbuff.h
/typedef unsigned char *sk_buff_data_t;
(2), Skb_reserve
The Skb_reserve function is used to reserve some space in the head of the buffer, which is defined as follows:
/* 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;
The Skb_reserve function simply updates the data and tail two pointers, as shown below (the picture comes from "Understanding Linuxnetwork Internals"):
(3), Skb_put
The Skb_put function adds a block of data to the end of the buffer.
/* 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;
}
Where the Skb_tail_pointer function is undefined when the net_skbuff_data_uses_offset is defined, it is defined as follows:
/* linux-2.6.38.8/include/linux/skbuff.h
/static inline unsigned char *skb_tail_pointer (const struct SK_BUFF )
{return
skb->tail;
}
For the ARM architecture, Skb_linear_assert is defined as follows when both Config_bug and Config_debug_bugverbose are configured:
/* 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
(;;);
}
When a function skb_is_nonlinear returns a value other than 0 (that is, the Skb->data_len value is not 0), Skb_linear_assert produces a oops message. The definition of skb_is_nonlinear is as follows:
/* linux-2.6.38.8/include/linux/skbuff.h
/static inline int skb_is_nonlinear (const struct Sk_buff *skb)
{ return
skb->data_len;
}
The Skb_put function does not actually add data to the buffer, but simply updates the values of Skb->tail and Skb->len, as shown in the following image (image from understanding Linux network Internals):
(4), Skb_push
The Skb_push function adds a block of data to the beginning of the buffer.
/* 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;
}
The Skb_push function does not actually add data to the buffer, but simply updates the values of Skb->data and Skb->len, as shown in the following image (image from understanding Linux network Internals):
(5), Skb_pull
The Skb_pull function deletes a block of data from the top of the buffer.
/* 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, 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;
The Skb_pull function does not actually remove the data from the buffer, but simply updates the values of Skb->data and Skb->len, as shown in the following image (image from understanding Linux network Internals):