| From eeea1b0381e6ef529091b90c7889def6f1c96753 Mon Sep 17 00:00:00 2001 |
| From: Jiri Kosina <jkosina@suse.cz> |
| Date: Fri, 27 Jul 2012 10:38:50 +0000 |
| Subject: [PATCH] tcp: perform DMA to userspace only if there is a task waiting |
| for it |
| |
| commit 59ea33a68a9083ac98515e4861c00e71efdc49a1 upstream. |
| |
| Back in 2006, commit 1a2449a87b ("[I/OAT]: TCP recv offload to I/OAT") |
| added support for receive offloading to IOAT dma engine if available. |
| |
| The code in tcp_rcv_established() tries to perform early DMA copy if |
| applicable. It however does so without checking whether the userspace |
| task is actually expecting the data in the buffer. |
| |
| This is not a problem under normal circumstances, but there is a corner |
| case where this doesn't work -- and that's when MSG_TRUNC flag to |
| recvmsg() is used. |
| |
| If the IOAT dma engine is not used, the code properly checks whether |
| there is a valid ucopy.task and the socket is owned by userspace, but |
| misses the check in the dmaengine case. |
| |
| This problem can be observed in real trivially -- for example 'tbench' is a |
| good reproducer, as it makes a heavy use of MSG_TRUNC. On systems utilizing |
| IOAT, you will soon find tbench waiting indefinitely in sk_wait_data(), as they |
| have been already early-copied in tcp_rcv_established() using dma engine. |
| |
| This patch introduces the same check we are performing in the simple |
| iovec copy case to the IOAT case as well. It fixes the indefinite |
| recvmsg(MSG_TRUNC) hangs. |
| |
| Signed-off-by: Jiri Kosina <jkosina@suse.cz> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| --- |
| net/ipv4/tcp_input.c | 4 +++- |
| 1 file changed, 3 insertions(+), 1 deletion(-) |
| |
| diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c |
| index add69a11754d..df5de7dd22d2 100644 |
| --- a/net/ipv4/tcp_input.c |
| +++ b/net/ipv4/tcp_input.c |
| @@ -5315,7 +5315,9 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, |
| if (tp->copied_seq == tp->rcv_nxt && |
| len - tcp_header_len <= tp->ucopy.len) { |
| #ifdef CONFIG_NET_DMA |
| - if (tcp_dma_try_early_copy(sk, skb, tcp_header_len)) { |
| + if (tp->ucopy.task == current && |
| + sock_owned_by_user(sk) && |
| + tcp_dma_try_early_copy(sk, skb, tcp_header_len)) { |
| copied_early = 1; |
| eaten = 1; |
| } |
| -- |
| 1.8.5.2 |
| |