From: Willem de Bruijn <will...@google.com>

Support sending more than 1B payload and passing segment size.

- add option '-s' to send more than 1B payload
- add option '-m' to configure mss

If size exceeds mss, enable UDP_SEGMENT.

Optionally also allow configuring multi release time.

- add option '-M' to set release interval (msec)
- add option '-N' to set release segment count

Both options have to be specified, or neither.

Add a testcase to so_txtime.sh over loopback.
Add a testscript so_txtime_multi.sh that operates over veth using
txonly/rxonly mode.

Also fix a small sock_extended_err parsing bug, mixing up
ee_code and ee_errno

Signed-off-by: Willem de Bruijn <will...@google.com>
---
 tools/testing/selftests/net/so_txtime.c       | 80 +++++++++++++++----
 tools/testing/selftests/net/so_txtime.sh      |  7 ++
 .../testing/selftests/net/so_txtime_multi.sh  | 68 ++++++++++++++++
 3 files changed, 141 insertions(+), 14 deletions(-)
 create mode 100755 tools/testing/selftests/net/so_txtime_multi.sh

diff --git a/tools/testing/selftests/net/so_txtime.c 
b/tools/testing/selftests/net/so_txtime.c
index fa748e4209c0..da4ef411693c 100644
--- a/tools/testing/selftests/net/so_txtime.c
+++ b/tools/testing/selftests/net/so_txtime.c
@@ -17,6 +17,7 @@
 #include <linux/errqueue.h>
 #include <linux/ipv6.h>
 #include <linux/tcp.h>
+#include <netinet/udp.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -32,7 +33,11 @@ static const char *cfg_addr;
 static int     cfg_clockid     = CLOCK_TAI;
 static bool    cfg_do_ipv4;
 static bool    cfg_do_ipv6;
+static uint8_t cfg_mr_num;
+static uint8_t cfg_mr_ival;
+static int     cfg_mss         = 1400;
 static bool    cfg_rxonly;
+static uint16_t        cfg_size        = 1;
 static int     cfg_timeout_sec;
 static bool    cfg_txonly;
 static uint16_t        cfg_port        = 8000;
@@ -66,6 +71,7 @@ static uint64_t gettime_ns(void)
 
 static void do_send_one(int fdt, struct timed_send *ts)
 {
+       static char buf[1 << 16];
        char control[CMSG_SPACE(sizeof(uint64_t))];
        struct msghdr msg = {0};
        struct iovec iov = {0};
@@ -73,8 +79,10 @@ static void do_send_one(int fdt, struct timed_send *ts)
        uint64_t tdeliver;
        int ret;
 
-       iov.iov_base = &ts->data;
-       iov.iov_len = 1;
+       memset(buf, ts->data, cfg_size);
+
+       iov.iov_base = buf;
+       iov.iov_len = cfg_size;
 
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
@@ -85,6 +93,11 @@ static void do_send_one(int fdt, struct timed_send *ts)
                msg.msg_controllen = sizeof(control);
 
                tdeliver = glob_tstart + ts->delay_us * 1000;
+               if (cfg_mr_ival) {
+                       tdeliver &= ~0xFF;
+                       tdeliver |= cfg_mr_ival << 4;
+                       tdeliver |= cfg_mr_num;
+               }
 
                cm = CMSG_FIRSTHDR(&msg);
                cm->cmsg_level = SOL_SOCKET;
@@ -104,30 +117,41 @@ static void do_send_one(int fdt, struct timed_send *ts)
 static bool do_recv_one(int fdr, struct timed_send *ts)
 {
        int64_t tstop, texpect;
+       int total = 0;
        char rbuf[2];
        int ret;
 
-       ret = recv(fdr, rbuf, sizeof(rbuf), 0);
+read_again:
+       ret = recv(fdr, rbuf, sizeof(rbuf), MSG_TRUNC);
        if (ret == -1 && errno == EAGAIN)
-               return true;
+               goto timedout;
        if (ret == -1)
                error(1, errno, "read");
-       if (ret != 1)
-               error(1, 0, "read: %dB", ret);
 
        tstop = (gettime_ns() - glob_tstart) / 1000;
        texpect = ts->delay_us >= 0 ? ts->delay_us : 0;
 
-       fprintf(stderr, "payload:%c delay:%lld expected:%lld (us)\n",
-                       rbuf[0], (long long)tstop, (long long)texpect);
+       fprintf(stderr, "payload:%c delay:%lld expected:%lld (us) -- 
read=%d,len=%d,total=%d\n",
+                       rbuf[0], (long long)tstop, (long long)texpect,
+                       total, ret, cfg_size);
 
        if (rbuf[0] != ts->data)
                error(1, 0, "payload mismatch. expected %c", ts->data);
 
-       if (labs(tstop - texpect) > cfg_variance_us)
+       total += ret;
+       if (total < cfg_size)
+               goto read_again;
+
+       /* measure latency if all data arrives in a single datagram (not GSO) */
+       if (ret == cfg_size && labs(tstop - texpect) > cfg_variance_us)
                error(1, 0, "exceeds variance (%d us)", cfg_variance_us);
 
        return false;
+
+timedout:
+       if (total != 0 && total != cfg_size)
+               error(1, 0, "timeout mid-read");
+       return true;
 }
 
 static void do_recv_verify_empty(int fdr)
@@ -168,7 +192,9 @@ static void do_recv_errqueue_timeout(int fdt)
                        break;
                if (ret == -1)
                        error(1, errno, "errqueue");
-               if (msg.msg_flags != MSG_ERRQUEUE)
+               if (ret != sizeof(data))
+                       error(1, errno, "insufficient data");
+               if (msg.msg_flags & ~(MSG_ERRQUEUE | MSG_TRUNC))
                        error(1, 0, "errqueue: flags 0x%x\n", msg.msg_flags);
 
                cm = CMSG_FIRSTHDR(&msg);
@@ -180,7 +206,9 @@ static void do_recv_errqueue_timeout(int fdt)
                err = (struct sock_extended_err *)CMSG_DATA(cm);
                if (err->ee_origin != SO_EE_ORIGIN_TXTIME)
                        error(1, 0, "errqueue: origin 0x%x\n", err->ee_origin);
-               if (err->ee_code != ECANCELED)
+               if (err->ee_errno != ECANCELED)
+                       error(1, 0, "errqueue: errno 0x%x\n", err->ee_errno);
+               if (err->ee_code != SO_EE_CODE_TXTIME_MISSED)
                        error(1, 0, "errqueue: code 0x%x\n", err->ee_code);
 
                tstamp = ((int64_t) err->ee_data) << 32 | err->ee_info;
@@ -202,7 +230,7 @@ static void setsockopt_txtime(int fd)
        struct sock_txtime so_txtime_val_read = { 0 };
        socklen_t vallen = sizeof(so_txtime_val);
 
-       so_txtime_val.flags = SOF_TXTIME_REPORT_ERRORS;
+       so_txtime_val.flags = SOF_TXTIME_REPORT_ERRORS | 
SOF_TXTIME_MULTI_RELEASE;
 
        if (setsockopt(fd, SOL_SOCKET, SO_TXTIME,
                       &so_txtime_val, sizeof(so_txtime_val)))
@@ -230,6 +258,12 @@ static int setup_tx(struct sockaddr *addr, socklen_t alen)
 
        setsockopt_txtime(fd);
 
+       if (cfg_size > cfg_mss) {
+               if (setsockopt(fd, SOL_UDP, UDP_SEGMENT,
+                              &cfg_mss, sizeof(cfg_mss)))
+                       error(1, errno, "setsockopt udp segment");
+       }
+
        return fd;
 }
 
@@ -321,7 +355,7 @@ static void parse_opts(int argc, char **argv)
 {
        int c, ilen, olen;
 
-       while ((c = getopt(argc, argv, "46A:c:rtT:")) != -1) {
+       while ((c = getopt(argc, argv, "46A:c:m:M:N:rs:tT:")) != -1) {
                switch (c) {
                case '4':
                        cfg_do_ipv4 = true;
@@ -341,9 +375,25 @@ static void parse_opts(int argc, char **argv)
                        else
                                error(1, 0, "unknown clock id %s", optarg);
                        break;
+               case 'm':
+                       cfg_mss = strtol(optarg, NULL, 0);
+                       break;
+               case 'M':
+                       cfg_mr_ival = atoi(optarg);
+                       if (cfg_mr_ival > 0xF)
+                               error(1, 0, "multi release ival exceeds max");
+                       break;
+               case 'N':
+                       cfg_mr_num = atoi(optarg);
+                       if (cfg_mr_num > 0xF)
+                               error(1, 0, "multi release count exceeds max");
+                       break;
                case 'r':
                        cfg_rxonly = true;
                        break;
+               case 's':
+                       cfg_size = atoi(optarg);
+                       break;
                case 't':
                        cfg_txonly = true;
                        break;
@@ -356,12 +406,14 @@ static void parse_opts(int argc, char **argv)
        }
 
        if (argc - optind != 2)
-               error(1, 0, "Usage: %s [-46rt] [-A addr] [-c clock] [-T 
timeout] <in> <out>", argv[0]);
+               error(1, 0, "Usage: %s [-46rt] [-A addr] [-c clock] [-m mtu] 
[-M ival] [-N num] [-s size] [-T timeout] <in> <out>", argv[0]);
 
        if (cfg_rxonly && cfg_txonly)
                error(1, 0, "Select rx-only or tx-only, not both");
        if (cfg_addr && cfg_do_ipv4 && cfg_do_ipv6)
                error(1, 0, "Cannot run both IPv4 and IPv6 when passing 
address");
+       if (!!cfg_mr_ival ^ !!cfg_mr_num)
+               error(1, 0, "Multi release pacing requires both -M and -N");
 
        ilen = parse_io(argv[optind], cfg_in);
        olen = parse_io(argv[optind + 1], cfg_out);
diff --git a/tools/testing/selftests/net/so_txtime.sh 
b/tools/testing/selftests/net/so_txtime.sh
index 3f7800eaecb1..7c60c11717e4 100755
--- a/tools/testing/selftests/net/so_txtime.sh
+++ b/tools/testing/selftests/net/so_txtime.sh
@@ -16,13 +16,20 @@ fi
 
 set -e
 
+ip link set dev lo mtu 1500
 tc qdisc add dev lo root fq
+
 ./so_txtime -4 -6 -c mono a,-1 a,-1
 ./so_txtime -4 -6 -c mono a,0 a,0
 ./so_txtime -4 -6 -c mono a,10 a,10
 ./so_txtime -4 -6 -c mono a,10,b,20 a,10,b,20
 ./so_txtime -4 -6 -c mono a,20,b,10 b,20,a,20
 
+# test gso
+./so_txtime -4 -6 -m 1000 -s 3500 -c mono a,50,b,100 a,50,b,100
+./so_txtime -4 -6 -m 1000 -s 3500 -M 5 -N 1 -c mono a,50,b,100 a,50,b,100
+./so_txtime -4 -6 -m 1000 -s 3500 -M 5 -N 2 -c mono a,50,b,100 a,50,b,100
+
 if tc qdisc replace dev lo root etf clockid CLOCK_TAI delta 400000; then
        ! ./so_txtime -4 -6 -c tai a,-1 a,-1
        ! ./so_txtime -4 -6 -c tai a,0 a,0
diff --git a/tools/testing/selftests/net/so_txtime_multi.sh 
b/tools/testing/selftests/net/so_txtime_multi.sh
new file mode 100755
index 000000000000..4e5ab06fd178
--- /dev/null
+++ b/tools/testing/selftests/net/so_txtime_multi.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Regression tests for the SO_TXTIME interface
+
+readonly ns_prefix="ns-sotxtime-"
+readonly ns1="${ns_prefix}1"
+readonly ns2="${ns_prefix}2"
+
+readonly ns1_v4=192.168.1.1
+readonly ns2_v4=192.168.1.2
+readonly ns1_v6=fd::1
+readonly ns2_v6=fd::2
+
+set -eu
+
+cleanup() {
+       ip netns del "${ns2}"
+       ip netns del "${ns1}"
+}
+
+setup() {
+       ip netns add "${ns1}"
+       ip netns add "${ns2}"
+
+       ip link add dev veth1 mtu 1500 netns "${ns1}" type veth \
+             peer name veth2 mtu 1500 netns "${ns2}"
+
+       ip -netns "${ns1}" link set veth1 up
+       ip -netns "${ns2}" link set veth2 up
+
+       ip -netns "${ns1}" -4 addr add "${ns1_v4}/24" dev veth1
+       ip -netns "${ns2}" -4 addr add "${ns2_v4}/24" dev veth2
+       ip -netns "${ns1}" -6 addr add "${ns1_v6}/64" dev veth1 nodad
+       ip -netns "${ns2}" -6 addr add "${ns2_v6}/64" dev veth2 nodad
+
+       ip netns exec "${ns1}" tc qdisc add dev veth1 root fq
+}
+
+run_test() {
+       ip netns exec "${ns2}" ./so_txtime -r -T 1 $@ &
+       sleep 0.1
+       ip netns exec "${ns1}" ./so_txtime -t $@
+       wait
+}
+
+run_test_46() {
+       run_test -4 -A "${ns2_v4}" $@
+       run_test -6 -A "${ns2_v6}" $@
+}
+
+trap cleanup EXIT
+setup
+
+echo "pacing"
+TEST_ARGS="-c mono a,10 a,10"
+run_test_46 ${TEST_ARGS}
+
+echo "gso + pacing"
+TEST_ARGS_GSO="-m 1000 -s 4500 ${TEST_ARGS}"
+run_test_46 ${TEST_ARGS_GSO}
+
+echo "gso + multi release pacing"
+run_test_46 -M 5 -N 1 ${TEST_ARGS_GSO}
+run_test_46 -M 5 -N 2 ${TEST_ARGS_GSO}
+
+# Does not validate pacing delay yet. Check manually.
+echo "Ok. Executed tests."
-- 
2.27.0.278.ge193c7cf3a9-goog

Reply via email to