xref: /linux/tools/testing/selftests/net/forwarding/tsn_lib.sh (revision ebd297a2affadb6f6f4d2e5d975c1eda18ac762d)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3# Copyright 2021-2022 NXP
4
5tc_testing_scripts_dir=$(dirname $0)/../../tc-testing/scripts
6
7REQUIRE_ISOCHRON=${REQUIRE_ISOCHRON:=yes}
8REQUIRE_LINUXPTP=${REQUIRE_LINUXPTP:=yes}
9
10# Tunables
11UTC_TAI_OFFSET=37
12ISOCHRON_CPU=1
13
14if [[ "$REQUIRE_ISOCHRON" = "yes" ]]; then
15	# https://github.com/vladimiroltean/tsn-scripts
16	# WARNING: isochron versions pre-1.0 are unstable,
17	# always use the latest version
18	require_command isochron
19fi
20if [[ "$REQUIRE_LINUXPTP" = "yes" ]]; then
21	require_command phc2sys
22	require_command ptp4l
23	require_command phc_ctl
24fi
25
26phc2sys_start()
27{
28	local uds_address=$1
29	local extra_args=""
30
31	if ! [ -z "${uds_address}" ]; then
32		extra_args="${extra_args} -z ${uds_address}"
33	fi
34
35	phc2sys_log="$(mktemp)"
36
37	chrt -f 10 phc2sys -m \
38		-a -rr \
39		--step_threshold 0.00002 \
40		--first_step_threshold 0.00002 \
41		${extra_args} \
42		> "${phc2sys_log}" 2>&1 &
43	phc2sys_pid=$!
44
45	echo "phc2sys logs to ${phc2sys_log} and has pid ${phc2sys_pid}"
46
47	sleep 1
48}
49
50phc2sys_stop()
51{
52	{ kill ${phc2sys_pid} && wait ${phc2sys_pid}; } 2> /dev/null
53	rm "${phc2sys_log}" 2> /dev/null
54}
55
56# Replace space separators from interface list with underscores
57if_names_to_label()
58{
59	local if_name_list="$1"
60
61	echo "${if_name_list/ /_}"
62}
63
64ptp4l_start()
65{
66	local if_names="$1"
67	local slave_only=$2
68	local uds_address=$3
69	local log="ptp4l_log_$(if_names_to_label ${if_names})"
70	local pid="ptp4l_pid_$(if_names_to_label ${if_names})"
71	local extra_args=""
72
73	for if_name in ${if_names}; do
74		extra_args="${extra_args} -i ${if_name}"
75	done
76
77	if [ "${slave_only}" = true ]; then
78		extra_args="${extra_args} -s"
79	fi
80
81	# declare dynamic variables ptp4l_log_${if_name} and ptp4l_pid_${if_name}
82	# as global, so that they can be referenced later
83	declare -g "${log}=$(mktemp)"
84
85	chrt -f 10 ptp4l -m -2 -P \
86		--step_threshold 0.00002 \
87		--first_step_threshold 0.00002 \
88		--tx_timestamp_timeout 100 \
89		--uds_address="${uds_address}" \
90		${extra_args} \
91		> "${!log}" 2>&1 &
92	declare -g "${pid}=$!"
93
94	echo "ptp4l for interfaces ${if_names} logs to ${!log} and has pid ${!pid}"
95
96	sleep 1
97}
98
99ptp4l_stop()
100{
101	local if_names="$1"
102	local log="ptp4l_log_$(if_names_to_label ${if_names})"
103	local pid="ptp4l_pid_$(if_names_to_label ${if_names})"
104
105	{ kill ${!pid} && wait ${!pid}; } 2> /dev/null
106	rm "${!log}" 2> /dev/null
107}
108
109cpufreq_max()
110{
111	local cpu=$1
112	local freq="cpu${cpu}_freq"
113	local governor="cpu${cpu}_governor"
114
115	# Kernel may be compiled with CONFIG_CPU_FREQ disabled
116	if ! [ -d /sys/bus/cpu/devices/cpu${cpu}/cpufreq ]; then
117		return
118	fi
119
120	# declare dynamic variables cpu${cpu}_freq and cpu${cpu}_governor as
121	# global, so they can be referenced later
122	declare -g "${freq}=$(cat /sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_min_freq)"
123	declare -g "${governor}=$(cat /sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_governor)"
124
125	cat /sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_max_freq > \
126		/sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_min_freq
127	echo -n "performance" > \
128		/sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_governor
129}
130
131cpufreq_restore()
132{
133	local cpu=$1
134	local freq="cpu${cpu}_freq"
135	local governor="cpu${cpu}_governor"
136
137	if ! [ -d /sys/bus/cpu/devices/cpu${cpu}/cpufreq ]; then
138		return
139	fi
140
141	echo "${!freq}" > /sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_min_freq
142	echo -n "${!governor}" > \
143		/sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_governor
144}
145
146isochron_recv_start()
147{
148	local if_name=$1
149	local uds=$2
150	local stats_port=$3
151	local extra_args=$4
152	local pid="isochron_pid_${stats_port}"
153
154	if ! [ -z "${uds}" ]; then
155		extra_args="${extra_args} --unix-domain-socket ${uds}"
156	fi
157
158	isochron rcv \
159		--interface ${if_name} \
160		--sched-priority 98 \
161		--sched-fifo \
162		--utc-tai-offset ${UTC_TAI_OFFSET} \
163		--stats-port ${stats_port} \
164		--quiet \
165		${extra_args} & \
166	declare -g "${pid}=$!"
167
168	sleep 1
169}
170
171isochron_recv_stop()
172{
173	local stats_port=$1
174	local pid="isochron_pid_${stats_port}"
175
176	{ kill ${!pid} && wait ${!pid}; } 2> /dev/null
177}
178
179isochron_do()
180{
181	local sender_if_name=$1; shift
182	local receiver_if_name=$1; shift
183	local sender_uds=$1; shift
184	local receiver_uds=$1; shift
185	local base_time=$1; shift
186	local cycle_time=$1; shift
187	local shift_time=$1; shift
188	local window_size=$1; shift
189	local num_pkts=$1; shift
190	local vid=$1; shift
191	local priority=$1; shift
192	local dst_ip=$1; shift
193	local isochron_dat=$1; shift
194	local extra_args=""
195	local receiver_extra_args=""
196	local vrf="$(master_name_get ${sender_if_name})"
197	local use_l2="true"
198
199	if ! [ -z "${dst_ip}" ]; then
200		use_l2="false"
201	fi
202
203	if ! [ -z "${vrf}" ]; then
204		dst_ip="${dst_ip}%${vrf}"
205	fi
206
207	if ! [ -z "${vid}" ]; then
208		vid="--vid=${vid}"
209	fi
210
211	if [ -z "${receiver_uds}" ]; then
212		extra_args="${extra_args} --omit-remote-sync"
213	fi
214
215	if ! [ -z "${shift_time}" ]; then
216		extra_args="${extra_args} --shift-time=${shift_time}"
217	fi
218
219	if ! [ -z "${window_size}" ]; then
220		extra_args="${extra_args} --window-size=${window_size}"
221	fi
222
223	if [ "${use_l2}" = "true" ]; then
224		extra_args="${extra_args} --l2 --etype=0xdead ${vid}"
225		receiver_extra_args="--l2 --etype=0xdead"
226	else
227		extra_args="${extra_args} --l4 --ip-destination=${dst_ip}"
228		receiver_extra_args="--l4"
229	fi
230
231	cpufreq_max ${ISOCHRON_CPU}
232
233	isochron_recv_start "${h2}" "${receiver_uds}" 5000 "${receiver_extra_args}"
234
235	isochron send \
236		--interface ${sender_if_name} \
237		--unix-domain-socket ${sender_uds} \
238		--priority ${priority} \
239		--base-time ${base_time} \
240		--cycle-time ${cycle_time} \
241		--num-frames ${num_pkts} \
242		--frame-size 64 \
243		--txtime \
244		--utc-tai-offset ${UTC_TAI_OFFSET} \
245		--cpu-mask $((1 << ${ISOCHRON_CPU})) \
246		--sched-fifo \
247		--sched-priority 98 \
248		--client 127.0.0.1 \
249		--sync-threshold 5000 \
250		--output-file ${isochron_dat} \
251		${extra_args} \
252		--quiet
253
254	isochron_recv_stop 5000
255
256	cpufreq_restore ${ISOCHRON_CPU}
257}
258
259isochron_report_num_received()
260{
261	local isochron_dat=$1; shift
262
263	# Count all received packets by looking at the non-zero RX timestamps
264	isochron report \
265		--input-file "${isochron_dat}" \
266		--printf-format "%u\n" --printf-args "R" | \
267		grep -w -v '0' | wc -l
268}
269
270taprio_wait_for_admin()
271{
272	local if_name="$1"; shift
273
274	"$tc_testing_scripts_dir/taprio_wait_for_admin.sh" "$(which tc)" "$if_name"
275}
276