# SPDX-License-Identifier: GPL-2.0

# This is a template for ETS Qdisc test.
#
# This test sends from H1 several traffic streams with 802.1p-tagged packets.
# The tags are used at $swp1 to prioritize the traffic. Each stream is then
# queued at a different ETS band according to the assigned priority. After
# runnig for a while, counters at H2 are consulted to determine whether the
# traffic scheduling was according to the ETS configuration.
#
# This template is supposed to be embedded by a test driver, which implements
# statistics collection, any HW-specific stuff, and prominently configures the
# system to assure that there is overcommitment at $swp2. That is necessary so
# that the ETS traffic selection algorithm kicks in and has to schedule some
# traffic at the expense of other.
#
# A driver for veth-based testing is in sch_ets.sh, an example of a driver for
# an offloaded data path is in selftests/drivers/net/mlxsw/sch_ets.sh.
#
# +---------------------------------------------------------------------+
# | H1                                                                  |
# |     + $h1.10              + $h1.11              + $h1.12            |
# |     | 192.0.2.1/28        | 192.0.2.17/28       | 192.0.2.33/28     |
# |     | egress-qos-map      | egress-qos-map      | egress-qos-map    |
# |     |  0:0                |  0:1                |  0:2              |
# |     \____________________ | ____________________/                   |
# |                          \|/                                        |
# |                           + $h1                                     |
# +---------------------------|-----------------------------------------+
#                             |
# +---------------------------|-----------------------------------------+
# | SW                        + $swp1                                   |
# |                           | >1Gbps                                  |
# |      ____________________/|\____________________                    |
# |     /                     |                     \                   |
# |  +--|----------------+ +--|----------------+ +--|----------------+  |
# |  |  + $swp1.10       | |  + $swp1.11       | |  + $swp1.12       |  |
# |  |    ingress-qos-map| |    ingress-qos-map| |    ingress-qos-map|  |
# |  |     0:0 1:1 2:2   | |     0:0 1:1 2:2   | |     0:0 1:1 2:2   |  |
# |  |                   | |                   | |                   |  |
# |  |    BR10           | |    BR11           | |    BR12           |  |
# |  |                   | |                   | |                   |  |
# |  |  + $swp2.10       | |  + $swp2.11       | |  + $swp2.12       |  |
# |  +--|----------------+ +--|----------------+ +--|----------------+  |
# |     \____________________ | ____________________/                   |
# |                          \|/                                        |
# |                           + $swp2                                   |
# |                           | 1Gbps (ethtool or HTB qdisc)            |
# |                           | qdisc ets quanta $W0 $W1 $W2            |
# |                           |           priomap 0 1 2                 |
# +---------------------------|-----------------------------------------+
#                             |
# +---------------------------|-----------------------------------------+
# | H2                        + $h2                                     |
# |      ____________________/|\____________________                    |
# |     /                     |                     \                   |
# |     + $h2.10              + $h2.11              + $h2.12            |
# |       192.0.2.2/28          192.0.2.18/28         192.0.2.34/28     |
# +---------------------------------------------------------------------+

NUM_NETIFS=4
CHECK_TC=yes
source $lib_dir/lib.sh
source $lib_dir/sch_ets_tests.sh

PARENT=root
QDISC_DEV=

sip()
{
	echo 192.0.2.$((16 * $1 + 1))
}

dip()
{
	echo 192.0.2.$((16 * $1 + 2))
}

# Callback from sch_ets_tests.sh
ets_start_traffic()
{
	local dst_mac=$(mac_get $h2)
	local i=$1; shift

	start_traffic $h1.1$i $(sip $i) $(dip $i) $dst_mac
}

ETS_CHANGE_QDISC=

priomap_mode()
{
	echo "Running in priomap mode"
	ets_delete_qdisc
	ETS_CHANGE_QDISC=ets_change_qdisc_priomap
}

classifier_mode()
{
	echo "Running in classifier mode"
	ets_delete_qdisc
	ETS_CHANGE_QDISC=ets_change_qdisc_classifier
}

ets_change_qdisc_priomap()
{
	local dev=$1; shift
	local nstrict=$1; shift
	local priomap=$1; shift
	local quanta=("${@}")

	local op=$(if [[ -n $QDISC_DEV ]]; then echo change; else echo add; fi)

	tc qdisc $op dev $dev $PARENT handle 10: ets			       \
		$(if ((nstrict)); then echo strict $nstrict; fi)	       \
		$(if ((${#quanta[@]})); then echo quanta ${quanta[@]}; fi)     \
		priomap $priomap
	QDISC_DEV=$dev
}

ets_change_qdisc_classifier()
{
	local dev=$1; shift
	local nstrict=$1; shift
	local priomap=$1; shift
	local quanta=("${@}")

	local op=$(if [[ -n $QDISC_DEV ]]; then echo change; else echo add; fi)

	tc qdisc $op dev $dev $PARENT handle 10: ets			       \
		$(if ((nstrict)); then echo strict $nstrict; fi)	       \
		$(if ((${#quanta[@]})); then echo quanta ${quanta[@]}; fi)

	if [[ $op == add ]]; then
		local prio=0
		local band

		for band in $priomap; do
			tc filter add dev $dev parent 10: basic \
				match "meta(priority eq $prio)" \
				flowid 10:$((band + 1))
			((prio++))
		done
	fi
	QDISC_DEV=$dev
}

# Callback from sch_ets_tests.sh
ets_change_qdisc()
{
	if [[ -z "$ETS_CHANGE_QDISC" ]]; then
		exit 1
	fi
	$ETS_CHANGE_QDISC "$@"
}

ets_delete_qdisc()
{
	if [[ -n $QDISC_DEV ]]; then
		tc qdisc del dev $QDISC_DEV $PARENT
		QDISC_DEV=
	fi
}

h1_create()
{
	local i;

	simple_if_init $h1
	mtu_set $h1 9900
	for i in {0..2}; do
		vlan_create $h1 1$i v$h1 $(sip $i)/28
		ip link set dev $h1.1$i type vlan egress 0:$i
	done
}

h1_destroy()
{
	local i

	for i in {0..2}; do
		vlan_destroy $h1 1$i
	done
	mtu_restore $h1
	simple_if_fini $h1
}

h2_create()
{
	local i

	simple_if_init $h2
	mtu_set $h2 9900
	for i in {0..2}; do
		vlan_create $h2 1$i v$h2 $(dip $i)/28
	done
}

h2_destroy()
{
	local i

	for i in {0..2}; do
		vlan_destroy $h2 1$i
	done
	mtu_restore $h2
	simple_if_fini $h2
}

ets_switch_create()
{
	local i

	ip link set dev $swp1 up
	mtu_set $swp1 9900

	ip link set dev $swp2 up
	mtu_set $swp2 9900

	for i in {0..2}; do
		vlan_create $swp1 1$i
		ip link set dev $swp1.1$i type vlan ingress 0:0 1:1 2:2

		vlan_create $swp2 1$i

		ip link add dev br1$i type bridge
		ip link set dev $swp1.1$i master br1$i
		ip link set dev $swp2.1$i master br1$i

		ip link set dev br1$i up
		ip link set dev $swp1.1$i up
		ip link set dev $swp2.1$i up
	done
}

ets_switch_destroy()
{
	local i

	ets_delete_qdisc

	for i in {0..2}; do
		ip link del dev br1$i
		vlan_destroy $swp2 1$i
		vlan_destroy $swp1 1$i
	done

	mtu_restore $swp2
	ip link set dev $swp2 down

	mtu_restore $swp1
	ip link set dev $swp1 down
}

setup_prepare()
{
	h1=${NETIFS[p1]}
	swp1=${NETIFS[p2]}

	swp2=${NETIFS[p3]}
	h2=${NETIFS[p4]}

	put=$swp2
	hut=$h2

	vrf_prepare

	h1_create
	h2_create
	switch_create
}

cleanup()
{
	pre_cleanup

	switch_destroy
	h2_destroy
	h1_destroy

	vrf_cleanup
}

ping_ipv4()
{
	ping_test $h1.10 $(dip 0) " vlan 10"
	ping_test $h1.11 $(dip 1) " vlan 11"
	ping_test $h1.12 $(dip 2) " vlan 12"
}

ets_run()
{
	trap cleanup EXIT

	setup_prepare
	setup_wait

	tests_run

	exit $EXIT_STATUS
}