@@ -22,3 +22,5 @@ ipv6_flowlabel_mgr
so_txtime
tcp_fastopen_backup_key
nettest
+fin_ack_lat_accept
+fin_ack_lat_connect
@@ -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
new file mode 100755
@@ -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"
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}