+ -
当前位置:首页 → 问答吧 → 讨论一下 skb_copy_bits

讨论一下 skb_copy_bits

时间:2010-09-29

来源:互联网

本帖最后由 Godbach 于 2010-09-29 10:48 编辑

记得通常在 IP 层处理 skb 的时候,如果是线性的,即排除 skb->data_len 不为 0 的情况,copy 数据使用 memcpy 就可以了。
skb_copy_bits也是用来实现 copy 数据的,可以指定偏移和长度。但是其具体实现就不是想象的那么简单了。以下是代码


QUOTE:
/* Copy some data bits from skb to kernel buffer. */

int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
{
        int i, copy;
        int start = skb_headlen(skb);

        if (offset > (int)skb->len - len)
                goto fault;

        /* Copy header. */ /* 这里进行判断,以确定是否需要copy 头部*/
        if ((copy = start - offset) > 0) {
                if (copy > len)
                        copy = len;
                skb_copy_from_linear_data_offset(skb, offset, to, copy);
                if ((len -= copy) == 0)
                        return 0;
                offset += copy;
                to     += copy;
        }
        /* 这个循环是遍历 frags,根据指定的 offset 和 len 进行数据的 copy */
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;

                BUG_TRAP(start <= offset + len);

                end = start + skb_shinfo(skb)->frags.size;
                if ((copy = end - offset) > 0) {
                        u8 *vaddr;

                        if (copy > len)
                                copy = len;

                        vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags);
                        memcpy(to,
                               vaddr + skb_shinfo(skb)->frags.page_offset+
                               offset - start, copy);
                        kunmap_skb_frag(vaddr);

                        if ((len -= copy) == 0)
                                return 0;
                        offset += copy;
                        to     += copy;
                }
                start = end;
        }
       /* 如果还没有 copy 完足够长度的数据,还要遍历 frag_list 继续  copy 数据,而且 copy 数据的方式是递归调用该函数 */
        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_copy_bits 中能够用于 copy 数据的地方有三处
(1) 当前 skb 的缓冲区
(2)skb 保存的 shinfo 中对应的 frags 中的数据,以 page 的方式存储;
(3)skb 保存的 shinfo 中对应的 frag_list 中的数据,由于 frag_list 的成员仍然是 sk_buff 结构体,因此这部分数据的 copy 的方式和本函数要实现的功能一致。

因此,有如下问题:
(1)shinfo 中 frags 对应的数据,还有frag_list 中对应的数据,分别是在什么时候被填充的。
(2)这个函数被调用的时机有哪些?

作者: Godbach   发布时间: 2010-09-29

frags 不记得了, frag_list在ip分片重组时被填充的,如果没有线性化即头skb->data_len不为0时就要用这个拷贝了。

作者: shank941   发布时间: 2010-09-29

多谢 LS 的。
frags 估计是和本机发出去的包时有关,从传输层到 IP 层时会读取的

作者: Godbach   发布时间: 2010-09-29