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)
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] [txtime-delay delay]"
57 "CLOCKID must be a valid SYS-V id (i.e. CLOCK_TAI)\n");
60 static void explain_clockid(const char *val
)
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");
66 static int get_clockid(__s32
*val
, const char *arg
)
68 const struct static_clockid
*c
;
70 /* Drop the CLOCK_ prefix if that is being used. */
71 if (strcasestr(arg
, "CLOCK_") != NULL
)
72 arg
+= sizeof("CLOCK_") - 1;
74 for (c
= clockids_sysv
; c
->name
; c
++) {
75 if (strcasecmp(c
->name
, arg
) == 0) {
85 static const char* get_clock_name(clockid_t clockid
)
87 const struct static_clockid
*c
;
89 for (c
= clockids_sysv
; c
->name
; c
++) {
90 if (clockid
== c
->clockid
)
97 static const char *entry_cmd_to_str(__u8 cmd
)
100 case TC_TAPRIO_CMD_SET_GATES
:
107 static int str_to_entry_cmd(const char *str
)
109 if (strcmp(str
, "S") == 0)
110 return TC_TAPRIO_CMD_SET_GATES
;
115 static int add_sched_list(struct list_head
*sched_entries
, struct nlmsghdr
*n
)
117 struct sched_entry
*e
;
119 list_for_each_entry(e
, sched_entries
, list
) {
122 a
= addattr_nest(n
, 1024, TCA_TAPRIO_SCHED_ENTRY
);
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
));
128 addattr_nest_end(n
, a
);
134 static void explain_sched_entry(void)
136 fprintf(stderr
, "Usage: ... taprio ... sched-entry <cmd> <gate mask> <interval>\n");
139 static struct sched_entry
*create_entry(uint32_t gatemask
, uint32_t interval
, uint8_t cmd
)
141 struct sched_entry
*e
;
143 e
= calloc(1, sizeof(*e
));
147 e
->gatemask
= gatemask
;
148 e
->interval
= interval
;
154 static int taprio_parse_opt(struct qdisc_util
*qu
, int argc
,
155 char **argv
, struct nlmsghdr
*n
, const char *dev
)
157 __s32 clockid
= CLOCKID_INVALID
;
158 struct tc_mqprio_qopt opt
= { };
159 __s64 cycle_time_extension
= 0;
160 struct list_head sched_entries
;
161 struct rtattr
*tail
, *l
;
162 __u32 taprio_flags
= 0;
163 __u32 txtime_delay
= 0;
164 __s64 cycle_time
= 0;
168 INIT_LIST_HEAD(&sched_entries
);
172 if (strcmp(*argv
, "num_tc") == 0) {
174 if (get_u8(&opt
.num_tc
, *argv
, 10)) {
175 fprintf(stderr
, "Illegal \"num_tc\"\n");
178 } else if (strcmp(*argv
, "map") == 0) {
179 while (idx
< TC_QOPT_MAX_QUEUE
&& NEXT_ARG_OK()) {
181 if (get_u8(&opt
.prio_tc_map
[idx
], *argv
, 10)) {
187 for ( ; idx
< TC_QOPT_MAX_QUEUE
; idx
++)
188 opt
.prio_tc_map
[idx
] = 0;
189 } else if (strcmp(*argv
, "queues") == 0) {
192 while (idx
< TC_QOPT_MAX_QUEUE
&& NEXT_ARG_OK()) {
199 tok
= strtok(tmp
, "@");
200 if (get_u16(&opt
.count
[idx
], tok
, 10)) {
205 tok
= strtok(NULL
, "@");
206 if (get_u16(&opt
.offset
[idx
], tok
, 10)) {
214 } else if (strcmp(*argv
, "sched-entry") == 0) {
215 uint32_t mask
, interval
;
216 struct sched_entry
*e
;
220 err
= str_to_entry_cmd(*argv
);
222 explain_sched_entry();
228 if (get_u32(&mask
, *argv
, 16)) {
229 explain_sched_entry();
234 if (get_u32(&interval
, *argv
, 0)) {
235 explain_sched_entry();
239 e
= create_entry(mask
, interval
, cmd
);
241 fprintf(stderr
, "taprio: not enough memory for new schedule entry\n");
245 list_add_tail(&e
->list
, &sched_entries
);
247 } else if (strcmp(*argv
, "base-time") == 0) {
249 if (get_s64(&base_time
, *argv
, 10)) {
253 } else if (strcmp(*argv
, "cycle-time") == 0) {
256 fprintf(stderr
, "taprio: duplicate \"cycle-time\" specification\n");
260 if (get_s64(&cycle_time
, *argv
, 10)) {
265 } else if (strcmp(*argv
, "cycle-time-extension") == 0) {
267 if (cycle_time_extension
) {
268 fprintf(stderr
, "taprio: duplicate \"cycle-time-extension\" specification\n");
272 if (get_s64(&cycle_time_extension
, *argv
, 10)) {
276 } else if (strcmp(*argv
, "clockid") == 0) {
278 if (clockid
!= CLOCKID_INVALID
) {
279 fprintf(stderr
, "taprio: duplicate \"clockid\" specification\n");
282 if (get_clockid(&clockid
, *argv
)) {
283 explain_clockid(*argv
);
286 } else if (strcmp(*argv
, "flags") == 0) {
289 fprintf(stderr
, "taprio: duplicate \"flags\" specification\n");
292 if (get_u32(&taprio_flags
, *argv
, 0)) {
297 } else if (strcmp(*argv
, "txtime-delay") == 0) {
299 if (txtime_delay
!= 0) {
300 fprintf(stderr
, "taprio: duplicate \"txtime-delay\" specification\n");
303 if (get_u32(&txtime_delay
, *argv
, 0)) {
308 } else if (strcmp(*argv
, "help") == 0) {
312 fprintf(stderr
, "Unknown argument\n");
318 tail
= NLMSG_TAIL(n
);
319 addattr_l(n
, 1024, TCA_OPTIONS
, NULL
, 0);
321 if (clockid
!= CLOCKID_INVALID
)
322 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_SCHED_CLOCKID
, &clockid
, sizeof(clockid
));
325 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_FLAGS
, &taprio_flags
, sizeof(taprio_flags
));
328 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_PRIOMAP
, &opt
, sizeof(opt
));
331 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_TXTIME_DELAY
, &txtime_delay
, sizeof(txtime_delay
));
334 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_SCHED_BASE_TIME
, &base_time
, sizeof(base_time
));
337 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME
,
338 &cycle_time
, sizeof(cycle_time
));
340 if (cycle_time_extension
)
341 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION
,
342 &cycle_time_extension
, sizeof(cycle_time_extension
));
344 l
= addattr_nest(n
, 1024, TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST
| NLA_F_NESTED
);
346 err
= add_sched_list(&sched_entries
, n
);
348 fprintf(stderr
, "Could not add schedule to netlink message\n");
352 addattr_nest_end(n
, l
);
354 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
359 static int print_sched_list(FILE *f
, struct rtattr
*list
)
367 rem
= RTA_PAYLOAD(list
);
369 open_json_array(PRINT_JSON
, "schedule");
373 for (item
= RTA_DATA(list
); RTA_OK(item
, rem
); item
= RTA_NEXT(item
, rem
)) {
374 struct rtattr
*tb
[TCA_TAPRIO_SCHED_ENTRY_MAX
+ 1];
375 __u32 index
= 0, gatemask
= 0, interval
= 0;
378 parse_rtattr_nested(tb
, TCA_TAPRIO_SCHED_ENTRY_MAX
, item
);
380 if (tb
[TCA_TAPRIO_SCHED_ENTRY_INDEX
])
381 index
= rta_getattr_u32(tb
[TCA_TAPRIO_SCHED_ENTRY_INDEX
]);
383 if (tb
[TCA_TAPRIO_SCHED_ENTRY_CMD
])
384 command
= rta_getattr_u8(tb
[TCA_TAPRIO_SCHED_ENTRY_CMD
]);
386 if (tb
[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK
])
387 gatemask
= rta_getattr_u32(tb
[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK
]);
389 if (tb
[TCA_TAPRIO_SCHED_ENTRY_INTERVAL
])
390 interval
= rta_getattr_u32(tb
[TCA_TAPRIO_SCHED_ENTRY_INTERVAL
]);
392 open_json_object(NULL
);
393 print_uint(PRINT_ANY
, "index", "\tindex %u", index
);
394 print_string(PRINT_ANY
, "cmd", " cmd %s", entry_cmd_to_str(command
));
395 print_0xhex(PRINT_ANY
, "gatemask", " gatemask %#llx", gatemask
);
396 print_uint(PRINT_ANY
, "interval", " interval %u", interval
);
402 close_json_array(PRINT_ANY
, "");
407 static int print_schedule(FILE *f
, struct rtattr
**tb
)
409 int64_t base_time
= 0, cycle_time
= 0, cycle_time_extension
= 0;
411 if (tb
[TCA_TAPRIO_ATTR_SCHED_BASE_TIME
])
412 base_time
= rta_getattr_s64(tb
[TCA_TAPRIO_ATTR_SCHED_BASE_TIME
]);
414 if (tb
[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME
])
415 cycle_time
= rta_getattr_s64(tb
[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME
]);
417 if (tb
[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION
])
418 cycle_time_extension
= rta_getattr_s64(
419 tb
[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION
]);
421 print_lluint(PRINT_ANY
, "base_time", "\tbase-time %lld", base_time
);
423 print_lluint(PRINT_ANY
, "cycle_time", " cycle-time %lld", cycle_time
);
425 print_lluint(PRINT_ANY
, "cycle_time_extension",
426 " cycle-time-extension %lld", cycle_time_extension
);
428 print_sched_list(f
, tb
[TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST
]);
433 static int taprio_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
435 struct rtattr
*tb
[TCA_TAPRIO_ATTR_MAX
+ 1];
436 struct tc_mqprio_qopt
*qopt
= 0;
437 __s32 clockid
= CLOCKID_INVALID
;
443 parse_rtattr_nested(tb
, TCA_TAPRIO_ATTR_MAX
, opt
);
445 if (tb
[TCA_TAPRIO_ATTR_PRIOMAP
] == NULL
)
448 qopt
= RTA_DATA(tb
[TCA_TAPRIO_ATTR_PRIOMAP
]);
450 print_uint(PRINT_ANY
, "tc", "tc %u ", qopt
->num_tc
);
452 open_json_array(PRINT_ANY
, "map");
453 for (i
= 0; i
<= TC_PRIO_MAX
; i
++)
454 print_uint(PRINT_ANY
, NULL
, " %u", qopt
->prio_tc_map
[i
]);
455 close_json_array(PRINT_ANY
, "");
459 open_json_array(PRINT_ANY
, "queues");
460 for (i
= 0; i
< qopt
->num_tc
; i
++) {
461 open_json_object(NULL
);
462 print_uint(PRINT_ANY
, "offset", " offset %u", qopt
->offset
[i
]);
463 print_uint(PRINT_ANY
, "count", " count %u", qopt
->count
[i
]);
466 close_json_array(PRINT_ANY
, "");
470 if (tb
[TCA_TAPRIO_ATTR_SCHED_CLOCKID
])
471 clockid
= rta_getattr_s32(tb
[TCA_TAPRIO_ATTR_SCHED_CLOCKID
]);
473 print_string(PRINT_ANY
, "clockid", "clockid %s", get_clock_name(clockid
));
475 if (tb
[TCA_TAPRIO_ATTR_FLAGS
]) {
478 flags
= rta_getattr_u32(tb
[TCA_TAPRIO_ATTR_FLAGS
]);
479 print_0xhex(PRINT_ANY
, "flags", " flags %#x", flags
);
482 if (tb
[TCA_TAPRIO_ATTR_TXTIME_DELAY
]) {
485 txtime_delay
= rta_getattr_s32(tb
[TCA_TAPRIO_ATTR_TXTIME_DELAY
]);
486 print_uint(PRINT_ANY
, "txtime_delay", " txtime delay %d", txtime_delay
);
489 print_schedule(f
, tb
);
491 if (tb
[TCA_TAPRIO_ATTR_ADMIN_SCHED
]) {
492 struct rtattr
*t
[TCA_TAPRIO_ATTR_MAX
+ 1];
494 parse_rtattr_nested(t
, TCA_TAPRIO_ATTR_MAX
,
495 tb
[TCA_TAPRIO_ATTR_ADMIN_SCHED
]);
497 open_json_object(NULL
);
499 print_schedule(f
, t
);
507 struct qdisc_util taprio_qdisc_util
= {
509 .parse_qopt
= taprio_parse_opt
,
510 .print_qopt
= taprio_print_opt
,