Coverage Report

Created: 2026-01-22 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/network/tc/fq.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later
2
 * Copyright © 2019 VMware, Inc. */
3
4
#include <linux/pkt_sched.h>
5
6
#include "sd-netlink.h"
7
8
#include "fq.h"
9
#include "log.h"
10
#include "logarithm.h"
11
#include "parse-util.h"
12
#include "string-util.h"
13
#include "strv.h"
14
15
563
static int fair_queueing_init(QDisc *qdisc) {
16
563
        FairQueueing *fq;
17
18
563
        assert(qdisc);
19
20
563
        fq = FQ(qdisc);
21
22
563
        fq->pacing = -1;
23
563
        fq->ce_threshold_usec = USEC_INFINITY;
24
25
563
        return 0;
26
563
}
27
28
0
static int fair_queueing_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
29
0
        FairQueueing *fq;
30
0
        int r;
31
32
0
        assert(link);
33
0
        assert(qdisc);
34
0
        assert(req);
35
36
0
        assert_se(fq = FQ(qdisc));
37
38
0
        r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "fq");
39
0
        if (r < 0)
40
0
                return r;
41
42
0
        if (fq->packet_limit > 0) {
43
0
                r = sd_netlink_message_append_u32(req, TCA_FQ_PLIMIT, fq->packet_limit);
44
0
                if (r < 0)
45
0
                        return r;
46
0
        }
47
48
0
        if (fq->flow_limit > 0) {
49
0
                r = sd_netlink_message_append_u32(req, TCA_FQ_FLOW_PLIMIT, fq->flow_limit);
50
0
                if (r < 0)
51
0
                        return r;
52
0
        }
53
54
0
        if (fq->quantum > 0) {
55
0
                r = sd_netlink_message_append_u32(req, TCA_FQ_QUANTUM, fq->quantum);
56
0
                if (r < 0)
57
0
                        return r;
58
0
        }
59
60
0
        if (fq->initial_quantum > 0) {
61
0
                r = sd_netlink_message_append_u32(req, TCA_FQ_INITIAL_QUANTUM, fq->initial_quantum);
62
0
                if (r < 0)
63
0
                        return r;
64
0
        }
65
66
0
        if (fq->pacing >= 0) {
67
0
                r = sd_netlink_message_append_u32(req, TCA_FQ_RATE_ENABLE, fq->pacing);
68
0
                if (r < 0)
69
0
                        return r;
70
0
        }
71
72
0
        if (fq->max_rate > 0) {
73
0
                r = sd_netlink_message_append_u32(req, TCA_FQ_FLOW_MAX_RATE, fq->max_rate);
74
0
                if (r < 0)
75
0
                        return r;
76
0
        }
77
78
0
        if (fq->buckets > 0) {
79
0
                uint32_t l;
80
81
0
                l = log2u(fq->buckets);
82
0
                r = sd_netlink_message_append_u32(req, TCA_FQ_BUCKETS_LOG, l);
83
0
                if (r < 0)
84
0
                        return r;
85
0
        }
86
87
0
        if (fq->orphan_mask > 0) {
88
0
                r = sd_netlink_message_append_u32(req, TCA_FQ_ORPHAN_MASK, fq->orphan_mask);
89
0
                if (r < 0)
90
0
                        return r;
91
0
        }
92
93
0
        if (fq->ce_threshold_usec != USEC_INFINITY) {
94
0
                r = sd_netlink_message_append_u32(req, TCA_FQ_CE_THRESHOLD, fq->ce_threshold_usec);
95
0
                if (r < 0)
96
0
                        return r;
97
0
        }
98
99
0
        r = sd_netlink_message_close_container(req);
100
0
        if (r < 0)
101
0
                return r;
102
103
0
        return 0;
104
0
}
105
106
int config_parse_fq_u32(
107
                const char *unit,
108
                const char *filename,
109
                unsigned line,
110
                const char *section,
111
                unsigned section_line,
112
                const char *lvalue,
113
                int ltype,
114
                const char *rvalue,
115
                void *data,
116
1.00k
                void *userdata) {
117
118
1.00k
        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
119
1.00k
        FairQueueing *fq;
120
1.00k
        Network *network = ASSERT_PTR(data);
121
1.00k
        uint32_t *p;
122
1.00k
        int r;
123
124
1.00k
        assert(filename);
125
1.00k
        assert(lvalue);
126
1.00k
        assert(rvalue);
127
128
1.00k
        r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
129
1.00k
        if (r == -ENOMEM)
130
0
                return log_oom();
131
1.00k
        if (r < 0) {
132
0
                log_syntax(unit, LOG_WARNING, filename, line, r,
133
0
                           "More than one kind of queueing discipline, ignoring assignment: %m");
134
0
                return 0;
135
0
        }
136
137
1.00k
        fq = FQ(qdisc);
138
139
1.00k
        if (streq(lvalue, "PacketLimit"))
140
339
                p = &fq->packet_limit;
141
666
        else if (streq(lvalue, "FlowLimit"))
142
195
                p = &fq->flow_limit;
143
471
        else if (streq(lvalue, "Buckets"))
144
277
                p = &fq->buckets;
145
194
        else if (streq(lvalue, "OrphanMask"))
146
194
                p = &fq->orphan_mask;
147
0
        else
148
0
                assert_not_reached();
149
150
1.00k
        if (isempty(rvalue)) {
151
391
                *p = 0;
152
153
391
                qdisc = NULL;
154
391
                return 0;
155
391
        }
156
157
614
        r = safe_atou32(rvalue, p);
158
614
        if (r < 0) {
159
388
                log_syntax(unit, LOG_WARNING, filename, line, r,
160
388
                           "Failed to parse '%s=', ignoring assignment: %s",
161
388
                           lvalue, rvalue);
162
388
                return 0;
163
388
        }
164
165
226
        qdisc = NULL;
166
167
226
        return 0;
168
614
}
169
170
int config_parse_fq_size(
171
                const char *unit,
172
                const char *filename,
173
                unsigned line,
174
                const char *section,
175
                unsigned section_line,
176
                const char *lvalue,
177
                int ltype,
178
                const char *rvalue,
179
                void *data,
180
1.05k
                void *userdata) {
181
182
1.05k
        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
183
1.05k
        FairQueueing *fq;
184
1.05k
        Network *network = ASSERT_PTR(data);
185
1.05k
        uint64_t sz;
186
1.05k
        uint32_t *p;
187
1.05k
        int r;
188
189
1.05k
        assert(filename);
190
1.05k
        assert(lvalue);
191
1.05k
        assert(rvalue);
192
193
1.05k
        r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
194
1.05k
        if (r == -ENOMEM)
195
0
                return log_oom();
196
1.05k
        if (r < 0) {
197
0
                log_syntax(unit, LOG_WARNING, filename, line, r,
198
0
                           "More than one kind of queueing discipline, ignoring assignment: %m");
199
0
                return 0;
200
0
        }
201
202
1.05k
        fq = FQ(qdisc);
203
204
1.05k
        if (STR_IN_SET(lvalue, "QuantumBytes", "Quantum"))
205
864
                p = &fq->quantum;
206
194
        else if (STR_IN_SET(lvalue, "InitialQuantumBytes", "InitialQuantum"))
207
194
                p = &fq->initial_quantum;
208
0
        else
209
0
                assert_not_reached();
210
211
1.05k
        if (isempty(rvalue)) {
212
382
                *p = 0;
213
214
382
                qdisc = NULL;
215
382
                return 0;
216
382
        }
217
218
676
        r = parse_size(rvalue, 1024, &sz);
219
676
        if (r < 0) {
220
203
                log_syntax(unit, LOG_WARNING, filename, line, r,
221
203
                           "Failed to parse '%s=', ignoring assignment: %s",
222
203
                           lvalue, rvalue);
223
203
                return 0;
224
203
        }
225
473
        if (sz > UINT32_MAX) {
226
236
                log_syntax(unit, LOG_WARNING, filename, line, 0,
227
236
                           "Specified '%s=' is too large, ignoring assignment: %s",
228
236
                           lvalue, rvalue);
229
236
                return 0;
230
236
        }
231
232
237
        *p = sz;
233
237
        qdisc = NULL;
234
235
237
        return 0;
236
473
}
237
238
int config_parse_fq_bool(
239
                const char *unit,
240
                const char *filename,
241
                unsigned line,
242
                const char *section,
243
                unsigned section_line,
244
                const char *lvalue,
245
                int ltype,
246
                const char *rvalue,
247
                void *data,
248
417
                void *userdata) {
249
250
417
        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
251
417
        FairQueueing *fq;
252
417
        Network *network = ASSERT_PTR(data);
253
417
        int r;
254
255
417
        assert(filename);
256
417
        assert(lvalue);
257
417
        assert(rvalue);
258
259
417
        r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
260
417
        if (r == -ENOMEM)
261
0
                return log_oom();
262
417
        if (r < 0) {
263
0
                log_syntax(unit, LOG_WARNING, filename, line, r,
264
0
                           "More than one kind of queueing discipline, ignoring assignment: %m");
265
0
                return 0;
266
0
        }
267
268
417
        fq = FQ(qdisc);
269
270
417
        r = parse_tristate(rvalue, &fq->pacing);
271
417
        if (r < 0) {
272
199
                log_syntax(unit, LOG_WARNING, filename, line, r,
273
199
                           "Failed to parse '%s=', ignoring assignment: %s",
274
199
                           lvalue, rvalue);
275
199
                return 0;
276
199
        }
277
278
218
        fq->pacing = r;
279
218
        TAKE_PTR(qdisc);
280
281
218
        return 0;
282
417
}
283
284
int config_parse_fq_usec(
285
                const char *unit,
286
                const char *filename,
287
                unsigned line,
288
                const char *section,
289
                unsigned section_line,
290
                const char *lvalue,
291
                int ltype,
292
                const char *rvalue,
293
                void *data,
294
969
                void *userdata) {
295
296
969
        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
297
969
        FairQueueing *fq;
298
969
        Network *network = ASSERT_PTR(data);
299
969
        usec_t sec;
300
969
        int r;
301
302
969
        assert(filename);
303
969
        assert(lvalue);
304
969
        assert(rvalue);
305
306
969
        r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
307
969
        if (r == -ENOMEM)
308
0
                return log_oom();
309
969
        if (r < 0) {
310
0
                log_syntax(unit, LOG_WARNING, filename, line, r,
311
0
                           "More than one kind of queueing discipline, ignoring assignment: %m");
312
0
                return 0;
313
0
        }
314
315
969
        fq = FQ(qdisc);
316
317
969
        if (isempty(rvalue)) {
318
323
                fq->ce_threshold_usec = USEC_INFINITY;
319
320
323
                qdisc = NULL;
321
323
                return 0;
322
323
        }
323
324
646
        r = parse_sec(rvalue, &sec);
325
646
        if (r < 0) {
326
197
                log_syntax(unit, LOG_WARNING, filename, line, r,
327
197
                           "Failed to parse '%s=', ignoring assignment: %s",
328
197
                           lvalue, rvalue);
329
197
                return 0;
330
197
        }
331
449
        if (sec > UINT32_MAX) {
332
233
                log_syntax(unit, LOG_WARNING, filename, line, 0,
333
233
                           "Specified '%s=' is too large, ignoring assignment: %s",
334
233
                           lvalue, rvalue);
335
233
                return 0;
336
233
        }
337
338
216
        fq->ce_threshold_usec = sec;
339
216
        qdisc = NULL;
340
341
216
        return 0;
342
449
}
343
344
int config_parse_fq_max_rate(
345
                const char *unit,
346
                const char *filename,
347
                unsigned line,
348
                const char *section,
349
                unsigned section_line,
350
                const char *lvalue,
351
                int ltype,
352
                const char *rvalue,
353
                void *data,
354
963
                void *userdata) {
355
356
963
        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
357
963
        FairQueueing *fq;
358
963
        Network *network = ASSERT_PTR(data);
359
963
        uint64_t sz;
360
963
        int r;
361
362
963
        assert(filename);
363
963
        assert(lvalue);
364
963
        assert(rvalue);
365
366
963
        r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
367
963
        if (r == -ENOMEM)
368
0
                return log_oom();
369
963
        if (r < 0) {
370
0
                log_syntax(unit, LOG_WARNING, filename, line, r,
371
0
                           "More than one kind of queueing discipline, ignoring assignment: %m");
372
0
                return 0;
373
0
        }
374
375
963
        fq = FQ(qdisc);
376
377
963
        if (isempty(rvalue)) {
378
319
                fq->max_rate = 0;
379
380
319
                qdisc = NULL;
381
319
                return 0;
382
319
        }
383
384
644
        r = parse_size(rvalue, 1000, &sz);
385
644
        if (r < 0) {
386
201
                log_syntax(unit, LOG_WARNING, filename, line, r,
387
201
                           "Failed to parse '%s=', ignoring assignment: %s",
388
201
                           lvalue, rvalue);
389
201
                return 0;
390
201
        }
391
443
        if (sz / 8 > UINT32_MAX) {
392
235
                log_syntax(unit, LOG_WARNING, filename, line, 0,
393
235
                           "Specified '%s=' is too large, ignoring assignment: %s",
394
235
                           lvalue, rvalue);
395
235
                return 0;
396
235
        }
397
398
208
        fq->max_rate = sz / 8;
399
208
        qdisc = NULL;
400
401
208
        return 0;
402
443
}
403
404
const QDiscVTable fq_vtable = {
405
        .init = fair_queueing_init,
406
        .object_size = sizeof(FairQueueing),
407
        .tca_kind = "fq",
408
        .fill_message = fair_queueing_fill_message,
409
};