1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-27 18:55:40 +03:00

Merge pull request #16106 from yuwata/network-tc-ets

network: tc: introduce ETS
This commit is contained in:
Lennart Poettering 2020-06-24 19:04:43 +02:00 committed by GitHub
commit f9044b74c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 510 additions and 0 deletions

View File

@ -2929,6 +2929,58 @@
</variablelist>
</refsect1>
<refsect1>
<title>[EnhancedTransmissionSelection] Section Options</title>
<para>The <literal>[EnhancedTransmissionSelection]</literal> section manages the queueing discipline (qdisc) of
Enhanced Transmission Selection (ETS).</para>
<variablelist class='network-directives'>
<xi:include href="tc.xml" xpointer="qdisc-parent" />
<xi:include href="tc.xml" xpointer="qdisc-handle" />
<varlistentry>
<term><varname>Bands=</varname></term>
<listitem>
<para>Specifies the number of bands. An unsigned integer ranges 1 to 16. This value has to be
at least large enough to cover the strict bands specified through the
<varname>StrictBands=</varname> and bandwidth-sharing bands specified in
<varname>QuantumBytes=</varname>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>StrictBands=</varname></term>
<listitem>
<para>Specifies the number of bands that should be created in strict mode. An unsigned integer
ranges 1 to 16.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>QuantumBytes=</varname></term>
<listitem>
<para>Specifies the white-space separated list of quantum used in band-sharing bands. When
suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes,
respectively, to the base of 1024. This setting can be specified multiple times. If an empty
string is assigned, then the all previous assignments are cleared.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>PriorityMap=</varname></term>
<listitem>
<para>The priority map maps the priority of a packet to a band. The argument is a white-space
separated list of numbers. The first number indicates which band the packets with priority
0 should be put to, the second is for priority 1, and so on. There can be up to 16 numbers in
the list. If there are fewer, the default band that traffic with one of the unmentioned
priorities goes to is the last one. Each band number must be 0..255. This setting can be
specified multiple times. If an empty string is assigned, then the all previous assignments
are cleared.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[GenericRandomEarlyDetection] Section Options</title>
<para>The <literal>[GenericRandomEarlyDetection]</literal> section manages the queueing discipline

View File

@ -762,6 +762,32 @@ static const NLType rtnl_tca_option_data_drr_types[] = {
[TCA_DRR_QUANTUM] = { .type = NETLINK_TYPE_U32 },
};
static const NLType rtnl_tca_option_data_ets_quanta_types[] = {
[TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32, },
};
static const NLTypeSystem rtnl_tca_option_data_ets_quanta_type_system = {
.count = ELEMENTSOF(rtnl_tca_option_data_ets_quanta_types),
.types = rtnl_tca_option_data_ets_quanta_types,
};
static const NLType rtnl_tca_option_data_ets_prio_types[] = {
[TCA_ETS_PRIOMAP_BAND] = { .type = NETLINK_TYPE_U8, },
};
static const NLTypeSystem rtnl_tca_option_data_ets_prio_type_system = {
.count = ELEMENTSOF(rtnl_tca_option_data_ets_prio_types),
.types = rtnl_tca_option_data_ets_prio_types,
};
static const NLType rtnl_tca_option_data_ets_types[] = {
[TCA_ETS_NBANDS] = { .type = NETLINK_TYPE_U8 },
[TCA_ETS_NSTRICT] = { .type = NETLINK_TYPE_U8 },
[TCA_ETS_QUANTA] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_quanta_type_system },
[TCA_ETS_PRIOMAP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_prio_type_system },
[TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32 },
};
static const NLType rtnl_tca_option_data_fq_types[] = {
[TCA_FQ_PLIMIT] = { .type = NETLINK_TYPE_U32 },
[TCA_FQ_FLOW_PLIMIT] = { .type = NETLINK_TYPE_U32 },
@ -833,6 +859,7 @@ static const char* const nl_union_tca_option_data_table[] = {
[NL_UNION_TCA_OPTION_DATA_CAKE] = "cake",
[NL_UNION_TCA_OPTION_DATA_CODEL] = "codel",
[NL_UNION_TCA_OPTION_DATA_DRR] = "drr",
[NL_UNION_TCA_OPTION_DATA_ETS] = "ets",
[NL_UNION_TCA_OPTION_DATA_FQ] = "fq",
[NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = "fq_codel",
[NL_UNION_TCA_OPTION_DATA_GRED] = "gred",
@ -853,6 +880,8 @@ static const NLTypeSystem rtnl_tca_option_data_type_systems[] = {
.types = rtnl_tca_option_data_codel_types },
[NL_UNION_TCA_OPTION_DATA_DRR] = { .count = ELEMENTSOF(rtnl_tca_option_data_drr_types),
.types = rtnl_tca_option_data_drr_types },
[NL_UNION_TCA_OPTION_DATA_ETS] = { .count = ELEMENTSOF(rtnl_tca_option_data_ets_types),
.types = rtnl_tca_option_data_ets_types },
[NL_UNION_TCA_OPTION_DATA_FQ] = { .count = ELEMENTSOF(rtnl_tca_option_data_fq_types),
.types = rtnl_tca_option_data_fq_types },
[NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = { .count = ELEMENTSOF(rtnl_tca_option_data_fq_codel_types),

View File

@ -99,6 +99,7 @@ typedef enum NLUnionTCAOptionData {
NL_UNION_TCA_OPTION_DATA_CAKE,
NL_UNION_TCA_OPTION_DATA_CODEL,
NL_UNION_TCA_OPTION_DATA_DRR,
NL_UNION_TCA_OPTION_DATA_ETS,
NL_UNION_TCA_OPTION_DATA_FQ,
NL_UNION_TCA_OPTION_DATA_FQ_CODEL,
NL_UNION_TCA_OPTION_DATA_GRED,

View File

@ -115,6 +115,8 @@ sources = files('''
tc/codel.h
tc/drr.c
tc/drr.h
tc/ets.c
tc/ets.h
tc/fifo.c
tc/fifo.h
tc/fq.c

View File

@ -309,6 +309,12 @@ DeficitRoundRobinScheduler.Handle, config_parse_qdisc_handle,
DeficitRoundRobinSchedulerClass.Parent, config_parse_tclass_parent, TCLASS_KIND_DRR, 0
DeficitRoundRobinSchedulerClass.ClassId, config_parse_tclass_classid, TCLASS_KIND_DRR, 0
DeficitRoundRobinSchedulerClass.Quantum, config_parse_drr_size, TCLASS_KIND_DRR, 0
EnhancedTransmissionSelection.Parent, config_parse_qdisc_parent, QDISC_KIND_ETS, 0
EnhancedTransmissionSelection.Handle, config_parse_qdisc_handle, QDISC_KIND_ETS, 0
EnhancedTransmissionSelection.Bands, config_parse_ets_u8, QDISC_KIND_ETS, 0
EnhancedTransmissionSelection.StrictBands, config_parse_ets_u8, QDISC_KIND_ETS, 0
EnhancedTransmissionSelection.QuantumBytes, config_parse_ets_quanta, QDISC_KIND_ETS, 0
EnhancedTransmissionSelection.PriorityMap, config_parse_ets_prio, QDISC_KIND_ETS, 0
PFIFO.Parent, config_parse_qdisc_parent, QDISC_KIND_PFIFO, 0
PFIFO.Handle, config_parse_qdisc_handle, QDISC_KIND_PFIFO, 0
PFIFO.PacketLimit, config_parse_pfifo_size, QDISC_KIND_PFIFO, 0

View File

@ -512,6 +512,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
"ControlledDelay\0"
"DeficitRoundRobinScheduler\0"
"DeficitRoundRobinSchedulerClass\0"
"EnhancedTransmissionSelection\0"
"FairQueueing\0"
"FairQueueingControlledDelay\0"
"GenericRandomEarlyDetection\0"

338
src/network/tc/ets.c Normal file
View File

@ -0,0 +1,338 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <linux/pkt_sched.h>
#include "alloc-util.h"
#include "conf-parser.h"
#include "ets.h"
#include "memory-util.h"
#include "netlink-util.h"
#include "parse-util.h"
#include "qdisc.h"
#include "string-util.h"
#include "tc-util.h"
static int enhanced_transmission_selection_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
EnhancedTransmissionSelection *ets;
int r;
assert(link);
assert(qdisc);
assert(req);
ets = ETS(qdisc);
r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "ets");
if (r < 0)
return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
r = sd_netlink_message_append_u8(req, TCA_ETS_NBANDS, ets->n_bands);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_ETS_NBANDS attribute: %m");
if (ets->n_strict > 0) {
r = sd_netlink_message_append_u8(req, TCA_ETS_NSTRICT, ets->n_strict);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_ETS_NSTRICT attribute: %m");
}
if (ets->n_quanta > 0) {
r = sd_netlink_message_open_container(req, TCA_ETS_QUANTA);
if (r < 0)
return log_link_error_errno(link, r, "Could not open container TCA_ETS_QUANTA: %m");
for (unsigned i = 0; i < ets->n_quanta; i++) {
r = sd_netlink_message_append_u32(req, TCA_ETS_QUANTA_BAND, ets->quanta[i]);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_ETS_QUANTA_BAND attribute: %m");
}
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_link_error_errno(link, r, "Could not close container TCA_ETS_QUANTA: %m");
}
if (ets->n_prio > 0) {
r = sd_netlink_message_open_container(req, TCA_ETS_PRIOMAP);
if (r < 0)
return log_link_error_errno(link, r, "Could not open container TCA_ETS_PRIOMAP: %m");
for (unsigned i = 0; i < ets->n_prio; i++) {
r = sd_netlink_message_append_u8(req, TCA_ETS_PRIOMAP_BAND, ets->prio[i]);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_ETS_PRIOMAP_BAND attribute: %m");
}
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_link_error_errno(link, r, "Could not close container TCA_ETS_PRIOMAP: %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_ets_u8(
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;
EnhancedTransmissionSelection *ets;
Network *network = data;
uint8_t v, *p;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_ETS, 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");
ets = ETS(qdisc);
if (streq(lvalue, "Bands"))
p = &ets->n_bands;
else if (streq(lvalue, "StrictBands"))
p = &ets->n_strict;
else
assert_not_reached("Invalid lvalue.");
if (isempty(rvalue)) {
*p = 0;
qdisc = NULL;
return 0;
}
r = safe_atou8(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 > TCQ_ETS_MAX_BANDS) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Invalid '%s='. The value must be <= %d, ignoring assignment: %s",
lvalue, TCQ_ETS_MAX_BANDS, rvalue);
return 0;
}
*p = v;
qdisc = NULL;
return 0;
}
int config_parse_ets_quanta(
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;
EnhancedTransmissionSelection *ets;
Network *network = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_ETS, 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");
ets = ETS(qdisc);
if (isempty(rvalue)) {
memzero(ets->quanta, sizeof(uint32_t) * TCQ_ETS_MAX_BANDS);
ets->n_quanta = 0;
qdisc = NULL;
return 0;
}
for (const char *p = rvalue;;) {
_cleanup_free_ char *word = NULL;
uint64_t v;
r = extract_first_word(&p, &word, NULL, 0);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to extract next value, ignoring: %m");
continue;
}
if (r == 0)
break;
r = parse_size(word, 1024, &v);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, word);
continue;
}
if (v == 0 || v > UINT32_MAX) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Invalid '%s=', ignoring assignment: %s",
lvalue, word);
continue;
}
if (ets->n_quanta >= TCQ_ETS_MAX_BANDS) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Too many quanta in '%s=', ignoring assignment: %s",
lvalue, word);
continue;
}
ets->quanta[ets->n_quanta++] = v;
}
qdisc = NULL;
return 0;
}
int config_parse_ets_prio(
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;
EnhancedTransmissionSelection *ets;
Network *network = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_ETS, 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");
ets = ETS(qdisc);
if (isempty(rvalue)) {
memzero(ets->prio, sizeof(uint8_t) * (TC_PRIO_MAX + 1));
ets->n_prio = 0;
qdisc = NULL;
return 0;
}
for (const char *p = rvalue;;) {
_cleanup_free_ char *word = NULL;
uint8_t v;
r = extract_first_word(&p, &word, NULL, 0);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to extract next value, ignoring: %m");
continue;
}
if (r == 0)
break;
r = safe_atou8(word, &v);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, word);
continue;
}
if (ets->n_quanta > TC_PRIO_MAX) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Too many priomap in '%s=', ignoring assignment: %s",
lvalue, word);
continue;
}
ets->prio[ets->n_prio++] = v;
}
qdisc = NULL;
return 0;
}
static int enhanced_transmission_selection_verify(QDisc *qdisc) {
EnhancedTransmissionSelection *ets;
assert(qdisc);
ets = ETS(qdisc);
if (ets->n_bands == 0)
ets->n_bands = ets->n_strict + ets->n_quanta;
if (ets->n_bands == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: At least one of Band=, Strict=, or Quanta= must be specified. "
"Ignoring [EnhancedTransmissionSelection] section from line %u.",
qdisc->section->filename, qdisc->section->line);
if (ets->n_bands < ets->n_strict + ets->n_quanta)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Not enough total bands to cover all the strict bands and quanta. "
"Ignoring [EnhancedTransmissionSelection] section from line %u.",
qdisc->section->filename, qdisc->section->line);
for (unsigned i = 0; i < ets->n_prio; i++)
if (ets->prio[i] >= ets->n_bands)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: PriorityMap= element is out of bands. "
"Ignoring [EnhancedTransmissionSelection] section from line %u.",
qdisc->section->filename, qdisc->section->line);
return 0;
}
const QDiscVTable ets_vtable = {
.object_size = sizeof(EnhancedTransmissionSelection),
.tca_kind = "ets",
.fill_message = enhanced_transmission_selection_fill_message,
.verify = enhanced_transmission_selection_verify,
};

25
src/network/tc/ets.h Normal file
View File

@ -0,0 +1,25 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <linux/pkt_sched.h>
#include "conf-parser.h"
#include "qdisc.h"
typedef struct EnhancedTransmissionSelection {
QDisc meta;
uint8_t n_bands;
uint8_t n_strict;
unsigned n_quanta;
uint32_t quanta[TCQ_ETS_MAX_BANDS];
unsigned n_prio;
uint8_t prio[TC_PRIO_MAX + 1];
} EnhancedTransmissionSelection;
DEFINE_QDISC_CAST(ETS, EnhancedTransmissionSelection);
extern const QDiscVTable ets_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_ets_u8);
CONFIG_PARSER_PROTOTYPE(config_parse_ets_quanta);
CONFIG_PARSER_PROTOTYPE(config_parse_ets_prio);

View File

@ -20,6 +20,7 @@ const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = {
[QDISC_KIND_CAKE] = &cake_vtable,
[QDISC_KIND_CODEL] = &codel_vtable,
[QDISC_KIND_DRR] = &drr_vtable,
[QDISC_KIND_ETS] = &ets_vtable,
[QDISC_KIND_FQ] = &fq_vtable,
[QDISC_KIND_FQ_CODEL] = &fq_codel_vtable,
[QDISC_KIND_GRED] = &gred_vtable,

View File

@ -13,6 +13,7 @@ typedef enum QDiscKind {
QDISC_KIND_CAKE,
QDISC_KIND_CODEL,
QDISC_KIND_DRR,
QDISC_KIND_ETS,
QDISC_KIND_FQ,
QDISC_KIND_FQ_CODEL,
QDISC_KIND_GRED,
@ -87,6 +88,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_handle);
#include "cake.h"
#include "codel.h"
#include "ets.h"
#include "fifo.h"
#include "fq-codel.h"
#include "fq.h"

View File

@ -431,6 +431,13 @@ Handle=
Parent=
ClassId=
Quantum=
[EnhancedTransmissionSelection]
Parent=
Handle=
Bands=
StrictBands=
QuantumBytes=
PriorityMap=
[HeavyHitterFilter]
Parent=
Handle=

View File

@ -0,0 +1,20 @@
[Match]
Name=dummy98
[Network]
IPv6AcceptRA=no
Address=10.1.2.3/16
[EnhancedTransmissionSelection]
Parent=root
Handle=3a
Bands=10
StrictBands=3
QuantumBytes=2 4 6
QuantumBytes=
QuantumBytes=1 2 3
QuantumBytes=4 5
PriorityMap=8 7 6 5
PriorityMap=
PriorityMap=3 4 5
PriorityMap=6 7

View File

@ -194,6 +194,18 @@ def expectedFailureIfHHFIsNotAvailable():
return f
def expectedFailureIfETSIsNotAvailable():
def f(func):
call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
rc = call('tc qdisc add dev dummy98 parent root ets bands 10', stderr=subprocess.DEVNULL)
call('ip link del dummy98', stderr=subprocess.DEVNULL)
if rc == 0:
return func
else:
return unittest.expectedFailure(func)
return f
def setUpModule():
global running_units
@ -1673,6 +1685,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-qdisc-cake.network',
'25-qdisc-clsact-and-htb.network',
'25-qdisc-drr.network',
'25-qdisc-ets.network',
'25-qdisc-hhf.network',
'25-qdisc-ingress-netem-compat.network',
'25-qdisc-pie.network',
@ -2480,6 +2493,19 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'qdisc hhf 3a: root')
self.assertRegex(output, 'limit 1022p')
@expectedFailureIfETSIsNotAvailable()
def test_qdisc_ets(self):
copy_unit_to_networkd_unit_path('25-qdisc-ets.network', '12-dummy.netdev')
start_networkd()
self.wait_online(['dummy98:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
self.assertRegex(output, 'qdisc ets 3a: root')
self.assertRegex(output, 'bands 10 strict 3')
self.assertRegex(output, 'quanta 1 2 3 4 5')
self.assertRegex(output, 'priomap 3 4 5 6 7')
class NetworkdStateFileTests(unittest.TestCase, Utilities):
links = [
'dummy98',