]> git.proxmox.com Git - mirror_iproute2.git/blame - tc/q_taprio.c
treewide: refactor help messages
[mirror_iproute2.git] / tc / q_taprio.c
CommitLineData
0dd16449
VCG
1/*
2 * q_taprio.c Time Aware Priority Scheduler
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Vinicius Costa Gomes <vinicius.gomes@intel.com>
10 * Jesus Sanchez-Palencia <jesus.sanchez-palencia@intel.com>
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <syslog.h>
17#include <fcntl.h>
18#include <inttypes.h>
19#include <sys/socket.h>
20#include <netinet/in.h>
21#include <arpa/inet.h>
22#include <string.h>
23
24#include "utils.h"
25#include "tc_util.h"
26#include "list.h"
27
28struct sched_entry {
29 struct list_head list;
30 uint32_t index;
31 uint32_t interval;
32 uint32_t gatemask;
33 uint8_t cmd;
34};
35
36#define CLOCKID_INVALID (-1)
37static const struct static_clockid {
38 const char *name;
39 clockid_t clockid;
40} clockids_sysv[] = {
41 { "REALTIME", CLOCK_REALTIME },
42 { "TAI", CLOCK_TAI },
43 { "BOOTTIME", CLOCK_BOOTTIME },
44 { "MONOTONIC", CLOCK_MONOTONIC },
45 { NULL }
46};
47
48static void explain(void)
49{
8589eb4e
MC
50 fprintf(stderr,
51 "Usage: ... taprio clockid CLOCKID\n"
52 " [num_tc NUMBER] [map P0 P1 ...] "
53 " [queues COUNT@OFFSET COUNT@OFFSET COUNT@OFFSET ...] "
54 " [ [sched-entry index cmd gate-mask interval] ... ] "
55 " [base-time time] "
56 "\n"
57 "CLOCKID must be a valid SYS-V id (i.e. CLOCK_TAI)\n");
0dd16449
VCG
58}
59
60static void explain_clockid(const char *val)
61{
62 fprintf(stderr, "taprio: illegal value for \"clockid\": \"%s\".\n", val);
63 fprintf(stderr, "It must be a valid SYS-V id (i.e. CLOCK_TAI)\n");
64}
65
66static int get_clockid(__s32 *val, const char *arg)
67{
68 const struct static_clockid *c;
69
70 /* Drop the CLOCK_ prefix if that is being used. */
71 if (strcasestr(arg, "CLOCK_") != NULL)
72 arg += sizeof("CLOCK_") - 1;
73
74 for (c = clockids_sysv; c->name; c++) {
75 if (strcasecmp(c->name, arg) == 0) {
76 *val = c->clockid;
77
78 return 0;
79 }
80 }
81
82 return -1;
83}
84
85static const char* get_clock_name(clockid_t clockid)
86{
87 const struct static_clockid *c;
88
89 for (c = clockids_sysv; c->name; c++) {
90 if (clockid == c->clockid)
91 return c->name;
92 }
93
94 return "invalid";
95}
96
97static const char *entry_cmd_to_str(__u8 cmd)
98{
99 switch (cmd) {
100 case TC_TAPRIO_CMD_SET_GATES:
101 return "S";
102 default:
103 return "Invalid";
104 }
105}
106
107static int str_to_entry_cmd(const char *str)
108{
109 if (strcmp(str, "S") == 0)
110 return TC_TAPRIO_CMD_SET_GATES;
111
112 return -1;
113}
114
115static int add_sched_list(struct list_head *sched_entries, struct nlmsghdr *n)
116{
117 struct sched_entry *e;
118
119 list_for_each_entry(e, sched_entries, list) {
120 struct rtattr *a;
121
122 a = addattr_nest(n, 1024, TCA_TAPRIO_SCHED_ENTRY);
123
124 addattr_l(n, 1024, TCA_TAPRIO_SCHED_ENTRY_CMD, &e->cmd, sizeof(e->cmd));
125 addattr_l(n, 1024, TCA_TAPRIO_SCHED_ENTRY_GATE_MASK, &e->gatemask, sizeof(e->gatemask));
126 addattr_l(n, 1024, TCA_TAPRIO_SCHED_ENTRY_INTERVAL, &e->interval, sizeof(e->interval));
127
128 addattr_nest_end(n, a);
129 }
130
131 return 0;
132}
133
134static void explain_sched_entry(void)
135{
136 fprintf(stderr, "Usage: ... taprio ... sched-entry <cmd> <gate mask> <interval>\n");
137}
138
139static struct sched_entry *create_entry(uint32_t gatemask, uint32_t interval, uint8_t cmd)
140{
141 struct sched_entry *e;
142
143 e = calloc(1, sizeof(*e));
144 if (!e)
145 return NULL;
146
147 e->gatemask = gatemask;
148 e->interval = interval;
149 e->cmd = cmd;
150
151 return e;
152}
153
154static int taprio_parse_opt(struct qdisc_util *qu, int argc,
155 char **argv, struct nlmsghdr *n, const char *dev)
156{
157 __s32 clockid = CLOCKID_INVALID;
158 struct tc_mqprio_qopt opt = { };
92f4b603 159 __s64 cycle_time_extension = 0;
0dd16449 160 struct list_head sched_entries;
92f4b603
VCG
161 struct rtattr *tail, *l;
162 __s64 cycle_time = 0;
0dd16449
VCG
163 __s64 base_time = 0;
164 int err, idx;
165
166 INIT_LIST_HEAD(&sched_entries);
167
168 while (argc > 0) {
169 idx = 0;
170 if (strcmp(*argv, "num_tc") == 0) {
171 NEXT_ARG();
172 if (get_u8(&opt.num_tc, *argv, 10)) {
173 fprintf(stderr, "Illegal \"num_tc\"\n");
174 return -1;
175 }
176 } else if (strcmp(*argv, "map") == 0) {
177 while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
178 NEXT_ARG();
179 if (get_u8(&opt.prio_tc_map[idx], *argv, 10)) {
180 PREV_ARG();
181 break;
182 }
183 idx++;
184 }
185 for ( ; idx < TC_QOPT_MAX_QUEUE; idx++)
186 opt.prio_tc_map[idx] = 0;
187 } else if (strcmp(*argv, "queues") == 0) {
188 char *tmp, *tok;
189
190 while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
191 NEXT_ARG();
192
193 tmp = strdup(*argv);
194 if (!tmp)
195 break;
196
197 tok = strtok(tmp, "@");
198 if (get_u16(&opt.count[idx], tok, 10)) {
199 free(tmp);
200 PREV_ARG();
201 break;
202 }
203 tok = strtok(NULL, "@");
204 if (get_u16(&opt.offset[idx], tok, 10)) {
205 free(tmp);
206 PREV_ARG();
207 break;
208 }
209 free(tmp);
210 idx++;
211 }
212 } else if (strcmp(*argv, "sched-entry") == 0) {
213 uint32_t mask, interval;
214 struct sched_entry *e;
215 uint8_t cmd;
216
217 NEXT_ARG();
218 err = str_to_entry_cmd(*argv);
219 if (err < 0) {
220 explain_sched_entry();
221 return -1;
222 }
223 cmd = err;
224
225 NEXT_ARG();
226 if (get_u32(&mask, *argv, 16)) {
227 explain_sched_entry();
228 return -1;
229 }
230
231 NEXT_ARG();
232 if (get_u32(&interval, *argv, 0)) {
233 explain_sched_entry();
234 return -1;
235 }
236
237 e = create_entry(mask, interval, cmd);
238 if (!e) {
239 fprintf(stderr, "taprio: not enough memory for new schedule entry\n");
240 return -1;
241 }
242
243 list_add_tail(&e->list, &sched_entries);
244
245 } else if (strcmp(*argv, "base-time") == 0) {
246 NEXT_ARG();
247 if (get_s64(&base_time, *argv, 10)) {
248 PREV_ARG();
249 break;
250 }
92f4b603
VCG
251 } else if (strcmp(*argv, "cycle-time") == 0) {
252 NEXT_ARG();
253 if (cycle_time) {
254 fprintf(stderr, "taprio: duplicate \"cycle-time\" specification\n");
255 return -1;
256 }
257
258 if (get_s64(&cycle_time, *argv, 10)) {
259 PREV_ARG();
260 break;
261 }
262
263 } else if (strcmp(*argv, "cycle-time-extension") == 0) {
264 NEXT_ARG();
265 if (cycle_time_extension) {
266 fprintf(stderr, "taprio: duplicate \"cycle-time-extension\" specification\n");
267 return -1;
268 }
269
270 if (get_s64(&cycle_time_extension, *argv, 10)) {
271 PREV_ARG();
272 break;
273 }
0dd16449
VCG
274 } else if (strcmp(*argv, "clockid") == 0) {
275 NEXT_ARG();
276 if (clockid != CLOCKID_INVALID) {
277 fprintf(stderr, "taprio: duplicate \"clockid\" specification\n");
278 return -1;
279 }
280 if (get_clockid(&clockid, *argv)) {
281 explain_clockid(*argv);
282 return -1;
283 }
284 } else if (strcmp(*argv, "help") == 0) {
285 explain();
286 return -1;
287 } else {
288 fprintf(stderr, "Unknown argument\n");
289 return -1;
290 }
291 argc--; argv++;
292 }
293
294 tail = NLMSG_TAIL(n);
295 addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
296
602fae85
VCG
297 if (clockid != CLOCKID_INVALID)
298 addattr_l(n, 1024, TCA_TAPRIO_ATTR_SCHED_CLOCKID, &clockid, sizeof(clockid));
299
0dd16449
VCG
300 if (opt.num_tc > 0)
301 addattr_l(n, 1024, TCA_TAPRIO_ATTR_PRIOMAP, &opt, sizeof(opt));
302
303 if (base_time)
304 addattr_l(n, 1024, TCA_TAPRIO_ATTR_SCHED_BASE_TIME, &base_time, sizeof(base_time));
305
92f4b603
VCG
306 if (cycle_time)
307 addattr_l(n, 1024, TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME,
308 &cycle_time, sizeof(cycle_time));
0dd16449 309
92f4b603
VCG
310 if (cycle_time_extension)
311 addattr_l(n, 1024, TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION,
312 &cycle_time_extension, sizeof(cycle_time_extension));
313
314 l = addattr_nest(n, 1024, TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST | NLA_F_NESTED);
0dd16449 315
92f4b603
VCG
316 err = add_sched_list(&sched_entries, n);
317 if (err < 0) {
318 fprintf(stderr, "Could not add schedule to netlink message\n");
319 return -1;
0dd16449
VCG
320 }
321
92f4b603
VCG
322 addattr_nest_end(n, l);
323
0dd16449
VCG
324 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
325
326 return 0;
327}
328
329static int print_sched_list(FILE *f, struct rtattr *list)
330{
331 struct rtattr *item;
332 int rem;
333
334 if (list == NULL)
335 return 0;
336
337 rem = RTA_PAYLOAD(list);
338
339 open_json_array(PRINT_JSON, "schedule");
340
602fae85
VCG
341 print_string(PRINT_FP, NULL, "%s", _SL_);
342
0dd16449
VCG
343 for (item = RTA_DATA(list); RTA_OK(item, rem); item = RTA_NEXT(item, rem)) {
344 struct rtattr *tb[TCA_TAPRIO_SCHED_ENTRY_MAX + 1];
345 __u32 index = 0, gatemask = 0, interval = 0;
346 __u8 command = 0;
347
348 parse_rtattr_nested(tb, TCA_TAPRIO_SCHED_ENTRY_MAX, item);
349
350 if (tb[TCA_TAPRIO_SCHED_ENTRY_INDEX])
351 index = rta_getattr_u32(tb[TCA_TAPRIO_SCHED_ENTRY_INDEX]);
352
353 if (tb[TCA_TAPRIO_SCHED_ENTRY_CMD])
354 command = rta_getattr_u8(tb[TCA_TAPRIO_SCHED_ENTRY_CMD]);
355
356 if (tb[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK])
357 gatemask = rta_getattr_u32(tb[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK]);
358
359 if (tb[TCA_TAPRIO_SCHED_ENTRY_INTERVAL])
360 interval = rta_getattr_u32(tb[TCA_TAPRIO_SCHED_ENTRY_INTERVAL]);
361
362 open_json_object(NULL);
363 print_uint(PRINT_ANY, "index", "\tindex %u", index);
364 print_string(PRINT_ANY, "cmd", " cmd %s", entry_cmd_to_str(command));
90c5c969 365 print_0xhex(PRINT_ANY, "gatemask", " gatemask %#llx", gatemask);
0dd16449
VCG
366 print_uint(PRINT_ANY, "interval", " interval %u", interval);
367 close_json_object();
368
369 print_string(PRINT_FP, NULL, "%s", _SL_);
370 }
371
372 close_json_array(PRINT_ANY, "");
373
374 return 0;
375}
376
602fae85
VCG
377static int print_schedule(FILE *f, struct rtattr **tb)
378{
92f4b603 379 int64_t base_time = 0, cycle_time = 0, cycle_time_extension = 0;
602fae85
VCG
380
381 if (tb[TCA_TAPRIO_ATTR_SCHED_BASE_TIME])
382 base_time = rta_getattr_s64(tb[TCA_TAPRIO_ATTR_SCHED_BASE_TIME]);
383
92f4b603
VCG
384 if (tb[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME])
385 cycle_time = rta_getattr_s64(tb[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME]);
386
387 if (tb[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION])
388 cycle_time_extension = rta_getattr_s64(
389 tb[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION]);
390
602fae85
VCG
391 print_lluint(PRINT_ANY, "base_time", "\tbase-time %lld", base_time);
392
92f4b603
VCG
393 print_lluint(PRINT_ANY, "cycle_time", " cycle-time %lld", cycle_time);
394
395 print_lluint(PRINT_ANY, "cycle_time_extension",
396 " cycle-time-extension %lld", cycle_time_extension);
397
602fae85
VCG
398 print_sched_list(f, tb[TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST]);
399
400 return 0;
401}
402
0dd16449
VCG
403static int taprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
404{
405 struct rtattr *tb[TCA_TAPRIO_ATTR_MAX + 1];
406 struct tc_mqprio_qopt *qopt = 0;
407 __s32 clockid = CLOCKID_INVALID;
0dd16449
VCG
408 int i;
409
410 if (opt == NULL)
411 return 0;
412
413 parse_rtattr_nested(tb, TCA_TAPRIO_ATTR_MAX, opt);
414
415 if (tb[TCA_TAPRIO_ATTR_PRIOMAP] == NULL)
416 return -1;
417
418 qopt = RTA_DATA(tb[TCA_TAPRIO_ATTR_PRIOMAP]);
419
420 print_uint(PRINT_ANY, "tc", "tc %u ", qopt->num_tc);
421
422 open_json_array(PRINT_ANY, "map");
423 for (i = 0; i <= TC_PRIO_MAX; i++)
424 print_uint(PRINT_ANY, NULL, " %u", qopt->prio_tc_map[i]);
425 close_json_array(PRINT_ANY, "");
426
427 print_string(PRINT_FP, NULL, "%s", _SL_);
428
429 open_json_array(PRINT_ANY, "queues");
430 for (i = 0; i < qopt->num_tc; i++) {
431 open_json_object(NULL);
432 print_uint(PRINT_ANY, "offset", " offset %u", qopt->offset[i]);
433 print_uint(PRINT_ANY, "count", " count %u", qopt->count[i]);
434 close_json_object();
435 }
436 close_json_array(PRINT_ANY, "");
437
438 print_string(PRINT_FP, NULL, "%s", _SL_);
439
0dd16449
VCG
440 if (tb[TCA_TAPRIO_ATTR_SCHED_CLOCKID])
441 clockid = rta_getattr_s32(tb[TCA_TAPRIO_ATTR_SCHED_CLOCKID]);
442
443 print_string(PRINT_ANY, "clockid", "clockid %s", get_clock_name(clockid));
444
602fae85 445 print_schedule(f, tb);
0dd16449 446
602fae85
VCG
447 if (tb[TCA_TAPRIO_ATTR_ADMIN_SCHED]) {
448 struct rtattr *t[TCA_TAPRIO_ATTR_MAX + 1];
449
450 parse_rtattr_nested(t, TCA_TAPRIO_ATTR_MAX,
451 tb[TCA_TAPRIO_ATTR_ADMIN_SCHED]);
0dd16449 452
602fae85
VCG
453 open_json_object(NULL);
454
455 print_schedule(f, t);
456
457 close_json_object();
458 }
459
460 return 0;
0dd16449
VCG
461}
462
463struct qdisc_util taprio_qdisc_util = {
464 .id = "taprio",
465 .parse_qopt = taprio_parse_opt,
466 .print_qopt = taprio_print_opt,
467};