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
31static 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 */
49char *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 */
62int nl_str2ovl_strategy(const char *name)
63{
64	return __str2type(name, ovl_strategies, ARRAY_SIZE(ovl_strategies));
65}
66
67static 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
76static inline struct rtnl_cbq *cbq_qdisc(struct rtnl_tca *tca)
77{
78	return (struct rtnl_cbq *) tca->tc_subdata;
79}
80
81static 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
90static 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
116static int cbq_qdisc_msg_parser(struct rtnl_qdisc *qdisc)
117{
118	return cbq_msg_parser((struct rtnl_tca *) qdisc);
119}
120
121static int cbq_class_msg_parser(struct rtnl_class *class)
122{
123	return cbq_msg_parser((struct rtnl_tca *) class);
124}
125
126static void cbq_qdisc_free_data(struct rtnl_qdisc *qdisc)
127{
128	free(qdisc->q_subdata);
129}
130
131static 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
141static 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
146static void cbq_class_free_data(struct rtnl_class *class)
147{
148	free(class->c_subdata);
149}
150
151static 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
156static 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
173static 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
179static 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
185static 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
225static 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
231static 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
237static 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
250static 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
256static 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
262static 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
274static 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
286static void __init cbq_init(void)
287{
288	rtnl_qdisc_register(&cbq_qdisc_ops);
289	rtnl_class_register(&cbq_class_ops);
290}
291
292static void __exit cbq_exit(void)
293{
294	rtnl_qdisc_unregister(&cbq_qdisc_ops);
295	rtnl_class_unregister(&cbq_class_ops);
296}
297
298/** @} */
299