# SPDX-License-Identifier: GPL-2.0

# Global interface:
#  $put -- port under test (e.g. $swp2)
#  collect_stats($streams...) -- A function to get stats for individual streams
#  ets_start_traffic($band) -- Start traffic for this band
#  ets_change_qdisc($op, $dev, $nstrict, $quanta...) -- Add or change qdisc

# WS describes the Qdisc configuration. It has one value per band (so the
# number of array elements indicates the number of bands). If the value is
# 0, it is a strict band, otherwise the it's a DRR band and the value is
# that band's quantum.
declare -a WS

qdisc_describe()
{
	local nbands=${#WS[@]}
	local nstrict=0
	local i

	for ((i = 0; i < nbands; i++)); do
		if ((!${WS[$i]})); then
			: $((nstrict++))
		fi
	done

	echo -n "ets bands $nbands"
	if ((nstrict)); then
		echo -n " strict $nstrict"
	fi
	if ((nstrict < nbands)); then
		echo -n " quanta"
		for ((i = nstrict; i < nbands; i++)); do
			echo -n " ${WS[$i]}"
		done
	fi
}

__strict_eval()
{
	local desc=$1; shift
	local d=$1; shift
	local total=$1; shift
	local above=$1; shift

	RET=0

	if ((! total)); then
		check_err 1 "No traffic observed"
		log_test "$desc"
		return
	fi

	local ratio=$(echo "scale=2; 100 * $d / $total" | bc -l)
	if ((above)); then
		test $(echo "$ratio > 95.0" | bc -l) -eq 1
		check_err $? "Not enough traffic"
		log_test "$desc"
		log_info "Expected ratio >95% Measured ratio $ratio"
	else
		test $(echo "$ratio < 5" | bc -l) -eq 1
		check_err $? "Too much traffic"
		log_test "$desc"
		log_info "Expected ratio <5% Measured ratio $ratio"
	fi
}

strict_eval()
{
	__strict_eval "$@" 1
}

notraf_eval()
{
	__strict_eval "$@" 0
}

__ets_dwrr_test()
{
	local -a streams=("$@")

	local low_stream=${streams[0]}
	local seen_strict=0
	local -a t0 t1 d
	local stream
	local total
	local i

	echo "Testing $(qdisc_describe), streams ${streams[@]}"

	for stream in ${streams[@]}; do
		ets_start_traffic $stream
	done

	sleep 10

	t0=($(collect_stats "${streams[@]}"))

	sleep 10

	t1=($(collect_stats "${streams[@]}"))
	d=($(for ((i = 0; i < ${#streams[@]}; i++)); do
		 echo $((${t1[$i]} - ${t0[$i]}))
	     done))
	total=$(echo ${d[@]} | sed 's/ /+/g' | bc)

	for ((i = 0; i < ${#streams[@]}; i++)); do
		local stream=${streams[$i]}
		if ((seen_strict)); then
			notraf_eval "band $stream" ${d[$i]} $total
		elif ((${WS[$stream]} == 0)); then
			strict_eval "band $stream" ${d[$i]} $total
			seen_strict=1
		elif ((stream == low_stream)); then
			# Low stream is used as DWRR evaluation reference.
			continue
		else
			multipath_eval "bands $low_stream:$stream" \
				       ${WS[$low_stream]} ${WS[$stream]} \
				       ${d[0]} ${d[$i]}
		fi
	done

	for stream in ${streams[@]}; do
		stop_traffic
	done
}

ets_dwrr_test_012()
{
	__ets_dwrr_test 0 1 2
}

ets_dwrr_test_01()
{
	__ets_dwrr_test 0 1
}

ets_dwrr_test_12()
{
	__ets_dwrr_test 1 2
}

ets_qdisc_setup()
{
	local dev=$1; shift
	local nstrict=$1; shift
	local -a quanta=("$@")

	local ndwrr=${#quanta[@]}
	local nbands=$((nstrict + ndwrr))
	local nstreams=$(if ((nbands > 3)); then echo 3; else echo $nbands; fi)
	local priomap=$(seq 0 $((nstreams - 1)))
	local i

	WS=($(
		for ((i = 0; i < nstrict; i++)); do
			echo 0
		done
		for ((i = 0; i < ndwrr; i++)); do
			echo ${quanta[$i]}
		done
	))

	ets_change_qdisc $dev $nstrict "$priomap" ${quanta[@]}
}

ets_set_dwrr_uniform()
{
	ets_qdisc_setup $put 0 3300 3300 3300
}

ets_set_dwrr_varying()
{
	ets_qdisc_setup $put 0 5000 3500 1500
}

ets_set_strict()
{
	ets_qdisc_setup $put 3
}

ets_set_mixed()
{
	ets_qdisc_setup $put 1 5000 2500 1500
}

ets_change_quantum()
{
	tc class change dev $put classid 10:2 ets quantum 8000
	WS[1]=8000
}

ets_set_dwrr_two_bands()
{
	ets_qdisc_setup $put 0 5000 2500
}

ets_test_strict()
{
	ets_set_strict
	ets_dwrr_test_01
	ets_dwrr_test_12
}

ets_test_mixed()
{
	ets_set_mixed
	ets_dwrr_test_01
	ets_dwrr_test_12
}

ets_test_dwrr()
{
	ets_set_dwrr_uniform
	ets_dwrr_test_012
	ets_set_dwrr_varying
	ets_dwrr_test_012
	ets_change_quantum
	ets_dwrr_test_012
	ets_set_dwrr_two_bands
	ets_dwrr_test_01
}