]> git.proxmox.com Git - mirror_iproute2.git/blob - dcb/dcb_ets.c
Merge branch 'main' into next
[mirror_iproute2.git] / dcb / dcb_ets.c
1 // SPDX-License-Identifier: GPL-2.0+
2
3 #include <errno.h>
4 #include <stdio.h>
5 #include <linux/dcbnl.h>
6
7 #include "dcb.h"
8 #include "utils.h"
9
10 static void dcb_ets_help_set(void)
11 {
12 fprintf(stderr,
13 "Usage: dcb ets set dev STRING\n"
14 " [ willing { on | off } ]\n"
15 " [ { tc-tsa | reco-tc-tsa } TSA-MAP ]\n"
16 " [ { pg-bw | tc-bw | reco-tc-bw } BW-MAP ]\n"
17 " [ { prio-tc | reco-prio-tc } PRIO-MAP ]\n"
18 "\n"
19 " where TSA-MAP := [ TSA-MAP ] TSA-MAPPING\n"
20 " TSA-MAPPING := { all | TC }:{ strict | cbs | ets | vendor }\n"
21 " BW-MAP := [ BW-MAP ] BW-MAPPING\n"
22 " BW-MAPPING := { all | TC }:INTEGER\n"
23 " PRIO-MAP := [ PRIO-MAP ] PRIO-MAPPING\n"
24 " PRIO-MAPPING := { all | PRIO }:TC\n"
25 " TC := { 0 .. 7 }\n"
26 " PRIO := { 0 .. 7 }\n"
27 "\n"
28 );
29 }
30
31 static void dcb_ets_help_show(void)
32 {
33 fprintf(stderr,
34 "Usage: dcb ets show dev STRING\n"
35 " [ willing ] [ ets-cap ] [ cbs ] [ tc-tsa ]\n"
36 " [ reco-tc-tsa ] [ pg-bw ] [ tc-bw ] [ reco-tc-bw ]\n"
37 " [ prio-tc ] [ reco-prio-tc ]\n"
38 "\n"
39 );
40 }
41
42 static void dcb_ets_help(void)
43 {
44 fprintf(stderr,
45 "Usage: dcb ets help\n"
46 "\n"
47 );
48 dcb_ets_help_show();
49 dcb_ets_help_set();
50 }
51
52 static const char *const tsa_names[] = {
53 [IEEE_8021QAZ_TSA_STRICT] = "strict",
54 [IEEE_8021QAZ_TSA_CB_SHAPER] = "cbs",
55 [IEEE_8021QAZ_TSA_ETS] = "ets",
56 [IEEE_8021QAZ_TSA_VENDOR] = "vendor",
57 };
58
59 static int dcb_ets_parse_mapping_tc_tsa(__u32 key, char *value, void *data)
60 {
61 __u8 tsa;
62 int ret;
63
64 tsa = parse_one_of("TSA", value, tsa_names, ARRAY_SIZE(tsa_names), &ret);
65 if (ret)
66 return ret;
67
68 return dcb_parse_mapping("TC", key, IEEE_8021QAZ_MAX_TCS - 1,
69 "TSA", tsa, -1U,
70 dcb_set_u8, data);
71 }
72
73 static int dcb_ets_parse_mapping_tc_bw(__u32 key, char *value, void *data)
74 {
75 __u8 bw;
76
77 if (get_u8(&bw, value, 0))
78 return -EINVAL;
79
80 return dcb_parse_mapping("TC", key, IEEE_8021QAZ_MAX_TCS - 1,
81 "BW", bw, 100,
82 dcb_set_u8, data);
83 }
84
85 static int dcb_ets_parse_mapping_prio_tc(unsigned int key, char *value, void *data)
86 {
87 __u8 tc;
88
89 if (get_u8(&tc, value, 0))
90 return -EINVAL;
91
92 return dcb_parse_mapping("PRIO", key, IEEE_8021QAZ_MAX_TCS - 1,
93 "TC", tc, IEEE_8021QAZ_MAX_TCS - 1,
94 dcb_set_u8, data);
95 }
96
97 static void dcb_print_array_tsa(const __u8 *array, size_t size)
98 {
99 dcb_print_array_kw(array, size, tsa_names, ARRAY_SIZE(tsa_names));
100 }
101
102 static void dcb_ets_print_willing(const struct ieee_ets *ets)
103 {
104 print_on_off(PRINT_ANY, "willing", "willing %s ", ets->willing);
105 }
106
107 static void dcb_ets_print_ets_cap(const struct ieee_ets *ets)
108 {
109 print_uint(PRINT_ANY, "ets_cap", "ets-cap %d ", ets->ets_cap);
110 }
111
112 static void dcb_ets_print_cbs(const struct ieee_ets *ets)
113 {
114 print_on_off(PRINT_ANY, "cbs", "cbs %s ", ets->cbs);
115 }
116
117 static void dcb_ets_print_tc_bw(const struct ieee_ets *ets)
118 {
119 dcb_print_named_array("tc_bw", "tc-bw",
120 ets->tc_tx_bw, ARRAY_SIZE(ets->tc_tx_bw),
121 dcb_print_array_u8);
122 }
123
124 static void dcb_ets_print_pg_bw(const struct ieee_ets *ets)
125 {
126 dcb_print_named_array("pg_bw", "pg-bw",
127 ets->tc_rx_bw, ARRAY_SIZE(ets->tc_rx_bw),
128 dcb_print_array_u8);
129 }
130
131 static void dcb_ets_print_tc_tsa(const struct ieee_ets *ets)
132 {
133 dcb_print_named_array("tc_tsa", "tc-tsa",
134 ets->tc_tsa, ARRAY_SIZE(ets->tc_tsa),
135 dcb_print_array_tsa);
136 }
137
138 static void dcb_ets_print_prio_tc(const struct ieee_ets *ets)
139 {
140 dcb_print_named_array("prio_tc", "prio-tc",
141 ets->prio_tc, ARRAY_SIZE(ets->prio_tc),
142 dcb_print_array_u8);
143 }
144
145 static void dcb_ets_print_reco_tc_bw(const struct ieee_ets *ets)
146 {
147 dcb_print_named_array("reco_tc_bw", "reco-tc-bw",
148 ets->tc_reco_bw, ARRAY_SIZE(ets->tc_reco_bw),
149 dcb_print_array_u8);
150 }
151
152 static void dcb_ets_print_reco_tc_tsa(const struct ieee_ets *ets)
153 {
154 dcb_print_named_array("reco_tc_tsa", "reco-tc-tsa",
155 ets->tc_reco_tsa, ARRAY_SIZE(ets->tc_reco_tsa),
156 dcb_print_array_tsa);
157 }
158
159 static void dcb_ets_print_reco_prio_tc(const struct ieee_ets *ets)
160 {
161 dcb_print_named_array("reco_prio_tc", "reco-prio-tc",
162 ets->reco_prio_tc, ARRAY_SIZE(ets->reco_prio_tc),
163 dcb_print_array_u8);
164 }
165
166 static void dcb_ets_print(const struct ieee_ets *ets)
167 {
168 dcb_ets_print_willing(ets);
169 dcb_ets_print_ets_cap(ets);
170 dcb_ets_print_cbs(ets);
171 print_nl();
172
173 dcb_ets_print_tc_bw(ets);
174 print_nl();
175
176 dcb_ets_print_pg_bw(ets);
177 print_nl();
178
179 dcb_ets_print_tc_tsa(ets);
180 print_nl();
181
182 dcb_ets_print_prio_tc(ets);
183 print_nl();
184
185 dcb_ets_print_reco_tc_bw(ets);
186 print_nl();
187
188 dcb_ets_print_reco_tc_tsa(ets);
189 print_nl();
190
191 dcb_ets_print_reco_prio_tc(ets);
192 print_nl();
193 }
194
195 static int dcb_ets_get(struct dcb *dcb, const char *dev, struct ieee_ets *ets)
196 {
197 return dcb_get_attribute(dcb, dev, DCB_ATTR_IEEE_ETS, ets, sizeof(*ets));
198 }
199
200 static int dcb_ets_validate_bw(const __u8 bw[], const __u8 tsa[], const char *what)
201 {
202 bool has_ets = false;
203 unsigned int total = 0;
204 unsigned int tc;
205
206 for (tc = 0; tc < IEEE_8021QAZ_MAX_TCS; tc++) {
207 if (tsa[tc] == IEEE_8021QAZ_TSA_ETS) {
208 has_ets = true;
209 break;
210 }
211 }
212
213 /* TC bandwidth is only intended for ETS, but 802.1Q-2018 only requires
214 * that the sum be 100, and individual entries 0..100. It explicitly
215 * notes that non-ETS TCs can have non-0 TC bandwidth during
216 * reconfiguration.
217 */
218 for (tc = 0; tc < IEEE_8021QAZ_MAX_TCS; tc++) {
219 if (bw[tc] > 100) {
220 fprintf(stderr, "%d%% for TC %d of %s is not a valid bandwidth percentage, expected 0..100%%\n",
221 bw[tc], tc, what);
222 return -EINVAL;
223 }
224 total += bw[tc];
225 }
226
227 /* This is what 802.1Q-2018 requires. */
228 if (total == 100)
229 return 0;
230
231 /* But this requirement does not make sense for all-strict
232 * configurations. Anything else than 0 does not make sense: either BW
233 * has not been reconfigured for the all-strict allocation yet, at which
234 * point we expect sum of 100. Or it has already been reconfigured, at
235 * which point accept 0.
236 */
237 if (!has_ets && total == 0)
238 return 0;
239
240 fprintf(stderr, "Bandwidth percentages in %s sum to %d%%, expected %d%%\n",
241 what, total, has_ets ? 100 : 0);
242 return -EINVAL;
243 }
244
245 static int dcb_ets_set(struct dcb *dcb, const char *dev, const struct ieee_ets *ets)
246 {
247 /* Do not validate pg-bw, which is not standard and has unclear
248 * meaning.
249 */
250 if (dcb_ets_validate_bw(ets->tc_tx_bw, ets->tc_tsa, "tc-bw") ||
251 dcb_ets_validate_bw(ets->tc_reco_bw, ets->tc_reco_tsa, "reco-tc-bw"))
252 return -EINVAL;
253
254 return dcb_set_attribute(dcb, dev, DCB_ATTR_IEEE_ETS, ets, sizeof(*ets));
255 }
256
257 static int dcb_cmd_ets_set(struct dcb *dcb, const char *dev, int argc, char **argv)
258 {
259 struct ieee_ets ets;
260 int ret;
261
262 if (!argc) {
263 dcb_ets_help_set();
264 return 1;
265 }
266
267 ret = dcb_ets_get(dcb, dev, &ets);
268 if (ret)
269 return ret;
270
271 do {
272 if (matches(*argv, "help") == 0) {
273 dcb_ets_help_set();
274 return 0;
275 } else if (matches(*argv, "willing") == 0) {
276 NEXT_ARG();
277 ets.willing = parse_on_off("willing", *argv, &ret);
278 if (ret)
279 return ret;
280 } else if (matches(*argv, "tc-tsa") == 0) {
281 NEXT_ARG();
282 ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_tc_tsa,
283 ets.tc_tsa);
284 if (ret) {
285 fprintf(stderr, "Invalid tc-tsa mapping %s\n", *argv);
286 return ret;
287 }
288 continue;
289 } else if (matches(*argv, "reco-tc-tsa") == 0) {
290 NEXT_ARG();
291 ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_tc_tsa,
292 ets.tc_reco_tsa);
293 if (ret) {
294 fprintf(stderr, "Invalid reco-tc-tsa mapping %s\n", *argv);
295 return ret;
296 }
297 continue;
298 } else if (matches(*argv, "tc-bw") == 0) {
299 NEXT_ARG();
300 ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_tc_bw,
301 ets.tc_tx_bw);
302 if (ret) {
303 fprintf(stderr, "Invalid tc-bw mapping %s\n", *argv);
304 return ret;
305 }
306 continue;
307 } else if (matches(*argv, "pg-bw") == 0) {
308 NEXT_ARG();
309 ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_tc_bw,
310 ets.tc_rx_bw);
311 if (ret) {
312 fprintf(stderr, "Invalid pg-bw mapping %s\n", *argv);
313 return ret;
314 }
315 continue;
316 } else if (matches(*argv, "reco-tc-bw") == 0) {
317 NEXT_ARG();
318 ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_tc_bw,
319 ets.tc_reco_bw);
320 if (ret) {
321 fprintf(stderr, "Invalid reco-tc-bw mapping %s\n", *argv);
322 return ret;
323 }
324 continue;
325 } else if (matches(*argv, "prio-tc") == 0) {
326 NEXT_ARG();
327 ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_prio_tc,
328 ets.prio_tc);
329 if (ret) {
330 fprintf(stderr, "Invalid prio-tc mapping %s\n", *argv);
331 return ret;
332 }
333 continue;
334 } else if (matches(*argv, "reco-prio-tc") == 0) {
335 NEXT_ARG();
336 ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_prio_tc,
337 ets.reco_prio_tc);
338 if (ret) {
339 fprintf(stderr, "Invalid reco-prio-tc mapping %s\n", *argv);
340 return ret;
341 }
342 continue;
343 } else {
344 fprintf(stderr, "What is \"%s\"?\n", *argv);
345 dcb_ets_help_set();
346 return -EINVAL;
347 }
348
349 NEXT_ARG_FWD();
350 } while (argc > 0);
351
352 return dcb_ets_set(dcb, dev, &ets);
353 }
354
355 static int dcb_cmd_ets_show(struct dcb *dcb, const char *dev, int argc, char **argv)
356 {
357 struct ieee_ets ets;
358 int ret;
359
360 ret = dcb_ets_get(dcb, dev, &ets);
361 if (ret)
362 return ret;
363
364 open_json_object(NULL);
365
366 if (!argc) {
367 dcb_ets_print(&ets);
368 goto out;
369 }
370
371 do {
372 if (matches(*argv, "help") == 0) {
373 dcb_ets_help_show();
374 return 0;
375 } else if (matches(*argv, "willing") == 0) {
376 dcb_ets_print_willing(&ets);
377 print_nl();
378 } else if (matches(*argv, "ets-cap") == 0) {
379 dcb_ets_print_ets_cap(&ets);
380 print_nl();
381 } else if (matches(*argv, "cbs") == 0) {
382 dcb_ets_print_cbs(&ets);
383 print_nl();
384 } else if (matches(*argv, "tc-tsa") == 0) {
385 dcb_ets_print_tc_tsa(&ets);
386 print_nl();
387 } else if (matches(*argv, "reco-tc-tsa") == 0) {
388 dcb_ets_print_reco_tc_tsa(&ets);
389 print_nl();
390 } else if (matches(*argv, "tc-bw") == 0) {
391 dcb_ets_print_tc_bw(&ets);
392 print_nl();
393 } else if (matches(*argv, "pg-bw") == 0) {
394 dcb_ets_print_pg_bw(&ets);
395 print_nl();
396 } else if (matches(*argv, "reco-tc-bw") == 0) {
397 dcb_ets_print_reco_tc_bw(&ets);
398 print_nl();
399 } else if (matches(*argv, "prio-tc") == 0) {
400 dcb_ets_print_prio_tc(&ets);
401 print_nl();
402 } else if (matches(*argv, "reco-prio-tc") == 0) {
403 dcb_ets_print_reco_prio_tc(&ets);
404 print_nl();
405 } else {
406 fprintf(stderr, "What is \"%s\"?\n", *argv);
407 dcb_ets_help_show();
408 return -EINVAL;
409 }
410
411 NEXT_ARG_FWD();
412 } while (argc > 0);
413
414 out:
415 close_json_object();
416 return 0;
417 }
418
419 int dcb_cmd_ets(struct dcb *dcb, int argc, char **argv)
420 {
421 if (!argc || matches(*argv, "help") == 0) {
422 dcb_ets_help();
423 return 0;
424 } else if (matches(*argv, "show") == 0) {
425 NEXT_ARG_FWD();
426 return dcb_cmd_parse_dev(dcb, argc, argv, dcb_cmd_ets_show, dcb_ets_help_show);
427 } else if (matches(*argv, "set") == 0) {
428 NEXT_ARG_FWD();
429 return dcb_cmd_parse_dev(dcb, argc, argv, dcb_cmd_ets_set, dcb_ets_help_set);
430 } else {
431 fprintf(stderr, "What is \"%s\"?\n", *argv);
432 dcb_ets_help();
433 return -EINVAL;
434 }
435 }