diff --git a/src/network/tc/fq-codel.c b/src/network/tc/fq-codel.c index ae872c686f..4ae2ca913b 100644 --- a/src/network/tc/fq-codel.c +++ b/src/network/tc/fq-codel.c @@ -10,25 +10,16 @@ #include "qdisc.h" #include "string-util.h" -int fair_queuing_controlled_delay_new(FairQueuingControlledDelay **ret) { - FairQueuingControlledDelay *fqcd = NULL; - - fqcd = new0(FairQueuingControlledDelay, 1); - if (!fqcd) - return -ENOMEM; - - *ret = TAKE_PTR(fqcd); - - return 0; -} - -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; assert(link); - assert(fqcd); + assert(qdisc); assert(req); + fqcd = FQ_CODEL(qdisc); + r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "fq_codel"); if (r < 0) return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); @@ -57,6 +48,7 @@ int config_parse_tc_fair_queuing_controlled_delay_limit( void *userdata) { _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + FairQueuingControlledDelay *fqcd; Network *network = data; int r; @@ -65,18 +57,23 @@ int config_parse_tc_fair_queuing_controlled_delay_limit( assert(rvalue); 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) - 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)) { - qdisc->fq_codel.limit = 0; + fqcd->limit = 0; qdisc = NULL; return 0; } - r = safe_atou32(rvalue, &qdisc->fq_codel.limit); + r = safe_atou32(rvalue, &fqcd->limit); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", @@ -84,8 +81,13 @@ int config_parse_tc_fair_queuing_controlled_delay_limit( return 0; } - qdisc->has_fair_queuing_controlled_delay = true; qdisc = NULL; return 0; } + +const QDiscVTable fq_codel_vtable = { + .object_size = sizeof(FairQueuingControlledDelay), + .tca_kind = "fq_codel", + .fill_message = fair_queuing_controlled_delay_fill_message, +}; diff --git a/src/network/tc/fq-codel.h b/src/network/tc/fq-codel.h index 0ddb5099a8..47c3cb5b8e 100644 --- a/src/network/tc/fq-codel.h +++ b/src/network/tc/fq-codel.h @@ -2,16 +2,15 @@ * Copyright © 2019 VMware, Inc. */ #pragma once -#include "sd-netlink.h" - #include "conf-parser.h" -#include "networkd-link.h" +#include "qdisc.h" typedef struct FairQueuingControlledDelay { + QDisc meta; uint32_t limit; } FairQueuingControlledDelay; -int fair_queuing_controlled_delay_new(FairQueuingControlledDelay **ret); -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); diff --git a/src/network/tc/netem.c b/src/network/tc/netem.c index 25a53150b0..f74be288e1 100644 --- a/src/network/tc/netem.c +++ b/src/network/tc/netem.c @@ -13,33 +13,19 @@ #include "string-util.h" #include "tc-util.h" -int network_emulator_new(NetworkEmulator **ret) { - NetworkEmulator *ne = NULL; - - ne = new(NetworkEmulator, 1); - if (!ne) - return -ENOMEM; - - *ne = (NetworkEmulator) { - .delay = USEC_INFINITY, - .jitter = USEC_INFINITY, - }; - - *ret = TAKE_PTR(ne); - - return 0; -} - -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 = { .limit = 1000, }; + NetworkEmulator *ne; int r; assert(link); - assert(ne); + assert(qdisc); assert(req); + ne = NETEM(qdisc); + if (ne->limit > 0) opt.limit = ne->limit; @@ -82,6 +68,7 @@ int config_parse_tc_network_emulator_delay( _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; Network *network = data; + NetworkEmulator *ne; usec_t u; int r; @@ -90,15 +77,20 @@ int config_parse_tc_network_emulator_delay( assert(rvalue); 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) - 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 (streq(lvalue, "NetworkEmulatorDelaySec")) - qdisc->ne.delay = USEC_INFINITY; + ne->delay = USEC_INFINITY; else if (streq(lvalue, "NetworkEmulatorDelayJitterSec")) - qdisc->ne.jitter = USEC_INFINITY; + ne->jitter = USEC_INFINITY; qdisc = NULL; return 0; @@ -113,11 +105,10 @@ int config_parse_tc_network_emulator_delay( } if (streq(lvalue, "NetworkEmulatorDelaySec")) - qdisc->ne.delay = u; + ne->delay = u; else if (streq(lvalue, "NetworkEmulatorDelayJitterSec")) - qdisc->ne.jitter = u; + ne->jitter = u; - qdisc->has_network_emulator = true; qdisc = NULL; return 0; @@ -137,6 +128,7 @@ int config_parse_tc_network_emulator_rate( _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; Network *network = data; + NetworkEmulator *ne; uint32_t rate; int r; @@ -145,12 +137,20 @@ int config_parse_tc_network_emulator_rate( assert(rvalue); 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) - 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)) { - qdisc->ne.loss = 0; + if (streq(lvalue, "NetworkEmulatorLossRate")) + ne->loss = 0; + else if (streq(lvalue, "NetworkEmulatorDuplicateRate")) + ne->duplicate = 0; qdisc = NULL; return 0; @@ -165,9 +165,9 @@ int config_parse_tc_network_emulator_rate( } if (streq(lvalue, "NetworkEmulatorLossRate")) - qdisc->ne.loss = rate; + ne->loss = rate; else if (streq(lvalue, "NetworkEmulatorDuplicateRate")) - qdisc->ne.duplicate = rate; + ne->duplicate = rate; qdisc = NULL; return 0; @@ -187,6 +187,7 @@ int config_parse_tc_network_emulator_packet_limit( _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; Network *network = data; + NetworkEmulator *ne; int r; assert(filename); @@ -194,18 +195,23 @@ int config_parse_tc_network_emulator_packet_limit( assert(rvalue); 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) - 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)) { - qdisc->ne.limit = 0; + ne->limit = 0; qdisc = NULL; return 0; } - r = safe_atou(rvalue, &qdisc->ne.limit); + r = safe_atou(rvalue, &ne->limit); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 'NetworkEmulatorPacketLimit=', ignoring assignment: %s", @@ -216,3 +222,9 @@ int config_parse_tc_network_emulator_packet_limit( qdisc = NULL; return 0; } + +const QDiscVTable netem_vtable = { + .object_size = sizeof(NetworkEmulator), + .tca_kind = "netem", + .fill_message = network_emulator_fill_message, +}; diff --git a/src/network/tc/netem.h b/src/network/tc/netem.h index 66c3476a6b..7bf27e34fd 100644 --- a/src/network/tc/netem.h +++ b/src/network/tc/netem.h @@ -2,13 +2,13 @@ * Copyright © 2019 VMware, Inc. */ #pragma once -#include "sd-netlink.h" - #include "conf-parser.h" -#include "networkd-link.h" +#include "qdisc.h" #include "time-util.h" typedef struct NetworkEmulator { + QDisc meta; + usec_t delay; usec_t jitter; @@ -17,8 +17,8 @@ typedef struct NetworkEmulator { uint32_t duplicate; } NetworkEmulator; -int network_emulator_new(NetworkEmulator **ret); -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_rate); diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index d066e0713f..ee5aafe5e9 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -13,65 +13,94 @@ #include "set.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 = new(QDisc, 1); - if (!qdisc) - return -ENOMEM; + if (kind == _QDISC_KIND_INVALID) { + qdisc = new(QDisc, 1); + if (!qdisc) + return -ENOMEM; - *qdisc = (QDisc) { - .family = AF_UNSPEC, - .parent = TC_H_ROOT, - }; + *qdisc = (QDisc) { + .family = AF_UNSPEC, + .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); 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_(qdisc_freep) QDisc *qdisc = NULL; + QDisc *existing; int r; assert(network); assert(ret); - assert(!!filename == (section_line > 0)); + assert(filename); + assert(section_line > 0); - if (filename) { - r = network_config_section_new(filename, section_line, &n); - if (r < 0) - return r; + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; - qdisc = ordered_hashmap_get(network->qdiscs_by_section, n); - if (qdisc) { - *ret = TAKE_PTR(qdisc); + existing = ordered_hashmap_get(network->qdiscs_by_section, n); + if (existing) { + 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; } } - r = qdisc_new(&qdisc); + r = qdisc_new(kind, &qdisc); if (r < 0) 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->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; + qdisc_free(ordered_hashmap_remove(network->qdiscs_by_section, n)); } - *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; } @@ -116,8 +145,6 @@ static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int qdisc_configure(Link *link, QDisc *qdisc) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - _cleanup_free_ char *tca_kind = NULL; - char *p; int r; 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"); } - if (qdisc->has_network_emulator) { - r = free_and_strdup(&tca_kind, "netem"); + if (QDISC_VTABLE(qdisc)) { + r = sd_netlink_message_append_string(req, TCA_KIND, QDISC_VTABLE(qdisc)->tca_kind); 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) return r; - } - - 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); + } else { + r = sd_netlink_message_append_string(req, TCA_KIND, qdisc->tca_kind); if (r < 0) 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) { - unsigned i; int r; assert(qdisc); @@ -207,15 +200,8 @@ int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) { if (section_is_invalid(qdisc->section)) return -EINVAL; - i = qdisc->has_network_emulator + qdisc->has_token_buffer_filter + qdisc->has_stochastic_fairness_queueing; - if (i > 1) - 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 (QDISC_VTABLE(qdisc) && QDISC_VTABLE(qdisc)->verify) { + r = QDISC_VTABLE(qdisc)->verify(qdisc); if (r < 0) return r; } @@ -260,7 +246,7 @@ int config_parse_tc_qdiscs_parent( assert(rvalue); 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) return r; diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h index 53968bcf1f..8e0ae28855 100644 --- a/src/network/tc/qdisc.h +++ b/src/network/tc/qdisc.h @@ -3,44 +3,65 @@ #pragma once #include "conf-parser.h" -#include "fq-codel.h" -#include "netem.h" #include "networkd-link.h" #include "networkd-network.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 { NetworkConfigSection *section; Network *network; - Link *link; - int family; - uint32_t handle; uint32_t parent; char *tca_kind; - bool has_network_emulator:1; - 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; + QDiscKind kind; } 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); -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_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact); DEFINE_NETWORK_SECTION_FUNCTIONS(QDisc, qdisc_free); CONFIG_PARSER_PROTOTYPE(config_parse_tc_qdiscs_parent); + +#include "fq-codel.h" +#include "netem.h" +#include "sfq.h" +#include "tbf.h" diff --git a/src/network/tc/sfq.c b/src/network/tc/sfq.c index 393b0e12e1..fc0c9bfc27 100644 --- a/src/network/tc/sfq.c +++ b/src/network/tc/sfq.c @@ -11,26 +11,17 @@ #include "sfq.h" #include "string-util.h" -int stochastic_fairness_queueing_new(StochasticFairnessQueueing **ret) { - StochasticFairnessQueueing *sfq = NULL; - - sfq = new0(StochasticFairnessQueueing, 1); - if (!sfq) - return -ENOMEM; - - *ret = TAKE_PTR(sfq); - - return 0; -} - -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 = {}; int r; assert(link); - assert(sfq); + assert(qdisc); assert(req); + sfq = SFQ(qdisc); + 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)); @@ -53,6 +44,7 @@ int config_parse_tc_stochastic_fairness_queueing_perturb_period( void *userdata) { _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + StochasticFairnessQueueing *sfq; Network *network = data; int r; @@ -61,18 +53,23 @@ int config_parse_tc_stochastic_fairness_queueing_perturb_period( assert(rvalue); 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) - 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)) { - qdisc->sfq.perturb_period = 0; + sfq->perturb_period = 0; qdisc = NULL; return 0; } - r = parse_sec(rvalue, &qdisc->sfq.perturb_period); + r = parse_sec(rvalue, &sfq->perturb_period); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", @@ -80,8 +77,13 @@ int config_parse_tc_stochastic_fairness_queueing_perturb_period( return 0; } - qdisc->has_stochastic_fairness_queueing = true; qdisc = NULL; return 0; } + +const QDiscVTable sfq_vtable = { + .object_size = sizeof(StochasticFairnessQueueing), + .tca_kind = "sfq", + .fill_message = stochastic_fairness_queueing_fill_message, +}; diff --git a/src/network/tc/sfq.h b/src/network/tc/sfq.h index 8c00e0e713..d29bcc2e93 100644 --- a/src/network/tc/sfq.h +++ b/src/network/tc/sfq.h @@ -2,16 +2,17 @@ * Copyright © 2019 VMware, Inc. */ #pragma once -#include "sd-netlink.h" - #include "conf-parser.h" -#include "networkd-link.h" +#include "qdisc.h" +#include "time-util.h" typedef struct StochasticFairnessQueueing { + QDisc meta; + usec_t perturb_period; } StochasticFairnessQueueing; -int stochastic_fairness_queueing_new(StochasticFairnessQueueing **ret); -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); diff --git a/src/network/tc/tbf.c b/src/network/tc/tbf.c index eff5c1db7c..7dfc565111 100644 --- a/src/network/tc/tbf.c +++ b/src/network/tc/tbf.c @@ -15,27 +15,18 @@ #include "tc-util.h" #include "util.h" -int token_buffer_filter_new(TokenBufferFilter **ret) { - TokenBufferFilter *ne = NULL; - - ne = new0(TokenBufferFilter, 1); - if (!ne) - return -ENOMEM; - - *ret = TAKE_PTR(ne); - - return 0; -} - -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]; struct tc_tbf_qopt opt = {}; + TokenBufferFilter *tbf; int r; assert(link); - assert(tbf); + assert(qdisc); assert(req); + tbf = TBF(qdisc); + opt.rate.rate = tbf->rate >= (1ULL << 32) ? ~0U : tbf->rate; opt.peakrate.rate = tbf->peak_rate >= (1ULL << 32) ? ~0U : tbf->peak_rate; @@ -133,6 +124,7 @@ int config_parse_tc_token_buffer_filter_size( _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; Network *network = data; + TokenBufferFilter *tbf; uint64_t k; int r; @@ -141,23 +133,28 @@ int config_parse_tc_token_buffer_filter_size( assert(rvalue); 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) - 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 (streq(lvalue, "TokenBufferFilterRate")) - qdisc->tbf.rate = 0; + tbf->rate = 0; else if (streq(lvalue, "TokenBufferFilterBurst")) - qdisc->tbf.burst = 0; + tbf->burst = 0; else if (streq(lvalue, "TokenBufferFilterLimitSize")) - qdisc->tbf.limit = 0; + tbf->limit = 0; else if (streq(lvalue, "TokenBufferFilterMTUBytes")) - qdisc->tbf.mtu = 0; + tbf->mtu = 0; else if (streq(lvalue, "TokenBufferFilterMPUBytes")) - qdisc->tbf.mpu = 0; + tbf->mpu = 0; else if (streq(lvalue, "TokenBufferFilterPeakRate")) - qdisc->tbf.peak_rate = 0; + tbf->peak_rate = 0; qdisc = NULL; return 0; @@ -172,19 +169,18 @@ int config_parse_tc_token_buffer_filter_size( } if (streq(lvalue, "TokenBufferFilterRate")) - qdisc->tbf.rate = k / 8; + tbf->rate = k / 8; else if (streq(lvalue, "TokenBufferFilterBurst")) - qdisc->tbf.burst = k; + tbf->burst = k; else if (streq(lvalue, "TokenBufferFilterLimitSize")) - qdisc->tbf.limit = k; + tbf->limit = k; else if (streq(lvalue, "TokenBufferFilterMPUBytes")) - qdisc->tbf.mpu = k; + tbf->mpu = k; else if (streq(lvalue, "TokenBufferFilterMTUBytes")) - qdisc->tbf.mtu = k; + tbf->mtu = k; else if (streq(lvalue, "TokenBufferFilterPeakRate")) - qdisc->tbf.peak_rate = k / 8; + tbf->peak_rate = k / 8; - qdisc->has_token_buffer_filter = true; qdisc = NULL; return 0; @@ -204,6 +200,7 @@ int config_parse_tc_token_buffer_filter_latency( _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; Network *network = data; + TokenBufferFilter *tbf; usec_t u; int r; @@ -212,12 +209,17 @@ int config_parse_tc_token_buffer_filter_latency( assert(rvalue); 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) - 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)) { - qdisc->tbf.latency = 0; + tbf->latency = 0; qdisc = NULL; return 0; @@ -231,44 +233,52 @@ int config_parse_tc_token_buffer_filter_latency( return 0; } - qdisc->tbf.latency = u; + tbf->latency = u; - qdisc->has_token_buffer_filter = true; qdisc = NULL; 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) return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "%s: Specifying both TokenBufferFilterLimitSize= and TokenBufferFilterLatencySec= is not allowed. " "Ignoring [TrafficControlQueueingDiscipline] section from line %u.", - section->filename, section->line); + qdisc->section->filename, qdisc->section->line); if (tbf->limit == 0 && tbf->latency == 0) return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "%s: Either TokenBufferFilterLimitSize= or TokenBufferFilterLatencySec= is required. " "Ignoring [TrafficControlQueueingDiscipline] section from line %u.", - section->filename, section->line); + qdisc->section->filename, qdisc->section->line); if (tbf->rate == 0) return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "%s: TokenBufferFilterRate= is mandatory. " "Ignoring [TrafficControlQueueingDiscipline] section from line %u.", - section->filename, section->line); + qdisc->section->filename, qdisc->section->line); if (tbf->burst == 0) return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "%s: TokenBufferFilterBurst= is mandatory. " "Ignoring [TrafficControlQueueingDiscipline] section from line %u.", - section->filename, section->line); + qdisc->section->filename, qdisc->section->line); if (tbf->peak_rate > 0 && tbf->mtu == 0) return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "%s: TokenBufferFilterMTUBytes= is mandatory when TokenBufferFilterPeakRate= is specified. " "Ignoring [TrafficControlQueueingDiscipline] section from line %u.", - section->filename, section->line); + qdisc->section->filename, qdisc->section->line); 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 +}; diff --git a/src/network/tc/tbf.h b/src/network/tc/tbf.h index e0bdc3b85f..317dc03107 100644 --- a/src/network/tc/tbf.h +++ b/src/network/tc/tbf.h @@ -2,14 +2,13 @@ * Copyright © 2019 VMware, Inc. */ #pragma once -#include "sd-netlink.h" - #include "conf-parser.h" -#include "networkd-link.h" -#include "networkd-util.h" -#include "tc-util.h" +#include "qdisc.h" +#include "time-util.h" typedef struct TokenBufferFilter { + QDisc meta; + uint64_t rate; uint64_t peak_rate; uint32_t burst; @@ -19,9 +18,8 @@ typedef struct TokenBufferFilter { size_t mpu; } TokenBufferFilter; -int token_buffer_filter_new(TokenBufferFilter **ret); -int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, sd_netlink_message *req); -int token_buffer_filter_section_verify(const TokenBufferFilter *tbf, const NetworkConfigSection *section); +DEFINE_QDISC_CAST(TBF, TokenBufferFilter); +extern const QDiscVTable tbf_vtable; CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_latency); CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_size);