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