]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: introduce request_hash_ops to dedup requests
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 19 May 2021 02:30:35 +0000 (11:30 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 7 Jun 2021 21:33:27 +0000 (06:33 +0900)
If KeepConfiguration= or ConfigureWithoutCarrier= is set, then the same
requests may be queued.

src/network/networkd-address.c
src/network/networkd-dhcp4.c
src/network/networkd-dhcp6.c
src/network/networkd-manager.c
src/network/networkd-ndisc.c
src/network/networkd-queue.c
src/network/networkd-queue.h

index 5192bbcc006d18aac0bf316cd6ff70080f0e1a4f..06c5c39f1ce6169d3a06ec259a6c2b572a891a3f 100644 (file)
@@ -1148,6 +1148,8 @@ int link_request_static_addresses(Link *link) {
                                          static_address_handler, &req);
                 if (r < 0)
                         return r;
+                if (r == 0)
+                        continue;
 
                 req->after_configure = static_address_after_configure;
         }
@@ -1178,6 +1180,8 @@ int link_request_static_addresses(Link *link) {
                                          static_address_handler, &req);
                 if (r < 0)
                         return r;
+                if (r == 0)
+                        continue;
 
                 req->after_configure = static_address_after_configure;
         }
@@ -1203,8 +1207,8 @@ int link_request_static_addresses(Link *link) {
                                                  static_address_handler, &req);
                         if (r < 0)
                                 return r;
-
-                        req->after_configure = static_address_after_configure;
+                        if (r > 0)
+                                req->after_configure = static_address_after_configure;
                 }
         }
 
index e9e6fe72c41b99f54b426c014a54fdcfcd4f98d4..934ee05002d3ea034093c432ca61c1cc2b5cac12 100644 (file)
@@ -208,7 +208,7 @@ static int dhcp4_request_route(Route *in, Link *link) {
 
         r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp4_messages,
                                dhcp4_route_handler, &req);
-        if (r < 0)
+        if (r <= 0)
                 return r;
 
         req->after_configure = dhcp4_after_route_configure;
@@ -1064,6 +1064,8 @@ static int dhcp4_request_address(Link *link, bool announce) {
                                  dhcp4_address_handler, &req);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to request DHCPv4 address: %m");
+        if (r == 0)
+                return 0;
 
         req->after_configure = dhcp4_after_address_configure;
 
index 7a2e7c106cbc9ba7132efede5c94a2a02b35d9b9..488f90332935a4ca3885b7188ff609625657da6d 100644 (file)
@@ -312,6 +312,8 @@ static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix, con
                                dhcp6_pd_route_handler, &req);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to request DHCPv6 prefix route: %m");
+        if (r == 0)
+                return 0;
 
         req->after_configure = dhcp6_pd_after_route_configure;
 
@@ -470,6 +472,8 @@ static int dhcp6_pd_request_address(
                                  dhcp6_pd_address_handler, &req);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to request DHCPv6 delegated prefix address: %m");
+        if (r == 0)
+                return 0;
 
         req->after_configure = dhcp6_pd_after_address_configure;
 
@@ -897,6 +901,8 @@ static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *ad
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to request unreachable route for DHCPv6 delegated subnet %s: %m",
                                             strna(buf));
+        if (r == 0)
+                return 0;
 
         req->after_configure = dhcp6_after_route_configure;
 
@@ -1173,6 +1179,8 @@ static int dhcp6_request_address(
                                  dhcp6_address_handler, &req);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to request DHCPv6 address %s: %m", strna(buffer));
+        if (r == 0)
+                return 0;
 
         req->after_configure = dhcp6_after_address_configure;
 
index 60e6d6f3690e24bc14dba68fbf2f9e5c8fda09ac..9406f8c630b80bc0c880d2a3db61cd0b07cbaaee 100644 (file)
@@ -452,7 +452,7 @@ Manager* manager_free(Manager *m) {
         HASHMAP_FOREACH(link, m->links)
                 (void) link_stop_engines(link, true);
 
-        m->request_queue = ordered_set_free_with_destructor(m->request_queue, request_free);
+        m->request_queue = ordered_set_free(m->request_queue);
 
         m->dhcp6_prefixes = hashmap_free_with_destructor(m->dhcp6_prefixes, dhcp6_pd_free);
         m->dhcp6_pd_prefixes = set_free_with_destructor(m->dhcp6_pd_prefixes, dhcp6_pd_free);
index 9ce579bfe5271141d91538f4465996b3802d8fe6..80cb80247a8b6bb65efbbdc104a17cd24c48de16 100644 (file)
@@ -395,7 +395,7 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
 
         r = link_request_route(link, TAKE_PTR(route), true, &link->ndisc_routes_messages,
                                ndisc_route_handler, &req);
-        if (r < 0)
+        if (r <= 0)
                 return r;
 
         req->userdata = sd_ndisc_router_ref(rt);
@@ -507,7 +507,7 @@ static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) {
 
         r = link_request_address(link, TAKE_PTR(address), true, &link->ndisc_addresses_messages,
                                  ndisc_address_handler, &req);
-        if (r < 0)
+        if (r <= 0)
                 return r;
 
         req->userdata = sd_ndisc_router_ref(rt);
index ccc1b37b402421a771598574ee1092628a56755c..01636468c6a5b16b9e02aa5babb0a5ad68899e8e 100644 (file)
@@ -49,16 +49,19 @@ static void request_free_object(RequestType type, void *object) {
         }
 }
 
-Request *request_free(Request *req) {
+static Request *request_free(Request *req) {
         if (!req)
                 return NULL;
 
+        if (req->link && req->link->manager)
+                /* To prevent from triggering assertions in hash functions, remove this request before
+                 * freeing object below. */
+                ordered_set_remove(req->link->manager->request_queue, req);
         if (req->on_free)
+                /* on_free() may use object. So, let's call this earlier. */
                 req->on_free(req);
         if (req->consume_object)
                 request_free_object(req->type, req->object);
-        if (req->link && req->link->manager)
-                ordered_set_remove(req->link->manager->request_queue, req);
         link_unref(req->link);
 
         return mfree(req);
@@ -73,6 +76,95 @@ void request_drop(Request *req) {
         request_free(req);
 }
 
+static void request_hash_func(const Request *req, struct siphash *state) {
+        assert(req);
+        assert(req->link);
+        assert(state);
+
+        siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state);
+        siphash24_compress(&req->type, sizeof(req->type), state);
+
+        switch(req->type) {
+        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:
+                /* 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:
+                /* This type does not have object. */
+                break;
+        case REQUEST_TYPE_IPV6_PROXY_NDP:
+                in6_addr_hash_func(req->ipv6_proxy_ndp, state);
+                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_ROUTE:
+                route_hash_func(req->route, state);
+                break;
+        case REQUEST_TYPE_ROUTING_POLICY_RULE:
+                routing_policy_rule_hash_func(req->rule, state);
+                break;
+        default:
+                assert_not_reached("invalid request type.");
+        }
+}
+
+static int request_compare_func(const struct Request *a, const struct Request *b) {
+        int r;
+
+        assert(a);
+        assert(b);
+        assert(a->link);
+        assert(b->link);
+
+        r = CMP(a->link->ifindex, b->link->ifindex);
+        if (r != 0)
+                return r;
+
+        r = CMP(a->type, b->type);
+        if (r != 0)
+                return r;
+
+        switch (a->type) {
+        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:
+                return trivial_compare_func(a->object, b->object);
+        case REQUEST_TYPE_DHCP_SERVER:
+                return 0;
+        case REQUEST_TYPE_IPV6_PROXY_NDP:
+                return in6_addr_compare_func(a->ipv6_proxy_ndp, b->ipv6_proxy_ndp);
+        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_ROUTING_POLICY_RULE:
+                return routing_policy_rule_compare_func(a->rule, b->rule);
+        default:
+                assert_not_reached("invalid request type.");
+        }
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                request_hash_ops,
+                Request,
+                request_hash_func,
+                request_compare_func,
+                request_free);
+
 int link_queue_request(
                 Link *link,
                 RequestType type,
@@ -101,7 +193,7 @@ int link_queue_request(
         }
 
         *req = (Request) {
-                .link = link,
+                .link = link_ref(link),
                 .type = type,
                 .object = object,
                 .consume_object = consume_object,
@@ -109,11 +201,18 @@ int link_queue_request(
                 .netlink_handler = netlink_handler,
         };
 
-        link_ref(link);
+        r = ordered_set_ensure_put(&link->manager->request_queue, &request_hash_ops, req);
+        if (r < 0) {
+                /* To prevent from removing duplicated request. */
+                req->link = link_unref(req->link);
 
-        r = ordered_set_ensure_put(&link->manager->request_queue, NULL, req);
-        if (r < 0)
+                if (r == -EEXIST) {
+                        if (ret)
+                                *ret = NULL;
+                        return 0;
+                }
                 return r;
+        }
 
         if (req->message_counter)
                 (*req->message_counter)++;
@@ -122,7 +221,7 @@ int link_queue_request(
                 *ret = req;
 
         TAKE_PTR(req);
-        return 0;
+        return 1;
 }
 
 int manager_process_requests(sd_event_source *s, void *userdata) {
index 813ef260b0d41777dfcb83d4a59f9f7ca2c47442..9fe954aa83411bf7c4e649407a82595acc225e21 100644 (file)
@@ -57,7 +57,6 @@ typedef struct Request {
         request_on_free_handler_t on_free;
 } Request;
 
-Request *request_free(Request *req);
 void request_drop(Request *req);
 
 int link_queue_request(