diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index db55b467f80..43372caa355 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1097,7 +1097,7 @@ static int link_configure(Link *link) { if (r < 0) return r; - r = link_configure_traffic_control(link); + r = link_request_traffic_control(link); if (r < 0) return r; diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index 1aa4bbae554..d673c2f1550 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -16,6 +16,7 @@ #include "networkd-routing-policy-rule.h" #include "networkd-queue.h" #include "networkd-setlink.h" +#include "tc.h" static void request_free_object(RequestType type, void *object) { switch (type) { @@ -58,6 +59,10 @@ static void request_free_object(RequestType type, void *object) { break; case REQUEST_TYPE_SET_LINK: case REQUEST_TYPE_STACKED_NETDEV: + break; + case REQUEST_TYPE_TRAFFIC_CONTROL: + traffic_control_free(object); + break; case REQUEST_TYPE_UP_DOWN: break; default: @@ -143,6 +148,9 @@ static void request_hash_func(const Request *req, struct siphash *state) { case REQUEST_TYPE_SET_LINK: trivial_hash_func(req->set_link_operation_ptr, state); break; + case REQUEST_TYPE_TRAFFIC_CONTROL: + traffic_control_hash_func(req->traffic_control, state); + break; case REQUEST_TYPE_UP_DOWN: break; default: @@ -196,6 +204,8 @@ static int request_compare_func(const struct Request *a, const struct Request *b return routing_policy_rule_compare_func(a->rule, b->rule); case REQUEST_TYPE_SET_LINK: return trivial_compare_func(a->set_link_operation_ptr, b->set_link_operation_ptr); + case REQUEST_TYPE_TRAFFIC_CONTROL: + return traffic_control_compare_func(a->traffic_control, b->traffic_control); case REQUEST_TYPE_UP_DOWN: return 0; default: @@ -241,7 +251,8 @@ int link_queue_request( REQUEST_TYPE_DHCP4_CLIENT, REQUEST_TYPE_DHCP6_CLIENT, REQUEST_TYPE_NDISC, - REQUEST_TYPE_RADV) || + REQUEST_TYPE_RADV, + REQUEST_TYPE_TRAFFIC_CONTROL) || netlink_handler); req = new(Request, 1); @@ -347,6 +358,9 @@ int manager_process_requests(sd_event_source *s, void *userdata) { case REQUEST_TYPE_STACKED_NETDEV: r = request_process_stacked_netdev(req); break; + case REQUEST_TYPE_TRAFFIC_CONTROL: + r = request_process_traffic_control(req); + break; case REQUEST_TYPE_UP_DOWN: r = request_process_link_up_or_down(req); break; diff --git a/src/network/networkd-queue.h b/src/network/networkd-queue.h index ac33e885f2b..ac02b9d8159 100644 --- a/src/network/networkd-queue.h +++ b/src/network/networkd-queue.h @@ -14,6 +14,7 @@ typedef struct NetDev NetDev; typedef struct NextHop NextHop; typedef struct Route Route; typedef struct RoutingPolicyRule RoutingPolicyRule; +typedef struct TrafficControl TrafficControl; typedef enum RequestType { REQUEST_TYPE_ACTIVATE_LINK, @@ -33,6 +34,7 @@ typedef enum RequestType { REQUEST_TYPE_ROUTING_POLICY_RULE, REQUEST_TYPE_SET_LINK, REQUEST_TYPE_STACKED_NETDEV, + REQUEST_TYPE_TRAFFIC_CONTROL, REQUEST_TYPE_UP_DOWN, _REQUEST_TYPE_MAX, _REQUEST_TYPE_INVALID = -EINVAL, @@ -54,6 +56,7 @@ typedef struct Request { RoutingPolicyRule *rule; void *set_link_operation_ptr; NetDev *netdev; + TrafficControl *traffic_control; void *object; }; void *userdata; diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index d1b689793ca..4fa1a6f5eac 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -8,6 +8,7 @@ #include "in-addr-util.h" #include "netlink-util.h" #include "networkd-manager.h" +#include "networkd-queue.h" #include "parse-util.h" #include "qdisc.h" #include "set.h" @@ -123,6 +124,7 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns qdisc->network = network; qdisc->section = TAKE_PTR(n); + qdisc->source = NETWORK_CONFIG_SOURCE_STATIC; r = ordered_hashmap_ensure_put(&network->tc_by_section, &config_section_hash_ops, qdisc->section, TC(qdisc)); if (r < 0) @@ -211,6 +213,35 @@ static int qdisc_add(Link *link, QDisc *qdisc) { return 0; } +static int qdisc_dup(const QDisc *src, QDisc **ret) { + _cleanup_(qdisc_freep) QDisc *dst = NULL; + + assert(src); + assert(ret); + + if (QDISC_VTABLE(src)) + dst = memdup(src, QDISC_VTABLE(src)->object_size); + else + dst = newdup(QDisc, src, 1); + if (!dst) + return -ENOMEM; + + /* clear all pointers */ + dst->network = NULL; + dst->section = NULL; + dst->link = NULL; + dst->tca_kind = NULL; + + if (src->tca_kind) { + dst->tca_kind = strdup(src->tca_kind); + if (!dst->tca_kind) + return -ENOMEM; + } + + *ret = TAKE_PTR(dst); + return 0; +} + static void log_qdisc_debug(QDisc *qdisc, Link *link, const char *str) { _cleanup_free_ char *state = NULL; @@ -264,6 +295,8 @@ int qdisc_configure(Link *link, QDisc *qdisc) { assert(link->manager->rtnl); assert(link->ifindex > 0); + log_qdisc_debug(qdisc, link, "Configuring"); + r = sd_rtnl_message_new_traffic_control(link->manager->rtnl, &req, RTM_NEWQDISC, link->ifindex, qdisc->handle, qdisc->parent); if (r < 0) @@ -284,11 +317,52 @@ int qdisc_configure(Link *link, QDisc *qdisc) { return log_link_debug_errno(link, r, "Could not send netlink message: %m"); link_ref(link); - link->tc_messages++; + qdisc_enter_configuring(qdisc); return 0; } +int qdisc_is_ready_to_configure(Link *link, QDisc *qdisc) { + assert(link); + assert(qdisc); + + return true; +} + +int link_request_qdisc(Link *link, QDisc *qdisc) { + QDisc *existing; + int r; + + assert(link); + assert(qdisc); + + if (qdisc_get(link, qdisc, &existing) < 0) { + _cleanup_(qdisc_freep) QDisc *tmp = NULL; + + r = qdisc_dup(qdisc, &tmp); + if (r < 0) + return log_oom(); + + r = qdisc_add(link, tmp); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to store QDisc: %m"); + + existing = TAKE_PTR(tmp); + } else + existing->source = qdisc->source; + + log_qdisc_debug(existing, link, "Requesting"); + r = link_queue_request(link, REQUEST_TYPE_TRAFFIC_CONTROL, TC(existing), false, + &link->tc_messages, NULL, NULL); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to request QDisc: %m"); + if (r == 0) + return 0; + + qdisc_enter_requesting(existing); + return 1; +} + int manager_rtnl_process_qdisc(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { _cleanup_(qdisc_freep) QDisc *tmp = NULL; QDisc *qdisc = NULL; diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h index 2fc1dc2b039..dc4156ad0af 100644 --- a/src/network/tc/qdisc.h +++ b/src/network/tc/qdisc.h @@ -83,6 +83,8 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns void qdisc_hash_func(const QDisc *qdic, struct siphash *state); int qdisc_compare_func(const QDisc *a, const QDisc *b); +int link_request_qdisc(Link *link, QDisc *qdisc); +int qdisc_is_ready_to_configure(Link *link, QDisc *qdisc); int qdisc_configure(Link *link, QDisc *qdisc); int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact); diff --git a/src/network/tc/tc.c b/src/network/tc/tc.c index 55d1b0c9371..cca5a92e620 100644 --- a/src/network/tc/tc.c +++ b/src/network/tc/tc.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "macro.h" +#include "networkd-queue.h" #include "qdisc.h" #include "tc.h" #include "tclass.h" @@ -112,34 +113,87 @@ static int traffic_control_configure(Link *link, TrafficControl *tc) { } } -int link_configure_traffic_control(Link *link) { +static int link_request_traffic_control_one(Link *link, TrafficControl *tc) { + assert(link); + assert(tc); + + switch (tc->kind) { + case TC_KIND_QDISC: + return link_request_qdisc(link, TC_TO_QDISC(tc)); + case TC_KIND_TCLASS: + return link_request_tclass(link, TC_TO_TCLASS(tc)); + default: + assert_not_reached(); + } +} + +int link_request_traffic_control(Link *link) { TrafficControl *tc; int r; assert(link); assert(link->network); - if (link->tc_messages != 0) { - log_link_debug(link, "Traffic control is configuring."); - return 0; - } - link->tc_configured = false; ORDERED_HASHMAP_FOREACH(tc, link->network->tc_by_section) { - r = traffic_control_configure(link, tc); + r = link_request_traffic_control_one(link, tc); if (r < 0) - return log_link_error_errno(link, r, "Could not create send configuration message: %m"); + return r; } - if (link->tc_messages == 0) + if (link->tc_messages == 0) { link->tc_configured = true; - else - log_link_debug(link, "Configuring traffic control"); + link_check_ready(link); + } else { + log_link_debug(link, "Setting traffic control"); + link_set_state(link, LINK_STATE_CONFIGURING); + } return 0; } +static int traffic_control_is_ready_to_configure(Link *link, TrafficControl *tc) { + assert(link); + assert(tc); + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return false; + + switch(tc->kind) { + case TC_KIND_QDISC: + return qdisc_is_ready_to_configure(link, TC_TO_QDISC(tc)); + case TC_KIND_TCLASS: + return tclass_is_ready_to_configure(link, TC_TO_TCLASS(tc)); + default: + assert_not_reached(); + } +} + +int request_process_traffic_control(Request *req) { + TrafficControl *tc; + Link *link; + int r; + + assert(req); + assert(req->traffic_control); + assert(req->type == REQUEST_TYPE_TRAFFIC_CONTROL); + + link = ASSERT_PTR(req->link); + tc = ASSERT_PTR(req->traffic_control); + + r = traffic_control_is_ready_to_configure(link, tc); + if (r <= 0) { + return r; + } + + r = traffic_control_configure(link, tc); + if (r < 0) + return r; + + return 1; +} + static int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact) { assert(tc); diff --git a/src/network/tc/tc.h b/src/network/tc/tc.h index 954dcd39f9c..57458ee65e9 100644 --- a/src/network/tc/tc.h +++ b/src/network/tc/tc.h @@ -3,6 +3,8 @@ #include "networkd-link.h" +typedef struct Request Request; + typedef enum TrafficControlKind { TC_KIND_QDISC, TC_KIND_TCLASS, @@ -34,7 +36,6 @@ typedef struct TrafficControl { #define TC(tc) (&(tc)->meta) void traffic_control_free(TrafficControl *tc); -int link_configure_traffic_control(Link *link); void network_drop_invalid_traffic_control(Network *network); void traffic_control_hash_func(const TrafficControl *tc, struct siphash *state); @@ -42,3 +43,6 @@ int traffic_control_compare_func(const TrafficControl *a, const TrafficControl * int traffic_control_get(Link *link, const TrafficControl *in, TrafficControl **ret); int traffic_control_add(Link *link, TrafficControl *tc); + +int link_request_traffic_control(Link *link); +int request_process_traffic_control(Request *req); diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c index cd61b42c4e2..2553caa76d9 100644 --- a/src/network/tc/tclass.c +++ b/src/network/tc/tclass.c @@ -8,6 +8,7 @@ #include "in-addr-util.h" #include "netlink-util.h" #include "networkd-manager.h" +#include "networkd-queue.h" #include "parse-util.h" #include "set.h" #include "string-util.h" @@ -94,6 +95,7 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u tclass->network = network; tclass->section = TAKE_PTR(n); + tclass->source = NETWORK_CONFIG_SOURCE_STATIC; r = ordered_hashmap_ensure_put(&network->tc_by_section, &config_section_hash_ops, tclass->section, tclass); if (r < 0) @@ -182,6 +184,35 @@ static int tclass_add(Link *link, TClass *tclass) { return 0; } +static int tclass_dup(const TClass *src, TClass **ret) { + _cleanup_(tclass_freep) TClass *dst = NULL; + + assert(src); + assert(ret); + + if (TCLASS_VTABLE(src)) + dst = memdup(src, TCLASS_VTABLE(src)->object_size); + else + dst = newdup(TClass, src, 1); + if (!dst) + return -ENOMEM; + + /* clear all pointers */ + dst->network = NULL; + dst->section = NULL; + dst->link = NULL; + dst->tca_kind = NULL; + + if (src->tca_kind) { + dst->tca_kind = strdup(src->tca_kind); + if (!dst->tca_kind) + return -ENOMEM; + } + + *ret = TAKE_PTR(dst); + return 0; +} + static void log_tclass_debug(TClass *tclass, Link *link, const char *str) { _cleanup_free_ char *state = NULL; @@ -235,6 +266,8 @@ int tclass_configure(Link *link, TClass *tclass) { assert(link->manager->rtnl); assert(link->ifindex > 0); + log_tclass_debug(tclass, link, "Configuring"); + r = sd_rtnl_message_new_traffic_control(link->manager->rtnl, &req, RTM_NEWTCLASS, link->ifindex, tclass->classid, tclass->parent); if (r < 0) @@ -255,11 +288,52 @@ int tclass_configure(Link *link, TClass *tclass) { return log_link_debug_errno(link, r, "Could not send netlink message: %m"); link_ref(link); - link->tc_messages++; + tclass_enter_configuring(tclass); return 0; } +int tclass_is_ready_to_configure(Link *link, TClass *tclass) { + assert(link); + assert(tclass); + + return true; +} + +int link_request_tclass(Link *link, TClass *tclass) { + TClass *existing; + int r; + + assert(link); + assert(tclass); + + if (tclass_get(link, tclass, &existing) < 0) { + _cleanup_(tclass_freep) TClass *tmp = NULL; + + r = tclass_dup(tclass, &tmp); + if (r < 0) + return log_oom(); + + r = tclass_add(link, tmp); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to store TClass: %m"); + + existing = TAKE_PTR(tmp); + } else + existing->source = tclass->source; + + log_tclass_debug(existing, link, "Requesting"); + r = link_queue_request(link, REQUEST_TYPE_TRAFFIC_CONTROL, TC(existing), false, + &link->tc_messages, NULL, NULL); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to request TClass: %m"); + if (r == 0) + return 0; + + tclass_enter_requesting(existing); + return 1; +} + int manager_rtnl_process_tclass(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { _cleanup_(tclass_freep) TClass *tmp = NULL; TClass *tclass = NULL; diff --git a/src/network/tc/tclass.h b/src/network/tc/tclass.h index 9e57e9566f8..525630a87cb 100644 --- a/src/network/tc/tclass.h +++ b/src/network/tc/tclass.h @@ -65,6 +65,8 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u void tclass_hash_func(const TClass *tclass, struct siphash *state); int tclass_compare_func(const TClass *a, const TClass *b); +int link_request_tclass(Link *link, TClass *tclass); +int tclass_is_ready_to_configure(Link *link, TClass *tclass); int tclass_configure(Link *link, TClass *tclass); int tclass_section_verify(TClass *tclass);