networkd tc: introduce tbf

See https://linux.die.net/man/8/tc-tbf
This commit is contained in:
Susant Sahani 2019-11-17 07:30:03 +01:00 committed by Yu Watanabe
parent c0dd326953
commit ba5841b520
9 changed files with 236 additions and 1 deletions

View File

@ -2368,6 +2368,33 @@
</listitem>
</varlistentry>
<varlistentry>
<term><varname>TokenBufferFilterLatencySec=</varname></term>
<listitem>
<para>Specifies the latency parameter, which specifies the maximum amount of time a
packet can sit in the Token Buffer Filter (TBF). Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>TokenBufferFilterBurst=</varname></term>
<listitem>
<para>Specifies the size of the bucket. This is the maximum amount of bytes that tokens
can be available for instantaneous transfer. 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>TokenBufferFilterRate=</varname></term>
<listitem>
<para>Specifies the device specific bandwidth. When suffixed with K, M, or G, the specified
bandwidth is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000.
Defaults to unset.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -532,7 +532,6 @@ int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) {
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
assert_return(m->n_containers > 0, -EINVAL);
r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
if (r < 0)

View File

@ -109,6 +109,8 @@ sources = files('''
tc/netem.h
tc/qdisc.c
tc/qdisc.h
tc/tbf.c
tc/tbf.h
tc/tc-util.c
tc/tc-util.h
'''.split())

View File

@ -250,6 +250,9 @@ TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, config_pars
TrafficControlQueueingDiscipline.NetworkEmulatorLossRate, config_parse_tc_network_emulator_rate, 0, 0
TrafficControlQueueingDiscipline.NetworkEmulatorDuplicateRate, config_parse_tc_network_emulator_rate, 0, 0
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.TokenBufferFilterLatencySec, config_parse_tc_token_buffer_filter_latency, 0, 0
/* backwards compatibility: do not add new entries to this section */
Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)

View File

@ -152,6 +152,16 @@ int qdisc_configure(Link *link, QDiscs *qdisc) {
return r;
}
if (qdisc->has_token_buffer_filter) {
r = free_and_strdup(&tca_kind, "tbf");
if (r < 0)
return log_oom();
r = token_buffer_filter_fill_message(link, &qdisc->tbf, req);
if (r < 0)
return r;
}
if (tca_kind) {
r = sd_netlink_message_append_string(req, TCA_KIND, tca_kind);
if (r < 0)

View File

@ -7,6 +7,7 @@
#include "networkd-link.h"
#include "networkd-network.h"
#include "networkd-util.h"
#include "tbf.h"
typedef struct QDiscs {
NetworkConfigSection *section;
@ -20,8 +21,10 @@ typedef struct QDiscs {
uint32_t parent;
bool has_network_emulator:1;
bool has_token_buffer_filter:1;
NetworkEmulator ne;
TokenBufferFilter tbf;
} QDiscs;
void qdisc_free(QDiscs *qdisc);

167
src/network/tc/tbf.c Normal file
View File

@ -0,0 +1,167 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc. */
#include <linux/pkt_sched.h>
#include <math.h>
#include "alloc-util.h"
#include "conf-parser.h"
#include "netem.h"
#include "netlink-util.h"
#include "networkd-manager.h"
#include "parse-util.h"
#include "qdisc.h"
#include "string-util.h"
#include "util.h"
int token_buffer_filter_new(TokenBufferFilter **ret) {
TokenBufferFilter *ne = NULL;
ne = new0(TokenBufferFilter, 1);
if (!ne)
return -ENOMEM;
*ret = TAKE_PTR(ne);
return 0;
}
int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, sd_netlink_message *req) {
struct tc_tbf_qopt opt = {};
int r;
assert(link);
assert(tbf);
assert(req);
opt.rate.rate = tbf->rate >= (1ULL << 32) ? ~0U : tbf->rate;
opt.limit = tbf->rate * (double) tbf->latency / USEC_PER_SEC + tbf->burst;
r = sd_netlink_message_open_array(req, TCA_OPTIONS);
if (r < 0)
return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
r = sd_netlink_message_append_data(req, TCA_TBF_PARMS, &opt, sizeof(struct tc_tbf_qopt));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_TBF_PARMS attribute: %m");
r = sd_netlink_message_append_data(req, TCA_TBF_BURST, &tbf->burst, sizeof(tbf->burst));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_TBF_BURST attribute: %m");
if (tbf->rate >= (1ULL << 32)) {
r = sd_netlink_message_append_data(req, TCA_TBF_RATE64, &tbf->rate, sizeof(tbf->rate));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_TBF_RATE64 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_tc_token_buffer_filter_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) QDiscs *qdisc = NULL;
Network *network = data;
uint64_t k;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc);
if (r < 0)
return r;
if (isempty(rvalue)) {
if (streq(lvalue, "TokenBufferFilterRate"))
qdisc->tbf.rate = 0;
else if (streq(lvalue, "TokenBufferFilterBurst"))
qdisc->tbf.burst = 0;
qdisc = NULL;
return 0;
}
r = parse_size(rvalue, 1000, &k);
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, "TokenBufferFilterRate"))
qdisc->tbf.rate = k / 8;
else if (streq(lvalue, "TokenBufferFilterBurst"))
qdisc->tbf.burst = k;
qdisc->has_token_buffer_filter = true;
qdisc = NULL;
return 0;
}
int config_parse_tc_token_buffer_filter_latency(
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) QDiscs *qdisc = NULL;
Network *network = data;
usec_t u;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc);
if (r < 0)
return r;
if (isempty(rvalue)) {
qdisc->tbf.latency = 0;
qdisc = NULL;
return 0;
}
r = parse_sec(rvalue, &u);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
qdisc->tbf.latency = u;
qdisc->has_token_buffer_filter = true;
qdisc = NULL;
return 0;
}

21
src/network/tc/tbf.h Normal file
View File

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc. */
#pragma once
#include "sd-netlink.h"
#include "conf-parser.h"
#include "networkd-link.h"
typedef struct TokenBufferFilter {
uint64_t rate;
uint32_t burst;
uint32_t latency;
} TokenBufferFilter;
int token_buffer_filter_new(TokenBufferFilter **ret);
int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, sd_netlink_message *req);
CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_latency);
CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_size);

View File

@ -270,3 +270,6 @@ NetworkEmulatorDelayJitterSec=
NetworkEmulatorLossRate=
NetworkEmulatorDuplicateRate=
NetworkEmulatorPacketLimit=
TokenBufferFilterRate=
TokenBufferFilterBurst=
TokenBufferFilterLatencySec=