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

systemd / systemd / 13424846548

19 Feb 2025 10:09PM UTC coverage: 71.768% (+0.02%) from 71.753%
13424846548

push

github

web-flow
tree-wide: tweaks to mount point inode creation (#36308)

Some love for make_mount_point_inode_from_xyz() and ports PID 1 over to
it for mount units.

Alternative to #36290

58 of 91 new or added lines in 7 files covered. (63.74%)

1322 existing lines in 48 files now uncovered.

293681 of 409206 relevant lines covered (71.77%)

717000.31 hits per line

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

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

3
#include "netdev.h"
4
#include "netlink-util.h"
5
#include "networkd-link.h"
6
#include "networkd-manager.h"
7
#include "networkd-queue.h"
8
#include "string-table.h"
9

10
#define REPLY_CALLBACK_COUNT_THRESHOLD 128
11

12
static Request* request_detach_impl(Request *req) {
10,135✔
13
        assert(req);
10,135✔
14

15
        if (!req->manager)
10,135✔
16
                return NULL;
17

18
        ordered_set_remove(req->manager->request_queue, req);
4,931✔
19
        req->manager = NULL;
4,931✔
20
        return req;
4,931✔
21
}
22

23
void request_detach(Request *req) {
4,949✔
24
        request_unref(request_detach_impl(req));
4,949✔
25
}
4,949✔
26

27
static Request *request_free(Request *req) {
5,186✔
28
        if (!req)
5,186✔
29
                return NULL;
30

31
        /* To prevent from triggering assertions in the hash and compare functions, remove this request
32
         * from the set before freeing userdata below. */
33
        request_detach_impl(req);
5,186✔
34

35
        if (req->free_func)
5,186✔
36
                req->free_func(req->userdata);
3,577✔
37

38
        if (req->counter)
5,186✔
39
                (*req->counter)--;
146✔
40

41
        link_unref(req->link); /* link may be NULL, but link_unref() can handle it gracefully. */
5,186✔
42

43
        return mfree(req);
5,186✔
44
}
45

46
DEFINE_TRIVIAL_REF_UNREF_FUNC(Request, request, request_free);
136,248✔
47

48
static void request_destroy_callback(Request *req) {
4,031✔
49
        assert(req);
4,031✔
50

51
        request_detach(req);
4,031✔
52
        request_unref(req);
4,031✔
53
}
4,031✔
54

55
static void request_hash_func(const Request *req, struct siphash *state) {
44,644✔
56
        assert(req);
44,644✔
57
        assert(state);
44,644✔
58

59
        siphash24_compress_typesafe(req->type, state);
44,644✔
60

61
        if (!IN_SET(req->type,
44,644✔
62
                    REQUEST_TYPE_NEXTHOP,
63
                    REQUEST_TYPE_ROUTE,
64
                    REQUEST_TYPE_ROUTING_POLICY_RULE)) {
65

66
                siphash24_compress_boolean(req->link, state);
22,996✔
67
                if (req->link)
22,996✔
68
                        siphash24_compress_typesafe(req->link->ifindex, state);
21,827✔
69
        }
70

71
        siphash24_compress_typesafe(req->hash_func, state);
44,644✔
72
        siphash24_compress_typesafe(req->compare_func, state);
44,644✔
73

74
        if (req->hash_func)
44,644✔
75
                req->hash_func(req->userdata, state);
39,815✔
76
}
44,644✔
77

78
static int request_compare_func(const struct Request *a, const struct Request *b) {
20,753✔
79
        int r;
20,753✔
80

81
        assert(a);
20,753✔
82
        assert(b);
20,753✔
83

84
        r = CMP(a->type, b->type);
20,753✔
85
        if (r != 0)
14,945✔
86
                return r;
7,724✔
87

88
        if (!IN_SET(a->type,
13,029✔
89
                    REQUEST_TYPE_NEXTHOP,
90
                    REQUEST_TYPE_ROUTE,
91
                    REQUEST_TYPE_ROUTING_POLICY_RULE)) {
92

93
                r = CMP(!!a->link, !!b->link);
8,126✔
94
                if (r != 0)
8,126✔
95
                        return r;
×
96

97
                if (a->link) {
8,126✔
98
                        r = CMP(a->link->ifindex, b->link->ifindex);
7,509✔
99
                        if (r != 0)
7,403✔
100
                                return r;
206✔
101
                }
102
        }
103

104
        r = CMP(PTR_TO_UINT64(a->hash_func), PTR_TO_UINT64(b->hash_func));
12,823✔
105
        if (r != 0)
12,823✔
106
                return r;
×
107

108
        r = CMP(PTR_TO_UINT64(a->compare_func), PTR_TO_UINT64(b->compare_func));
12,823✔
109
        if (r != 0)
12,823✔
110
                return r;
×
111

112
        if (a->compare_func)
12,823✔
113
                return a->compare_func(a->userdata, b->userdata);
11,493✔
114

115
        return 0;
116
}
117

118
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
4✔
119
                request_hash_ops,
120
                Request,
121
                request_hash_func,
122
                request_compare_func,
123
                request_detach);
124

125
static int request_new(
5,186✔
126
                Manager *manager,
127
                Link *link,
128
                RequestType type,
129
                void *userdata,
130
                mfree_func_t free_func,
131
                hash_func_t hash_func,
132
                compare_func_t compare_func,
133
                request_process_func_t process,
134
                unsigned *counter,
135
                request_netlink_handler_t netlink_handler,
136
                Request **ret) {
137

138
        _cleanup_(request_unrefp) Request *req = NULL;
5,186✔
139
        Request *existing;
5,186✔
140
        int r;
5,186✔
141

142
        assert(manager);
5,186✔
143
        assert(process);
5,186✔
144

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

154
        req = new(Request, 1);
5,186✔
155
        if (!req)
5,186✔
156
                return -ENOMEM;
157

158
        *req = (Request) {
10,372✔
159
                .n_ref = 1,
160
                .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
5,186✔
161
                .type = type,
162
                .userdata = userdata,
163
                .hash_func = hash_func,
164
                .compare_func = compare_func,
165
                .process = process,
166
                .netlink_handler = netlink_handler,
167
        };
168

169
        existing = ordered_set_get(manager->request_queue, req);
5,186✔
170
        if (existing) {
5,186✔
171
                if (ret)
255✔
172
                        *ret = existing;
×
173
                return 0;
255✔
174
        }
175

176
        r = ordered_set_ensure_put(&manager->request_queue, &request_hash_ops, req);
4,931✔
177
        if (r < 0)
4,931✔
178
                return r;
179

180
        req->manager = manager;
4,931✔
181
        req->free_func = free_func;
4,931✔
182
        req->counter = counter;
4,931✔
183
        if (req->counter)
4,931✔
184
                (*req->counter)++;
4,136✔
185

186
        /* If this is called in the ORDERED_SET_FOREACH() loop of manager_process_requests(), we need to
187
         * exit from the loop, due to the limitation of the iteration on OrderedSet. */
188
        manager->request_queued = true;
4,931✔
189

190
        if (ret)
4,931✔
191
                *ret = req;
240✔
192

193
        TAKE_PTR(req);
4,931✔
194
        return 1;
4,931✔
195
}
196

197
int netdev_queue_request(
424✔
198
                NetDev *netdev,
199
                request_process_func_t process,
200
                Request **ret) {
201

202
        int r;
424✔
203

204
        assert(netdev);
424✔
205
        assert(netdev->manager);
424✔
206

207
        r = request_new(netdev->manager, NULL, REQUEST_TYPE_NETDEV_INDEPENDENT,
424✔
208
                        netdev, (mfree_func_t) netdev_unref,
209
                        trivial_hash_func, trivial_compare_func,
210
                        process, NULL, NULL, ret);
211
        if (r <= 0)
424✔
212
                return r;
213

214
        netdev_ref(netdev);
424✔
215
        return 1;
424✔
216
}
217

218
int link_queue_request_full(
4,761✔
219
                Link *link,
220
                RequestType type,
221
                void *userdata,
222
                mfree_func_t free_func,
223
                hash_func_t hash_func,
224
                compare_func_t compare_func,
225
                request_process_func_t process,
226
                unsigned *counter,
227
                request_netlink_handler_t netlink_handler,
228
                Request **ret) {
229

230
        assert(link);
4,761✔
231

232
        return request_new(link->manager, link, type,
4,761✔
233
                           userdata, free_func, hash_func, compare_func,
234
                           process, counter, netlink_handler, ret);
235
}
236

237
int manager_queue_request_full(
1✔
238
                Manager *manager,
239
                RequestType type,
240
                void *userdata,
241
                mfree_func_t free_func,
242
                hash_func_t hash_func,
243
                compare_func_t compare_func,
244
                request_process_func_t process,
245
                unsigned *counter,
246
                request_netlink_handler_t netlink_handler,
247
                Request **ret) {
248

249
        return request_new(manager, NULL, type,
1✔
250
                           userdata, free_func, hash_func, compare_func,
251
                           process, counter, netlink_handler, ret);
252
}
253

254
int link_requeue_request(Link *link, Request *req, void *userdata, Request **ret) {
47✔
255
        assert(link);
47✔
256
        assert(req);
47✔
257

258
        return link_queue_request_full(
47✔
259
                        link,
260
                        req->type,
261
                        userdata,
262
                        req->free_func,
263
                        req->hash_func,
264
                        req->compare_func,
265
                        req->process,
266
                        req->counter,
267
                        req->netlink_handler,
268
                        ret);
269
}
270

271
int manager_process_requests(Manager *manager) {
56,538✔
272
        Request *req;
56,538✔
273
        int r;
56,538✔
274

275
        assert(manager);
56,538✔
276

277
        /* Process only when no remove request is queued. */
278
        if (!ordered_set_isempty(manager->remove_request_queue))
56,538✔
279
                return 0;
56,538✔
280

281
        manager->request_queued = false;
54,660✔
282

283
        ORDERED_SET_FOREACH(req, manager->request_queue) {
320,242✔
284
                if (req->waiting_reply)
266,432✔
285
                        continue; /* Already processed, and waiting for netlink reply. */
204,154✔
286

287
                /* Typically, requests send netlink message asynchronously. If there are many requests
288
                 * queued, then this event may make reply callback queue in sd-netlink full. */
289
                if (netlink_get_reply_callback_count(manager->rtnl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
123,753✔
290
                    netlink_get_reply_callback_count(manager->genl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
122,950✔
291
                    fw_ctx_get_reply_callback_count(manager->fw_ctx) >= REPLY_CALLBACK_COUNT_THRESHOLD)
61,475✔
292
                        break;
293

294
                /* Avoid the request and link freed by req->process() and request_detach(). */
295
                _unused_ _cleanup_(request_unrefp) Request *req_unref = request_ref(req);
122,950✔
296
                _cleanup_(link_unrefp) Link *link = link_ref(req->link);
122,950✔
297

298
                assert(req->process);
61,475✔
299
                r = req->process(req, link, req->userdata);
61,475✔
300
                if (r < 0) {
61,475✔
UNCOV
301
                        request_detach(req);
×
302

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

316
                if (manager->request_queued)
61,475✔
317
                        break; /* New request is queued. Exit from the loop. */
318
        }
319

320
        return 0;
54,660✔
321
}
322

323
static int request_netlink_handler(sd_netlink *nl, sd_netlink_message *m, Request *req) {
4,031✔
324
        assert(req);
4,031✔
325

326
        if (req->counter) {
4,031✔
327
                assert(*req->counter > 0);
3,976✔
328
                (*req->counter)--;
3,976✔
329
                req->counter = NULL; /* To prevent double decrement on free. */
3,976✔
330
        }
331

332
        if (req->link && IN_SET(req->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
4,031✔
333
                return 0;
334

335
        if (req->netlink_handler)
4,025✔
336
                return req->netlink_handler(nl, m, req, req->link, req->userdata);
4,025✔
337

338
        return 0;
339
}
340

341
int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *req) {
4,031✔
342
        int r;
4,031✔
343

344
        assert(nl);
4,031✔
345
        assert(m);
4,031✔
346
        assert(req);
4,031✔
347

348
        r = netlink_call_async(nl, NULL, m, request_netlink_handler, request_destroy_callback, req);
4,031✔
349
        if (r < 0)
4,031✔
350
                return r;
351

352
        request_ref(req);
4,031✔
353
        req->waiting_reply = true;
4,031✔
354
        return 0;
4,031✔
355
}
356

357
static const char *const request_type_table[_REQUEST_TYPE_MAX] = {
358
        [REQUEST_TYPE_ACTIVATE_LINK]                    = "activate link",
359
        [REQUEST_TYPE_ADDRESS]                          = "address",
360
        [REQUEST_TYPE_ADDRESS_LABEL]                    = "address label",
361
        [REQUEST_TYPE_BRIDGE_FDB]                       = "bridge FDB",
362
        [REQUEST_TYPE_BRIDGE_MDB]                       = "bridge MDB",
363
        [REQUEST_TYPE_DHCP_SERVER]                      = "DHCP server",
364
        [REQUEST_TYPE_DHCP4_CLIENT]                     = "DHCPv4 client",
365
        [REQUEST_TYPE_DHCP6_CLIENT]                     = "DHCPv6 client",
366
        [REQUEST_TYPE_IPV6_PROXY_NDP]                   = "IPv6 proxy NDP",
367
        [REQUEST_TYPE_NDISC]                            = "NDisc",
368
        [REQUEST_TYPE_NEIGHBOR]                         = "neighbor",
369
        [REQUEST_TYPE_NETDEV_INDEPENDENT]               = "independent netdev",
370
        [REQUEST_TYPE_NETDEV_STACKED]                   = "stacked netdev",
371
        [REQUEST_TYPE_NEXTHOP]                          = "nexthop",
372
        [REQUEST_TYPE_RADV]                             = "RADV",
373
        [REQUEST_TYPE_ROUTE]                            = "route",
374
        [REQUEST_TYPE_ROUTING_POLICY_RULE]              = "routing policy rule",
375
        [REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE] = "IPv6LL address generation mode",
376
        [REQUEST_TYPE_SET_LINK_BOND]                    = "bond configurations",
377
        [REQUEST_TYPE_SET_LINK_BRIDGE]                  = "bridge configurations",
378
        [REQUEST_TYPE_SET_LINK_BRIDGE_VLAN]             = "bridge VLAN configurations (step 1)",
379
        [REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN]             = "bridge VLAN configurations (step 2)",
380
        [REQUEST_TYPE_SET_LINK_CAN]                     = "CAN interface configurations",
381
        [REQUEST_TYPE_SET_LINK_FLAGS]                   = "link flags",
382
        [REQUEST_TYPE_SET_LINK_GROUP]                   = "interface group",
383
        [REQUEST_TYPE_SET_LINK_IPOIB]                   = "IPoIB configurations",
384
        [REQUEST_TYPE_SET_LINK_MAC]                     = "MAC address",
385
        [REQUEST_TYPE_SET_LINK_MASTER]                  = "master interface",
386
        [REQUEST_TYPE_SET_LINK_MTU]                     = "MTU",
387
        [REQUEST_TYPE_SRIOV]                            = "SR-IOV",
388
        [REQUEST_TYPE_TC_QDISC]                         = "QDisc",
389
        [REQUEST_TYPE_TC_CLASS]                         = "TClass",
390
        [REQUEST_TYPE_UP_DOWN]                          = "bring link up or down",
391
};
392

393
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(request_type, RequestType);
638✔
394

395
static RemoveRequest* remove_request_free(RemoveRequest *req) {
1,709✔
396
        if (!req)
1,709✔
397
                return NULL;
398

399
        if (req->manager)
1,709✔
400
                ordered_set_remove(req->manager->remove_request_queue, req);
1,709✔
401

402
        if (req->unref_func)
1,709✔
403
                req->unref_func(req->userdata);
1,709✔
404

405
        link_unref(req->link);
1,709✔
406
        sd_netlink_unref(req->netlink);
1,709✔
407
        sd_netlink_message_unref(req->message);
1,709✔
408

409
        return mfree(req);
1,709✔
410
}
411

412
DEFINE_TRIVIAL_CLEANUP_FUNC(RemoveRequest*, remove_request_free);
1,709✔
413
DEFINE_TRIVIAL_DESTRUCTOR(remove_request_destroy_callback, RemoveRequest, remove_request_free);
1,709✔
414
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
415
                remove_request_hash_ops,
416
                void,
417
                trivial_hash_func,
418
                trivial_compare_func,
419
                remove_request_free);
420

421
int remove_request_add(
1,709✔
422
                Manager *manager,
423
                Link *link,
424
                void *userdata,
425
                mfree_func_t unref_func,
426
                sd_netlink *netlink,
427
                sd_netlink_message *message,
428
                remove_request_netlink_handler_t netlink_handler) {
429

430
        _cleanup_(remove_request_freep) RemoveRequest *req = NULL;
1,709✔
431
        int r;
1,709✔
432

433
        assert(manager);
1,709✔
434
        assert(userdata);
1,709✔
435
        assert(netlink);
1,709✔
436
        assert(message);
1,709✔
437

438
        /* Unlike request_new(), remove requests will be also processed when the manager is in
439
         * MANAGER_TERMINATING or MANAGER_RESTARTING. When the manager is in MANAGER_STOPPED, we cannot
440
         * queue new remove requests anymore with the same reason explained in request_new(). */
441
        if (manager->state == MANAGER_STOPPED)
1,709✔
442
                return 0; /* ignored */
443

444
        req = new(RemoveRequest, 1);
1,709✔
445
        if (!req)
1,709✔
446
                return -ENOMEM;
447

448
        *req = (RemoveRequest) {
3,418✔
449
                .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
1,709✔
450
                .userdata = userdata,
451
                .netlink = sd_netlink_ref(netlink),
1,709✔
452
                .message = sd_netlink_message_ref(message),
1,709✔
453
                .netlink_handler = netlink_handler,
454
        };
455

456
        r = ordered_set_ensure_put(&manager->remove_request_queue, &remove_request_hash_ops, req);
1,709✔
457
        if (r < 0)
1,709✔
458
                return r;
459
        assert(r > 0);
1,709✔
460

461
        req->manager = manager;
1,709✔
462
        req->unref_func = unref_func;
1,709✔
463

464
        TAKE_PTR(req);
1,709✔
465
        return 1; /* queued */
1,709✔
466
}
467

468
int manager_process_remove_requests(Manager *manager) {
56,973✔
469
        RemoveRequest *req;
56,973✔
470
        int r;
56,973✔
471

472
        assert(manager);
56,973✔
473

474
        while ((req = ordered_set_first(manager->remove_request_queue))) {
58,682✔
475

476
                /* Do not make the reply callback queue in sd-netlink full. */
477
                if (netlink_get_reply_callback_count(req->netlink) >= REPLY_CALLBACK_COUNT_THRESHOLD)
3,587✔
478
                        return 0;
479

480
                r = netlink_call_async(
1,709✔
481
                                req->netlink, NULL, req->message,
482
                                req->netlink_handler,
483
                                remove_request_destroy_callback,
484
                                req);
485
                if (r < 0) {
1,709✔
486
                        _cleanup_(link_unrefp) Link *link = link_ref(req->link);
×
487

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

490
                        /* First free the request. */
491
                        remove_request_free(req);
×
492

493
                        /* Then, make the link enter the failed state. */
494
                        if (link)
×
495
                                link_enter_failed(link);
×
496

497
                } else {
498
                        /* On success, netlink needs to be unref()ed. Otherwise, the netlink and remove
499
                         * request may not freed on shutting down. */
500
                        req->netlink = sd_netlink_unref(req->netlink);
1,709✔
501
                        ordered_set_remove(manager->remove_request_queue, req);
1,709✔
502
                }
503
        }
504

505
        return 0;
506
}
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