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