]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/queue: introduce RemoveRequest and relevant functions
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 2 Jan 2024 19:41:34 +0000 (04:41 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 12 Jan 2024 00:36:00 +0000 (09:36 +0900)
This is similar to Request, but will be used on removing configuration
(e.g. address, route, and so on).

By using another queue for removing configuration, then we can avoid to
fill the reply callback buffer in sd-netlink by remove message calls.

Follow-up for 4e6a35e2b2fad0f167a71b63525f4210bc858bc6.

src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-queue.c
src/network/networkd-queue.h

index 8933fc49776d6053b37c412ab6f9d90d293d3daf..6f3d90d64c9debe56a4637a61d9fa97051784aad 100644 (file)
@@ -424,6 +424,7 @@ static int manager_connect_rtnl(Manager *m, int fd) {
 static int manager_post_handler(sd_event_source *s, void *userdata) {
         Manager *manager = ASSERT_PTR(userdata);
 
+        (void) manager_process_remove_requests(manager);
         (void) manager_process_requests(manager);
         (void) manager_clean_all(manager);
         return 0;
@@ -594,6 +595,7 @@ Manager* manager_free(Manager *m) {
                 (void) link_stop_engines(link, true);
 
         m->request_queue = ordered_set_free(m->request_queue);
+        m->remove_request_queue = ordered_set_free(m->remove_request_queue);
 
         m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
         m->new_wlan_ifindices = set_free(m->new_wlan_ifindices);
index a2edfd0e79c0c8c807bca728e1e243271b6e8f4a..a97ae8ea213e67149a0d902cab6a2381a40b847d 100644 (file)
@@ -102,6 +102,7 @@ struct Manager {
 
         bool request_queued;
         OrderedSet *request_queue;
+        OrderedSet *remove_request_queue;
 
         Hashmap *tuntap_fds_by_name;
 };
index 1678510d522950007028c6086c7d728677c65848..0b89ec97364b2971c36d8413ad5db7aeb97d51d5 100644 (file)
@@ -224,6 +224,10 @@ int manager_process_requests(Manager *manager) {
 
         assert(manager);
 
+        /* Process only when no remove request is queued. */
+        if (!ordered_set_isempty(manager->remove_request_queue))
+                return 0;
+
         manager->request_queued = false;
 
         ORDERED_SET_FOREACH(req, manager->request_queue) {
@@ -339,3 +343,110 @@ static const char *const request_type_table[_REQUEST_TYPE_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(request_type, RequestType);
+
+static RemoveRequest* remove_request_free(RemoveRequest *req) {
+        if (!req)
+                return NULL;
+
+        if (req->manager)
+                ordered_set_remove(req->manager->remove_request_queue, req);
+
+        if (req->unref_func)
+                req->unref_func(req->userdata);
+
+        link_unref(req->link);
+        sd_netlink_unref(req->netlink);
+        sd_netlink_message_unref(req->message);
+
+        return mfree(req);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(RemoveRequest*, remove_request_free);
+DEFINE_TRIVIAL_DESTRUCTOR(remove_request_destroy_callback, RemoveRequest, remove_request_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                remove_request_hash_ops,
+                void,
+                trivial_hash_func,
+                trivial_compare_func,
+                remove_request_free);
+
+int remove_request_add(
+                Manager *manager,
+                Link *link,
+                void *userdata,
+                mfree_func_t unref_func,
+                sd_netlink *netlink,
+                sd_netlink_message *message,
+                remove_request_netlink_handler_t netlink_handler) {
+
+        _cleanup_(remove_request_freep) RemoveRequest *req = NULL;
+        int r;
+
+        assert(manager);
+        assert(userdata);
+        assert(netlink);
+        assert(message);
+
+        req = new(RemoveRequest, 1);
+        if (!req)
+                return -ENOMEM;
+
+        *req = (RemoveRequest) {
+                .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
+                .userdata = userdata,
+                .netlink = sd_netlink_ref(netlink),
+                .message = sd_netlink_message_ref(message),
+                .netlink_handler = netlink_handler,
+        };
+
+        r = ordered_set_ensure_put(&manager->remove_request_queue, &remove_request_hash_ops, req);
+        if (r < 0)
+                return r;
+        assert(r > 0);
+
+        req->manager = manager;
+        req->unref_func = unref_func;
+
+        TAKE_PTR(req);
+        return 0;
+}
+
+int manager_process_remove_requests(Manager *manager) {
+        RemoveRequest *req;
+        int r;
+
+        assert(manager);
+
+        while ((req = ordered_set_first(manager->remove_request_queue))) {
+
+                /* Do not make the reply callback queue in sd-netlink full. */
+                if (netlink_get_reply_callback_count(req->netlink) >= REPLY_CALLBACK_COUNT_THRESHOLD)
+                        return 0;
+
+                r = netlink_call_async(
+                                req->netlink, NULL, req->message,
+                                req->netlink_handler,
+                                remove_request_destroy_callback,
+                                req);
+                if (r < 0) {
+                        _cleanup_(link_unrefp) Link *link = link_ref(req->link);
+
+                        log_link_warning_errno(link, r, "Failed to call netlink message: %m");
+
+                        /* First free the request. */
+                        remove_request_free(req);
+
+                        /* Then, make the link enter the failed state. */
+                        if (link)
+                                link_enter_failed(link);
+
+                } else {
+                        /* On success, netlink needs to be unref()ed. Otherwise, the netlink and remove
+                         * request may not freed on shutting down. */
+                        req->netlink = sd_netlink_unref(req->netlink);
+                        ordered_set_remove(manager->remove_request_queue, req);
+                }
+        }
+
+        return 0;
+}
index 21fa7d9453475ae1182b4f31b20254e9c4ab0a0c..e911fdd33a82eb0511043a06c77cc25915829205 100644 (file)
@@ -139,3 +139,52 @@ int manager_process_requests(Manager *manager);
 int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *req);
 
 const char* request_type_to_string(RequestType t) _const_;
+
+typedef struct RemoveRequest RemoveRequest;
+typedef int (*remove_request_netlink_handler_t)(sd_netlink *nl, sd_netlink_message *m, RemoveRequest *req);
+
+struct RemoveRequest {
+        Manager *manager;
+        Link *link;
+        void *userdata; /* e.g. Address */
+        mfree_func_t unref_func; /* e.g. address_unref() */
+        sd_netlink *netlink;
+        sd_netlink_message *message;
+        remove_request_netlink_handler_t netlink_handler;
+};
+
+int remove_request_add(
+                Manager *manager,
+                Link *link,
+                void *userdata, /* This is unref()ed when the call failed. */
+                mfree_func_t unref_func,
+                sd_netlink *netlink,
+                sd_netlink_message *message,
+                remove_request_netlink_handler_t netlink_handler);
+
+#define _remove_request_add(manager, link, data, name, nl, m, handler)  \
+        ({                                                              \
+                typeof(*data) *_data = (data);                          \
+                int _r;                                                 \
+                                                                        \
+                _r = remove_request_add(manager, link, _data,           \
+                                        (mfree_func_t) name##_unref,    \
+                                        nl, m, handler);                \
+                if (_r >= 0)                                            \
+                        name##_ref(_data);                              \
+                _r;                                                     \
+        })
+
+
+#define link_remove_request_add(link, data, name, nl, m, handler)       \
+        ({                                                              \
+                Link *_link = (link);                                   \
+                                                                        \
+                _remove_request_add(_link->manager, _link, data, name,  \
+                                    nl, m, handler);                    \
+        })
+
+#define manager_remove_request_add(manager, data, name, nl, m, handler) \
+        _remove_request_add(manager, NULL, data, name, nl, m, handler)
+
+int manager_process_remove_requests(Manager *manager);