network: tc: support HTB class

This commit is contained in:
Yu Watanabe 2020-02-12 09:52:56 +09:00
parent 4666f63bb8
commit 19f86a6351
8 changed files with 269 additions and 1 deletions

View File

@ -2832,6 +2832,60 @@
</variablelist>
</refsect1>
<refsect1>
<title>[HierarchyTokenBucketClass] Section Options</title>
<para>The <literal>[HierarchyTokenBucketClass]</literal> section manages the traffic control class of
hierarchy token bucket (htb).</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>Parent=</varname></term>
<listitem>
<para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
or a qdisc id. The qdisc id takes the major and minor number in hexadecimal ranges 1 to ffff
separated with a colon (<literal>major:minor</literal>). Defaults to <literal>root</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ClassId=</varname></term>
<listitem>
<para>Specifies the major and minur number of unique identifier of the class, known as the
class ID. Each number is in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<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>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Rate=</varname></term>
<listitem>
<para>Specifies the maximum rate this class and all its children are guaranteed. When suffixed
with K, M, or G, the specified size is parsed as Kilobits, Megabits, or Gigabits, respectively,
to the base of 1000. This setting is mandatory.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>CeilRate=</varname></term>
<listitem>
<para>Specifies the maximum rate at which a class can send, if its parent has bandwidth to spare.
When suffixed with K, M, or G, the specified size is parsed as Kilobits, Megabits, or Gigabits,
respectively, to the base of 1000. When unset, the value specified with <varname>Rate=</varname>
is used.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[BridgeVLAN] Section Options</title>
<para>The <literal>[BridgeVLAN]</literal> section manages the VLAN ID configuration of a bridge port and accepts

View File

@ -781,7 +781,12 @@ static const NLType rtnl_tca_option_data_fq_codel_types[] = {
};
static const NLType rtnl_tca_option_data_htb_types[] = {
[TCA_HTB_INIT] = { .size = sizeof(struct tc_htb_glob) },
[TCA_HTB_PARMS] = { .size = sizeof(struct tc_htb_opt) },
[TCA_HTB_INIT] = { .size = sizeof(struct tc_htb_glob) },
[TCA_HTB_CTAB] = { .size = TC_RTAB_SIZE },
[TCA_HTB_RTAB] = { .size = TC_RTAB_SIZE },
[TCA_HTB_RATE64] = { .type = NETLINK_TYPE_U64 },
[TCA_HTB_CEIL64] = { .type = NETLINK_TYPE_U64 },
};
static const NLType rtnl_tca_option_data_tbf_types[] = {

View File

@ -285,6 +285,11 @@ FairQueueingControlledDelay.ECN, config_parse_fair_queueing_controll
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
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
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

@ -488,6 +488,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
"FairQueueing\0"
"FairQueueingControlledDelay\0"
"HierarchyTokenBucket\0"
"HierarchyTokenBucketClass\0"
"NetworkEmulator\0"
"StochasticFairnessQueueing\0"
"TokenBucketFilter\0"

View File

@ -9,6 +9,7 @@
#include "qdisc.h"
#include "htb.h"
#include "string-util.h"
#include "tc-util.h"
static int hierarchy_token_bucket_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
HierarchyTokenBucket *htb;
@ -96,3 +97,183 @@ const QDiscVTable htb_vtable = {
.tca_kind = "htb",
.fill_message = hierarchy_token_bucket_fill_message,
};
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 */
int r;
assert(link);
assert(tclass);
assert(req);
htb = TCLASS_TO_HTB(tclass);
if (htb->ceil_rate == 0)
htb->ceil_rate = htb->rate;
opt.prio = htb->priority;
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);
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);
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);
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);
if (r < 0)
return log_link_error_errno(link, r, "Failed to calculate ceil rate table: %m");
r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "htb");
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_HTB_PARMS, &opt, sizeof(opt));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_HTB_PARMS attribute: %m");
if (htb->rate >= (1ULL << 32)) {
r = sd_netlink_message_append_u64(req, TCA_HTB_RATE64, htb->rate);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_HTB_RATE64 attribute: %m");
}
if (htb->ceil_rate >= (1ULL << 32)) {
r = sd_netlink_message_append_u64(req, TCA_HTB_CEIL64, htb->ceil_rate);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_HTB_CEIL64 attribute: %m");
}
r = sd_netlink_message_append_data(req, TCA_HTB_RTAB, rtab, sizeof(rtab));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_HTB_RTAB attribute: %m");
r = sd_netlink_message_append_data(req, TCA_HTB_CTAB, ctab, sizeof(ctab));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_HTB_CTAB 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_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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
HierarchyTokenBucketClass *htb;
Network *network = data;
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)) {
htb->priority = 0;
tclass = NULL;
return 0;
}
r = safe_atou32(rvalue, &htb->priority);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
tclass = NULL;
return 0;
}
int config_parse_hierarchy_token_bucket_rate(
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 (streq(lvalue, "Rate"))
v = &htb->rate;
else if (streq(lvalue, "CeilRate"))
v = &htb->ceil_rate;
else
assert_not_reached("Invalid lvalue");
if (isempty(rvalue)) {
*v = 0;
tclass = NULL;
return 0;
}
r = parse_size(rvalue, 1000, v);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
*v /= 8;
tclass = NULL;
return 0;
}
const TClassVTable htb_tclass_vtable = {
.object_size = sizeof(HierarchyTokenBucketClass),
.tca_kind = "htb",
.fill_message = hierarchy_token_bucket_class_fill_message,
};

View File

@ -3,6 +3,7 @@
#include "conf-parser.h"
#include "qdisc.h"
#include "tclass.h"
typedef struct HierarchyTokenBucket {
QDisc meta;
@ -14,3 +15,17 @@ DEFINE_QDISC_CAST(HTB, HierarchyTokenBucket);
extern const QDiscVTable htb_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_default_class);
typedef struct HierarchyTokenBucketClass {
TClass meta;
uint32_t priority;
uint64_t rate;
uint64_t ceil_rate;
} 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);

View File

@ -16,6 +16,7 @@
#include "tclass.h"
const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = {
[TCLASS_KIND_HTB] = &htb_tclass_vtable,
};
static int tclass_new(TClassKind kind, TClass **ret) {

View File

@ -341,3 +341,9 @@ Id=
Parent=
Handle=
DefaultClass=
[HierarchyTokenBucketClass]
Parent=
ClassId=
Priority=
Rate=
CeilRate=