new file mode 100755
@@ -0,0 +1,65 @@
+#!/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}" tcpdump -q -n -i veth2 udp &
+ ip netns exec "${ns2}" ./udpgso_bench_rx &
+ sleep 0.1
+ ip netns exec "${ns1}" ./udpgso_bench_tx $@
+ pkill -P $$
+}
+
+run_test_46() {
+ run_test -4 -D "${ns2_v4}" $@
+ run_test -6 -D "${ns2_v6}" $@
+}
+
+trap cleanup EXIT
+setup
+
+echo "gso + pacing"
+TEST_ARGS="-l 1 -s 3500 -S 1000 -v -d 200000 -x 1000000"
+run_test_46 ${TEST_ARGS} -x 1000000
+
+echo "gso + multi release pacing"
+run_test_46 ${TEST_ARGS} -X -x 0x989611
+run_test_46 ${TEST_ARGS} -X -x 0x9896A2
+
+# Does not validate pacing delay yet. Check manually.
+echo "Ok. Executed tests."
@@ -23,6 +23,7 @@
#include <sys/time.h>
#include <sys/poll.h>
#include <sys/types.h>
+#include <time.h>
#include <unistd.h>
#include "../kselftest.h"
@@ -56,6 +57,7 @@
static bool cfg_cache_trash;
static int cfg_cpu = -1;
static int cfg_connected = true;
+static int cfg_delay_us;
static int cfg_family = PF_UNSPEC;
static uint16_t cfg_mss;
static int cfg_payload_len = (1472 * 42);
@@ -65,6 +67,8 @@ static bool cfg_poll;
static bool cfg_segment;
static bool cfg_sendmmsg;
static bool cfg_tcp;
+static uint64_t cfg_txtime;
+static bool cfg_txtime_multi;
static uint32_t cfg_tx_ts = SOF_TIMESTAMPING_TX_SOFTWARE;
static bool cfg_tx_tstamp;
static bool cfg_audit;
@@ -306,6 +310,34 @@ static void send_ts_cmsg(struct cmsghdr *cm)
*valp = cfg_tx_ts;
}
+static uint64_t gettime_ns(void)
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts))
+ error(1, errno, "gettime");
+
+ return ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec;
+}
+
+static void send_txtime_cmsg(struct cmsghdr *cm)
+{
+ uint64_t tdeliver, *valp;
+
+ tdeliver = gettime_ns() + cfg_txtime;
+
+ if (cfg_txtime_multi) {
+ tdeliver &= ~0xFF;
+ tdeliver |= cfg_txtime & 0xFF;
+ }
+
+ cm->cmsg_level = SOL_SOCKET;
+ cm->cmsg_type = SCM_TXTIME;
+ cm->cmsg_len = CMSG_LEN(sizeof(cfg_txtime));
+ valp = (void *)CMSG_DATA(cm);
+ *valp = tdeliver;
+}
+
static int send_udp_sendmmsg(int fd, char *data)
{
char control[CMSG_SPACE(sizeof(cfg_tx_ts))] = {0};
@@ -373,7 +405,8 @@ static void send_udp_segment_cmsg(struct cmsghdr *cm)
static int send_udp_segment(int fd, char *data)
{
char control[CMSG_SPACE(sizeof(cfg_gso_size)) +
- CMSG_SPACE(sizeof(cfg_tx_ts))] = {0};
+ CMSG_SPACE(sizeof(cfg_tx_ts)) +
+ CMSG_SPACE(sizeof(uint64_t))] = {0};
struct msghdr msg = {0};
struct iovec iov = {0};
size_t msg_controllen;
@@ -390,12 +423,17 @@ static int send_udp_segment(int fd, char *data)
msg.msg_controllen = sizeof(control);
cmsg = CMSG_FIRSTHDR(&msg);
send_udp_segment_cmsg(cmsg);
- msg_controllen = CMSG_SPACE(sizeof(cfg_mss));
+ msg_controllen = CMSG_SPACE(sizeof(cfg_gso_size));
if (cfg_tx_tstamp) {
cmsg = CMSG_NXTHDR(&msg, cmsg);
send_ts_cmsg(cmsg);
msg_controllen += CMSG_SPACE(sizeof(cfg_tx_ts));
}
+ if (cfg_txtime) {
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ send_txtime_cmsg(cmsg);
+ msg_controllen += CMSG_SPACE(sizeof(cfg_txtime));
+ }
msg.msg_controllen = msg_controllen;
msg.msg_name = (void *)&cfg_dst_addr;
@@ -413,7 +451,7 @@ static int send_udp_segment(int fd, char *data)
static void usage(const char *filepath)
{
- error(1, 0, "Usage: %s [-46acmHPtTuvz] [-C cpu] [-D dst ip] [-l secs] [-M messagenr] [-p port] [-s sendsize] [-S gsosize]",
+ error(1, 0, "Usage: %s [-46acmHPtTuvXz] [-C cpu] [-d delay] [-D dst ip] [-l secs] [-M messagenr] [-p port] [-s sendsize] [-S gsosize] [-x time]",
filepath);
}
@@ -422,7 +460,7 @@ static void parse_opts(int argc, char **argv)
int max_len, hdrlen;
int c;
- while ((c = getopt(argc, argv, "46acC:D:Hl:mM:p:s:PS:tTuvz")) != -1) {
+ while ((c = getopt(argc, argv, "46acC:d:D:Hl:mM:p:s:PS:tTuvx:Xz")) != -1) {
switch (c) {
case '4':
if (cfg_family != PF_UNSPEC)
@@ -445,6 +483,9 @@ static void parse_opts(int argc, char **argv)
case 'C':
cfg_cpu = strtol(optarg, NULL, 0);
break;
+ case 'd':
+ cfg_delay_us = strtol(optarg, NULL, 0);
+ break;
case 'D':
setup_sockaddr(cfg_family, optarg, &cfg_dst_addr);
break;
@@ -486,6 +527,12 @@ static void parse_opts(int argc, char **argv)
case 'v':
cfg_verbose = true;
break;
+ case 'x':
+ cfg_txtime = strtoull(optarg, NULL, 0);
+ break;
+ case 'X':
+ cfg_txtime_multi = true;
+ break;
case 'z':
cfg_zerocopy = true;
break;
@@ -551,6 +598,17 @@ static void set_tx_timestamping(int fd)
error(1, errno, "setsockopt tx timestamping");
}
+static void set_txtime(int fd)
+{
+ struct sock_txtime txt = { .clockid = CLOCK_MONOTONIC };
+
+ if (cfg_txtime_multi)
+ txt.flags = SOF_TXTIME_MULTI_RELEASE;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_TXTIME, &txt, sizeof(txt)))
+ error(1, errno, "setsockopt txtime");
+}
+
static void print_audit_report(unsigned long num_msgs, unsigned long num_sends)
{
unsigned long tdelta;
@@ -652,6 +710,9 @@ int main(int argc, char **argv)
if (cfg_tx_tstamp)
set_tx_timestamping(fd);
+ if (cfg_txtime)
+ set_txtime(fd);
+
num_msgs = num_sends = 0;
tnow = gettimeofday_ms();
tstart = tnow;
@@ -687,6 +748,9 @@ int main(int argc, char **argv)
if (cfg_cache_trash)
i = ++i < NUM_PKT ? i : 0;
+ if (cfg_delay_us)
+ usleep(cfg_delay_us);
+
} while (!interrupted && (cfg_runtime_ms == -1 || tnow < tstop));
if (cfg_zerocopy || cfg_tx_tstamp)