]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
d474aa51 YW |
2 | |
3 | #include <linux/pkt_sched.h> | |
4 | ||
5 | #include "alloc-util.h" | |
6 | #include "conf-parser.h" | |
7 | #include "ets.h" | |
3a67b8bb | 8 | #include "extract-word.h" |
d474aa51 YW |
9 | #include "memory-util.h" |
10 | #include "netlink-util.h" | |
11 | #include "parse-util.h" | |
12 | #include "qdisc.h" | |
13 | #include "string-util.h" | |
14 | #include "tc-util.h" | |
15 | ||
16 | static int enhanced_transmission_selection_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { | |
17 | EnhancedTransmissionSelection *ets; | |
18 | int r; | |
19 | ||
20 | assert(link); | |
21 | assert(qdisc); | |
22 | assert(req); | |
23 | ||
16924f54 | 24 | assert_se(ets = ETS(qdisc)); |
d474aa51 YW |
25 | |
26 | r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "ets"); | |
27 | if (r < 0) | |
16924f54 | 28 | return r; |
d474aa51 YW |
29 | |
30 | r = sd_netlink_message_append_u8(req, TCA_ETS_NBANDS, ets->n_bands); | |
31 | if (r < 0) | |
16924f54 | 32 | return r; |
d474aa51 YW |
33 | |
34 | if (ets->n_strict > 0) { | |
35 | r = sd_netlink_message_append_u8(req, TCA_ETS_NSTRICT, ets->n_strict); | |
36 | if (r < 0) | |
16924f54 | 37 | return r; |
d474aa51 YW |
38 | } |
39 | ||
40 | if (ets->n_quanta > 0) { | |
41 | r = sd_netlink_message_open_container(req, TCA_ETS_QUANTA); | |
42 | if (r < 0) | |
16924f54 | 43 | return r; |
d474aa51 YW |
44 | |
45 | for (unsigned i = 0; i < ets->n_quanta; i++) { | |
46 | r = sd_netlink_message_append_u32(req, TCA_ETS_QUANTA_BAND, ets->quanta[i]); | |
47 | if (r < 0) | |
16924f54 | 48 | return r; |
d474aa51 YW |
49 | } |
50 | ||
51 | r = sd_netlink_message_close_container(req); | |
52 | if (r < 0) | |
16924f54 | 53 | return r; |
d474aa51 YW |
54 | } |
55 | ||
56 | if (ets->n_prio > 0) { | |
57 | r = sd_netlink_message_open_container(req, TCA_ETS_PRIOMAP); | |
58 | if (r < 0) | |
16924f54 | 59 | return r; |
d474aa51 YW |
60 | |
61 | for (unsigned i = 0; i < ets->n_prio; i++) { | |
62 | r = sd_netlink_message_append_u8(req, TCA_ETS_PRIOMAP_BAND, ets->prio[i]); | |
63 | if (r < 0) | |
16924f54 | 64 | return r; |
d474aa51 YW |
65 | } |
66 | ||
67 | r = sd_netlink_message_close_container(req); | |
68 | if (r < 0) | |
16924f54 | 69 | return r; |
d474aa51 YW |
70 | } |
71 | ||
72 | r = sd_netlink_message_close_container(req); | |
73 | if (r < 0) | |
16924f54 | 74 | return r; |
d474aa51 YW |
75 | |
76 | return 0; | |
77 | } | |
78 | ||
79 | int config_parse_ets_u8( | |
80 | const char *unit, | |
81 | const char *filename, | |
82 | unsigned line, | |
83 | const char *section, | |
84 | unsigned section_line, | |
85 | const char *lvalue, | |
86 | int ltype, | |
87 | const char *rvalue, | |
88 | void *data, | |
89 | void *userdata) { | |
90 | ||
91 | _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; | |
92 | EnhancedTransmissionSelection *ets; | |
99534007 | 93 | Network *network = ASSERT_PTR(data); |
d474aa51 YW |
94 | uint8_t v, *p; |
95 | int r; | |
96 | ||
97 | assert(filename); | |
98 | assert(lvalue); | |
99 | assert(rvalue); | |
d474aa51 YW |
100 | |
101 | r = qdisc_new_static(QDISC_KIND_ETS, network, filename, section_line, &qdisc); | |
102 | if (r == -ENOMEM) | |
103 | return log_oom(); | |
d96edb2c YW |
104 | if (r < 0) { |
105 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
106 | "More than one kind of queueing discipline, ignoring assignment: %m"); | |
107 | return 0; | |
108 | } | |
d474aa51 YW |
109 | |
110 | ets = ETS(qdisc); | |
111 | if (streq(lvalue, "Bands")) | |
112 | p = &ets->n_bands; | |
113 | else if (streq(lvalue, "StrictBands")) | |
114 | p = &ets->n_strict; | |
115 | else | |
04499a70 | 116 | assert_not_reached(); |
d474aa51 YW |
117 | |
118 | if (isempty(rvalue)) { | |
119 | *p = 0; | |
120 | ||
121 | qdisc = NULL; | |
122 | return 0; | |
123 | } | |
124 | ||
125 | r = safe_atou8(rvalue, &v); | |
126 | if (r < 0) { | |
d96edb2c | 127 | log_syntax(unit, LOG_WARNING, filename, line, r, |
d474aa51 YW |
128 | "Failed to parse '%s=', ignoring assignment: %s", |
129 | lvalue, rvalue); | |
130 | return 0; | |
131 | } | |
132 | if (v > TCQ_ETS_MAX_BANDS) { | |
d96edb2c | 133 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
d474aa51 YW |
134 | "Invalid '%s='. The value must be <= %d, ignoring assignment: %s", |
135 | lvalue, TCQ_ETS_MAX_BANDS, rvalue); | |
136 | return 0; | |
137 | } | |
138 | ||
139 | *p = v; | |
140 | qdisc = NULL; | |
141 | ||
142 | return 0; | |
143 | } | |
144 | ||
145 | int config_parse_ets_quanta( | |
146 | const char *unit, | |
147 | const char *filename, | |
148 | unsigned line, | |
149 | const char *section, | |
150 | unsigned section_line, | |
151 | const char *lvalue, | |
152 | int ltype, | |
153 | const char *rvalue, | |
154 | void *data, | |
155 | void *userdata) { | |
156 | ||
157 | _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; | |
158 | EnhancedTransmissionSelection *ets; | |
99534007 | 159 | Network *network = ASSERT_PTR(data); |
d474aa51 YW |
160 | int r; |
161 | ||
162 | assert(filename); | |
163 | assert(lvalue); | |
164 | assert(rvalue); | |
d474aa51 YW |
165 | |
166 | r = qdisc_new_static(QDISC_KIND_ETS, network, filename, section_line, &qdisc); | |
167 | if (r == -ENOMEM) | |
168 | return log_oom(); | |
d96edb2c YW |
169 | if (r < 0) { |
170 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
171 | "More than one kind of queueing discipline, ignoring assignment: %m"); | |
172 | return 0; | |
173 | } | |
d474aa51 YW |
174 | |
175 | ets = ETS(qdisc); | |
176 | ||
177 | if (isempty(rvalue)) { | |
178 | memzero(ets->quanta, sizeof(uint32_t) * TCQ_ETS_MAX_BANDS); | |
179 | ets->n_quanta = 0; | |
180 | ||
181 | qdisc = NULL; | |
182 | return 0; | |
183 | } | |
184 | ||
185 | for (const char *p = rvalue;;) { | |
186 | _cleanup_free_ char *word = NULL; | |
187 | uint64_t v; | |
188 | ||
189 | r = extract_first_word(&p, &word, NULL, 0); | |
190 | if (r == -ENOMEM) | |
191 | return log_oom(); | |
192 | if (r < 0) { | |
d96edb2c | 193 | log_syntax(unit, LOG_WARNING, filename, line, r, |
d474aa51 | 194 | "Failed to extract next value, ignoring: %m"); |
d96edb2c | 195 | break; |
d474aa51 YW |
196 | } |
197 | if (r == 0) | |
198 | break; | |
199 | ||
200 | r = parse_size(word, 1024, &v); | |
201 | if (r < 0) { | |
d96edb2c | 202 | log_syntax(unit, LOG_WARNING, filename, line, r, |
d474aa51 YW |
203 | "Failed to parse '%s=', ignoring assignment: %s", |
204 | lvalue, word); | |
205 | continue; | |
206 | } | |
207 | if (v == 0 || v > UINT32_MAX) { | |
d96edb2c | 208 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
d474aa51 YW |
209 | "Invalid '%s=', ignoring assignment: %s", |
210 | lvalue, word); | |
211 | continue; | |
212 | } | |
213 | if (ets->n_quanta >= TCQ_ETS_MAX_BANDS) { | |
d96edb2c | 214 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
d474aa51 YW |
215 | "Too many quanta in '%s=', ignoring assignment: %s", |
216 | lvalue, word); | |
217 | continue; | |
218 | } | |
219 | ||
220 | ets->quanta[ets->n_quanta++] = v; | |
221 | } | |
222 | ||
223 | qdisc = NULL; | |
224 | ||
225 | return 0; | |
226 | } | |
227 | ||
228 | int config_parse_ets_prio( | |
229 | const char *unit, | |
230 | const char *filename, | |
231 | unsigned line, | |
232 | const char *section, | |
233 | unsigned section_line, | |
234 | const char *lvalue, | |
235 | int ltype, | |
236 | const char *rvalue, | |
237 | void *data, | |
238 | void *userdata) { | |
239 | ||
240 | _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; | |
241 | EnhancedTransmissionSelection *ets; | |
99534007 | 242 | Network *network = ASSERT_PTR(data); |
d474aa51 YW |
243 | int r; |
244 | ||
245 | assert(filename); | |
246 | assert(lvalue); | |
247 | assert(rvalue); | |
d474aa51 YW |
248 | |
249 | r = qdisc_new_static(QDISC_KIND_ETS, network, filename, section_line, &qdisc); | |
250 | if (r == -ENOMEM) | |
251 | return log_oom(); | |
d96edb2c YW |
252 | if (r < 0) { |
253 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
254 | "More than one kind of queueing discipline, ignoring assignment: %m"); | |
255 | return 0; | |
256 | } | |
d474aa51 YW |
257 | |
258 | ets = ETS(qdisc); | |
259 | ||
260 | if (isempty(rvalue)) { | |
261 | memzero(ets->prio, sizeof(uint8_t) * (TC_PRIO_MAX + 1)); | |
262 | ets->n_prio = 0; | |
263 | ||
264 | qdisc = NULL; | |
265 | return 0; | |
266 | } | |
267 | ||
268 | for (const char *p = rvalue;;) { | |
269 | _cleanup_free_ char *word = NULL; | |
270 | uint8_t v; | |
271 | ||
272 | r = extract_first_word(&p, &word, NULL, 0); | |
273 | if (r == -ENOMEM) | |
274 | return log_oom(); | |
275 | if (r < 0) { | |
d96edb2c | 276 | log_syntax(unit, LOG_WARNING, filename, line, r, |
d474aa51 | 277 | "Failed to extract next value, ignoring: %m"); |
d96edb2c | 278 | break; |
d474aa51 YW |
279 | } |
280 | if (r == 0) | |
281 | break; | |
282 | ||
283 | r = safe_atou8(word, &v); | |
284 | if (r < 0) { | |
d96edb2c | 285 | log_syntax(unit, LOG_WARNING, filename, line, r, |
d474aa51 YW |
286 | "Failed to parse '%s=', ignoring assignment: %s", |
287 | lvalue, word); | |
288 | continue; | |
289 | } | |
e26538dd | 290 | if (ets->n_prio > TC_PRIO_MAX) { |
d96edb2c | 291 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
d474aa51 YW |
292 | "Too many priomap in '%s=', ignoring assignment: %s", |
293 | lvalue, word); | |
294 | continue; | |
295 | } | |
296 | ||
297 | ets->prio[ets->n_prio++] = v; | |
298 | } | |
299 | ||
300 | qdisc = NULL; | |
301 | ||
302 | return 0; | |
303 | } | |
304 | ||
305 | static int enhanced_transmission_selection_verify(QDisc *qdisc) { | |
306 | EnhancedTransmissionSelection *ets; | |
307 | ||
308 | assert(qdisc); | |
309 | ||
310 | ets = ETS(qdisc); | |
311 | ||
312 | if (ets->n_bands == 0) | |
313 | ets->n_bands = ets->n_strict + ets->n_quanta; | |
314 | ||
315 | if (ets->n_bands == 0) | |
316 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
317 | "%s: At least one of Band=, Strict=, or Quanta= must be specified. " | |
318 | "Ignoring [EnhancedTransmissionSelection] section from line %u.", | |
319 | qdisc->section->filename, qdisc->section->line); | |
320 | ||
321 | if (ets->n_bands < ets->n_strict + ets->n_quanta) | |
322 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
323 | "%s: Not enough total bands to cover all the strict bands and quanta. " | |
324 | "Ignoring [EnhancedTransmissionSelection] section from line %u.", | |
325 | qdisc->section->filename, qdisc->section->line); | |
326 | ||
327 | for (unsigned i = 0; i < ets->n_prio; i++) | |
328 | if (ets->prio[i] >= ets->n_bands) | |
329 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
330 | "%s: PriorityMap= element is out of bands. " | |
331 | "Ignoring [EnhancedTransmissionSelection] section from line %u.", | |
332 | qdisc->section->filename, qdisc->section->line); | |
333 | ||
334 | return 0; | |
335 | } | |
336 | ||
337 | const QDiscVTable ets_vtable = { | |
338 | .object_size = sizeof(EnhancedTransmissionSelection), | |
339 | .tca_kind = "ets", | |
340 | .fill_message = enhanced_transmission_selection_fill_message, | |
341 | .verify = enhanced_transmission_selection_verify, | |
342 | }; |