]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: tc: support more attributes for FQ-CoDel
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 12 Dec 2019 08:25:29 +0000 (17:25 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 12 Dec 2019 14:36:48 +0000 (23:36 +0900)
man/systemd.network.xml
src/network/networkd-network-gperf.gperf
src/network/tc/fq-codel.c
src/network/tc/fq-codel.h
src/network/tc/qdisc.c
src/network/tc/qdisc.h
test/fuzz/fuzz-network-parser/directives.network

index 8509a8485a19a82d67ffa486c383c20f3343d1d2..148ddac913bfffefb4e91e0d43e720a296ba0216 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>FairQueuingControlledDelayMemoryLimit=</varname></term>
+        <listitem>
+          <para>Specifies the limit on the total number of bytes that can be queued in this FQ-CoDel instance.
+          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.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>FairQueuingControlledDelayFlows=</varname></term>
+        <listitem>
+          <para>Specifies the number of flows into which the incoming packets are classified.
+          Defaults to unset and kernel's default is used.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>FairQueuingControlledDelayTargetSec=</varname></term>
+        <listitem>
+          <para>Takes a timespan. Specifies the acceptable minimum standing/persistent queue delay.
+          Defaults to unset and kernel's default is used.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>FairQueuingControlledDelayIntervalSec=</varname></term>
+        <listitem>
+          <para>Takes a timespan. This is used to ensure that the measured minimum delay does not
+          become too stale. Defaults to unset and kernel's default is used.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>FairQueuingControlledDelayQuantum=</varname></term>
+        <listitem>
+          <para>Specifies the number of bytes used as 'deficit' in the fair queuing algorithmtimespan.
+          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.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>FairQueuingControlledDelayECN=</varname></term>
+        <listitem>
+          <para>Takes a boolean. This can be used to mark packets instead of dropping them. Defaults to
+          unset and kernel's default is used.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>FairQueuingControlledDelayCEThresholdSec=</varname></term>
+        <listitem>
+          <para>Takes a timespan. This sets a threshold above which all packets are marked with ECN
+          Congestion Experienced (CE). Defaults to unset and kernel's default is used.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>FairQueueTrafficPolicingPacketLimit=</varname></term>
         <listitem>
index 5a1e0c0dadf6a31f4e81492cb53cce5f68c01ce0..9e0948fc2bf904e06a1517bfcee0ac4da9f5db51 100644 (file)
@@ -258,7 +258,14 @@ TrafficControlQueueingDiscipline.TokenBufferFilterMPUBytes,                  con
 TrafficControlQueueingDiscipline.TokenBufferFilterPeakRate,                  config_parse_tc_token_buffer_filter_size,                    0, 0
 TrafficControlQueueingDiscipline.TokenBufferFilterLatencySec,                config_parse_tc_token_buffer_filter_latency,                 0, 0
 TrafficControlQueueingDiscipline.StochasticFairnessQueueingPerturbPeriodSec, config_parse_tc_stochastic_fairness_queueing_perturb_period, 0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayPacketLimit,      config_parse_tc_fair_queuing_controlled_delay_limit,         0, 0
+TrafficControlQueueingDiscipline.FairQueuingControlledDelayPacketLimit,      config_parse_tc_fair_queuing_controlled_delay_u32,           0, 0
+TrafficControlQueueingDiscipline.FairQueuingControlledDelayMemoryLimit,      config_parse_tc_fair_queuing_controlled_delay_size,          0, 0
+TrafficControlQueueingDiscipline.FairQueuingControlledDelayFlows,            config_parse_tc_fair_queuing_controlled_delay_u32,           0, 0
+TrafficControlQueueingDiscipline.FairQueuingControlledDelayQuantum,          config_parse_tc_fair_queuing_controlled_delay_size,          0, 0
+TrafficControlQueueingDiscipline.FairQueuingControlledDelayTargetSec,        config_parse_tc_fair_queuing_controlled_delay_usec,          0, 0
+TrafficControlQueueingDiscipline.FairQueuingControlledDelayIntervalSec,      config_parse_tc_fair_queuing_controlled_delay_usec,          0, 0
+TrafficControlQueueingDiscipline.FairQueuingControlledDelayCEThresholdSec,   config_parse_tc_fair_queuing_controlled_delay_usec,          0, 0
+TrafficControlQueueingDiscipline.FairQueuingControlledDelayECN,              config_parse_tc_fair_queuing_controlled_delay_bool,          0, 0
 TrafficControlQueueingDiscipline.FairQueueTrafficPolicingPacketLimit,        config_parse_tc_fair_queue_traffic_policing_packet_limit,    0, 0
 /* backwards compatibility: do not add new entries to this section */
 Network.IPv4LL,                         config_parse_ipv4ll,                             0,                             offsetof(Network, link_local)
index 4ae2ca913b76372516748fee2ff4479467d22162..a64b77651cda733a116174757f557a214bab9212 100644 (file)
 #include "qdisc.h"
 #include "string-util.h"
 
+static int fair_queuing_controlled_delay_init(QDisc *qdisc) {
+        FairQueuingControlledDelay *fqcd;
+
+        assert(qdisc);
+
+        fqcd = FQ_CODEL(qdisc);
+
+        fqcd->memory_limit = UINT32_MAX;
+        fqcd->ce_threshold_usec = USEC_INFINITY;
+        fqcd->ecn = -1;
+
+        return 0;
+}
+
 static int fair_queuing_controlled_delay_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
         FairQueuingControlledDelay *fqcd;
         int r;
@@ -24,9 +38,53 @@ static int fair_queuing_controlled_delay_fill_message(Link *link, QDisc *qdisc,
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
 
-        r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_LIMIT, fqcd->limit);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_LIMIT attribute: %m");
+        if (fqcd->packet_limit > 0) {
+                r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_LIMIT, fqcd->packet_limit);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_LIMIT attribute: %m");
+        }
+
+        if (fqcd->flows > 0) {
+                r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_FLOWS, fqcd->flows);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_FLOWS attribute: %m");
+        }
+
+        if (fqcd->quantum > 0) {
+                r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_QUANTUM, fqcd->quantum);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_QUANTUM attribute: %m");
+        }
+
+        if (fqcd->interval_usec > 0) {
+                r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_INTERVAL, fqcd->interval_usec);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_INTERVAL attribute: %m");
+        }
+
+        if (fqcd->target_usec > 0) {
+                r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_TARGET, fqcd->target_usec);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_TARGET attribute: %m");
+        }
+
+        if (fqcd->ecn >= 0) {
+                r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_ECN, fqcd->ecn);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_ECN attribute: %m");
+        }
+
+        if (fqcd->ce_threshold_usec != USEC_INFINITY) {
+                r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_CE_THRESHOLD, fqcd->ce_threshold_usec);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_CE_THRESHOLD attribute: %m");
+        }
+
+        if (fqcd->memory_limit != UINT32_MAX) {
+                r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_MEMORY_LIMIT, fqcd->memory_limit);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_MEMORY_LIMIT attribute: %m");
+        }
 
         r = sd_netlink_message_close_container(req);
         if (r < 0)
@@ -35,7 +93,7 @@ static int fair_queuing_controlled_delay_fill_message(Link *link, QDisc *qdisc,
         return 0;
 }
 
-int config_parse_tc_fair_queuing_controlled_delay_limit(
+int config_parse_tc_fair_queuing_controlled_delay_u32(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -50,6 +108,7 @@ int config_parse_tc_fair_queuing_controlled_delay_limit(
         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
         FairQueuingControlledDelay *fqcd;
         Network *network = data;
+        uint32_t *p;
         int r;
 
         assert(filename);
@@ -66,14 +125,21 @@ int config_parse_tc_fair_queuing_controlled_delay_limit(
 
         fqcd = FQ_CODEL(qdisc);
 
+        if (streq(lvalue, "FairQueuingControlledDelayPacketLimit"))
+                p = &fqcd->packet_limit;
+        else if (streq(lvalue, "FairQueuingControlledDelayFlows"))
+                p = &fqcd->flows;
+        else
+                assert_not_reached("Invalid lvalue.");
+
         if (isempty(rvalue)) {
-                fqcd->limit = 0;
+                *p = 0;
 
                 qdisc = NULL;
                 return 0;
         }
 
-        r = safe_atou32(rvalue, &fqcd->limit);
+        r = safe_atou32(rvalue, p);
         if (r < 0) {
                 log_syntax(unit, LOG_ERR, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
@@ -86,8 +152,195 @@ int config_parse_tc_fair_queuing_controlled_delay_limit(
         return 0;
 }
 
+int config_parse_tc_fair_queuing_controlled_delay_usec(
+                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;
+        FairQueuingControlledDelay *fqcd;
+        Network *network = data;
+        usec_t *p;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_FQ_CODEL, 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");
+
+        fqcd = FQ_CODEL(qdisc);
+
+        if (streq(lvalue, "FairQueuingControlledDelayTargetSec"))
+                p = &fqcd->target_usec;
+        else if (streq(lvalue, "FairQueuingControlledDelayIntervalSec"))
+                p = &fqcd->interval_usec;
+        else if (streq(lvalue, "FairQueuingControlledDelayCEThresholdSec"))
+                p = &fqcd->ce_threshold_usec;
+        else
+                assert_not_reached("Invalid lvalue.");
+
+        if (isempty(rvalue)) {
+                if (streq(lvalue, "FairQueuingControlledDelayCEThresholdSec"))
+                        *p = USEC_INFINITY;
+                else
+                        *p = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = parse_sec(rvalue, p);
+        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;
+}
+
+int config_parse_tc_fair_queuing_controlled_delay_bool(
+                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;
+        FairQueuingControlledDelay *fqcd;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_FQ_CODEL, 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");
+
+        fqcd = FQ_CODEL(qdisc);
+
+        if (isempty(rvalue)) {
+                fqcd->ecn = -1;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        fqcd->ecn = r;
+        qdisc = NULL;
+
+        return 0;
+}
+
+int config_parse_tc_fair_queuing_controlled_delay_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;
+        FairQueuingControlledDelay *fqcd;
+        Network *network = data;
+        uint64_t sz;
+        uint32_t *p;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_FQ_CODEL, 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");
+
+        fqcd = FQ_CODEL(qdisc);
+
+        if (streq(lvalue, "FairQueuingControlledDelayMemoryLimit"))
+                p = &fqcd->memory_limit;
+        else if (streq(lvalue, "FairQueuingControlledDelayQuantum"))
+                p = &fqcd->quantum;
+        else
+                assert_not_reached("Invalid lvalue.");
+
+        if (isempty(rvalue)) {
+                if (streq(lvalue, "FairQueuingControlledMemoryLimit"))
+                        *p = UINT32_MAX;
+                else
+                        *p = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = parse_size(rvalue, 1024, &sz);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+        if (sz >= UINT32_MAX) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Specified '%s=' is too large, ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        *p = sz;
+        qdisc = NULL;
+
+        return 0;
+}
+
 const QDiscVTable fq_codel_vtable = {
         .object_size = sizeof(FairQueuingControlledDelay),
         .tca_kind = "fq_codel",
+        .init = fair_queuing_controlled_delay_init,
         .fill_message = fair_queuing_controlled_delay_fill_message,
 };
index 47c3cb5b8e58b7071733754c0c6fe62c3ab1f1f9..aa74216fa40d99d312c4f8dd6e01ff3c23d8594f 100644 (file)
@@ -4,13 +4,25 @@
 
 #include "conf-parser.h"
 #include "qdisc.h"
+#include "time-util.h"
 
 typedef struct FairQueuingControlledDelay {
         QDisc meta;
-        uint32_t limit;
+
+        uint32_t packet_limit;
+        uint32_t flows;
+        uint32_t quantum;
+        uint32_t memory_limit;
+        usec_t target_usec;
+        usec_t interval_usec;
+        usec_t ce_threshold_usec;
+        int ecn;
 } FairQueuingControlledDelay;
 
 DEFINE_QDISC_CAST(FQ_CODEL, FairQueuingControlledDelay);
 extern const QDiscVTable fq_codel_vtable;
 
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_limit);
+CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_usec);
+CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_bool);
+CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_size);
index 118754a91ceb08ad8797c7f319e310c68a1f0647..55b8fc63815cbe2d4a6b2daca114958600dab43b 100644 (file)
@@ -23,6 +23,7 @@ const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = {
 
 static int qdisc_new(QDiscKind kind, QDisc **ret) {
         QDisc *qdisc;
+        int r;
 
         if (kind == _QDISC_KIND_INVALID) {
                 qdisc = new(QDisc, 1);
@@ -42,6 +43,12 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) {
                 qdisc->family = AF_UNSPEC;
                 qdisc->parent = TC_H_ROOT;
                 qdisc->kind = kind;
+
+                if (QDISC_VTABLE(qdisc)->init) {
+                        r = QDISC_VTABLE(qdisc)->init(qdisc);
+                        if (r < 0)
+                                return r;
+                }
         }
 
         *ret = TAKE_PTR(qdisc);
index cef540a4d53d94824a8b2e45a042eefcdcc1269c..29d465ff6e8e5020b969267c3845c350b29f4ae7 100644 (file)
@@ -32,6 +32,8 @@ typedef struct QDisc {
 typedef struct QDiscVTable {
         size_t object_size;
         const char *tca_kind;
+        /* called in qdisc_new() */
+        int (*init)(QDisc *qdisc);
         int (*fill_message)(Link *link, QDisc *qdisc, sd_netlink_message *m);
         int (*verify)(QDisc *qdisc);
 } QDiscVTable;
index 9d6e37484caa44150529feba088972d3f55ec5a9..a37fa40517530fe956b08a143bfda6a295946d65 100644 (file)
@@ -279,4 +279,11 @@ TokenBufferFilterPeakRate=
 TokenBufferFilterLatencySec=
 StochasticFairnessQueueingPerturbPeriodSec=
 FairQueuingControlledDelayPacketLimit=
+FairQueuingControlledDelayMemoryLimit=
+FairQueuingControlledDelayFlows=
+FairQueuingControlledDelayQuantum=
+FairQueuingControlledDelayTargetSec=
+FairQueuingControlledDelayIntervalSec=
+FairQueuingControlledDelayCEThresholdSec=
+FairQueuingControlledDelayECN=
 FairQueueTrafficPolicingPacketLimit=