From 4d7ddaf97bb18cd3d75aaf7814e2ec267256ace2 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 3 Jun 2020 15:07:45 +0900 Subject: [PATCH] network: tc: introduce [QuickFairQueueingClass] section --- man/systemd.network.xml | 28 +++ src/network/networkd-network-gperf.gperf | 4 + src/network/networkd-network.c | 1 + src/network/tc/qfq.c | 160 ++++++++++++++++++ src/network/tc/qfq.h | 14 ++ src/network/tc/tclass.c | 1 + src/network/tc/tclass.h | 2 + .../fuzz-network-parser/directives.network | 5 + 8 files changed, 215 insertions(+) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 472172094d4..a4ca67a27f7 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -3263,6 +3263,34 @@ + + [QuickFairQueueingClass] Section Options + The [QuickFairQueueingClass] section manages the traffic control class of + Quick Fair Queueing (qfq). + + + + + + + Weight= + + Specifies the weight of the class. Takse an integer in the range 1..1023. Defaults to + unset in which case the kernel default is used. + + + + + MaxPacketSize= + + Specifies the maximum packet size in bytes for the class. When suffixed with K, M, or G, the specified + size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000. When unset, + the kernel default is used. + + + + + [BridgeVLAN] Section Options The [BridgeVLAN] section manages the VLAN ID configuration of a bridge port and accepts diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index a27a02ca244..39188906648 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -314,6 +314,10 @@ PFIFOHeadDrop.Handle, config_parse_qdisc_handle, PFIFOHeadDrop.PacketLimit, config_parse_pfifo_size, QDISC_KIND_PFIFO_HEAD_DROP, 0 QuickFairQueueing.Parent, config_parse_qdisc_parent, QDISC_KIND_QFQ, 0 QuickFairQueueing.Handle, config_parse_qdisc_handle, QDISC_KIND_QFQ, 0 +QuickFairQueueingClass.Parent, config_parse_tclass_parent, TCLASS_KIND_QFQ, 0 +QuickFairQueueingClass.ClassId, config_parse_tclass_classid, TCLASS_KIND_QFQ, 0 +QuickFairQueueingClass.Weight, config_parse_quick_fair_queueing_weight, TCLASS_KIND_QFQ, 0 +QuickFairQueueingClass.MaxPacketSize, config_parse_quick_fair_queueing_max_packet, TCLASS_KIND_QFQ, 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 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index fc8581c983d..b657b2401c2 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -520,6 +520,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi "PFIFOHeadDrop\0" "PIE\0" "QuickFairQueueing\0" + "QuickFairQueueingClass\0" "StochasticFairBlue\0" "StochasticFairnessQueueing\0" "TokenBucketFilter\0" diff --git a/src/network/tc/qfq.c b/src/network/tc/qfq.c index 8084cda8012..71d5b15e81c 100644 --- a/src/network/tc/qfq.c +++ b/src/network/tc/qfq.c @@ -1,10 +1,170 @@ /* SPDX-License-Identifier: LGPL-2.1+ * Copyright © 2020 VMware, Inc. */ +#include + +#include "parse-util.h" #include "qdisc.h" #include "qfq.h" +#include "string-util.h" + +#define QFQ_MAX_WEIGHT (1 << 10) +#define QFQ_MIN_MAX_PACKET 512 +#define QFQ_MAX_MAX_PACKET (1 << 16) const QDiscVTable qfq_vtable = { .object_size = sizeof(QuickFairQueueing), .tca_kind = "qfq", }; + +static int quick_fair_queueing_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) { + QuickFairQueueingClass *qfq; + int r; + + assert(link); + assert(tclass); + assert(req); + + qfq = TCLASS_TO_QFQ(tclass); + + r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "qfq"); + if (r < 0) + return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + + if (qfq->weight > 0) { + r = sd_netlink_message_append_u32(req, TCA_QFQ_WEIGHT, qfq->weight); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_QFQ_WEIGHT attribute: %m"); + } + + if (qfq->max_packet > 0) { + r = sd_netlink_message_append_u32(req, TCA_QFQ_LMAX, qfq->max_packet); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_QFQ_LMAX 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_quick_fair_queueing_weight( + 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; + QuickFairQueueingClass *qfq; + Network *network = data; + uint32_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = tclass_new_static(TCLASS_KIND_QFQ, 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"); + + qfq = TCLASS_TO_QFQ(tclass); + + if (isempty(rvalue)) { + qfq->weight = 0; + tclass = NULL; + return 0; + } + + r = safe_atou32(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + if (v == 0 || v > QFQ_MAX_WEIGHT) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + qfq->weight = v; + tclass = NULL; + + return 0; +} + +int config_parse_quick_fair_queueing_max_packet( + 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; + QuickFairQueueingClass *qfq; + Network *network = data; + uint64_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = tclass_new_static(TCLASS_KIND_QFQ, 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"); + + qfq = TCLASS_TO_QFQ(tclass); + + if (isempty(rvalue)) { + qfq->max_packet = 0; + tclass = NULL; + return 0; + } + + r = parse_size(rvalue, 1000, &v); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + if (v < QFQ_MIN_MAX_PACKET || v > QFQ_MAX_MAX_PACKET) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + qfq->max_packet = (uint32_t) v; + tclass = NULL; + + return 0; +} + +const TClassVTable qfq_tclass_vtable = { + .object_size = sizeof(QuickFairQueueingClass), + .tca_kind = "qfq", + .fill_message = quick_fair_queueing_class_fill_message, +}; diff --git a/src/network/tc/qfq.h b/src/network/tc/qfq.h index aa1bad219b4..10bab3e642d 100644 --- a/src/network/tc/qfq.h +++ b/src/network/tc/qfq.h @@ -2,6 +2,7 @@ * Copyright © 2020 VMware, Inc. */ #pragma once +#include "conf-parser.h" #include "qdisc.h" typedef struct QuickFairQueueing { @@ -10,3 +11,16 @@ typedef struct QuickFairQueueing { DEFINE_QDISC_CAST(QFQ, QuickFairQueueing); extern const QDiscVTable qfq_vtable; + +typedef struct QuickFairQueueingClass { + TClass meta; + + uint32_t weight; + uint32_t max_packet; +} QuickFairQueueingClass; + +DEFINE_TCLASS_CAST(QFQ, QuickFairQueueingClass); +extern const TClassVTable qfq_tclass_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_quick_fair_queueing_weight); +CONFIG_PARSER_PROTOTYPE(config_parse_quick_fair_queueing_max_packet); diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c index 219ffa2ea6f..4f75e1b42dc 100644 --- a/src/network/tc/tclass.c +++ b/src/network/tc/tclass.c @@ -18,6 +18,7 @@ const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = { [TCLASS_KIND_DRR] = &drr_tclass_vtable, [TCLASS_KIND_HTB] = &htb_tclass_vtable, + [TCLASS_KIND_QFQ] = &qfq_tclass_vtable, }; static int tclass_new(TClassKind kind, TClass **ret) { diff --git a/src/network/tc/tclass.h b/src/network/tc/tclass.h index 217dbfe0017..dc6886ac3fe 100644 --- a/src/network/tc/tclass.h +++ b/src/network/tc/tclass.h @@ -11,6 +11,7 @@ typedef enum TClassKind { TCLASS_KIND_DRR, TCLASS_KIND_HTB, + TCLASS_KIND_QFQ, _TCLASS_KIND_MAX, _TCLASS_KIND_INVALID = -1, } TClassKind; @@ -67,3 +68,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_tclass_classid); #include "drr.h" #include "htb.h" +#include "qfq.h" diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index f5c6d472962..7cade0e9edc 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -414,6 +414,11 @@ PacketLimit= [QuickFairQueueing] Parent= Handle= +[QuickFairQueueingClass] +Parent= +ClassId= +Weight= +MaxPacketSize= [DeficitRoundRobinScheduler] Parent= Handle= -- 2.39.2