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

systemd / systemd / 29544664670

16 Jul 2026 04:17PM UTC coverage: 73.034% (+0.05%) from 72.983%
29544664670

push

github

yuwata
boot: fix MEMMAP_DEVICE_PATH EndingAddress field calculation

Let's do what EDK2 does.

Fixes: #43038

346521 of 474466 relevant lines covered (73.03%)

1340443.02 hits per line

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

94.95
/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,679✔
18
        assert(req);
11,679✔
19

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

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

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

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

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

41
        if (req->free_func)
5,982✔
42
                req->free_func(req->userdata);
4,012✔
43

44
        if (req->counter)
5,982✔
45
                (*req->counter)--;
152✔
46

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

49
        return mfree(req);
5,982✔
50
}
51

52
DEFINE_TRIVIAL_REF_UNREF_FUNC(Request, request, request_free);
158,315✔
53

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

57
        request_detach(req);
4,653✔
58
        request_unref(req);
4,653✔
59
}
4,653✔
60

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

65
        siphash24_compress_typesafe(req->type, state);
49,599✔
66

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

72
                siphash24_compress_boolean(req->link, state);
26,173✔
73
                if (req->link)
26,173✔
74
                        siphash24_compress_typesafe(req->link->ifindex, state);
24,829✔
75
        }
76

77
        siphash24_compress_typesafe(req->hash_func, state);
49,599✔
78
        siphash24_compress_typesafe(req->compare_func, state);
49,599✔
79

80
        if (req->hash_func)
49,599✔
81
                req->hash_func(req->userdata, state);
43,533✔
82
}
49,599✔
83

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

87
        assert(a);
23,357✔
88
        assert(b);
23,357✔
89

90
        r = CMP(a->type, b->type);
23,357✔
91
        if (r != 0)
16,795✔
92
                return r;
93

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

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

103
                if (a->link) {
9,397✔
104
                        r = CMP(a->link->ifindex, b->link->ifindex);
8,734✔
105
                        if (r != 0)
8,604✔
106
                                return r;
107
                }
108
        }
109

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

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

118
        if (a->compare_func)
14,292✔
119
                return a->compare_func(a->userdata, b->userdata);
12,648✔
120

121
        return 0;
122
}
123

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

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

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

148
        assert(manager);
5,982✔
149
        assert(process);
5,982✔
150

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

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

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

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

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

186
        req->manager = manager;
5,678✔
187
        req->free_func = free_func;
5,678✔
188
        req->counter = counter;
5,678✔
189
        if (req->counter)
5,678✔
190
                (*req->counter)++;
4,741✔
191

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

196
        if (ret)
5,678✔
197
                *ret = req;
358✔
198

199
        TAKE_PTR(req);
5,678✔
200
        return 1;
5,678✔
201
}
202

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

208
        int r;
474✔
209

210
        assert(netdev);
474✔
211
        assert(netdev->manager);
474✔
212

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

220
        netdev_ref(netdev);
474✔
221
        return 1;
474✔
222
}
223

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

236
        assert(link);
5,505✔
237

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

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

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

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

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

277
int manager_process_requests(Manager *manager) {
92,311✔
278
        Request *req;
92,311✔
279
        int r;
92,311✔
280

281
        assert(manager);
92,311✔
282

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

287
        manager->request_queued = false;
91,798✔
288

289
        ORDERED_SET_FOREACH(req, manager->request_queue) {
374,572✔
290
                if (req->waiting_reply)
283,626✔
291
                        continue; /* Already processed, and waiting for netlink reply. */
211,335✔
292

293
                /* Typically, requests send netlink message asynchronously. If there are many requests
294
                 * queued, then this event may make reply callback queue in sd-netlink full. */
295
                if (netlink_get_reply_callback_count(manager->rtnl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
143,779✔
296
                    netlink_get_reply_callback_count(manager->genl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
142,976✔
297
                    netlink_get_reply_callback_count(manager->nfnl) >= REPLY_CALLBACK_COUNT_THRESHOLD)
71,488✔
298
                        break;
299

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

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

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

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

326
        return 0;
91,798✔
327
}
328

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

332
        assert(req->netlink_slot);
4,646✔
333
        assert_se(sd_netlink_slot_set_floating(req->netlink_slot, true) >= 0);
4,646✔
334
        req->netlink_slot = sd_netlink_slot_unref(req->netlink_slot);
4,646✔
335

336
        if (req->counter) {
4,646✔
337
                assert(*req->counter > 0);
4,575✔
338
                (*req->counter)--;
4,575✔
339
                req->counter = NULL; /* To prevent double decrement on free. */
4,575✔
340
        }
341

342
        if (req->link && IN_SET(req->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
4,646✔
343
                return 0;
344

345
        if (req->netlink_handler)
4,646✔
346
                return req->netlink_handler(nl, m, req, req->link, req->userdata);
4,646✔
347

348
        return 0;
349
}
350

351
int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *req) {
4,653✔
352
        int r;
4,653✔
353

354
        assert(nl);
4,653✔
355
        assert(m);
4,653✔
356
        assert(req);
4,653✔
357

358
        r = netlink_call_async(nl, &req->netlink_slot, m, request_netlink_handler, request_destroy_callback, req);
4,653✔
359
        if (r < 0)
4,653✔
360
                return r;
361

362
        request_ref(req);
4,653✔
363
        req->waiting_reply = true;
4,653✔
364
        return 0;
4,653✔
365
}
366

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

409
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(request_type, RequestType);
997✔
410

411
static RemoveRequest* remove_request_free(RemoveRequest *req) {
1,890✔
412
        if (!req)
1,890✔
413
                return NULL;
414

415
        if (req->manager)
1,890✔
416
                ordered_set_remove(req->manager->remove_request_queue, req);
1,890✔
417

418
        if (req->unref_func)
1,890✔
419
                req->unref_func(req->userdata);
1,890✔
420

421
        link_unref(req->link);
1,890✔
422
        sd_netlink_unref(req->netlink);
1,890✔
423
        sd_netlink_message_unref(req->message);
1,890✔
424

425
        return mfree(req);
1,890✔
426
}
427

428
DEFINE_TRIVIAL_CLEANUP_FUNC(RemoveRequest*, remove_request_free);
1,890✔
429
DEFINE_TRIVIAL_DESTRUCTOR(remove_request_destroy_callback, RemoveRequest, remove_request_free);
1,890✔
430
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
431
                remove_request_hash_ops,
432
                void,
433
                trivial_hash_func,
434
                trivial_compare_func,
435
                remove_request_free);
436

437
int remove_request_add(
1,890✔
438
                Manager *manager,
439
                Link *link,
440
                void *userdata,
441
                mfree_func_t unref_func,
442
                sd_netlink *netlink,
443
                sd_netlink_message *message,
444
                remove_request_netlink_handler_t netlink_handler) {
445

446
        _cleanup_(remove_request_freep) RemoveRequest *req = NULL;
1,890✔
447
        int r;
1,890✔
448

449
        assert(manager);
1,890✔
450
        assert(userdata);
1,890✔
451
        assert(netlink);
1,890✔
452
        assert(message);
1,890✔
453

454
        /* Unlike request_new(), remove requests will be also processed when the manager is in
455
         * MANAGER_TERMINATING or MANAGER_RESTARTING. When the manager is in MANAGER_STOPPED, we cannot
456
         * queue new remove requests anymore with the same reason explained in request_new(). */
457
        if (manager->state == MANAGER_STOPPED)
1,890✔
458
                return 0; /* ignored */
459

460
        req = new(RemoveRequest, 1);
1,890✔
461
        if (!req)
1,890✔
462
                return -ENOMEM;
463

464
        *req = (RemoveRequest) {
3,780✔
465
                .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
1,890✔
466
                .userdata = userdata,
467
                .netlink = sd_netlink_ref(netlink),
1,890✔
468
                .message = sd_netlink_message_ref(message),
1,890✔
469
                .netlink_handler = netlink_handler,
470
        };
471

472
        r = ordered_set_ensure_put(&manager->remove_request_queue, &remove_request_hash_ops, req);
1,890✔
473
        if (r < 0)
1,890✔
474
                return r;
475
        assert(r > 0);
1,890✔
476

477
        req->manager = manager;
1,890✔
478
        req->unref_func = unref_func;
1,890✔
479

480
        TAKE_PTR(req);
1,890✔
481
        return 1; /* queued */
1,890✔
482
}
483

484
int manager_process_remove_requests(Manager *manager) {
92,808✔
485
        RemoveRequest *req;
92,808✔
486
        int r;
92,808✔
487

488
        assert(manager);
92,808✔
489

490
        while ((req = ordered_set_first(manager->remove_request_queue))) {
94,698✔
491

492
                /* Do not make the reply callback queue in sd-netlink full. */
493
                if (netlink_get_reply_callback_count(req->netlink) >= REPLY_CALLBACK_COUNT_THRESHOLD)
2,403✔
494
                        return 0;
495

496
                r = netlink_call_async(
1,890✔
497
                                req->netlink, NULL, req->message,
498
                                req->netlink_handler,
499
                                remove_request_destroy_callback,
500
                                req);
501
                if (r < 0) {
1,890✔
502
                        _cleanup_(link_unrefp) Link *link = link_ref(req->link);
×
503

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

506
                        /* First free the request. */
507
                        remove_request_free(req);
×
508

509
                        /* Then, make the link enter the failed state. */
510
                        if (link)
×
511
                                link_enter_failed(link);
×
512

513
                } else {
514
                        /* On success, netlink needs to be unref()ed. Otherwise, the netlink and remove
515
                         * request may not freed on shutting down. */
516
                        req->netlink = sd_netlink_unref(req->netlink);
1,890✔
517
                        ordered_set_remove(manager->remove_request_queue, req);
1,890✔
518
                }
519
        }
520

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

© 2026 Coveralls, Inc