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 struct list_head sched_entries
;
163 INIT_LIST_HEAD(&sched_entries
);
167 if (strcmp(*argv
, "num_tc") == 0) {
169 if (get_u8(&opt
.num_tc
, *argv
, 10)) {
170 fprintf(stderr
, "Illegal \"num_tc\"\n");
173 } else if (strcmp(*argv
, "map") == 0) {
174 while (idx
< TC_QOPT_MAX_QUEUE
&& NEXT_ARG_OK()) {
176 if (get_u8(&opt
.prio_tc_map
[idx
], *argv
, 10)) {
182 for ( ; idx
< TC_QOPT_MAX_QUEUE
; idx
++)
183 opt
.prio_tc_map
[idx
] = 0;
184 } else if (strcmp(*argv
, "queues") == 0) {
187 while (idx
< TC_QOPT_MAX_QUEUE
&& NEXT_ARG_OK()) {
194 tok
= strtok(tmp
, "@");
195 if (get_u16(&opt
.count
[idx
], tok
, 10)) {
200 tok
= strtok(NULL
, "@");
201 if (get_u16(&opt
.offset
[idx
], tok
, 10)) {
209 } else if (strcmp(*argv
, "sched-entry") == 0) {
210 uint32_t mask
, interval
;
211 struct sched_entry
*e
;
215 err
= str_to_entry_cmd(*argv
);
217 explain_sched_entry();
223 if (get_u32(&mask
, *argv
, 16)) {
224 explain_sched_entry();
229 if (get_u32(&interval
, *argv
, 0)) {
230 explain_sched_entry();
234 e
= create_entry(mask
, interval
, cmd
);
236 fprintf(stderr
, "taprio: not enough memory for new schedule entry\n");
240 list_add_tail(&e
->list
, &sched_entries
);
242 } else if (strcmp(*argv
, "base-time") == 0) {
244 if (get_s64(&base_time
, *argv
, 10)) {
248 } else if (strcmp(*argv
, "clockid") == 0) {
250 if (clockid
!= CLOCKID_INVALID
) {
251 fprintf(stderr
, "taprio: duplicate \"clockid\" specification\n");
254 if (get_clockid(&clockid
, *argv
)) {
255 explain_clockid(*argv
);
258 } else if (strcmp(*argv
, "help") == 0) {
262 fprintf(stderr
, "Unknown argument\n");
268 tail
= NLMSG_TAIL(n
);
269 addattr_l(n
, 1024, TCA_OPTIONS
, NULL
, 0);
272 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_PRIOMAP
, &opt
, sizeof(opt
));
275 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_SCHED_BASE_TIME
, &base_time
, sizeof(base_time
));
277 addattr_l(n
, 1024, TCA_TAPRIO_ATTR_SCHED_CLOCKID
, &clockid
, sizeof(clockid
));
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
);
283 err
= add_sched_list(&sched_entries
, n
);
285 fprintf(stderr
, "Could not add schedule to netlink message\n");
289 addattr_nest_end(n
, entry_list
);
292 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
297 static int print_sched_list(FILE *f
, struct rtattr
*list
)
305 rem
= RTA_PAYLOAD(list
);
307 open_json_array(PRINT_JSON
, "schedule");
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;
314 parse_rtattr_nested(tb
, TCA_TAPRIO_SCHED_ENTRY_MAX
, item
);
316 if (tb
[TCA_TAPRIO_SCHED_ENTRY_INDEX
])
317 index
= rta_getattr_u32(tb
[TCA_TAPRIO_SCHED_ENTRY_INDEX
]);
319 if (tb
[TCA_TAPRIO_SCHED_ENTRY_CMD
])
320 command
= rta_getattr_u8(tb
[TCA_TAPRIO_SCHED_ENTRY_CMD
]);
322 if (tb
[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK
])
323 gatemask
= rta_getattr_u32(tb
[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK
]);
325 if (tb
[TCA_TAPRIO_SCHED_ENTRY_INTERVAL
])
326 interval
= rta_getattr_u32(tb
[TCA_TAPRIO_SCHED_ENTRY_INTERVAL
]);
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 %#llx", gatemask
);
332 print_uint(PRINT_ANY
, "interval", " interval %u", interval
);
335 print_string(PRINT_FP
, NULL
, "%s", _SL_
);
338 close_json_array(PRINT_ANY
, "");
343 static int taprio_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
345 struct rtattr
*tb
[TCA_TAPRIO_ATTR_MAX
+ 1];
346 struct tc_mqprio_qopt
*qopt
= 0;
347 __s32 clockid
= CLOCKID_INVALID
;
354 parse_rtattr_nested(tb
, TCA_TAPRIO_ATTR_MAX
, opt
);
356 if (tb
[TCA_TAPRIO_ATTR_PRIOMAP
] == NULL
)
359 qopt
= RTA_DATA(tb
[TCA_TAPRIO_ATTR_PRIOMAP
]);
361 print_uint(PRINT_ANY
, "tc", "tc %u ", qopt
->num_tc
);
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
, "");
368 print_string(PRINT_FP
, NULL
, "%s", _SL_
);
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
]);
377 close_json_array(PRINT_ANY
, "");
379 print_string(PRINT_FP
, NULL
, "%s", _SL_
);
381 if (tb
[TCA_TAPRIO_ATTR_SCHED_BASE_TIME
])
382 base_time
= rta_getattr_s64(tb
[TCA_TAPRIO_ATTR_SCHED_BASE_TIME
]);
384 if (tb
[TCA_TAPRIO_ATTR_SCHED_CLOCKID
])
385 clockid
= rta_getattr_s32(tb
[TCA_TAPRIO_ATTR_SCHED_CLOCKID
]);
387 print_string(PRINT_ANY
, "clockid", "clockid %s", get_clock_name(clockid
));
389 print_lluint(PRINT_ANY
, "base_time", " base-time %lld", base_time
);
391 print_string(PRINT_FP
, NULL
, "%s", _SL_
);
393 return print_sched_list(f
, tb
[TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST
]);
396 struct qdisc_util taprio_qdisc_util
= {
398 .parse_qopt
= taprio_parse_opt
,
399 .print_qopt
= taprio_print_opt
,