#!/bin/bash # SPDX-License-Identifier: GPL-2.0 # Copyright 2021-2022 NXP 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 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 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 [ "${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} }