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

systemd / systemd / 26546993077

27 May 2026 08:34PM UTC coverage: 72.995% (+0.3%) from 72.667%
26546993077

push

github

bluca
test-pressure: set timeout to make not wait forever

If this runs on a slow or busy machine, then we may not get enough
pressure to trigger the event sources. In such case, the test does not
finish. It is problematic when the test is _not_ run with 'meson test',
e.g. debian/ubuntu CIs.

Let's introduce a timeout for each event loop, and skip test cases
gracefully.

8 of 12 new or added lines in 1 file covered. (66.67%)

19671 existing lines in 226 files now uncovered.

337119 of 461841 relevant lines covered (72.99%)

1326365.62 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,625✔
18
        assert(req);
11,625✔
19

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

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

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

32
static Request *request_free(Request *req) {
5,951✔
33
        if (!req)
5,951✔
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,951✔
39

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

43
        if (req->counter)
5,951✔
44
                (*req->counter)--;
144✔
45

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

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

51
DEFINE_TRIVIAL_REF_UNREF_FUNC(Request, request, request_free);
155,753✔
52

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

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

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

64
        siphash24_compress_typesafe(req->type, state);
50,146✔
65

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

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

76
        siphash24_compress_typesafe(req->hash_func, state);
50,146✔
77
        siphash24_compress_typesafe(req->compare_func, state);
50,146✔
78

79
        if (req->hash_func)
50,146✔
80
                req->hash_func(req->userdata, state);
44,198✔
81
}
50,146✔
82

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

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

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

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

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

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

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

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

117
        if (a->compare_func)
14,320✔
118
                return a->compare_func(a->userdata, b->userdata);
12,686✔
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,951✔
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,951✔
144
        Request *existing;
5,951✔
145
        int r;
5,951✔
146

147
        assert(manager);
5,951✔
148
        assert(process);
5,951✔
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,951✔
157
                return -EBUSY;
158

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

163
        *req = (Request) {
11,902✔
164
                .n_ref = 1,
165
                .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
5,951✔
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,951✔
175
        if (existing) {
5,951✔
176
                if (ret)
295✔
177
                        *ret = existing;
×
178
                return 0;
179
        }
180

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

185
        req->manager = manager;
5,656✔
186
        req->free_func = free_func;
5,656✔
187
        req->counter = counter;
5,656✔
188
        if (req->counter)
5,656✔
189
                (*req->counter)++;
4,723✔
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,656✔
194

195
        if (ret)
5,656✔
196
                *ret = req;
355✔
197

198
        TAKE_PTR(req);
5,656✔
199
        return 1;
5,656✔
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,479✔
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,479✔
236

237
        return request_new(link->manager, link, type,
5,479✔
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,573✔
277
        Request *req;
91,573✔
278
        int r;
91,573✔
279

280
        assert(manager);
91,573✔
281

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

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

288
        ORDERED_SET_FOREACH(req, manager->request_queue) {
370,290✔
289
                if (req->waiting_reply)
280,805✔
290
                        continue; /* Already processed, and waiting for netlink reply. */
209,762✔
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 ||
141,282✔
295
                    netlink_get_reply_callback_count(manager->genl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
140,478✔
296
                    netlink_get_reply_callback_count(manager->nfnl) >= REPLY_CALLBACK_COUNT_THRESHOLD)
70,239✔
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);
140,478✔
301
                _cleanup_(link_unrefp) Link *link = link_ref(req->link);
140,478✔
302

303
                assert(req->process);
70,239✔
304
                r = req->process(req, link, req->userdata);
70,239✔
305
                if (r < 0) {
70,239✔
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)
70,239✔
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)
70,239✔
322
                        break; /* New request is queued. Exit from the loop. */
323
        }
324

325
        return 0;
90,338✔
326
}
327

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

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

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

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

343
        return 0;
344
}
345

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

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

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

357
        request_ref(req);
4,637✔
358
        req->waiting_reply = true;
4,637✔
359
        return 0;
4,637✔
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);
994✔
405

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

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

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

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

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

423
DEFINE_TRIVIAL_CLEANUP_FUNC(RemoveRequest*, remove_request_free);
1,877✔
424
DEFINE_TRIVIAL_DESTRUCTOR(remove_request_destroy_callback, RemoveRequest, remove_request_free);
1,877✔
UNCOV
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,877✔
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,877✔
442
        int r;
1,877✔
443

444
        assert(manager);
1,877✔
445
        assert(userdata);
1,877✔
446
        assert(netlink);
1,877✔
447
        assert(message);
1,877✔
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,877✔
453
                return 0; /* ignored */
454

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

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

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

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

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

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

483
        assert(manager);
92,061✔
484

485
        while ((req = ordered_set_first(manager->remove_request_queue))) {
93,938✔
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)
3,112✔
489
                        return 0;
490

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

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

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

504
                        /* Then, make the link enter the failed state. */
505
                        if (link)
×
UNCOV
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,877✔
512
                        ordered_set_remove(manager->remove_request_queue, req);
1,877✔
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