diff --git a/man/systemd.network.xml b/man/systemd.network.xml index da48dd04f4..f1625dfde4 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2500,6 +2500,38 @@ + + [PIE] Section Options + The [PIE] section manages the queueing discipline + (qdisc) of Proportional Integral controller-Enhanced (PIE). + + + + Parent= + + Specifies the parent Queueing Discipline (qdisc). Takes one of root, + clsact or ingress. Defaults to root. + + + + + Handle= + + Specifies the major number of unique identifier of the qdisc, known as the handle. + Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset. + + + + + PacketLimit= + + Specifies the hard limit on the queue size in number of packets. When this limit is reached, incoming packets are + dropped. An unsigned integer ranges 1 to 4294967294. Defaults to unset and kernel's default is used. + + + + + [StochasticFairBlue] Section Options The [StochasticFairBlue] section manages the queueing discipline @@ -2565,6 +2597,40 @@ + + [BFIFO] Section Options + The [BFIFO] section manages the queueing discipline (qdisc) of + Byte limited Packet First In First Out (bfifo). + + + + Parent= + + Specifies the parent Queueing Discipline (qdisc). Takes one of root, + clsact or ingress. Defaults to root. + + + + + Handle= + + Specifies the major number of unique identifier of the qdisc, known as the handle. + Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset. + + + + + LimitSize= + + Specifies the hard limit on the FIFO size in bytes. The size limit (a buffer size) to prevent it + from overflowing in case it is unable to dequeue packets as quickly as it receives them. When this limit + is reached, incoming packets are dropped. When suffixed with K, M, or G, the specified size is parsed as + Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults to unset and kernel's default is used. + + + + + [PFIFO] Section Options The [PFIFO] section manages the queueing discipline (qdisc) of @@ -2598,6 +2664,54 @@ + + [PFIFOHeadDrop] Section Options + The [PFIFOHeadDrop] section manages the queueing discipline (qdisc) of + Packet First In First Out Head Drop (pfifo_head_drop). + + + + Parent= + + As in [PFIFO] section. + + + + Handle= + + As in [PFIFO] section.. + + + + + PacketLimit= + + As in [PFIFO] section. + + + + + + [PFIFOFast] Section Options + The [PFIFOFast] section manages the queueing discipline (qdisc) of + Packet First In First Out Fast (pfifo_fast). + + + + Parent= + + As in [PFIFO] section. + + + + Handle= + + As in [PFIFO] section.. + + + + + [CAKE] Section Options The [CAKE] section manages the queueing discipline (qdisc) of @@ -2707,6 +2821,68 @@ + + [DeficitRoundRobinScheduler] Section Options + The [DeficitRoundRobinScheduler] section manages the queueing discipline (qdisc) of + Deficit Round Robin Scheduler (DRR). + + + + Parent= + + Specifies the parent Queueing Discipline (qdisc). Takes one of root, + clsact, ingress or a class id. The class id takes the + major and minor number in hexadecimal ranges 1 to ffff separated with a colon + (major:minor). Defaults to root. + + + + + Handle= + + Specifies the major number of unique identifier of the qdisc, known as the handle. + Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset. + + + + + + + [DeficitRoundRobinSchedulerClass] Section Options + The [DeficitRoundRobinSchedulerClass] section manages the traffic control class of + Deficit Round Robin Scheduler (DRR). + + + + Parent= + + Specifies the parent Queueing Discipline (qdisc). Takes one of root, + clsact, ingress or a class id. The class id takes the + major and minor number in hexadecimal ranges 1 to ffff separated with a colon + (major:minor). Defaults to root. + + + + + ClassId= + + Specifies the major and minur number of unique identifier of the class, known as the + class ID. Each number is in hexadecimal ranges 1 to ffff. Defaults to unset. + + + + + Quantum= + + Specifies the amount of bytes a flow is allowed to dequeue before the + scheduler moves to the next class. An unsigned integer ranges 1 to 4294967294. + Defaults to the MTU of the interface. + + + + + + [GenericRandomEarlyDetection] Section Options The [GenericRandomEarlyDetection] section manages the queueing discipline @@ -3070,7 +3246,38 @@ is used. + + + + [HeavyHitterFilter] Section Options + The [HeavyHitterFilter] section manages the queueing discipline + (qdisc) of Heavy Hitter Filter (hhf). + + + + Parent= + + Specifies the parent Queueing Discipline (qdisc). Takes one of root, + clsact or ingress. Defaults to root. + + + + + Handle= + + Specifies the major number of unique identifier of the qdisc, known as the handle. + Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset. + + + + + PacketLimit= + + Specifies the hard limit on the queue size in number of packets. When this limit is reached, incoming packets are + dropped. An unsigned integer ranges 0 to 4294967294. Defaults to unset and kernel's default is used. + + diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index 102cec49a1..720e48e227 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -760,6 +760,10 @@ static const NLType rtnl_tca_option_data_codel_types[] = { [TCA_CODEL_CE_THRESHOLD] = { .type = NETLINK_TYPE_U32 }, }; +static const NLType rtnl_tca_option_data_drr_types[] = { + [TCA_DRR_QUANTUM] = { .type = NETLINK_TYPE_U32 }, +}; + static const NLType rtnl_tca_option_data_fq_types[] = { [TCA_FQ_PLIMIT] = { .type = NETLINK_TYPE_U32 }, [TCA_FQ_FLOW_PLIMIT] = { .type = NETLINK_TYPE_U32 }, @@ -791,6 +795,10 @@ static const NLType rtnl_tca_option_data_gred_types[] = { [TCA_GRED_DPS] = { .size = sizeof(struct tc_gred_sopt) }, }; +static const NLType rtnl_tca_option_data_hhf_types[] = { + [TCA_HHF_BACKLOG_LIMIT] = { .type = NETLINK_TYPE_U32 }, +}; + static const NLType rtnl_tca_option_data_htb_types[] = { [TCA_HTB_PARMS] = { .size = sizeof(struct tc_htb_opt) }, [TCA_HTB_INIT] = { .size = sizeof(struct tc_htb_glob) }, @@ -800,6 +808,10 @@ static const NLType rtnl_tca_option_data_htb_types[] = { [TCA_HTB_CEIL64] = { .type = NETLINK_TYPE_U64 }, }; +static const NLType rtnl_tca_option_data_pie_types[] = { + [TCA_PIE_LIMIT] = { .type = NETLINK_TYPE_U32 }, +}; + static const NLType rtnl_tca_option_data_sfb_types[] = { [TCA_SFB_PARMS] = { .size = sizeof(struct tc_sfb_qopt) }, }; @@ -817,10 +829,13 @@ static const NLType rtnl_tca_option_data_tbf_types[] = { static const char* const nl_union_tca_option_data_table[] = { [NL_UNION_TCA_OPTION_DATA_CAKE] = "cake", [NL_UNION_TCA_OPTION_DATA_CODEL] = "codel", + [NL_UNION_TCA_OPTION_DATA_DRR] = "drr", [NL_UNION_TCA_OPTION_DATA_FQ] = "fq", [NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = "fq_codel", [NL_UNION_TCA_OPTION_DATA_GRED] = "gred", + [NL_UNION_TCA_OPTION_DATA_HHF] = "hhf", [NL_UNION_TCA_OPTION_DATA_HTB] = "htb", + [NL_UNION_TCA_OPTION_DATA_PIE] = "pie", [NL_UNION_TCA_OPTION_DATA_SFB] = "sfb", [NL_UNION_TCA_OPTION_DATA_TBF] = "tbf", }; @@ -832,14 +847,20 @@ static const NLTypeSystem rtnl_tca_option_data_type_systems[] = { .types = rtnl_tca_option_data_cake_types }, [NL_UNION_TCA_OPTION_DATA_CODEL] = { .count = ELEMENTSOF(rtnl_tca_option_data_codel_types), .types = rtnl_tca_option_data_codel_types }, + [NL_UNION_TCA_OPTION_DATA_DRR] = { .count = ELEMENTSOF(rtnl_tca_option_data_drr_types), + .types = rtnl_tca_option_data_drr_types }, [NL_UNION_TCA_OPTION_DATA_FQ] = { .count = ELEMENTSOF(rtnl_tca_option_data_fq_types), .types = rtnl_tca_option_data_fq_types }, [NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = { .count = ELEMENTSOF(rtnl_tca_option_data_fq_codel_types), .types = rtnl_tca_option_data_fq_codel_types }, [NL_UNION_TCA_OPTION_DATA_GRED] = { .count = ELEMENTSOF(rtnl_tca_option_data_gred_types), .types = rtnl_tca_option_data_gred_types }, + [NL_UNION_TCA_OPTION_DATA_HHF] = { .count = ELEMENTSOF(rtnl_tca_option_data_hhf_types), + .types = rtnl_tca_option_data_hhf_types }, [NL_UNION_TCA_OPTION_DATA_HTB] = { .count = ELEMENTSOF(rtnl_tca_option_data_htb_types), .types = rtnl_tca_option_data_htb_types }, + [NL_UNION_TCA_OPTION_DATA_PIE] = { .count = ELEMENTSOF(rtnl_tca_option_data_pie_types), + .types = rtnl_tca_option_data_pie_types }, [NL_UNION_TCA_OPTION_DATA_SFB] = { .count = ELEMENTSOF(rtnl_tca_option_data_sfb_types), .types = rtnl_tca_option_data_sfb_types }, [NL_UNION_TCA_OPTION_DATA_TBF] = { .count = ELEMENTSOF(rtnl_tca_option_data_tbf_types), diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h index edc4a580b8..08b91c78c0 100644 --- a/src/libsystemd/sd-netlink/netlink-types.h +++ b/src/libsystemd/sd-netlink/netlink-types.h @@ -98,10 +98,13 @@ NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_; typedef enum NLUnionTCAOptionData { NL_UNION_TCA_OPTION_DATA_CAKE, NL_UNION_TCA_OPTION_DATA_CODEL, + NL_UNION_TCA_OPTION_DATA_DRR, NL_UNION_TCA_OPTION_DATA_FQ, NL_UNION_TCA_OPTION_DATA_FQ_CODEL, NL_UNION_TCA_OPTION_DATA_GRED, + NL_UNION_TCA_OPTION_DATA_HHF, NL_UNION_TCA_OPTION_DATA_HTB, + NL_UNION_TCA_OPTION_DATA_PIE, NL_UNION_TCA_OPTION_DATA_SFB, NL_UNION_TCA_OPTION_DATA_TBF, _NL_UNION_TCA_OPTION_DATA_MAX, diff --git a/src/network/meson.build b/src/network/meson.build index 57d3772c3e..35b35a76a6 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -111,6 +111,8 @@ sources = files(''' tc/cake.h tc/codel.c tc/codel.h + tc/drr.c + tc/drr.h tc/fifo.c tc/fifo.h tc/fq.c @@ -119,10 +121,14 @@ sources = files(''' tc/fq-codel.h tc/gred.c tc/gred.h + tc/hhf.c + tc/hhf.h tc/htb.c tc/htb.h tc/netem.c tc/netem.h + tc/pie.c + tc/pie.h tc/qdisc.c tc/qdisc.h tc/sfb.c diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 8bf80afddd..e1b9b5687a 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -258,6 +258,9 @@ CAN.TripleSampling, config_parse_tristate, CAN.Termination, config_parse_tristate, 0, offsetof(Network, can_termination) QDisc.Parent, config_parse_qdisc_parent, _QDISC_KIND_INVALID, 0 QDisc.Handle, config_parse_qdisc_handle, _QDISC_KIND_INVALID, 0 +BFIFO.Parent, config_parse_qdisc_parent, QDISC_KIND_BFIFO, 0 +BFIFO.Handle, config_parse_qdisc_handle, QDISC_KIND_BFIFO, 0 +BFIFO.LimitSize, config_parse_bfifo_size, QDISC_KIND_BFIFO, 0 CAKE.Parent, config_parse_qdisc_parent, QDISC_KIND_CAKE, 0 CAKE.Handle, config_parse_qdisc_handle, QDISC_KIND_CAKE, 0 CAKE.Bandwidth, config_parse_cake_bandwidth, QDISC_KIND_CAKE, 0 @@ -269,9 +272,19 @@ ControlledDelay.TargetSec, config_parse_controlled_delay_usec, ControlledDelay.IntervalSec, config_parse_controlled_delay_usec, QDISC_KIND_CODEL, 0 ControlledDelay.CEThresholdSec, config_parse_controlled_delay_usec, QDISC_KIND_CODEL, 0 ControlledDelay.ECN, config_parse_controlled_delay_bool, QDISC_KIND_CODEL, 0 +DeficitRoundRobinScheduler.Parent, config_parse_qdisc_parent, QDISC_KIND_DRR, 0 +DeficitRoundRobinScheduler.Handle, config_parse_qdisc_handle, QDISC_KIND_DRR, 0 +DeficitRoundRobinSchedulerClass.Parent, config_parse_tclass_parent, TCLASS_KIND_DRR, 0 +DeficitRoundRobinSchedulerClass.ClassId, config_parse_tclass_classid, TCLASS_KIND_DRR, 0 +DeficitRoundRobinSchedulerClass.Quantum, config_parse_drr_size, TCLASS_KIND_DRR, 0 PFIFO.Parent, config_parse_qdisc_parent, QDISC_KIND_PFIFO, 0 PFIFO.Handle, config_parse_qdisc_handle, QDISC_KIND_PFIFO, 0 -PFIFO.PacketLimit, config_parse_fifo_size, QDISC_KIND_PFIFO, 0 +PFIFO.PacketLimit, config_parse_pfifo_size, QDISC_KIND_PFIFO, 0 +PFIFOFast.Parent, config_parse_qdisc_parent, QDISC_KIND_PFIFO_FAST, 0 +PFIFOFast.Handle, config_parse_qdisc_handle, QDISC_KIND_PFIFO_FAST, 0 +PFIFOHeadDrop.Parent, config_parse_qdisc_parent, QDISC_KIND_PFIFO_HEAD_DROP, 0 +PFIFOHeadDrop.Handle, config_parse_qdisc_handle, QDISC_KIND_PFIFO_HEAD_DROP, 0 +PFIFOHeadDrop.PacketLimit, config_parse_pfifo_size, QDISC_KIND_PFIFO_HEAD_DROP, 0 FairQueueing.Parent, config_parse_qdisc_parent, QDISC_KIND_FQ, 0 FairQueueing.Handle, config_parse_qdisc_handle, QDISC_KIND_FQ, 0 FairQueueing.PacketLimit, config_parse_fair_queueing_u32, QDISC_KIND_FQ, 0 @@ -298,6 +311,9 @@ GenericRandomEarlyDetection.Handle, config_parse_qdisc_handle, GenericRandomEarlyDetection.VirtualQueues, config_parse_generic_random_early_detection_u32, QDISC_KIND_GRED, 0 GenericRandomEarlyDetection.DefaultVirtualQueue, config_parse_generic_random_early_detection_u32, QDISC_KIND_GRED, 0 GenericRandomEarlyDetection.GenericRIO, config_parse_generic_random_early_detection_bool, QDISC_KIND_GRED, 0 +HeavyHitterFilter.Parent, config_parse_qdisc_parent, QDISC_KIND_HHF, 0 +HeavyHitterFilter.Handle, config_parse_qdisc_handle, QDISC_KIND_HHF, 0 +HeavyHitterFilter.PacketLimit, config_parse_heavy_hitter_filter_packet_limit, QDISC_KIND_HHF, 0 HierarchyTokenBucket.Parent, config_parse_qdisc_parent, QDISC_KIND_HTB, 0 HierarchyTokenBucket.Handle, config_parse_qdisc_handle, QDISC_KIND_HTB, 0 HierarchyTokenBucket.DefaultClass, config_parse_hierarchy_token_bucket_default_class, QDISC_KIND_HTB, 0 @@ -313,6 +329,9 @@ NetworkEmulator.DelayJitterSec, config_parse_network_emulator_delay NetworkEmulator.LossRate, config_parse_network_emulator_rate, QDISC_KIND_NETEM, 0 NetworkEmulator.DuplicateRate, config_parse_network_emulator_rate, QDISC_KIND_NETEM, 0 NetworkEmulator.PacketLimit, config_parse_network_emulator_packet_limit, QDISC_KIND_NETEM, 0 +PIE.Parent, config_parse_qdisc_parent, QDISC_KIND_PIE, 0 +PIE.Handle, config_parse_qdisc_handle, QDISC_KIND_PIE, 0 +PIE.PacketLimit, config_parse_pie_packet_limit, QDISC_KIND_PIE, 0 StochasticFairBlue.Parent, config_parse_qdisc_parent, QDISC_KIND_SFB, 0 StochasticFairBlue.Handle, config_parse_qdisc_handle, QDISC_KIND_SFB, 0 StochasticFairBlue.PacketLimit, config_parse_stochastic_fair_blue_u32, QDISC_KIND_SFB, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 40e4009bec..f99886d5f9 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -486,15 +486,22 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi "TrafficControlQueueingDiscipline\0" "CAN\0" "QDisc\0" + "BFIFO\0" "CAKE\0" "ControlledDelay\0" + "DeficitRoundRobinScheduler\0" + "DeficitRoundRobinSchedulerClass\0" "PFIFO\0" + "PFIFOFast\0" + "PFIFOHeadDrop\0" "FairQueueing\0" "FairQueueingControlledDelay\0" "GenericRandomEarlyDetection\0" + "HeavyHitterFilter\0" "HierarchyTokenBucket\0" "HierarchyTokenBucketClass\0" "NetworkEmulator\0" + "PIE\0" "StochasticFairBlue\0" "StochasticFairnessQueueing\0" "TokenBucketFilter\0" diff --git a/src/network/tc/drr.c b/src/network/tc/drr.c new file mode 100644 index 0000000000..ac64eb8b03 --- /dev/null +++ b/src/network/tc/drr.c @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ + +#include + +#include "alloc-util.h" +#include "conf-parser.h" +#include "drr.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "string-util.h" + +const QDiscVTable drr_vtable = { + .object_size = sizeof(DeficitRoundRobinScheduler), + .tca_kind = "drr", +}; + +static int drr_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) { + DeficitRoundRobinSchedulerClass *drr; + int r; + + assert(link); + assert(tclass); + assert(req); + + drr = TCLASS_TO_DRR(tclass); + + r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "drr"); + if (r < 0) + return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + + if (drr->quantum > 0) { + r = sd_netlink_message_append_u32(req, TCA_DRR_QUANTUM, drr->quantum); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_DRR_QUANTUM, attribute: %m"); + } + + r = sd_netlink_message_close_container(req); + if (r < 0) + return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + + return 0; +} + +int config_parse_drr_size( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL; + DeficitRoundRobinSchedulerClass *drr; + Network *network = data; + uint64_t u; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = tclass_new_static(TCLASS_KIND_DRR, network, filename, section_line, &tclass); + if (r < 0) + return log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to create traffic control class, ignoring assignment: %m"); + + drr = TCLASS_TO_DRR(tclass); + + if (isempty(rvalue)) { + drr->quantum = 0; + + tclass = NULL; + return 0; + } + + r = parse_size(rvalue, 1000, &u); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (u > UINT32_MAX) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + drr->quantum = (uint32_t) u; + + tclass = NULL; + return 0; +} + +const TClassVTable drr_tclass_vtable = { + .object_size = sizeof(DeficitRoundRobinSchedulerClass), + .tca_kind = "drr", + .fill_message = drr_class_fill_message, +}; diff --git a/src/network/tc/drr.h b/src/network/tc/drr.h new file mode 100644 index 0000000000..ff2b658acc --- /dev/null +++ b/src/network/tc/drr.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ +#pragma once + +#include "qdisc.h" + +typedef struct DeficitRoundRobinScheduler { + QDisc meta; +} DeficitRoundRobinScheduler; + +DEFINE_QDISC_CAST(DRR, DeficitRoundRobinScheduler); +extern const QDiscVTable drr_vtable; + +typedef struct DeficitRoundRobinSchedulerClass { + TClass meta; + + uint32_t quantum; +} DeficitRoundRobinSchedulerClass; + +DEFINE_TCLASS_CAST(DRR, DeficitRoundRobinSchedulerClass); +extern const TClassVTable drr_tclass_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_drr_size); diff --git a/src/network/tc/fifo.c b/src/network/tc/fifo.c index 2d2a863e3a..8ef6e513bb 100644 --- a/src/network/tc/fifo.c +++ b/src/network/tc/fifo.c @@ -19,7 +19,19 @@ static int fifo_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) assert(qdisc); assert(req); - fifo = PFIFO(qdisc); + switch(qdisc->kind) { + case QDISC_KIND_PFIFO: + fifo = PFIFO(qdisc); + break; + case QDISC_KIND_BFIFO: + fifo = BFIFO(qdisc); + break; + case QDISC_KIND_PFIFO_HEAD_DROP: + fifo = PFIFO_HEAD_DROP(qdisc); + break; + default: + assert_not_reached("Invalid QDisc kind."); + } opt.limit = fifo->limit; @@ -30,7 +42,7 @@ static int fifo_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) return 0; } -int config_parse_fifo_size( +int config_parse_pfifo_size( const char *unit, const char *filename, unsigned line, @@ -52,14 +64,23 @@ int config_parse_fifo_size( assert(rvalue); assert(data); - r = qdisc_new_static(QDISC_KIND_PFIFO, network, filename, section_line, &qdisc); + r = qdisc_new_static(ltype, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); if (r < 0) return log_syntax(unit, LOG_ERR, filename, line, r, "More than one kind of queueing discipline, ignoring assignment: %m"); - fifo = PFIFO(qdisc); + switch(qdisc->kind) { + case QDISC_KIND_PFIFO: + fifo = PFIFO(qdisc); + break; + case QDISC_KIND_PFIFO_HEAD_DROP: + fifo = PFIFO_HEAD_DROP(qdisc); + break; + default: + assert_not_reached("Invalid QDisc kind."); + } if (isempty(rvalue)) { fifo->limit = 0; @@ -80,8 +101,83 @@ int config_parse_fifo_size( return 0; } +int config_parse_bfifo_size( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + Network *network = data; + FirstInFirstOut *fifo; + uint64_t u; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_BFIFO, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_syntax(unit, LOG_ERR, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + + fifo = BFIFO(qdisc); + + if (isempty(rvalue)) { + fifo->limit = 0; + + qdisc = NULL; + return 0; + } + + r = parse_size(rvalue, 1000, &u); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (u > UINT32_MAX) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + fifo->limit = (uint32_t) u; + + qdisc = NULL; + return 0; +} + const QDiscVTable pfifo_vtable = { .object_size = sizeof(FirstInFirstOut), .tca_kind = "pfifo", .fill_message = fifo_fill_message, }; + +const QDiscVTable bfifo_vtable = { + .object_size = sizeof(FirstInFirstOut), + .tca_kind = "bfifo", + .fill_message = fifo_fill_message, +}; + +const QDiscVTable pfifo_head_drop_vtable = { + .object_size = sizeof(FirstInFirstOut), + .tca_kind = "pfifo_head_drop", + .fill_message = fifo_fill_message, +}; + +const QDiscVTable pfifo_fast_vtable = { + .object_size = sizeof(FirstInFirstOut), + .tca_kind = "pfifo_fast", +}; diff --git a/src/network/tc/fifo.h b/src/network/tc/fifo.h index 02976568dd..e4c976b942 100644 --- a/src/network/tc/fifo.h +++ b/src/network/tc/fifo.h @@ -12,6 +12,14 @@ typedef struct FirstInFirstOut { } FirstInFirstOut; DEFINE_QDISC_CAST(PFIFO, FirstInFirstOut); -extern const QDiscVTable pfifo_vtable; +DEFINE_QDISC_CAST(BFIFO, FirstInFirstOut); +DEFINE_QDISC_CAST(PFIFO_HEAD_DROP, FirstInFirstOut); +DEFINE_QDISC_CAST(PFIFO_FAST, FirstInFirstOut); -CONFIG_PARSER_PROTOTYPE(config_parse_fifo_size); +extern const QDiscVTable pfifo_vtable; +extern const QDiscVTable bfifo_vtable; +extern const QDiscVTable pfifo_head_drop_vtable; +extern const QDiscVTable pfifo_fast_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_pfifo_size); +CONFIG_PARSER_PROTOTYPE(config_parse_bfifo_size); diff --git a/src/network/tc/hhf.c b/src/network/tc/hhf.c new file mode 100644 index 0000000000..a9d0bdad2f --- /dev/null +++ b/src/network/tc/hhf.c @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ + +#include + +#include "alloc-util.h" +#include "conf-parser.h" +#include "hhf.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "string-util.h" +#include "util.h" + +static int heavy_hitter_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { + HeavyHitterFilter *hhf; + int r; + + assert(link); + assert(qdisc); + assert(req); + + hhf = HHF(qdisc); + + r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "hhf"); + if (r < 0) + return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + + if (hhf->packet_limit > 0) { + r = sd_netlink_message_append_u32(req, TCA_HHF_BACKLOG_LIMIT, hhf->packet_limit); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_HHF_BACKLOG_LIMIT attribute: %m"); + } + + r = sd_netlink_message_close_container(req); + if (r < 0) + return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + + return 0; +} + +int config_parse_heavy_hitter_filter_packet_limit( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + HeavyHitterFilter *hhf; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_HHF, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_syntax(unit, LOG_ERR, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + + hhf = HHF(qdisc); + + if (isempty(rvalue)) { + hhf->packet_limit = 0; + + qdisc = NULL; + return 0; + } + + r = safe_atou32(rvalue, &hhf->packet_limit); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + qdisc = NULL; + + return 0; +} + +const QDiscVTable hhf_vtable = { + .object_size = sizeof(HeavyHitterFilter), + .tca_kind = "hhf", + .fill_message = heavy_hitter_filter_fill_message, +}; diff --git a/src/network/tc/hhf.h b/src/network/tc/hhf.h new file mode 100644 index 0000000000..a555998eee --- /dev/null +++ b/src/network/tc/hhf.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ +#pragma once + +#include "conf-parser.h" +#include "qdisc.h" + +typedef struct HeavyHitterFilter { + QDisc meta; + + uint32_t packet_limit; +} HeavyHitterFilter; + +DEFINE_QDISC_CAST(HHF, HeavyHitterFilter); +extern const QDiscVTable hhf_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_heavy_hitter_filter_packet_limit); diff --git a/src/network/tc/pie.c b/src/network/tc/pie.c new file mode 100644 index 0000000000..e92ced1343 --- /dev/null +++ b/src/network/tc/pie.c @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ + +#include + +#include "alloc-util.h" +#include "conf-parser.h" +#include "pie.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "string-util.h" + +static int pie_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { + proportional_integral_controller_enhanced *pie; + int r; + + assert(link); + assert(qdisc); + assert(req); + + pie = PIE(qdisc); + + r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "pie"); + if (r < 0) + return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + + if (pie->packet_limit > 0) { + r = sd_netlink_message_append_u32(req, TCA_PIE_LIMIT, pie->packet_limit); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_PIE_PLIMIT attribute: %m"); + } + + r = sd_netlink_message_close_container(req); + if (r < 0) + return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + + return 0; +} + +int config_parse_pie_packet_limit( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + proportional_integral_controller_enhanced *pie; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_PIE, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_syntax(unit, LOG_ERR, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + + pie = PIE(qdisc); + + if (isempty(rvalue)) { + pie->packet_limit = 0; + + qdisc = NULL; + return 0; + } + + r = safe_atou32(rvalue, &pie->packet_limit); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + qdisc = NULL; + + return 0; +} + +const QDiscVTable pie_vtable = { + .object_size = sizeof(proportional_integral_controller_enhanced), + .tca_kind = "pie", + .fill_message = pie_fill_message, +}; diff --git a/src/network/tc/pie.h b/src/network/tc/pie.h new file mode 100644 index 0000000000..44214f1ba1 --- /dev/null +++ b/src/network/tc/pie.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ +#pragma once + +#include "conf-parser.h" +#include "qdisc.h" + +typedef struct proportional_integral_controller_enhanced { + QDisc meta; + + uint32_t packet_limit; +} proportional_integral_controller_enhanced; + +DEFINE_QDISC_CAST(PIE, proportional_integral_controller_enhanced); +extern const QDiscVTable pie_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_pie_packet_limit); diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index 31b6a716c4..543f71330f 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -16,14 +16,20 @@ #include "tc-util.h" const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = { + [QDISC_KIND_BFIFO] = &bfifo_vtable, [QDISC_KIND_CAKE] = &cake_vtable, [QDISC_KIND_CODEL] = &codel_vtable, + [QDISC_KIND_DRR] = &drr_vtable, [QDISC_KIND_FQ] = &fq_vtable, [QDISC_KIND_FQ_CODEL] = &fq_codel_vtable, [QDISC_KIND_GRED] = &gred_vtable, + [QDISC_KIND_HHF] = &hhf_vtable, [QDISC_KIND_HTB] = &htb_vtable, [QDISC_KIND_NETEM] = &netem_vtable, + [QDISC_KIND_PIE] = &pie_vtable, [QDISC_KIND_PFIFO] = &pfifo_vtable, + [QDISC_KIND_PFIFO_FAST] = &pfifo_fast_vtable, + [QDISC_KIND_PFIFO_HEAD_DROP] = &pfifo_head_drop_vtable, [QDISC_KIND_SFB] = &sfb_vtable, [QDISC_KIND_SFQ] = &sfq_vtable, [QDISC_KIND_TBF] = &tbf_vtable, diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h index 403d6a5b53..5c43d7a838 100644 --- a/src/network/tc/qdisc.h +++ b/src/network/tc/qdisc.h @@ -9,14 +9,20 @@ #include "tc.h" typedef enum QDiscKind { + QDISC_KIND_BFIFO, QDISC_KIND_CAKE, QDISC_KIND_CODEL, + QDISC_KIND_DRR, QDISC_KIND_FQ, QDISC_KIND_FQ_CODEL, QDISC_KIND_GRED, + QDISC_KIND_HHF, QDISC_KIND_HTB, QDISC_KIND_NETEM, QDISC_KIND_PFIFO, + QDISC_KIND_PFIFO_FAST, + QDISC_KIND_PFIFO_HEAD_DROP, + QDISC_KIND_PIE, QDISC_KIND_SFB, QDISC_KIND_SFQ, QDISC_KIND_TBF, @@ -84,8 +90,11 @@ CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_handle); #include "fq-codel.h" #include "fq.h" #include "gred.h" +#include "hhf.h" #include "htb.h" +#include "pie.h" #include "netem.h" +#include "drr.h" #include "sfb.h" #include "sfq.h" #include "tbf.h" diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c index 87f5bcf704..219ffa2ea6 100644 --- a/src/network/tc/tclass.c +++ b/src/network/tc/tclass.c @@ -16,6 +16,7 @@ #include "tclass.h" const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = { + [TCLASS_KIND_DRR] = &drr_tclass_vtable, [TCLASS_KIND_HTB] = &htb_tclass_vtable, }; diff --git a/src/network/tc/tclass.h b/src/network/tc/tclass.h index 9698e582fa..217dbfe001 100644 --- a/src/network/tc/tclass.h +++ b/src/network/tc/tclass.h @@ -9,6 +9,7 @@ #include "tc.h" typedef enum TClassKind { + TCLASS_KIND_DRR, TCLASS_KIND_HTB, _TCLASS_KIND_MAX, _TCLASS_KIND_INVALID = -1, @@ -64,4 +65,5 @@ DEFINE_TC_CAST(TCLASS, TClass); CONFIG_PARSER_PROTOTYPE(config_parse_tclass_parent); CONFIG_PARSER_PROTOTYPE(config_parse_tclass_classid); +#include "drr.h" #include "htb.h" diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index b97daca959..a729adcc86 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -356,10 +356,21 @@ ClassId= Priority= Rate= CeilRate= +[BFIFO] +Parent= +Handle= +LimitSize= [PFIFO] Parent= Handle= PacketLimit= +[PFIFOHeadDrop] +Parent= +Handle= +PacketLimit= +[PFIFOFast] +Parent= +Handle= [GenericRandomEarlyDetection] Parent= Handle= @@ -370,3 +381,18 @@ GenericRIO= Parent= Handle= PacketLimit= +[PIE] +Parent= +Handle= +PacketLimit= +[DeficitRoundRobinScheduler] +Parent= +Handle= +[DeficitRoundRobinSchedulerClass] +Parent= +ClassId= +Quantum= +[HeavyHitterFilter] +Parent= +Handle= +PacketLimit= diff --git a/test/test-network/conf/25-qdisc-clsact-and-htb.network b/test/test-network/conf/25-qdisc-clsact-and-htb.network index 039a2ffa65..0d6815710c 100644 --- a/test/test-network/conf/25-qdisc-clsact-and-htb.network +++ b/test/test-network/conf/25-qdisc-clsact-and-htb.network @@ -160,3 +160,38 @@ CeilRate=0.5M Parent=2:39 Handle=0039 PacketLimit=200000 + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:003a +Priority=1 +Rate=1M +CeilRate=0.5M + +[BFIFO] +Parent=2:3a +Handle=003a +LimitSize=1M + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:003b +Priority=1 +Rate=1M +CeilRate=0.5M + +[PFIFOHeadDrop] +Parent=2:3b +Handle=003b +PacketLimit=1023 + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:003c +Priority=1 +Rate=1M +CeilRate=0.5M + +[PFIFOFast] +Parent=2:3c +Handle=003c diff --git a/test/test-network/conf/25-qdisc-drr.network b/test/test-network/conf/25-qdisc-drr.network new file mode 100644 index 0000000000..4632740061 --- /dev/null +++ b/test/test-network/conf/25-qdisc-drr.network @@ -0,0 +1,15 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +Address=10.1.2.3/16 + +[DeficitRoundRobinScheduler] +Parent=root +Handle=0002 + +[DeficitRoundRobinSchedulerClass] +Parent=root +ClassId=0002:0030 +Quantum=2000 diff --git a/test/test-network/conf/25-qdisc-hhf.network b/test/test-network/conf/25-qdisc-hhf.network new file mode 100644 index 0000000000..950536259f --- /dev/null +++ b/test/test-network/conf/25-qdisc-hhf.network @@ -0,0 +1,11 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +Address=10.1.2.3/16 + +[HeavyHitterFilter] +Parent=root +Handle=3a +PacketLimit=1022 diff --git a/test/test-network/conf/25-qdisc-pie.network b/test/test-network/conf/25-qdisc-pie.network new file mode 100644 index 0000000000..d41ceba3ef --- /dev/null +++ b/test/test-network/conf/25-qdisc-pie.network @@ -0,0 +1,11 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +Address=10.1.2.3/16 + +[PIE] +Parent=root +Handle=3a +PacketLimit=200000 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index fb0048b3e6..5df1fed985 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -169,6 +169,30 @@ def expectedFailureIfCAKEIsNotAvailable(): return f +def expectedFailureIfPIEIsNotAvailable(): + def f(func): + call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL) + rc = call('tc qdisc add dev dummy98 parent root pie', stderr=subprocess.DEVNULL) + call('ip link del dummy98', stderr=subprocess.DEVNULL) + if rc == 0: + return func + else: + return unittest.expectedFailure(func) + + return f + +def expectedFailureIfHHFIsNotAvailable(): + def f(func): + call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL) + rc = call('tc qdisc add dev dummy98 parent root hhf', stderr=subprocess.DEVNULL) + call('ip link del dummy98', stderr=subprocess.DEVNULL) + if rc == 0: + return func + else: + return unittest.expectedFailure(func) + + return f + def setUpModule(): global running_units @@ -1635,7 +1659,10 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): '25-nexthop.network', '25-qdisc-cake.network', '25-qdisc-clsact-and-htb.network', + '25-qdisc-drr.network', + '25-qdisc-hhf.network', '25-qdisc-ingress-netem-compat.network', + '25-qdisc-pie.network', '25-route-ipv6-src.network', '25-route-static.network', '25-route-vrf.network', @@ -2316,6 +2343,14 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'qdisc sfb 39: parent 2:39') self.assertRegex(output, 'limit 200000') + self.assertRegex(output, 'qdisc bfifo 3a: parent 2:3a') + self.assertRegex(output, 'limit 1000000') + + self.assertRegex(output, 'qdisc pfifo_head_drop 3b: parent 2:3b') + self.assertRegex(output, 'limit 1023p') + + self.assertRegex(output, 'qdisc pfifo_fast 3c: parent 2:3c') + output = check_output('tc class show dev dummy98') print(output) self.assertRegex(output, 'class htb 2:30 root leaf 30:') @@ -2328,8 +2363,24 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'class htb 2:37 root leaf 37:') self.assertRegex(output, 'class htb 2:38 root leaf 38:') self.assertRegex(output, 'class htb 2:39 root leaf 39:') + self.assertRegex(output, 'class htb 2:3a root leaf 3a:') + self.assertRegex(output, 'class htb 2:3b root leaf 3b:') + self.assertRegex(output, 'class htb 2:3c root leaf 3c:') self.assertRegex(output, 'prio 1 rate 1Mbit ceil 500Kbit') + def test_qdisc2(self): + copy_unit_to_networkd_unit_path('25-qdisc-drr.network', '12-dummy.netdev') + start_networkd() + + self.wait_online(['dummy98:routable']) + + output = check_output('tc qdisc show dev dummy98') + print(output) + self.assertRegex(output, 'qdisc drr 2: root') + output = check_output('tc class show dev dummy98') + print(output) + self.assertRegex(output, 'class drr 2:30 root quantum 2000b') + @expectedFailureIfCAKEIsNotAvailable() def test_qdisc_cake(self): copy_unit_to_networkd_unit_path('25-qdisc-cake.network', '12-dummy.netdev') @@ -2342,6 +2393,28 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'bandwidth 500Mbit') self.assertRegex(output, 'overhead 128') + @expectedFailureIfPIEIsNotAvailable() + def test_qdisc_pie(self): + copy_unit_to_networkd_unit_path('25-qdisc-pie.network', '12-dummy.netdev') + start_networkd() + self.wait_online(['dummy98:routable']) + + output = check_output('tc qdisc show dev dummy98') + print(output) + self.assertRegex(output, 'qdisc pie 3a: root') + self.assertRegex(output, 'limit 200000') + + @expectedFailureIfHHFIsNotAvailable() + def test_qdisc_hhf(self): + copy_unit_to_networkd_unit_path('25-qdisc-hhf.network', '12-dummy.netdev') + start_networkd() + self.wait_online(['dummy98:routable']) + + output = check_output('tc qdisc show dev dummy98') + print(output) + self.assertRegex(output, 'qdisc hhf 3a: root') + self.assertRegex(output, 'limit 1022p') + class NetworkdStateFileTests(unittest.TestCase, Utilities): links = [ 'dummy98',