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 "netlink-util.h"
9 #include "parse-util.h"
10 #include "qdisc.h"
11 #include "string-util.h"
12 
controlled_delay_init(QDisc * qdisc)13 static int controlled_delay_init(QDisc *qdisc) {
14         ControlledDelay *cd;
15 
16         assert(qdisc);
17 
18         cd = CODEL(qdisc);
19 
20         cd->ce_threshold_usec = USEC_INFINITY;
21         cd->ecn = -1;
22 
23         return 0;
24 }
25 
controlled_delay_fill_message(Link * link,QDisc * qdisc,sd_netlink_message * req)26 static int controlled_delay_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
27         ControlledDelay *cd;
28         int r;
29 
30         assert(link);
31         assert(qdisc);
32         assert(req);
33 
34         assert_se(cd = CODEL(qdisc));
35 
36         r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "codel");
37         if (r < 0)
38                 return r;
39 
40         if (cd->packet_limit > 0) {
41                 r = sd_netlink_message_append_u32(req, TCA_CODEL_LIMIT, cd->packet_limit);
42                 if (r < 0)
43                         return r;
44         }
45 
46         if (cd->interval_usec > 0) {
47                 r = sd_netlink_message_append_u32(req, TCA_CODEL_INTERVAL, cd->interval_usec);
48                 if (r < 0)
49                         return r;
50         }
51 
52         if (cd->target_usec > 0) {
53                 r = sd_netlink_message_append_u32(req, TCA_CODEL_TARGET, cd->target_usec);
54                 if (r < 0)
55                         return r;
56         }
57 
58         if (cd->ecn >= 0) {
59                 r = sd_netlink_message_append_u32(req, TCA_CODEL_ECN, cd->ecn);
60                 if (r < 0)
61                         return r;
62         }
63 
64         if (cd->ce_threshold_usec != USEC_INFINITY) {
65                 r = sd_netlink_message_append_u32(req, TCA_CODEL_CE_THRESHOLD, cd->ce_threshold_usec);
66                 if (r < 0)
67                         return r;
68         }
69 
70         r = sd_netlink_message_close_container(req);
71         if (r < 0)
72                 return r;
73 
74         return 0;
75 }
76 
config_parse_controlled_delay_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)77 int config_parse_controlled_delay_u32(
78                 const char *unit,
79                 const char *filename,
80                 unsigned line,
81                 const char *section,
82                 unsigned section_line,
83                 const char *lvalue,
84                 int ltype,
85                 const char *rvalue,
86                 void *data,
87                 void *userdata) {
88 
89         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
90         ControlledDelay *cd;
91         Network *network = data;
92         int r;
93 
94         assert(filename);
95         assert(lvalue);
96         assert(rvalue);
97         assert(data);
98 
99         r = qdisc_new_static(QDISC_KIND_CODEL, network, filename, section_line, &qdisc);
100         if (r == -ENOMEM)
101                 return log_oom();
102         if (r < 0) {
103                 log_syntax(unit, LOG_WARNING, filename, line, r,
104                            "More than one kind of queueing discipline, ignoring assignment: %m");
105                 return 0;
106         }
107 
108         cd = CODEL(qdisc);
109 
110         if (isempty(rvalue)) {
111                 cd->packet_limit = 0;
112 
113                 qdisc = NULL;
114                 return 0;
115         }
116 
117         r = safe_atou32(rvalue, &cd->packet_limit);
118         if (r < 0) {
119                 log_syntax(unit, LOG_WARNING, filename, line, r,
120                            "Failed to parse '%s=', ignoring assignment: %s",
121                            lvalue, rvalue);
122                 return 0;
123         }
124 
125         qdisc = NULL;
126 
127         return 0;
128 }
129 
config_parse_controlled_delay_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)130 int config_parse_controlled_delay_usec(
131                 const char *unit,
132                 const char *filename,
133                 unsigned line,
134                 const char *section,
135                 unsigned section_line,
136                 const char *lvalue,
137                 int ltype,
138                 const char *rvalue,
139                 void *data,
140                 void *userdata) {
141 
142         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
143         ControlledDelay *cd;
144         Network *network = data;
145         usec_t *p;
146         int r;
147 
148         assert(filename);
149         assert(lvalue);
150         assert(rvalue);
151         assert(data);
152 
153         r = qdisc_new_static(QDISC_KIND_CODEL, network, filename, section_line, &qdisc);
154         if (r == -ENOMEM)
155                 return log_oom();
156         if (r < 0) {
157                 log_syntax(unit, LOG_WARNING, filename, line, r,
158                            "More than one kind of queueing discipline, ignoring assignment: %m");
159                 return 0;
160         }
161 
162         cd = CODEL(qdisc);
163 
164         if (streq(lvalue, "TargetSec"))
165                 p = &cd->target_usec;
166         else if (streq(lvalue, "IntervalSec"))
167                 p = &cd->interval_usec;
168         else if (streq(lvalue, "CEThresholdSec"))
169                 p = &cd->ce_threshold_usec;
170         else
171                 assert_not_reached();
172 
173         if (isempty(rvalue)) {
174                 if (streq(lvalue, "CEThresholdSec"))
175                         *p = USEC_INFINITY;
176                 else
177                         *p = 0;
178 
179                 qdisc = NULL;
180                 return 0;
181         }
182 
183         r = parse_sec(rvalue, p);
184         if (r < 0) {
185                 log_syntax(unit, LOG_WARNING, filename, line, r,
186                            "Failed to parse '%s=', ignoring assignment: %s",
187                            lvalue, rvalue);
188                 return 0;
189         }
190 
191         qdisc = NULL;
192 
193         return 0;
194 }
195 
config_parse_controlled_delay_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)196 int config_parse_controlled_delay_bool(
197                 const char *unit,
198                 const char *filename,
199                 unsigned line,
200                 const char *section,
201                 unsigned section_line,
202                 const char *lvalue,
203                 int ltype,
204                 const char *rvalue,
205                 void *data,
206                 void *userdata) {
207 
208         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
209         ControlledDelay *cd;
210         Network *network = data;
211         int r;
212 
213         assert(filename);
214         assert(lvalue);
215         assert(rvalue);
216         assert(data);
217 
218         r = qdisc_new_static(QDISC_KIND_CODEL, network, filename, section_line, &qdisc);
219         if (r == -ENOMEM)
220                 return log_oom();
221         if (r < 0) {
222                 log_syntax(unit, LOG_WARNING, filename, line, r,
223                            "More than one kind of queueing discipline, ignoring assignment: %m");
224                 return 0;
225         }
226 
227         cd = CODEL(qdisc);
228 
229         if (isempty(rvalue)) {
230                 cd->ecn = -1;
231 
232                 qdisc = NULL;
233                 return 0;
234         }
235 
236         r = parse_boolean(rvalue);
237         if (r < 0) {
238                 log_syntax(unit, LOG_WARNING, filename, line, r,
239                            "Failed to parse '%s=', ignoring assignment: %s",
240                            lvalue, rvalue);
241                 return 0;
242         }
243 
244         cd->ecn = r;
245         qdisc = NULL;
246 
247         return 0;
248 }
249 
250 const QDiscVTable codel_vtable = {
251         .object_size = sizeof(ControlledDelay),
252         .tca_kind = "codel",
253         .init = controlled_delay_init,
254         .fill_message = controlled_delay_fill_message,
255 };
256