/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 | | }; |