]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: tc: add more settings for HTB
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 8 Jun 2020 08:36:52 +0000 (17:36 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 17 Jun 2020 07:49:46 +0000 (16:49 +0900)
Closes #15213.

man/systemd.network.xml
src/network/networkd-network-gperf.gperf
src/network/tc/htb.c
src/network/tc/htb.h
src/network/tc/tc-util.c
src/network/tc/tc-util.h
test/fuzz/fuzz-network-parser/directives.network

index 851570e723ff330434ed2e48527d0aaa55442aac..515cfe23cdbc4f9084d265ccb7fbd04986c0a57b 100644 (file)
           to the class. Defaults to unset.</para>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>RateToQuantum=</varname></term>
+        <listitem>
+          <para>Takes an unsigned integer. The DRR quantums are calculated by dividing the value
+          configured in <varname>Rate=</varname> by <varname>RateToQuantum=</varname>.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
         <term><varname>Priority=</varname></term>
         <listitem>
           <para>Specifies the priority of the class. In the round-robin process, classes with the lowest
-          priority field are tried for packets first. This setting is mandatory.</para>
+          priority field are tried for packets first.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>QuantumBytes=</varname></term>
+        <listitem>
+          <para>Specifies how many bytes to serve from leaf at once. When suffixed with K, M, or G, the
+          specified size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of
+          1024.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>MTUBytes=</varname></term>
+        <listitem>
+          <para>Specifies the maximum packet size we create. When suffixed with K, M, or G, the specified
+          size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>OverheadBytes=</varname></term>
+        <listitem>
+          <para>Takes an unsigned integer which specifies per-packet size overhead used in rate
+          computations. When suffixed with K, M, or G, the specified size is parsed as Kilobytes,
+          Megabytes, or Gigabytes, respectively, to the base of 1024.</para>
         </listitem>
       </varlistentry>
 
           is used.</para>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>BufferBytes=</varname></term>
+        <listitem>
+          <para>Specifies the maximum bytes burst which can be accumulated during idle period. When suffixed
+          with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively,
+          to the base of 1024.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>CeilBufferBytes=</varname></term>
+        <listitem>
+          <para>Specifies the maximum bytes burst for ceil which can be accumulated during idle period.
+          When suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes,
+          respectively, to the base of 1024.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 84ea5d552e9997367cce109c4b2884010ac25c88..030ed6fba60b8c665c049015a1609dd6c9cb0564 100644 (file)
@@ -352,11 +352,17 @@ HeavyHitterFilter.PacketLimit,               config_parse_heavy_hitter_filter_pa
 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
+HierarchyTokenBucket.RateToQuantum,          config_parse_hierarchy_token_bucket_u32,                  QDISC_KIND_HTB,                0
 HierarchyTokenBucketClass.Parent,            config_parse_tclass_parent,                               TCLASS_KIND_HTB,               0
 HierarchyTokenBucketClass.ClassId,           config_parse_tclass_classid,                              TCLASS_KIND_HTB,               0
-HierarchyTokenBucketClass.Priority,          config_parse_hierarchy_token_bucket_u32,                  TCLASS_KIND_HTB,               0
-HierarchyTokenBucketClass.Rate,              config_parse_hierarchy_token_bucket_rate,                 TCLASS_KIND_HTB,               0
-HierarchyTokenBucketClass.CeilRate,          config_parse_hierarchy_token_bucket_rate,                 TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.Priority,          config_parse_hierarchy_token_bucket_class_u32,            TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.QuantumBytes,      config_parse_hierarchy_token_bucket_class_size,           TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.MTUBytes,          config_parse_hierarchy_token_bucket_class_size,           TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.OverheadBytes,     config_parse_hierarchy_token_bucket_class_size,           TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.Rate,              config_parse_hierarchy_token_bucket_class_rate,           TCLASS_KIND_HTB,               0
+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
 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
index f2b9c4507ea72c1d6d1660e455b5b94b85892f0c..227d6233e78f02722a9d1e3c6b6950f763f2b516 100644 (file)
 #include "string-util.h"
 #include "tc-util.h"
 
+#define HTB_DEFAULT_RATE_TO_QUANTUM  10
+#define HTB_DEFAULT_MTU              1600  /* Ethernet packet length */
+
 static int hierarchy_token_bucket_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
         HierarchyTokenBucket *htb;
         struct tc_htb_glob opt = {
-                .rate2quantum = 10,
                 .version = 3,
         };
         int r;
@@ -25,6 +27,7 @@ static int hierarchy_token_bucket_fill_message(Link *link, QDisc *qdisc, sd_netl
 
         htb = HTB(qdisc);
 
+        opt.rate2quantum = htb->rate_to_quantum;
         opt.defcls = htb->default_class;
 
         r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "htb");
@@ -92,16 +95,80 @@ int config_parse_hierarchy_token_bucket_default_class(
         return 0;
 }
 
+int config_parse_hierarchy_token_bucket_u32(
+                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;
+        HierarchyTokenBucket *htb;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_HTB, 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");
+
+        htb = HTB(qdisc);
+
+        if (isempty(rvalue)) {
+                htb->rate_to_quantum = HTB_DEFAULT_RATE_TO_QUANTUM;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &htb->rate_to_quantum);
+        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;
+}
+
+static int hierarchy_token_bucket_init(QDisc *qdisc) {
+        HierarchyTokenBucket *htb;
+
+        assert(qdisc);
+
+        htb = HTB(qdisc);
+
+        htb->rate_to_quantum = HTB_DEFAULT_RATE_TO_QUANTUM;
+
+        return 0;
+}
+
 const QDiscVTable htb_vtable = {
         .object_size = sizeof(HierarchyTokenBucket),
         .tca_kind = "htb",
         .fill_message = hierarchy_token_bucket_fill_message,
+        .init = hierarchy_token_bucket_init,
 };
 
 static int hierarchy_token_bucket_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) {
         HierarchyTokenBucketClass *htb;
         struct tc_htb_opt opt = {};
-        uint32_t rtab[256], ctab[256], mtu = 1600; /* Ethernet packet length */
+        uint32_t rtab[256], ctab[256];
         int r;
 
         assert(link);
@@ -110,25 +177,26 @@ static int hierarchy_token_bucket_class_fill_message(Link *link, TClass *tclass,
 
         htb = TCLASS_TO_HTB(tclass);
 
-        if (htb->ceil_rate == 0)
-                htb->ceil_rate = htb->rate;
-
         opt.prio = htb->priority;
+        opt.quantum = htb->quantum;
         opt.rate.rate = (htb->rate >= (1ULL << 32)) ? ~0U : htb->rate;
         opt.ceil.rate = (htb->ceil_rate >= (1ULL << 32)) ? ~0U : htb->ceil_rate;
-        r = tc_transmit_time(htb->rate, mtu, &opt.buffer);
+        opt.rate.overhead = htb->overhead;
+        opt.ceil.overhead = htb->overhead;
+
+        r = tc_transmit_time(htb->rate, htb->buffer, &opt.buffer);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to calculate buffer size: %m");
 
-        r = tc_transmit_time(htb->ceil_rate, mtu, &opt.cbuffer);
+        r = tc_transmit_time(htb->ceil_rate, htb->ceil_buffer, &opt.cbuffer);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to calculate ceil buffer size: %m");
 
-        r = tc_fill_ratespec_and_table(&opt.rate, rtab, mtu);
+        r = tc_fill_ratespec_and_table(&opt.rate, rtab, htb->mtu);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to calculate rate table: %m");
 
-        r = tc_fill_ratespec_and_table(&opt.ceil, ctab, mtu);
+        r = tc_fill_ratespec_and_table(&opt.ceil, ctab, htb->mtu);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to calculate ceil rate table: %m");
 
@@ -166,7 +234,7 @@ static int hierarchy_token_bucket_class_fill_message(Link *link, TClass *tclass,
         return 0;
 }
 
-int config_parse_hierarchy_token_bucket_u32(
+int config_parse_hierarchy_token_bucket_class_u32(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -181,6 +249,7 @@ int config_parse_hierarchy_token_bucket_u32(
         _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
         HierarchyTokenBucketClass *htb;
         Network *network = data;
+        uint32_t v;
         int r;
 
         assert(filename);
@@ -197,25 +266,105 @@ int config_parse_hierarchy_token_bucket_u32(
 
         if (isempty(rvalue)) {
                 htb->priority = 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;
+        }
+
+        htb->priority = v;
+        tclass = NULL;
+
+        return 0;
+}
+
+int config_parse_hierarchy_token_bucket_class_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;
+        HierarchyTokenBucketClass *htb;
+        Network *network = data;
+        uint64_t v;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = tclass_new_static(TCLASS_KIND_HTB, 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");
+
+        htb = TCLASS_TO_HTB(tclass);
+
+        if (isempty(rvalue)) {
+                if (streq(lvalue, "QuantumBytes"))
+                        htb->quantum = 0;
+                else if (streq(lvalue, "MTUBytes"))
+                        htb->mtu = HTB_DEFAULT_MTU;
+                else if (streq(lvalue, "OverheadBytes"))
+                        htb->overhead = 0;
+                else if (streq(lvalue, "BufferBytes"))
+                        htb->buffer = 0;
+                else if (streq(lvalue, "CeilBufferBytes"))
+                        htb->ceil_buffer = 0;
+                else
+                        assert_not_reached("Invalid lvalue");
 
                 tclass = NULL;
                 return 0;
         }
 
-        r = safe_atou32(rvalue, &htb->priority);
+        r = parse_size(rvalue, 1024, &v);
         if (r < 0) {
                 log_syntax(unit, LOG_ERR, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
         }
+        if ((streq(lvalue, "OverheadBytes") && v > UINT16_MAX) || v > UINT32_MAX) {
+                log_syntax(unit, LOG_ERR, filename, line, 0,
+                           "Invalid '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        if (streq(lvalue, "QuantumBytes"))
+                htb->quantum = v;
+        else if (streq(lvalue, "OverheadBytes"))
+                htb->overhead = v;
+        else if (streq(lvalue, "MTUBytes"))
+                htb->mtu = v;
+        else if (streq(lvalue, "BufferBytes"))
+                htb->buffer = v;
+        else if (streq(lvalue, "CeilBufferBytes"))
+                htb->ceil_buffer = v;
+        else
+                assert_not_reached("Invalid lvalue");
 
         tclass = NULL;
 
         return 0;
 }
 
-int config_parse_hierarchy_token_bucket_rate(
+int config_parse_hierarchy_token_bucket_class_rate(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -272,8 +421,53 @@ int config_parse_hierarchy_token_bucket_rate(
         return 0;
 }
 
+static int hierarchy_token_bucket_class_init(TClass *tclass) {
+        HierarchyTokenBucketClass *htb;
+
+        assert(tclass);
+
+        htb = TCLASS_TO_HTB(tclass);
+
+        htb->mtu = HTB_DEFAULT_MTU;
+
+        return 0;
+}
+
+static int hierarchy_token_bucket_class_verify(TClass *tclass) {
+        HierarchyTokenBucketClass *htb;
+        uint32_t hz;
+        int r;
+
+        assert(tclass);
+
+        htb = TCLASS_TO_HTB(tclass);
+
+        if (htb->rate == 0)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: Rate= is mandatory. "
+                                         "Ignoring [HierarchyTokenBucketClass] section from line %u.",
+                                         tclass->section->filename, tclass->section->line);
+
+        /* if CeilRate= setting is missing, use the same as Rate= */
+        if (htb->ceil_rate == 0)
+                htb->ceil_rate = htb->rate;
+
+        r = tc_init(NULL, &hz);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read /proc/net/psched: %m");
+
+        if (htb->buffer == 0)
+                htb->buffer = htb->rate / hz + htb->mtu;
+        if (htb->ceil_buffer == 0)
+                htb->ceil_buffer = htb->ceil_rate / hz + htb->mtu;
+
+        return 0;
+}
+
 const TClassVTable htb_tclass_vtable = {
         .object_size = sizeof(HierarchyTokenBucketClass),
         .tca_kind = "htb",
         .fill_message = hierarchy_token_bucket_class_fill_message,
+        .init = hierarchy_token_bucket_class_init,
+        .verify = hierarchy_token_bucket_class_verify,
 };
index c8dce2c1e3d2852a952647453669f947bed408bf..b385872e0a696fe29eea215893c3e60e24d4e66d 100644 (file)
@@ -9,23 +9,31 @@ typedef struct HierarchyTokenBucket {
         QDisc meta;
 
         uint32_t default_class;
+        uint32_t rate_to_quantum;
 } HierarchyTokenBucket;
 
 DEFINE_QDISC_CAST(HTB, HierarchyTokenBucket);
 extern const QDiscVTable htb_vtable;
 
 CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_default_class);
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_u32);
 
 typedef struct HierarchyTokenBucketClass {
         TClass meta;
 
         uint32_t priority;
+        uint32_t quantum;
+        uint32_t mtu;
+        uint16_t overhead;
         uint64_t rate;
+        uint32_t buffer;
         uint64_t ceil_rate;
+        uint32_t ceil_buffer;
 } HierarchyTokenBucketClass;
 
 DEFINE_TCLASS_CAST(HTB, HierarchyTokenBucketClass);
 extern const TClassVTable htb_tclass_vtable;
 
-CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_u32);
-CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_rate);
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_class_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_class_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_class_rate);
index 47371a841b974432005af046e4c47cf4524de214..5f25acbdd63d439360e5b8056f62025b2be06da1 100644 (file)
@@ -8,38 +8,46 @@
 #include "tc-util.h"
 #include "time-util.h"
 
-static int tc_init(double *ticks_in_usec) {
-        uint32_t clock_resolution, ticks_to_usec, usec_to_ticks;
-        _cleanup_free_ char *line = NULL;
-        double clock_factor;
-        int r;
+int tc_init(double *ret_ticks_in_usec, uint32_t *ret_hz) {
+        static double ticks_in_usec = -1;
+        static uint32_t hz;
 
-        r = read_one_line_file("/proc/net/psched", &line);
-        if (r < 0)
-                return r;
+        if (ticks_in_usec < 0) {
+                uint32_t clock_resolution, ticks_to_usec, usec_to_ticks;
+                _cleanup_free_ char *line = NULL;
+                double clock_factor;
+                int r;
 
-        r = sscanf(line, "%08x%08x%08x", &ticks_to_usec, &usec_to_ticks, &clock_resolution);
-        if (r < 3)
-                return -EIO;
+                r = read_one_line_file("/proc/net/psched", &line);
+                if (r < 0)
+                        return r;
 
-        clock_factor =  (double) clock_resolution / USEC_PER_SEC;
-        *ticks_in_usec = (double) ticks_to_usec / usec_to_ticks * clock_factor;
+                r = sscanf(line, "%08x%08x%08x%08x", &ticks_to_usec, &usec_to_ticks, &clock_resolution, &hz);
+                if (r < 4)
+                        return -EIO;
+
+                clock_factor =  (double) clock_resolution / USEC_PER_SEC;
+                ticks_in_usec = (double) ticks_to_usec / usec_to_ticks * clock_factor;
+        }
+
+        if (ret_ticks_in_usec)
+                *ret_ticks_in_usec = ticks_in_usec;
+        if (ret_hz)
+                *ret_hz = hz;
 
         return 0;
 }
 
 int tc_time_to_tick(usec_t t, uint32_t *ret) {
-        static double ticks_in_usec = -1;
+        double ticks_in_usec;
         usec_t a;
         int r;
 
         assert(ret);
 
-        if (ticks_in_usec < 0) {
-                r = tc_init(&ticks_in_usec);
-                if (r < 0)
-                        return r;
-        }
+        r = tc_init(&ticks_in_usec, NULL);
+        if (r < 0)
+                return r;
 
         a = t * ticks_in_usec;
         if (a > UINT32_MAX)
index 38b9d0786d58aa9fdd814ad9c3c6a6b6e63c0bd9..6287b35a76571e1311172bfb2601afbcca225fd2 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "time-util.h"
 
+int tc_init(double *ret_ticks_in_usec, uint32_t *ret_hz);
 int tc_time_to_tick(usec_t t, uint32_t *ret);
 int parse_tc_percent(const char *s, uint32_t *percent);
 int tc_transmit_time(uint64_t rate, uint32_t size, uint32_t *ret);
index a6ef817360fc018ab33f4d8918ba06c86bfa7921..40b936e9dc3a7ed8aa38c75bbedaa834d615e97b 100644 (file)
@@ -378,12 +378,18 @@ Id=
 Parent=
 Handle=
 DefaultClass=
+RateToQuantum=
 [HierarchyTokenBucketClass]
 Parent=
 ClassId=
 Priority=
+QuantumBytes=
+MTUBytes=
+OverheadBytes=
 Rate=
 CeilRate=
+BufferBytes=
+CeilBufferBytes=
 [BFIFO]
 Parent=
 Handle=