| #!/bin/bash | |
| # SPDX-License-Identifier: GPL-2.0 | |
| # Copyright 2021-2022 NXP | |
| tc_testing_scripts_dir=$(dirname $0)/../../tc-testing/scripts | |
| REQUIRE_ISOCHRON=${REQUIRE_ISOCHRON:=yes} | |
| REQUIRE_LINUXPTP=${REQUIRE_LINUXPTP:=yes} | |
| # Tunables | |
| UTC_TAI_OFFSET=37 | |
| ISOCHRON_CPU=1 | |
| if [[ "$REQUIRE_ISOCHRON" = "yes" ]]; then | |
| # https://github.com/vladimiroltean/tsn-scripts | |
| # WARNING: isochron versions pre-1.0 are unstable, | |
| # always use the latest version | |
| require_command isochron | |
| fi | |
| if [[ "$REQUIRE_LINUXPTP" = "yes" ]]; then | |
| require_command phc2sys | |
| require_command ptp4l | |
| require_command phc_ctl | |
| fi | |
| phc2sys_start() | |
| { | |
| local uds_address=$1 | |
| local extra_args="" | |
| if ! [ -z "${uds_address}" ]; then | |
| extra_args="${extra_args} -z ${uds_address}" | |
| fi | |
| phc2sys_log="$(mktemp)" | |
| chrt -f 10 phc2sys -m \ | |
| -a -rr \ | |
| --step_threshold 0.00002 \ | |
| --first_step_threshold 0.00002 \ | |
| ${extra_args} \ | |
| > "${phc2sys_log}" 2>&1 & | |
| phc2sys_pid=$! | |
| echo "phc2sys logs to ${phc2sys_log} and has pid ${phc2sys_pid}" | |
| sleep 1 | |
| } | |
| phc2sys_stop() | |
| { | |
| { kill ${phc2sys_pid} && wait ${phc2sys_pid}; } 2> /dev/null | |
| rm "${phc2sys_log}" 2> /dev/null | |
| } | |
| # Replace space separators from interface list with underscores | |
| if_names_to_label() | |
| { | |
| local if_name_list="$1" | |
| echo "${if_name_list/ /_}" | |
| } | |
| ptp4l_start() | |
| { | |
| local if_names="$1" | |
| local slave_only=$2 | |
| local uds_address=$3 | |
| local log="ptp4l_log_$(if_names_to_label ${if_names})" | |
| local pid="ptp4l_pid_$(if_names_to_label ${if_names})" | |
| local extra_args="" | |
| for if_name in ${if_names}; do | |
| extra_args="${extra_args} -i ${if_name}" | |
| done | |
| if [ "${slave_only}" = true ]; then | |
| extra_args="${extra_args} -s" | |
| fi | |
| # declare dynamic variables ptp4l_log_${if_name} and ptp4l_pid_${if_name} | |
| # as global, so that they can be referenced later | |
| declare -g "${log}=$(mktemp)" | |
| chrt -f 10 ptp4l -m -2 -P \ | |
| --step_threshold 0.00002 \ | |
| --first_step_threshold 0.00002 \ | |
| --tx_timestamp_timeout 100 \ | |
| --uds_address="${uds_address}" \ | |
| ${extra_args} \ | |
| > "${!log}" 2>&1 & | |
| declare -g "${pid}=$!" | |
| echo "ptp4l for interfaces ${if_names} logs to ${!log} and has pid ${!pid}" | |
| sleep 1 | |
| } | |
| ptp4l_stop() | |
| { | |
| local if_names="$1" | |
| local log="ptp4l_log_$(if_names_to_label ${if_names})" | |
| local pid="ptp4l_pid_$(if_names_to_label ${if_names})" | |
| { kill ${!pid} && wait ${!pid}; } 2> /dev/null | |
| rm "${!log}" 2> /dev/null | |
| } | |
| cpufreq_max() | |
| { | |
| local cpu=$1 | |
| local freq="cpu${cpu}_freq" | |
| local governor="cpu${cpu}_governor" | |
| # Kernel may be compiled with CONFIG_CPU_FREQ disabled | |
| if ! [ -d /sys/bus/cpu/devices/cpu${cpu}/cpufreq ]; then | |
| return | |
| fi | |
| # declare dynamic variables cpu${cpu}_freq and cpu${cpu}_governor as | |
| # global, so they can be referenced later | |
| declare -g "${freq}=$(cat /sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_min_freq)" | |
| declare -g "${governor}=$(cat /sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_governor)" | |
| cat /sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_max_freq > \ | |
| /sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_min_freq | |
| echo -n "performance" > \ | |
| /sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_governor | |
| } | |
| cpufreq_restore() | |
| { | |
| local cpu=$1 | |
| local freq="cpu${cpu}_freq" | |
| local governor="cpu${cpu}_governor" | |
| if ! [ -d /sys/bus/cpu/devices/cpu${cpu}/cpufreq ]; then | |
| return | |
| fi | |
| echo "${!freq}" > /sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_min_freq | |
| echo -n "${!governor}" > \ | |
| /sys/bus/cpu/devices/cpu${cpu}/cpufreq/scaling_governor | |
| } | |
| isochron_recv_start() | |
| { | |
| local if_name=$1 | |
| local uds=$2 | |
| local stats_port=$3 | |
| local extra_args=$4 | |
| local pid="isochron_pid_${stats_port}" | |
| if ! [ -z "${uds}" ]; then | |
| extra_args="${extra_args} --unix-domain-socket ${uds}" | |
| fi | |
| isochron rcv \ | |
| --interface ${if_name} \ | |
| --sched-priority 98 \ | |
| --sched-fifo \ | |
| --utc-tai-offset ${UTC_TAI_OFFSET} \ | |
| --stats-port ${stats_port} \ | |
| --quiet \ | |
| ${extra_args} & \ | |
| declare -g "${pid}=$!" | |
| sleep 1 | |
| } | |
| isochron_recv_stop() | |
| { | |
| local stats_port=$1 | |
| local pid="isochron_pid_${stats_port}" | |
| { kill ${!pid} && wait ${!pid}; } 2> /dev/null | |
| } | |
| isochron_do() | |
| { | |
| local sender_if_name=$1; shift | |
| local receiver_if_name=$1; shift | |
| local sender_uds=$1; shift | |
| local receiver_uds=$1; shift | |
| local base_time=$1; shift | |
| local cycle_time=$1; shift | |
| local shift_time=$1; shift | |
| local window_size=$1; shift | |
| local num_pkts=$1; shift | |
| local vid=$1; shift | |
| local priority=$1; shift | |
| local dst_ip=$1; shift | |
| local isochron_dat=$1; shift | |
| local extra_args="" | |
| local receiver_extra_args="" | |
| local vrf="$(master_name_get ${sender_if_name})" | |
| local use_l2="true" | |
| if ! [ -z "${dst_ip}" ]; then | |
| use_l2="false" | |
| fi | |
| if ! [ -z "${vrf}" ]; then | |
| dst_ip="${dst_ip}%${vrf}" | |
| fi | |
| if ! [ -z "${vid}" ]; then | |
| vid="--vid=${vid}" | |
| fi | |
| if [ -z "${receiver_uds}" ]; then | |
| extra_args="${extra_args} --omit-remote-sync" | |
| fi | |
| if ! [ -z "${shift_time}" ]; then | |
| extra_args="${extra_args} --shift-time=${shift_time}" | |
| fi | |
| if ! [ -z "${window_size}" ]; then | |
| extra_args="${extra_args} --window-size=${window_size}" | |
| fi | |
| if [ "${use_l2}" = "true" ]; then | |
| extra_args="${extra_args} --l2 --etype=0xdead ${vid}" | |
| receiver_extra_args="--l2 --etype=0xdead" | |
| else | |
| extra_args="${extra_args} --l4 --ip-destination=${dst_ip}" | |
| receiver_extra_args="--l4" | |
| fi | |
| cpufreq_max ${ISOCHRON_CPU} | |
| isochron_recv_start "${h2}" "${receiver_uds}" 5000 "${receiver_extra_args}" | |
| isochron send \ | |
| --interface ${sender_if_name} \ | |
| --unix-domain-socket ${sender_uds} \ | |
| --priority ${priority} \ | |
| --base-time ${base_time} \ | |
| --cycle-time ${cycle_time} \ | |
| --num-frames ${num_pkts} \ | |
| --frame-size 64 \ | |
| --txtime \ | |
| --utc-tai-offset ${UTC_TAI_OFFSET} \ | |
| --cpu-mask $((1 << ${ISOCHRON_CPU})) \ | |
| --sched-fifo \ | |
| --sched-priority 98 \ | |
| --client 127.0.0.1 \ | |
| --sync-threshold 5000 \ | |
| --output-file ${isochron_dat} \ | |
| ${extra_args} \ | |
| --quiet | |
| isochron_recv_stop 5000 | |
| cpufreq_restore ${ISOCHRON_CPU} | |
| } | |
| isochron_report_num_received() | |
| { | |
| local isochron_dat=$1; shift | |
| # Count all received packets by looking at the non-zero RX timestamps | |
| isochron report \ | |
| --input-file "${isochron_dat}" \ | |
| --printf-format "%u\n" --printf-args "R" | \ | |
| grep -w -v '0' | wc -l | |
| } | |
| taprio_wait_for_admin() | |
| { | |
| local if_name="$1"; shift | |
| "$tc_testing_scripts_dir/taprio_wait_for_admin.sh" "$(which tc)" "$if_name" | |
| } |