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