From: SeongJae Park <sjpark@xxxxxxxxx> This commit adds a test for FIN_ACK process races related reconnection latency spike issues. The issue has described and solved by the previous commit ("tcp: Reduce SYN resend delay if a suspicous ACK is received"). Signed-off-by: SeongJae Park <sjpark@xxxxxxxxx> --- tools/testing/selftests/net/.gitignore | 2 + tools/testing/selftests/net/Makefile | 2 + tools/testing/selftests/net/fin_ack_lat.sh | 42 ++++++++++ .../selftests/net/fin_ack_lat_accept.c | 49 +++++++++++ .../selftests/net/fin_ack_lat_connect.c | 81 +++++++++++++++++++ 5 files changed, 176 insertions(+) create mode 100755 tools/testing/selftests/net/fin_ack_lat.sh create mode 100644 tools/testing/selftests/net/fin_ack_lat_accept.c create mode 100644 tools/testing/selftests/net/fin_ack_lat_connect.c diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 8aefd81fbc86..1bcf7b5498dd 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -22,3 +22,5 @@ ipv6_flowlabel_mgr so_txtime tcp_fastopen_backup_key nettest +fin_ack_lat_accept +fin_ack_lat_connect diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index a8e04d665b69..e4938c26ce3f 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -11,6 +11,7 @@ TEST_PROGS += udpgso_bench.sh fib_rule_tests.sh msg_zerocopy.sh psock_snd.sh TEST_PROGS += udpgro_bench.sh udpgro.sh test_vxlan_under_vrf.sh reuseport_addr_any.sh TEST_PROGS += test_vxlan_fdb_changelink.sh so_txtime.sh ipv6_flowlabel.sh TEST_PROGS += tcp_fastopen_backup_key.sh fcnal-test.sh l2tp.sh traceroute.sh +TEST_PROGS += fin_ack_lat.sh TEST_PROGS_EXTENDED := in_netns.sh TEST_GEN_FILES = socket nettest TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy reuseport_addr_any @@ -18,6 +19,7 @@ TEST_GEN_FILES += tcp_mmap tcp_inq psock_snd txring_overwrite TEST_GEN_FILES += udpgso udpgso_bench_tx udpgso_bench_rx ip_defrag TEST_GEN_FILES += so_txtime ipv6_flowlabel ipv6_flowlabel_mgr TEST_GEN_FILES += tcp_fastopen_backup_key +TEST_GEN_FILES += fin_ack_lat_accept fin_ack_lat_connect TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls diff --git a/tools/testing/selftests/net/fin_ack_lat.sh b/tools/testing/selftests/net/fin_ack_lat.sh new file mode 100755 index 000000000000..0a398c837b7a --- /dev/null +++ b/tools/testing/selftests/net/fin_ack_lat.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Test latency spikes caused by FIN/ACK handling race. + +set +x +set -e + +tmpfile=$(mktemp /tmp/fin_ack_latency.XXXX.log) + +kill_accept() { + kill $ACCEPT_PID +} + +cleanup() { + kill_accept + rm -f $tmpfile +} + +trap cleanup EXIT + +do_test() { + RUNTIME=$1 + + ./fin_ack_lat_accept & + ACCEPT_PID=$! + sleep 1 + + ./fin_ack_lat_connect | tee $tmpfile & + sleep $RUNTIME + NR_SPIKES=$(wc -l $tmpfile | awk '{print $1}') + rm $tmpfile + if [ $NR_SPIKES -gt 0 ] + then + echo "FAIL: $NR_SPIKES spikes detected" + return 1 + fi + return 0 +} + +do_test "30" +echo "test done" diff --git a/tools/testing/selftests/net/fin_ack_lat_accept.c b/tools/testing/selftests/net/fin_ack_lat_accept.c new file mode 100644 index 000000000000..a0f0210f12b4 --- /dev/null +++ b/tools/testing/selftests/net/fin_ack_lat_accept.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <error.h> +#include <netinet/in.h> +#include <stdio.h> +#include <sys/socket.h> +#include <unistd.h> + +int main(int argc, char const *argv[]) +{ + int sock, new_sock; + int opt = 1; + struct sockaddr_in address; + int addrlen = sizeof(address); + int buffer; + int rc; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (!sock) + error(-1, -1, "socket"); + + rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, + &opt, sizeof(opt)); + if (rc == -1) + error(-1, -1, "setsockopt"); + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(4242); + + rc = bind(sock, (struct sockaddr *)&address, sizeof(address)); + if (rc < 0) + error(-1, -1, "bind"); + + rc = listen(sock, 3); + if (rc < 0) + error(-1, -1, "listen"); + + while (1) { + new_sock = accept(sock, (struct sockaddr *)&address, + (socklen_t *)&addrlen); + if (new_sock < 0) + error(-1, -1, "accept"); + + rc = read(new_sock, &buffer, sizeof(buffer)); + close(new_sock); + } + return 0; +} diff --git a/tools/testing/selftests/net/fin_ack_lat_connect.c b/tools/testing/selftests/net/fin_ack_lat_connect.c new file mode 100644 index 000000000000..abfdd79f2e17 --- /dev/null +++ b/tools/testing/selftests/net/fin_ack_lat_connect.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <arpa/inet.h> +#include <error.h> +#include <netinet/tcp.h> +#include <stdio.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <unistd.h> + +static unsigned long timediff(struct timeval s, struct timeval e) +{ + if (s.tv_sec > e.tv_sec) + return 0; + return (e.tv_sec - s.tv_sec) * 1000000 + e.tv_usec - s.tv_usec; +} + +int main(int argc, char const *argv[]) +{ + int sock = 0; + struct sockaddr_in addr, laddr; + socklen_t len = sizeof(laddr); + struct linger sl; + int flag = 1; + int buffer; + int rc; + struct timeval start, end; + unsigned long lat, sum_lat = 0, nr_lat = 0; + + while (1) { + gettimeofday(&start, NULL); + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + error(-1, -1, "socket creation"); + + sl.l_onoff = 1; + sl.l_linger = 0; + if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl))) + error(-1, -1, "setsockopt(linger)"); + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + &flag, sizeof(flag))) + error(-1, -1, "setsockopt(nodelay)"); + + addr.sin_family = AF_INET; + addr.sin_port = htons(4242); + + rc = inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); + if (rc <= 0) + error(-1, -1, "inet_pton"); + + rc = connect(sock, (struct sockaddr *)&addr, sizeof(addr)); + if (rc < 0) + error(-1, -1, "connect"); + + send(sock, &buffer, sizeof(buffer), 0); + + rc = read(sock, &buffer, sizeof(buffer)); + + gettimeofday(&end, NULL); + lat = timediff(start, end); + sum_lat += lat; + nr_lat++; + if (lat > 100000) { + rc = getsockname(sock, (struct sockaddr *)&laddr, &len); + if (rc == -1) + error(-1, -1, "getsockname"); + printf("port: %d, lat: %lu, avg: %lu, nr: %lu\n", + ntohs(laddr.sin_port), lat, + sum_lat / nr_lat, nr_lat); + } + + if (nr_lat % 1000 == 0) + fflush(stdout); + + + close(sock); + } + return 0; +} -- 2.17.1