1/*
2 * lib/route/qdisc/fq_codel.c		fq_codel
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) 2013 Cong Wang <xiyou.wangcong@gmail.com>
10 */
11
12/**
13 * @ingroup qdisc
14 * @defgroup qdisc_fq_codel Fair Queue CoDel
15 * @brief
16 *
17 * @{
18 */
19
20#include <netlink-private/netlink.h>
21#include <netlink-private/tc.h>
22#include <netlink/netlink.h>
23#include <netlink-private/route/tc-api.h>
24#include <netlink/route/qdisc.h>
25#include <netlink/route/qdisc/fq_codel.h>
26#include <netlink/utils.h>
27
28/** @cond SKIP */
29#define SCH_FQ_CODEL_ATTR_TARGET	0x1
30#define SCH_FQ_CODEL_ATTR_LIMIT		0x2
31#define SCH_FQ_CODEL_ATTR_INTERVAL	0x4
32#define SCH_FQ_CODEL_ATTR_FLOWS		0x8
33#define SCH_FQ_CODEL_ATTR_QUANTUM	0x10
34#define SCH_FQ_CODEL_ATTR_ECN		0x20
35/** @endcond */
36
37static struct nla_policy fq_codel_policy[TCA_FQ_CODEL_MAX + 1] = {
38	[TCA_FQ_CODEL_TARGET]   = { .type = NLA_U32 },
39	[TCA_FQ_CODEL_LIMIT]    = { .type = NLA_U32 },
40	[TCA_FQ_CODEL_INTERVAL] = { .type = NLA_U32 },
41	[TCA_FQ_CODEL_ECN]      = { .type = NLA_U32 },
42	[TCA_FQ_CODEL_FLOWS]    = { .type = NLA_U32 },
43	[TCA_FQ_CODEL_QUANTUM]  = { .type = NLA_U32 },
44};
45
46static int fq_codel_msg_parser(struct rtnl_tc *tc, void *data)
47{
48	struct rtnl_fq_codel *fq_codel = data;
49	struct nlattr *tb[TCA_FQ_CODEL_MAX + 1];
50	int err;
51
52	err = tca_parse(tb, TCA_FQ_CODEL_MAX, tc, fq_codel_policy);
53	if (err < 0)
54		return err;
55
56	if (tb[TCA_FQ_CODEL_TARGET]) {
57		fq_codel->fq_target =  nla_get_u32(tb[TCA_FQ_CODEL_TARGET]);
58		fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_TARGET;
59	}
60
61	if (tb[TCA_FQ_CODEL_INTERVAL]) {
62		fq_codel->fq_interval =  nla_get_u32(tb[TCA_FQ_CODEL_INTERVAL]);
63		fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_INTERVAL;
64	}
65
66	if (tb[TCA_FQ_CODEL_LIMIT]) {
67		fq_codel->fq_limit =  nla_get_u32(tb[TCA_FQ_CODEL_LIMIT]);
68		fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_LIMIT;
69	}
70
71	if (tb[TCA_FQ_CODEL_QUANTUM]) {
72		fq_codel->fq_quantum =  nla_get_u32(tb[TCA_FQ_CODEL_QUANTUM]);
73		fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_QUANTUM;
74	}
75
76	if (tb[TCA_FQ_CODEL_FLOWS]) {
77		fq_codel->fq_flows =  nla_get_u32(tb[TCA_FQ_CODEL_FLOWS]);
78		fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_FLOWS;
79	}
80
81	if (tb[TCA_FQ_CODEL_ECN]) {
82		fq_codel->fq_ecn =  nla_get_u32(tb[TCA_FQ_CODEL_ECN]);
83		fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_ECN;
84	}
85
86	return 0;
87}
88
89static void fq_codel_dump_line(struct rtnl_tc *tc, void *data,
90			    struct nl_dump_params *p)
91{
92	struct rtnl_fq_codel *fq_codel = data;
93
94	if (!fq_codel)
95		return;
96
97	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_LIMIT)
98		nl_dump(p, " limit %u packets", fq_codel->fq_limit);
99	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_TARGET)
100		nl_dump(p, " target %u", fq_codel->fq_target);
101	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_INTERVAL)
102		nl_dump(p, " interval %u", fq_codel->fq_interval);
103	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_ECN)
104		nl_dump(p, " ecn %u", fq_codel->fq_ecn);
105	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_FLOWS)
106		nl_dump(p, " flows %u", fq_codel->fq_flows);
107	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_QUANTUM)
108		nl_dump(p, " quantum %u", fq_codel->fq_quantum);
109}
110
111static int fq_codel_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
112{
113	struct rtnl_fq_codel *fq_codel = data;
114
115	if (!fq_codel)
116		return -NLE_INVAL;
117
118	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_LIMIT)
119		NLA_PUT_U32(msg, TCA_FQ_CODEL_LIMIT, fq_codel->fq_limit);
120	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_INTERVAL)
121		NLA_PUT_U32(msg, TCA_FQ_CODEL_INTERVAL, fq_codel->fq_interval);
122	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_TARGET)
123		NLA_PUT_U32(msg, TCA_FQ_CODEL_TARGET, fq_codel->fq_target);
124	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_QUANTUM)
125		NLA_PUT_U32(msg, TCA_FQ_CODEL_QUANTUM, fq_codel->fq_quantum);
126	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_FLOWS)
127		NLA_PUT_U32(msg, TCA_FQ_CODEL_FLOWS, fq_codel->fq_flows);
128	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_ECN)
129		NLA_PUT_U32(msg, TCA_FQ_CODEL_ECN, fq_codel->fq_ecn);
130	return 0;
131
132nla_put_failure:
133	return -NLE_MSGSIZE;
134
135}
136
137/**
138 * @name Attribute Modification
139 * @{
140 */
141
142/**
143 * Set limit of fq_codel qdisc.
144 * @arg qdisc		fq_codel qdisc to be modified.
145 * @arg limit		New limit.
146 * @return 0 on success or a negative error code.
147 */
148int rtnl_qdisc_fq_codel_set_limit(struct rtnl_qdisc *qdisc, int limit)
149{
150	struct rtnl_fq_codel *fq_codel;
151
152	if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
153		return -NLE_NOMEM;
154
155	fq_codel->fq_limit = limit;
156	fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_LIMIT;
157
158	return 0;
159}
160
161/**
162 * Get limit of a fq_codel qdisc.
163 * @arg qdisc		fq_codel qdisc.
164 * @return Numeric limit or a negative error code.
165 */
166int rtnl_qdisc_fq_codel_get_limit(struct rtnl_qdisc *qdisc)
167{
168	struct rtnl_fq_codel *fq_codel;
169
170	if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
171		return -NLE_NOMEM;
172
173	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_LIMIT)
174		return fq_codel->fq_limit;
175	else
176		return -NLE_NOATTR;
177}
178
179/**
180 * Set target of fq_codel qdisc.
181 * @arg qdisc		fq_codel qdisc to be modified.
182 * @arg target		New target.
183 * @return 0 on success or a negative error code.
184 */
185int rtnl_qdisc_fq_codel_set_target(struct rtnl_qdisc *qdisc, uint32_t target)
186{
187	struct rtnl_fq_codel *fq_codel;
188
189	if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
190		return -NLE_NOMEM;
191
192	fq_codel->fq_target = target;
193	fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_TARGET;
194
195	return 0;
196}
197
198/**
199 * Get target of a fq_codel qdisc.
200 * @arg qdisc		fq_codel qdisc.
201 * @return Numeric target or zero.
202 */
203uint32_t rtnl_qdisc_fq_codel_get_target(struct rtnl_qdisc *qdisc)
204{
205	struct rtnl_fq_codel *fq_codel;
206
207	if ((fq_codel = rtnl_tc_data(TC_CAST(qdisc))) &&
208	    fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_TARGET)
209		return fq_codel->fq_target;
210	else
211		return 0;
212}
213
214/**
215 * Set interval of fq_codel qdisc.
216 * @arg qdisc		fq_codel qdisc to be modified.
217 * @arg interval	New interval.
218 * @return 0 on success or a negative error code.
219 */
220int rtnl_qdisc_fq_codel_set_interval(struct rtnl_qdisc *qdisc, uint32_t interval)
221{
222	struct rtnl_fq_codel *fq_codel;
223
224	if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
225		return -NLE_NOMEM;
226
227	fq_codel->fq_interval = interval;
228	fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_INTERVAL;
229
230	return 0;
231}
232
233/**
234 * Get target of a fq_codel qdisc.
235 * @arg qdisc		fq_codel qdisc.
236 * @return Numeric interval or zero.
237 */
238uint32_t rtnl_qdisc_fq_codel_get_interval(struct rtnl_qdisc *qdisc)
239{
240	struct rtnl_fq_codel *fq_codel;
241
242	if ((fq_codel = rtnl_tc_data(TC_CAST(qdisc))) &&
243	     fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_INTERVAL)
244		return fq_codel->fq_interval;
245	else
246		return 0;
247}
248
249/**
250 * Set quantum of fq_codel qdisc.
251 * @arg qdisc		fq_codel qdisc to be modified.
252 * @arg quantum		New quantum.
253 * @return 0 on success or a negative error code.
254 */
255int rtnl_qdisc_fq_codel_set_quantum(struct rtnl_qdisc *qdisc, uint32_t quantum)
256{
257	struct rtnl_fq_codel *fq_codel;
258
259	if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
260		return -NLE_NOMEM;
261
262	fq_codel->fq_quantum = quantum;
263	fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_QUANTUM;
264
265	return 0;
266}
267
268/**
269 * Get quantum of a fq_codel qdisc.
270 * @arg qdisc		fq_codel qdisc.
271 * @return Numeric quantum or zero.
272 */
273uint32_t rtnl_qdisc_fq_codel_get_quantum(struct rtnl_qdisc *qdisc)
274{
275	struct rtnl_fq_codel *fq_codel;
276
277	if ((fq_codel = rtnl_tc_data(TC_CAST(qdisc))) &&
278	    (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_QUANTUM))
279		return fq_codel->fq_quantum;
280	else
281		return 0;
282}
283
284/**
285 * Set flows of fq_codel qdisc.
286 * @arg qdisc		fq_codel qdisc to be modified.
287 * @arg flows		New flows value.
288 * @return 0 on success or a negative error code.
289 */
290int rtnl_qdisc_fq_codel_set_flows(struct rtnl_qdisc *qdisc, int flows)
291{
292	struct rtnl_fq_codel *fq_codel;
293
294	if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
295		return -NLE_NOMEM;
296
297	fq_codel->fq_flows = flows;
298	fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_FLOWS;
299
300	return 0;
301}
302
303/**
304 * Get flows of a fq_codel qdisc.
305 * @arg qdisc		fq_codel qdisc.
306 * @return Numeric flows or a negative error code.
307 */
308int rtnl_qdisc_fq_codel_get_flows(struct rtnl_qdisc *qdisc)
309{
310	struct rtnl_fq_codel *fq_codel;
311
312	if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
313		return -NLE_NOMEM;
314
315	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_FLOWS)
316		return fq_codel->fq_flows;
317	else
318		return -NLE_NOATTR;
319}
320/**
321 * Set ecn of fq_codel qdisc.
322 * @arg qdisc		fq_codel qdisc to be modified.
323 * @arg ecn		New ecn value.
324 * @return 0 on success or a negative error code.
325 */
326int rtnl_qdisc_fq_codel_set_ecn(struct rtnl_qdisc *qdisc, int ecn)
327{
328	struct rtnl_fq_codel *fq_codel;
329
330	if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
331		return -NLE_NOMEM;
332
333	fq_codel->fq_ecn = ecn;
334	fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_ECN;
335
336	return 0;
337}
338
339/**
340 * Get ecn of a fq_codel qdisc.
341 * @arg qdisc		fq_codel qdisc.
342 * @return Numeric ecn or a negative error code.
343 */
344int rtnl_qdisc_fq_codel_get_ecn(struct rtnl_qdisc *qdisc)
345{
346	struct rtnl_fq_codel *fq_codel;
347
348	if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
349		return -NLE_NOMEM;
350
351	if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_ECN)
352		return fq_codel->fq_ecn;
353	else
354		return -NLE_NOATTR;
355}
356/** @} */
357
358static struct rtnl_tc_ops fq_codel_ops = {
359	.to_kind		= "fq_codel",
360	.to_type		= RTNL_TC_TYPE_QDISC,
361	.to_size		= sizeof(struct rtnl_fq_codel),
362	.to_msg_parser		= fq_codel_msg_parser,
363	.to_dump[NL_DUMP_LINE]	= fq_codel_dump_line,
364	.to_msg_fill		= fq_codel_msg_fill,
365};
366
367static void __init fq_codel_init(void)
368{
369	rtnl_tc_register(&fq_codel_ops);
370}
371
372static void __exit fq_codel_exit(void)
373{
374	rtnl_tc_unregister(&fq_codel_ops);
375}
376
377/** @} */
378