struct iovec数据结构在网络和IO的很多地方都使用,数据结构比较简单:
-
struct iovec
-
{
-
void __user *iov_base; /* BSD uses caddr_t (1003.1g requires void *) */
-
__kernel_size_t iov_len; /* Must be size_t (1003.1g) */
-
};
1:skb_copy_datagram_iovec,该函数在收包流程udp_recvmsg中就有用到,作用是把skb相关的数据拷贝到iovec的数据结构中,传给用户,调该函数的时候L4底下的协议已经处理完了,data指向L4的头开始处:
-
int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, //offset为L4的头大小
-
struct iovec *to, int len) //len为min{用户传入的值大小,skb带负载的大小}
-
{
-
int start = skb_headlen(skb);//线性区大小
-
int i, copy = start - offset;
-
struct sk_buff *frag_iter;
-
-
trace_skb_copy_datagram_iovec(skb, len); //debugfs中有该函数的trace点
-
-
/* Copy header. */
-
if (copy > 0) { //1:拷贝线性区
-
if (copy > len)
-
copy = len;
-
if (memcpy_toiovec(to, skb->data + offset, copy)) //copy_to_user
-
goto fault;
-
if ((len -= copy) == 0)
-
return 0;
-
offset += copy;
-
}
-
-
/* Copy paged appendix. Hmm... why does this look so complicated? */
-
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { //2:拷贝SG区,该段代码作用很简单,但是逻辑有点复杂,需要考虑L4头的具体位置
-
int end;
-
-
WARN_ON(start > offset + len);
-
-
end = start + skb_shinfo(skb)->frags[i].size;
-
if ((copy = end - offset) > 0) {
-
int err;
-
u8 *vaddr;
-
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
-
struct page *page = frag->page;
-
-
if (copy > len)
-
copy = len;
-
vaddr = kmap(page);
-
err = memcpy_toiovec(to, vaddr + frag->page_offset +
-
offset - start, copy); //如果L4在线性区的话,不需要这么复杂,但是如果L4在当前区域的话,需要略过头
-
kunmap(page);
-
if (err)
-
goto fault;
-
if (!(len -= copy))
-
return 0;
-
offset += copy;
-
}
-
start = end;
-
}
-
-
skb_walk_frags(skb, frag_iter) { //3:拷贝fraglist,如ip defrag后
-
int end;
-
-
WARN_ON(start > offset + len);
-
-
end = start + frag_iter->len;
-
if ((copy = end - offset) > 0) {
-
if (copy > len)
-
copy = len;
-
if (skb_copy_datagram_iovec(frag_iter, //递归调用,最深不会调用2次
-
offset - start,
-
to, copy))
-
goto fault;
-
if ((len -= copy) == 0)
-
return 0;
-
offset += copy;
-
}
-
start = end;
-
}
-
if (!len)
-
return 0;
-
-
fault:
-
return -EFAULT;
-
}
阅读(7846) | 评论(0) | 转发(0) |