Chinaunix

标题: 【TCP/IP】dev_alloc_skb()为什么要多申请16个字节? [打印本页]

作者: new_learner    时间: 2008-11-14 15:47
标题: 【TCP/IP】dev_alloc_skb()为什么要多申请16个字节?
初学linux的TCP/IP代码,看到这个函数:__dev_alloc_skb(),定义如下:
static inline struct sk_buff *__dev_alloc_skb(unsigned int length,
                          int gfp_mask)
{
    struct sk_buff *skb = alloc_skb(length + 16, gfp_mask);
    if (likely(skb))
        skb_reserve(skb, 16);
    return skb;
}

在调用alloc_skb的时候,第一个参数为什么要加上16个字节? 看资料提到这16个byte是headroom,如果是这样,那为什么只有16个byte呢?

另外,alloc_skb 的实现如下:
struct sk_buff *alloc_skb(unsigned int size, int gfp_mask)
{
    struct sk_buff *skb;
    u8 *data;

    /* Get the HEAD */
    skb = kmem_cache_alloc(skbuff_head_cache,
                   gfp_mask & ~__GFP_DMA);
    if (!skb)
        goto out;

    /* Get the DATA. Size must match skb_add_mtu(). */
    size = SKB_DATA_ALIGN(size);
    data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
    if (!data)
        goto nodata;

    memset(skb, 0, offsetof(struct sk_buff, truesize));
    skb->truesize = size + sizeof(struct sk_buff);
    atomic_set(&skb->users, 1);
    skb->head = data;
    skb->data = data;
    skb->tail = data;
    skb->end  = data + size;

    atomic_set(&(skb_shinfo(skb)->dataref), 1);
    skb_shinfo(skb)->nr_frags  = 0;
    skb_shinfo(skb)->tso_size = 0;
    skb_shinfo(skb)->tso_segs = 0;
    skb_shinfo(skb)->frag_list = NULL;
out:
    return skb;
nodata:
    kmem_cache_free(skbuff_head_cache, skb);
    skb = NULL;
    goto out;
}

其中,size = SKB_DATA_ALIGN(size);表示16bit对齐,不太没明白为什么需要16bit对齐呢?注释上说的是/*Size must match skb_add_mtu().*/,哪位兄弟能帮忙解释一下?

多谢了!
作者: Godbach    时间: 2008-11-14 16:01
应该是用于对齐的。LZ仔细搜索一下,之前偶看到过解释
作者: Arthur_    时间: 2008-11-14 16:17
在额外加两个正好对齐+ mac地址14个字节 (对齐)+ IP
作者: qtdszws    时间: 2008-11-14 16:30
唯一的解释就是为了方便扩展,例如在头部加额外的信息而不需要移动或复制整个包
作者: new_learner    时间: 2008-11-14 16:53
原帖由 Arthur_ 于 2008-11-14 16:17 发表
在额外加两个正好对齐+ mac地址14个字节 (对齐)+ IP

偶太菜,不太明白你的意思。。。
能不能详细说下,16是怎样得出来的?
作者: Arthur_    时间: 2008-11-14 17:08
原帖由 new_learner 于 2008-11-14 16:53 发表

偶太菜,不太明白你的意思。。。
能不能详细说下,16是怎样得出来的?


我也菜, 让我慢点说:
先什么不考虑:
首先L2的地址一共是14(6+6+2)个BYTE:

L2+L3(IP addr)+L4

按照常理L2地址是在L3后加, 所以在ALLOCSKB的时候要留14个BYTE,为了以后给L2用.
但是计算机一般是4字节对齐的, 如果留14个BYTE那么IP只能排在15个byte位子, ip头要经常访问所以这样效率似乎不好.
于是在预留2个(14+2 = 16) 正好让IP头4字节对齐.

自己罗嗦了.=.=
作者: new_learner    时间: 2008-11-14 17:45
多谢ls几位,特别是Arthur_ 兄~~非常详细:)


还有一个疑问,正如我在顶楼提到的,在alloc_skb()函数里,有这么一句话: size = SKB_DATA_ALIGN(size);
我看《The Linux Networking Architecture》里的解释是:
alloc_skb(size, gpf_mask) allocates memory for a socket buffer structure and the corresponding packet memory. In this case, size specifies the size of the packet data space, where this space will be increased (aligned) to the next 16-bit address.
不太明白为什么要对齐到16-bit呢?计算机是4字节对齐,那16-bit有什么用?而且,注释里对于size = SKB_DATA_ALIGN(size);的解释是:/* Get the DATA. Size must match skb_add_mtu(). */  不太明白意思。

能大概提示一下吗?
作者: qtdszws    时间: 2008-11-14 19:37
>>所以在ALLOCSKB的时候要留14个BYTE,为了以后给L2用.

这种解释有问题,设备是不用这16byte空间的.

下面是一个设备接收数据的例子
    p = dev_alloc_skb(len + 2);
    if (!p) return NULL;

    p->dev = dev;
    skb_reserve(p, 2);       
它自己会reserve 2,保证ip对齐到4字节边界

>>/* Get the DATA. Size must match skb_add_mtu(). */
>>size = SKB_DATA_ALIGN(size);
>>data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);

为了把skb_shared_info放入独立的cache lines,提高效率

[ 本帖最后由 qtdszws 于 2008-11-14 20:44 编辑 ]
作者: Arthur_    时间: 2008-11-15 08:35
原帖由 qtdszws 于 2008-11-14 19:37 发表
>>所以在ALLOCSKB的时候要留14个BYTE,为了以后给L2用.

这种解释有问题,设备是不用这16byte空间的.

下面是一个设备接收数据的例子
    p = dev_alloc_skb(len + 2);
    if (!p) return NULL;

    p- ...


我说L2可能你理解成driver了, 其实我指的是neighbour system(对于IPV4就是ARP)。

cacheline? 能具体说否?
把share_info对齐到4byte难道没有这个cacheline快吗?
作者: 雷锋不谢    时间: 2014-10-11 17:20
本帖最后由 雷锋不谢 于 2014-10-11 17:24 编辑

回复 8# qtdszws


    支持!!!

            alloc_skb的简单代码如下:
        {
                struct sk_buff *skb;
                skb=kemme_cache_alloc(skbuff_head_cache,gfp_mask&~__GFP_DMA);
                。。。。。。
                size=SKB_DATA_ALIGH(size);//主要是为了对其size,适当地把size扩大到边界
                data=kmalloc(size+sizeof(struct skb_shared_info),gfp_mask);
                .....
        }

执行完此函数的布局图如下:


   dev_alloc_skb一般在设备驱动中分配skb的空间,且是在中断模式下,其封装了上面的alloc_skb。
      
       static inline struct sk_buff* dev_alloc_skb(unsigned int length)
        {
                return __dev_alloc_skb(length,GFP_ATOMIC);
        }
        static inline struct sk_buff*__dev_alloc_skb(unsigned int length,int gfp_mask)
        {
                struct sk_buff*skb=alloc_skb(length+16,gfp_mask);
                if(likely(skb))
                        skb_reserve(skb,16);//这个reserver是前面的多申请的16,所以把data tail的位置下移16。
                return skb.
        }
        执行玩此函数的布局如图:

     


     而在一般的驱动中,还会在reserver 2 个字节的位置,例如:

      {
        skb=dev_alloc_skb(length);
        if(skb)
          skb_reserve(skb,2);
       ....
     }
     这是因为,在以太网的头部为14B,如果直接把ip头部接到后面,则ip头部从15B的位置开始,这样不太好,为了对其,就进而预留2B的位置,如图:

     这样就可以看到,ip头部是从16B的位置开始的。。。













欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2