psfp.sh (7083B)
1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# Copyright 2021-2022 NXP 4 5# Note: On LS1028A, in lack of enough user ports, this setup requires patching 6# the device tree to use the second CPU port as a user port 7 8WAIT_TIME=1 9NUM_NETIFS=4 10STABLE_MAC_ADDRS=yes 11NETIF_CREATE=no 12lib_dir=$(dirname $0)/../../../net/forwarding 13source $lib_dir/tc_common.sh 14source $lib_dir/lib.sh 15source $lib_dir/tsn_lib.sh 16 17UDS_ADDRESS_H1="/var/run/ptp4l_h1" 18UDS_ADDRESS_SWP1="/var/run/ptp4l_swp1" 19 20# Tunables 21NUM_PKTS=1000 22STREAM_VID=100 23STREAM_PRIO=6 24# Use a conservative cycle of 10 ms to allow the test to still pass when the 25# kernel has some extra overhead like lockdep etc 26CYCLE_TIME_NS=10000000 27# Create two Gate Control List entries, one OPEN and one CLOSE, of equal 28# durations 29GATE_DURATION_NS=$((${CYCLE_TIME_NS} / 2)) 30# Give 2/3 of the cycle time to user space and 1/3 to the kernel 31FUDGE_FACTOR=$((${CYCLE_TIME_NS} / 3)) 32# Shift the isochron base time by half the gate time, so that packets are 33# always received by swp1 close to the middle of the time slot, to minimize 34# inaccuracies due to network sync 35SHIFT_TIME_NS=$((${GATE_DURATION_NS} / 2)) 36 37h1=${NETIFS[p1]} 38swp1=${NETIFS[p2]} 39swp2=${NETIFS[p3]} 40h2=${NETIFS[p4]} 41 42H1_IPV4="192.0.2.1" 43H2_IPV4="192.0.2.2" 44H1_IPV6="2001:db8:1::1" 45H2_IPV6="2001:db8:1::2" 46 47# Chain number exported by the ocelot driver for 48# Per-Stream Filtering and Policing filters 49PSFP() 50{ 51 echo 30000 52} 53 54psfp_chain_create() 55{ 56 local if_name=$1 57 58 tc qdisc add dev $if_name clsact 59 60 tc filter add dev $if_name ingress chain 0 pref 49152 flower \ 61 skip_sw action goto chain $(PSFP) 62} 63 64psfp_chain_destroy() 65{ 66 local if_name=$1 67 68 tc qdisc del dev $if_name clsact 69} 70 71psfp_filter_check() 72{ 73 local expected=$1 74 local packets="" 75 local drops="" 76 local stats="" 77 78 stats=$(tc -j -s filter show dev ${swp1} ingress chain $(PSFP) pref 1) 79 packets=$(echo ${stats} | jq ".[1].options.actions[].stats.packets") 80 drops=$(echo ${stats} | jq ".[1].options.actions[].stats.drops") 81 82 if ! [ "${packets}" = "${expected}" ]; then 83 printf "Expected filter to match on %d packets but matched on %d instead\n" \ 84 "${expected}" "${packets}" 85 fi 86 87 echo "Hardware filter reports ${drops} drops" 88} 89 90h1_create() 91{ 92 simple_if_init $h1 $H1_IPV4/24 $H1_IPV6/64 93} 94 95h1_destroy() 96{ 97 simple_if_fini $h1 $H1_IPV4/24 $H1_IPV6/64 98} 99 100h2_create() 101{ 102 simple_if_init $h2 $H2_IPV4/24 $H2_IPV6/64 103} 104 105h2_destroy() 106{ 107 simple_if_fini $h2 $H2_IPV4/24 $H2_IPV6/64 108} 109 110switch_create() 111{ 112 local h2_mac_addr=$(mac_get $h2) 113 114 ip link set ${swp1} up 115 ip link set ${swp2} up 116 117 ip link add br0 type bridge vlan_filtering 1 118 ip link set ${swp1} master br0 119 ip link set ${swp2} master br0 120 ip link set br0 up 121 122 bridge vlan add dev ${swp2} vid ${STREAM_VID} 123 bridge vlan add dev ${swp1} vid ${STREAM_VID} 124 # PSFP on Ocelot requires the filter to also be added to the bridge 125 # FDB, and not be removed 126 bridge fdb add dev ${swp2} \ 127 ${h2_mac_addr} vlan ${STREAM_VID} static master 128 129 psfp_chain_create ${swp1} 130 131 tc filter add dev ${swp1} ingress chain $(PSFP) pref 1 \ 132 protocol 802.1Q flower skip_sw \ 133 dst_mac ${h2_mac_addr} vlan_id ${STREAM_VID} \ 134 action gate base-time 0.000000000 \ 135 sched-entry OPEN ${GATE_DURATION_NS} -1 -1 \ 136 sched-entry CLOSE ${GATE_DURATION_NS} -1 -1 137} 138 139switch_destroy() 140{ 141 psfp_chain_destroy ${swp1} 142 ip link del br0 143} 144 145txtime_setup() 146{ 147 local if_name=$1 148 149 tc qdisc add dev ${if_name} clsact 150 # Classify PTP on TC 7 and isochron on TC 6 151 tc filter add dev ${if_name} egress protocol 0x88f7 \ 152 flower action skbedit priority 7 153 tc filter add dev ${if_name} egress protocol 802.1Q \ 154 flower vlan_ethtype 0xdead action skbedit priority 6 155 tc qdisc add dev ${if_name} handle 100: parent root mqprio num_tc 8 \ 156 queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 \ 157 map 0 1 2 3 4 5 6 7 \ 158 hw 1 159 # Set up TC 6 for SO_TXTIME. tc-mqprio queues count from 1. 160 tc qdisc replace dev ${if_name} parent 100:$((${STREAM_PRIO} + 1)) etf \ 161 clockid CLOCK_TAI offload delta ${FUDGE_FACTOR} 162} 163 164txtime_cleanup() 165{ 166 local if_name=$1 167 168 tc qdisc del dev ${if_name} root 169 tc qdisc del dev ${if_name} clsact 170} 171 172setup_prepare() 173{ 174 vrf_prepare 175 176 h1_create 177 h2_create 178 switch_create 179 180 txtime_setup ${h1} 181 182 # Set up swp1 as a master PHC for h1, synchronized to the local 183 # CLOCK_REALTIME. 184 phc2sys_start ${swp1} ${UDS_ADDRESS_SWP1} 185 186 # Assumption true for LS1028A: h1 and h2 use the same PHC. So by 187 # synchronizing h1 to swp1 via PTP, h2 is also implicitly synchronized 188 # to swp1 (and both to CLOCK_REALTIME). 189 ptp4l_start ${h1} true ${UDS_ADDRESS_H1} 190 ptp4l_start ${swp1} false ${UDS_ADDRESS_SWP1} 191 192 # Make sure there are no filter matches at the beginning of the test 193 psfp_filter_check 0 194} 195 196cleanup() 197{ 198 pre_cleanup 199 200 ptp4l_stop ${swp1} 201 ptp4l_stop ${h1} 202 phc2sys_stop 203 isochron_recv_stop 204 205 txtime_cleanup ${h1} 206 207 h2_destroy 208 h1_destroy 209 switch_destroy 210 211 vrf_cleanup 212} 213 214debug_incorrectly_dropped_packets() 215{ 216 local isochron_dat=$1 217 local dropped_seqids 218 local seqid 219 220 echo "Packets incorrectly dropped:" 221 222 dropped_seqids=$(isochron report \ 223 --input-file "${isochron_dat}" \ 224 --printf-format "%u RX hw %T\n" \ 225 --printf-args "qR" | \ 226 grep 'RX hw 0.000000000' | \ 227 awk '{print $1}') 228 229 for seqid in ${dropped_seqids}; do 230 isochron report \ 231 --input-file "${isochron_dat}" \ 232 --start ${seqid} --stop ${seqid} \ 233 --printf-format "seqid %u scheduled for %T, HW TX timestamp %T\n" \ 234 --printf-args "qST" 235 done 236} 237 238debug_incorrectly_received_packets() 239{ 240 local isochron_dat=$1 241 242 echo "Packets incorrectly received:" 243 244 isochron report \ 245 --input-file "${isochron_dat}" \ 246 --printf-format "seqid %u scheduled for %T, HW TX timestamp %T, HW RX timestamp %T\n" \ 247 --printf-args "qSTR" | 248 grep -v 'HW RX timestamp 0.000000000' 249} 250 251run_test() 252{ 253 local base_time=$1 254 local expected=$2 255 local test_name=$3 256 local debug=$4 257 local isochron_dat="$(mktemp)" 258 local extra_args="" 259 local received 260 261 isochron_do \ 262 "${h1}" \ 263 "${h2}" \ 264 "${UDS_ADDRESS_H1}" \ 265 "" \ 266 "${base_time}" \ 267 "${CYCLE_TIME_NS}" \ 268 "${SHIFT_TIME_NS}" \ 269 "${NUM_PKTS}" \ 270 "${STREAM_VID}" \ 271 "${STREAM_PRIO}" \ 272 "" \ 273 "${isochron_dat}" 274 275 # Count all received packets by looking at the non-zero RX timestamps 276 received=$(isochron report \ 277 --input-file "${isochron_dat}" \ 278 --printf-format "%u\n" --printf-args "R" | \ 279 grep -w -v '0' | wc -l) 280 281 if [ "${received}" = "${expected}" ]; then 282 RET=0 283 else 284 RET=1 285 echo "Expected isochron to receive ${expected} packets but received ${received}" 286 fi 287 288 log_test "${test_name}" 289 290 if [ "$RET" = "1" ]; then 291 ${debug} "${isochron_dat}" 292 fi 293 294 rm ${isochron_dat} 2> /dev/null 295} 296 297test_gate_in_band() 298{ 299 # Send packets in-band with the OPEN gate entry 300 run_test 0.000000000 ${NUM_PKTS} "In band" \ 301 debug_incorrectly_dropped_packets 302 303 psfp_filter_check ${NUM_PKTS} 304} 305 306test_gate_out_of_band() 307{ 308 # Send packets in-band with the CLOSE gate entry 309 run_test 0.005000000 0 "Out of band" \ 310 debug_incorrectly_received_packets 311 312 psfp_filter_check $((2 * ${NUM_PKTS})) 313} 314 315trap cleanup EXIT 316 317ALL_TESTS=" 318 test_gate_in_band 319 test_gate_out_of_band 320" 321 322setup_prepare 323setup_wait 324 325tests_run 326 327exit $EXIT_STATUS