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