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