diff --git a/tools/testing/selftests/net/udpgso_bench_multi.sh b/tools/testing/selftests/net/udpgso_bench_multi.sh
new file mode 100755
index 000000000000..c29f75aec759
--- /dev/null
+++ b/tools/testing/selftests/net/udpgso_bench_multi.sh
@@ -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."
diff --git a/tools/testing/selftests/net/udpgso_bench_tx.c b/tools/testing/selftests/net/udpgso_bench_tx.c
index 17512a43885e..264222c2b94e 100644
--- a/tools/testing/selftests/net/udpgso_bench_tx.c
+++ b/tools/testing/selftests/net/udpgso_bench_tx.c
@@ -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)
