]> git.proxmox.com Git - mirror_iproute2.git/blob - tc/q_taprio.c
Merge branch 'master' into iproute2-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 struct list_head sched_entries;
159 struct rtattr *tail;
160 __s64 base_time = 0;
161 int err, idx;
162
163 INIT_LIST_HEAD(&sched_entries);
164
165 while (argc > 0) {
166 idx = 0;
167 if (strcmp(*argv, "num_tc") == 0) {
168 NEXT_ARG();
169 if (get_u8(&opt.num_tc, *argv, 10)) {
170 fprintf(stderr, "Illegal \"num_tc\"\n");
171 return -1;
172 }
173 } else if (strcmp(*argv, "map") == 0) {
174 while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
175 NEXT_ARG();
176 if (get_u8(&opt.prio_tc_map[idx], *argv, 10)) {
177 PREV_ARG();
178 break;
179 }
180 idx++;
181 }
182 for ( ; idx < TC_QOPT_MAX_QUEUE; idx++)
183 opt.prio_tc_map[idx] = 0;
184 } else if (strcmp(*argv, "queues") == 0) {
185 char *tmp, *tok;
186
187 while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
188 NEXT_ARG();
189
190 tmp = strdup(*argv);
191 if (!tmp)
192 break;
193
194 tok = strtok(tmp, "@");
195 if (get_u16(&opt.count[idx], tok, 10)) {
196 free(tmp);
197 PREV_ARG();
198 break;
199 }
200 tok = strtok(NULL, "@");
201 if (get_u16(&opt.offset[idx], tok, 10)) {
202 free(tmp);
203 PREV_ARG();
204 break;
205 }
206 free(tmp);
207 idx++;
208 }
209 } else if (strcmp(*argv, "sched-entry") == 0) {
210 uint32_t mask, interval;
211 struct sched_entry *e;
212 uint8_t cmd;
213
214 NEXT_ARG();
215 err = str_to_entry_cmd(*argv);
216 if (err < 0) {
217 explain_sched_entry();
218 return -1;
219 }
220 cmd = err;
221
222 NEXT_ARG();
223 if (get_u32(&mask, *argv, 16)) {
224 explain_sched_entry();
225 return -1;
226 }
227
228 NEXT_ARG();
229 if (get_u32(&interval, *argv, 0)) {
230 explain_sched_entry();
231 return -1;
232 }
233
234 e = create_entry(mask, interval, cmd);
235 if (!e) {
236 fprintf(stderr, "taprio: not enough memory for new schedule entry\n");
237 return -1;
238 }
239
240 list_add_tail(&e->list, &sched_entries);
241
242 } else if (strcmp(*argv, "base-time") == 0) {
243 NEXT_ARG();
244 if (get_s64(&base_time, *argv, 10)) {
245 PREV_ARG();
246 break;
247 }
248 } else if (strcmp(*argv, "clockid") == 0) {
249 NEXT_ARG();
250 if (clockid != CLOCKID_INVALID) {
251 fprintf(stderr, "taprio: duplicate \"clockid\" specification\n");
252 return -1;
253 }
254 if (get_clockid(&clockid, *argv)) {
255 explain_clockid(*argv);
256 return -1;
257 }
258 } else if (strcmp(*argv, "help") == 0) {
259 explain();
260 return -1;
261 } else {
262 fprintf(stderr, "Unknown argument\n");
263 return -1;
264 }
265 argc--; argv++;
266 }
267
268 tail = NLMSG_TAIL(n);
269 addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
270
271 if (opt.num_tc > 0)
272 addattr_l(n, 1024, TCA_TAPRIO_ATTR_PRIOMAP, &opt, sizeof(opt));
273
274 if (base_time)
275 addattr_l(n, 1024, TCA_TAPRIO_ATTR_SCHED_BASE_TIME, &base_time, sizeof(base_time));
276
277 addattr_l(n, 1024, TCA_TAPRIO_ATTR_SCHED_CLOCKID, &clockid, sizeof(clockid));
278
279 if (!list_empty(&sched_entries)) {
280 struct rtattr *entry_list;
281 entry_list = addattr_nest(n, 1024, TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST | NLA_F_NESTED);
282
283 err = add_sched_list(&sched_entries, n);
284 if (err < 0) {
285 fprintf(stderr, "Could not add schedule to netlink message\n");
286 return -1;
287 }
288
289 addattr_nest_end(n, entry_list);
290 }
291
292 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
293
294 return 0;
295 }
296
297 static int print_sched_list(FILE *f, struct rtattr *list)
298 {
299 struct rtattr *item;
300 int rem;
301
302 if (list == NULL)
303 return 0;
304
305 rem = RTA_PAYLOAD(list);
306
307 open_json_array(PRINT_JSON, "schedule");
308
309 for (item = RTA_DATA(list); RTA_OK(item, rem); item = RTA_NEXT(item, rem)) {
310 struct rtattr *tb[TCA_TAPRIO_SCHED_ENTRY_MAX + 1];
311 __u32 index = 0, gatemask = 0, interval = 0;
312 __u8 command = 0;
313
314 parse_rtattr_nested(tb, TCA_TAPRIO_SCHED_ENTRY_MAX, item);
315
316 if (tb[TCA_TAPRIO_SCHED_ENTRY_INDEX])
317 index = rta_getattr_u32(tb[TCA_TAPRIO_SCHED_ENTRY_INDEX]);
318
319 if (tb[TCA_TAPRIO_SCHED_ENTRY_CMD])
320 command = rta_getattr_u8(tb[TCA_TAPRIO_SCHED_ENTRY_CMD]);
321
322 if (tb[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK])
323 gatemask = rta_getattr_u32(tb[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK]);
324
325 if (tb[TCA_TAPRIO_SCHED_ENTRY_INTERVAL])
326 interval = rta_getattr_u32(tb[TCA_TAPRIO_SCHED_ENTRY_INTERVAL]);
327
328 open_json_object(NULL);
329 print_uint(PRINT_ANY, "index", "\tindex %u", index);
330 print_string(PRINT_ANY, "cmd", " cmd %s", entry_cmd_to_str(command));
331 print_0xhex(PRINT_ANY, "gatemask", " gatemask %#x", gatemask);
332 print_uint(PRINT_ANY, "interval", " interval %u", interval);
333 close_json_object();
334
335 print_string(PRINT_FP, NULL, "%s", _SL_);
336 }
337
338 close_json_array(PRINT_ANY, "");
339
340 return 0;
341 }
342
343 static int taprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
344 {
345 struct rtattr *tb[TCA_TAPRIO_ATTR_MAX + 1];
346 struct tc_mqprio_qopt *qopt = 0;
347 __s32 clockid = CLOCKID_INVALID;
348 __s64 base_time = 0;
349 int i;
350
351 if (opt == NULL)
352 return 0;
353
354 parse_rtattr_nested(tb, TCA_TAPRIO_ATTR_MAX, opt);
355
356 if (tb[TCA_TAPRIO_ATTR_PRIOMAP] == NULL)
357 return -1;
358
359 qopt = RTA_DATA(tb[TCA_TAPRIO_ATTR_PRIOMAP]);
360
361 print_uint(PRINT_ANY, "tc", "tc %u ", qopt->num_tc);
362
363 open_json_array(PRINT_ANY, "map");
364 for (i = 0; i <= TC_PRIO_MAX; i++)
365 print_uint(PRINT_ANY, NULL, " %u", qopt->prio_tc_map[i]);
366 close_json_array(PRINT_ANY, "");
367
368 print_string(PRINT_FP, NULL, "%s", _SL_);
369
370 open_json_array(PRINT_ANY, "queues");
371 for (i = 0; i < qopt->num_tc; i++) {
372 open_json_object(NULL);
373 print_uint(PRINT_ANY, "offset", " offset %u", qopt->offset[i]);
374 print_uint(PRINT_ANY, "count", " count %u", qopt->count[i]);
375 close_json_object();
376 }
377 close_json_array(PRINT_ANY, "");
378
379 print_string(PRINT_FP, NULL, "%s", _SL_);
380
381 if (tb[TCA_TAPRIO_ATTR_SCHED_BASE_TIME])
382 base_time = rta_getattr_s64(tb[TCA_TAPRIO_ATTR_SCHED_BASE_TIME]);
383
384 if (tb[TCA_TAPRIO_ATTR_SCHED_CLOCKID])
385 clockid = rta_getattr_s32(tb[TCA_TAPRIO_ATTR_SCHED_CLOCKID]);
386
387 print_string(PRINT_ANY, "clockid", "clockid %s", get_clock_name(clockid));
388
389 print_lluint(PRINT_ANY, "base_time", " base-time %lld", base_time);
390
391 print_string(PRINT_FP, NULL, "%s", _SL_);
392
393 return print_sched_list(f, tb[TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST]);
394 }
395
396 struct qdisc_util taprio_qdisc_util = {
397 .id = "taprio",
398 .parse_qopt = taprio_parse_opt,
399 .print_qopt = taprio_print_opt,
400 };