1954f46d2SVladimir Oltean#!/bin/bash 2954f46d2SVladimir Oltean# SPDX-License-Identifier: GPL-2.0 3954f46d2SVladimir Oltean# Copyright 2021-2022 NXP 4954f46d2SVladimir Oltean 5954f46d2SVladimir Oltean# Note: On LS1028A, in lack of enough user ports, this setup requires patching 6954f46d2SVladimir Oltean# the device tree to use the second CPU port as a user port 7954f46d2SVladimir Oltean 8954f46d2SVladimir OlteanWAIT_TIME=1 9954f46d2SVladimir OlteanNUM_NETIFS=4 10954f46d2SVladimir OlteanSTABLE_MAC_ADDRS=yes 11954f46d2SVladimir OlteanNETIF_CREATE=no 12954f46d2SVladimir Olteanlib_dir=$(dirname $0)/../../../net/forwarding 13954f46d2SVladimir Olteansource $lib_dir/tc_common.sh 14954f46d2SVladimir Olteansource $lib_dir/lib.sh 15954f46d2SVladimir Olteansource $lib_dir/tsn_lib.sh 16954f46d2SVladimir Oltean 17954f46d2SVladimir OlteanUDS_ADDRESS_H1="/var/run/ptp4l_h1" 18954f46d2SVladimir OlteanUDS_ADDRESS_SWP1="/var/run/ptp4l_swp1" 19954f46d2SVladimir Oltean 20954f46d2SVladimir Oltean# Tunables 21954f46d2SVladimir OlteanNUM_PKTS=1000 22954f46d2SVladimir OlteanSTREAM_VID=100 23954f46d2SVladimir OlteanSTREAM_PRIO=6 24954f46d2SVladimir Oltean# Use a conservative cycle of 10 ms to allow the test to still pass when the 25954f46d2SVladimir Oltean# kernel has some extra overhead like lockdep etc 26954f46d2SVladimir OlteanCYCLE_TIME_NS=10000000 27954f46d2SVladimir Oltean# Create two Gate Control List entries, one OPEN and one CLOSE, of equal 28954f46d2SVladimir Oltean# durations 29954f46d2SVladimir OlteanGATE_DURATION_NS=$((${CYCLE_TIME_NS} / 2)) 30954f46d2SVladimir Oltean# Give 2/3 of the cycle time to user space and 1/3 to the kernel 31954f46d2SVladimir OlteanFUDGE_FACTOR=$((${CYCLE_TIME_NS} / 3)) 32954f46d2SVladimir Oltean# Shift the isochron base time by half the gate time, so that packets are 33954f46d2SVladimir Oltean# always received by swp1 close to the middle of the time slot, to minimize 34954f46d2SVladimir Oltean# inaccuracies due to network sync 35954f46d2SVladimir OlteanSHIFT_TIME_NS=$((${GATE_DURATION_NS} / 2)) 36954f46d2SVladimir Oltean 37954f46d2SVladimir Olteanh1=${NETIFS[p1]} 38954f46d2SVladimir Olteanswp1=${NETIFS[p2]} 39954f46d2SVladimir Olteanswp2=${NETIFS[p3]} 40954f46d2SVladimir Olteanh2=${NETIFS[p4]} 41954f46d2SVladimir Oltean 42954f46d2SVladimir OlteanH1_IPV4="192.0.2.1" 43954f46d2SVladimir OlteanH2_IPV4="192.0.2.2" 44954f46d2SVladimir OlteanH1_IPV6="2001:db8:1::1" 45954f46d2SVladimir OlteanH2_IPV6="2001:db8:1::2" 46954f46d2SVladimir Oltean 47954f46d2SVladimir Oltean# Chain number exported by the ocelot driver for 48954f46d2SVladimir Oltean# Per-Stream Filtering and Policing filters 49954f46d2SVladimir OlteanPSFP() 50954f46d2SVladimir Oltean{ 51954f46d2SVladimir Oltean echo 30000 52954f46d2SVladimir Oltean} 53954f46d2SVladimir Oltean 54954f46d2SVladimir Olteanpsfp_chain_create() 55954f46d2SVladimir Oltean{ 56954f46d2SVladimir Oltean local if_name=$1 57954f46d2SVladimir Oltean 58954f46d2SVladimir Oltean tc qdisc add dev $if_name clsact 59954f46d2SVladimir Oltean 60954f46d2SVladimir Oltean tc filter add dev $if_name ingress chain 0 pref 49152 flower \ 61954f46d2SVladimir Oltean skip_sw action goto chain $(PSFP) 62954f46d2SVladimir Oltean} 63954f46d2SVladimir Oltean 64954f46d2SVladimir Olteanpsfp_chain_destroy() 65954f46d2SVladimir Oltean{ 66954f46d2SVladimir Oltean local if_name=$1 67954f46d2SVladimir Oltean 68954f46d2SVladimir Oltean tc qdisc del dev $if_name clsact 69954f46d2SVladimir Oltean} 70954f46d2SVladimir Oltean 71954f46d2SVladimir Olteanpsfp_filter_check() 72954f46d2SVladimir Oltean{ 73954f46d2SVladimir Oltean local expected=$1 74954f46d2SVladimir Oltean local packets="" 75954f46d2SVladimir Oltean local drops="" 76954f46d2SVladimir Oltean local stats="" 77954f46d2SVladimir Oltean 78954f46d2SVladimir Oltean stats=$(tc -j -s filter show dev ${swp1} ingress chain $(PSFP) pref 1) 79954f46d2SVladimir Oltean packets=$(echo ${stats} | jq ".[1].options.actions[].stats.packets") 80954f46d2SVladimir Oltean drops=$(echo ${stats} | jq ".[1].options.actions[].stats.drops") 81954f46d2SVladimir Oltean 82954f46d2SVladimir Oltean if ! [ "${packets}" = "${expected}" ]; then 83954f46d2SVladimir Oltean printf "Expected filter to match on %d packets but matched on %d instead\n" \ 84954f46d2SVladimir Oltean "${expected}" "${packets}" 85954f46d2SVladimir Oltean fi 86954f46d2SVladimir Oltean 87954f46d2SVladimir Oltean echo "Hardware filter reports ${drops} drops" 88954f46d2SVladimir Oltean} 89954f46d2SVladimir Oltean 90954f46d2SVladimir Olteanh1_create() 91954f46d2SVladimir Oltean{ 92954f46d2SVladimir Oltean simple_if_init $h1 $H1_IPV4/24 $H1_IPV6/64 93954f46d2SVladimir Oltean} 94954f46d2SVladimir Oltean 95954f46d2SVladimir Olteanh1_destroy() 96954f46d2SVladimir Oltean{ 97954f46d2SVladimir Oltean simple_if_fini $h1 $H1_IPV4/24 $H1_IPV6/64 98954f46d2SVladimir Oltean} 99954f46d2SVladimir Oltean 100954f46d2SVladimir Olteanh2_create() 101954f46d2SVladimir Oltean{ 102954f46d2SVladimir Oltean simple_if_init $h2 $H2_IPV4/24 $H2_IPV6/64 103954f46d2SVladimir Oltean} 104954f46d2SVladimir Oltean 105954f46d2SVladimir Olteanh2_destroy() 106954f46d2SVladimir Oltean{ 107954f46d2SVladimir Oltean simple_if_fini $h2 $H2_IPV4/24 $H2_IPV6/64 108954f46d2SVladimir Oltean} 109954f46d2SVladimir Oltean 110954f46d2SVladimir Olteanswitch_create() 111954f46d2SVladimir Oltean{ 112954f46d2SVladimir Oltean local h2_mac_addr=$(mac_get $h2) 113954f46d2SVladimir Oltean 114954f46d2SVladimir Oltean ip link set ${swp1} up 115954f46d2SVladimir Oltean ip link set ${swp2} up 116954f46d2SVladimir Oltean 117954f46d2SVladimir Oltean ip link add br0 type bridge vlan_filtering 1 118954f46d2SVladimir Oltean ip link set ${swp1} master br0 119954f46d2SVladimir Oltean ip link set ${swp2} master br0 120954f46d2SVladimir Oltean ip link set br0 up 121954f46d2SVladimir Oltean 122954f46d2SVladimir Oltean bridge vlan add dev ${swp2} vid ${STREAM_VID} 123954f46d2SVladimir Oltean bridge vlan add dev ${swp1} vid ${STREAM_VID} 124954f46d2SVladimir Oltean # PSFP on Ocelot requires the filter to also be added to the bridge 125954f46d2SVladimir Oltean # FDB, and not be removed 126954f46d2SVladimir Oltean bridge fdb add dev ${swp2} \ 127954f46d2SVladimir Oltean ${h2_mac_addr} vlan ${STREAM_VID} static master 128954f46d2SVladimir Oltean 129954f46d2SVladimir Oltean psfp_chain_create ${swp1} 130954f46d2SVladimir Oltean 131954f46d2SVladimir Oltean tc filter add dev ${swp1} ingress chain $(PSFP) pref 1 \ 132954f46d2SVladimir Oltean protocol 802.1Q flower skip_sw \ 133954f46d2SVladimir Oltean dst_mac ${h2_mac_addr} vlan_id ${STREAM_VID} \ 134954f46d2SVladimir Oltean action gate base-time 0.000000000 \ 135954f46d2SVladimir Oltean sched-entry OPEN ${GATE_DURATION_NS} -1 -1 \ 136954f46d2SVladimir Oltean sched-entry CLOSE ${GATE_DURATION_NS} -1 -1 137954f46d2SVladimir Oltean} 138954f46d2SVladimir Oltean 139954f46d2SVladimir Olteanswitch_destroy() 140954f46d2SVladimir Oltean{ 141954f46d2SVladimir Oltean psfp_chain_destroy ${swp1} 142954f46d2SVladimir Oltean ip link del br0 143954f46d2SVladimir Oltean} 144954f46d2SVladimir Oltean 145954f46d2SVladimir Olteantxtime_setup() 146954f46d2SVladimir Oltean{ 147954f46d2SVladimir Oltean local if_name=$1 148954f46d2SVladimir Oltean 149954f46d2SVladimir Oltean tc qdisc add dev ${if_name} clsact 150954f46d2SVladimir Oltean # Classify PTP on TC 7 and isochron on TC 6 151954f46d2SVladimir Oltean tc filter add dev ${if_name} egress protocol 0x88f7 \ 152954f46d2SVladimir Oltean flower action skbedit priority 7 153954f46d2SVladimir Oltean tc filter add dev ${if_name} egress protocol 802.1Q \ 154954f46d2SVladimir Oltean flower vlan_ethtype 0xdead action skbedit priority 6 155954f46d2SVladimir Oltean tc qdisc add dev ${if_name} handle 100: parent root mqprio num_tc 8 \ 156954f46d2SVladimir Oltean queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 \ 157954f46d2SVladimir Oltean map 0 1 2 3 4 5 6 7 \ 158954f46d2SVladimir Oltean hw 1 159954f46d2SVladimir Oltean # Set up TC 6 for SO_TXTIME. tc-mqprio queues count from 1. 160954f46d2SVladimir Oltean tc qdisc replace dev ${if_name} parent 100:$((${STREAM_PRIO} + 1)) etf \ 161954f46d2SVladimir Oltean clockid CLOCK_TAI offload delta ${FUDGE_FACTOR} 162954f46d2SVladimir Oltean} 163954f46d2SVladimir Oltean 164954f46d2SVladimir Olteantxtime_cleanup() 165954f46d2SVladimir Oltean{ 166954f46d2SVladimir Oltean local if_name=$1 167954f46d2SVladimir Oltean 168954f46d2SVladimir Oltean tc qdisc del dev ${if_name} root 169954f46d2SVladimir Oltean tc qdisc del dev ${if_name} clsact 170954f46d2SVladimir Oltean} 171954f46d2SVladimir Oltean 172954f46d2SVladimir Olteansetup_prepare() 173954f46d2SVladimir Oltean{ 174954f46d2SVladimir Oltean vrf_prepare 175954f46d2SVladimir Oltean 176954f46d2SVladimir Oltean h1_create 177954f46d2SVladimir Oltean h2_create 178954f46d2SVladimir Oltean switch_create 179954f46d2SVladimir Oltean 180954f46d2SVladimir Oltean txtime_setup ${h1} 181954f46d2SVladimir Oltean 182954f46d2SVladimir Oltean # Set up swp1 as a master PHC for h1, synchronized to the local 183954f46d2SVladimir Oltean # CLOCK_REALTIME. 184*162d52dfSVladimir Oltean phc2sys_start ${UDS_ADDRESS_SWP1} 185954f46d2SVladimir Oltean 186954f46d2SVladimir Oltean # Assumption true for LS1028A: h1 and h2 use the same PHC. So by 187954f46d2SVladimir Oltean # synchronizing h1 to swp1 via PTP, h2 is also implicitly synchronized 188954f46d2SVladimir Oltean # to swp1 (and both to CLOCK_REALTIME). 189954f46d2SVladimir Oltean ptp4l_start ${h1} true ${UDS_ADDRESS_H1} 190954f46d2SVladimir Oltean ptp4l_start ${swp1} false ${UDS_ADDRESS_SWP1} 191954f46d2SVladimir Oltean 192954f46d2SVladimir Oltean # Make sure there are no filter matches at the beginning of the test 193954f46d2SVladimir Oltean psfp_filter_check 0 194954f46d2SVladimir Oltean} 195954f46d2SVladimir Oltean 196954f46d2SVladimir Olteancleanup() 197954f46d2SVladimir Oltean{ 198954f46d2SVladimir Oltean pre_cleanup 199954f46d2SVladimir Oltean 200954f46d2SVladimir Oltean ptp4l_stop ${swp1} 201954f46d2SVladimir Oltean ptp4l_stop ${h1} 202954f46d2SVladimir Oltean phc2sys_stop 203954f46d2SVladimir Oltean isochron_recv_stop 204954f46d2SVladimir Oltean 205954f46d2SVladimir Oltean txtime_cleanup ${h1} 206954f46d2SVladimir Oltean 207954f46d2SVladimir Oltean h2_destroy 208954f46d2SVladimir Oltean h1_destroy 209954f46d2SVladimir Oltean switch_destroy 210954f46d2SVladimir Oltean 211954f46d2SVladimir Oltean vrf_cleanup 212954f46d2SVladimir Oltean} 213954f46d2SVladimir Oltean 214954f46d2SVladimir Olteandebug_incorrectly_dropped_packets() 215954f46d2SVladimir Oltean{ 216954f46d2SVladimir Oltean local isochron_dat=$1 217954f46d2SVladimir Oltean local dropped_seqids 218954f46d2SVladimir Oltean local seqid 219954f46d2SVladimir Oltean 220954f46d2SVladimir Oltean echo "Packets incorrectly dropped:" 221954f46d2SVladimir Oltean 222954f46d2SVladimir Oltean dropped_seqids=$(isochron report \ 223954f46d2SVladimir Oltean --input-file "${isochron_dat}" \ 224954f46d2SVladimir Oltean --printf-format "%u RX hw %T\n" \ 225954f46d2SVladimir Oltean --printf-args "qR" | \ 226954f46d2SVladimir Oltean grep 'RX hw 0.000000000' | \ 227954f46d2SVladimir Oltean awk '{print $1}') 228954f46d2SVladimir Oltean 229954f46d2SVladimir Oltean for seqid in ${dropped_seqids}; do 230954f46d2SVladimir Oltean isochron report \ 231954f46d2SVladimir Oltean --input-file "${isochron_dat}" \ 232954f46d2SVladimir Oltean --start ${seqid} --stop ${seqid} \ 233954f46d2SVladimir Oltean --printf-format "seqid %u scheduled for %T, HW TX timestamp %T\n" \ 234954f46d2SVladimir Oltean --printf-args "qST" 235954f46d2SVladimir Oltean done 236954f46d2SVladimir Oltean} 237954f46d2SVladimir Oltean 238954f46d2SVladimir Olteandebug_incorrectly_received_packets() 239954f46d2SVladimir Oltean{ 240954f46d2SVladimir Oltean local isochron_dat=$1 241954f46d2SVladimir Oltean 242954f46d2SVladimir Oltean echo "Packets incorrectly received:" 243954f46d2SVladimir Oltean 244954f46d2SVladimir Oltean isochron report \ 245954f46d2SVladimir Oltean --input-file "${isochron_dat}" \ 246954f46d2SVladimir Oltean --printf-format "seqid %u scheduled for %T, HW TX timestamp %T, HW RX timestamp %T\n" \ 247954f46d2SVladimir Oltean --printf-args "qSTR" | 248954f46d2SVladimir Oltean grep -v 'HW RX timestamp 0.000000000' 249954f46d2SVladimir Oltean} 250954f46d2SVladimir Oltean 251954f46d2SVladimir Olteanrun_test() 252954f46d2SVladimir Oltean{ 253954f46d2SVladimir Oltean local base_time=$1 254954f46d2SVladimir Oltean local expected=$2 255954f46d2SVladimir Oltean local test_name=$3 256954f46d2SVladimir Oltean local debug=$4 257954f46d2SVladimir Oltean local isochron_dat="$(mktemp)" 258954f46d2SVladimir Oltean local extra_args="" 259954f46d2SVladimir Oltean local received 260954f46d2SVladimir Oltean 261954f46d2SVladimir Oltean isochron_do \ 262954f46d2SVladimir Oltean "${h1}" \ 263954f46d2SVladimir Oltean "${h2}" \ 264954f46d2SVladimir Oltean "${UDS_ADDRESS_H1}" \ 265954f46d2SVladimir Oltean "" \ 266954f46d2SVladimir Oltean "${base_time}" \ 267954f46d2SVladimir Oltean "${CYCLE_TIME_NS}" \ 268954f46d2SVladimir Oltean "${SHIFT_TIME_NS}" \ 269954f46d2SVladimir Oltean "${NUM_PKTS}" \ 270954f46d2SVladimir Oltean "${STREAM_VID}" \ 271954f46d2SVladimir Oltean "${STREAM_PRIO}" \ 272954f46d2SVladimir Oltean "" \ 273954f46d2SVladimir Oltean "${isochron_dat}" 274954f46d2SVladimir Oltean 275954f46d2SVladimir Oltean # Count all received packets by looking at the non-zero RX timestamps 276954f46d2SVladimir Oltean received=$(isochron report \ 277954f46d2SVladimir Oltean --input-file "${isochron_dat}" \ 278954f46d2SVladimir Oltean --printf-format "%u\n" --printf-args "R" | \ 279954f46d2SVladimir Oltean grep -w -v '0' | wc -l) 280954f46d2SVladimir Oltean 281954f46d2SVladimir Oltean if [ "${received}" = "${expected}" ]; then 282954f46d2SVladimir Oltean RET=0 283954f46d2SVladimir Oltean else 284954f46d2SVladimir Oltean RET=1 285954f46d2SVladimir Oltean echo "Expected isochron to receive ${expected} packets but received ${received}" 286954f46d2SVladimir Oltean fi 287954f46d2SVladimir Oltean 288954f46d2SVladimir Oltean log_test "${test_name}" 289954f46d2SVladimir Oltean 290954f46d2SVladimir Oltean if [ "$RET" = "1" ]; then 291954f46d2SVladimir Oltean ${debug} "${isochron_dat}" 292954f46d2SVladimir Oltean fi 293954f46d2SVladimir Oltean 294954f46d2SVladimir Oltean rm ${isochron_dat} 2> /dev/null 295954f46d2SVladimir Oltean} 296954f46d2SVladimir Oltean 297954f46d2SVladimir Olteantest_gate_in_band() 298954f46d2SVladimir Oltean{ 299954f46d2SVladimir Oltean # Send packets in-band with the OPEN gate entry 300954f46d2SVladimir Oltean run_test 0.000000000 ${NUM_PKTS} "In band" \ 301954f46d2SVladimir Oltean debug_incorrectly_dropped_packets 302954f46d2SVladimir Oltean 303954f46d2SVladimir Oltean psfp_filter_check ${NUM_PKTS} 304954f46d2SVladimir Oltean} 305954f46d2SVladimir Oltean 306954f46d2SVladimir Olteantest_gate_out_of_band() 307954f46d2SVladimir Oltean{ 308954f46d2SVladimir Oltean # Send packets in-band with the CLOSE gate entry 309954f46d2SVladimir Oltean run_test 0.005000000 0 "Out of band" \ 310954f46d2SVladimir Oltean debug_incorrectly_received_packets 311954f46d2SVladimir Oltean 312954f46d2SVladimir Oltean psfp_filter_check $((2 * ${NUM_PKTS})) 313954f46d2SVladimir Oltean} 314954f46d2SVladimir Oltean 315954f46d2SVladimir Olteantrap cleanup EXIT 316954f46d2SVladimir Oltean 317954f46d2SVladimir OlteanALL_TESTS=" 318954f46d2SVladimir Oltean test_gate_in_band 319954f46d2SVladimir Oltean test_gate_out_of_band 320954f46d2SVladimir Oltean" 321954f46d2SVladimir Oltean 322954f46d2SVladimir Olteansetup_prepare 323954f46d2SVladimir Olteansetup_wait 324954f46d2SVladimir Oltean 325954f46d2SVladimir Olteantests_run 326954f46d2SVladimir Oltean 327954f46d2SVladimir Olteanexit $EXIT_STATUS 328