• 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 	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 
cbq_parse_class_opt(struct qdisc_util * qu,int argc,char ** argv,struct nlmsghdr * n)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 
cbq_print_opt(struct qdisc_util * qu,FILE * f,struct rtattr * opt)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 
cbq_print_xstats(struct qdisc_util * qu,FILE * f,struct rtattr * xstats)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