network: tc: introduce Enhanced Transmission Selection (ETS)

Closes #15264.
This commit is contained in:
Yu Watanabe 2020-06-09 17:23:11 +09:00
parent 4dec921889
commit d474aa51bf
9 changed files with 434 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

@ -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=