Commit 641a305b authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-multi-level-qdisc-offload'

Ido Schimmel says:

====================
mlxsw: Multi-level qdisc offload

Petr says:

Currently, mlxsw admits for offload a suitable root qdisc, and its
children. Thus up to two levels of hierarchy are offloaded. Often, this is
enough: one can configure TCs with RED and TCs with a shaper on, and can
even see counters for each TC by looking at a qdisc at a sufficiently
shallow position.

While simple, the system has obvious shortcomings. It is not possible to
configure both RED and shaping on one TC. It is not possible to place a
PRIO below root TBF, which would then be offloaded as port shaper. FIFOs
are only offloaded at root or directly below, which is confusing to users,
because RED and TBF of course have their own FIFO.

This patch set lifts assumptions that prevent offloading multi-level qdisc
trees.

In patch #1, offload of a graft operation is added to TBF. Grafts are
issued as another qdisc is linked to the qdisc in question, and give
drivers a chance to react to the linking. The absence of this event was not
a major issue so far, because TBF was not considered classful, which
changes with this patchset.

The codebase currently assumes that ETS and PRIO are the only classful
qdiscs. The following patches gradually lift this assumption.

In patch #2, calculation of traffic class and priomap of a qdisc is fixed.

Patch #3 fixes handling of future FIFOs. Child FIFO qdiscs may be created
and notified before their parent qdisc exists and therefore need special
handling.

Patches #4, #5 and #6 unify, respectively, child destruction, child
grafting, and cleanup of statistics.

Patch #7 adds a function that validates whether a given qdisc topology is
offloadable.

Finally in patch #8, TBF and RED become classful. At this point, FIFO
qdiscs grafted to an offloaded qdisc should always be offloaded.

Patch #9 adds a selftest to verify some offloadable and unoffloadable qdisc
trees.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents aaa55706 29c1eac2
...@@ -977,6 +977,7 @@ enum tc_tbf_command { ...@@ -977,6 +977,7 @@ enum tc_tbf_command {
TC_TBF_REPLACE, TC_TBF_REPLACE,
TC_TBF_DESTROY, TC_TBF_DESTROY,
TC_TBF_STATS, TC_TBF_STATS,
TC_TBF_GRAFT,
}; };
struct tc_tbf_qopt_offload_replace_params { struct tc_tbf_qopt_offload_replace_params {
...@@ -992,6 +993,7 @@ struct tc_tbf_qopt_offload { ...@@ -992,6 +993,7 @@ struct tc_tbf_qopt_offload {
union { union {
struct tc_tbf_qopt_offload_replace_params replace_params; struct tc_tbf_qopt_offload_replace_params replace_params;
struct tc_qopt_offload_stats stats; struct tc_qopt_offload_stats stats;
u32 child_handle;
}; };
}; };
......
...@@ -184,6 +184,20 @@ static int tbf_offload_dump(struct Qdisc *sch) ...@@ -184,6 +184,20 @@ static int tbf_offload_dump(struct Qdisc *sch)
return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_TBF, &qopt); return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_TBF, &qopt);
} }
static void tbf_offload_graft(struct Qdisc *sch, struct Qdisc *new,
struct Qdisc *old, struct netlink_ext_ack *extack)
{
struct tc_tbf_qopt_offload graft_offload = {
.handle = sch->handle,
.parent = sch->parent,
.child_handle = new->handle,
.command = TC_TBF_GRAFT,
};
qdisc_offload_graft_helper(qdisc_dev(sch), sch, new, old,
TC_SETUP_QDISC_TBF, &graft_offload, extack);
}
/* GSO packet is too big, segment it so that tbf can transmit /* GSO packet is too big, segment it so that tbf can transmit
* each segment in time * each segment in time
*/ */
...@@ -547,6 +561,8 @@ static int tbf_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, ...@@ -547,6 +561,8 @@ static int tbf_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
new = &noop_qdisc; new = &noop_qdisc;
*old = qdisc_replace(sch, new, &q->qdisc); *old = qdisc_replace(sch, new, &q->qdisc);
tbf_offload_graft(sch, new, *old, extack);
return 0; return 0;
} }
......
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Test qdisc offload indication
ALL_TESTS="
test_root
test_etsprio
"
NUM_NETIFS=1
lib_dir=$(dirname $0)/../../../net/forwarding
source $lib_dir/lib.sh
check_not_offloaded()
{
local handle=$1; shift
local h
local offloaded
h=$(qdisc_stats_get $h1 "$handle" .handle)
[[ $h == '"'$handle'"' ]]
check_err $? "Qdisc with handle $handle does not exist"
offloaded=$(qdisc_stats_get $h1 "$handle" .offloaded)
[[ $offloaded == true ]]
check_fail $? "Qdisc with handle $handle offloaded, but should not be"
}
check_all_offloaded()
{
local handle=$1; shift
if [[ ! -z $handle ]]; then
local offloaded=$(qdisc_stats_get $h1 "$handle" .offloaded)
[[ $offloaded == true ]]
check_err $? "Qdisc with handle $handle not offloaded"
fi
local unoffloaded=$(tc q sh dev $h1 invisible |
grep -v offloaded |
sed s/root/parent\ root/ |
cut -d' ' -f 5)
[[ -z $unoffloaded ]]
check_err $? "Qdiscs with following parents not offloaded: $unoffloaded"
pre_cleanup
}
with_ets()
{
local handle=$1; shift
local locus=$1; shift
tc qdisc add dev $h1 $locus handle $handle \
ets bands 8 priomap 7 6 5 4 3 2 1 0
"$@"
tc qdisc del dev $h1 $locus
}
with_prio()
{
local handle=$1; shift
local locus=$1; shift
tc qdisc add dev $h1 $locus handle $handle \
prio bands 8 priomap 7 6 5 4 3 2 1 0
"$@"
tc qdisc del dev $h1 $locus
}
with_red()
{
local handle=$1; shift
local locus=$1; shift
tc qdisc add dev $h1 $locus handle $handle \
red limit 1000000 min 200000 max 300000 probability 0.5 avpkt 1500
"$@"
tc qdisc del dev $h1 $locus
}
with_tbf()
{
local handle=$1; shift
local locus=$1; shift
tc qdisc add dev $h1 $locus handle $handle \
tbf rate 400Mbit burst 128K limit 1M
"$@"
tc qdisc del dev $h1 $locus
}
with_pfifo()
{
local handle=$1; shift
local locus=$1; shift
tc qdisc add dev $h1 $locus handle $handle pfifo limit 100K
"$@"
tc qdisc del dev $h1 $locus
}
with_bfifo()
{
local handle=$1; shift
local locus=$1; shift
tc qdisc add dev $h1 $locus handle $handle bfifo limit 100K
"$@"
tc qdisc del dev $h1 $locus
}
with_drr()
{
local handle=$1; shift
local locus=$1; shift
tc qdisc add dev $h1 $locus handle $handle drr
"$@"
tc qdisc del dev $h1 $locus
}
with_qdiscs()
{
local handle=$1; shift
local parent=$1; shift
local kind=$1; shift
local next_handle=$((handle * 2))
local locus;
if [[ $kind == "--" ]]; then
local cmd=$1; shift
$cmd $(printf %x: $parent) "$@"
else
if ((parent == 0)); then
locus=root
else
locus=$(printf "parent %x:1" $parent)
fi
with_$kind $(printf %x: $handle) "$locus" \
with_qdiscs $next_handle $handle "$@"
fi
}
get_name()
{
local parent=$1; shift
local name=$(echo "" "${@^^}" | tr ' ' -)
if ((parent != 0)); then
kind=$(qdisc_stats_get $h1 $parent: .kind)
kind=${kind%\"}
kind=${kind#\"}
name="-${kind^^}$name"
fi
echo root$name
}
do_test_offloaded()
{
local handle=$1; shift
local parent=$1; shift
RET=0
with_qdiscs $handle $parent "$@" -- check_all_offloaded
log_test $(get_name $parent "$@")" offloaded"
}
do_test_nooffload()
{
local handle=$1; shift
local parent=$1; shift
local name=$(echo "${@^^}" | tr ' ' -)
local kind
RET=0
with_qdiscs $handle $parent "$@" -- check_not_offloaded
log_test $(get_name $parent "$@")" not offloaded"
}
do_test_combinations()
{
local handle=$1; shift
local parent=$1; shift
local cont
local leaf
local fifo
for cont in "" ets prio; do
for leaf in "" red tbf "red tbf" "tbf red"; do
for fifo in "" pfifo bfifo; do
if [[ -z "$cont$leaf$fifo" ]]; then
continue
fi
do_test_offloaded $handle $parent \
$cont $leaf $fifo
done
done
done
for cont in ets prio; do
for leaf in red tbf; do
do_test_nooffload $handle $parent $cont red tbf $leaf
do_test_nooffload $handle $parent $cont tbf red $leaf
done
for leaf in "red red" "tbf tbf"; do
do_test_nooffload $handle $parent $cont $leaf
done
done
do_test_nooffload $handle $parent drr
}
test_root()
{
do_test_combinations 1 0
}
do_test_etsprio()
{
local parent=$1; shift
local tbfpfx=$1; shift
local cont
for cont in ets prio; do
RET=0
with_$cont 8: "$parent" \
with_red 11: "parent 8:1" \
with_red 12: "parent 8:2" \
with_tbf 13: "parent 8:3" \
with_tbf 14: "parent 8:4" \
check_all_offloaded
log_test "root$tbfpfx-ETS-{RED,TBF} offloaded"
RET=0
with_$cont 8: "$parent" \
with_red 81: "parent 8:1" \
with_tbf 811: "parent 81:1" \
with_tbf 84: "parent 8:4" \
with_red 841: "parent 84:1" \
check_all_offloaded
log_test "root$tbfpfx-ETS-{RED-TBF,TBF-RED} offloaded"
RET=0
with_$cont 8: "$parent" \
with_red 81: "parent 8:1" \
with_tbf 811: "parent 81:1" \
with_bfifo 8111: "parent 811:1" \
with_tbf 82: "parent 8:2" \
with_red 821: "parent 82:1" \
with_bfifo 8211: "parent 821:1" \
check_all_offloaded
log_test "root$tbfpfx-ETS-{RED-TBF-bFIFO,TBF-RED-bFIFO} offloaded"
done
}
test_etsprio()
{
do_test_etsprio root ""
}
cleanup()
{
tc qdisc del dev $h1 root &>/dev/null
}
trap cleanup EXIT
h1=${NETIFS[p1]}
tests_run
exit $EXIT_STATUS
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