Commit 22339f2f authored by David S. Miller's avatar David S. Miller

Merge branch 'selftests-updates-for-mlxsw-driver-test'

Jiri Pirko says:

====================
selftests: updates for mlxsw driver test

This patchset contains tweaks to the existing tests and is also adding
couple of new ones, namely tests for shared buffer and red offload.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 2b99e54b 3eba4137
......@@ -45,6 +45,7 @@ ALL_TESTS="
blackhole_ipv6
"
NUM_NETIFS=4
: ${TIMEOUT:=20000} # ms
source $lib_dir/tc_common.sh
source $lib_dir/lib.sh
......@@ -123,7 +124,7 @@ blackhole_ipv4()
skip_hw dst_ip 198.51.100.1 src_ip 192.0.2.1 ip_proto icmp \
action pass
ip -4 route show 198.51.100.0/30 | grep -q offload
busywait "$TIMEOUT" wait_for_offload ip -4 route show 198.51.100.0/30
check_err $? "route not marked as offloaded when should"
ping_do $h1 198.51.100.1
......@@ -147,7 +148,7 @@ blackhole_ipv6()
skip_hw dst_ip 2001:db8:2::1 src_ip 2001:db8:1::1 \
ip_proto icmpv6 action pass
ip -6 route show 2001:db8:2::/120 | grep -q offload
busywait "$TIMEOUT" wait_for_offload ip -6 route show 2001:db8:2::/120
check_err $? "route not marked as offloaded when should"
ping6_do $h1 2001:db8:2::1
......
......@@ -641,13 +641,9 @@ erif_disabled_test()
mz_pid=$!
sleep 5
# In order to see this trap we need a route that points to disabled RIF.
# When ipv6 address is flushed, there is a delay and the routes are
# deleted before the RIF and we cannot get state that we have route
# to disabled RIF.
# Delete IPv6 address first and then check this trap with flushing IPv4.
ip -6 add flush dev br0
ip -4 add flush dev br0
# Unlinking the port from the bridge will disable the RIF associated
# with br0 as it is no longer an upper of any mlxsw port.
ip link set dev $rp1 nomaster
t1_packets=$(devlink_trap_rx_packets_get $trap_name)
t1_bytes=$(devlink_trap_rx_bytes_get $trap_name)
......@@ -659,7 +655,6 @@ erif_disabled_test()
log_test "Egress RIF disabled"
kill $mz_pid && wait $mz_pid &> /dev/null
ip link set dev $rp1 nomaster
__addr_add_del $rp1 add 192.0.2.2/24 2001:db8:1::2/64
ip link del dev br0 type bridge
devlink_trap_action_set $trap_name "drop"
......
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
##############################################################################
# Defines
if [[ ! -v MLXSW_CHIP ]]; then
MLXSW_CHIP=$(devlink -j dev info $DEVLINK_DEV | jq -r '.[][]["driver"]')
if [ -z "$MLXSW_CHIP" ]; then
echo "SKIP: Device $DEVLINK_DEV doesn't support devlink info command"
exit 1
fi
fi
......@@ -2,16 +2,15 @@
# SPDX-License-Identifier: GPL-2.0
ROUTER_NUM_NETIFS=4
: ${TIMEOUT:=20000} # ms
router_h1_create()
{
simple_if_init $h1 192.0.1.1/24
ip route add 193.0.0.0/8 via 192.0.1.2 dev $h1
}
router_h1_destroy()
{
ip route del 193.0.0.0/8 via 192.0.1.2 dev $h1
simple_if_fini $h1 192.0.1.1/24
}
......@@ -64,13 +63,15 @@ router_setup_prepare()
router_create
}
router_offload_validate()
wait_for_routes()
{
local route_count=$1
local offloaded_count
local t0=$1; shift
local route_count=$1; shift
offloaded_count=$(ip route | grep -o 'offload' | wc -l)
[[ $offloaded_count -ge $route_count ]]
local t1=$(ip route | grep -o 'offload' | wc -l)
local delta=$((t1 - t0))
echo $delta
[[ $delta -ge $route_count ]]
}
router_routes_create()
......@@ -90,8 +91,8 @@ router_routes_create()
break 3
fi
echo route add 193.${i}.${j}.${k}/32 via \
192.0.2.1 dev $rp2 >> $ROUTE_FILE
echo route add 193.${i}.${j}.${k}/32 dev $rp2 \
>> $ROUTE_FILE
((count++))
done
done
......@@ -111,45 +112,19 @@ router_test()
{
local route_count=$1
local should_fail=$2
local count=0
local delta
RET=0
local t0=$(ip route | grep -o 'offload' | wc -l)
router_routes_create $route_count
delta=$(busywait "$TIMEOUT" wait_for_routes $t0 $route_count)
router_offload_validate $route_count
check_err_fail $should_fail $? "Offload of $route_count routes"
check_err_fail $should_fail $? "Offload routes: Expected $route_count, got $delta."
if [[ $RET -ne 0 ]] || [[ $should_fail -eq 1 ]]; then
return
fi
tc filter add dev $h2 ingress protocol ip pref 1 flower \
skip_sw dst_ip 193.0.0.0/8 action drop
for i in {0..255}
do
for j in {0..255}
do
for k in {0..255}
do
if [[ $count -eq $route_count ]]; then
break 3
fi
$MZ $h1 -c 1 -p 64 -a $h1mac -b $rp1mac \
-A 192.0.1.1 -B 193.${i}.${j}.${k} \
-t ip -q
((count++))
done
done
done
tc_check_packets "dev $h2 ingress" 1 $route_count
check_err $? "Offload mismatch"
tc filter del dev $h2 ingress protocol ip pref 1 flower \
skip_sw dst_ip 193.0.0.0/8 action drop
router_routes_destroy
}
......
......@@ -32,6 +32,7 @@ ALL_TESTS="
devlink_reload_test
"
NUM_NETIFS=2
: ${TIMEOUT:=20000} # ms
source $lib_dir/lib.sh
source $lib_dir/devlink_lib.sh
......@@ -360,20 +361,24 @@ vlan_rif_refcount_test()
ip link add link br0 name br0.10 up type vlan id 10
ip -6 address add 2001:db8:1::1/64 dev br0.10
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10
check_err $? "vlan rif was not created before adding port to vlan"
bridge vlan add vid 10 dev $swp1
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10
check_err $? "vlan rif was destroyed after adding port to vlan"
bridge vlan del vid 10 dev $swp1
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10
check_err $? "vlan rif was destroyed after removing port from vlan"
ip link set dev $swp1 nomaster
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
check_fail $? "vlan rif was not destroyed after unlinking port from bridge"
busywait "$TIMEOUT" not wait_for_offload \
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10
check_err $? "vlan rif was not destroyed after unlinking port from bridge"
log_test "vlan rif refcount"
......@@ -401,22 +406,28 @@ subport_rif_refcount_test()
ip -6 address add 2001:db8:1::1/64 dev bond1
ip -6 address add 2001:db8:2::1/64 dev bond1.10
ip -6 route get fibmatch 2001:db8:1::2 dev bond1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip -6 route get fibmatch 2001:db8:1::2 dev bond1
check_err $? "subport rif was not created on lag device"
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10
check_err $? "subport rif was not created on vlan device"
ip link set dev $swp1 nomaster
ip -6 route get fibmatch 2001:db8:1::2 dev bond1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip -6 route get fibmatch 2001:db8:1::2 dev bond1
check_err $? "subport rif of lag device was destroyed when should not"
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10
check_err $? "subport rif of vlan device was destroyed when should not"
ip link set dev $swp2 nomaster
ip -6 route get fibmatch 2001:db8:1::2 dev bond1 | grep -q offload
check_fail $? "subport rif of lag device was not destroyed when should"
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10 | grep -q offload
check_fail $? "subport rif of vlan device was not destroyed when should"
busywait "$TIMEOUT" not wait_for_offload \
ip -6 route get fibmatch 2001:db8:1::2 dev bond1
check_err $? "subport rif of lag device was not destroyed when should"
busywait "$TIMEOUT" not wait_for_offload \
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10
check_err $? "subport rif of vlan device was not destroyed when should"
log_test "subport rif refcount"
......@@ -575,7 +586,8 @@ bridge_extern_learn_test()
bridge fdb add de:ad:be:ef:13:37 dev $swp1 master extern_learn
bridge fdb show brport $swp1 | grep de:ad:be:ef:13:37 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
bridge fdb show brport $swp1 de:ad:be:ef:13:37
check_err $? "fdb entry not marked as offloaded when should"
log_test "externally learned fdb entry"
......@@ -595,9 +607,11 @@ neigh_offload_test()
ip -6 neigh add 2001:db8:1::2 lladdr de:ad:be:ef:13:37 nud perm \
dev $swp1
ip -4 neigh show dev $swp1 | grep 192.0.2.2 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip -4 neigh show dev $swp1 192.0.2.2
check_err $? "ipv4 neigh entry not marked as offloaded when should"
ip -6 neigh show dev $swp1 | grep 2001:db8:1::2 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip -6 neigh show dev $swp1 2001:db8:1::2
check_err $? "ipv6 neigh entry not marked as offloaded when should"
log_test "neighbour offload indication"
......@@ -623,25 +637,31 @@ nexthop_offload_test()
ip -6 route add 2001:db8:2::/64 vrf v$swp1 \
nexthop via 2001:db8:1::2 dev $swp1
ip -4 route show 198.51.100.0/24 vrf v$swp1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip -4 route show 198.51.100.0/24 vrf v$swp1
check_err $? "ipv4 nexthop not marked as offloaded when should"
ip -6 route show 2001:db8:2::/64 vrf v$swp1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip -6 route show 2001:db8:2::/64 vrf v$swp1
check_err $? "ipv6 nexthop not marked as offloaded when should"
ip link set dev $swp2 down
sleep 1
ip -4 route show 198.51.100.0/24 vrf v$swp1 | grep -q offload
check_fail $? "ipv4 nexthop marked as offloaded when should not"
ip -6 route show 2001:db8:2::/64 vrf v$swp1 | grep -q offload
check_fail $? "ipv6 nexthop marked as offloaded when should not"
busywait "$TIMEOUT" not wait_for_offload \
ip -4 route show 198.51.100.0/24 vrf v$swp1
check_err $? "ipv4 nexthop marked as offloaded when should not"
busywait "$TIMEOUT" not wait_for_offload \
ip -6 route show 2001:db8:2::/64 vrf v$swp1
check_err $? "ipv6 nexthop marked as offloaded when should not"
ip link set dev $swp2 up
setup_wait
ip -4 route show 198.51.100.0/24 vrf v$swp1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip -4 route show 198.51.100.0/24 vrf v$swp1
check_err $? "ipv4 nexthop not marked as offloaded after neigh add"
ip -6 route show 2001:db8:2::/64 vrf v$swp1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip -6 route show 2001:db8:2::/64 vrf v$swp1
check_err $? "ipv6 nexthop not marked as offloaded after neigh add"
log_test "nexthop offload indication"
......
# SPDX-License-Identifier: GPL-2.0
# This test sends a >1Gbps stream of traffic from H1, to the switch, which
# forwards it to a 1Gbps port. This 1Gbps stream is then looped back to the
# switch and forwarded to the port under test $swp3, which is also 1Gbps.
#
# This way, $swp3 should be 100% filled with traffic without any of it spilling
# to the backlog. Any extra packets sent should almost 1:1 go to backlog. That
# is what H2 is used for--it sends the extra traffic to create backlog.
#
# A RED Qdisc is installed on $swp3. The configuration is such that the minimum
# and maximum size are 1 byte apart, so there is a very clear border under which
# no marking or dropping takes place, and above which everything is marked or
# dropped.
#
# The test uses the buffer build-up behavior to test the installed RED.
#
# In order to test WRED, $swp3 actually contains RED under PRIO, with two
# different configurations. Traffic is prioritized using 802.1p and relies on
# the implicit mlxsw configuration, where packet priority is taken 1:1 from the
# 802.1p marking.
#
# +--------------------------+ +--------------------------+
# | H1 | | H2 |
# | + $h1.10 | | + $h2.10 |
# | | 192.0.2.1/28 | | | 192.0.2.2/28 |
# | | | | | |
# | | $h1.11 + | | | $h2.11 + |
# | | 192.0.2.17/28 | | | | 192.0.2.18/28 | |
# | | | | | | | |
# | \______ ______/ | | \______ ______/ |
# | \ / | | \ / |
# | + $h1 | | + $h2 |
# +-------------|------------+ +-------------|------------+
# | >1Gbps |
# +-------------|------------------------------------------------|------------+
# | SW + $swp1 + $swp2 |
# | _______/ \___________ ___________/ \_______ |
# | / \ / \ |
# | +-|-----------------+ | +-|-----------------+ | |
# | | + $swp1.10 | | | + $swp2.10 | | |
# | | | | .-------------+ $swp5.10 | | |
# | | BR1_10 | | | | | | |
# | | | | | | BR2_10 | | |
# | | + $swp2.10 | | | | | | |
# | +-|-----------------+ | | | + $swp3.10 | | |
# | | | | +-|-----------------+ | |
# | | +-----------------|-+ | | +-----------------|-+ |
# | | | $swp1.11 + | | | | $swp2.11 + | |
# | | | | | .-----------------+ $swp5.11 | |
# | | | BR1_11 | | | | | | |
# | | | | | | | | BR2_11 | |
# | | | $swp2.11 + | | | | | | |
# | | +-----------------|-+ | | | | $swp3.11 + | |
# | | | | | | +-----------------|-+ |
# | \_______ ___________/ | | \___________ _______/ |
# | \ / \ / \ / |
# | + $swp4 + $swp5 + $swp3 |
# +-------------|----------------------|-------------------------|------------+
# | | | 1Gbps
# \________1Gbps_________/ |
# +----------------------------|------------+
# | H3 + $h3 |
# | _____________________/ \_______ |
# | / \ |
# | | | |
# | + $h3.10 $h3.11 + |
# | 192.0.2.3/28 192.0.2.19/28 |
# +-----------------------------------------+
NUM_NETIFS=8
CHECK_TC="yes"
lib_dir=$(dirname $0)/../../../net/forwarding
source $lib_dir/lib.sh
source $lib_dir/devlink_lib.sh
source qos_lib.sh
ipaddr()
{
local host=$1; shift
local vlan=$1; shift
echo 192.0.2.$((16 * (vlan - 10) + host))
}
host_create()
{
local dev=$1; shift
local host=$1; shift
simple_if_init $dev
mtu_set $dev 10000
vlan_create $dev 10 v$dev $(ipaddr $host 10)/28
ip link set dev $dev.10 type vlan egress 0:0
vlan_create $dev 11 v$dev $(ipaddr $host 11)/28
ip link set dev $dev.11 type vlan egress 0:1
}
host_destroy()
{
local dev=$1; shift
vlan_destroy $dev 11
vlan_destroy $dev 10
mtu_restore $dev
simple_if_fini $dev
}
h1_create()
{
host_create $h1 1
}
h1_destroy()
{
host_destroy $h1
}
h2_create()
{
host_create $h2 2
# Some of the tests in this suite use multicast traffic. As this traffic
# enters BR2_10 resp. BR2_11, it is flooded to all other ports. Thus
# e.g. traffic ingressing through $swp2 is flooded to $swp3 (the
# intended destination) and $swp5 (which is intended as ingress for
# another stream of traffic).
#
# This is generally not a problem, but if the $swp5 throughput is lower
# than $swp2 throughput, there will be a build-up at $swp5. That may
# cause packets to fail to queue up at $swp3 due to shared buffer
# quotas, and the test to spuriously fail.
#
# Prevent this by setting the speed of $h2 to 1Gbps.
ethtool -s $h2 speed 1000 autoneg off
}
h2_destroy()
{
ethtool -s $h2 autoneg on
host_destroy $h2
}
h3_create()
{
host_create $h3 3
ethtool -s $h3 speed 1000 autoneg off
}
h3_destroy()
{
ethtool -s $h3 autoneg on
host_destroy $h3
}
switch_create()
{
local intf
local vlan
ip link add dev br1_10 type bridge
ip link add dev br1_11 type bridge
ip link add dev br2_10 type bridge
ip link add dev br2_11 type bridge
for intf in $swp1 $swp2 $swp3 $swp4 $swp5; do
ip link set dev $intf up
mtu_set $intf 10000
done
for intf in $swp1 $swp4; do
for vlan in 10 11; do
vlan_create $intf $vlan
ip link set dev $intf.$vlan master br1_$vlan
ip link set dev $intf.$vlan up
done
done
for intf in $swp2 $swp3 $swp5; do
for vlan in 10 11; do
vlan_create $intf $vlan
ip link set dev $intf.$vlan master br2_$vlan
ip link set dev $intf.$vlan up
done
done
ip link set dev $swp4.10 type vlan egress 0:0
ip link set dev $swp4.11 type vlan egress 0:1
for intf in $swp1 $swp2 $swp5; do
for vlan in 10 11; do
ip link set dev $intf.$vlan type vlan ingress 0:0 1:1
done
done
for intf in $swp2 $swp3 $swp4 $swp5; do
ethtool -s $intf speed 1000 autoneg off
done
ip link set dev br1_10 up
ip link set dev br1_11 up
ip link set dev br2_10 up
ip link set dev br2_11 up
local size=$(devlink_pool_size_thtype 0 | cut -d' ' -f 1)
devlink_port_pool_th_set $swp3 8 $size
}
switch_destroy()
{
local intf
local vlan
devlink_port_pool_th_restore $swp3 8
tc qdisc del dev $swp3 root 2>/dev/null
ip link set dev br2_11 down
ip link set dev br2_10 down
ip link set dev br1_11 down
ip link set dev br1_10 down
for intf in $swp5 $swp4 $swp3 $swp2; do
ethtool -s $intf autoneg on
done
for intf in $swp5 $swp3 $swp2 $swp4 $swp1; do
for vlan in 11 10; do
ip link set dev $intf.$vlan down
ip link set dev $intf.$vlan nomaster
vlan_destroy $intf $vlan
done
mtu_restore $intf
ip link set dev $intf down
done
ip link del dev br2_11
ip link del dev br2_10
ip link del dev br1_11
ip link del dev br1_10
}
setup_prepare()
{
h1=${NETIFS[p1]}
swp1=${NETIFS[p2]}
swp2=${NETIFS[p3]}
h2=${NETIFS[p4]}
swp3=${NETIFS[p5]}
h3=${NETIFS[p6]}
swp4=${NETIFS[p7]}
swp5=${NETIFS[p8]}
h3_mac=$(mac_get $h3)
vrf_prepare
h1_create
h2_create
h3_create
switch_create
}
cleanup()
{
pre_cleanup
switch_destroy
h3_destroy
h2_destroy
h1_destroy
vrf_cleanup
}
ping_ipv4()
{
ping_test $h1.10 $(ipaddr 3 10) " from host 1, vlan 10"
ping_test $h1.11 $(ipaddr 3 11) " from host 1, vlan 11"
ping_test $h2.10 $(ipaddr 3 10) " from host 2, vlan 10"
ping_test $h2.11 $(ipaddr 3 11) " from host 2, vlan 11"
}
get_tc()
{
local vlan=$1; shift
echo $((vlan - 10))
}
get_qdisc_handle()
{
local vlan=$1; shift
local tc=$(get_tc $vlan)
local band=$((8 - tc))
# Handle is 107: for TC1, 108: for TC0.
echo "10$band:"
}
get_qdisc_backlog()
{
local vlan=$1; shift
qdisc_stats_get $swp3 $(get_qdisc_handle $vlan) .backlog
}
get_mc_transmit_queue()
{
local vlan=$1; shift
local tc=$(($(get_tc $vlan) + 8))
ethtool_stats_get $swp3 tc_transmit_queue_tc_$tc
}
get_nmarked()
{
local vlan=$1; shift
ethtool_stats_get $swp3 ecn_marked
}
get_qdisc_npackets()
{
local vlan=$1; shift
busywait_for_counter 1100 +1 \
qdisc_stats_get $swp3 $(get_qdisc_handle $vlan) .packets
}
# This sends traffic in an attempt to build a backlog of $size. Returns 0 on
# success. After 10 failed attempts it bails out and returns 1. It dumps the
# backlog size to stdout.
build_backlog()
{
local vlan=$1; shift
local size=$1; shift
local proto=$1; shift
local tc=$((vlan - 10))
local band=$((8 - tc))
local cur=-1
local i=0
while :; do
local cur=$(busywait 1100 until_counter_is $((cur + 1)) \
get_qdisc_backlog $vlan)
local diff=$((size - cur))
local pkts=$(((diff + 7999) / 8000))
if ((cur >= size)); then
echo $cur
return 0
elif ((i++ > 10)); then
echo $cur
return 1
fi
$MZ $h2.$vlan -p 8000 -a own -b $h3_mac \
-A $(ipaddr 2 $vlan) -B $(ipaddr 3 $vlan) \
-t $proto -q -c $pkts "$@"
done
}
check_marking()
{
local vlan=$1; shift
local cond=$1; shift
local npackets_0=$(get_qdisc_npackets $vlan)
local nmarked_0=$(get_nmarked $vlan)
sleep 5
local npackets_1=$(get_qdisc_npackets $vlan)
local nmarked_1=$(get_nmarked $vlan)
local nmarked_d=$((nmarked_1 - nmarked_0))
local npackets_d=$((npackets_1 - npackets_0))
local pct=$((100 * nmarked_d / npackets_d))
echo $pct
((pct $cond))
}
do_ecn_test()
{
local vlan=$1; shift
local limit=$1; shift
local backlog
local pct
# Main stream.
start_tcp_traffic $h1.$vlan $(ipaddr 1 $vlan) $(ipaddr 3 $vlan) \
$h3_mac tos=0x01
# Build the below-the-limit backlog using UDP. We could use TCP just
# fine, but this way we get a proof that UDP is accepted when queue
# length is below the limit. The main stream is using TCP, and if the
# limit is misconfigured, we would see this traffic being ECN marked.
RET=0
backlog=$(build_backlog $vlan $((2 * limit / 3)) udp)
check_err $? "Could not build the requested backlog"
pct=$(check_marking $vlan "== 0")
check_err $? "backlog $backlog / $limit Got $pct% marked packets, expected == 0."
log_test "TC $((vlan - 10)): ECN backlog < limit"
# Now push TCP, because non-TCP traffic would be early-dropped after the
# backlog crosses the limit, and we want to make sure that the backlog
# is above the limit.
RET=0
backlog=$(build_backlog $vlan $((3 * limit / 2)) tcp tos=0x01)
check_err $? "Could not build the requested backlog"
pct=$(check_marking $vlan ">= 95")
check_err $? "backlog $backlog / $limit Got $pct% marked packets, expected >= 95."
log_test "TC $((vlan - 10)): ECN backlog > limit"
# Up there we saw that UDP gets accepted when backlog is below the
# limit. Now that it is above, it should all get dropped, and backlog
# building should fail.
RET=0
build_backlog $vlan $((2 * limit)) udp >/dev/null
check_fail $? "UDP traffic went into backlog instead of being early-dropped"
log_test "TC $((vlan - 10)): ECN backlog > limit: UDP early-dropped"
stop_traffic
sleep 1
}
do_red_test()
{
local vlan=$1; shift
local limit=$1; shift
local backlog
local pct
# Use ECN-capable TCP to verify there's no marking even though the queue
# is above limit.
start_tcp_traffic $h1.$vlan $(ipaddr 1 $vlan) $(ipaddr 3 $vlan) \
$h3_mac tos=0x01
# Pushing below the queue limit should work.
RET=0
backlog=$(build_backlog $vlan $((2 * limit / 3)) tcp tos=0x01)
check_err $? "Could not build the requested backlog"
pct=$(check_marking $vlan "== 0")
check_err $? "backlog $backlog / $limit Got $pct% marked packets, expected == 0."
log_test "TC $((vlan - 10)): RED backlog < limit"
# Pushing above should not.
RET=0
backlog=$(build_backlog $vlan $((3 * limit / 2)) tcp tos=0x01)
check_fail $? "Traffic went into backlog instead of being early-dropped"
pct=$(check_marking $vlan "== 0")
check_err $? "backlog $backlog / $limit Got $pct% marked packets, expected == 0."
local diff=$((limit - backlog))
pct=$((100 * diff / limit))
((0 <= pct && pct <= 5))
check_err $? "backlog $backlog / $limit expected <= 5% distance"
log_test "TC $((vlan - 10)): RED backlog > limit"
stop_traffic
sleep 1
}
do_mc_backlog_test()
{
local vlan=$1; shift
local limit=$1; shift
local backlog
local pct
RET=0
start_tcp_traffic $h1.$vlan $(ipaddr 1 $vlan) $(ipaddr 3 $vlan) bc
start_tcp_traffic $h2.$vlan $(ipaddr 2 $vlan) $(ipaddr 3 $vlan) bc
qbl=$(busywait 5000 until_counter_is 500000 \
get_qdisc_backlog $vlan)
check_err $? "Could not build MC backlog"
# Verify that we actually see the backlog on BUM TC. Do a busywait as
# well, performance blips might cause false fail.
local ebl
ebl=$(busywait 5000 until_counter_is 500000 \
get_mc_transmit_queue $vlan)
check_err $? "MC backlog reported by qdisc not visible in ethtool"
stop_traffic
stop_traffic
log_test "TC $((vlan - 10)): Qdisc reports MC backlog"
}
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
ALL_TESTS="
ping_ipv4
ecn_test
red_test
mc_backlog_test
"
: ${QDISC:=ets}
source sch_red_core.sh
# do_ecn_test first build 2/3 of the requested backlog and expects no marking,
# and then builds 3/2 of it and does expect marking. The values of $BACKLOG1 and
# $BACKLOG2 are far enough not to overlap, so that we can assume that if we do
# see (do not see) marking, it is actually due to the configuration of that one
# TC, and not due to configuration of the other TC leaking over.
BACKLOG1=200000
BACKLOG2=500000
install_qdisc()
{
local -a args=("$@")
tc qdisc add dev $swp3 root handle 10: $QDISC \
bands 8 priomap 7 6 5 4 3 2 1 0
tc qdisc add dev $swp3 parent 10:8 handle 108: red \
limit 1000000 min $BACKLOG1 max $((BACKLOG1 + 1)) \
probability 1.0 avpkt 8000 burst 38 "${args[@]}"
tc qdisc add dev $swp3 parent 10:7 handle 107: red \
limit 1000000 min $BACKLOG2 max $((BACKLOG2 + 1)) \
probability 1.0 avpkt 8000 burst 63 "${args[@]}"
sleep 1
}
uninstall_qdisc()
{
tc qdisc del dev $swp3 parent 10:7
tc qdisc del dev $swp3 parent 10:8
tc qdisc del dev $swp3 root
}
ecn_test()
{
install_qdisc ecn
do_ecn_test 10 $BACKLOG1
do_ecn_test 11 $BACKLOG2
uninstall_qdisc
}
red_test()
{
install_qdisc
do_red_test 10 $BACKLOG1
do_red_test 11 $BACKLOG2
uninstall_qdisc
}
mc_backlog_test()
{
install_qdisc
# Note that the backlog numbers here do not correspond to RED
# configuration, but are arbitrary.
do_mc_backlog_test 10 $BACKLOG1
do_mc_backlog_test 11 $BACKLOG2
uninstall_qdisc
}
trap cleanup EXIT
setup_prepare
setup_wait
bail_on_lldpad
tests_run
exit $EXIT_STATUS
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
QDISC=prio
source sch_red_ets.sh
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
ALL_TESTS="
ping_ipv4
ecn_test
red_test
mc_backlog_test
"
source sch_red_core.sh
BACKLOG=300000
install_qdisc()
{
local -a args=("$@")
tc qdisc add dev $swp3 root handle 108: red \
limit 1000000 min $BACKLOG max $((BACKLOG + 1)) \
probability 1.0 avpkt 8000 burst 38 "${args[@]}"
sleep 1
}
uninstall_qdisc()
{
tc qdisc del dev $swp3 root
}
ecn_test()
{
install_qdisc ecn
do_ecn_test 10 $BACKLOG
uninstall_qdisc
}
red_test()
{
install_qdisc
do_red_test 10 $BACKLOG
uninstall_qdisc
}
mc_backlog_test()
{
install_qdisc
# Note that the backlog value here does not correspond to RED
# configuration, but is arbitrary.
do_mc_backlog_test 10 $BACKLOG
uninstall_qdisc
}
trap cleanup EXIT
setup_prepare
setup_wait
bail_on_lldpad
tests_run
exit $EXIT_STATUS
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
ALL_TESTS="
port_pool_test
port_tc_ip_test
port_tc_arp_test
"
NUM_NETIFS=2
source ../../../net/forwarding/lib.sh
source ../../../net/forwarding/devlink_lib.sh
source mlxsw_lib.sh
SB_POOL_ING=0
SB_POOL_EGR_CPU=10
SB_ITC_CPU_IP=3
SB_ITC_CPU_ARP=2
SB_ITC=0
h1_create()
{
simple_if_init $h1 192.0.1.1/24
}
h1_destroy()
{
simple_if_fini $h1 192.0.1.1/24
}
h2_create()
{
simple_if_init $h2 192.0.1.2/24
}
h2_destroy()
{
simple_if_fini $h2 192.0.1.2/24
}
sb_occ_pool_check()
{
local dl_port=$1; shift
local pool=$1; shift
local exp_max_occ=$1
local max_occ
local err=0
max_occ=$(devlink sb -j occupancy show $dl_port \
| jq -e ".[][][\"pool\"][\"$pool\"][\"max\"]")
if [[ "$max_occ" -ne "$exp_max_occ" ]]; then
err=1
fi
echo $max_occ
return $err
}
sb_occ_itc_check()
{
local dl_port=$1; shift
local itc=$1; shift
local exp_max_occ=$1
local max_occ
local err=0
max_occ=$(devlink sb -j occupancy show $dl_port \
| jq -e ".[][][\"itc\"][\"$itc\"][\"max\"]")
if [[ "$max_occ" -ne "$exp_max_occ" ]]; then
err=1
fi
echo $max_occ
return $err
}
sb_occ_etc_check()
{
local dl_port=$1; shift
local etc=$1; shift
local exp_max_occ=$1; shift
local max_occ
local err=0
max_occ=$(devlink sb -j occupancy show $dl_port \
| jq -e ".[][][\"etc\"][\"$etc\"][\"max\"]")
if [[ "$max_occ" -ne "$exp_max_occ" ]]; then
err=1
fi
echo $max_occ
return $err
}
port_pool_test()
{
local exp_max_occ=288
local max_occ
devlink sb occupancy clearmax $DEVLINK_DEV
$MZ $h1 -c 1 -p 160 -a $h1mac -b $h2mac -A 192.0.1.1 -B 192.0.1.2 \
-t ip -q
devlink sb occupancy snapshot $DEVLINK_DEV
RET=0
max_occ=$(sb_occ_pool_check $dl_port1 $SB_POOL_ING $exp_max_occ)
check_err $? "Expected iPool($SB_POOL_ING) max occupancy to be $exp_max_occ, but got $max_occ"
log_test "physical port's($h1) ingress pool"
RET=0
max_occ=$(sb_occ_pool_check $dl_port2 $SB_POOL_ING $exp_max_occ)
check_err $? "Expected iPool($SB_POOL_ING) max occupancy to be $exp_max_occ, but got $max_occ"
log_test "physical port's($h2) ingress pool"
RET=0
max_occ=$(sb_occ_pool_check $cpu_dl_port $SB_POOL_EGR_CPU $exp_max_occ)
check_err $? "Expected ePool($SB_POOL_EGR_CPU) max occupancy to be $exp_max_occ, but got $max_occ"
log_test "CPU port's egress pool"
}
port_tc_ip_test()
{
local exp_max_occ=288
local max_occ
devlink sb occupancy clearmax $DEVLINK_DEV
$MZ $h1 -c 1 -p 160 -a $h1mac -b $h2mac -A 192.0.1.1 -B 192.0.1.2 \
-t ip -q
devlink sb occupancy snapshot $DEVLINK_DEV
RET=0
max_occ=$(sb_occ_itc_check $dl_port2 $SB_ITC $exp_max_occ)
check_err $? "Expected ingress TC($SB_ITC) max occupancy to be $exp_max_occ, but got $max_occ"
log_test "physical port's($h1) ingress TC - IP packet"
RET=0
max_occ=$(sb_occ_itc_check $dl_port2 $SB_ITC $exp_max_occ)
check_err $? "Expected ingress TC($SB_ITC) max occupancy to be $exp_max_occ, but got $max_occ"
log_test "physical port's($h2) ingress TC - IP packet"
RET=0
max_occ=$(sb_occ_etc_check $cpu_dl_port $SB_ITC_CPU_IP $exp_max_occ)
check_err $? "Expected egress TC($SB_ITC_CPU_IP) max occupancy to be $exp_max_occ, but got $max_occ"
log_test "CPU port's egress TC - IP packet"
}
port_tc_arp_test()
{
local exp_max_occ=96
local max_occ
if [[ $MLXSW_CHIP != "mlxsw_spectrum" ]]; then
exp_max_occ=144
fi
devlink sb occupancy clearmax $DEVLINK_DEV
$MZ $h1 -c 1 -p 160 -a $h1mac -A 192.0.1.1 -t arp -q
devlink sb occupancy snapshot $DEVLINK_DEV
RET=0
max_occ=$(sb_occ_itc_check $dl_port2 $SB_ITC $exp_max_occ)
check_err $? "Expected ingress TC($SB_ITC) max occupancy to be $exp_max_occ, but got $max_occ"
log_test "physical port's($h1) ingress TC - ARP packet"
RET=0
max_occ=$(sb_occ_itc_check $dl_port2 $SB_ITC $exp_max_occ)
check_err $? "Expected ingress TC($SB_ITC) max occupancy to be $exp_max_occ, but got $max_occ"
log_test "physical port's($h2) ingress TC - ARP packet"
RET=0
max_occ=$(sb_occ_etc_check $cpu_dl_port $SB_ITC_CPU_ARP $exp_max_occ)
check_err $? "Expected egress TC($SB_ITC_IP2ME) max occupancy to be $exp_max_occ, but got $max_occ"
log_test "CPU port's egress TC - ARP packet"
}
setup_prepare()
{
h1=${NETIFS[p1]}
h2=${NETIFS[p2]}
h1mac=$(mac_get $h1)
h2mac=$(mac_get $h2)
dl_port1=$(devlink_port_by_netdev $h1)
dl_port2=$(devlink_port_by_netdev $h2)
cpu_dl_port=$(devlink_cpu_port_get)
vrf_prepare
h1_create
h2_create
}
cleanup()
{
pre_cleanup
h2_destroy
h1_destroy
vrf_cleanup
}
trap cleanup EXIT
setup_prepare
setup_wait
tests_run
exit $EXIT_STATUS
#!/usr/bin/python
# SPDX-License-Identifier: GPL-2.0
import subprocess
import json as j
import random
class SkipTest(Exception):
pass
class RandomValuePicker:
"""
Class for storing shared buffer configuration. Can handle 3 different
objects, pool, tcbind and portpool. Provide an interface to get random
values for a specific object type as the follow:
1. Pool:
- random size
2. TcBind:
- random pool number
- random threshold
3. PortPool:
- random threshold
"""
def __init__(self, pools):
self._pools = []
for pool in pools:
self._pools.append(pool)
def _cell_size(self):
return self._pools[0]["cell_size"]
def _get_static_size(self, th):
# For threshold of 16, this works out to be about 12MB on Spectrum-1,
# and about 17MB on Spectrum-2.
return th * 8000 * self._cell_size()
def _get_size(self):
return self._get_static_size(16)
def _get_thtype(self):
return "static"
def _get_th(self, pool):
# Threshold value could be any integer between 3 to 16
th = random.randint(3, 16)
if pool["thtype"] == "dynamic":
return th
else:
return self._get_static_size(th)
def _get_pool(self, direction):
ing_pools = []
egr_pools = []
for pool in self._pools:
if pool["type"] == "ingress":
ing_pools.append(pool)
else:
egr_pools.append(pool)
if direction == "ingress":
arr = ing_pools
else:
arr = egr_pools
return arr[random.randint(0, len(arr) - 1)]
def get_value(self, objid):
if isinstance(objid, Pool):
if objid["pool"] in [4, 8, 9, 10]:
# The threshold type of pools 4, 8, 9 and 10 cannot be changed
raise SkipTest()
else:
return (self._get_size(), self._get_thtype())
if isinstance(objid, TcBind):
if objid["tc"] >= 8:
# Multicast TCs cannot be changed
raise SkipTest()
else:
pool = self._get_pool(objid["type"])
th = self._get_th(pool)
pool_n = pool["pool"]
return (pool_n, th)
if isinstance(objid, PortPool):
pool_n = objid["pool"]
pool = self._pools[pool_n]
assert pool["pool"] == pool_n
th = self._get_th(pool)
return (th,)
class RecordValuePickerException(Exception):
pass
class RecordValuePicker:
"""
Class for storing shared buffer configuration. Can handle 2 different
objects, pool and tcbind. Provide an interface to get the stored values per
object type.
"""
def __init__(self, objlist):
self._recs = []
for item in objlist:
self._recs.append({"objid": item, "value": item.var_tuple()})
def get_value(self, objid):
if isinstance(objid, Pool) and objid["pool"] in [4, 8, 9, 10]:
# The threshold type of pools 4, 8, 9 and 10 cannot be changed
raise SkipTest()
if isinstance(objid, TcBind) and objid["tc"] >= 8:
# Multicast TCs cannot be changed
raise SkipTest()
for rec in self._recs:
if rec["objid"].weak_eq(objid):
return rec["value"]
raise RecordValuePickerException()
def run_cmd(cmd, json=False):
out = subprocess.check_output(cmd, shell=True)
if json:
return j.loads(out)
return out
def run_json_cmd(cmd):
return run_cmd(cmd, json=True)
def log_test(test_name, err_msg=None):
if err_msg:
print("\t%s" % err_msg)
print("TEST: %-80s [FAIL]" % test_name)
else:
print("TEST: %-80s [ OK ]" % test_name)
class CommonItem(dict):
varitems = []
def var_tuple(self):
ret = []
self.varitems.sort()
for key in self.varitems:
ret.append(self[key])
return tuple(ret)
def weak_eq(self, other):
for key in self:
if key in self.varitems:
continue
if self[key] != other[key]:
return False
return True
class CommonList(list):
def get_by(self, by_obj):
for item in self:
if item.weak_eq(by_obj):
return item
return None
def del_by(self, by_obj):
for item in self:
if item.weak_eq(by_obj):
self.remove(item)
class Pool(CommonItem):
varitems = ["size", "thtype"]
def dl_set(self, dlname, size, thtype):
run_cmd("devlink sb pool set {} sb {} pool {} size {} thtype {}".format(dlname, self["sb"],
self["pool"],
size, thtype))
class PoolList(CommonList):
pass
def get_pools(dlname, direction=None):
d = run_json_cmd("devlink sb pool show -j")
pools = PoolList()
for pooldict in d["pool"][dlname]:
if not direction or direction == pooldict["type"]:
pools.append(Pool(pooldict))
return pools
def do_check_pools(dlname, pools, vp):
for pool in pools:
pre_pools = get_pools(dlname)
try:
(size, thtype) = vp.get_value(pool)
except SkipTest:
continue
pool.dl_set(dlname, size, thtype)
post_pools = get_pools(dlname)
pool = post_pools.get_by(pool)
err_msg = None
if pool["size"] != size:
err_msg = "Incorrect pool size (got {}, expected {})".format(pool["size"], size)
if pool["thtype"] != thtype:
err_msg = "Incorrect pool threshold type (got {}, expected {})".format(pool["thtype"], thtype)
pre_pools.del_by(pool)
post_pools.del_by(pool)
if pre_pools != post_pools:
err_msg = "Other pool setup changed as well"
log_test("pool {} of sb {} set verification".format(pool["pool"],
pool["sb"]), err_msg)
def check_pools(dlname, pools):
# Save defaults
record_vp = RecordValuePicker(pools)
# For each pool, set random size and static threshold type
do_check_pools(dlname, pools, RandomValuePicker(pools))
# Restore defaults
do_check_pools(dlname, pools, record_vp)
class TcBind(CommonItem):
varitems = ["pool", "threshold"]
def __init__(self, port, d):
super(TcBind, self).__init__(d)
self["dlportname"] = port.name
def dl_set(self, pool, th):
run_cmd("devlink sb tc bind set {} sb {} tc {} type {} pool {} th {}".format(self["dlportname"],
self["sb"],
self["tc"],
self["type"],
pool, th))
class TcBindList(CommonList):
pass
def get_tcbinds(ports, verify_existence=False):
d = run_json_cmd("devlink sb tc bind show -j -n")
tcbinds = TcBindList()
for port in ports:
err_msg = None
if port.name not in d["tc_bind"] or len(d["tc_bind"][port.name]) == 0:
err_msg = "No tc bind for port"
else:
for tcbinddict in d["tc_bind"][port.name]:
tcbinds.append(TcBind(port, tcbinddict))
if verify_existence:
log_test("tc bind existence for port {} verification".format(port.name), err_msg)
return tcbinds
def do_check_tcbind(ports, tcbinds, vp):
for tcbind in tcbinds:
pre_tcbinds = get_tcbinds(ports)
try:
(pool, th) = vp.get_value(tcbind)
except SkipTest:
continue
tcbind.dl_set(pool, th)
post_tcbinds = get_tcbinds(ports)
tcbind = post_tcbinds.get_by(tcbind)
err_msg = None
if tcbind["pool"] != pool:
err_msg = "Incorrect pool (got {}, expected {})".format(tcbind["pool"], pool)
if tcbind["threshold"] != th:
err_msg = "Incorrect threshold (got {}, expected {})".format(tcbind["threshold"], th)
pre_tcbinds.del_by(tcbind)
post_tcbinds.del_by(tcbind)
if pre_tcbinds != post_tcbinds:
err_msg = "Other tc bind setup changed as well"
log_test("tc bind {}-{} of sb {} set verification".format(tcbind["dlportname"],
tcbind["tc"],
tcbind["sb"]), err_msg)
def check_tcbind(dlname, ports, pools):
tcbinds = get_tcbinds(ports, verify_existence=True)
# Save defaults
record_vp = RecordValuePicker(tcbinds)
# Bind each port and unicast TC (TCs < 8) to a random pool and a random
# threshold
do_check_tcbind(ports, tcbinds, RandomValuePicker(pools))
# Restore defaults
do_check_tcbind(ports, tcbinds, record_vp)
class PortPool(CommonItem):
varitems = ["threshold"]
def __init__(self, port, d):
super(PortPool, self).__init__(d)
self["dlportname"] = port.name
def dl_set(self, th):
run_cmd("devlink sb port pool set {} sb {} pool {} th {}".format(self["dlportname"],
self["sb"],
self["pool"], th))
class PortPoolList(CommonList):
pass
def get_portpools(ports, verify_existence=False):
d = run_json_cmd("devlink sb port pool -j -n")
portpools = PortPoolList()
for port in ports:
err_msg = None
if port.name not in d["port_pool"] or len(d["port_pool"][port.name]) == 0:
err_msg = "No port pool for port"
else:
for portpooldict in d["port_pool"][port.name]:
portpools.append(PortPool(port, portpooldict))
if verify_existence:
log_test("port pool existence for port {} verification".format(port.name), err_msg)
return portpools
def do_check_portpool(ports, portpools, vp):
for portpool in portpools:
pre_portpools = get_portpools(ports)
(th,) = vp.get_value(portpool)
portpool.dl_set(th)
post_portpools = get_portpools(ports)
portpool = post_portpools.get_by(portpool)
err_msg = None
if portpool["threshold"] != th:
err_msg = "Incorrect threshold (got {}, expected {})".format(portpool["threshold"], th)
pre_portpools.del_by(portpool)
post_portpools.del_by(portpool)
if pre_portpools != post_portpools:
err_msg = "Other port pool setup changed as well"
log_test("port pool {}-{} of sb {} set verification".format(portpool["dlportname"],
portpool["pool"],
portpool["sb"]), err_msg)
def check_portpool(dlname, ports, pools):
portpools = get_portpools(ports, verify_existence=True)
# Save defaults
record_vp = RecordValuePicker(portpools)
# For each port pool, set a random threshold
do_check_portpool(ports, portpools, RandomValuePicker(pools))
# Restore defaults
do_check_portpool(ports, portpools, record_vp)
class Port:
def __init__(self, name):
self.name = name
class PortList(list):
pass
def get_ports(dlname):
d = run_json_cmd("devlink port show -j")
ports = PortList()
for name in d["port"]:
if name.find(dlname) == 0 and d["port"][name]["flavour"] == "physical":
ports.append(Port(name))
return ports
def get_device():
devices_info = run_json_cmd("devlink -j dev info")["info"]
for d in devices_info:
if "mlxsw_spectrum" in devices_info[d]["driver"]:
return d
return None
class UnavailableDevlinkNameException(Exception):
pass
def test_sb_configuration():
# Use static seed
random.seed(0)
dlname = get_device()
if not dlname:
raise UnavailableDevlinkNameException()
ports = get_ports(dlname)
pools = get_pools(dlname)
check_pools(dlname, pools)
check_tcbind(dlname, ports, pools)
check_portpool(dlname, ports, pools)
test_sb_configuration()
......@@ -8,8 +8,9 @@ source $lib_dir/lib.sh
source $lib_dir/tc_common.sh
source $lib_dir/devlink_lib.sh
if [ "$DEVLINK_VIDDID" != "15b3:cf6c" ]; then
echo "SKIP: test is tailored for Mellanox Spectrum-2"
if [[ "$DEVLINK_VIDDID" != "15b3:cf6c" && \
"$DEVLINK_VIDDID" != "15b3:cf70" ]]; then
echo "SKIP: test is tailored for Mellanox Spectrum-2 and Spectrum-3"
exit 1
fi
......
......@@ -3,7 +3,11 @@
lib_dir=$(dirname $0)/../../../net/forwarding
ALL_TESTS="shared_block_drop_test"
ALL_TESTS="
shared_block_drop_test
egress_redirect_test
multi_mirror_test
"
NUM_NETIFS=2
source $lib_dir/tc_common.sh
......@@ -69,6 +73,88 @@ shared_block_drop_test()
log_test "shared block drop"
}
egress_redirect_test()
{
RET=0
# It is forbidden in mlxsw driver to have mirred redirect on
# egress-bound block.
tc qdisc add dev $swp1 ingress_block 22 clsact
check_err $? "Failed to create clsact with ingress block"
tc filter add block 22 protocol ip pref 1 handle 101 flower \
skip_sw dst_ip 192.0.2.2 \
action mirred egress redirect dev $swp2
check_err $? "Failed to add redirect rule to ingress bound block"
tc qdisc add dev $swp2 ingress_block 22 clsact
check_err $? "Failed to create another clsact with ingress shared block"
tc qdisc del dev $swp2 clsact
tc qdisc add dev $swp2 egress_block 22 clsact
check_fail $? "Incorrect success to create another clsact with egress shared block"
tc filter del block 22 protocol ip pref 1 handle 101 flower
tc qdisc add dev $swp2 egress_block 22 clsact
check_err $? "Failed to create another clsact with egress shared block after blocker redirect rule removed"
tc filter add block 22 protocol ip pref 1 handle 101 flower \
skip_sw dst_ip 192.0.2.2 \
action mirred egress redirect dev $swp2
check_fail $? "Incorrect success to add redirect rule to mixed bound block"
tc qdisc del dev $swp1 clsact
tc qdisc add dev $swp1 egress_block 22 clsact
check_err $? "Failed to create another clsact with egress shared block"
tc filter add block 22 protocol ip pref 1 handle 101 flower \
skip_sw dst_ip 192.0.2.2 \
action mirred egress redirect dev $swp2
check_fail $? "Incorrect success to add redirect rule to egress bound shared block"
tc qdisc del dev $swp2 clsact
tc filter add block 22 protocol ip pref 1 handle 101 flower \
skip_sw dst_ip 192.0.2.2 \
action mirred egress redirect dev $swp2
check_fail $? "Incorrect success to add redirect rule to egress bound block"
tc qdisc del dev $swp1 clsact
log_test "shared block drop"
}
multi_mirror_test()
{
RET=0
# It is forbidden in mlxsw driver to have multiple mirror
# actions in a single rule.
tc qdisc add dev $swp1 clsact
tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 flower \
skip_sw dst_ip 192.0.2.2 \
action mirred egress mirror dev $swp2
check_err $? "Failed to add rule with single mirror action"
tc filter del dev $swp1 ingress protocol ip pref 1 handle 101 flower
tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 flower \
skip_sw dst_ip 192.0.2.2 \
action mirred egress mirror dev $swp2 \
action mirred egress mirror dev $swp1
check_fail $? "Incorrect success to add rule with two mirror actions"
tc qdisc del dev $swp1 clsact
log_test "multi mirror"
}
setup_prepare()
{
swp1=${NETIFS[p1]}
......
......@@ -2,9 +2,9 @@
# SPDX-License-Identifier: GPL-2.0
# Test for resource limit of offloaded flower rules. The test adds a given
# number of flower matches for different IPv6 addresses, then generates traffic,
# and ensures each was hit exactly once. This file contains functions to set up
# a testing topology and run the test, and is meant to be sourced from a test
# number of flower matches for different IPv6 addresses, then check the offload
# indication for all of the tc flower rules. This file contains functions to set
# up a testing topology and run the test, and is meant to be sourced from a test
# script that calls the testing routine with a given number of rules.
TC_FLOWER_NUM_NETIFS=2
......@@ -94,22 +94,15 @@ __tc_flower_test()
tc_flower_rules_create $count $should_fail
for ((i = 0; i < count; ++i)); do
$MZ $h1 -q -c 1 -t ip -p 20 -b bc -6 \
-A 2001:db8:2::1 \
-B $(tc_flower_addr $i)
done
MISMATCHES=$(
tc -j -s filter show dev $h2 ingress |
jq -r '[ .[] | select(.kind == "flower") | .options |
values as $rule | .actions[].stats.packets |
select(. != 1) | "\(.) on \($rule.keys.dst_ip)" ] |
join(", ")'
)
test -z "$MISMATCHES"
check_err $? "Expected to capture 1 packet for each IP, but got $MISMATCHES"
offload_count=$(tc -j -s filter show dev $h2 ingress |
jq -r '[ .[] | select(.kind == "flower") |
.options | .in_hw ]' | jq .[] | wc -l)
[[ $((offload_count - 1)) -eq $count ]]
if [[ $should_fail -eq 0 ]]; then
check_err $? "Offload mismatch"
else
check_err_fail $should_fail $? "Offload more than expacted"
fi
}
tc_flower_test()
......
......@@ -9,6 +9,7 @@ lib_dir=$(dirname $0)/../../../net/forwarding
ALL_TESTS="sanitization_test offload_indication_test \
sanitization_vlan_aware_test offload_indication_vlan_aware_test"
NUM_NETIFS=2
: ${TIMEOUT:=20000} # ms
source $lib_dir/lib.sh
setup_prepare()
......@@ -470,8 +471,8 @@ offload_indication_fdb_flood_test()
bridge fdb append 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.2
bridge fdb show brport vxlan0 | grep 00:00:00:00:00:00 \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb 00:00:00:00:00:00 \
bridge fdb show brport vxlan0
check_err $?
bridge fdb del 00:00:00:00:00:00 dev vxlan0 self
......@@ -486,11 +487,11 @@ offload_indication_fdb_bridge_test()
bridge fdb add de:ad:be:ef:13:37 dev vxlan0 self master static \
dst 198.51.100.2
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self bridge fdb show brport vxlan0
check_err $?
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan0
check_err $?
log_test "vxlan entry offload indication - initial state"
......@@ -500,9 +501,9 @@ offload_indication_fdb_bridge_test()
RET=0
bridge fdb del de:ad:be:ef:13:37 dev vxlan0 master
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
| grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self bridge fdb show brport vxlan0
check_err $?
log_test "vxlan entry offload indication - after removal from bridge"
......@@ -511,11 +512,11 @@ offload_indication_fdb_bridge_test()
RET=0
bridge fdb add de:ad:be:ef:13:37 dev vxlan0 master static
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self bridge fdb show brport vxlan0
check_err $?
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan0
check_err $?
log_test "vxlan entry offload indication - after re-add to bridge"
......@@ -525,9 +526,9 @@ offload_indication_fdb_bridge_test()
RET=0
bridge fdb del de:ad:be:ef:13:37 dev vxlan0 self
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
| grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan0
check_err $?
log_test "vxlan entry offload indication - after removal from vxlan"
......@@ -536,11 +537,11 @@ offload_indication_fdb_bridge_test()
RET=0
bridge fdb add de:ad:be:ef:13:37 dev vxlan0 self dst 198.51.100.2
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self bridge fdb show brport vxlan0
check_err $?
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan0
check_err $?
log_test "vxlan entry offload indication - after re-add to vxlan"
......@@ -558,27 +559,32 @@ offload_indication_decap_route_test()
{
RET=0
ip route show table local | grep 198.51.100.1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
ip link set dev vxlan0 down
ip route show table local | grep 198.51.100.1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
ip link set dev vxlan1 down
ip route show table local | grep 198.51.100.1 | grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
log_test "vxlan decap route - vxlan device down"
RET=0
ip link set dev vxlan1 up
ip route show table local | grep 198.51.100.1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
ip link set dev vxlan0 up
ip route show table local | grep 198.51.100.1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
log_test "vxlan decap route - vxlan device up"
......@@ -586,11 +592,13 @@ offload_indication_decap_route_test()
RET=0
ip address delete 198.51.100.1/32 dev lo
ip route show table local | grep 198.51.100.1 | grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
ip address add 198.51.100.1/32 dev lo
ip route show table local | grep 198.51.100.1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
log_test "vxlan decap route - add local route"
......@@ -598,16 +606,19 @@ offload_indication_decap_route_test()
RET=0
ip link set dev $swp1 nomaster
ip route show table local | grep 198.51.100.1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
ip link set dev $swp2 nomaster
ip route show table local | grep 198.51.100.1 | grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
ip link set dev $swp1 master br0
ip link set dev $swp2 master br1
ip route show table local | grep 198.51.100.1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
log_test "vxlan decap route - local ports enslavement"
......@@ -615,12 +626,14 @@ offload_indication_decap_route_test()
RET=0
ip link del dev br0
ip route show table local | grep 198.51.100.1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
ip link del dev br1
ip route show table local | grep 198.51.100.1 | grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
log_test "vxlan decap route - bridge device deletion"
......@@ -632,16 +645,19 @@ offload_indication_decap_route_test()
ip link set dev $swp2 master br1
ip link set dev vxlan0 master br0
ip link set dev vxlan1 master br1
ip route show table local | grep 198.51.100.1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
ip link del dev vxlan0
ip route show table local | grep 198.51.100.1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
ip link del dev vxlan1
ip route show table local | grep 198.51.100.1 | grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
log_test "vxlan decap route - vxlan device deletion"
......@@ -656,12 +672,15 @@ check_fdb_offloaded()
local mac=00:11:22:33:44:55
local zmac=00:00:00:00:00:00
bridge fdb show dev vxlan0 | grep $mac | grep self | grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $mac self \
bridge fdb show dev vxlan0
check_err $?
bridge fdb show dev vxlan0 | grep $mac | grep master | grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $mac master \
bridge fdb show dev vxlan0
check_err $?
bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $zmac self \
bridge fdb show dev vxlan0
check_err $?
}
......@@ -672,13 +691,15 @@ check_vxlan_fdb_not_offloaded()
bridge fdb show dev vxlan0 | grep $mac | grep -q self
check_err $?
bridge fdb show dev vxlan0 | grep $mac | grep self | grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb $mac self \
bridge fdb show dev vxlan0
check_err $?
bridge fdb show dev vxlan0 | grep $zmac | grep -q self
check_err $?
bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb $zmac self \
bridge fdb show dev vxlan0
check_err $?
}
check_bridge_fdb_not_offloaded()
......@@ -688,8 +709,9 @@ check_bridge_fdb_not_offloaded()
bridge fdb show dev vxlan0 | grep $mac | grep -q master
check_err $?
bridge fdb show dev vxlan0 | grep $mac | grep master | grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb $mac master \
bridge fdb show dev vxlan0
check_err $?
}
__offload_indication_join_vxlan_first()
......@@ -771,12 +793,14 @@ __offload_indication_join_vxlan_last()
ip link set dev $swp1 master br0
bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb $zmac self \
bridge fdb show dev vxlan0
check_err $?
ip link set dev vxlan0 master br0
bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $zmac self \
bridge fdb show dev vxlan0
check_err $?
log_test "offload indication - attach vxlan last"
......@@ -866,8 +890,9 @@ sanitization_vlan_aware_test()
ip link set dev $swp1 master br0 &> /dev/null
check_fail $?
ip route show table local | grep 198.51.100.1 | grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
log_test "vlan-aware - failed enslavement to bridge due to conflict"
......@@ -929,11 +954,11 @@ offload_indication_vlan_aware_fdb_test()
bridge fdb add de:ad:be:ef:13:37 dev vxlan10 self master static \
dst 198.51.100.2 vlan 10
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self bridge fdb show brport vxlan10
check_err $?
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan10
check_err $?
log_test "vxlan entry offload indication - initial state"
......@@ -943,9 +968,9 @@ offload_indication_vlan_aware_fdb_test()
RET=0
bridge fdb del de:ad:be:ef:13:37 dev vxlan10 master vlan 10
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
| grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self bridge fdb show brport vxlan10
check_err $?
log_test "vxlan entry offload indication - after removal from bridge"
......@@ -954,11 +979,11 @@ offload_indication_vlan_aware_fdb_test()
RET=0
bridge fdb add de:ad:be:ef:13:37 dev vxlan10 master static vlan 10
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self bridge fdb show brport vxlan10
check_err $?
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan10
check_err $?
log_test "vxlan entry offload indication - after re-add to bridge"
......@@ -968,9 +993,9 @@ offload_indication_vlan_aware_fdb_test()
RET=0
bridge fdb del de:ad:be:ef:13:37 dev vxlan10 self
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
| grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan10
check_err $?
log_test "vxlan entry offload indication - after removal from vxlan"
......@@ -979,11 +1004,11 @@ offload_indication_vlan_aware_fdb_test()
RET=0
bridge fdb add de:ad:be:ef:13:37 dev vxlan10 self dst 198.51.100.2
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self bridge fdb show brport vxlan10
check_err $?
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan10
check_err $?
log_test "vxlan entry offload indication - after re-add to vxlan"
......@@ -995,28 +1020,31 @@ offload_indication_vlan_aware_decap_route_test()
{
RET=0
ip route show table local | grep 198.51.100.1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
# Toggle PVID flag on one VxLAN device and make sure route is still
# marked as offloaded
bridge vlan add vid 10 dev vxlan10 untagged
ip route show table local | grep 198.51.100.1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
# Toggle PVID flag on second VxLAN device and make sure route is no
# longer marked as offloaded
bridge vlan add vid 20 dev vxlan20 untagged
ip route show table local | grep 198.51.100.1 | grep -q offload
check_fail $?
busywait "$TIMEOUT" not wait_for_offload \
ip route show table local 198.51.100.1
check_err $?
# Toggle PVID flag back and make sure route is marked as offloaded
bridge vlan add vid 10 dev vxlan10 pvid untagged
bridge vlan add vid 20 dev vxlan20 pvid untagged
ip route show table local | grep 198.51.100.1 | grep -q offload
busywait "$TIMEOUT" wait_for_offload ip route show table local 198.51.100.1
check_err $?
log_test "vxlan decap route - vni map/unmap"
......@@ -1069,33 +1097,33 @@ offload_indication_vlan_aware_l3vni_test()
ip link set dev vxlan0 master br0
bridge vlan add dev vxlan0 vid 10 pvid untagged
bridge fdb show brport vxlan0 | grep $zmac | grep self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $zmac self \
bridge fdb show brport vxlan0
check_err $? "vxlan tunnel not offloaded when should"
# Configure a VLAN interface and make sure tunnel is offloaded
ip link add link br0 name br10 up type vlan id 10
sysctl_set net.ipv6.conf.br10.disable_ipv6 0
ip -6 address add 2001:db8:1::1/64 dev br10
bridge fdb show brport vxlan0 | grep $zmac | grep self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $zmac self \
bridge fdb show brport vxlan0
check_err $? "vxlan tunnel not offloaded when should"
# Unlink the VXLAN device, make sure tunnel is no longer offloaded,
# then add it back to the bridge and make sure it is offloaded
ip link set dev vxlan0 nomaster
bridge fdb show brport vxlan0 | grep $zmac | grep self \
| grep -q offload
check_fail $? "vxlan tunnel offloaded after unlinked from bridge"
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb $zmac self \
bridge fdb show brport vxlan0
check_err $? "vxlan tunnel offloaded after unlinked from bridge"
ip link set dev vxlan0 master br0
bridge fdb show brport vxlan0 | grep $zmac | grep self \
| grep -q offload
check_fail $? "vxlan tunnel offloaded despite no matching vid"
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb $zmac self \
bridge fdb show brport vxlan0
check_err $? "vxlan tunnel offloaded despite no matching vid"
bridge vlan add dev vxlan0 vid 10 pvid untagged
bridge fdb show brport vxlan0 | grep $zmac | grep self \
| grep -q offload
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $zmac self \
bridge fdb show brport vxlan0
check_err $? "vxlan tunnel not offloaded after adding vid"
log_test "vxlan - l3 vni"
......
......@@ -35,6 +35,12 @@ if [ $? -ne 0 ]; then
exit 1
fi
devlink dev help 2>&1 | grep info &> /dev/null
if [ $? -ne 0 ]; then
echo "SKIP: iproute2 too old, missing devlink dev info support"
exit 1
fi
##############################################################################
# Devlink helpers
......@@ -413,3 +419,19 @@ devlink_trap_drop_cleanup()
kill $mz_pid && wait $mz_pid &> /dev/null
tc filter del dev $dev egress protocol $proto pref $pref handle $handle flower
}
devlink_port_by_netdev()
{
local if_name=$1
devlink -j port show $if_name | jq -e '.[] | keys' | jq -r '.[]'
}
devlink_cpu_port_get()
{
local cpu_dl_port_num=$(devlink port list | grep "$DEVLINK_DEV" |
grep cpu | cut -d/ -f3 | cut -d: -f1 |
sed -n '1p')
echo "$DEVLINK_DEV/$cpu_dl_port_num"
}
......@@ -248,6 +248,33 @@ busywait()
done
}
not()
{
"$@"
[[ $? != 0 ]]
}
grep_bridge_fdb()
{
local addr=$1; shift
local word
local flag
if [ "$1" == "self" ] || [ "$1" == "master" ]; then
word=$1; shift
if [ "$1" == "-v" ]; then
flag=$1; shift
fi
fi
$@ | grep $addr | grep $flag "$word"
}
wait_for_offload()
{
"$@" | grep -q offload
}
until_counter_is()
{
local value=$1; shift
......@@ -607,6 +634,16 @@ ethtool_stats_get()
ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
}
qdisc_stats_get()
{
local dev=$1; shift
local handle=$1; shift
local selector=$1; shift
tc -j -s qdisc show dev "$dev" \
| jq '.[] | select(.handle == "'"$handle"'") | '"$selector"
}
humanize()
{
local speed=$1; shift
......@@ -1132,18 +1169,29 @@ flood_test()
flood_multicast_test $br_port $host1_if $host2_if
}
start_traffic()
__start_traffic()
{
local proto=$1; shift
local h_in=$1; shift # Where the traffic egresses the host
local sip=$1; shift
local dip=$1; shift
local dmac=$1; shift
$MZ $h_in -p 8000 -A $sip -B $dip -c 0 \
-a own -b $dmac -t udp -q &
-a own -b $dmac -t "$proto" -q "$@" &
sleep 1
}
start_traffic()
{
__start_traffic udp "$@"
}
start_tcp_traffic()
{
__start_traffic tcp "$@"
}
stop_traffic()
{
# Suppress noise from killing mausezahn.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment