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