network: tc: add more settings for HTB

Closes #15213.
This commit is contained in:
Yu Watanabe 2020-06-08 17:36:52 +09:00
parent 120b5c0bbe
commit d9eacc1cdd
7 changed files with 313 additions and 38 deletions

View File

@ -3209,6 +3209,14 @@
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>
@ -3225,7 +3233,33 @@
<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>
@ -3247,6 +3281,24 @@
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>

View 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

View File

@ -11,10 +11,12 @@
#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,12 +266,11 @@ int config_parse_hierarchy_token_bucket_u32(
if (isempty(rvalue)) {
htb->priority = 0;
tclass = NULL;
return 0;
}
r = safe_atou32(rvalue, &htb->priority);
r = safe_atou32(rvalue, &v);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
@ -210,12 +278,93 @@ int config_parse_hierarchy_token_bucket_u32(
return 0;
}
htb->priority = v;
tclass = NULL;
return 0;
}
int config_parse_hierarchy_token_bucket_rate(
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 = 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_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,
};

View 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);

View 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)

View 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);

View File

@ -378,12 +378,18 @@ Id=
Parent=
Handle=
DefaultClass=
RateToQuantum=
[HierarchyTokenBucketClass]
Parent=
ClassId=
Priority=
QuantumBytes=
MTUBytes=
OverheadBytes=
Rate=
CeilRate=
BufferBytes=
CeilBufferBytes=
[BFIFO]
Parent=
Handle=