• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 27078126363

06 Jun 2026 12:44PM UTC coverage: 72.973%. Remained the same
27078126363

push

github

bluca
docs/INHIBITOR_LOCKS: Update sentence for the new mode added

804874d26 added a new mode but the
sentence wasn't updated and it was still stating that there are Two modes
instead.

337405 of 462369 relevant lines covered (72.97%)

1289653.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.85
/src/network/networkd-queue.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "sd-netlink.h"
4

5
#include "alloc-util.h"
6
#include "netdev.h"
7
#include "netlink-util.h"
8
#include "networkd-link.h"
9
#include "networkd-manager.h"
10
#include "networkd-queue.h"
11
#include "ordered-set.h"
12
#include "siphash24.h"
13
#include "string-table.h"
14

15
#define REPLY_CALLBACK_COUNT_THRESHOLD 128
16

17
static Request* request_detach_impl(Request *req) {
11,624✔
18
        assert(req);
11,624✔
19

20
        if (!req->manager)
11,624✔
21
                return NULL;
22

23
        ordered_set_remove(req->manager->request_queue, req);
5,657✔
24
        req->manager = NULL;
5,657✔
25
        return req;
5,657✔
26
}
27

28
void request_detach(Request *req) {
5,678✔
29
        request_unref(request_detach_impl(req));
5,678✔
30
}
5,678✔
31

32
static Request *request_free(Request *req) {
5,946✔
33
        if (!req)
5,946✔
34
                return NULL;
35

36
        /* To prevent from triggering assertions in the hash and compare functions, remove this request
37
         * from the set before freeing userdata below. */
38
        request_detach_impl(req);
5,946✔
39

40
        if (req->free_func)
5,946✔
41
                req->free_func(req->userdata);
3,991✔
42

43
        if (req->counter)
5,946✔
44
                (*req->counter)--;
141✔
45

46
        link_unref(req->link); /* link may be NULL, but link_unref() can handle it gracefully. */
5,946✔
47

48
        return mfree(req);
5,946✔
49
}
50

51
DEFINE_TRIVIAL_REF_UNREF_FUNC(Request, request, request_free);
153,643✔
52

53
static void request_destroy_callback(Request *req) {
4,641✔
54
        assert(req);
4,641✔
55

56
        request_detach(req);
4,641✔
57
        request_unref(req);
4,641✔
58
}
4,641✔
59

60
static void request_hash_func(const Request *req, struct siphash *state) {
49,790✔
61
        assert(req);
49,790✔
62
        assert(state);
49,790✔
63

64
        siphash24_compress_typesafe(req->type, state);
49,790✔
65

66
        if (!IN_SET(req->type,
49,790✔
67
                    REQUEST_TYPE_NEXTHOP,
68
                    REQUEST_TYPE_ROUTE,
69
                    REQUEST_TYPE_ROUTING_POLICY_RULE)) {
70

71
                siphash24_compress_boolean(req->link, state);
25,985✔
72
                if (req->link)
25,985✔
73
                        siphash24_compress_typesafe(req->link->ifindex, state);
24,687✔
74
        }
75

76
        siphash24_compress_typesafe(req->hash_func, state);
49,790✔
77
        siphash24_compress_typesafe(req->compare_func, state);
49,790✔
78

79
        if (req->hash_func)
49,790✔
80
                req->hash_func(req->userdata, state);
43,803✔
81
}
49,790✔
82

83
static int request_compare_func(const struct Request *a, const struct Request *b) {
23,123✔
84
        int r;
23,123✔
85

86
        assert(a);
23,123✔
87
        assert(b);
23,123✔
88

89
        r = CMP(a->type, b->type);
23,123✔
90
        if (r != 0)
16,457✔
91
                return r;
92

93
        if (!IN_SET(a->type,
14,292✔
94
                    REQUEST_TYPE_NEXTHOP,
95
                    REQUEST_TYPE_ROUTE,
96
                    REQUEST_TYPE_ROUTING_POLICY_RULE)) {
97

98
                r = CMP(!!a->link, !!b->link);
9,145✔
99
                if (r != 0)
9,145✔
100
                        return r;
101

102
                if (a->link) {
9,145✔
103
                        r = CMP(a->link->ifindex, b->link->ifindex);
8,467✔
104
                        if (r != 0)
8,334✔
105
                                return r;
106
                }
107
        }
108

109
        r = CMP(PTR_TO_UINT64(a->hash_func), PTR_TO_UINT64(b->hash_func));
14,044✔
110
        if (r != 0)
14,044✔
111
                return r;
112

113
        r = CMP(PTR_TO_UINT64(a->compare_func), PTR_TO_UINT64(b->compare_func));
14,044✔
114
        if (r != 0)
14,044✔
115
                return r;
116

117
        if (a->compare_func)
14,044✔
118
                return a->compare_func(a->userdata, b->userdata);
12,413✔
119

120
        return 0;
121
}
122

123
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
4✔
124
                request_hash_ops,
125
                Request,
126
                request_hash_func,
127
                request_compare_func,
128
                request_detach);
129

130
static int request_new(
5,946✔
131
                Manager *manager,
132
                Link *link,
133
                RequestType type,
134
                void *userdata,
135
                mfree_func_t free_func,
136
                hash_func_t hash_func,
137
                compare_func_t compare_func,
138
                request_process_func_t process,
139
                unsigned *counter,
140
                request_netlink_handler_t netlink_handler,
141
                Request **ret) {
142

143
        _cleanup_(request_unrefp) Request *req = NULL;
5,946✔
144
        Request *existing;
5,946✔
145
        int r;
5,946✔
146

147
        assert(manager);
5,946✔
148
        assert(process);
5,946✔
149

150
        /* Note, requests will be processed only when the manager is in MANAGER_RUNNING. If a new operation
151
         * is requested when the manager is in MANAGER_TERMINATING or MANAGER_RESTARTING, the request will be
152
         * successfully queued but will never be processed. Then, here why we refuse new requests when the
153
         * manager is in MANAGER_STOPPED? This is because we cannot call link_ref() in that case, as this may
154
         * be called during link_free(), that means the reference counter of the link is already 0 and
155
         * calling link_ref() below triggers assertion. */
156
        if (manager->state == MANAGER_STOPPED)
5,946✔
157
                return -EBUSY;
158

159
        req = new(Request, 1);
5,946✔
160
        if (!req)
5,946✔
161
                return -ENOMEM;
162

163
        *req = (Request) {
11,892✔
164
                .n_ref = 1,
165
                .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
5,946✔
166
                .type = type,
167
                .userdata = userdata,
168
                .hash_func = hash_func,
169
                .compare_func = compare_func,
170
                .process = process,
171
                .netlink_handler = netlink_handler,
172
        };
173

174
        existing = ordered_set_get(manager->request_queue, req);
5,946✔
175
        if (existing) {
5,946✔
176
                if (ret)
289✔
177
                        *ret = existing;
×
178
                return 0;
179
        }
180

181
        r = ordered_set_ensure_put(&manager->request_queue, &request_hash_ops, req);
5,657✔
182
        if (r < 0)
5,657✔
183
                return r;
184

185
        req->manager = manager;
5,657✔
186
        req->free_func = free_func;
5,657✔
187
        req->counter = counter;
5,657✔
188
        if (req->counter)
5,657✔
189
                (*req->counter)++;
4,724✔
190

191
        /* If this is called in the ORDERED_SET_FOREACH() loop of manager_process_requests(), we need to
192
         * exit from the loop, due to the limitation of the iteration on OrderedSet. */
193
        manager->request_queued = true;
5,657✔
194

195
        if (ret)
5,657✔
196
                *ret = req;
353✔
197

198
        TAKE_PTR(req);
5,657✔
199
        return 1;
5,657✔
200
}
201

202
int netdev_queue_request(
471✔
203
                NetDev *netdev,
204
                request_process_func_t process,
205
                Request **ret) {
206

207
        int r;
471✔
208

209
        assert(netdev);
471✔
210
        assert(netdev->manager);
471✔
211

212
        r = request_new(netdev->manager, NULL, REQUEST_TYPE_NETDEV_INDEPENDENT,
471✔
213
                        netdev, (mfree_func_t) netdev_unref,
214
                        trivial_hash_func, trivial_compare_func,
215
                        process, NULL, NULL, ret);
216
        if (r <= 0)
471✔
217
                return r;
218

219
        netdev_ref(netdev);
471✔
220
        return 1;
471✔
221
}
222

223
int link_queue_request_full(
5,474✔
224
                Link *link,
225
                RequestType type,
226
                void *userdata,
227
                mfree_func_t free_func,
228
                hash_func_t hash_func,
229
                compare_func_t compare_func,
230
                request_process_func_t process,
231
                unsigned *counter,
232
                request_netlink_handler_t netlink_handler,
233
                Request **ret) {
234

235
        assert(link);
5,474✔
236

237
        return request_new(link->manager, link, type,
5,474✔
238
                           userdata, free_func, hash_func, compare_func,
239
                           process, counter, netlink_handler, ret);
240
}
241

242
int manager_queue_request_full(
1✔
243
                Manager *manager,
244
                RequestType type,
245
                void *userdata,
246
                mfree_func_t free_func,
247
                hash_func_t hash_func,
248
                compare_func_t compare_func,
249
                request_process_func_t process,
250
                unsigned *counter,
251
                request_netlink_handler_t netlink_handler,
252
                Request **ret) {
253

254
        return request_new(manager, NULL, type,
1✔
255
                           userdata, free_func, hash_func, compare_func,
256
                           process, counter, netlink_handler, ret);
257
}
258

259
int link_requeue_request(Link *link, Request *req, void *userdata, Request **ret) {
49✔
260
        assert(link);
49✔
261
        assert(req);
49✔
262

263
        return link_queue_request_full(
49✔
264
                        link,
265
                        req->type,
266
                        userdata,
267
                        req->free_func,
268
                        req->hash_func,
269
                        req->compare_func,
270
                        req->process,
271
                        req->counter,
272
                        req->netlink_handler,
273
                        ret);
274
}
275

276
int manager_process_requests(Manager *manager) {
91,560✔
277
        Request *req;
91,560✔
278
        int r;
91,560✔
279

280
        assert(manager);
91,560✔
281

282
        /* Process only when no remove request is queued. */
283
        if (!ordered_set_isempty(manager->remove_request_queue))
91,560✔
284
                return 0;
91,560✔
285

286
        manager->request_queued = false;
90,686✔
287

288
        ORDERED_SET_FOREACH(req, manager->request_queue) {
370,574✔
289
                if (req->waiting_reply)
280,740✔
290
                        continue; /* Already processed, and waiting for netlink reply. */
210,756✔
291

292
                /* Typically, requests send netlink message asynchronously. If there are many requests
293
                 * queued, then this event may make reply callback queue in sd-netlink full. */
294
                if (netlink_get_reply_callback_count(manager->rtnl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
139,165✔
295
                    netlink_get_reply_callback_count(manager->genl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
138,362✔
296
                    netlink_get_reply_callback_count(manager->nfnl) >= REPLY_CALLBACK_COUNT_THRESHOLD)
69,181✔
297
                        break;
298

299
                /* Avoid the request and link freed by req->process() and request_detach(). */
300
                _unused_ _cleanup_(request_unrefp) Request *req_unref = request_ref(req);
138,362✔
301
                _cleanup_(link_unrefp) Link *link = link_ref(req->link);
138,362✔
302

303
                assert(req->process);
69,181✔
304
                r = req->process(req, link, req->userdata);
69,181✔
305
                if (r < 0) {
69,181✔
306
                        request_detach(req);
×
307

308
                        if (link) {
×
309
                                link_enter_failed(link);
×
310
                                /* link_enter_failed() may detach multiple requests from the queue.
311
                                 * Hence, we need to exit from the loop. */
312
                                break;
313
                        }
314
                }
315
                if (r > 0 && !req->waiting_reply)
69,181✔
316
                        /* If the request sends netlink message, e.g. for Address or so, the Request object is
317
                         * referenced by the netlink slot, and will be detached later by its destroy callback.
318
                         * Otherwise, e.g. for DHCP client or so, detach the request from queue now. */
319
                        request_detach(req);
916✔
320

321
                if (manager->request_queued)
69,181✔
322
                        break; /* New request is queued. Exit from the loop. */
323
        }
324

325
        return 0;
90,686✔
326
}
327

328
static int request_netlink_handler(sd_netlink *nl, sd_netlink_message *m, Request *req) {
4,641✔
329
        assert(req);
4,641✔
330

331
        if (req->counter) {
4,641✔
332
                assert(*req->counter > 0);
4,569✔
333
                (*req->counter)--;
4,569✔
334
                req->counter = NULL; /* To prevent double decrement on free. */
4,569✔
335
        }
336

337
        if (req->link && IN_SET(req->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
4,641✔
338
                return 0;
339

340
        if (req->netlink_handler)
4,635✔
341
                return req->netlink_handler(nl, m, req, req->link, req->userdata);
4,635✔
342

343
        return 0;
344
}
345

346
int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *req) {
4,641✔
347
        int r;
4,641✔
348

349
        assert(nl);
4,641✔
350
        assert(m);
4,641✔
351
        assert(req);
4,641✔
352

353
        r = netlink_call_async(nl, NULL, m, request_netlink_handler, request_destroy_callback, req);
4,641✔
354
        if (r < 0)
4,641✔
355
                return r;
356

357
        request_ref(req);
4,641✔
358
        req->waiting_reply = true;
4,641✔
359
        return 0;
4,641✔
360
}
361

362
static const char *const request_type_table[_REQUEST_TYPE_MAX] = {
363
        [REQUEST_TYPE_ACTIVATE_LINK]                    = "activate link",
364
        [REQUEST_TYPE_ADDRESS]                          = "address",
365
        [REQUEST_TYPE_ADDRESS_LABEL]                    = "address label",
366
        [REQUEST_TYPE_BRIDGE_FDB]                       = "bridge FDB",
367
        [REQUEST_TYPE_BRIDGE_MDB]                       = "bridge MDB",
368
        [REQUEST_TYPE_DHCP_RELAY]                       = "DHCP relay agent",
369
        [REQUEST_TYPE_DHCP_SERVER]                      = "DHCP server",
370
        [REQUEST_TYPE_DHCP4_CLIENT]                     = "DHCPv4 client",
371
        [REQUEST_TYPE_DHCP6_CLIENT]                     = "DHCPv6 client",
372
        [REQUEST_TYPE_IPV6_PROXY_NDP]                   = "IPv6 proxy NDP",
373
        [REQUEST_TYPE_NDISC]                            = "NDisc",
374
        [REQUEST_TYPE_NEIGHBOR]                         = "neighbor",
375
        [REQUEST_TYPE_NETDEV_INDEPENDENT]               = "independent netdev",
376
        [REQUEST_TYPE_NETDEV_STACKED]                   = "stacked netdev",
377
        [REQUEST_TYPE_NEXTHOP]                          = "nexthop",
378
        [REQUEST_TYPE_RADV]                             = "RADV",
379
        [REQUEST_TYPE_ROUTE]                            = "route",
380
        [REQUEST_TYPE_ROUTING_POLICY_RULE]              = "routing policy rule",
381
        [REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE] = "IPv6LL address generation mode",
382
        [REQUEST_TYPE_SET_LINK_BOND]                    = "bond configurations",
383
        [REQUEST_TYPE_SET_LINK_BRIDGE]                  = "bridge configurations",
384
        [REQUEST_TYPE_SET_LINK_BRIDGE_VLAN]             = "bridge VLAN configurations (step 1)",
385
        [REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN]             = "bridge VLAN configurations (step 2)",
386
        [REQUEST_TYPE_SET_LINK_CAN]                     = "CAN interface configurations",
387
        [REQUEST_TYPE_SET_LINK_FLAGS]                   = "link flags",
388
        [REQUEST_TYPE_SET_LINK_GROUP]                   = "interface group",
389
        [REQUEST_TYPE_SET_LINK_IPOIB]                   = "IPoIB configurations",
390
        [REQUEST_TYPE_SET_LINK_MAC]                     = "MAC address",
391
        [REQUEST_TYPE_SET_LINK_MASTER]                  = "master interface",
392
        [REQUEST_TYPE_SET_LINK_MTU]                     = "MTU",
393
        [REQUEST_TYPE_SRIOV_VF_MAC]                     = "SR-IOV VF MAC address",
394
        [REQUEST_TYPE_SRIOV_VF_SPOOFCHK]                = "SR-IOV VF spoof check",
395
        [REQUEST_TYPE_SRIOV_VF_RSS_QUERY_EN]            = "SR-IOV VF RSS query",
396
        [REQUEST_TYPE_SRIOV_VF_TRUST]                   = "SR-IOV VF trust",
397
        [REQUEST_TYPE_SRIOV_VF_LINK_STATE]              = "SR-IOV VF link state",
398
        [REQUEST_TYPE_SRIOV_VF_VLAN_LIST]               = "SR-IOV VF vlan list",
399
        [REQUEST_TYPE_TC_QDISC]                         = "QDisc",
400
        [REQUEST_TYPE_TC_CLASS]                         = "TClass",
401
        [REQUEST_TYPE_UP_DOWN]                          = "bring link up or down",
402
};
403

404
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(request_type, RequestType);
992✔
405

406
static RemoveRequest* remove_request_free(RemoveRequest *req) {
1,902✔
407
        if (!req)
1,902✔
408
                return NULL;
409

410
        if (req->manager)
1,902✔
411
                ordered_set_remove(req->manager->remove_request_queue, req);
1,902✔
412

413
        if (req->unref_func)
1,902✔
414
                req->unref_func(req->userdata);
1,902✔
415

416
        link_unref(req->link);
1,902✔
417
        sd_netlink_unref(req->netlink);
1,902✔
418
        sd_netlink_message_unref(req->message);
1,902✔
419

420
        return mfree(req);
1,902✔
421
}
422

423
DEFINE_TRIVIAL_CLEANUP_FUNC(RemoveRequest*, remove_request_free);
1,902✔
424
DEFINE_TRIVIAL_DESTRUCTOR(remove_request_destroy_callback, RemoveRequest, remove_request_free);
1,902✔
425
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
426
                remove_request_hash_ops,
427
                void,
428
                trivial_hash_func,
429
                trivial_compare_func,
430
                remove_request_free);
431

432
int remove_request_add(
1,902✔
433
                Manager *manager,
434
                Link *link,
435
                void *userdata,
436
                mfree_func_t unref_func,
437
                sd_netlink *netlink,
438
                sd_netlink_message *message,
439
                remove_request_netlink_handler_t netlink_handler) {
440

441
        _cleanup_(remove_request_freep) RemoveRequest *req = NULL;
1,902✔
442
        int r;
1,902✔
443

444
        assert(manager);
1,902✔
445
        assert(userdata);
1,902✔
446
        assert(netlink);
1,902✔
447
        assert(message);
1,902✔
448

449
        /* Unlike request_new(), remove requests will be also processed when the manager is in
450
         * MANAGER_TERMINATING or MANAGER_RESTARTING. When the manager is in MANAGER_STOPPED, we cannot
451
         * queue new remove requests anymore with the same reason explained in request_new(). */
452
        if (manager->state == MANAGER_STOPPED)
1,902✔
453
                return 0; /* ignored */
454

455
        req = new(RemoveRequest, 1);
1,902✔
456
        if (!req)
1,902✔
457
                return -ENOMEM;
458

459
        *req = (RemoveRequest) {
3,804✔
460
                .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
1,902✔
461
                .userdata = userdata,
462
                .netlink = sd_netlink_ref(netlink),
1,902✔
463
                .message = sd_netlink_message_ref(message),
1,902✔
464
                .netlink_handler = netlink_handler,
465
        };
466

467
        r = ordered_set_ensure_put(&manager->remove_request_queue, &remove_request_hash_ops, req);
1,902✔
468
        if (r < 0)
1,902✔
469
                return r;
470
        assert(r > 0);
1,902✔
471

472
        req->manager = manager;
1,902✔
473
        req->unref_func = unref_func;
1,902✔
474

475
        TAKE_PTR(req);
1,902✔
476
        return 1; /* queued */
1,902✔
477
}
478

479
int manager_process_remove_requests(Manager *manager) {
92,048✔
480
        RemoveRequest *req;
92,048✔
481
        int r;
92,048✔
482

483
        assert(manager);
92,048✔
484

485
        while ((req = ordered_set_first(manager->remove_request_queue))) {
93,950✔
486

487
                /* Do not make the reply callback queue in sd-netlink full. */
488
                if (netlink_get_reply_callback_count(req->netlink) >= REPLY_CALLBACK_COUNT_THRESHOLD)
2,776✔
489
                        return 0;
490

491
                r = netlink_call_async(
1,902✔
492
                                req->netlink, NULL, req->message,
493
                                req->netlink_handler,
494
                                remove_request_destroy_callback,
495
                                req);
496
                if (r < 0) {
1,902✔
497
                        _cleanup_(link_unrefp) Link *link = link_ref(req->link);
×
498

499
                        log_link_warning_errno(link, r, "Failed to call netlink message: %m");
×
500

501
                        /* First free the request. */
502
                        remove_request_free(req);
×
503

504
                        /* Then, make the link enter the failed state. */
505
                        if (link)
×
506
                                link_enter_failed(link);
×
507

508
                } else {
509
                        /* On success, netlink needs to be unref()ed. Otherwise, the netlink and remove
510
                         * request may not freed on shutting down. */
511
                        req->netlink = sd_netlink_unref(req->netlink);
1,902✔
512
                        ordered_set_remove(manager->remove_request_queue, req);
1,902✔
513
                }
514
        }
515

516
        return 0;
517
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc