iproute2/tc/q_gred.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0-or-later */
   2/*
   3 * q_gred.c             GRED.
   4 *
   5 * Authors:    J Hadi Salim(hadi@nortelnetworks.com)
   6 *             code ruthlessly ripped from
   7 *             Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   8 *
   9 */
  10
  11#include <stdio.h>
  12#include <stdlib.h>
  13#include <unistd.h>
  14#include <fcntl.h>
  15#include <sys/socket.h>
  16#include <netinet/in.h>
  17#include <arpa/inet.h>
  18#include <string.h>
  19#include <math.h>
  20
  21#include "utils.h"
  22#include "tc_util.h"
  23
  24#include "tc_red.h"
  25
  26#ifdef DEBUG
  27#define DPRINTF(format, args...) fprintf(stderr, format, ##args)
  28#else
  29#define DPRINTF(format, args...)
  30#endif
  31
  32static void explain(void)
  33{
  34        fprintf(stderr,
  35                "Usage: tc qdisc { add | replace | change } ... gred setup vqs NUMBER\n"
  36                "           default DEFAULT_VQ [ grio ] [ limit BYTES ] [ecn] [harddrop]\n"
  37                "       tc qdisc change ... gred vq VQ [ prio VALUE ] limit BYTES\n"
  38                "           min BYTES max BYTES avpkt BYTES [ burst PACKETS ]\n"
  39                "           [ probability PROBABILITY ] [ bandwidth KBPS ] [ecn] [harddrop]\n");
  40}
  41
  42static int init_gred(const struct qdisc_util *qu, int argc, char **argv,
  43                     struct nlmsghdr *n)
  44{
  45
  46        struct rtattr *tail;
  47        struct tc_gred_sopt opt = { 0 };
  48        __u32 limit = 0;
  49
  50        opt.def_DP = MAX_DPs;
  51
  52        while (argc > 0) {
  53                DPRINTF("init_gred: invoked with %s\n", *argv);
  54                if (strcmp(*argv, "vqs") == 0 ||
  55                    strcmp(*argv, "DPs") == 0) {
  56                        NEXT_ARG();
  57                        if (get_unsigned(&opt.DPs, *argv, 10)) {
  58                                fprintf(stderr, "Illegal \"vqs\"\n");
  59                                return -1;
  60                        } else if (opt.DPs > MAX_DPs) {
  61                                fprintf(stderr, "GRED: only %u VQs are currently supported\n",
  62                                        MAX_DPs);
  63                                return -1;
  64                        }
  65                } else if (strcmp(*argv, "default") == 0) {
  66                        if (opt.DPs == 0) {
  67                                fprintf(stderr, "\"default\" must be defined after \"vqs\"\n");
  68                                return -1;
  69                        }
  70                        NEXT_ARG();
  71                        if (get_unsigned(&opt.def_DP, *argv, 10)) {
  72                                fprintf(stderr, "Illegal \"default\"\n");
  73                                return -1;
  74                        } else if (opt.def_DP >= opt.DPs) {
  75                                fprintf(stderr, "\"default\" must be less than \"vqs\"\n");
  76                                return -1;
  77                        }
  78                } else if (strcmp(*argv, "grio") == 0) {
  79                        opt.grio = 1;
  80                } else if (strcmp(*argv, "limit") == 0) {
  81                        NEXT_ARG();
  82                        if (get_size(&limit, *argv)) {
  83                                fprintf(stderr, "Illegal \"limit\"\n");
  84                                return -1;
  85                        }
  86                } else if (strcmp(*argv, "ecn") == 0) {
  87                        opt.flags |= TC_RED_ECN;
  88                } else if (strcmp(*argv, "harddrop") == 0) {
  89                        opt.flags |= TC_RED_HARDDROP;
  90                } else if (strcmp(*argv, "help") == 0) {
  91                        explain();
  92                        return -1;
  93                } else {
  94                        fprintf(stderr, "What is \"%s\"?\n", *argv);
  95                        explain();
  96                        return -1;
  97                }
  98                argc--; argv++;
  99        }
 100
 101        if (!opt.DPs || opt.def_DP == MAX_DPs) {
 102                fprintf(stderr, "Illegal gred setup parameters\n");
 103                return -1;
 104        }
 105
 106        DPRINTF("TC_GRED: sending DPs=%u def_DP=%u\n", opt.DPs, opt.def_DP);
 107        n->nlmsg_flags |= NLM_F_CREATE;
 108        tail = addattr_nest(n, 1024, TCA_OPTIONS);
 109        addattr_l(n, 1024, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt));
 110        if (limit)
 111                addattr32(n, 1024, TCA_GRED_LIMIT, limit);
 112        addattr_nest_end(n, tail);
 113        return 0;
 114}
 115/*
 116^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 117*/
 118static int gred_parse_opt(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev)
 119{
 120        struct rtattr *tail, *entry, *vqs;
 121        int ok = 0;
 122        struct tc_gred_qopt opt = { 0 };
 123        unsigned int burst = 0;
 124        unsigned int avpkt = 0;
 125        unsigned int flags = 0;
 126        double probability = 0.02;
 127        unsigned int rate = 0;
 128        int parm;
 129        __u8 sbuf[256];
 130        __u32 max_P;
 131
 132        opt.DP = MAX_DPs;
 133
 134        while (argc > 0) {
 135                if (strcmp(*argv, "limit") == 0) {
 136                        NEXT_ARG();
 137                        if (get_size(&opt.limit, *argv)) {
 138                                fprintf(stderr, "Illegal \"limit\"\n");
 139                                return -1;
 140                        }
 141                        ok++;
 142                } else if (strcmp(*argv, "setup") == 0) {
 143                        if (ok) {
 144                                fprintf(stderr, "Illegal \"setup\"\n");
 145                                return -1;
 146                        }
 147                        return init_gred(qu, argc-1, argv+1, n);
 148                } else if (strcmp(*argv, "min") == 0) {
 149                        NEXT_ARG();
 150                        if (get_size(&opt.qth_min, *argv)) {
 151                                fprintf(stderr, "Illegal \"min\"\n");
 152                                return -1;
 153                        }
 154                        ok++;
 155                } else if (strcmp(*argv, "max") == 0) {
 156                        NEXT_ARG();
 157                        if (get_size(&opt.qth_max, *argv)) {
 158                                fprintf(stderr, "Illegal \"max\"\n");
 159                                return -1;
 160                        }
 161                        ok++;
 162                } else if (strcmp(*argv, "vq") == 0 ||
 163                           strcmp(*argv, "DP") == 0) {
 164                        NEXT_ARG();
 165                        if (get_unsigned(&opt.DP, *argv, 10)) {
 166                                fprintf(stderr, "Illegal \"vq\"\n");
 167                                return -1;
 168                        } else if (opt.DP >= MAX_DPs) {
 169                                fprintf(stderr, "GRED: only %u VQs are currently supported\n",
 170                                        MAX_DPs);
 171                                return -1;
 172                        } /* need a better error check */
 173                        ok++;
 174                } else if (strcmp(*argv, "burst") == 0) {
 175                        NEXT_ARG();
 176                        if (get_unsigned(&burst, *argv, 0)) {
 177                                fprintf(stderr, "Illegal \"burst\"\n");
 178                                return -1;
 179                        }
 180                        ok++;
 181                } else if (strcmp(*argv, "avpkt") == 0) {
 182                        NEXT_ARG();
 183                        if (get_size(&avpkt, *argv)) {
 184                                fprintf(stderr, "Illegal \"avpkt\"\n");
 185                                return -1;
 186                        }
 187                        ok++;
 188                } else if (strcmp(*argv, "probability") == 0) {
 189                        NEXT_ARG();
 190                        if (sscanf(*argv, "%lg", &probability) != 1) {
 191                                fprintf(stderr, "Illegal \"probability\"\n");
 192                                return -1;
 193                        }
 194                        ok++;
 195                } else if (strcmp(*argv, "prio") == 0) {
 196                        NEXT_ARG();
 197                        opt.prio = strtol(*argv, (char **)NULL, 10);
 198                        /* some error check here */
 199                        ok++;
 200                } else if (strcmp(*argv, "bandwidth") == 0) {
 201                        NEXT_ARG();
 202                        if (strchr(*argv, '%')) {
 203                                if (get_percent_rate(&rate, *argv, dev)) {
 204                                        fprintf(stderr, "Illegal \"bandwidth\"\n");
 205                                        return -1;
 206                                }
 207                        } else if (get_rate(&rate, *argv)) {
 208                                fprintf(stderr, "Illegal \"bandwidth\"\n");
 209                                return -1;
 210                        }
 211                        ok++;
 212                } else if (strcmp(*argv, "ecn") == 0) {
 213                        flags |= TC_RED_ECN;
 214                } else if (strcmp(*argv, "harddrop") == 0) {
 215                        flags |= TC_RED_HARDDROP;
 216                } else if (strcmp(*argv, "help") == 0) {
 217                        explain();
 218                        return -1;
 219                } else {
 220                        fprintf(stderr, "What is \"%s\"?\n", *argv);
 221                        explain();
 222                        return -1;
 223                }
 224                argc--; argv++;
 225        }
 226
 227        if (!ok) {
 228                explain();
 229                return -1;
 230        }
 231        if (opt.DP == MAX_DPs || !opt.limit || !opt.qth_min || !opt.qth_max ||
 232            !avpkt) {
 233                fprintf(stderr, "Required parameter (vq, limit, min, max, avpkt) is missing\n");
 234                return -1;
 235        }
 236        if (!burst) {
 237                burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt);
 238                fprintf(stderr, "GRED: set burst to %u\n", burst);
 239        }
 240        if (!rate) {
 241                get_rate(&rate, "10Mbit");
 242                fprintf(stderr, "GRED: set bandwidth to 10Mbit\n");
 243        }
 244        if ((parm = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) {
 245                fprintf(stderr, "GRED: failed to calculate EWMA constant.\n");
 246                return -1;
 247        }
 248        if (parm >= 10)
 249                fprintf(stderr, "GRED: WARNING. Burst %u seems to be too large.\n",
 250                    burst);
 251        opt.Wlog = parm;
 252        if ((parm = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) {
 253                fprintf(stderr, "GRED: failed to calculate probability.\n");
 254                return -1;
 255        }
 256        opt.Plog = parm;
 257        if ((parm = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0)
 258            {
 259                fprintf(stderr, "GRED: failed to calculate idle damping table.\n");
 260                return -1;
 261        }
 262        opt.Scell_log = parm;
 263
 264        tail = addattr_nest(n, 1024, TCA_OPTIONS);
 265        addattr_l(n, 1024, TCA_GRED_PARMS, &opt, sizeof(opt));
 266        addattr_l(n, 1024, TCA_GRED_STAB, sbuf, 256);
 267        max_P = probability * pow(2, 32);
 268        addattr32(n, 1024, TCA_GRED_MAX_P, max_P);
 269
 270        vqs = addattr_nest(n, 1024, TCA_GRED_VQ_LIST);
 271        entry = addattr_nest(n, 1024, TCA_GRED_VQ_ENTRY);
 272        addattr32(n, 1024, TCA_GRED_VQ_DP, opt.DP);
 273        addattr32(n, 1024, TCA_GRED_VQ_FLAGS, flags);
 274        addattr_nest_end(n, entry);
 275        addattr_nest_end(n, vqs);
 276
 277        addattr_nest_end(n, tail);
 278        return 0;
 279}
 280
 281struct tc_gred_info {
 282        bool    flags_present;
 283        __u64   bytes;
 284        __u32   packets;
 285        __u32   backlog;
 286        __u32   prob_drop;
 287        __u32   prob_mark;
 288        __u32   forced_drop;
 289        __u32   forced_mark;
 290        __u32   pdrop;
 291        __u32   other;
 292        __u32   flags;
 293};
 294
 295static void
 296gred_parse_vqs(struct tc_gred_info *info, struct rtattr *vqs)
 297{
 298        int rem = RTA_PAYLOAD(vqs);
 299        unsigned int offset = 0;
 300
 301        while (rem > offset) {
 302                struct rtattr *tb_entry[TCA_GRED_VQ_ENTRY_MAX + 1] = {};
 303                struct rtattr *tb[TCA_GRED_VQ_MAX + 1] = {};
 304                struct rtattr *entry;
 305                unsigned int len;
 306                unsigned int dp;
 307
 308                entry = RTA_DATA(vqs) + offset;
 309
 310                parse_rtattr(tb_entry, TCA_GRED_VQ_ENTRY_MAX, entry,
 311                             rem - offset);
 312                len = RTA_LENGTH(RTA_PAYLOAD(entry));
 313                offset += len;
 314
 315                if (!tb_entry[TCA_GRED_VQ_ENTRY]) {
 316                        fprintf(stderr,
 317                                "ERROR: Failed to parse Virtual Queue entry\n");
 318                        continue;
 319                }
 320
 321                parse_rtattr_nested(tb, TCA_GRED_VQ_MAX,
 322                                    tb_entry[TCA_GRED_VQ_ENTRY]);
 323
 324                if (!tb[TCA_GRED_VQ_DP]) {
 325                        fprintf(stderr,
 326                                "ERROR: Virtual Queue without DP attribute\n");
 327                        continue;
 328                }
 329
 330                dp = rta_getattr_u32(tb[TCA_GRED_VQ_DP]);
 331
 332                if (tb[TCA_GRED_VQ_STAT_BYTES])
 333                        info[dp].bytes =
 334                                rta_getattr_u32(tb[TCA_GRED_VQ_STAT_BYTES]);
 335                if (tb[TCA_GRED_VQ_STAT_PACKETS])
 336                        info[dp].packets =
 337                                rta_getattr_u32(tb[TCA_GRED_VQ_STAT_PACKETS]);
 338                if (tb[TCA_GRED_VQ_STAT_BACKLOG])
 339                        info[dp].backlog =
 340                                rta_getattr_u32(tb[TCA_GRED_VQ_STAT_BACKLOG]);
 341                if (tb[TCA_GRED_VQ_STAT_PROB_DROP])
 342                        info[dp].prob_drop =
 343                                rta_getattr_u32(tb[TCA_GRED_VQ_STAT_PROB_DROP]);
 344                if (tb[TCA_GRED_VQ_STAT_PROB_MARK])
 345                        info[dp].prob_mark =
 346                                rta_getattr_u32(tb[TCA_GRED_VQ_STAT_PROB_MARK]);
 347                if (tb[TCA_GRED_VQ_STAT_FORCED_DROP])
 348                        info[dp].forced_drop =
 349                                rta_getattr_u32(tb[TCA_GRED_VQ_STAT_FORCED_DROP]);
 350                if (tb[TCA_GRED_VQ_STAT_FORCED_MARK])
 351                        info[dp].forced_mark =
 352                                rta_getattr_u32(tb[TCA_GRED_VQ_STAT_FORCED_MARK]);
 353                if (tb[TCA_GRED_VQ_STAT_PDROP])
 354                        info[dp].pdrop =
 355                                rta_getattr_u32(tb[TCA_GRED_VQ_STAT_PDROP]);
 356                if (tb[TCA_GRED_VQ_STAT_OTHER])
 357                        info[dp].other =
 358                                rta_getattr_u32(tb[TCA_GRED_VQ_STAT_OTHER]);
 359                info[dp].flags_present = !!tb[TCA_GRED_VQ_FLAGS];
 360                if (tb[TCA_GRED_VQ_FLAGS])
 361                        info[dp].flags =
 362                                rta_getattr_u32(tb[TCA_GRED_VQ_FLAGS]);
 363        }
 364}
 365
 366static void
 367gred_print_stats(struct tc_gred_info *info, struct tc_gred_qopt *qopt)
 368{
 369        __u64 bytes = info ? info->bytes : qopt->bytesin;
 370
 371        if (!is_json_context())
 372                printf("\n  Queue size: ");
 373
 374        print_size(PRINT_ANY, "qave", "average %s ", qopt->qave);
 375        print_size(PRINT_ANY, "backlog", "current %s ", qopt->backlog);
 376
 377        if (!is_json_context())
 378                printf("\n  Dropped packets: ");
 379
 380        if (info) {
 381                print_uint(PRINT_ANY, "forced_drop", "forced %u ",
 382                           info->forced_drop);
 383                print_uint(PRINT_ANY, "prob_drop", "early %u ",
 384                           info->prob_drop);
 385                print_uint(PRINT_ANY, "pdrop", "pdrop %u ", info->pdrop);
 386                print_uint(PRINT_ANY, "other", "other %u ", info->other);
 387
 388                if (!is_json_context())
 389                        printf("\n  Marked packets: ");
 390                print_uint(PRINT_ANY, "forced_mark", "forced %u ",
 391                           info->forced_mark);
 392                print_uint(PRINT_ANY, "prob_mark", "early %u ",
 393                           info->prob_mark);
 394        } else {
 395                print_uint(PRINT_ANY, "forced_drop", "forced %u ",
 396                           qopt->forced);
 397                print_uint(PRINT_ANY, "prob_drop", "early %u ", qopt->early);
 398                print_uint(PRINT_ANY, "pdrop", "pdrop %u ", qopt->pdrop);
 399                print_uint(PRINT_ANY, "other", "other %u ", qopt->other);
 400        }
 401
 402        if (!is_json_context())
 403                printf("\n  Total packets: ");
 404
 405        print_uint(PRINT_ANY, "packets", "%u ", qopt->packets);
 406        print_size(PRINT_ANY, "bytes", "(%s) ", bytes);
 407}
 408
 409static int gred_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 410{
 411        struct tc_gred_info infos[MAX_DPs] = {};
 412        struct rtattr *tb[TCA_GRED_MAX + 1];
 413        struct tc_gred_sopt *sopt;
 414        struct tc_gred_qopt *qopt;
 415        bool vq_info = false;
 416        __u32 *max_p = NULL;
 417        __u32 *limit = NULL;
 418        unsigned int i;
 419
 420        if (opt == NULL)
 421                return 0;
 422
 423        parse_rtattr_nested(tb, TCA_GRED_MAX, opt);
 424
 425        if (tb[TCA_GRED_PARMS] == NULL)
 426                return -1;
 427
 428        if (tb[TCA_GRED_MAX_P] &&
 429            RTA_PAYLOAD(tb[TCA_GRED_MAX_P]) >= sizeof(__u32) * MAX_DPs)
 430                max_p = RTA_DATA(tb[TCA_GRED_MAX_P]);
 431
 432        if (tb[TCA_GRED_LIMIT] &&
 433            RTA_PAYLOAD(tb[TCA_GRED_LIMIT]) == sizeof(__u32))
 434                limit = RTA_DATA(tb[TCA_GRED_LIMIT]);
 435
 436        sopt = RTA_DATA(tb[TCA_GRED_DPS]);
 437        qopt = RTA_DATA(tb[TCA_GRED_PARMS]);
 438        if (RTA_PAYLOAD(tb[TCA_GRED_DPS]) < sizeof(*sopt) ||
 439            RTA_PAYLOAD(tb[TCA_GRED_PARMS]) < sizeof(*qopt)*MAX_DPs) {
 440                fprintf(f, "\n GRED received message smaller than expected\n");
 441                return -1;
 442        }
 443
 444        if (tb[TCA_GRED_VQ_LIST]) {
 445                gred_parse_vqs(infos, tb[TCA_GRED_VQ_LIST]);
 446                vq_info = true;
 447        }
 448
 449        print_uint(PRINT_ANY, "dp_cnt", "vqs %u ", sopt->DPs);
 450        print_uint(PRINT_ANY, "dp_default", "default %u ", sopt->def_DP);
 451
 452        if (sopt->grio)
 453                print_bool(PRINT_ANY, "grio", "grio ", true);
 454        else
 455                print_bool(PRINT_ANY, "grio", NULL, false);
 456
 457        if (limit)
 458                print_size(PRINT_ANY, "limit", "limit %s ", *limit);
 459
 460        tc_red_print_flags(sopt->flags);
 461
 462        open_json_array(PRINT_JSON, "vqs");
 463        for (i = 0; i < MAX_DPs; i++, qopt++) {
 464                if (qopt->DP >= MAX_DPs)
 465                        continue;
 466
 467                open_json_object(NULL);
 468
 469                print_uint(PRINT_ANY, "vq", "\n vq %u ", qopt->DP);
 470                print_hhu(PRINT_ANY, "prio", "prio %hhu ", qopt->prio);
 471                print_size(PRINT_ANY, "limit", "limit %s ", qopt->limit);
 472                print_size(PRINT_ANY, "min", "min %s ", qopt->qth_min);
 473                print_size(PRINT_ANY, "max", "max %s ", qopt->qth_max);
 474
 475                if (infos[i].flags_present)
 476                        tc_red_print_flags(infos[i].flags);
 477
 478                if (show_details) {
 479                        print_uint(PRINT_ANY, "ewma", "ewma %u ", qopt->Wlog);
 480                        if (max_p)
 481                                print_float(PRINT_ANY, "probability",
 482                                            "probability %lg ",
 483                                            max_p[i] / pow(2, 32));
 484                        else
 485                                print_uint(PRINT_ANY, "Plog", "Plog %u ",
 486                                           qopt->Plog);
 487                        print_uint(PRINT_ANY, "Scell_log", "Scell_log %u ",
 488                                   qopt->Scell_log);
 489                }
 490                if (show_stats)
 491                        gred_print_stats(vq_info ? &infos[i] : NULL, qopt);
 492                close_json_object();
 493        }
 494        close_json_array(PRINT_JSON, "vqs");
 495        return 0;
 496}
 497
 498struct qdisc_util gred_qdisc_util = {
 499        .id             = "gred",
 500        .parse_qopt     = gred_parse_opt,
 501        .print_qopt     = gred_print_opt,
 502};
 503