network: re-design request queue

This makes Request object takes hash, compare, free, and process functions.

With this change, the logic in networkd-queue.c can be mostly
independent of the type of the request or the object (e.g. Address) assigned
to the request, and it becomes simpler.
This commit is contained in:
Yu Watanabe
2022-03-11 14:20:31 +09:00
parent 4c8b81caa5
commit 09d09207de
38 changed files with 285 additions and 554 deletions
+79 -366
View File
@@ -1,93 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "netdev.h"
#include "netlink-util.h"
#include "networkd-address.h"
#include "networkd-address-label.h"
#include "networkd-bridge-fdb.h"
#include "networkd-bridge-mdb.h"
#include "networkd-dhcp-server.h"
#include "networkd-dhcp4.h"
#include "networkd-dhcp6.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-ndisc.h"
#include "networkd-neighbor.h"
#include "networkd-nexthop.h"
#include "networkd-route.h"
#include "networkd-routing-policy-rule.h"
#include "networkd-queue.h"
#include "networkd-setlink.h"
#include "qdisc.h"
#include "string-table.h"
#include "tclass.h"
static void request_free_object(RequestType type, void *object) {
switch (type) {
case REQUEST_TYPE_ACTIVATE_LINK:
break;
case REQUEST_TYPE_ADDRESS:
address_free(object);
break;
case REQUEST_TYPE_ADDRESS_LABEL:
address_label_free(object);
break;
case REQUEST_TYPE_BRIDGE_FDB:
bridge_fdb_free(object);
break;
case REQUEST_TYPE_BRIDGE_MDB:
bridge_mdb_free(object);
break;
case REQUEST_TYPE_DHCP_SERVER:
case REQUEST_TYPE_DHCP4_CLIENT:
case REQUEST_TYPE_DHCP6_CLIENT:
break;
case REQUEST_TYPE_IPV6_PROXY_NDP:
free(object);
break;
case REQUEST_TYPE_NDISC:
break;
case REQUEST_TYPE_NEIGHBOR:
neighbor_free(object);
break;
case REQUEST_TYPE_NETDEV_INDEPENDENT:
case REQUEST_TYPE_NETDEV_STACKED:
netdev_unref(object);
break;
case REQUEST_TYPE_NEXTHOP:
nexthop_free(object);
break;
case REQUEST_TYPE_RADV:
break;
case REQUEST_TYPE_ROUTE:
route_free(object);
break;
case REQUEST_TYPE_ROUTING_POLICY_RULE:
routing_policy_rule_free(object);
break;
case REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE:
case REQUEST_TYPE_SET_LINK_BOND:
case REQUEST_TYPE_SET_LINK_BRIDGE:
case REQUEST_TYPE_SET_LINK_BRIDGE_VLAN:
case REQUEST_TYPE_SET_LINK_CAN:
case REQUEST_TYPE_SET_LINK_FLAGS:
case REQUEST_TYPE_SET_LINK_GROUP:
case REQUEST_TYPE_SET_LINK_IPOIB:
case REQUEST_TYPE_SET_LINK_MAC:
case REQUEST_TYPE_SET_LINK_MASTER:
case REQUEST_TYPE_SET_LINK_MTU:
break;
case REQUEST_TYPE_TC_QDISC:
qdisc_free(object);
break;
case REQUEST_TYPE_TC_CLASS:
tclass_free(object);
break;
case REQUEST_TYPE_UP_DOWN:
break;
default:
assert_not_reached();
}
}
static Request *request_free(Request *req) {
if (!req)
@@ -98,8 +16,8 @@ static Request *request_free(Request *req) {
if (req->manager)
ordered_set_remove(req->manager->request_queue, req);
if (req->consume_object)
request_free_object(req->type, req->object);
if (req->free_func)
req->free_func(req->userdata);
if (req->counter)
(*req->counter)--;
@@ -136,70 +54,11 @@ static void request_hash_func(const Request *req, struct siphash *state) {
siphash24_compress(&req->type, sizeof(req->type), state);
switch (req->type) {
case REQUEST_TYPE_ACTIVATE_LINK:
break;
case REQUEST_TYPE_ADDRESS:
address_hash_func(req->address, state);
break;
case REQUEST_TYPE_ADDRESS_LABEL:
case REQUEST_TYPE_BRIDGE_FDB:
case REQUEST_TYPE_BRIDGE_MDB:
case REQUEST_TYPE_NETDEV_INDEPENDENT:
case REQUEST_TYPE_NETDEV_STACKED:
/* TODO: Currently, these types do not have any specific hash and compare functions.
* Fortunately, all these objects are 'static', thus we can use the trivial functions. */
trivial_hash_func(req->object, state);
break;
case REQUEST_TYPE_DHCP_SERVER:
case REQUEST_TYPE_DHCP4_CLIENT:
case REQUEST_TYPE_DHCP6_CLIENT:
/* These types do not have an object. */
break;
case REQUEST_TYPE_IPV6_PROXY_NDP:
in6_addr_hash_func(req->ipv6_proxy_ndp, state);
break;
case REQUEST_TYPE_NDISC:
/* This type does not have an object. */
break;
case REQUEST_TYPE_NEIGHBOR:
neighbor_hash_func(req->neighbor, state);
break;
case REQUEST_TYPE_NEXTHOP:
nexthop_hash_func(req->nexthop, state);
break;
case REQUEST_TYPE_RADV:
/* This type does not have an object. */
break;
case REQUEST_TYPE_ROUTE:
route_hash_func(req->route, state);
break;
case REQUEST_TYPE_ROUTING_POLICY_RULE:
routing_policy_rule_hash_func(req->rule, state);
break;
case REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE:
case REQUEST_TYPE_SET_LINK_BOND:
case REQUEST_TYPE_SET_LINK_BRIDGE:
case REQUEST_TYPE_SET_LINK_BRIDGE_VLAN:
case REQUEST_TYPE_SET_LINK_CAN:
case REQUEST_TYPE_SET_LINK_FLAGS:
case REQUEST_TYPE_SET_LINK_GROUP:
case REQUEST_TYPE_SET_LINK_IPOIB:
case REQUEST_TYPE_SET_LINK_MAC:
case REQUEST_TYPE_SET_LINK_MASTER:
case REQUEST_TYPE_SET_LINK_MTU:
break;
case REQUEST_TYPE_TC_QDISC:
qdisc_hash_func(req->qdisc, state);
break;
case REQUEST_TYPE_TC_CLASS:
tclass_hash_func(req->tclass, state);
break;
case REQUEST_TYPE_UP_DOWN:
break;
default:
assert_not_reached();
}
siphash24_compress(&req->hash_func, sizeof(req->hash_func), state);
siphash24_compress(&req->compare_func, sizeof(req->compare_func), state);
if (req->hash_func)
req->hash_func(req->userdata, state);
}
static int request_compare_func(const struct Request *a, const struct Request *b) {
@@ -222,56 +81,18 @@ static int request_compare_func(const struct Request *a, const struct Request *b
if (r != 0)
return r;
switch (a->type) {
case REQUEST_TYPE_ACTIVATE_LINK:
return 0;
case REQUEST_TYPE_ADDRESS:
return address_compare_func(a->address, b->address);
case REQUEST_TYPE_ADDRESS_LABEL:
case REQUEST_TYPE_BRIDGE_FDB:
case REQUEST_TYPE_BRIDGE_MDB:
case REQUEST_TYPE_NETDEV_INDEPENDENT:
case REQUEST_TYPE_NETDEV_STACKED:
return trivial_compare_func(a->object, b->object);
case REQUEST_TYPE_DHCP_SERVER:
case REQUEST_TYPE_DHCP4_CLIENT:
case REQUEST_TYPE_DHCP6_CLIENT:
return 0;
case REQUEST_TYPE_IPV6_PROXY_NDP:
return in6_addr_compare_func(a->ipv6_proxy_ndp, b->ipv6_proxy_ndp);
case REQUEST_TYPE_NDISC:
return 0;
case REQUEST_TYPE_NEIGHBOR:
return neighbor_compare_func(a->neighbor, b->neighbor);
case REQUEST_TYPE_NEXTHOP:
return nexthop_compare_func(a->nexthop, b->nexthop);
case REQUEST_TYPE_ROUTE:
return route_compare_func(a->route, b->route);
case REQUEST_TYPE_RADV:
return 0;
case REQUEST_TYPE_ROUTING_POLICY_RULE:
return routing_policy_rule_compare_func(a->rule, b->rule);
case REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE:
case REQUEST_TYPE_SET_LINK_BOND:
case REQUEST_TYPE_SET_LINK_BRIDGE:
case REQUEST_TYPE_SET_LINK_BRIDGE_VLAN:
case REQUEST_TYPE_SET_LINK_CAN:
case REQUEST_TYPE_SET_LINK_FLAGS:
case REQUEST_TYPE_SET_LINK_GROUP:
case REQUEST_TYPE_SET_LINK_IPOIB:
case REQUEST_TYPE_SET_LINK_MAC:
case REQUEST_TYPE_SET_LINK_MASTER:
case REQUEST_TYPE_SET_LINK_MTU:
return 0;
case REQUEST_TYPE_TC_QDISC:
return qdisc_compare_func(a->qdisc, b->qdisc);
case REQUEST_TYPE_TC_CLASS:
return tclass_compare_func(a->tclass, b->tclass);
case REQUEST_TYPE_UP_DOWN:
return 0;
default:
assert_not_reached();
}
r = CMP(PTR_TO_UINT64(a->hash_func), PTR_TO_UINT64(b->hash_func));
if (r != 0)
return r;
r = CMP(PTR_TO_UINT64(a->compare_func), PTR_TO_UINT64(b->compare_func));
if (r != 0)
return r;
if (a->compare_func)
return a->compare_func(a->userdata, b->userdata);
return 0;
}
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
@@ -281,54 +102,15 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
request_compare_func,
request_unref);
int netdev_queue_request(
NetDev *netdev,
Request **ret) {
_cleanup_(request_unrefp) Request *req = NULL;
Request *existing;
int r;
assert(netdev);
assert(netdev->manager);
req = new(Request, 1);
if (!req)
return -ENOMEM;
*req = (Request) {
.n_ref = 1,
.manager = netdev->manager,
.netdev = netdev_ref(netdev),
.type = REQUEST_TYPE_NETDEV_INDEPENDENT,
.consume_object = true,
};
existing = ordered_set_get(netdev->manager->request_queue, req);
if (existing) {
if (ret)
*ret = existing;
return 0;
}
r = ordered_set_ensure_put(&netdev->manager->request_queue, &request_hash_ops, req);
if (r < 0)
return r;
req->manager = netdev->manager;
if (ret)
*ret = req;
TAKE_PTR(req);
return 1;
}
int link_queue_request(
static int request_new(
Manager *manager,
Link *link,
RequestType type,
void *object,
bool consume_object,
void *userdata,
mfree_func_t free_func,
hash_func_t hash_func,
compare_func_t compare_func,
request_process_func_t process,
unsigned *counter,
request_netlink_handler_t netlink_handler,
Request **ret) {
@@ -337,65 +119,40 @@ int link_queue_request(
Request *existing;
int r;
assert(link);
assert(link->manager);
assert(type >= 0 && type < _REQUEST_TYPE_MAX);
assert(IN_SET(type,
REQUEST_TYPE_ACTIVATE_LINK,
REQUEST_TYPE_DHCP_SERVER,
REQUEST_TYPE_DHCP4_CLIENT,
REQUEST_TYPE_DHCP6_CLIENT,
REQUEST_TYPE_NDISC,
REQUEST_TYPE_RADV,
REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE,
REQUEST_TYPE_SET_LINK_BOND,
REQUEST_TYPE_SET_LINK_BRIDGE,
REQUEST_TYPE_SET_LINK_BRIDGE_VLAN,
REQUEST_TYPE_SET_LINK_CAN,
REQUEST_TYPE_SET_LINK_FLAGS,
REQUEST_TYPE_SET_LINK_GROUP,
REQUEST_TYPE_SET_LINK_IPOIB,
REQUEST_TYPE_SET_LINK_MAC,
REQUEST_TYPE_SET_LINK_MASTER,
REQUEST_TYPE_SET_LINK_MTU,
REQUEST_TYPE_UP_DOWN) ||
object);
assert(IN_SET(type,
REQUEST_TYPE_DHCP_SERVER,
REQUEST_TYPE_DHCP4_CLIENT,
REQUEST_TYPE_DHCP6_CLIENT,
REQUEST_TYPE_NDISC,
REQUEST_TYPE_RADV) ||
netlink_handler);
assert(manager);
assert(process);
req = new(Request, 1);
if (!req) {
if (consume_object)
request_free_object(type, object);
if (free_func)
free_func(userdata);
return -ENOMEM;
}
*req = (Request) {
.n_ref = 1,
.link = link_ref(link),
.link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
.type = type,
.object = object,
.consume_object = consume_object,
.userdata = userdata,
.free_func = free_func,
.hash_func = hash_func,
.compare_func = compare_func,
.process = process,
.netlink_handler = netlink_handler,
};
existing = ordered_set_get(link->manager->request_queue, req);
existing = ordered_set_get(manager->request_queue, req);
if (existing) {
if (ret)
*ret = existing;
return 0;
}
r = ordered_set_ensure_put(&link->manager->request_queue, &request_hash_ops, req);
r = ordered_set_ensure_put(&manager->request_queue, &request_hash_ops, req);
if (r < 0)
return r;
req->manager = link->manager;
req->manager = manager;
req->counter = counter;
if (req->counter)
(*req->counter)++;
@@ -407,11 +164,41 @@ int link_queue_request(
return 1;
}
int manager_process_requests(sd_event_source *s, void *userdata) {
Manager *manager = userdata;
int r;
int netdev_queue_request(
NetDev *netdev,
request_process_func_t process,
Request **ret) {
assert(manager);
assert(netdev);
return request_new(netdev->manager, NULL, REQUEST_TYPE_NETDEV_INDEPENDENT,
netdev_ref(netdev), (mfree_func_t) netdev_unref,
trivial_hash_func, trivial_compare_func,
process, NULL, NULL, ret);
}
int link_queue_request_full(
Link *link,
RequestType type,
void *userdata,
mfree_func_t free_func,
hash_func_t hash_func,
compare_func_t compare_func,
request_process_func_t process,
unsigned *counter,
request_netlink_handler_t netlink_handler,
Request **ret) {
assert(link);
return request_new(link->manager, link, type,
userdata, free_func, hash_func, compare_func,
process, counter, netlink_handler, ret);
}
int manager_process_requests(sd_event_source *s, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
int r;
for (;;) {
bool processed = false;
@@ -421,83 +208,9 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
_unused_ _cleanup_(request_unrefp) Request *ref = request_ref(req);
_cleanup_(link_unrefp) Link *link = link_ref(req->link);
switch (req->type) {
case REQUEST_TYPE_ACTIVATE_LINK:
r = link_process_activation(req, req->link, req->userdata);
break;
case REQUEST_TYPE_ADDRESS:
r = address_process_request(req, req->link, req->address);
break;
case REQUEST_TYPE_ADDRESS_LABEL:
r = address_label_process_request(req, req->link, req->label);
break;
case REQUEST_TYPE_BRIDGE_FDB:
r = bridge_fdb_process_request(req, req->link, req->fdb);
break;
case REQUEST_TYPE_BRIDGE_MDB:
r = bridge_mdb_process_request(req, req->link, req->mdb);
break;
case REQUEST_TYPE_DHCP_SERVER:
r = dhcp_server_process_request(req, req->link, NULL);
break;
case REQUEST_TYPE_DHCP4_CLIENT:
r = dhcp4_process_request(req, req->link, NULL);
break;
case REQUEST_TYPE_DHCP6_CLIENT:
r = dhcp6_process_request(req, req->link, NULL);
break;
case REQUEST_TYPE_IPV6_PROXY_NDP:
r = ipv6_proxy_ndp_address_process_request(req, req->link, req->ipv6_proxy_ndp);
break;
case REQUEST_TYPE_NDISC:
r = ndisc_process_request(req, req->link, NULL);
break;
case REQUEST_TYPE_NEIGHBOR:
r = neighbor_process_request(req, req->link, req->neighbor);
break;
case REQUEST_TYPE_NETDEV_INDEPENDENT:
r = independent_netdev_process_request(req, req->link, req->netdev);
break;
case REQUEST_TYPE_NETDEV_STACKED:
r = stacked_netdev_process_request(req, req->link, req->netdev);
break;
case REQUEST_TYPE_NEXTHOP:
r = nexthop_process_request(req, req->link, req->nexthop);
break;
case REQUEST_TYPE_RADV:
r = radv_process_request(req, req->link, NULL);
break;
case REQUEST_TYPE_ROUTE:
r = route_process_request(req, req->link, req->route);
break;
case REQUEST_TYPE_ROUTING_POLICY_RULE:
r = routing_policy_rule_process_request(req, req->link, req->rule);
break;
case REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE:
case REQUEST_TYPE_SET_LINK_BOND:
case REQUEST_TYPE_SET_LINK_BRIDGE:
case REQUEST_TYPE_SET_LINK_BRIDGE_VLAN:
case REQUEST_TYPE_SET_LINK_CAN:
case REQUEST_TYPE_SET_LINK_FLAGS:
case REQUEST_TYPE_SET_LINK_GROUP:
case REQUEST_TYPE_SET_LINK_IPOIB:
case REQUEST_TYPE_SET_LINK_MAC:
case REQUEST_TYPE_SET_LINK_MASTER:
case REQUEST_TYPE_SET_LINK_MTU:
r = link_process_set_link(req, req->link, NULL);
break;
case REQUEST_TYPE_TC_QDISC:
r = qdisc_process_request(req, req->link, NULL);
break;
case REQUEST_TYPE_TC_CLASS:
r = tclass_process_request(req, req->link, NULL);
break;
case REQUEST_TYPE_UP_DOWN:
r = link_process_up_or_down(req, req->link, req->userdata);
break;
default:
return -EINVAL;
}
assert(req->process);
r = req->process(req, link, req->userdata);
if (r == 0)
continue;
@@ -533,7 +246,7 @@ static int request_netlink_handler(sd_netlink *nl, sd_netlink_message *m, Reques
return 0;
if (req->netlink_handler)
return req->netlink_handler(nl, m, req, req->link, req->object);
return req->netlink_handler(nl, m, req, req->link, req->userdata);
return 0;
}