Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tcp: fix TCP socket rehash stats mis-accounting

The previous commit 32efcc06d2a1 ("tcp: export count for rehash attempts")
would mis-account rehashing SNMP and socket stats:

a. During handshake of an active open, only counts the first
SYN timeout

b. After handshake of passive and active open, stop updating
after (roughly) TCP_RETRIES1 recurring RTOs

c. After the socket aborts, over count timeout_rehash by 1

This patch fixes this by checking the rehash result from sk_rethink_txhash.

Fixes: 32efcc06d2a1 ("tcp: export count for rehash attempts")
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Link: https://lore.kernel.org/r/20210119192619.1848270-1-ycheng@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Yuchung Cheng and committed by
Jakub Kicinski
9c30ae83 8e4052c3

+22 -22
+12 -5
include/net/sock.h
··· 1921 1921 sk->sk_txhash = net_tx_rndhash(); 1922 1922 } 1923 1923 1924 - static inline void sk_rethink_txhash(struct sock *sk) 1924 + static inline bool sk_rethink_txhash(struct sock *sk) 1925 1925 { 1926 - if (sk->sk_txhash) 1926 + if (sk->sk_txhash) { 1927 1927 sk_set_txhash(sk); 1928 + return true; 1929 + } 1930 + return false; 1928 1931 } 1929 1932 1930 1933 static inline struct dst_entry * ··· 1950 1947 return dst; 1951 1948 } 1952 1949 1953 - static inline void dst_negative_advice(struct sock *sk) 1950 + static inline void __dst_negative_advice(struct sock *sk) 1954 1951 { 1955 1952 struct dst_entry *ndst, *dst = __sk_dst_get(sk); 1956 - 1957 - sk_rethink_txhash(sk); 1958 1953 1959 1954 if (dst && dst->ops->negative_advice) { 1960 1955 ndst = dst->ops->negative_advice(dst); ··· 1963 1962 sk->sk_dst_pending_confirm = 0; 1964 1963 } 1965 1964 } 1965 + } 1966 + 1967 + static inline void dst_negative_advice(struct sock *sk) 1968 + { 1969 + sk_rethink_txhash(sk); 1970 + __dst_negative_advice(sk); 1966 1971 } 1967 1972 1968 1973 static inline void
+2 -3
net/ipv4/tcp_input.c
··· 4397 4397 * The receiver remembers and reflects via DSACKs. Leverage the 4398 4398 * DSACK state and change the txhash to re-route speculatively. 4399 4399 */ 4400 - if (TCP_SKB_CB(skb)->seq == tcp_sk(sk)->duplicate_sack[0].start_seq) { 4401 - sk_rethink_txhash(sk); 4400 + if (TCP_SKB_CB(skb)->seq == tcp_sk(sk)->duplicate_sack[0].start_seq && 4401 + sk_rethink_txhash(sk)) 4402 4402 NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPDUPLICATEDATAREHASH); 4403 - } 4404 4403 } 4405 4404 4406 4405 static void tcp_send_dupack(struct sock *sk, const struct sk_buff *skb)
+8 -14
net/ipv4/tcp_timer.c
··· 219 219 int retry_until; 220 220 221 221 if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { 222 - if (icsk->icsk_retransmits) { 223 - dst_negative_advice(sk); 224 - } else { 225 - sk_rethink_txhash(sk); 226 - tp->timeout_rehash++; 227 - __NET_INC_STATS(sock_net(sk), 228 - LINUX_MIB_TCPTIMEOUTREHASH); 229 - } 222 + if (icsk->icsk_retransmits) 223 + __dst_negative_advice(sk); 230 224 retry_until = icsk->icsk_syn_retries ? : net->ipv4.sysctl_tcp_syn_retries; 231 225 expired = icsk->icsk_retransmits >= retry_until; 232 226 } else { ··· 228 234 /* Black hole detection */ 229 235 tcp_mtu_probing(icsk, sk); 230 236 231 - dst_negative_advice(sk); 232 - } else { 233 - sk_rethink_txhash(sk); 234 - tp->timeout_rehash++; 235 - __NET_INC_STATS(sock_net(sk), 236 - LINUX_MIB_TCPTIMEOUTREHASH); 237 + __dst_negative_advice(sk); 237 238 } 238 239 239 240 retry_until = net->ipv4.sysctl_tcp_retries2; ··· 257 268 /* Has it gone just too far? */ 258 269 tcp_write_err(sk); 259 270 return 1; 271 + } 272 + 273 + if (sk_rethink_txhash(sk)) { 274 + tp->timeout_rehash++; 275 + __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPTIMEOUTREHASH); 260 276 } 261 277 262 278 return 0;