diff options
author | Eric Dumazet <edumazet@google.com> | 2016-04-14 01:05:39 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-04-15 16:45:44 -0400 |
commit | b3d051477cf94e9d71d6acadb8a90de15237b9c1 (patch) | |
tree | 59009bc698f472b31b15059972e6904cd9272d32 /net | |
parent | ac18dd9e842294377dbaf1e8d169493567a81fa1 (diff) |
tcp: do not mess with listener sk_wmem_alloc
When removing sk_refcnt manipulation on synflood, I missed that
using skb_set_owner_w() was racy, if sk->sk_wmem_alloc had already
transitioned to 0.
We should hold sk_refcnt instead, but this is a big deal under attack.
(Doing so increase performance from 3.2 Mpps to 3.8 Mpps only)
In this patch, I chose to not attach a socket to syncookies skb.
Performance is now 5 Mpps instead of 3.2 Mpps.
Following patch will remove last known false sharing in
tcp_rcv_state_process()
Fixes: 3b24d854cb35 ("tcp/dccp: do not touch listener sk_refcnt under synflood")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/tcp_input.c | 7 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 4 | ||||
-rw-r--r-- | net/ipv4/tcp_output.c | 16 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 4 |
4 files changed, 20 insertions, 11 deletions
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 983f04c11177..7ea7034af83f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c | |||
@@ -6327,7 +6327,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, | |||
6327 | } | 6327 | } |
6328 | if (fastopen_sk) { | 6328 | if (fastopen_sk) { |
6329 | af_ops->send_synack(fastopen_sk, dst, &fl, req, | 6329 | af_ops->send_synack(fastopen_sk, dst, &fl, req, |
6330 | &foc, false); | 6330 | &foc, TCP_SYNACK_FASTOPEN); |
6331 | /* Add the child socket directly into the accept queue */ | 6331 | /* Add the child socket directly into the accept queue */ |
6332 | inet_csk_reqsk_queue_add(sk, req, fastopen_sk); | 6332 | inet_csk_reqsk_queue_add(sk, req, fastopen_sk); |
6333 | sk->sk_data_ready(sk); | 6333 | sk->sk_data_ready(sk); |
@@ -6337,8 +6337,9 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, | |||
6337 | tcp_rsk(req)->tfo_listener = false; | 6337 | tcp_rsk(req)->tfo_listener = false; |
6338 | if (!want_cookie) | 6338 | if (!want_cookie) |
6339 | inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); | 6339 | inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); |
6340 | af_ops->send_synack(sk, dst, &fl, req, | 6340 | af_ops->send_synack(sk, dst, &fl, req, &foc, |
6341 | &foc, !want_cookie); | 6341 | !want_cookie ? TCP_SYNACK_NORMAL : |
6342 | TCP_SYNACK_COOKIE); | ||
6342 | if (want_cookie) { | 6343 | if (want_cookie) { |
6343 | reqsk_free(req); | 6344 | reqsk_free(req); |
6344 | return 0; | 6345 | return 0; |
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index f4f2a0a3849d..d2a5763e5abc 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -830,7 +830,7 @@ static int tcp_v4_send_synack(const struct sock *sk, struct dst_entry *dst, | |||
830 | struct flowi *fl, | 830 | struct flowi *fl, |
831 | struct request_sock *req, | 831 | struct request_sock *req, |
832 | struct tcp_fastopen_cookie *foc, | 832 | struct tcp_fastopen_cookie *foc, |
833 | bool attach_req) | 833 | enum tcp_synack_type synack_type) |
834 | { | 834 | { |
835 | const struct inet_request_sock *ireq = inet_rsk(req); | 835 | const struct inet_request_sock *ireq = inet_rsk(req); |
836 | struct flowi4 fl4; | 836 | struct flowi4 fl4; |
@@ -841,7 +841,7 @@ static int tcp_v4_send_synack(const struct sock *sk, struct dst_entry *dst, | |||
841 | if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL) | 841 | if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL) |
842 | return -1; | 842 | return -1; |
843 | 843 | ||
844 | skb = tcp_make_synack(sk, dst, req, foc, attach_req); | 844 | skb = tcp_make_synack(sk, dst, req, foc, synack_type); |
845 | 845 | ||
846 | if (skb) { | 846 | if (skb) { |
847 | __tcp_v4_send_check(skb, ireq->ir_loc_addr, ireq->ir_rmt_addr); | 847 | __tcp_v4_send_check(skb, ireq->ir_loc_addr, ireq->ir_rmt_addr); |
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 7d2dc015cd19..6451b83d81e9 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c | |||
@@ -2944,7 +2944,7 @@ int tcp_send_synack(struct sock *sk) | |||
2944 | struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, | 2944 | struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, |
2945 | struct request_sock *req, | 2945 | struct request_sock *req, |
2946 | struct tcp_fastopen_cookie *foc, | 2946 | struct tcp_fastopen_cookie *foc, |
2947 | bool attach_req) | 2947 | enum tcp_synack_type synack_type) |
2948 | { | 2948 | { |
2949 | struct inet_request_sock *ireq = inet_rsk(req); | 2949 | struct inet_request_sock *ireq = inet_rsk(req); |
2950 | const struct tcp_sock *tp = tcp_sk(sk); | 2950 | const struct tcp_sock *tp = tcp_sk(sk); |
@@ -2964,14 +2964,22 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, | |||
2964 | /* Reserve space for headers. */ | 2964 | /* Reserve space for headers. */ |
2965 | skb_reserve(skb, MAX_TCP_HEADER); | 2965 | skb_reserve(skb, MAX_TCP_HEADER); |
2966 | 2966 | ||
2967 | if (attach_req) { | 2967 | switch (synack_type) { |
2968 | case TCP_SYNACK_NORMAL: | ||
2968 | skb_set_owner_w(skb, req_to_sk(req)); | 2969 | skb_set_owner_w(skb, req_to_sk(req)); |
2969 | } else { | 2970 | break; |
2971 | case TCP_SYNACK_COOKIE: | ||
2972 | /* Under synflood, we do not attach skb to a socket, | ||
2973 | * to avoid false sharing. | ||
2974 | */ | ||
2975 | break; | ||
2976 | case TCP_SYNACK_FASTOPEN: | ||
2970 | /* sk is a const pointer, because we want to express multiple | 2977 | /* sk is a const pointer, because we want to express multiple |
2971 | * cpu might call us concurrently. | 2978 | * cpu might call us concurrently. |
2972 | * sk->sk_wmem_alloc in an atomic, we can promote to rw. | 2979 | * sk->sk_wmem_alloc in an atomic, we can promote to rw. |
2973 | */ | 2980 | */ |
2974 | skb_set_owner_w(skb, (struct sock *)sk); | 2981 | skb_set_owner_w(skb, (struct sock *)sk); |
2982 | break; | ||
2975 | } | 2983 | } |
2976 | skb_dst_set(skb, dst); | 2984 | skb_dst_set(skb, dst); |
2977 | 2985 | ||
@@ -3516,7 +3524,7 @@ int tcp_rtx_synack(const struct sock *sk, struct request_sock *req) | |||
3516 | int res; | 3524 | int res; |
3517 | 3525 | ||
3518 | tcp_rsk(req)->txhash = net_tx_rndhash(); | 3526 | tcp_rsk(req)->txhash = net_tx_rndhash(); |
3519 | res = af_ops->send_synack(sk, NULL, &fl, req, NULL, true); | 3527 | res = af_ops->send_synack(sk, NULL, &fl, req, NULL, TCP_SYNACK_NORMAL); |
3520 | if (!res) { | 3528 | if (!res) { |
3521 | TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); | 3529 | TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); |
3522 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS); | 3530 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS); |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 0e621bc1ae11..800265c7fd3f 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -439,7 +439,7 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst, | |||
439 | struct flowi *fl, | 439 | struct flowi *fl, |
440 | struct request_sock *req, | 440 | struct request_sock *req, |
441 | struct tcp_fastopen_cookie *foc, | 441 | struct tcp_fastopen_cookie *foc, |
442 | bool attach_req) | 442 | enum tcp_synack_type synack_type) |
443 | { | 443 | { |
444 | struct inet_request_sock *ireq = inet_rsk(req); | 444 | struct inet_request_sock *ireq = inet_rsk(req); |
445 | struct ipv6_pinfo *np = inet6_sk(sk); | 445 | struct ipv6_pinfo *np = inet6_sk(sk); |
@@ -452,7 +452,7 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst, | |||
452 | IPPROTO_TCP)) == NULL) | 452 | IPPROTO_TCP)) == NULL) |
453 | goto done; | 453 | goto done; |
454 | 454 | ||
455 | skb = tcp_make_synack(sk, dst, req, foc, attach_req); | 455 | skb = tcp_make_synack(sk, dst, req, foc, synack_type); |
456 | 456 | ||
457 | if (skb) { | 457 | if (skb) { |
458 | __tcp_v6_send_check(skb, &ireq->ir_v6_loc_addr, | 458 | __tcp_v6_send_check(skb, &ireq->ir_v6_loc_addr, |