]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: tc: add more options for TBF
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 7 Dec 2019 20:54:33 +0000 (05:54 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 9 Dec 2019 16:28:38 +0000 (01:28 +0900)
man/systemd.network.xml
src/network/networkd-network-gperf.gperf
src/network/tc/qdisc.c
src/network/tc/tbf.c
src/network/tc/tbf.h
src/network/tc/tc-util.c
src/network/tc/tc-util.h
test/fuzz/fuzz-network-parser/directives.network

index 63dfb517b6a9fabd140b63447bbfb88551381dee..692a68602075f089ff8e40f62d2e8a294c273a3f 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>TokenBufferFilterLimitSize=</varname></term>
+        <listitem>
+          <para>Takes the number of bytes that can be queued waiting for tokens to become available.
+          When the size is suffixed with K, M, or G, it is parsed as Kilobytes, Megabytes, or Gigabytes,
+          respectively, to the base of 1000. Defaults to unset.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>TokenBufferFilterBurst=</varname></term>
         <listitem>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>TokenBufferFilterMPUBytes=</varname></term>
+        <listitem>
+          <para>The Minimum Packet Unit (MPU) determines the minimal token usage (specified in bytes)
+          for a packet. When suffixed with K, M, or G, the specified size is parsed as Kilobytes,
+          Megabytes, or Gigabytes, respectively, to the base of 1000. Defaults to zero.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>TokenBufferFilterPeakRate=</varname></term>
+        <listitem>
+          <para>Takes the maximum depletion rate of the bucket. When suffixed with K, M, or G, the
+          specified size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of
+          1000. Defaults to unset.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>TokenBufferFilterMTUBytes=</varname></term>
+        <listitem>
+          <para>Specifies the size of the peakrate bucket. When suffixed with K, M, or G, the specified
+          size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000.
+          Defaults to unset.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>StochasticFairnessQueueingPerturbPeriodSec=</varname></term>
         <listitem>
index 6f76f74ec9b5053bdc46281853e72498392efc1c..ea4ba31b80cbd9793f6efdfdd89781c13a3b5a76 100644 (file)
@@ -252,6 +252,10 @@ TrafficControlQueueingDiscipline.NetworkEmulatorDuplicateRate,               con
 TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit,                 config_parse_tc_network_emulator_packet_limit,               0, 0
 TrafficControlQueueingDiscipline.TokenBufferFilterRate,                      config_parse_tc_token_buffer_filter_size,                    0, 0
 TrafficControlQueueingDiscipline.TokenBufferFilterBurst,                     config_parse_tc_token_buffer_filter_size,                    0, 0
+TrafficControlQueueingDiscipline.TokenBufferFilterLimitSize,                 config_parse_tc_token_buffer_filter_size,                    0, 0
+TrafficControlQueueingDiscipline.TokenBufferFilterMTUBytes,                  config_parse_tc_token_buffer_filter_size,                    0, 0
+TrafficControlQueueingDiscipline.TokenBufferFilterMPUBytes,                  config_parse_tc_token_buffer_filter_size,                    0, 0
+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
 /* backwards compatibility: do not add new entries to this section */
index bb3b127647812d2fa22b89f563bb9a651a874956..74b2b7a2c287233d3bf63769e68aea307dc6bd25 100644 (file)
@@ -189,6 +189,7 @@ int qdisc_configure(Link *link, QDisc *qdisc) {
 
 int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) {
         unsigned i;
+        int r;
 
         assert(qdisc);
         assert(has_root);
@@ -204,6 +205,12 @@ int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) {
                                          "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
                                          qdisc->section->filename, qdisc->section->line);
 
+        if (qdisc->has_token_buffer_filter) {
+                r = token_buffer_filter_section_verify(&qdisc->tbf, qdisc->section);
+                if (r < 0)
+                        return r;
+        }
+
         if (qdisc->parent == TC_H_ROOT) {
                 if (*has_root)
                         return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
index a4ef9ab2992dd6f1224afcb03944c7c97227c128..4c15d6b4fd6ff67719828da638a101210939ede7 100644 (file)
@@ -12,6 +12,7 @@
 #include "parse-util.h"
 #include "qdisc.h"
 #include "string-util.h"
+#include "tc-util.h"
 #include "util.h"
 
 int token_buffer_filter_new(TokenBufferFilter **ret) {
@@ -27,6 +28,7 @@ int token_buffer_filter_new(TokenBufferFilter **ret) {
 }
 
 int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, sd_netlink_message *req) {
+        uint32_t rtab[256], ptab[256];
         struct tc_tbf_qopt opt = {};
         int r;
 
@@ -35,7 +37,42 @@ int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, s
         assert(req);
 
         opt.rate.rate = tbf->rate >= (1ULL << 32) ? ~0U : tbf->rate;
-        opt.limit = tbf->rate * (double) tbf->latency / USEC_PER_SEC + tbf->burst;
+        opt.peakrate.rate = tbf->peak_rate >= (1ULL << 32) ? ~0U : tbf->peak_rate;
+
+        if (tbf->limit > 0)
+                opt.limit = tbf->limit;
+        else {
+                double lim, lim2;
+
+                lim = tbf->rate * (double) tbf->latency / USEC_PER_SEC + tbf->burst;
+                if (tbf->peak_rate > 0) {
+                        lim2 = tbf->peak_rate * (double) tbf->latency / USEC_PER_SEC + tbf->mtu;
+                        lim = MIN(lim, lim2);
+                }
+                opt.limit = lim;
+        }
+
+        opt.rate.mpu = tbf->mpu;
+
+        r = tc_fill_ratespec_and_table(&opt.rate, rtab, tbf->mtu);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to calculate ratespec: %m");
+
+        r = tc_transmit_time(opt.rate.rate, tbf->burst, &opt.buffer);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to calculate buffer size: %m");
+
+        if (opt.peakrate.rate > 0) {
+                opt.peakrate.mpu = tbf->mpu;
+
+                r = tc_fill_ratespec_and_table(&opt.peakrate, ptab, tbf->mtu);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to calculate ratespec: %m");
+
+                r = tc_transmit_time(opt.peakrate.rate, tbf->mtu, &opt.mtu);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to calculate mtu size: %m");
+        }
 
         r = sd_netlink_message_open_array(req, TCA_OPTIONS);
         if (r < 0)
@@ -55,6 +92,26 @@ int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, s
                         return log_link_error_errno(link, r, "Could not append TCA_TBF_RATE64 attribute: %m");
         }
 
+        r = sd_netlink_message_append_data(req, TCA_TBF_RTAB, rtab, sizeof(rtab));
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_TBF_RTAB attribute: %m");
+
+        if (opt.peakrate.rate > 0) {
+                if (tbf->peak_rate >= (1ULL << 32)) {
+                        r = sd_netlink_message_append_data(req, TCA_TBF_PRATE64, &tbf->peak_rate, sizeof(tbf->peak_rate));
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not append TCA_TBF_PRATE64 attribute: %m");
+                }
+
+                r = sd_netlink_message_append_data(req, TCA_TBF_PBURST, &tbf->mtu, sizeof(tbf->mtu));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_TBF_PBURST attribute: %m");
+
+                r = sd_netlink_message_append_data(req, TCA_TBF_PTAB, ptab, sizeof(ptab));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_TBF_PTAB 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");
@@ -93,6 +150,14 @@ int config_parse_tc_token_buffer_filter_size(
                         qdisc->tbf.rate = 0;
                 else if (streq(lvalue, "TokenBufferFilterBurst"))
                         qdisc->tbf.burst = 0;
+                else if (streq(lvalue, "TokenBufferFilterLimitSize"))
+                        qdisc->tbf.limit = 0;
+                else if (streq(lvalue, "TokenBufferFilterMTUBytes"))
+                        qdisc->tbf.mtu = 0;
+                else if (streq(lvalue, "TokenBufferFilterMPUBytes"))
+                        qdisc->tbf.mpu = 0;
+                else if (streq(lvalue, "TokenBufferFilterPeakRate"))
+                        qdisc->tbf.peak_rate = 0;
 
                 qdisc = NULL;
                 return 0;
@@ -110,6 +175,14 @@ int config_parse_tc_token_buffer_filter_size(
                 qdisc->tbf.rate = k / 8;
         else if (streq(lvalue, "TokenBufferFilterBurst"))
                 qdisc->tbf.burst = k;
+        else if (streq(lvalue, "TokenBufferFilterLimitSize"))
+                qdisc->tbf.limit = k;
+        else if (streq(lvalue, "TokenBufferFilterMPUBytes"))
+                qdisc->tbf.mpu = k;
+        else if (streq(lvalue, "TokenBufferFilterMTUBytes"))
+                qdisc->tbf.mtu = k;
+        else if (streq(lvalue, "TokenBufferFilterPeakRate"))
+                qdisc->tbf.peak_rate = k / 8;
 
         qdisc->has_token_buffer_filter = true;
         qdisc = NULL;
@@ -165,3 +238,37 @@ int config_parse_tc_token_buffer_filter_latency(
 
         return 0;
 }
+
+int token_buffer_filter_section_verify(const TokenBufferFilter *tbf, const NetworkConfigSection *section) {
+        if (tbf->limit > 0 && tbf->latency > 0)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: Specifying both TokenBufferFilterLimitSize= and TokenBufferFilterLatencySec= is not allowed. "
+                                         "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                         section->filename, section->line);
+
+        if (tbf->limit == 0 && tbf->latency == 0)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: Either TokenBufferFilterLimitSize= or TokenBufferFilterLatencySec= is required. "
+                                         "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                         section->filename, section->line);
+
+        if (tbf->rate == 0)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: TokenBufferFilterRate= is mandatory. "
+                                         "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                         section->filename, section->line);
+
+        if (tbf->burst == 0)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: TokenBufferFilterBurst= is mandatory. "
+                                         "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                         section->filename, section->line);
+
+        if (tbf->peak_rate > 0 && tbf->mtu == 0)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: TokenBufferFilterMTUBytes= is mandatory when TokenBufferFilterPeakRate= is specified. "
+                                         "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                         section->filename, section->line);
+
+        return 0;
+}
index c8ae6d057d22b6bbd037369edce2578896861bcf..e0bdc3b85fdf5a2daca56f8de035e4c15d632a3f 100644 (file)
@@ -6,16 +6,22 @@
 
 #include "conf-parser.h"
 #include "networkd-link.h"
+#include "networkd-util.h"
+#include "tc-util.h"
 
 typedef struct TokenBufferFilter {
         uint64_t rate;
-
+        uint64_t peak_rate;
         uint32_t burst;
-        uint32_t latency;
+        uint32_t mtu;
+        usec_t latency;
+        size_t limit;
+        size_t mpu;
 } TokenBufferFilter;
 
 int token_buffer_filter_new(TokenBufferFilter **ret);
 int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, sd_netlink_message *req);
+int token_buffer_filter_section_verify(const TokenBufferFilter *tbf, const NetworkConfigSection *section);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_latency);
 CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_size);
index 7e1cf53e11522cf5e6569266d7031612603a48d3..c46550f955942907b7ba8f99c1d7be9c61cc1650 100644 (file)
@@ -61,3 +61,34 @@ int parse_tc_percent(const char *s, uint32_t *percent)  {
         *percent = (double) r / 1000 * UINT32_MAX;
         return 0;
 }
+
+int tc_transmit_time(uint64_t rate, uint32_t size, uint32_t *ret) {
+        return tc_time_to_tick(USEC_PER_SEC * ((double)size / (double)rate), ret);
+}
+
+int tc_fill_ratespec_and_table(struct tc_ratespec *rate, uint32_t *rtab, uint32_t mtu) {
+        uint32_t cell_log = 0;
+        int r;
+
+        if (mtu == 0)
+                mtu = 2047;
+
+        while ((mtu >> cell_log) > 255)
+                cell_log++;
+
+        for (size_t i = 0; i < 256; i++) {
+                uint32_t sz;
+
+                sz = (i + 1) << cell_log;
+                if (sz < rate->mpu)
+                        sz = rate->mpu;
+                r = tc_transmit_time(rate->rate, sz, &rtab[i]);
+                if (r < 0)
+                        return r;
+        }
+
+        rate->cell_align = -1;
+        rate->cell_log = cell_log;
+        rate->linklayer = TC_LINKLAYER_ETHERNET;
+        return 0;
+}
index ce7ab405385813e88b1d25b595ec39f67d0bc1be..c901f50691c342a1f31bebf403cd2e2719cb670d 100644 (file)
@@ -2,7 +2,11 @@
  * Copyright © 2019 VMware, Inc. */
 #pragma once
 
+#include <linux/pkt_sched.h>
+
 #include "time-util.h"
 
 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);
+int tc_fill_ratespec_and_table(struct tc_ratespec *rate, uint32_t *rtab, uint32_t mtu);
index 2a6f111d83b65b2da47c3ef7239d1287ffb69eaa..c3264522b41218b218309214542ca5c1042d6e2a 100644 (file)
@@ -272,5 +272,9 @@ NetworkEmulatorDuplicateRate=
 NetworkEmulatorPacketLimit=
 TokenBufferFilterRate=
 TokenBufferFilterBurst=
+TokenBufferFilterLimitSize=
+TokenBufferFilterMTUBytes=
+TokenBufferFilterMPUBytes=
+TokenBufferFilterPeakRate=
 TokenBufferFilterLatencySec=
 StochasticFairnessQueueingPerturbPeriodSec=