mirror of
https://github.com/systemd/systemd.git
synced 2025-03-31 14:50:15 +03:00
network: introduce struct TClass to prepare for supporting tc class
This commit is contained in:
parent
0ebb76de8b
commit
4666f63bb8
@ -127,6 +127,8 @@ sources = files('''
|
||||
tc/tc-util.h
|
||||
tc/tc.c
|
||||
tc/tc.h
|
||||
tc/tclass.c
|
||||
tc/tclass.h
|
||||
tc/teql.c
|
||||
tc/teql.h
|
||||
'''.split())
|
||||
|
@ -14,6 +14,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
|
||||
#include "networkd-ndisc.h"
|
||||
#include "networkd-network.h"
|
||||
#include "qdisc.h"
|
||||
#include "tclass.h"
|
||||
#include "vlan-util.h"
|
||||
%}
|
||||
struct ConfigPerfItem;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "macro.h"
|
||||
#include "qdisc.h"
|
||||
#include "tc.h"
|
||||
#include "tclass.h"
|
||||
|
||||
void traffic_control_free(TrafficControl *tc) {
|
||||
if (!tc)
|
||||
@ -12,6 +13,9 @@ void traffic_control_free(TrafficControl *tc) {
|
||||
case TC_KIND_QDISC:
|
||||
qdisc_free(TC_TO_QDISC(tc));
|
||||
break;
|
||||
case TC_KIND_TCLASS:
|
||||
tclass_free(TC_TO_TCLASS(tc));
|
||||
break;
|
||||
default:
|
||||
assert_not_reached("Invalid traffic control type");
|
||||
}
|
||||
@ -24,6 +28,8 @@ int traffic_control_configure(Link *link, TrafficControl *tc) {
|
||||
switch(tc->kind) {
|
||||
case TC_KIND_QDISC:
|
||||
return qdisc_configure(link, TC_TO_QDISC(tc));
|
||||
case TC_KIND_TCLASS:
|
||||
return tclass_configure(link, TC_TO_TCLASS(tc));
|
||||
default:
|
||||
assert_not_reached("Invalid traffic control type");
|
||||
}
|
||||
@ -35,6 +41,8 @@ int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, boo
|
||||
switch(tc->kind) {
|
||||
case TC_KIND_QDISC:
|
||||
return qdisc_section_verify(TC_TO_QDISC(tc), qdisc_has_root, qdisc_has_clsact);
|
||||
case TC_KIND_TCLASS:
|
||||
return tclass_section_verify(TC_TO_TCLASS(tc));
|
||||
default:
|
||||
assert_not_reached("Invalid traffic control type");
|
||||
}
|
||||
|
276
src/network/tc/tclass.c
Normal file
276
src/network/tc/tclass.c
Normal file
@ -0,0 +1,276 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+
|
||||
* Copyright © 2019 VMware, Inc. */
|
||||
|
||||
#include <linux/pkt_sched.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "conf-parser.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "netlink-util.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "parse-util.h"
|
||||
#include "set.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "tc-util.h"
|
||||
#include "tclass.h"
|
||||
|
||||
const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = {
|
||||
};
|
||||
|
||||
static int tclass_new(TClassKind kind, TClass **ret) {
|
||||
TClass *tclass;
|
||||
int r;
|
||||
|
||||
tclass = malloc0(tclass_vtable[kind]->object_size);
|
||||
if (!tclass)
|
||||
return -ENOMEM;
|
||||
|
||||
tclass->meta.kind = TC_KIND_TCLASS,
|
||||
tclass->parent = TC_H_ROOT;
|
||||
tclass->kind = kind;
|
||||
|
||||
if (TCLASS_VTABLE(tclass)->init) {
|
||||
r = TCLASS_VTABLE(tclass)->init(tclass);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(tclass);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret) {
|
||||
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
|
||||
_cleanup_(tclass_freep) TClass *tclass = NULL;
|
||||
TrafficControl *existing;
|
||||
int r;
|
||||
|
||||
assert(network);
|
||||
assert(ret);
|
||||
assert(filename);
|
||||
assert(section_line > 0);
|
||||
|
||||
r = network_config_section_new(filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
existing = ordered_hashmap_get(network->tc_by_section, n);
|
||||
if (existing) {
|
||||
TClass *t;
|
||||
|
||||
if (existing->kind != TC_KIND_TCLASS)
|
||||
return -EINVAL;
|
||||
|
||||
t = TC_TO_TCLASS(existing);
|
||||
|
||||
if (t->kind != kind)
|
||||
return -EINVAL;
|
||||
|
||||
*ret = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = tclass_new(kind, &tclass);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
tclass->network = network;
|
||||
tclass->section = TAKE_PTR(n);
|
||||
|
||||
r = ordered_hashmap_ensure_allocated(&network->tc_by_section, &network_config_hash_ops);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ordered_hashmap_put(network->tc_by_section, tclass->section, tclass);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(tclass);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tclass_free(TClass *tclass) {
|
||||
if (!tclass)
|
||||
return;
|
||||
|
||||
if (tclass->network && tclass->section)
|
||||
ordered_hashmap_remove(tclass->network->tc_by_section, tclass->section);
|
||||
|
||||
network_config_section_free(tclass->section);
|
||||
|
||||
free(tclass);
|
||||
}
|
||||
|
||||
static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->tc_messages > 0);
|
||||
link->tc_messages--;
|
||||
|
||||
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
|
||||
return 1;
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r < 0 && r != -EEXIST) {
|
||||
log_link_message_error_errno(link, m, r, "Could not set TClass");
|
||||
link_enter_failed(link);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (link->tc_messages == 0) {
|
||||
log_link_debug(link, "Traffic control configured");
|
||||
link->tc_configured = true;
|
||||
link_check_ready(link);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int tclass_configure(Link *link, TClass *tclass) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
assert(link->manager->rtnl);
|
||||
assert(link->ifindex > 0);
|
||||
|
||||
r = sd_rtnl_message_new_tclass(link->manager->rtnl, &req, RTM_NEWTCLASS, AF_UNSPEC, link->ifindex);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not create RTM_NEWTCLASS message: %m");
|
||||
|
||||
r = sd_rtnl_message_set_tclass_parent(req, tclass->parent);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not create tcm_parent message: %m");
|
||||
|
||||
if (tclass->classid != TC_H_UNSPEC) {
|
||||
r = sd_rtnl_message_set_tclass_handle(req, tclass->classid);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not set tcm_handle message: %m");
|
||||
}
|
||||
|
||||
r = sd_netlink_message_append_string(req, TCA_KIND, TCLASS_VTABLE(tclass)->tca_kind);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
|
||||
|
||||
if (TCLASS_VTABLE(tclass)->fill_message) {
|
||||
r = TCLASS_VTABLE(tclass)->fill_message(link, tclass, req);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = netlink_call_async(link->manager->rtnl, NULL, req, tclass_handler, link_netlink_destroy_callback, link);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
|
||||
|
||||
link_ref(link);
|
||||
link->tc_messages++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tclass_section_verify(TClass *tclass) {
|
||||
int r;
|
||||
|
||||
assert(tclass);
|
||||
|
||||
if (section_is_invalid(tclass->section))
|
||||
return -EINVAL;
|
||||
|
||||
if (TCLASS_VTABLE(tclass)->verify) {
|
||||
r = TCLASS_VTABLE(tclass)->verify(tclass);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_tclass_parent(
|
||||
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;
|
||||
Network *network = data;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = tclass_new_static(ltype, network, filename, section_line, &tclass);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (streq(rvalue, "root"))
|
||||
tclass->parent = TC_H_ROOT;
|
||||
else {
|
||||
r = parse_handle(rvalue, &tclass->parent);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r,
|
||||
"Failed to parse 'Parent=', ignoring assignment: %s",
|
||||
rvalue);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
tclass = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_tclass_classid(
|
||||
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;
|
||||
Network *network = data;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = tclass_new_static(ltype, network, filename, section_line, &tclass);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
tclass->classid = TC_H_UNSPEC;
|
||||
tclass = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = parse_handle(rvalue, &tclass->classid);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r,
|
||||
"Failed to parse 'ClassId=', ignoring assignment: %s",
|
||||
rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tclass = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
67
src/network/tc/tclass.h
Normal file
67
src/network/tc/tclass.h
Normal file
@ -0,0 +1,67 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+
|
||||
* Copyright © 2019 VMware, Inc. */
|
||||
#pragma once
|
||||
|
||||
#include "conf-parser.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-util.h"
|
||||
#include "tc.h"
|
||||
|
||||
typedef enum TClassKind {
|
||||
TCLASS_KIND_HTB,
|
||||
_TCLASS_KIND_MAX,
|
||||
_TCLASS_KIND_INVALID = -1,
|
||||
} TClassKind;
|
||||
|
||||
typedef struct TClass {
|
||||
TrafficControl meta;
|
||||
|
||||
NetworkConfigSection *section;
|
||||
Network *network;
|
||||
|
||||
uint32_t classid;
|
||||
uint32_t parent;
|
||||
|
||||
TClassKind kind;
|
||||
} TClass;
|
||||
|
||||
typedef struct TClassVTable {
|
||||
size_t object_size;
|
||||
const char *tca_kind;
|
||||
/* called in tclass_new() */
|
||||
int (*init)(TClass *tclass);
|
||||
int (*fill_message)(Link *link, TClass *tclass, sd_netlink_message *m);
|
||||
int (*verify)(TClass *tclass);
|
||||
} TClassVTable;
|
||||
|
||||
extern const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX];
|
||||
|
||||
#define TCLASS_VTABLE(t) ((t)->kind != _TCLASS_KIND_INVALID ? tclass_vtable[(t)->kind] : NULL)
|
||||
|
||||
/* For casting a tclass into the various tclass kinds */
|
||||
#define DEFINE_TCLASS_CAST(UPPERCASE, MixedCase) \
|
||||
static inline MixedCase* TCLASS_TO_##UPPERCASE(TClass *t) { \
|
||||
if (_unlikely_(!t || t->kind != TCLASS_KIND_##UPPERCASE)) \
|
||||
return NULL; \
|
||||
\
|
||||
return (MixedCase*) t; \
|
||||
}
|
||||
|
||||
/* For casting the various tclass kinds into a tclass */
|
||||
#define TCLASS(t) (&(t)->meta)
|
||||
|
||||
void tclass_free(TClass *tclass);
|
||||
int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret);
|
||||
|
||||
int tclass_configure(Link *link, TClass *tclass);
|
||||
int tclass_section_verify(TClass *tclass);
|
||||
|
||||
DEFINE_NETWORK_SECTION_FUNCTIONS(TClass, tclass_free);
|
||||
|
||||
DEFINE_TC_CAST(TCLASS, TClass);
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_tclass_parent);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_tclass_classid);
|
||||
|
||||
#include "htb.h"
|
Loading…
x
Reference in New Issue
Block a user