psfp.sh 6.8 KB

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