1 /*
2  * lib/route/sch/cbq.c	Class Based Queueing
3  *
4  *	This library is free software; you can redistribute it and/or
5  *	modify it under the terms of the GNU Lesser General Public
6  *	License as published by the Free Software Foundation version 2.1
7  *	of the License.
8  *
9  * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
10  */
11 
12 #include <netlink-local.h>
13 #include <netlink-tc.h>
14 #include <netlink/netlink.h>
15 #include <netlink/utils.h>
16 #include <netlink/route/qdisc.h>
17 #include <netlink/route/qdisc-modules.h>
18 #include <netlink/route/class.h>
19 #include <netlink/route/class-modules.h>
20 #include <netlink/route/link.h>
21 #include <netlink/route/sch/cbq.h>
22 #include <netlink/route/cls/police.h>
23 
24 /**
25  * @ingroup qdisc_api
26  * @ingroup class_api
27  * @defgroup cbq Class Based Queueing (CBQ)
28  * @{
29  */
30 
31 static struct trans_tbl ovl_strategies[] = {
32 	__ADD(TC_CBQ_OVL_CLASSIC,classic)
33 	__ADD(TC_CBQ_OVL_DELAY,delay)
34 	__ADD(TC_CBQ_OVL_LOWPRIO,lowprio)
35 	__ADD(TC_CBQ_OVL_DROP,drop)
36 	__ADD(TC_CBQ_OVL_RCLASSIC,rclassic)
37 };
38 
39 /**
40  * Convert a CBQ OVL strategy to a character string
41  * @arg type		CBQ OVL strategy
42  * @arg buf		destination buffer
43  * @arg len		length of destination buffer
44  *
45  * Converts a CBQ OVL strategy to a character string and stores in the
46  * provided buffer. Returns the destination buffer or the type
47  * encoded in hex if no match was found.
48  */
nl_ovl_strategy2str(int type,char * buf,size_t len)49 char *nl_ovl_strategy2str(int type, char *buf, size_t len)
50 {
51 	return __type2str(type, buf, len, ovl_strategies,
52 			    ARRAY_SIZE(ovl_strategies));
53 }
54 
55 /**
56  * Convert a string to a CBQ OVL strategy
57  * @arg name		CBQ OVL stragegy name
58  *
59  * Converts a CBQ OVL stragegy name to it's corresponding CBQ OVL strategy
60  * type. Returns the type or -1 if none was found.
61  */
nl_str2ovl_strategy(const char * name)62 int nl_str2ovl_strategy(const char *name)
63 {
64 	return __str2type(name, ovl_strategies, ARRAY_SIZE(ovl_strategies));
65 }
66 
67 static struct nla_policy cbq_policy[TCA_CBQ_MAX+1] = {
68 	[TCA_CBQ_LSSOPT]	= { .minlen = sizeof(struct tc_cbq_lssopt) },
69 	[TCA_CBQ_RATE]		= { .minlen = sizeof(struct tc_ratespec) },
70 	[TCA_CBQ_WRROPT]	= { .minlen = sizeof(struct tc_cbq_wrropt) },
71 	[TCA_CBQ_OVL_STRATEGY]	= { .minlen = sizeof(struct tc_cbq_ovl) },
72 	[TCA_CBQ_FOPT]		= { .minlen = sizeof(struct tc_cbq_fopt) },
73 	[TCA_CBQ_POLICE]	= { .minlen = sizeof(struct tc_cbq_police) },
74 };
75 
cbq_qdisc(struct rtnl_tca * tca)76 static inline struct rtnl_cbq *cbq_qdisc(struct rtnl_tca *tca)
77 {
78 	return (struct rtnl_cbq *) tca->tc_subdata;
79 }
80 
cbq_alloc(struct rtnl_tca * tca)81 static inline struct rtnl_cbq *cbq_alloc(struct rtnl_tca *tca)
82 {
83 	if (!tca->tc_subdata)
84 		tca->tc_subdata = calloc(1, sizeof(struct rtnl_qdisc));
85 
86 	return cbq_qdisc(tca);
87 }
88 
89 
cbq_msg_parser(struct rtnl_tca * tca)90 static int cbq_msg_parser(struct rtnl_tca *tca)
91 {
92 	struct nlattr *tb[TCA_CBQ_MAX + 1];
93 	struct rtnl_cbq *cbq;
94 	int err;
95 
96 	err = tca_parse(tb, TCA_CBQ_MAX, tca, cbq_policy);
97 	if (err < 0)
98 		return err;
99 
100 	cbq = cbq_alloc(tca);
101 	if (!cbq)
102 		return -NLE_NOMEM;
103 
104 	nla_memcpy(&cbq->cbq_lss, tb[TCA_CBQ_LSSOPT], sizeof(cbq->cbq_lss));
105 	nla_memcpy(&cbq->cbq_rate, tb[TCA_CBQ_RATE], sizeof(cbq->cbq_rate));
106 	nla_memcpy(&cbq->cbq_wrr, tb[TCA_CBQ_WRROPT], sizeof(cbq->cbq_wrr));
107 	nla_memcpy(&cbq->cbq_fopt, tb[TCA_CBQ_FOPT], sizeof(cbq->cbq_fopt));
108 	nla_memcpy(&cbq->cbq_ovl, tb[TCA_CBQ_OVL_STRATEGY],
109 		   sizeof(cbq->cbq_ovl));
110 	nla_memcpy(&cbq->cbq_police, tb[TCA_CBQ_POLICE],
111 		    sizeof(cbq->cbq_police));
112 
113 	return 0;
114 }
115 
cbq_qdisc_msg_parser(struct rtnl_qdisc * qdisc)116 static int cbq_qdisc_msg_parser(struct rtnl_qdisc *qdisc)
117 {
118 	return cbq_msg_parser((struct rtnl_tca *) qdisc);
119 }
120 
cbq_class_msg_parser(struct rtnl_class * class)121 static int cbq_class_msg_parser(struct rtnl_class *class)
122 {
123 	return cbq_msg_parser((struct rtnl_tca *) class);
124 }
125 
cbq_qdisc_free_data(struct rtnl_qdisc * qdisc)126 static void cbq_qdisc_free_data(struct rtnl_qdisc *qdisc)
127 {
128 	free(qdisc->q_subdata);
129 }
130 
cbq_clone(struct rtnl_tca * _dst,struct rtnl_tca * _src)131 static int cbq_clone(struct rtnl_tca *_dst, struct rtnl_tca *_src)
132 {
133 	struct rtnl_cbq *src = cbq_qdisc(_src);
134 
135 	if (src && !cbq_alloc(_dst))
136 		return -NLE_NOMEM;
137 	else
138 		return 0;
139 }
140 
cbq_qdisc_clone(struct rtnl_qdisc * dst,struct rtnl_qdisc * src)141 static int cbq_qdisc_clone(struct rtnl_qdisc *dst, struct rtnl_qdisc *src)
142 {
143 	return cbq_clone((struct rtnl_tca *) dst, (struct rtnl_tca *) src);
144 }
145 
cbq_class_free_data(struct rtnl_class * class)146 static void cbq_class_free_data(struct rtnl_class *class)
147 {
148 	free(class->c_subdata);
149 }
150 
cbq_class_clone(struct rtnl_class * dst,struct rtnl_class * src)151 static int cbq_class_clone(struct rtnl_class *dst, struct rtnl_class *src)
152 {
153 	return cbq_clone((struct rtnl_tca *) dst, (struct rtnl_tca *) src);
154 }
155 
cbq_dump_line(struct rtnl_tca * tca,struct nl_dump_params * p)156 static void cbq_dump_line(struct rtnl_tca *tca, struct nl_dump_params *p)
157 {
158 	struct rtnl_cbq *cbq;
159 	double r, rbit;
160 	char *ru, *rubit;
161 
162 	cbq = cbq_qdisc(tca);
163 	if (!cbq)
164 		return;
165 
166 	r = nl_cancel_down_bytes(cbq->cbq_rate.rate, &ru);
167 	rbit = nl_cancel_down_bits(cbq->cbq_rate.rate * 8, &rubit);
168 
169 	nl_dump(p, " rate %.2f%s/s (%.0f%s) prio %u",
170 		r, ru, rbit, rubit, cbq->cbq_wrr.priority);
171 }
172 
cbq_qdisc_dump_line(struct rtnl_qdisc * qdisc,struct nl_dump_params * p)173 static void cbq_qdisc_dump_line(struct rtnl_qdisc *qdisc,
174 				struct nl_dump_params *p)
175 {
176 	cbq_dump_line((struct rtnl_tca *) qdisc, p);
177 }
178 
cbq_class_dump_line(struct rtnl_class * class,struct nl_dump_params * p)179 static void cbq_class_dump_line(struct rtnl_class *class,
180 				struct nl_dump_params *p)
181 {
182 	cbq_dump_line((struct rtnl_tca *) class, p);
183 }
184 
cbq_dump_details(struct rtnl_tca * tca,struct nl_dump_params * p)185 static void cbq_dump_details(struct rtnl_tca *tca, struct nl_dump_params *p)
186 {
187 	struct rtnl_cbq *cbq;
188 	char *unit, buf[32];
189 	double w;
190 	uint32_t el;
191 
192 	cbq = cbq_qdisc(tca);
193 	if (!cbq)
194 		return;
195 
196 	w = nl_cancel_down_bits(cbq->cbq_wrr.weight * 8, &unit);
197 
198 	nl_dump(p, "avgpkt %u mpu %u cell %u allot %u weight %.0f%s\n",
199 		cbq->cbq_lss.avpkt,
200 		cbq->cbq_rate.mpu,
201 		1 << cbq->cbq_rate.cell_log,
202 		cbq->cbq_wrr.allot, w, unit);
203 
204 	el = cbq->cbq_lss.ewma_log;
205 	nl_dump_line(p, "  minidle %uus maxidle %uus offtime "
206 				"%uus level %u ewma_log %u\n",
207 		nl_ticks2us(cbq->cbq_lss.minidle >> el),
208 		nl_ticks2us(cbq->cbq_lss.maxidle >> el),
209 		nl_ticks2us(cbq->cbq_lss.offtime >> el),
210 		cbq->cbq_lss.level,
211 		cbq->cbq_lss.ewma_log);
212 
213 	nl_dump_line(p, "  penalty %uus strategy %s ",
214 		nl_ticks2us(cbq->cbq_ovl.penalty),
215 		nl_ovl_strategy2str(cbq->cbq_ovl.strategy, buf, sizeof(buf)));
216 
217 	nl_dump(p, "split %s defmap 0x%08x ",
218 		rtnl_tc_handle2str(cbq->cbq_fopt.split, buf, sizeof(buf)),
219 		cbq->cbq_fopt.defmap);
220 
221 	nl_dump(p, "police %s",
222 		nl_police2str(cbq->cbq_police.police, buf, sizeof(buf)));
223 }
224 
cbq_qdisc_dump_details(struct rtnl_qdisc * qdisc,struct nl_dump_params * p)225 static void cbq_qdisc_dump_details(struct rtnl_qdisc *qdisc,
226 				   struct nl_dump_params *p)
227 {
228 	cbq_dump_details((struct rtnl_tca *) qdisc, p);
229 }
230 
cbq_class_dump_details(struct rtnl_class * class,struct nl_dump_params * p)231 static void cbq_class_dump_details(struct rtnl_class *class,
232 				   struct nl_dump_params *p)
233 {
234 	cbq_dump_details((struct rtnl_tca *) class, p);
235 }
236 
cbq_dump_stats(struct rtnl_tca * tca,struct nl_dump_params * p)237 static void cbq_dump_stats(struct rtnl_tca *tca, struct nl_dump_params *p)
238 {
239 	struct tc_cbq_xstats *x = tca_xstats(tca);
240 
241 	if (!x)
242 		return;
243 
244 	nl_dump_line(p, "            borrows    overact  "
245 			"  avgidle  undertime\n");
246 	nl_dump_line(p, "         %10u %10u %10u %10u\n",
247 		     x->borrows, x->overactions, x->avgidle, x->undertime);
248 }
249 
cbq_qdisc_dump_stats(struct rtnl_qdisc * qdisc,struct nl_dump_params * p)250 static void cbq_qdisc_dump_stats(struct rtnl_qdisc *qdisc,
251 				 struct nl_dump_params *p)
252 {
253 	cbq_dump_stats((struct rtnl_tca *) qdisc, p);
254 }
255 
cbq_class_dump_stats(struct rtnl_class * class,struct nl_dump_params * p)256 static void cbq_class_dump_stats(struct rtnl_class *class,
257 				 struct nl_dump_params *p)
258 {
259 	cbq_dump_stats((struct rtnl_tca *) class, p);
260 }
261 
262 static struct rtnl_qdisc_ops cbq_qdisc_ops = {
263 	.qo_kind		= "cbq",
264 	.qo_msg_parser		= cbq_qdisc_msg_parser,
265 	.qo_free_data		= cbq_qdisc_free_data,
266 	.qo_clone		= cbq_qdisc_clone,
267 	.qo_dump = {
268 	    [NL_DUMP_LINE]	= cbq_qdisc_dump_line,
269 	    [NL_DUMP_DETAILS]	= cbq_qdisc_dump_details,
270 	    [NL_DUMP_STATS]	= cbq_qdisc_dump_stats,
271 	},
272 };
273 
274 static struct rtnl_class_ops cbq_class_ops = {
275 	.co_kind		= "cbq",
276 	.co_msg_parser		= cbq_class_msg_parser,
277 	.co_free_data		= cbq_class_free_data,
278 	.co_clone		= cbq_class_clone,
279 	.co_dump = {
280 	    [NL_DUMP_LINE]	= cbq_class_dump_line,
281 	    [NL_DUMP_DETAILS]	= cbq_class_dump_details,
282 	    [NL_DUMP_STATS]	= cbq_class_dump_stats,
283 	},
284 };
285 
cbq_init(void)286 static void __init cbq_init(void)
287 {
288 	rtnl_qdisc_register(&cbq_qdisc_ops);
289 	rtnl_class_register(&cbq_class_ops);
290 }
291 
cbq_exit(void)292 static void __exit cbq_exit(void)
293 {
294 	rtnl_qdisc_unregister(&cbq_qdisc_ops);
295 	rtnl_class_unregister(&cbq_class_ops);
296 }
297 
298 /** @} */
299