【发布时间】:2011-05-06 17:18:13
【问题描述】:
Linux 内核可以选择启用 TCP 接收复制卸载功能 (CONFIG_NET_DMA)。我使用iperf(TCP 窗口大小 = 250 KB 和缓冲区长度 = 2 MBytes)和 oprofile 来测试三种情况下的性能:启用和不启用 NET_DMA、启用 NET_DMA 和 sk_rcvlowat 设置为 200 KB。结果如下:
-
禁用 NET_DMA:带宽可以达到 930 Mbps,
__copy_tofrom_user消耗 36.1% 的 cpu 时间。 -
启用 NET_DMA:带宽小于上述情况 40 Mbps (890 Mbps),
__copy_tofrom_user消耗 33.5% 的 cpu 时间。 -
启用 NET_DMA (sk_rcvlowat = 200KB):带宽为 874 Mbps,
__copy_tofrom_user消耗 25.1% 的 cpu 时间。
我还尝试检查函数 tcp_recvmsg()(在 /net/ipv4/tcp.c 中)(内核版本为 2.6.32.2)。这是我理解 NET_DMA 工作方式的方式:
// 在 tcp_revmsg() 开始处
target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);#ifdef CONFIG_NET_DMA
tp->ucopy.dma_chan = NULL; preempt_disable(); skb = skb_peek_tail(&sk->sk_receive_queue); { int available = 0; if (skb) available = TCP_SKB_CB(skb)->seq + skb->len - (*seq); if ((available < target) && (len > sysctl_tcp_dma_copybreak) && !(flags & MSG_PEEK) && !sysctl_tcp_low_latency && dma_find_channel(DMA_MEMCPY)) { preempt_enable_no_resched(); tp->ucopy.pinned_list = dma_pin_iovec_pages(msg->msg_iov, len); } else { preempt_enable_no_resched(); } }#endif
len:为缓冲区长度,可通过iperf中的-l选项指定
target: 是tcp_recvmsg() 应该返回的最小字节数。如果 sk->sk_rcvlowat 没有设置,我看到目标通常得到值 1(在 target = 1 的情况下很少发生 DMA 传输)。
available:接收队列中第一个skb 可用的字节数。
我认为条件(目标 tcp_recvmsg() 是否应该使用 DMA 至关重要。正如我从 I/OAT 补丁文件中的 cmets 中读取的那样,当存在使进程进入睡眠状态并等待更多数据的上下文切换时,这种情况为真。
//在tcp_recvmsg()的while循环中
如果(已复制 >= 目标){
/* Do not sleep, just process backlog. */ release_sock(sk); lock_sock(sk);} 其他
sk_wait_data(sk, &timeo);
当进程休眠时,到达的数据包将被tcp_dma_try_early_copy() in tcp_rcv_established() (in /net/ipv4/tcp_input.c) 直接碰撞到用户空间缓冲区。也许这就是NET_DMA的高效点,进程进入休眠状态,但是数据可以通过硬件移动到它的缓冲区中。
//在/net/ipv4/tcp_input.c:tcp_dma_try_early_copy()中
如果 ((tp->ucopy.len == 0) ||
(tcp_flag_word(tcp_hdr(skb)) & TCP_FLAG_PSH) || (atomic_read(&sk->sk_rmem_alloc) > (sk->sk_rcvbuf >> 1))) { tp->ucopy.wakeup = 1; sk->sk_data_ready(sk, 0);}
tcp_dma_try_early_copy() 中的 DMA 处理将在没有更多缓冲区 (tp->ucopy.len == 0) 或分配的 skb 的总大小大于 1/2 sk_rcvbuf 时停止其工作并唤醒休眠进程 (我发现sk_rcvbuf被设置为iperf的TCP窗口大小。
这是我第一次在 Linux 中使用 TCP/IP 堆栈。我不确定我上面的结论是否正确,如果我错了,请纠正我。我的问题是:
Q1:为什么 NET_DMA 启用情况下的带宽总是低于没有 NET_DMA 情况下的带宽?
Q2:是否有一组很好的值(TCP 窗口大小、缓冲区长度、sk_rcvlowat)来提高启用 NET_DMA 的情况下的性能?
Q3:每次 DMA 传输只有大约 1448 字节。是否太小而无法进行 DMA 处理?
感谢任何建议。提前致谢。
【问题讨论】:
标签: performance linux-kernel tcp