2 * q_taprio.c Time Aware Priority Scheduler
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.
9 * Authors: Vinicius Costa Gomes <vinicius.gomes@intel.com>
10 * Jesus Sanchez-Palencia <jesus.sanchez-palencia@intel.com>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
29 struct list_head list
;
36 #define CLOCKID_INVALID (-1)
37 static const struct static_clockid
{
41 { "REALTIME", CLOCK_REALTIME
},
43 { "BOOTTIME", CLOCK_BOOTTIME
},
44 { "MONOTONIC", CLOCK_MONOTONIC
},
48 static void explain(void)
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");
59 static void explain_clockid(const char *val
)
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");
65 static int get_clockid(__s32
*val
, const char *arg
)
67 const struct static_clockid
*c
;
69 /* Drop the CLOCK_ prefix if that is being used. */
70 if (strcasestr(arg
, "CLOCK_") != NULL
)
71 arg
+= sizeof("CLOCK_") - 1;
73 for (c
= clockids_sysv
; c
->name
; c
++) {
74 if (strcasecmp(c
->name
, arg
) == 0) {
84 static const char* get_clock_name(clockid_t clockid
)
86 const struct static_clockid
*c
;
88 for (c
= clockids_sysv
; c
->name
; c
++) {
89 if (clockid
== c
->clockid
)
96 static const char *entry_cmd_to_str(__u8 cmd
)
99 case TC_TAPRIO_CMD_SET_GATES
:
106 static int str_to_entry_cmd(const char *str
)
108 if (strcmp(str
, "S") == 0)
109 return TC_TAPRIO_CMD_SET_GATES
;
114 static int add_sched_list(struct list_head
*sched_entries
, struct nlmsghdr
*n
)
116 struct sched_entry
*e
;
118 list_for_each_entry(e
, sched_entries
, list
) {
121 a
= addattr_nest(n
, 1024, TCA_TAPRIO_SCHED_ENTRY
);
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
));
127 addattr_nest_end(n
, a
);
133 static void explain_sched_entry(void)
135 fprintf(stderr
, "Usage: ... taprio ... sched-entry <cmd> <gate mask> <interval>\n");
138 static struct sched_entry
*create_entry(uint32_t gatemask
, uint32_t interval
, uint8_t cmd
)
140 struct sched_entry
*e
;
142 e
= calloc(1, sizeof(*e
));
146 e
->gatemask
= gatemask
;
147 e
->interval
= interval
;
153 static int taprio_parse_opt(struct qdisc_util
*qu
, int argc
,
154 char **argv
, struct nlmsghdr
*n
, const char *dev
)
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;
165 INIT_LIST_HEAD(&sched_entries
);
169 if (strcmp(*argv
, "num_tc") == 0) {
171 if (get_u8(&opt
.num_tc
, *argv
, 10)) {
172 fprintf(stderr
, "Illegal \"num_tc\"\n");
175 } else if (strcmp(*argv
, "map") == 0) {
176 while (idx
< TC_QOPT_MAX_QUEUE
&& NEXT_ARG_OK()) {
178 if (get_u8(&opt
.prio_tc_map
[idx
], *argv
, 10)) {
184 for ( ; idx
< TC_QOPT_MAX_QUEUE
; idx
++)
185 opt
.prio_tc_map
[idx
] = 0;
186 } else if (strcmp(*argv
, "queues") == 0) {
189 while (idx
< TC_QOPT_MAX_QUEUE
&& NEXT_ARG_OK()) {
196 tok
= strtok(tmp
, "@");
197 if (get_u16(&opt
.count
[idx
], tok
, 10)) {
202 tok
= strtok(NULL
, "@");
203 if (get_u16(&opt
.offset
[idx
], tok
, 10)) {
211 } else if (strcmp(*argv
, "sched-entry") == 0) {
212 uint32_t mask
, interval
;
213 struct sched_entry
*e
;
217 err
= str_to_entry_cmd(*argv
);
219 explain_sched_entry();
225 if (get_u32(&mask
, *argv
, 16)) {
226 explain_sched_entry();
231 if (get_u32(&interval
, *argv
, 0)) {
232 explain_sched_entry();
236 e
= create_entry(mask
, interval
, cmd
);
238 fprintf(stderr
, "taprio: not enough memory for new schedule entry\n");
242 list_add_tail(&e
->list
, &sched_entries
);
244 } else if (strcmp(*argv
, "base-time") == 0) {
246 if (get_s64(&base_time
, *argv
, 10)) {
250 } else if (strcmp(*argv
, "cycle-time") == 0) {
253 fprintf(stderr
, "taprio: duplicate \"cycle-time\" specification\n");
257 if (get_s64(&cycle_time
, *argv
, 10)) {
262 } else if (strcmp(*argv
, "cycle-time-extension") == 0) {
264 if (cycle_time_extension
) {
265 fprintf(stderr
, "taprio: duplicate \"cycle-time-extension\" specification\n");
269 if (get_s64(&cycle_time_extension
, *argv
, 10)) {
273 } else if (strcmp(*argv
, "clockid") == 0) {
275 if (clockid
!= CLOCKID_INVALID
) {
276 fprintf(stderr
, "taprio: duplicate \"clockid\" specification\n");
279 if (get_clockid(&clockid
, *argv
)) {
280 explain_clockid(*argv
);
283 } else if (strcmp(*argv
, "help") == 0) {
287 fprintf(stderr
, "Unknown argument\n");
293 tail
= NLMSG_TAIL(n
);
294 addattr_l(n
, 1024, TCA_OPTIONS
, NULL
, 0);
296 if (clockid
!= CLOCKID_INVALID
)
297 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_SCHED_CLOCKID
, &clockid
, sizeof(clockid
));
300 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_PRIOMAP
, &opt
, sizeof(opt
));
303 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_SCHED_BASE_TIME
, &base_time
, sizeof(base_time
));
306 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME
,
307 &cycle_time
, sizeof(cycle_time
));
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
));
313 l
= addattr_nest(n
, 1024, TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST
| NLA_F_NESTED
);
315 err
= add_sched_list(&sched_entries
, n
);
317 fprintf(stderr
, "Could not add schedule to netlink message\n");
321 addattr_nest_end(n
, l
);
323 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
328 static int print_sched_list(FILE *f
, struct rtattr
*list
)
336 rem
= RTA_PAYLOAD(list
);
338 open_json_array(PRINT_JSON
, "schedule");
340 print_string(PRINT_FP
, NULL
, "%s", _SL_
);
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;
347 parse_rtattr_nested(tb
, TCA_TAPRIO_SCHED_ENTRY_MAX
, item
);
349 if (tb
[TCA_TAPRIO_SCHED_ENTRY_INDEX
])
350 index
= rta_getattr_u32(tb
[TCA_TAPRIO_SCHED_ENTRY_INDEX
]);
352 if (tb
[TCA_TAPRIO_SCHED_ENTRY_CMD
])
353 command
= rta_getattr_u8(tb
[TCA_TAPRIO_SCHED_ENTRY_CMD
]);
355 if (tb
[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK
])
356 gatemask
= rta_getattr_u32(tb
[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK
]);
358 if (tb
[TCA_TAPRIO_SCHED_ENTRY_INTERVAL
])
359 interval
= rta_getattr_u32(tb
[TCA_TAPRIO_SCHED_ENTRY_INTERVAL
]);
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
);
368 print_string(PRINT_FP
, NULL
, "%s", _SL_
);
371 close_json_array(PRINT_ANY
, "");
376 static int print_schedule(FILE *f
, struct rtattr
**tb
)
378 int64_t base_time
= 0, cycle_time
= 0, cycle_time_extension
= 0;
380 if (tb
[TCA_TAPRIO_ATTR_SCHED_BASE_TIME
])
381 base_time
= rta_getattr_s64(tb
[TCA_TAPRIO_ATTR_SCHED_BASE_TIME
]);
383 if (tb
[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME
])
384 cycle_time
= rta_getattr_s64(tb
[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME
]);
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
]);
390 print_lluint(PRINT_ANY
, "base_time", "\tbase-time %lld", base_time
);
392 print_lluint(PRINT_ANY
, "cycle_time", " cycle-time %lld", cycle_time
);
394 print_lluint(PRINT_ANY
, "cycle_time_extension",
395 " cycle-time-extension %lld", cycle_time_extension
);
397 print_sched_list(f
, tb
[TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST
]);
402 static int taprio_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
404 struct rtattr
*tb
[TCA_TAPRIO_ATTR_MAX
+ 1];
405 struct tc_mqprio_qopt
*qopt
= 0;
406 __s32 clockid
= CLOCKID_INVALID
;
412 parse_rtattr_nested(tb
, TCA_TAPRIO_ATTR_MAX
, opt
);
414 if (tb
[TCA_TAPRIO_ATTR_PRIOMAP
] == NULL
)
417 qopt
= RTA_DATA(tb
[TCA_TAPRIO_ATTR_PRIOMAP
]);
419 print_uint(PRINT_ANY
, "tc", "tc %u ", qopt
->num_tc
);
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
, "");
426 print_string(PRINT_FP
, NULL
, "%s", _SL_
);
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
]);
435 close_json_array(PRINT_ANY
, "");
437 print_string(PRINT_FP
, NULL
, "%s", _SL_
);
439 if (tb
[TCA_TAPRIO_ATTR_SCHED_CLOCKID
])
440 clockid
= rta_getattr_s32(tb
[TCA_TAPRIO_ATTR_SCHED_CLOCKID
]);
442 print_string(PRINT_ANY
, "clockid", "clockid %s", get_clock_name(clockid
));
444 print_schedule(f
, tb
);
446 if (tb
[TCA_TAPRIO_ATTR_ADMIN_SCHED
]) {
447 struct rtattr
*t
[TCA_TAPRIO_ATTR_MAX
+ 1];
449 parse_rtattr_nested(t
, TCA_TAPRIO_ATTR_MAX
,
450 tb
[TCA_TAPRIO_ATTR_ADMIN_SCHED
]);
452 open_json_object(NULL
);
454 print_schedule(f
, t
);
462 struct qdisc_util taprio_qdisc_util
= {
464 .parse_qopt
= taprio_parse_opt
,
465 .print_qopt
= taprio_print_opt
,