diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 734a4f7c0b2..cc6a31484f2 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -6064,6 +6064,16 @@ ServerAddress=192.168.0.1/24 + + [BandMultiQueueing] Section Options + The [BandMultiQueueing] section manages the queueing discipline (qdisc) of Band Multi Queueing (multiq). + + + + + + + [HeavyHitterFilter] Section Options The [HeavyHitterFilter] section manages the queueing discipline (qdisc) of Heavy Hitter Filter diff --git a/src/network/meson.build b/src/network/meson.build index 275542daa27..3edcd48c83e 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -93,6 +93,7 @@ sources = files( 'tc/gred.c', 'tc/hhf.c', 'tc/htb.c', + 'tc/multiq.c', 'tc/netem.c', 'tc/pie.c', 'tc/qdisc.c', diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 0957eeef6ed..95fe0275a9b 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -552,6 +552,8 @@ HierarchyTokenBucketClass.Rate, config_parse_hierarchy_token_bucket HierarchyTokenBucketClass.CeilRate, config_parse_hierarchy_token_bucket_class_rate, TCLASS_KIND_HTB, 0 HierarchyTokenBucketClass.BufferBytes, config_parse_hierarchy_token_bucket_class_size, TCLASS_KIND_HTB, 0 HierarchyTokenBucketClass.CeilBufferBytes, config_parse_hierarchy_token_bucket_class_size, TCLASS_KIND_HTB, 0 +BandMultiQueueing.Parent, config_parse_qdisc_parent, QDISC_KIND_MULTIQ, 0 +BandMultiQueueing.Handle, config_parse_qdisc_handle, QDISC_KIND_MULTIQ, 0 NetworkEmulator.Parent, config_parse_qdisc_parent, QDISC_KIND_NETEM, 0 NetworkEmulator.Handle, config_parse_qdisc_handle, QDISC_KIND_NETEM, 0 NetworkEmulator.DelaySec, config_parse_network_emulator_delay, QDISC_KIND_NETEM, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index ecd54a3829d..2c2f8ad9390 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -551,6 +551,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi "HeavyHitterFilter\0" "HierarchyTokenBucket\0" "HierarchyTokenBucketClass\0" + "BandMultiQueueing\0" "NetworkEmulator\0" "PFIFO\0" "PFIFOFast\0" diff --git a/src/network/tc/multiq.c b/src/network/tc/multiq.c new file mode 100644 index 00000000000..c70d8c59061 --- /dev/null +++ b/src/network/tc/multiq.c @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "multiq.h" + +static int multi_queueing_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { + struct tc_multiq_qopt opt = {}; + + assert(req); + + /* It looks weird, but the multiq qdisc initialization wants to receive a tc_multiq_qopt attr even + * though it doesn't do anything with it. */ + return sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(opt)); +} + +const QDiscVTable multiq_vtable = { + .object_size = sizeof(BandMultiQueueing), + .tca_kind = "multiq", + .fill_message = multi_queueing_fill_message, +}; diff --git a/src/network/tc/multiq.h b/src/network/tc/multiq.h new file mode 100644 index 00000000000..e53ed57c716 --- /dev/null +++ b/src/network/tc/multiq.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "qdisc.h" + +typedef struct BandMultiQueueing { + QDisc meta; +} BandMultiQueueing; + +DEFINE_QDISC_CAST(MULTIQ, BandMultiQueueing); +extern const QDiscVTable multiq_vtable; diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index 0f89d844f58..5e8f97a7851 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -30,6 +30,7 @@ const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = { [QDISC_KIND_GRED] = &gred_vtable, [QDISC_KIND_HHF] = &hhf_vtable, [QDISC_KIND_HTB] = &htb_vtable, + [QDISC_KIND_MULTIQ] = &multiq_vtable, [QDISC_KIND_NETEM] = &netem_vtable, [QDISC_KIND_PIE] = &pie_vtable, [QDISC_KIND_QFQ] = &qfq_vtable, diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h index 50a8f4ead19..83853dcaa74 100644 --- a/src/network/tc/qdisc.h +++ b/src/network/tc/qdisc.h @@ -21,6 +21,7 @@ typedef enum QDiscKind { QDISC_KIND_GRED, QDISC_KIND_HHF, QDISC_KIND_HTB, + QDISC_KIND_MULTIQ, QDISC_KIND_NETEM, QDISC_KIND_PFIFO, QDISC_KIND_PFIFO_FAST, @@ -106,6 +107,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_handle); #include "gred.h" #include "hhf.h" #include "htb.h" +#include "multiq.h" #include "pie.h" #include "qfq.h" #include "netem.h" diff --git a/test/test-network/conf/25-qdisc-multiq.network b/test/test-network/conf/25-qdisc-multiq.network new file mode 100644 index 00000000000..a805c77124d --- /dev/null +++ b/test/test-network/conf/25-qdisc-multiq.network @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=testtun99 +Name=testtap99 + +[Network] +LinkLocalAddressing=yes +IPv6AcceptRA=no + +[BandMultiQueueing] +Parent=root +Handle=0002 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 1b61038d09e..a2b4eb40b23 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -4648,6 +4648,16 @@ class NetworkdTCTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'qdisc ingress') + @expectedFailureIfModuleIsNotAvailable('sch_multiq') + def test_qdisc_multiq(self): + copy_network_unit('25-tun.netdev', '25-tap.netdev', '25-qdisc-multiq.network') + start_networkd() + self.wait_online('testtun99:degraded', 'testtap99:degraded') + + output = check_output('tc qdisc show dev testtun99') + print(output) + self.assertIn('qdisc multiq 2: root', output) + @expectedFailureIfModuleIsNotAvailable('sch_netem') def test_qdisc_netem(self): copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',