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