aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp_ipv4.c
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2018-02-13 09:14:12 -0500
committerDavid S. Miller <davem@davemloft.net>2018-02-14 14:21:45 -0500
commite0f9759f530bf789e984961dce79f525b151ecf3 (patch)
tree8f384615d4648a8fc46c70db06dd9021a0a480b4 /net/ipv4/tcp_ipv4.c
parent1d631583ae5e40681dc9a41d7cfa845222c3c7ff (diff)
tcp: try to keep packet if SYN_RCV race is lost
배석진 reported that in some situations, packets for a given 5-tuple end up being processed by different CPUS. This involves RPS, and fragmentation. 배석진 is seeing packet drops when a SYN_RECV request socket is moved into ESTABLISH state. Other states are protected by socket lock. This is caused by a CPU losing the race, and simply not caring enough. Since this seems to occur frequently, we can do better and perform a second lookup. Note that all needed memory barriers are already in the existing code, thanks to the spin_lock()/spin_unlock() pair in inet_ehash_insert() and reqsk_put(). The second lookup must find the new socket, unless it has already been accepted and closed by another cpu. Note that the fragmentation could be avoided in the first place by use of a correct TCP MSS option in the SYN{ACK} packet, but this does not mean we can not be more robust. Many thanks to 배석진 for a very detailed analysis. Reported-by: 배석진 <soukjin.bae@samsung.com> Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_ipv4.c')
-rw-r--r--net/ipv4/tcp_ipv4.c13
1 files changed, 12 insertions, 1 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index ac16795486ea..f3e52bc98980 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1672,6 +1672,7 @@ process:
1672 1672
1673 if (sk->sk_state == TCP_NEW_SYN_RECV) { 1673 if (sk->sk_state == TCP_NEW_SYN_RECV) {
1674 struct request_sock *req = inet_reqsk(sk); 1674 struct request_sock *req = inet_reqsk(sk);
1675 bool req_stolen = false;
1675 struct sock *nsk; 1676 struct sock *nsk;
1676 1677
1677 sk = req->rsk_listener; 1678 sk = req->rsk_listener;
@@ -1694,10 +1695,20 @@ process:
1694 th = (const struct tcphdr *)skb->data; 1695 th = (const struct tcphdr *)skb->data;
1695 iph = ip_hdr(skb); 1696 iph = ip_hdr(skb);
1696 tcp_v4_fill_cb(skb, iph, th); 1697 tcp_v4_fill_cb(skb, iph, th);
1697 nsk = tcp_check_req(sk, skb, req, false); 1698 nsk = tcp_check_req(sk, skb, req, false, &req_stolen);
1698 } 1699 }
1699 if (!nsk) { 1700 if (!nsk) {
1700 reqsk_put(req); 1701 reqsk_put(req);
1702 if (req_stolen) {
1703 /* Another cpu got exclusive access to req
1704 * and created a full blown socket.
1705 * Try to feed this packet to this socket
1706 * instead of discarding it.
1707 */
1708 tcp_v4_restore_cb(skb);
1709 sock_put(sk);
1710 goto lookup;
1711 }
1701 goto discard_and_relse; 1712 goto discard_and_relse;
1702 } 1713 }
1703 if (nsk == sk) { 1714 if (nsk == sk) {