selftests: mlxsw: Add a test for FIB offload indication
authorIdo Schimmel <idosch@mellanox.com>
Tue, 18 Jun 2019 15:12:58 +0000 (18:12 +0300)
committerDavid S. Miller <davem@davemloft.net>
Tue, 18 Jun 2019 16:45:37 +0000 (09:45 -0700)
Test that the offload indication for unicast routes is correctly set in
different scenarios. IPv4 support will be added in the future.

Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
tools/testing/selftests/drivers/net/mlxsw/fib_offload.sh [new file with mode: 0755]

diff --git a/tools/testing/selftests/drivers/net/mlxsw/fib_offload.sh b/tools/testing/selftests/drivers/net/mlxsw/fib_offload.sh
new file mode 100755 (executable)
index 0000000..e99ae50
--- /dev/null
@@ -0,0 +1,349 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test unicast FIB offload indication.
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="
+       ipv6_route_add
+       ipv6_route_replace
+       ipv6_route_nexthop_group_share
+       ipv6_route_rate
+"
+NUM_NETIFS=4
+source $lib_dir/lib.sh
+source $lib_dir/devlink_lib.sh
+
+tor1_create()
+{
+       simple_if_init $tor1_p1 2001:db8:1::2/128 2001:db8:1::3/128
+}
+
+tor1_destroy()
+{
+       simple_if_fini $tor1_p1 2001:db8:1::2/128 2001:db8:1::3/128
+}
+
+tor2_create()
+{
+       simple_if_init $tor2_p1 2001:db8:2::2/128 2001:db8:2::3/128
+}
+
+tor2_destroy()
+{
+       simple_if_fini $tor2_p1 2001:db8:2::2/128 2001:db8:2::3/128
+}
+
+spine_create()
+{
+       ip link set dev $spine_p1 up
+       ip link set dev $spine_p2 up
+
+       __addr_add_del $spine_p1 add 2001:db8:1::1/64
+       __addr_add_del $spine_p2 add 2001:db8:2::1/64
+}
+
+spine_destroy()
+{
+       __addr_add_del $spine_p2 del 2001:db8:2::1/64
+       __addr_add_del $spine_p1 del 2001:db8:1::1/64
+
+       ip link set dev $spine_p2 down
+       ip link set dev $spine_p1 down
+}
+
+ipv6_offload_check()
+{
+       local pfx="$1"; shift
+       local expected_num=$1; shift
+       local num
+
+       # Try to avoid races with route offload
+       sleep .1
+
+       num=$(ip -6 route show match ${pfx} | grep "offload" | wc -l)
+
+       if [ $num -eq $expected_num ]; then
+               return 0
+       fi
+
+       return 1
+}
+
+ipv6_route_add_prefix()
+{
+       RET=0
+
+       # Add a prefix route and check that it is offloaded.
+       ip -6 route add 2001:db8:3::/64 dev $spine_p1 metric 100
+       ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 100" 1
+       check_err $? "prefix route not offloaded"
+
+       # Append an identical prefix route with an higher metric and check that
+       # offload indication did not change.
+       ip -6 route append 2001:db8:3::/64 dev $spine_p1 metric 200
+       ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 100" 1
+       check_err $? "lowest metric not offloaded after append"
+       ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 200" 0
+       check_err $? "highest metric offloaded when should not"
+
+       # Prepend an identical prefix route with lower metric and check that
+       # it is offloaded and the others are not.
+       ip -6 route append 2001:db8:3::/64 dev $spine_p1 metric 10
+       ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 10" 1
+       check_err $? "lowest metric not offloaded after prepend"
+       ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 100" 0
+       check_err $? "mid metric offloaded when should not"
+       ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 200" 0
+       check_err $? "highest metric offloaded when should not"
+
+       # Delete the routes and add the same route with a different nexthop
+       # device. Check that it is offloaded.
+       ip -6 route flush 2001:db8:3::/64 dev $spine_p1
+       ip -6 route add 2001:db8:3::/64 dev $spine_p2
+       ipv6_offload_check "2001:db8:3::/64 dev $spine_p2" 1
+
+       log_test "IPv6 prefix route add"
+
+       ip -6 route flush 2001:db8:3::/64
+}
+
+ipv6_route_add_mpath()
+{
+       RET=0
+
+       # Add a multipath route and check that it is offloaded.
+       ip -6 route add 2001:db8:3::/64 metric 100 \
+               nexthop via 2001:db8:1::2 dev $spine_p1 \
+               nexthop via 2001:db8:2::2 dev $spine_p2
+       ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+       check_err $? "multipath route not offloaded when should"
+
+       # Append another nexthop and check that it is offloaded as well.
+       ip -6 route append 2001:db8:3::/64 metric 100 \
+               nexthop via 2001:db8:1::3 dev $spine_p1
+       ipv6_offload_check "2001:db8:3::/64 metric 100" 3
+       check_err $? "appended nexthop not offloaded when should"
+
+       # Mimic route replace by removing the route and adding it back with
+       # only two nexthops.
+       ip -6 route del 2001:db8:3::/64
+       ip -6 route add 2001:db8:3::/64 metric 100 \
+               nexthop via 2001:db8:1::2 dev $spine_p1 \
+               nexthop via 2001:db8:2::2 dev $spine_p2
+       ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+       check_err $? "multipath route not offloaded after delete & add"
+
+       # Append a nexthop with an higher metric and check that the offload
+       # indication did not change.
+       ip -6 route append 2001:db8:3::/64 metric 200 \
+               nexthop via 2001:db8:1::3 dev $spine_p1
+       ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+       check_err $? "lowest metric not offloaded after append"
+       ipv6_offload_check "2001:db8:3::/64 metric 200" 0
+       check_err $? "highest metric offloaded when should not"
+
+       # Prepend a nexthop with a lower metric and check that it is offloaded
+       # and the others are not.
+       ip -6 route append 2001:db8:3::/64 metric 10 \
+               nexthop via 2001:db8:1::3 dev $spine_p1
+       ipv6_offload_check "2001:db8:3::/64 metric 10" 1
+       check_err $? "lowest metric not offloaded after prepend"
+       ipv6_offload_check "2001:db8:3::/64 metric 100" 0
+       check_err $? "mid metric offloaded when should not"
+       ipv6_offload_check "2001:db8:3::/64 metric 200" 0
+       check_err $? "highest metric offloaded when should not"
+
+       log_test "IPv6 multipath route add"
+
+       ip -6 route flush 2001:db8:3::/64
+}
+
+ipv6_route_add()
+{
+       ipv6_route_add_prefix
+       ipv6_route_add_mpath
+}
+
+ipv6_route_replace()
+{
+       RET=0
+
+       # Replace prefix route with prefix route.
+       ip -6 route add 2001:db8:3::/64 metric 100 dev $spine_p1
+       ipv6_offload_check "2001:db8:3::/64 metric 100" 1
+       check_err $? "prefix route not offloaded when should"
+       ip -6 route replace 2001:db8:3::/64 metric 100 dev $spine_p2
+       ipv6_offload_check "2001:db8:3::/64 metric 100" 1
+       check_err $? "prefix route not offloaded after replace"
+
+       # Replace prefix route with multipath route.
+       ip -6 route replace 2001:db8:3::/64 metric 100 \
+               nexthop via 2001:db8:1::2 dev $spine_p1 \
+               nexthop via 2001:db8:2::2 dev $spine_p2
+       ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+       check_err $? "multipath route not offloaded after replace"
+
+       # Replace multipath route with prefix route. A prefix route cannot
+       # replace a multipath route, so it is appended.
+       ip -6 route replace 2001:db8:3::/64 metric 100 dev $spine_p1
+       ipv6_offload_check "2001:db8:3::/64 metric 100 dev $spine_p1" 0
+       check_err $? "prefix route offloaded after 'replacing' multipath route"
+       ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+       check_err $? "multipath route not offloaded after being 'replaced' by prefix route"
+
+       # Replace multipath route with multipath route.
+       ip -6 route replace 2001:db8:3::/64 metric 100 \
+               nexthop via 2001:db8:1::3 dev $spine_p1 \
+               nexthop via 2001:db8:2::3 dev $spine_p2
+       ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+       check_err $? "multipath route not offloaded after replacing multipath route"
+
+       # Replace a non-existing multipath route with a multipath route and
+       # check that it is appended and not offloaded.
+       ip -6 route replace 2001:db8:3::/64 metric 200 \
+               nexthop via 2001:db8:1::3 dev $spine_p1 \
+               nexthop via 2001:db8:2::3 dev $spine_p2
+       ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+       check_err $? "multipath route not offloaded after non-existing route was 'replaced'"
+       ipv6_offload_check "2001:db8:3::/64 metric 200" 0
+       check_err $? "multipath route offloaded after 'replacing' non-existing route"
+
+       log_test "IPv6 route replace"
+
+       ip -6 route flush 2001:db8:3::/64
+}
+
+ipv6_route_nexthop_group_share()
+{
+       RET=0
+
+       # The driver consolidates identical nexthop groups in order to reduce
+       # the resource usage in its adjacency table. Check that the deletion
+       # of one multipath route using the group does not affect the other.
+       ip -6 route add 2001:db8:3::/64 \
+               nexthop via 2001:db8:1::2 dev $spine_p1 \
+               nexthop via 2001:db8:2::2 dev $spine_p2
+       ip -6 route add 2001:db8:4::/64 \
+               nexthop via 2001:db8:1::2 dev $spine_p1 \
+               nexthop via 2001:db8:2::2 dev $spine_p2
+       ipv6_offload_check "2001:db8:3::/64" 2
+       check_err $? "multipath route not offloaded when should"
+       ipv6_offload_check "2001:db8:4::/64" 2
+       check_err $? "multipath route not offloaded when should"
+       ip -6 route del 2001:db8:3::/64
+       ipv6_offload_check "2001:db8:4::/64" 2
+       check_err $? "multipath route not offloaded after deletion of route sharing the nexthop group"
+
+       # Check that after unsharing a nexthop group the routes are still
+       # marked as offloaded.
+       ip -6 route add 2001:db8:3::/64 \
+               nexthop via 2001:db8:1::2 dev $spine_p1 \
+               nexthop via 2001:db8:2::2 dev $spine_p2
+       ip -6 route del 2001:db8:4::/64 \
+               nexthop via 2001:db8:1::2 dev $spine_p1
+       ipv6_offload_check "2001:db8:4::/64" 1
+       check_err $? "singlepath route not offloaded after unsharing the nexthop group"
+       ipv6_offload_check "2001:db8:3::/64" 2
+       check_err $? "multipath route not offloaded after unsharing the nexthop group"
+
+       log_test "IPv6 nexthop group sharing"
+
+       ip -6 route flush 2001:db8:3::/64
+       ip -6 route flush 2001:db8:4::/64
+}
+
+ipv6_route_rate()
+{
+       local batch_dir=$(mktemp -d)
+       local num_rts=$((40 * 1024))
+       local num_nhs=16
+       local total
+       local start
+       local diff
+       local end
+       local nhs
+       local i
+
+       RET=0
+
+       # Prepare 40K /64 multipath routes with 16 nexthops each and check how
+       # long it takes to add them. A limit of 60 seconds is set. It is much
+       # higher than insertion should take and meant to flag a serious
+       # regression.
+       total=$((nums_nhs * num_rts))
+
+       for i in $(seq 1 $num_nhs); do
+               ip -6 address add 2001:db8:1::10:$i/128 dev $tor1_p1
+               nexthops+=" nexthop via 2001:db8:1::10:$i dev $spine_p1"
+       done
+
+       for i in $(seq 1 $num_rts); do
+               echo "route add 2001:db8:8:$(printf "%x" $i)::/64$nexthops" \
+                       >> $batch_dir/add.batch
+               echo "route del 2001:db8:8:$(printf "%x" $i)::/64$nexthops" \
+                       >> $batch_dir/del.batch
+       done
+
+       start=$(date +%s.%N)
+
+       ip -batch $batch_dir/add.batch
+       count=$(ip -6 route show | grep offload | wc -l)
+       while [ $count -lt $total ]; do
+               sleep .01
+               count=$(ip -6 route show | grep offload | wc -l)
+       done
+
+       end=$(date +%s.%N)
+
+       diff=$(echo "$end - $start" | bc -l)
+       test "$(echo "$diff > 60" | bc -l)" -eq 0
+       check_err $? "route insertion took too long"
+       log_info "inserted $num_rts routes in $diff seconds"
+
+       log_test "IPv6 routes insertion rate"
+
+       ip -batch $batch_dir/del.batch
+       for i in $(seq 1 $num_nhs); do
+               ip -6 address del 2001:db8:1::10:$i/128 dev $tor1_p1
+       done
+       rm -rf $batch_dir
+}
+
+setup_prepare()
+{
+       spine_p1=${NETIFS[p1]}
+       tor1_p1=${NETIFS[p2]}
+
+       spine_p2=${NETIFS[p3]}
+       tor2_p1=${NETIFS[p4]}
+
+       vrf_prepare
+       forwarding_enable
+
+       tor1_create
+       tor2_create
+       spine_create
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       spine_destroy
+       tor2_destroy
+       tor1_destroy
+
+       forwarding_restore
+       vrf_cleanup
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS