1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4# This test is for checking the A-TCAM and C-TCAM operation in Spectrum-2. 5# It tries to exercise as many code paths in the eRP state machine as 6# possible. 7 8lib_dir=$(dirname $0)/../../../../net/forwarding 9 10ALL_TESTS="single_mask_test identical_filters_test two_masks_test \ 11 multiple_masks_test ctcam_edge_cases_test delta_simple_test \ 12 bloom_simple_test bloom_complex_test bloom_delta_test" 13NUM_NETIFS=2 14source $lib_dir/tc_common.sh 15source $lib_dir/lib.sh 16 17tcflags="skip_hw" 18 19h1_create() 20{ 21 simple_if_init $h1 192.0.2.1/24 198.51.100.1/24 22} 23 24h1_destroy() 25{ 26 simple_if_fini $h1 192.0.2.1/24 198.51.100.1/24 27} 28 29h2_create() 30{ 31 simple_if_init $h2 192.0.2.2/24 198.51.100.2/24 32 tc qdisc add dev $h2 clsact 33} 34 35h2_destroy() 36{ 37 tc qdisc del dev $h2 clsact 38 simple_if_fini $h2 192.0.2.2/24 198.51.100.2/24 39} 40 41single_mask_test() 42{ 43 # When only a single mask is required, the device uses the master 44 # mask and not the eRP table. Verify that under this mode the right 45 # filter is matched 46 47 RET=0 48 49 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ 50 $tcflags dst_ip 192.0.2.2 action drop 51 52 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 53 -t ip -q 54 55 tc_check_packets "dev $h2 ingress" 101 1 56 check_err $? "Single filter - did not match" 57 58 tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ 59 $tcflags dst_ip 198.51.100.2 action drop 60 61 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 62 -t ip -q 63 64 tc_check_packets "dev $h2 ingress" 101 2 65 check_err $? "Two filters - did not match highest priority" 66 67 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 198.51.100.1 -B 198.51.100.2 \ 68 -t ip -q 69 70 tc_check_packets "dev $h2 ingress" 102 1 71 check_err $? "Two filters - did not match lowest priority" 72 73 tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower 74 75 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 198.51.100.1 -B 198.51.100.2 \ 76 -t ip -q 77 78 tc_check_packets "dev $h2 ingress" 102 2 79 check_err $? "Single filter - did not match after delete" 80 81 tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower 82 83 log_test "single mask test ($tcflags)" 84} 85 86identical_filters_test() 87{ 88 # When two filters that only differ in their priority are used, 89 # one needs to be inserted into the C-TCAM. This test verifies 90 # that filters are correctly spilled to C-TCAM and that the right 91 # filter is matched 92 93 RET=0 94 95 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ 96 $tcflags dst_ip 192.0.2.2 action drop 97 tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ 98 $tcflags dst_ip 192.0.2.2 action drop 99 100 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 101 -t ip -q 102 103 tc_check_packets "dev $h2 ingress" 101 1 104 check_err $? "Did not match A-TCAM filter" 105 106 tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower 107 108 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 109 -t ip -q 110 111 tc_check_packets "dev $h2 ingress" 102 1 112 check_err $? "Did not match C-TCAM filter after A-TCAM delete" 113 114 tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \ 115 $tcflags dst_ip 192.0.2.2 action drop 116 117 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 118 -t ip -q 119 120 tc_check_packets "dev $h2 ingress" 102 2 121 check_err $? "Did not match C-TCAM filter after A-TCAM add" 122 123 tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower 124 125 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 126 -t ip -q 127 128 tc_check_packets "dev $h2 ingress" 103 1 129 check_err $? "Did not match A-TCAM filter after C-TCAM delete" 130 131 tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower 132 133 log_test "identical filters test ($tcflags)" 134} 135 136two_masks_test() 137{ 138 # When more than one mask is required, the eRP table is used. This 139 # test verifies that the eRP table is correctly allocated and used 140 141 RET=0 142 143 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ 144 $tcflags dst_ip 192.0.2.2 action drop 145 tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \ 146 $tcflags dst_ip 192.0.0.0/8 action drop 147 148 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 149 -t ip -q 150 151 tc_check_packets "dev $h2 ingress" 101 1 152 check_err $? "Two filters - did not match highest priority" 153 154 tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower 155 156 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 157 -t ip -q 158 159 tc_check_packets "dev $h2 ingress" 103 1 160 check_err $? "Single filter - did not match" 161 162 tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ 163 $tcflags dst_ip 192.0.2.0/24 action drop 164 165 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 166 -t ip -q 167 168 tc_check_packets "dev $h2 ingress" 102 1 169 check_err $? "Two filters - did not match highest priority after add" 170 171 tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower 172 tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower 173 174 log_test "two masks test ($tcflags)" 175} 176 177multiple_masks_test() 178{ 179 # The number of masks in a region is limited. Once the maximum 180 # number of masks has been reached filters that require new 181 # masks are spilled to the C-TCAM. This test verifies that 182 # spillage is performed correctly and that the right filter is 183 # matched 184 185 local index 186 187 RET=0 188 189 NUM_MASKS=32 190 BASE_INDEX=100 191 192 for i in $(eval echo {1..$NUM_MASKS}); do 193 index=$((BASE_INDEX - i)) 194 195 tc filter add dev $h2 ingress protocol ip pref $index \ 196 handle $index \ 197 flower $tcflags dst_ip 192.0.2.2/${i} src_ip 192.0.2.1 \ 198 action drop 199 200 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 \ 201 -B 192.0.2.2 -t ip -q 202 203 tc_check_packets "dev $h2 ingress" $index 1 204 check_err $? "$i filters - did not match highest priority (add)" 205 done 206 207 for i in $(eval echo {$NUM_MASKS..1}); do 208 index=$((BASE_INDEX - i)) 209 210 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 \ 211 -B 192.0.2.2 -t ip -q 212 213 tc_check_packets "dev $h2 ingress" $index 2 214 check_err $? "$i filters - did not match highest priority (del)" 215 216 tc filter del dev $h2 ingress protocol ip pref $index \ 217 handle $index flower 218 done 219 220 log_test "multiple masks test ($tcflags)" 221} 222 223ctcam_two_atcam_masks_test() 224{ 225 RET=0 226 227 # First case: C-TCAM is disabled when there are two A-TCAM masks. 228 # We push a filter into the C-TCAM by using two identical filters 229 # as in identical_filters_test() 230 231 # Filter goes into A-TCAM 232 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ 233 $tcflags dst_ip 192.0.2.2 action drop 234 # Filter goes into C-TCAM 235 tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ 236 $tcflags dst_ip 192.0.2.2 action drop 237 # Filter goes into A-TCAM 238 tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \ 239 $tcflags dst_ip 192.0.0.0/16 action drop 240 241 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 242 -t ip -q 243 244 tc_check_packets "dev $h2 ingress" 101 1 245 check_err $? "Did not match A-TCAM filter" 246 247 # Delete both A-TCAM and C-TCAM filters and make sure the remaining 248 # A-TCAM filter still works 249 tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower 250 tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower 251 252 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 253 -t ip -q 254 255 tc_check_packets "dev $h2 ingress" 103 1 256 check_err $? "Did not match A-TCAM filter" 257 258 tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower 259 260 log_test "ctcam with two atcam masks test ($tcflags)" 261} 262 263ctcam_one_atcam_mask_test() 264{ 265 RET=0 266 267 # Second case: C-TCAM is disabled when there is one A-TCAM mask. 268 # The test is similar to identical_filters_test() 269 270 # Filter goes into A-TCAM 271 tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ 272 $tcflags dst_ip 192.0.2.2 action drop 273 # Filter goes into C-TCAM 274 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ 275 $tcflags dst_ip 192.0.2.2 action drop 276 277 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 278 -t ip -q 279 280 tc_check_packets "dev $h2 ingress" 101 1 281 check_err $? "Did not match C-TCAM filter" 282 283 tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower 284 285 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 286 -t ip -q 287 288 tc_check_packets "dev $h2 ingress" 102 1 289 check_err $? "Did not match A-TCAM filter" 290 291 tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower 292 293 log_test "ctcam with one atcam mask test ($tcflags)" 294} 295 296ctcam_no_atcam_masks_test() 297{ 298 RET=0 299 300 # Third case: C-TCAM is disabled when there are no A-TCAM masks 301 # This test exercises the code path that transitions the eRP table 302 # to its initial state after deleting the last C-TCAM mask 303 304 # Filter goes into A-TCAM 305 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ 306 $tcflags dst_ip 192.0.2.2 action drop 307 # Filter goes into C-TCAM 308 tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ 309 $tcflags dst_ip 192.0.2.2 action drop 310 311 tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower 312 tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower 313 314 log_test "ctcam with no atcam masks test ($tcflags)" 315} 316 317ctcam_edge_cases_test() 318{ 319 # When the C-TCAM is disabled after deleting the last C-TCAM 320 # mask, we want to make sure the eRP state machine is put in 321 # the correct state 322 323 ctcam_two_atcam_masks_test 324 ctcam_one_atcam_mask_test 325 ctcam_no_atcam_masks_test 326} 327 328tp_record() 329{ 330 local tracepoint=$1 331 local cmd=$2 332 333 perf record -q -e $tracepoint $cmd 334 return $? 335} 336 337tp_check_hits() 338{ 339 local tracepoint=$1 340 local count=$2 341 342 perf_output=`perf script -F trace:event,trace` 343 hits=`echo $perf_output | grep "$tracepoint:" | wc -l` 344 if [[ "$count" -ne "$hits" ]]; then 345 return 1 346 fi 347 return 0 348} 349 350delta_simple_test() 351{ 352 # The first filter will create eRP, the second filter will fit into 353 # the first eRP with delta. Remove the first rule then and check that 354 # the eRP stays (referenced by the second filter). 355 356 RET=0 357 358 if [[ "$tcflags" != "skip_sw" ]]; then 359 return 0; 360 fi 361 362 tp_record "objagg:*" "tc filter add dev $h2 ingress protocol ip \ 363 pref 1 handle 101 flower $tcflags dst_ip 192.0.0.0/24 \ 364 action drop" 365 tp_check_hits "objagg:objagg_obj_root_create" 1 366 check_err $? "eRP was not created" 367 368 tp_record "objagg:*" "tc filter add dev $h2 ingress protocol ip \ 369 pref 2 handle 102 flower $tcflags dst_ip 192.0.2.2 \ 370 action drop" 371 tp_check_hits "objagg:objagg_obj_root_create" 0 372 check_err $? "eRP was incorrectly created" 373 tp_check_hits "objagg:objagg_obj_parent_assign" 1 374 check_err $? "delta was not created" 375 376 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 377 -t ip -q 378 379 tc_check_packets "dev $h2 ingress" 101 1 380 check_fail $? "Matched a wrong filter" 381 382 tc_check_packets "dev $h2 ingress" 102 1 383 check_err $? "Did not match on correct filter" 384 385 tp_record "objagg:*" "tc filter del dev $h2 ingress protocol ip \ 386 pref 1 handle 101 flower" 387 tp_check_hits "objagg:objagg_obj_root_destroy" 0 388 check_err $? "eRP was incorrectly destroyed" 389 tp_check_hits "objagg:objagg_obj_parent_unassign" 0 390 check_err $? "delta was incorrectly destroyed" 391 392 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 393 -t ip -q 394 395 tc_check_packets "dev $h2 ingress" 102 2 396 check_err $? "Did not match on correct filter after the first was removed" 397 398 tp_record "objagg:*" "tc filter del dev $h2 ingress protocol ip \ 399 pref 2 handle 102 flower" 400 tp_check_hits "objagg:objagg_obj_parent_unassign" 1 401 check_err $? "delta was not destroyed" 402 tp_check_hits "objagg:objagg_obj_root_destroy" 1 403 check_err $? "eRP was not destroyed" 404 405 log_test "delta simple test ($tcflags)" 406} 407 408bloom_simple_test() 409{ 410 # Bloom filter requires that the eRP table is used. This test 411 # verifies that Bloom filter is not harming correctness of ACLs. 412 # First, make sure that eRP table is used and then set rule patterns 413 # which are distant enough and will result skipping a lookup after 414 # consulting the Bloom filter. Although some eRP lookups are skipped, 415 # the correct filter should be hit. 416 417 RET=0 418 419 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ 420 $tcflags dst_ip 192.0.2.2 action drop 421 tc filter add dev $h2 ingress protocol ip pref 5 handle 104 flower \ 422 $tcflags dst_ip 198.51.100.2 action drop 423 tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \ 424 $tcflags dst_ip 192.0.0.0/8 action drop 425 426 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 427 -t ip -q 428 429 tc_check_packets "dev $h2 ingress" 101 1 430 check_err $? "Two filters - did not match highest priority" 431 432 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 198.51.100.1 -B 198.51.100.2 \ 433 -t ip -q 434 435 tc_check_packets "dev $h2 ingress" 104 1 436 check_err $? "Single filter - did not match" 437 438 tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower 439 440 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 441 -t ip -q 442 443 tc_check_packets "dev $h2 ingress" 103 1 444 check_err $? "Low prio filter - did not match" 445 446 tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ 447 $tcflags dst_ip 198.0.0.0/8 action drop 448 449 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 198.51.100.1 -B 198.51.100.2 \ 450 -t ip -q 451 452 tc_check_packets "dev $h2 ingress" 102 1 453 check_err $? "Two filters - did not match highest priority after add" 454 455 tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower 456 tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower 457 tc filter del dev $h2 ingress protocol ip pref 5 handle 104 flower 458 459 log_test "bloom simple test ($tcflags)" 460} 461 462bloom_complex_test() 463{ 464 # Bloom filter index computation is affected from region ID, eRP 465 # ID and from the region key size. In order to excercise those parts 466 # of the Bloom filter code, use a series of regions, each with a 467 # different key size and send packet that should hit all of them. 468 local index 469 470 RET=0 471 NUM_CHAINS=4 472 BASE_INDEX=100 473 474 # Create chain with up to 2 key blocks (ip_proto only) 475 tc chain add dev $h2 ingress chain 1 protocol ip flower \ 476 ip_proto tcp &> /dev/null 477 # Create chain with 2-4 key blocks (ip_proto, src MAC) 478 tc chain add dev $h2 ingress chain 2 protocol ip flower \ 479 ip_proto tcp \ 480 src_mac 00:00:00:00:00:00/FF:FF:FF:FF:FF:FF &> /dev/null 481 # Create chain with 4-8 key blocks (ip_proto, src & dst MAC, IPv4 dest) 482 tc chain add dev $h2 ingress chain 3 protocol ip flower \ 483 ip_proto tcp \ 484 dst_mac 00:00:00:00:00:00/FF:FF:FF:FF:FF:FF \ 485 src_mac 00:00:00:00:00:00/FF:FF:FF:FF:FF:FF \ 486 dst_ip 0.0.0.0/32 &> /dev/null 487 # Default chain contains all fields and therefore is 8-12 key blocks 488 tc chain add dev $h2 ingress chain 4 489 490 # We need at least 2 rules in every region to have eRP table active 491 # so create a dummy rule per chain using a different pattern 492 for i in $(eval echo {0..$NUM_CHAINS}); do 493 index=$((BASE_INDEX - 1 - i)) 494 tc filter add dev $h2 ingress chain $i protocol ip \ 495 pref 2 handle $index flower \ 496 $tcflags ip_proto tcp action drop 497 done 498 499 # Add rules to test Bloom filter, each in a different chain 500 index=$BASE_INDEX 501 tc filter add dev $h2 ingress protocol ip \ 502 pref 1 handle $((++index)) flower \ 503 $tcflags dst_ip 192.0.0.0/16 action goto chain 1 504 tc filter add dev $h2 ingress chain 1 protocol ip \ 505 pref 1 handle $((++index)) flower \ 506 $tcflags action goto chain 2 507 tc filter add dev $h2 ingress chain 2 protocol ip \ 508 pref 1 handle $((++index)) flower \ 509 $tcflags src_mac $h1mac action goto chain 3 510 tc filter add dev $h2 ingress chain 3 protocol ip \ 511 pref 1 handle $((++index)) flower \ 512 $tcflags dst_ip 192.0.0.0/8 action goto chain 4 513 tc filter add dev $h2 ingress chain 4 protocol ip \ 514 pref 1 handle $((++index)) flower \ 515 $tcflags src_ip 192.0.2.0/24 action drop 516 517 # Send a packet that is supposed to hit all chains 518 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ 519 -t ip -q 520 521 for i in $(eval echo {0..$NUM_CHAINS}); do 522 index=$((BASE_INDEX + i + 1)) 523 tc_check_packets "dev $h2 ingress" $index 1 524 check_err $? "Did not match chain $i" 525 done 526 527 # Rules cleanup 528 for i in $(eval echo {$NUM_CHAINS..0}); do 529 index=$((BASE_INDEX - i - 1)) 530 tc filter del dev $h2 ingress chain $i \ 531 pref 2 handle $index flower 532 index=$((BASE_INDEX + i + 1)) 533 tc filter del dev $h2 ingress chain $i \ 534 pref 1 handle $index flower 535 done 536 537 # Chains cleanup 538 for i in $(eval echo {$NUM_CHAINS..1}); do 539 tc chain del dev $h2 ingress chain $i 540 done 541 542 log_test "bloom complex test ($tcflags)" 543} 544 545 546bloom_delta_test() 547{ 548 # When multiple masks are used, the eRP table is activated. When 549 # masks are close enough (delta) the masks reside on the same 550 # eRP table. This test verifies that the eRP table is correctly 551 # allocated and used in delta condition and that Bloom filter is 552 # still functional with delta. 553 554 RET=0 555 556 tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \ 557 $tcflags dst_ip 192.1.0.0/16 action drop 558 559 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.1.2.1 -B 192.1.2.2 \ 560 -t ip -q 561 562 tc_check_packets "dev $h2 ingress" 103 1 563 check_err $? "Single filter - did not match" 564 565 tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \ 566 $tcflags dst_ip 192.2.1.0/24 action drop 567 568 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.2.1.1 -B 192.2.1.2 \ 569 -t ip -q 570 571 tc_check_packets "dev $h2 ingress" 102 1 572 check_err $? "Delta filters - did not match second filter" 573 574 tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower 575 tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower 576 577 log_test "bloom delta test ($tcflags)" 578} 579 580setup_prepare() 581{ 582 h1=${NETIFS[p1]} 583 h2=${NETIFS[p2]} 584 h1mac=$(mac_get $h1) 585 h2mac=$(mac_get $h2) 586 587 vrf_prepare 588 589 h1_create 590 h2_create 591} 592 593cleanup() 594{ 595 pre_cleanup 596 597 h2_destroy 598 h1_destroy 599 600 vrf_cleanup 601} 602 603trap cleanup EXIT 604 605setup_prepare 606setup_wait 607 608tests_run 609 610if ! tc_offload_check; then 611 check_err 1 "Could not test offloaded functionality" 612 log_test "mlxsw-specific tests for tc flower" 613 exit 614else 615 tcflags="skip_sw" 616 tests_run 617fi 618 619exit $EXIT_STATUS 620