network: tc: introduce QDiscVTable for future extendability

This commit is contained in:
Yu Watanabe 2019-12-11 20:10:29 +09:00
parent 1f9dd3bfdf
commit e8c17dc078
10 changed files with 267 additions and 177 deletions

View file

@ -10,13 +10,16 @@
#include "qdisc.h" #include "qdisc.h"
#include "string-util.h" #include "string-util.h"
int fair_queuing_controlled_delay_fill_message(Link *link, const FairQueuingControlledDelay *fqcd, sd_netlink_message *req) { static int fair_queuing_controlled_delay_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
FairQueuingControlledDelay *fqcd;
int r; int r;
assert(link); assert(link);
assert(fqcd); assert(qdisc);
assert(req); assert(req);
fqcd = FQ_CODEL(qdisc);
r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "fq_codel"); r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "fq_codel");
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
@ -45,6 +48,7 @@ int config_parse_tc_fair_queuing_controlled_delay_limit(
void *userdata) { void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
FairQueuingControlledDelay *fqcd;
Network *network = data; Network *network = data;
int r; int r;
@ -53,18 +57,23 @@ int config_parse_tc_fair_queuing_controlled_delay_limit(
assert(rvalue); assert(rvalue);
assert(data); assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc); r = qdisc_new_static(QDISC_KIND_FQ_CODEL, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0) if (r < 0)
return r; return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
fqcd = FQ_CODEL(qdisc);
if (isempty(rvalue)) { if (isempty(rvalue)) {
qdisc->fq_codel.limit = 0; fqcd->limit = 0;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
} }
r = safe_atou32(rvalue, &qdisc->fq_codel.limit); r = safe_atou32(rvalue, &fqcd->limit);
if (r < 0) { if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s", "Failed to parse '%s=', ignoring assignment: %s",
@ -72,8 +81,13 @@ int config_parse_tc_fair_queuing_controlled_delay_limit(
return 0; return 0;
} }
qdisc->has_fair_queuing_controlled_delay = true;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
} }
const QDiscVTable fq_codel_vtable = {
.object_size = sizeof(FairQueuingControlledDelay),
.tca_kind = "fq_codel",
.fill_message = fair_queuing_controlled_delay_fill_message,
};

View file

@ -2,15 +2,15 @@
* Copyright © 2019 VMware, Inc. */ * Copyright © 2019 VMware, Inc. */
#pragma once #pragma once
#include "sd-netlink.h"
#include "conf-parser.h" #include "conf-parser.h"
#include "networkd-link.h" #include "qdisc.h"
typedef struct FairQueuingControlledDelay { typedef struct FairQueuingControlledDelay {
QDisc meta;
uint32_t limit; uint32_t limit;
} FairQueuingControlledDelay; } FairQueuingControlledDelay;
int fair_queuing_controlled_delay_fill_message(Link *link, const FairQueuingControlledDelay *sfq, sd_netlink_message *req); DEFINE_QDISC_CAST(FQ_CODEL, FairQueuingControlledDelay);
extern const QDiscVTable fq_codel_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_limit); CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_limit);

View file

@ -13,16 +13,19 @@
#include "string-util.h" #include "string-util.h"
#include "tc-util.h" #include "tc-util.h"
int network_emulator_fill_message(Link *link, const NetworkEmulator *ne, sd_netlink_message *req) { static int network_emulator_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
struct tc_netem_qopt opt = { struct tc_netem_qopt opt = {
.limit = 1000, .limit = 1000,
}; };
NetworkEmulator *ne;
int r; int r;
assert(link); assert(link);
assert(ne); assert(qdisc);
assert(req); assert(req);
ne = NETEM(qdisc);
if (ne->limit > 0) if (ne->limit > 0)
opt.limit = ne->limit; opt.limit = ne->limit;
@ -65,6 +68,7 @@ int config_parse_tc_network_emulator_delay(
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
Network *network = data; Network *network = data;
NetworkEmulator *ne;
usec_t u; usec_t u;
int r; int r;
@ -73,15 +77,20 @@ int config_parse_tc_network_emulator_delay(
assert(rvalue); assert(rvalue);
assert(data); assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc); r = qdisc_new_static(QDISC_KIND_NETEM, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0) if (r < 0)
return r; return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
ne = NETEM(qdisc);
if (isempty(rvalue)) { if (isempty(rvalue)) {
if (streq(lvalue, "NetworkEmulatorDelaySec")) if (streq(lvalue, "NetworkEmulatorDelaySec"))
qdisc->ne.delay = USEC_INFINITY; ne->delay = USEC_INFINITY;
else if (streq(lvalue, "NetworkEmulatorDelayJitterSec")) else if (streq(lvalue, "NetworkEmulatorDelayJitterSec"))
qdisc->ne.jitter = USEC_INFINITY; ne->jitter = USEC_INFINITY;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
@ -96,11 +105,10 @@ int config_parse_tc_network_emulator_delay(
} }
if (streq(lvalue, "NetworkEmulatorDelaySec")) if (streq(lvalue, "NetworkEmulatorDelaySec"))
qdisc->ne.delay = u; ne->delay = u;
else if (streq(lvalue, "NetworkEmulatorDelayJitterSec")) else if (streq(lvalue, "NetworkEmulatorDelayJitterSec"))
qdisc->ne.jitter = u; ne->jitter = u;
qdisc->has_network_emulator = true;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
@ -120,6 +128,7 @@ int config_parse_tc_network_emulator_rate(
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
Network *network = data; Network *network = data;
NetworkEmulator *ne;
uint32_t rate; uint32_t rate;
int r; int r;
@ -128,12 +137,20 @@ int config_parse_tc_network_emulator_rate(
assert(rvalue); assert(rvalue);
assert(data); assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc); r = qdisc_new_static(QDISC_KIND_NETEM, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0) if (r < 0)
return r; return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
ne = NETEM(qdisc);
if (isempty(rvalue)) { if (isempty(rvalue)) {
qdisc->ne.loss = 0; if (streq(lvalue, "NetworkEmulatorLossRate"))
ne->loss = 0;
else if (streq(lvalue, "NetworkEmulatorDuplicateRate"))
ne->duplicate = 0;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
@ -148,9 +165,9 @@ int config_parse_tc_network_emulator_rate(
} }
if (streq(lvalue, "NetworkEmulatorLossRate")) if (streq(lvalue, "NetworkEmulatorLossRate"))
qdisc->ne.loss = rate; ne->loss = rate;
else if (streq(lvalue, "NetworkEmulatorDuplicateRate")) else if (streq(lvalue, "NetworkEmulatorDuplicateRate"))
qdisc->ne.duplicate = rate; ne->duplicate = rate;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
@ -170,6 +187,7 @@ int config_parse_tc_network_emulator_packet_limit(
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
Network *network = data; Network *network = data;
NetworkEmulator *ne;
int r; int r;
assert(filename); assert(filename);
@ -177,18 +195,23 @@ int config_parse_tc_network_emulator_packet_limit(
assert(rvalue); assert(rvalue);
assert(data); assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc); r = qdisc_new_static(QDISC_KIND_NETEM, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0) if (r < 0)
return r; return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
ne = NETEM(qdisc);
if (isempty(rvalue)) { if (isempty(rvalue)) {
qdisc->ne.limit = 0; ne->limit = 0;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
} }
r = safe_atou(rvalue, &qdisc->ne.limit); r = safe_atou(rvalue, &ne->limit);
if (r < 0) { if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse 'NetworkEmulatorPacketLimit=', ignoring assignment: %s", "Failed to parse 'NetworkEmulatorPacketLimit=', ignoring assignment: %s",
@ -199,3 +222,9 @@ int config_parse_tc_network_emulator_packet_limit(
qdisc = NULL; qdisc = NULL;
return 0; return 0;
} }
const QDiscVTable netem_vtable = {
.object_size = sizeof(NetworkEmulator),
.tca_kind = "netem",
.fill_message = network_emulator_fill_message,
};

View file

@ -2,13 +2,13 @@
* Copyright © 2019 VMware, Inc. */ * Copyright © 2019 VMware, Inc. */
#pragma once #pragma once
#include "sd-netlink.h"
#include "conf-parser.h" #include "conf-parser.h"
#include "networkd-link.h" #include "qdisc.h"
#include "time-util.h" #include "time-util.h"
typedef struct NetworkEmulator { typedef struct NetworkEmulator {
QDisc meta;
usec_t delay; usec_t delay;
usec_t jitter; usec_t jitter;
@ -17,7 +17,8 @@ typedef struct NetworkEmulator {
uint32_t duplicate; uint32_t duplicate;
} NetworkEmulator; } NetworkEmulator;
int network_emulator_fill_message(Link *link, const NetworkEmulator *ne, sd_netlink_message *req); DEFINE_QDISC_CAST(NETEM, NetworkEmulator);
extern const QDiscVTable netem_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_delay); CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_delay);
CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_rate); CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_rate);

View file

@ -13,65 +13,94 @@
#include "set.h" #include "set.h"
#include "string-util.h" #include "string-util.h"
static int qdisc_new(QDisc **ret) { const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = {
[QDISC_KIND_FQ_CODEL] = &fq_codel_vtable,
[QDISC_KIND_NETEM] = &netem_vtable,
[QDISC_KIND_SFQ] = &sfq_vtable,
[QDISC_KIND_TBF] = &tbf_vtable,
};
static int qdisc_new(QDiscKind kind, QDisc **ret) {
QDisc *qdisc; QDisc *qdisc;
qdisc = new(QDisc, 1); if (kind == _QDISC_KIND_INVALID) {
if (!qdisc) qdisc = new(QDisc, 1);
return -ENOMEM; if (!qdisc)
return -ENOMEM;
*qdisc = (QDisc) { *qdisc = (QDisc) {
.family = AF_UNSPEC, .family = AF_UNSPEC,
.parent = TC_H_ROOT, .parent = TC_H_ROOT,
}; .kind = kind,
};
} else {
qdisc = malloc0(qdisc_vtable[kind]->object_size);
if (!qdisc)
return -ENOMEM;
qdisc->family = AF_UNSPEC;
qdisc->parent = TC_H_ROOT;
qdisc->kind = kind;
}
*ret = TAKE_PTR(qdisc); *ret = TAKE_PTR(qdisc);
return 0; return 0;
} }
int qdisc_new_static(Network *network, const char *filename, unsigned section_line, QDisc **ret) { int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(qdisc_freep) QDisc *qdisc = NULL; _cleanup_(qdisc_freep) QDisc *qdisc = NULL;
QDisc *existing;
int r; int r;
assert(network); assert(network);
assert(ret); assert(ret);
assert(!!filename == (section_line > 0)); assert(filename);
assert(section_line > 0);
if (filename) { r = network_config_section_new(filename, section_line, &n);
r = network_config_section_new(filename, section_line, &n); if (r < 0)
if (r < 0) return r;
return r;
qdisc = ordered_hashmap_get(network->qdiscs_by_section, n); existing = ordered_hashmap_get(network->qdiscs_by_section, n);
if (qdisc) { if (existing) {
*ret = TAKE_PTR(qdisc); if (existing->kind != _QDISC_KIND_INVALID &&
kind != _QDISC_KIND_INVALID &&
existing->kind != kind)
return -EINVAL;
if (existing->kind == kind || kind == _QDISC_KIND_INVALID) {
*ret = existing;
return 0; return 0;
} }
} }
r = qdisc_new(&qdisc); r = qdisc_new(kind, &qdisc);
if (r < 0) if (r < 0)
return r; return r;
qdisc->network = network; if (existing) {
qdisc->family = existing->family;
qdisc->handle = existing->handle;
qdisc->parent = existing->parent;
qdisc->tca_kind = TAKE_PTR(existing->tca_kind);
if (filename) { qdisc_free(ordered_hashmap_remove(network->qdiscs_by_section, n));
qdisc->section = TAKE_PTR(n);
r = ordered_hashmap_ensure_allocated(&network->qdiscs_by_section, &network_config_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(network->qdiscs_by_section, qdisc->section, qdisc);
if (r < 0)
return r;
} }
*ret = TAKE_PTR(qdisc); qdisc->network = network;
qdisc->section = TAKE_PTR(n);
r = ordered_hashmap_ensure_allocated(&network->qdiscs_by_section, &network_config_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(network->qdiscs_by_section, qdisc->section, qdisc);
if (r < 0)
return r;
*ret = TAKE_PTR(qdisc);
return 0; return 0;
} }
@ -116,8 +145,6 @@ static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int qdisc_configure(Link *link, QDisc *qdisc) { int qdisc_configure(Link *link, QDisc *qdisc) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
_cleanup_free_ char *tca_kind = NULL;
char *p;
int r; int r;
assert(link); assert(link);
@ -139,49 +166,16 @@ int qdisc_configure(Link *link, QDisc *qdisc) {
return log_link_error_errno(link, r, "Could not set tcm_handle message: %m"); return log_link_error_errno(link, r, "Could not set tcm_handle message: %m");
} }
if (qdisc->has_network_emulator) { if (QDISC_VTABLE(qdisc)) {
r = free_and_strdup(&tca_kind, "netem"); r = sd_netlink_message_append_string(req, TCA_KIND, QDISC_VTABLE(qdisc)->tca_kind);
if (r < 0) if (r < 0)
return log_oom(); return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
r = network_emulator_fill_message(link, &qdisc->ne, req); r = QDISC_VTABLE(qdisc)->fill_message(link, qdisc, req);
if (r < 0) if (r < 0)
return r; return r;
} } else {
r = sd_netlink_message_append_string(req, TCA_KIND, qdisc->tca_kind);
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 (qdisc->has_stochastic_fairness_queueing) {
r = free_and_strdup(&tca_kind, "sfq");
if (r < 0)
return log_oom();
r = stochastic_fairness_queueing_fill_message(link, &qdisc->sfq, req);
if (r < 0)
return r;
}
if (qdisc->has_fair_queuing_controlled_delay) {
r = free_and_strdup(&tca_kind, "fq_codel");
if (r < 0)
return log_oom();
r = fair_queuing_controlled_delay_fill_message(link, &qdisc->fq_codel, req);
if (r < 0)
return r;
}
p = tca_kind ?:qdisc->tca_kind;
if (p) {
r = sd_netlink_message_append_string(req, TCA_KIND, p);
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m"); return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
} }
@ -197,7 +191,6 @@ int qdisc_configure(Link *link, QDisc *qdisc) {
} }
int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) { int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) {
unsigned i;
int r; int r;
assert(qdisc); assert(qdisc);
@ -207,15 +200,8 @@ int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) {
if (section_is_invalid(qdisc->section)) if (section_is_invalid(qdisc->section))
return -EINVAL; return -EINVAL;
i = qdisc->has_network_emulator + qdisc->has_token_buffer_filter + qdisc->has_stochastic_fairness_queueing; if (QDISC_VTABLE(qdisc) && QDISC_VTABLE(qdisc)->verify) {
if (i > 1) r = QDISC_VTABLE(qdisc)->verify(qdisc);
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: TrafficControlQueueingDiscipline section has more than one type of discipline. "
"Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
qdisc->section->filename, qdisc->section->line);
if (qdisc->has_token_buffer_filter) {
r = token_buffer_filter_section_verify(&qdisc->tbf, qdisc->section);
if (r < 0) if (r < 0)
return r; return r;
} }
@ -260,7 +246,7 @@ int config_parse_tc_qdiscs_parent(
assert(rvalue); assert(rvalue);
assert(data); assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc); r = qdisc_new_static(_QDISC_KIND_INVALID, network, filename, section_line, &qdisc);
if (r < 0) if (r < 0)
return r; return r;

View file

@ -3,42 +3,65 @@
#pragma once #pragma once
#include "conf-parser.h" #include "conf-parser.h"
#include "fq-codel.h"
#include "netem.h"
#include "networkd-link.h" #include "networkd-link.h"
#include "networkd-network.h" #include "networkd-network.h"
#include "networkd-util.h" #include "networkd-util.h"
#include "sfq.h"
#include "tbf.h" typedef enum QDiscKind {
QDISC_KIND_FQ_CODEL,
QDISC_KIND_NETEM,
QDISC_KIND_SFQ,
QDISC_KIND_TBF,
_QDISC_KIND_MAX,
_QDISC_KIND_INVALID = -1,
} QDiscKind;
typedef struct QDisc { typedef struct QDisc {
NetworkConfigSection *section; NetworkConfigSection *section;
Network *network; Network *network;
int family; int family;
uint32_t handle; uint32_t handle;
uint32_t parent; uint32_t parent;
char *tca_kind; char *tca_kind;
bool has_network_emulator:1; QDiscKind kind;
bool has_token_buffer_filter:1;
bool has_stochastic_fairness_queueing:1;
bool has_fair_queuing_controlled_delay:1;
NetworkEmulator ne;
TokenBufferFilter tbf;
StochasticFairnessQueueing sfq;
FairQueuingControlledDelay fq_codel;
} QDisc; } QDisc;
typedef struct QDiscVTable {
size_t object_size;
const char *tca_kind;
int (*fill_message)(Link *link, QDisc *qdisc, sd_netlink_message *m);
int (*verify)(QDisc *qdisc);
} QDiscVTable;
extern const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX];
#define QDISC_VTABLE(q) ((q)->kind != _QDISC_KIND_INVALID ? qdisc_vtable[(q)->kind] : NULL)
/* For casting a qdisc into the various qdisc kinds */
#define DEFINE_QDISC_CAST(UPPERCASE, MixedCase) \
static inline MixedCase* UPPERCASE(QDisc *q) { \
if (_unlikely_(!q || q->kind != QDISC_KIND_##UPPERCASE)) \
return NULL; \
\
return (MixedCase*) q; \
}
/* For casting the various qdisc kinds into a qdisc */
#define QDISC(q) (&(q)->meta)
void qdisc_free(QDisc *qdisc); void qdisc_free(QDisc *qdisc);
int qdisc_new_static(Network *network, const char *filename, unsigned section_line, QDisc **ret); int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret);
int qdisc_configure(Link *link, QDisc *qdisc); int qdisc_configure(Link *link, QDisc *qdisc);
int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact); int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact);
DEFINE_NETWORK_SECTION_FUNCTIONS(QDisc, qdisc_free); DEFINE_NETWORK_SECTION_FUNCTIONS(QDisc, qdisc_free);
CONFIG_PARSER_PROTOTYPE(config_parse_tc_qdiscs_parent); CONFIG_PARSER_PROTOTYPE(config_parse_tc_qdiscs_parent);
#include "fq-codel.h"
#include "netem.h"
#include "sfq.h"
#include "tbf.h"

View file

@ -11,14 +11,17 @@
#include "sfq.h" #include "sfq.h"
#include "string-util.h" #include "string-util.h"
int stochastic_fairness_queueing_fill_message(Link *link, const StochasticFairnessQueueing *sfq, sd_netlink_message *req) { static int stochastic_fairness_queueing_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
StochasticFairnessQueueing *sfq;
struct tc_sfq_qopt_v1 opt = {}; struct tc_sfq_qopt_v1 opt = {};
int r; int r;
assert(link); assert(link);
assert(sfq); assert(qdisc);
assert(req); assert(req);
sfq = SFQ(qdisc);
opt.v0.perturb_period = sfq->perturb_period / USEC_PER_SEC; opt.v0.perturb_period = sfq->perturb_period / USEC_PER_SEC;
r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(struct tc_sfq_qopt_v1)); r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(struct tc_sfq_qopt_v1));
@ -41,6 +44,7 @@ int config_parse_tc_stochastic_fairness_queueing_perturb_period(
void *userdata) { void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
StochasticFairnessQueueing *sfq;
Network *network = data; Network *network = data;
int r; int r;
@ -49,18 +53,23 @@ int config_parse_tc_stochastic_fairness_queueing_perturb_period(
assert(rvalue); assert(rvalue);
assert(data); assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc); r = qdisc_new_static(QDISC_KIND_SFQ, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0) if (r < 0)
return r; return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
sfq = SFQ(qdisc);
if (isempty(rvalue)) { if (isempty(rvalue)) {
qdisc->sfq.perturb_period = 0; sfq->perturb_period = 0;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
} }
r = parse_sec(rvalue, &qdisc->sfq.perturb_period); r = parse_sec(rvalue, &sfq->perturb_period);
if (r < 0) { if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s", "Failed to parse '%s=', ignoring assignment: %s",
@ -68,8 +77,13 @@ int config_parse_tc_stochastic_fairness_queueing_perturb_period(
return 0; return 0;
} }
qdisc->has_stochastic_fairness_queueing = true;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
} }
const QDiscVTable sfq_vtable = {
.object_size = sizeof(StochasticFairnessQueueing),
.tca_kind = "sfq",
.fill_message = stochastic_fairness_queueing_fill_message,
};

View file

@ -2,15 +2,17 @@
* Copyright © 2019 VMware, Inc. */ * Copyright © 2019 VMware, Inc. */
#pragma once #pragma once
#include "sd-netlink.h"
#include "conf-parser.h" #include "conf-parser.h"
#include "networkd-link.h" #include "qdisc.h"
#include "time-util.h"
typedef struct StochasticFairnessQueueing { typedef struct StochasticFairnessQueueing {
QDisc meta;
usec_t perturb_period; usec_t perturb_period;
} StochasticFairnessQueueing; } StochasticFairnessQueueing;
int stochastic_fairness_queueing_fill_message(Link *link, const StochasticFairnessQueueing *sfq, sd_netlink_message *req); DEFINE_QDISC_CAST(SFQ, StochasticFairnessQueueing);
extern const QDiscVTable sfq_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_tc_stochastic_fairness_queueing_perturb_period); CONFIG_PARSER_PROTOTYPE(config_parse_tc_stochastic_fairness_queueing_perturb_period);

View file

@ -15,15 +15,18 @@
#include "tc-util.h" #include "tc-util.h"
#include "util.h" #include "util.h"
int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, sd_netlink_message *req) { static int token_buffer_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
uint32_t rtab[256], ptab[256]; uint32_t rtab[256], ptab[256];
struct tc_tbf_qopt opt = {}; struct tc_tbf_qopt opt = {};
TokenBufferFilter *tbf;
int r; int r;
assert(link); assert(link);
assert(tbf); assert(qdisc);
assert(req); assert(req);
tbf = TBF(qdisc);
opt.rate.rate = tbf->rate >= (1ULL << 32) ? ~0U : tbf->rate; opt.rate.rate = tbf->rate >= (1ULL << 32) ? ~0U : tbf->rate;
opt.peakrate.rate = tbf->peak_rate >= (1ULL << 32) ? ~0U : tbf->peak_rate; opt.peakrate.rate = tbf->peak_rate >= (1ULL << 32) ? ~0U : tbf->peak_rate;
@ -121,6 +124,7 @@ int config_parse_tc_token_buffer_filter_size(
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
Network *network = data; Network *network = data;
TokenBufferFilter *tbf;
uint64_t k; uint64_t k;
int r; int r;
@ -129,23 +133,28 @@ int config_parse_tc_token_buffer_filter_size(
assert(rvalue); assert(rvalue);
assert(data); assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc); r = qdisc_new_static(QDISC_KIND_TBF, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0) if (r < 0)
return r; return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
tbf = TBF(qdisc);
if (isempty(rvalue)) { if (isempty(rvalue)) {
if (streq(lvalue, "TokenBufferFilterRate")) if (streq(lvalue, "TokenBufferFilterRate"))
qdisc->tbf.rate = 0; tbf->rate = 0;
else if (streq(lvalue, "TokenBufferFilterBurst")) else if (streq(lvalue, "TokenBufferFilterBurst"))
qdisc->tbf.burst = 0; tbf->burst = 0;
else if (streq(lvalue, "TokenBufferFilterLimitSize")) else if (streq(lvalue, "TokenBufferFilterLimitSize"))
qdisc->tbf.limit = 0; tbf->limit = 0;
else if (streq(lvalue, "TokenBufferFilterMTUBytes")) else if (streq(lvalue, "TokenBufferFilterMTUBytes"))
qdisc->tbf.mtu = 0; tbf->mtu = 0;
else if (streq(lvalue, "TokenBufferFilterMPUBytes")) else if (streq(lvalue, "TokenBufferFilterMPUBytes"))
qdisc->tbf.mpu = 0; tbf->mpu = 0;
else if (streq(lvalue, "TokenBufferFilterPeakRate")) else if (streq(lvalue, "TokenBufferFilterPeakRate"))
qdisc->tbf.peak_rate = 0; tbf->peak_rate = 0;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
@ -160,19 +169,18 @@ int config_parse_tc_token_buffer_filter_size(
} }
if (streq(lvalue, "TokenBufferFilterRate")) if (streq(lvalue, "TokenBufferFilterRate"))
qdisc->tbf.rate = k / 8; tbf->rate = k / 8;
else if (streq(lvalue, "TokenBufferFilterBurst")) else if (streq(lvalue, "TokenBufferFilterBurst"))
qdisc->tbf.burst = k; tbf->burst = k;
else if (streq(lvalue, "TokenBufferFilterLimitSize")) else if (streq(lvalue, "TokenBufferFilterLimitSize"))
qdisc->tbf.limit = k; tbf->limit = k;
else if (streq(lvalue, "TokenBufferFilterMPUBytes")) else if (streq(lvalue, "TokenBufferFilterMPUBytes"))
qdisc->tbf.mpu = k; tbf->mpu = k;
else if (streq(lvalue, "TokenBufferFilterMTUBytes")) else if (streq(lvalue, "TokenBufferFilterMTUBytes"))
qdisc->tbf.mtu = k; tbf->mtu = k;
else if (streq(lvalue, "TokenBufferFilterPeakRate")) else if (streq(lvalue, "TokenBufferFilterPeakRate"))
qdisc->tbf.peak_rate = k / 8; tbf->peak_rate = k / 8;
qdisc->has_token_buffer_filter = true;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
@ -192,6 +200,7 @@ int config_parse_tc_token_buffer_filter_latency(
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
Network *network = data; Network *network = data;
TokenBufferFilter *tbf;
usec_t u; usec_t u;
int r; int r;
@ -200,12 +209,17 @@ int config_parse_tc_token_buffer_filter_latency(
assert(rvalue); assert(rvalue);
assert(data); assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc); r = qdisc_new_static(QDISC_KIND_TBF, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0) if (r < 0)
return r; return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
tbf = TBF(qdisc);
if (isempty(rvalue)) { if (isempty(rvalue)) {
qdisc->tbf.latency = 0; tbf->latency = 0;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
@ -219,44 +233,52 @@ int config_parse_tc_token_buffer_filter_latency(
return 0; return 0;
} }
qdisc->tbf.latency = u; tbf->latency = u;
qdisc->has_token_buffer_filter = true;
qdisc = NULL; qdisc = NULL;
return 0; return 0;
} }
int token_buffer_filter_section_verify(const TokenBufferFilter *tbf, const NetworkConfigSection *section) { static int token_buffer_filter_verify(QDisc *qdisc) {
TokenBufferFilter *tbf = TBF(qdisc);
if (tbf->limit > 0 && tbf->latency > 0) if (tbf->limit > 0 && tbf->latency > 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Specifying both TokenBufferFilterLimitSize= and TokenBufferFilterLatencySec= is not allowed. " "%s: Specifying both TokenBufferFilterLimitSize= and TokenBufferFilterLatencySec= is not allowed. "
"Ignoring [TrafficControlQueueingDiscipline] section from line %u.", "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
section->filename, section->line); qdisc->section->filename, qdisc->section->line);
if (tbf->limit == 0 && tbf->latency == 0) if (tbf->limit == 0 && tbf->latency == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Either TokenBufferFilterLimitSize= or TokenBufferFilterLatencySec= is required. " "%s: Either TokenBufferFilterLimitSize= or TokenBufferFilterLatencySec= is required. "
"Ignoring [TrafficControlQueueingDiscipline] section from line %u.", "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
section->filename, section->line); qdisc->section->filename, qdisc->section->line);
if (tbf->rate == 0) if (tbf->rate == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: TokenBufferFilterRate= is mandatory. " "%s: TokenBufferFilterRate= is mandatory. "
"Ignoring [TrafficControlQueueingDiscipline] section from line %u.", "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
section->filename, section->line); qdisc->section->filename, qdisc->section->line);
if (tbf->burst == 0) if (tbf->burst == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: TokenBufferFilterBurst= is mandatory. " "%s: TokenBufferFilterBurst= is mandatory. "
"Ignoring [TrafficControlQueueingDiscipline] section from line %u.", "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
section->filename, section->line); qdisc->section->filename, qdisc->section->line);
if (tbf->peak_rate > 0 && tbf->mtu == 0) if (tbf->peak_rate > 0 && tbf->mtu == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: TokenBufferFilterMTUBytes= is mandatory when TokenBufferFilterPeakRate= is specified. " "%s: TokenBufferFilterMTUBytes= is mandatory when TokenBufferFilterPeakRate= is specified. "
"Ignoring [TrafficControlQueueingDiscipline] section from line %u.", "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
section->filename, section->line); qdisc->section->filename, qdisc->section->line);
return 0; return 0;
} }
const QDiscVTable tbf_vtable = {
.object_size = sizeof(TokenBufferFilter),
.tca_kind = "tbf",
.fill_message = token_buffer_filter_fill_message,
.verify = token_buffer_filter_verify
};

View file

@ -2,14 +2,13 @@
* Copyright © 2019 VMware, Inc. */ * Copyright © 2019 VMware, Inc. */
#pragma once #pragma once
#include "sd-netlink.h"
#include "conf-parser.h" #include "conf-parser.h"
#include "networkd-link.h" #include "qdisc.h"
#include "networkd-util.h" #include "time-util.h"
#include "tc-util.h"
typedef struct TokenBufferFilter { typedef struct TokenBufferFilter {
QDisc meta;
uint64_t rate; uint64_t rate;
uint64_t peak_rate; uint64_t peak_rate;
uint32_t burst; uint32_t burst;
@ -19,8 +18,8 @@ typedef struct TokenBufferFilter {
size_t mpu; size_t mpu;
} TokenBufferFilter; } TokenBufferFilter;
int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, sd_netlink_message *req); DEFINE_QDISC_CAST(TBF, TokenBufferFilter);
int token_buffer_filter_section_verify(const TokenBufferFilter *tbf, const NetworkConfigSection *section); extern const QDiscVTable tbf_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_latency); CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_latency);
CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_size); CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_size);