iproute2/tc/q_cbq.c
<<
>>
Prefs
   1/*
   2 * q_cbq.c              CBQ.
   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:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  10 *
  11 */
  12
  13#include <stdio.h>
  14#include <stdlib.h>
  15#include <unistd.h>
  16#include <fcntl.h>
  17#include <sys/socket.h>
  18#include <netinet/in.h>
  19#include <arpa/inet.h>
  20#include <string.h>
  21
  22#include "utils.h"
  23#include "tc_util.h"
  24#include "tc_cbq.h"
  25
  26static void explain_class(void)
  27{
  28        fprintf(stderr,
  29                "Usage: ... cbq bandwidth BPS rate BPS maxburst PKTS [ avpkt BYTES ]\n"
  30                "               [ minburst PKTS ] [ bounded ] [ isolated ]\n"
  31                "               [ allot BYTES ] [ mpu BYTES ] [ weight RATE ]\n"
  32                "               [ prio NUMBER ] [ cell BYTES ] [ ewma LOG ]\n"
  33                "               [ estimator INTERVAL TIME_CONSTANT ]\n"
  34                "               [ split CLASSID ] [ defmap MASK/CHANGE ]\n"
  35                "               [ overhead BYTES ] [ linklayer TYPE ]\n");
  36}
  37
  38static void explain(void)
  39{
  40        fprintf(stderr,
  41                "Usage: ... cbq bandwidth BPS avpkt BYTES [ mpu BYTES ]\n"
  42                "               [ cell BYTES ] [ ewma LOG ]\n");
  43}
  44
  45static void explain1(char *arg)
  46{
  47        fprintf(stderr, "Illegal \"%s\"\n", arg);
  48}
  49
  50
  51static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev)
  52{
  53        struct tc_ratespec r = {};
  54        struct tc_cbq_lssopt lss = {};
  55        __u32 rtab[256];
  56        unsigned mpu = 0, avpkt = 0, allot = 0;
  57        unsigned short overhead = 0;
  58        unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
  59        int cell_log =  -1;
  60        int ewma_log =  -1;
  61        struct rtattr *tail;
  62
  63        while (argc > 0) {
  64                if (matches(*argv, "bandwidth") == 0 ||
  65                    matches(*argv, "rate") == 0) {
  66                        NEXT_ARG();
  67                        if (strchr(*argv, '%')) {
  68                                if (get_percent_rate(&r.rate, *argv, dev)) {
  69                                        explain1("bandwidth");
  70                                        return -1;
  71                                }
  72                        } else if (get_rate(&r.rate, *argv)) {
  73                                explain1("bandwidth");
  74                                return -1;
  75                        }
  76                } else if (matches(*argv, "ewma") == 0) {
  77                        NEXT_ARG();
  78                        if (get_integer(&ewma_log, *argv, 0)) {
  79                                explain1("ewma");
  80                                return -1;
  81                        }
  82                        if (ewma_log > 31) {
  83                                fprintf(stderr, "ewma_log must be < 32\n");
  84                                return -1;
  85                        }
  86                } else if (matches(*argv, "cell") == 0) {
  87                        unsigned int cell;
  88                        int i;
  89
  90                        NEXT_ARG();
  91                        if (get_size(&cell, *argv)) {
  92                                explain1("cell");
  93                                return -1;
  94                        }
  95                        for (i = 0; i < 32; i++)
  96                                if ((1<<i) == cell)
  97                                        break;
  98                        if (i >= 32) {
  99                                fprintf(stderr, "cell must be 2^n\n");
 100                                return -1;
 101                        }
 102                        cell_log = i;
 103                } else if (matches(*argv, "avpkt") == 0) {
 104                        NEXT_ARG();
 105                        if (get_size(&avpkt, *argv)) {
 106                                explain1("avpkt");
 107                                return -1;
 108                        }
 109                } else if (matches(*argv, "mpu") == 0) {
 110                        NEXT_ARG();
 111                        if (get_size(&mpu, *argv)) {
 112                                explain1("mpu");
 113                                return -1;
 114                        }
 115                } else if (matches(*argv, "allot") == 0) {
 116                        NEXT_ARG();
 117                        /* Accept and ignore "allot" for backward compatibility */
 118                        if (get_size(&allot, *argv)) {
 119                                explain1("allot");
 120                                return -1;
 121                        }
 122                } else if (matches(*argv, "overhead") == 0) {
 123                        NEXT_ARG();
 124                        if (get_u16(&overhead, *argv, 10)) {
 125                                explain1("overhead"); return -1;
 126                        }
 127                } else if (matches(*argv, "linklayer") == 0) {
 128                        NEXT_ARG();
 129                        if (get_linklayer(&linklayer, *argv)) {
 130                                explain1("linklayer"); return -1;
 131                        }
 132                } else if (matches(*argv, "help") == 0) {
 133                        explain();
 134                        return -1;
 135                } else {
 136                        fprintf(stderr, "What is \"%s\"?\n", *argv);
 137                        explain();
 138                        return -1;
 139                }
 140                argc--; argv++;
 141        }
 142
 143        /* OK. All options are parsed. */
 144
 145        if (r.rate == 0) {
 146                fprintf(stderr, "CBQ: bandwidth is required parameter.\n");
 147                return -1;
 148        }
 149        if (avpkt == 0) {
 150                fprintf(stderr, "CBQ: \"avpkt\" is required.\n");
 151                return -1;
 152        }
 153        if (allot < (avpkt*3)/2)
 154                allot = (avpkt*3)/2;
 155
 156        r.mpu = mpu;
 157        r.overhead = overhead;
 158        if (tc_calc_rtable(&r, rtab, cell_log, allot, linklayer) < 0) {
 159                fprintf(stderr, "CBQ: failed to calculate rate table.\n");
 160                return -1;
 161        }
 162
 163        if (ewma_log < 0)
 164                ewma_log = TC_CBQ_DEF_EWMA;
 165        lss.ewma_log = ewma_log;
 166        lss.maxidle = tc_calc_xmittime(r.rate, avpkt);
 167        lss.change = TCF_CBQ_LSS_MAXIDLE|TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
 168        lss.avpkt = avpkt;
 169
 170        tail = addattr_nest(n, 1024, TCA_OPTIONS);
 171        addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r));
 172        addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss));
 173        addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024);
 174        if (show_raw) {
 175                int i;
 176
 177                for (i = 0; i < 256; i++)
 178                        printf("%u ", rtab[i]);
 179                printf("\n");
 180        }
 181        addattr_nest_end(n, tail);
 182        return 0;
 183}
 184
 185static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev)
 186{
 187        int wrr_ok = 0, fopt_ok = 0;
 188        struct tc_ratespec r = {};
 189        struct tc_cbq_lssopt lss = {};
 190        struct tc_cbq_wrropt wrr = {};
 191        struct tc_cbq_fopt fopt = {};
 192        __u32 rtab[256];
 193        unsigned mpu = 0;
 194        int cell_log =  -1;
 195        int ewma_log =  -1;
 196        unsigned int bndw = 0;
 197        unsigned minburst = 0, maxburst = 0;
 198        unsigned short overhead = 0;
 199        unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
 200        struct rtattr *tail;
 201
 202        while (argc > 0) {
 203                if (matches(*argv, "rate") == 0) {
 204                        NEXT_ARG();
 205                        if (strchr(*argv, '%')) {
 206                                if (get_percent_rate(&r.rate, *argv, dev)) {
 207                                        explain1("rate");
 208                                        return -1;
 209                                }
 210                        } else if (get_rate(&r.rate, *argv)) {
 211                                explain1("rate");
 212                                return -1;
 213                        }
 214                } else if (matches(*argv, "bandwidth") == 0) {
 215                        NEXT_ARG();
 216                        if (strchr(*argv, '%')) {
 217                                if (get_percent_rate(&bndw, *argv, dev)) {
 218                                        explain1("bandwidth");
 219                                        return -1;
 220                                }
 221                        } else if (get_rate(&bndw, *argv)) {
 222                                explain1("bandwidth");
 223                                return -1;
 224                        }
 225                } else if (matches(*argv, "minidle") == 0) {
 226                        NEXT_ARG();
 227                        if (get_u32(&lss.minidle, *argv, 0)) {
 228                                explain1("minidle");
 229                                return -1;
 230                        }
 231                        lss.change |= TCF_CBQ_LSS_MINIDLE;
 232                } else if (matches(*argv, "minburst") == 0) {
 233                        NEXT_ARG();
 234                        if (get_u32(&minburst, *argv, 0)) {
 235                                explain1("minburst");
 236                                return -1;
 237                        }
 238                        lss.change |= TCF_CBQ_LSS_OFFTIME;
 239                } else if (matches(*argv, "maxburst") == 0) {
 240                        NEXT_ARG();
 241                        if (get_u32(&maxburst, *argv, 0)) {
 242                                explain1("maxburst");
 243                                return -1;
 244                        }
 245                        lss.change |= TCF_CBQ_LSS_MAXIDLE;
 246                } else if (matches(*argv, "bounded") == 0) {
 247                        lss.flags |= TCF_CBQ_LSS_BOUNDED;
 248                        lss.change |= TCF_CBQ_LSS_FLAGS;
 249                } else if (matches(*argv, "borrow") == 0) {
 250                        lss.flags &= ~TCF_CBQ_LSS_BOUNDED;
 251                        lss.change |= TCF_CBQ_LSS_FLAGS;
 252                } else if (matches(*argv, "isolated") == 0) {
 253                        lss.flags |= TCF_CBQ_LSS_ISOLATED;
 254                        lss.change |= TCF_CBQ_LSS_FLAGS;
 255                } else if (matches(*argv, "sharing") == 0) {
 256                        lss.flags &= ~TCF_CBQ_LSS_ISOLATED;
 257                        lss.change |= TCF_CBQ_LSS_FLAGS;
 258                } else if (matches(*argv, "ewma") == 0) {
 259                        NEXT_ARG();
 260                        if (get_integer(&ewma_log, *argv, 0)) {
 261                                explain1("ewma");
 262                                return -1;
 263                        }
 264                        if (ewma_log > 31) {
 265                                fprintf(stderr, "ewma_log must be < 32\n");
 266                                return -1;
 267                        }
 268                        lss.change |= TCF_CBQ_LSS_EWMA;
 269                } else if (matches(*argv, "cell") == 0) {
 270                        unsigned int cell;
 271                        int i;
 272
 273                        NEXT_ARG();
 274                        if (get_size(&cell, *argv)) {
 275                                explain1("cell");
 276                                return -1;
 277                        }
 278                        for (i = 0; i < 32; i++)
 279                                if ((1<<i) == cell)
 280                                        break;
 281                        if (i >= 32) {
 282                                fprintf(stderr, "cell must be 2^n\n");
 283                                return -1;
 284                        }
 285                        cell_log = i;
 286                } else if (matches(*argv, "prio") == 0) {
 287                        unsigned int prio;
 288
 289                        NEXT_ARG();
 290                        if (get_u32(&prio, *argv, 0)) {
 291                                explain1("prio");
 292                                return -1;
 293                        }
 294                        if (prio > TC_CBQ_MAXPRIO) {
 295                                fprintf(stderr, "\"prio\" must be number in the range 1...%d\n", TC_CBQ_MAXPRIO);
 296                                return -1;
 297                        }
 298                        wrr.priority = prio;
 299                        wrr_ok++;
 300                } else if (matches(*argv, "allot") == 0) {
 301                        NEXT_ARG();
 302                        if (get_size(&wrr.allot, *argv)) {
 303                                explain1("allot");
 304                                return -1;
 305                        }
 306                } else if (matches(*argv, "avpkt") == 0) {
 307                        NEXT_ARG();
 308                        if (get_size(&lss.avpkt, *argv)) {
 309                                explain1("avpkt");
 310                                return -1;
 311                        }
 312                        lss.change |= TCF_CBQ_LSS_AVPKT;
 313                } else if (matches(*argv, "mpu") == 0) {
 314                        NEXT_ARG();
 315                        if (get_size(&mpu, *argv)) {
 316                                explain1("mpu");
 317                                return -1;
 318                        }
 319                } else if (matches(*argv, "weight") == 0) {
 320                        NEXT_ARG();
 321                        if (get_size(&wrr.weight, *argv)) {
 322                                explain1("weight");
 323                                return -1;
 324                        }
 325                        wrr_ok++;
 326                } else if (matches(*argv, "split") == 0) {
 327                        NEXT_ARG();
 328                        if (get_tc_classid(&fopt.split, *argv)) {
 329                                fprintf(stderr, "Invalid split node ID.\n");
 330                                return -1;
 331                        }
 332                        fopt_ok++;
 333                } else if (matches(*argv, "defmap") == 0) {
 334                        int err;
 335
 336                        NEXT_ARG();
 337                        err = sscanf(*argv, "%08x/%08x", &fopt.defmap, &fopt.defchange);
 338                        if (err < 1) {
 339                                fprintf(stderr, "Invalid defmap, should be MASK32[/MASK]\n");
 340                                return -1;
 341                        }
 342                        if (err == 1)
 343                                fopt.defchange = ~0;
 344                        fopt_ok++;
 345                } else if (matches(*argv, "overhead") == 0) {
 346                        NEXT_ARG();
 347                        if (get_u16(&overhead, *argv, 10)) {
 348                                explain1("overhead"); return -1;
 349                        }
 350                } else if (matches(*argv, "linklayer") == 0) {
 351                        NEXT_ARG();
 352                        if (get_linklayer(&linklayer, *argv)) {
 353                                explain1("linklayer"); return -1;
 354                        }
 355                } else if (matches(*argv, "help") == 0) {
 356                        explain_class();
 357                        return -1;
 358                } else {
 359                        fprintf(stderr, "What is \"%s\"?\n", *argv);
 360                        explain_class();
 361                        return -1;
 362                }
 363                argc--; argv++;
 364        }
 365
 366        /* OK. All options are parsed. */
 367
 368        /* 1. Prepare link sharing scheduler parameters */
 369        if (r.rate) {
 370                unsigned int pktsize = wrr.allot;
 371
 372                if (wrr.allot < (lss.avpkt*3)/2)
 373                        wrr.allot = (lss.avpkt*3)/2;
 374                r.mpu = mpu;
 375                r.overhead = overhead;
 376                if (tc_calc_rtable(&r, rtab, cell_log, pktsize, linklayer) < 0) {
 377                        fprintf(stderr, "CBQ: failed to calculate rate table.\n");
 378                        return -1;
 379                }
 380        }
 381        if (ewma_log < 0)
 382                ewma_log = TC_CBQ_DEF_EWMA;
 383        lss.ewma_log = ewma_log;
 384        if (lss.change&(TCF_CBQ_LSS_OFFTIME|TCF_CBQ_LSS_MAXIDLE)) {
 385                if (lss.avpkt == 0) {
 386                        fprintf(stderr, "CBQ: avpkt is required for max/minburst.\n");
 387                        return -1;
 388                }
 389                if (bndw == 0 || r.rate == 0) {
 390                        fprintf(stderr, "CBQ: bandwidth&rate are required for max/minburst.\n");
 391                        return -1;
 392                }
 393        }
 394        if (wrr.priority == 0 && (n->nlmsg_flags&NLM_F_EXCL)) {
 395                wrr_ok = 1;
 396                wrr.priority = TC_CBQ_MAXPRIO;
 397                if (wrr.allot == 0)
 398                        wrr.allot = (lss.avpkt*3)/2;
 399        }
 400        if (wrr_ok) {
 401                if (wrr.weight == 0)
 402                        wrr.weight = (wrr.priority == TC_CBQ_MAXPRIO) ? 1 : r.rate;
 403                if (wrr.allot == 0) {
 404                        fprintf(stderr, "CBQ: \"allot\" is required to set WRR parameters.\n");
 405                        return -1;
 406                }
 407        }
 408        if (lss.change&TCF_CBQ_LSS_MAXIDLE) {
 409                lss.maxidle = tc_cbq_calc_maxidle(bndw, r.rate, lss.avpkt, ewma_log, maxburst);
 410                lss.change |= TCF_CBQ_LSS_MAXIDLE;
 411                lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
 412        }
 413        if (lss.change&TCF_CBQ_LSS_OFFTIME) {
 414                lss.offtime = tc_cbq_calc_offtime(bndw, r.rate, lss.avpkt, ewma_log, minburst);
 415                lss.change |= TCF_CBQ_LSS_OFFTIME;
 416                lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
 417        }
 418        if (lss.change&TCF_CBQ_LSS_MINIDLE) {
 419                lss.minidle <<= lss.ewma_log;
 420                lss.change |= TCF_CBQ_LSS_EWMA;
 421        }
 422
 423        tail = addattr_nest(n, 1024, TCA_OPTIONS);
 424        if (lss.change) {
 425                lss.change |= TCF_CBQ_LSS_FLAGS;
 426                addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss));
 427        }
 428        if (wrr_ok)
 429                addattr_l(n, 1024, TCA_CBQ_WRROPT, &wrr, sizeof(wrr));
 430        if (fopt_ok)
 431                addattr_l(n, 1024, TCA_CBQ_FOPT, &fopt, sizeof(fopt));
 432        if (r.rate) {
 433                addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r));
 434                addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024);
 435                if (show_raw) {
 436                        int i;
 437
 438                        for (i = 0; i < 256; i++)
 439                                printf("%u ", rtab[i]);
 440                        printf("\n");
 441                }
 442        }
 443        addattr_nest_end(n, tail);
 444        return 0;
 445}
 446
 447
 448static int cbq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 449{
 450        struct rtattr *tb[TCA_CBQ_MAX+1];
 451        struct tc_ratespec *r = NULL;
 452        struct tc_cbq_lssopt *lss = NULL;
 453        struct tc_cbq_wrropt *wrr = NULL;
 454        struct tc_cbq_fopt *fopt = NULL;
 455        struct tc_cbq_ovl *ovl = NULL;
 456        unsigned int linklayer;
 457
 458        SPRINT_BUF(b1);
 459        SPRINT_BUF(b2);
 460
 461        if (opt == NULL)
 462                return 0;
 463
 464        parse_rtattr_nested(tb, TCA_CBQ_MAX, opt);
 465
 466        if (tb[TCA_CBQ_RATE]) {
 467                if (RTA_PAYLOAD(tb[TCA_CBQ_RATE]) < sizeof(*r))
 468                        fprintf(stderr, "CBQ: too short rate opt\n");
 469                else
 470                        r = RTA_DATA(tb[TCA_CBQ_RATE]);
 471        }
 472        if (tb[TCA_CBQ_LSSOPT]) {
 473                if (RTA_PAYLOAD(tb[TCA_CBQ_LSSOPT]) < sizeof(*lss))
 474                        fprintf(stderr, "CBQ: too short lss opt\n");
 475                else
 476                        lss = RTA_DATA(tb[TCA_CBQ_LSSOPT]);
 477        }
 478        if (tb[TCA_CBQ_WRROPT]) {
 479                if (RTA_PAYLOAD(tb[TCA_CBQ_WRROPT]) < sizeof(*wrr))
 480                        fprintf(stderr, "CBQ: too short wrr opt\n");
 481                else
 482                        wrr = RTA_DATA(tb[TCA_CBQ_WRROPT]);
 483        }
 484        if (tb[TCA_CBQ_FOPT]) {
 485                if (RTA_PAYLOAD(tb[TCA_CBQ_FOPT]) < sizeof(*fopt))
 486                        fprintf(stderr, "CBQ: too short fopt\n");
 487                else
 488                        fopt = RTA_DATA(tb[TCA_CBQ_FOPT]);
 489        }
 490        if (tb[TCA_CBQ_OVL_STRATEGY]) {
 491                if (RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl))
 492                        fprintf(stderr, "CBQ: too short overlimit strategy %u/%u\n",
 493                                (unsigned int) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]),
 494                                (unsigned int) sizeof(*ovl));
 495                else
 496                        ovl = RTA_DATA(tb[TCA_CBQ_OVL_STRATEGY]);
 497        }
 498
 499        if (r) {
 500                tc_print_rate(PRINT_FP, NULL, "rate %s ", r->rate);
 501                linklayer = (r->linklayer & TC_LINKLAYER_MASK);
 502                if (linklayer > TC_LINKLAYER_ETHERNET || show_details)
 503                        fprintf(f, "linklayer %s ", sprint_linklayer(linklayer, b2));
 504                if (show_details) {
 505                        fprintf(f, "cell %ub ", 1<<r->cell_log);
 506                        if (r->mpu)
 507                                fprintf(f, "mpu %ub ", r->mpu);
 508                        if (r->overhead)
 509                                fprintf(f, "overhead %ub ", r->overhead);
 510                }
 511        }
 512        if (lss && lss->flags) {
 513                int comma = 0;
 514
 515                fprintf(f, "(");
 516                if (lss->flags&TCF_CBQ_LSS_BOUNDED) {
 517                        fprintf(f, "bounded");
 518                        comma = 1;
 519                }
 520                if (lss->flags&TCF_CBQ_LSS_ISOLATED) {
 521                        if (comma)
 522                                fprintf(f, ",");
 523                        fprintf(f, "isolated");
 524                }
 525                fprintf(f, ") ");
 526        }
 527        if (wrr) {
 528                if (wrr->priority != TC_CBQ_MAXPRIO)
 529                        fprintf(f, "prio %u", wrr->priority);
 530                else
 531                        fprintf(f, "prio no-transmit");
 532                if (show_details) {
 533                        fprintf(f, "/%u ", wrr->cpriority);
 534                        if (wrr->weight != 1)
 535                                tc_print_rate(PRINT_FP, NULL, "weight %s ",
 536                                              wrr->weight);
 537                        if (wrr->allot)
 538                                fprintf(f, "allot %ub ", wrr->allot);
 539                }
 540        }
 541        if (lss && show_details) {
 542                fprintf(f, "\nlevel %u ewma %u avpkt %ub ", lss->level, lss->ewma_log, lss->avpkt);
 543                if (lss->maxidle) {
 544                        fprintf(f, "maxidle %s ", sprint_ticks(lss->maxidle>>lss->ewma_log, b1));
 545                        if (show_raw)
 546                                fprintf(f, "[%08x] ", lss->maxidle);
 547                }
 548                if (lss->minidle != 0x7fffffff) {
 549                        fprintf(f, "minidle %s ", sprint_ticks(lss->minidle>>lss->ewma_log, b1));
 550                        if (show_raw)
 551                                fprintf(f, "[%08x] ", lss->minidle);
 552                }
 553                if (lss->offtime) {
 554                        fprintf(f, "offtime %s ", sprint_ticks(lss->offtime, b1));
 555                        if (show_raw)
 556                                fprintf(f, "[%08x] ", lss->offtime);
 557                }
 558        }
 559        if (fopt && show_details) {
 560                char buf[64];
 561
 562                print_tc_classid(buf, sizeof(buf), fopt->split);
 563                fprintf(f, "\nsplit %s ", buf);
 564                if (fopt->defmap) {
 565                        fprintf(f, "defmap %08x", fopt->defmap);
 566                }
 567        }
 568        return 0;
 569}
 570
 571static int cbq_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats)
 572{
 573        struct tc_cbq_xstats *st;
 574
 575        if (xstats == NULL)
 576                return 0;
 577
 578        if (RTA_PAYLOAD(xstats) < sizeof(*st))
 579                return -1;
 580
 581        st = RTA_DATA(xstats);
 582        fprintf(f, "  borrowed %u overactions %u avgidle %g undertime %g", st->borrows,
 583                st->overactions, (double)st->avgidle, (double)st->undertime);
 584        return 0;
 585}
 586
 587struct qdisc_util cbq_qdisc_util = {
 588        .id             = "cbq",
 589        .parse_qopt     = cbq_parse_opt,
 590        .print_qopt     = cbq_print_opt,
 591        .print_xstats   = cbq_print_xstats,
 592        .parse_copt     = cbq_parse_class_opt,
 593        .print_copt     = cbq_print_opt,
 594};
 595