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 8 WAIT_TIME=1 9 NUM_NETIFS=4 10 STABLE_MAC_ADDRS=yes 11 NETIF_CREATE=no 12 lib_dir=$(dirname $0)/../../../net/forwarding 13 source $lib_dir/tc_common.sh 14 source $lib_dir/lib.sh 15 source $lib_dir/tsn_lib.sh 16 17 UDS_ADDRESS_H1="/var/run/ptp4l_h1" 18 UDS_ADDRESS_SWP1="/var/run/ptp4l_swp1" 19 20 # Tunables 21 NUM_PKTS=1000 22 STREAM_VID=100 23 STREAM_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 26 CYCLE_TIME_NS=10000000 27 # Create two Gate Control List entries, one OPEN and one CLOSE, of equal 28 # durations 29 GATE_DURATION_NS=$((${CYCLE_TIME_NS} / 2)) 30 # Give 2/3 of the cycle time to user space and 1/3 to the kernel 31 FUDGE_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 35 SHIFT_TIME_NS=$((${GATE_DURATION_NS} / 2)) 36 37 h1=${NETIFS[p1]} 38 swp1=${NETIFS[p2]} 39 swp2=${NETIFS[p3]} 40 h2=${NETIFS[p4]} 41 42 H1_IPV4="192.0.2.1" 43 H2_IPV4="192.0.2.2" 44 H1_IPV6="2001:db8:1::1" 45 H2_IPV6="2001:db8:1::2" 46 47 # Chain number exported by the ocelot driver for 48 # Per-Stream Filtering and Policing filters 49 PSFP() 50 { 51 echo 30000 52 } 53 54 psfp_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 64 psfp_chain_destroy() 65 { 66 local if_name=$1 67 68 tc qdisc del dev $if_name clsact 69 } 70 71 psfp_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 90 h1_create() 91 { 92 simple_if_init $h1 $H1_IPV4/24 $H1_IPV6/64 93 } 94 95 h1_destroy() 96 { 97 simple_if_fini $h1 $H1_IPV4/24 $H1_IPV6/64 98 } 99 100 h2_create() 101 { 102 simple_if_init $h2 $H2_IPV4/24 $H2_IPV6/64 103 } 104 105 h2_destroy() 106 { 107 simple_if_fini $h2 $H2_IPV4/24 $H2_IPV6/64 108 } 109 110 switch_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 139 switch_destroy() 140 { 141 psfp_chain_destroy ${swp1} 142 ip link del br0 143 } 144 145 txtime_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 164 txtime_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 172 setup_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 ${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 196 cleanup() 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 214 debug_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 238 debug_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 251 run_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 297 test_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 306 test_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 315 trap cleanup EXIT 316 317 ALL_TESTS=" 318 test_gate_in_band 319 test_gate_out_of_band 320 " 321 322 setup_prepare 323 setup_wait 324 325 tests_run 326 327 exit $EXIT_STATUS
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.