• skb 操作函数skb_linearize


    skb_linearize:分配新的skb->data,将旧的skb->data、skb_shinfo(skb)->frags、skb_shinfo(skb)->frag_list中的内容拷贝到新skb->data的连续内存空间中,释放frags或frag_list

    //其中frags用于支持分散聚集IO,frags_list用于支持数据分片
    1.1 int __skb_linearize(struct sk_buff *skb, int gfp_mask)
    {
        unsigned int size;
        u8 *data;
        long offset;
        struct skb_shared_info *ninfo;
        int headerlen = skb->data - skb->head;
        int expand = (skb->tail + skb->data_len) - skb->end;
        //如果此skb被共享
        if (skb_shared(skb))
            BUG();//产生BUG oops
     
        //还需要的内存大小
        if (expand <= 0)
            expand = 0;
        //新申请的skb的大小
        size = skb->end - skb->head + expand;
        //将size对齐到SMP_CACHE_BYTES
        size = SKB_DATA_ALIGN(size);
        //分配物理上联系的内存
        data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
        if (!data)
            return -ENOMEM;
        //拷贝
        if (skb_copy_bits(skb, -headerlen, data, headerlen + skb->len))
            BUG();
     
        //初始化skb的skb_shared_info结构
        ninfo = (struct skb_shared_info*)(data + size);
        atomic_set(&ninfo->dataref, 1);
        ninfo->tso_size = skb_shinfo(skb)->tso_size;
        ninfo->tso_segs = skb_shinfo(skb)->tso_segs;
        //fraglist为NULL
        ninfo->nr_frags = 0;
        ninfo->frag_list = NULL;
     
        offset = data - skb->head;
     
        //释放之前skb的data
        skb_release_data(skb);
     
        //将skb指向新的data
        skb->head = data;
        skb->end  = data + size;
        //重新初始化新skb的各个报头指针
        skb->h.raw   += offset;
        skb->nh.raw  += offset;
        skb->mac.raw += offset;
        skb->tail    += offset;
        skb->data    += offset;
     
        skb->cloned    = 0;
     
        skb->tail     += skb->data_len;
        skb->data_len  = 0;
        return 0;
    }
     
    1.2 SKB_DATA_ALIGN(X)    (((X) + (SMP_CACHE_BYTES - 1)) & \
                     ~(SMP_CACHE_BYTES - 1))
     
    //将skb中起始offset的内容拷贝到to中,拷贝长度为len
    1.3 int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
    {
        int i, copy;
        //skb->len-skb->data_len,得到skb->head到skb->end之间的数据量
        int start = skb_headlen(skb);
        //偏移量+len > skb->len,说明可供拷贝的数据量不够
        if (offset > (int)skb->len - len)
            goto fault;
        //计算需要拷贝的数据量
        if ((copy = start - offset) > 0) {
            if (copy > len)
                copy = len;
            //拷贝
            memcpy(to, skb->data + offset, copy);
            if ((len -= copy) == 0)//拷贝量=需要拷贝的长度
                return 0;
            offset += copy;//更新偏移量
            to     += copy;
        }
        //接下来的数据从skb_shinfo的frags数组中进行拷贝
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
            int end;
     
            //遍历frags
            end = start + skb_shinfo(skb)->frags[i].size;
            if ((copy = end - offset) > 0) {
                u8 *vaddr;
     
                if (copy > len)
                    copy = len;
                //映射skb的frag到内核地址空间
                vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]);
                //拷贝
                memcpy(to,
                       vaddr + skb_shinfo(skb)->frags[i].page_offset+
                       offset - start, copy);
                //解除映射
                kunmap_skb_frag(vaddr);
     
                if ((len -= copy) == 0)
                    return 0;
                offset += copy;
                to     += copy;
            }
            start = end;
        }
        //从skb的frag_list中拷贝
        if (skb_shinfo(skb)->frag_list) {
            struct sk_buff *list = skb_shinfo(skb)->frag_list;
     
            for (; list; list = list->next) {
                int end;
     
                BUG_TRAP(start <= offset + len);
     
                end = start + list->len;
                if ((copy = end - offset) > 0) {
                    if (copy > len)
                        copy = len;
                    //递归调用
                    if (skb_copy_bits(list, offset - start,
                              to, copy))
                        goto fault;
                    if ((len -= copy) == 0)
                        return 0;
                    offset += copy;
                    to     += copy;
                }
                start = end;
            }
        }
        if (!len)
            return 0;
     
    fault:
        return -EFAULT;
    }
    //保证skb->data 到 skb->tail之间有len长度的数据
    2.1 static inline int pskb_may_pull(struct sk_buff *skb, unsigned int len)
    {
        //skb->data 到 skb->tail之间的数据足够len长度
        if (likely(len <= skb_headlen(skb)))
            return 1;
        //len长度超过skb总长度
        if (unlikely(len > skb->len))
            return 0;
        //移动后边的数据到skb->data中
        return __pskb_pull_tail(skb, len-skb_headlen(skb)) != NULL;
    }
     
    //调用流程pskb_may_pull->__pskb_pull_tail
    //delta为需要从frags或者frag_list向前移动的数据量
    2.2 unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta)
    {    //eat为去除当前skb可用内存,还需要多少内存
        int i, k, eat = (skb->tail + delta) - skb->end;
        //判断当前skb是否被克隆
        if (eat > 0 || skb_cloned(skb)) {
            //对sk_buff重新分配头
            if (pskb_expand_head(skb, 0, eat > 0 ? eat + 128 : 0,
                         GFP_ATOMIC))
                return NULL;
        }
        //从skb的offset(skb->tail),拷贝delta个字节到skb->tail之后
        if (skb_copy_bits(skb, skb_headlen(skb), skb->tail, delta))
            BUG();
        //没有分段
        if (!skb_shinfo(skb)->frag_list)
            goto pull_pages;
        //由于数据已经拷贝到了skb->data中,因此需要释放frags,frag_list中被拷贝过的数据
        //计算从frags数组中拷贝的数据量
        eat = delta;
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
            //寻找到满足eat这么多数据量的最后一个page
            if (skb_shinfo(skb)->frags[i].size >= eat)
                //在frags数组中的数据量可以满足delta时,则只释放frags即可
                goto pull_pages;
            eat -= skb_shinfo(skb)->frags[i].size;
        }
        //eat仍不为0,说明从frag_list中进行了拷贝,释放frag_list
        if (eat) {
            struct sk_buff *list = skb_shinfo(skb)->frag_list;
            struct sk_buff *clone = NULL;
            struct sk_buff *insp = NULL;
     
            do {
                //list为null,说明数据量不够
                if (!list)
                    BUG();
                //当前skb的长度小于需要的长度
                if (list->len <= eat) {
                    //找到下一个skb
                    eat -= list->len;
                    //list指向下一个需要的skb
                    list = list->next;
                    //insp指向当前的skb
                    insp = list;
                } else {
                    //此时insp指向前一个skb
                    //说明当前skb可以满足需要的数据量
                    if (skb_shared(list)) {//但是当前skb被共享
                        clone = skb_clone(list, GFP_ATOMIC);//对最后那个拷贝不完全的skb,进行克隆
                        if (!clone)
                            return NULL;
                        //list指向当前被克隆的的skb
                        //insp指向下一个skb
                        insp = list->next;
                        list = clone;
                    } else {
                        //list与insp指向当前的skb
                        insp = list;
                    }
                    //修改最后一个skb,移动指针,删除掉被拷贝的数据
                    if (!pskb_pull(list, eat)) {
                        if (clone)
                            kfree_skb(clone);//递减clone的引用计数
                        return NULL;
                    }
                    break;
                }
            } while (eat);
            //list指向frag_list头
            //直到list遍历到数据量足够的最后一个skb
            while ((list = skb_shinfo(skb)->frag_list) != insp) {
                skb_shinfo(skb)->frag_list = list->next;
                //释放当前的skb
                kfree_skb(list);//递减当前skb的引用技术,如果引用计数=0,则释放list
            }
            //说明最后一个skb只被拷贝了一部分,将此skb挂到frag_list头
            if (clone) {
                clone->next = list;
                skb_shinfo(skb)->frag_list = clone;
            }
        }
     
    pull_pages:
        eat = delta;
        k = 0;
        //释放frags中的page
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
            if (skb_shinfo(skb)->frags[i].size <= eat) {
                put_page(skb_shinfo(skb)->frags[i].page);
                eat -= skb_shinfo(skb)->frags[i].size;
            } else {
                skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
                if (eat) {
                    skb_shinfo(skb)->frags[k].page_offset += eat;
                    skb_shinfo(skb)->frags[k].size -= eat;
                    eat = 0;
                }
                k++;
            }
        }
        skb_shinfo(skb)->nr_frags = k;
     
        skb->tail     += delta;
        skb->data_len -= delta;
     
        return skb->tail;
    }
     
     
     
    //skb->users指定skb被引用的个数
    3.1 static inline int skb_shared(const struct sk_buff *skb)
    {
        return atomic_read(&skb->users) != 1;
    }
     

    关于skb data操作函数有:

    • skb_put:在tail偏移后面扩展n个字节的空间,但不会超过end偏移的限制空间。返回扩展空间的第一个字节指针
    • skb_push:在data指针前面扩展n个字节的空间,但不能超过head指针限制。返回新的data指针
    • skb_pull:将data指针后移n个字节,将来再使用skb_push操作就会将这n个字节的空间内容覆盖,返回新的data指针
    • skb_reserve:在缓冲区头部保留n个字节的空间,这个操作只允许对空的缓冲区进行操作
     
     
    分配内存
      定义在skbuff.c源文件猪的alloc_skb是分配缓冲区的主要函数。数据缓冲区和sk_buff结构是两个不同的实例,建议一个缓冲区会涉及两次内存分配,分配数据缓冲区和分配sk_buff结构。
    alloc_skb通过调研kmem_cache_alloc函数从一个缓存中取得一个sk_buff结构,然后调研kmalloc分配一个数据缓冲区:
    在调用kmalloc前,size参数会被SKB_DATA_ALIGN宏进行调整强制对齐。alloc_skb函数会对sk_buff结构中的元素进行参数初始化,其中几个参数的值如下图:
    在数据缓冲区的底端的skb_shared_info数据结构主要用于处理一些IP分片。
      dev_alloc_skb是由设备驱动程序使用的缓冲区分配函数,应该是在中断模式中执行。此函数是alloc_skb的一个封装函数,为了优化在申请大小上加了16个字节。
    而且由于此函数是由中断事件处理函数调用的,所以会要求原子操作(GFP_ATOMIC):
  • 相关阅读:
    积水识别算法需求
    Visual detection and feature recognition of underwater target using a novel modelbased method
    A deep learning techniquebased automatic monitoring method for experimental urban road inundation
    Identification of Urban Road Waterlogging Using Floating Car Data
    android新手学习笔记
    转:Android编程获取手机型号,本机电话号码,sdk版本及firmware版本号(即系统版本号)
    Jquery getJSON() 序列化类以及集合(转)
    windows下eclipse模拟运行apk
    Android使用AttributeSet自定义控件的方法
    Android 支持多屏幕机制
  • 原文地址:https://www.cnblogs.com/codestack/p/16182619.html
Copyright © 2020-2023  润新知