1/*
2 * lib/route/sch/sfq.c		SFQ Qdisc
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/**
13 * @ingroup qdisc_api
14 * @defgroup sfq Stochastic Fairness Queueing (SFQ)
15 * @brief
16 *
17 * @par Parameter Description
18 * - \b Quantum: Number of bytes to send out per slot and round.
19 * - \b Perturbation: Timer period between changing the hash function.
20 * - \b Limit:  Upper limit of queue in number of packets before SFQ starts
21 *	        dropping packets.
22 * - \b Divisor: Hash table divisor, i.e. size of hash table.
23 * @{
24 */
25
26#include <netlink-local.h>
27#include <netlink-tc.h>
28#include <netlink/netlink.h>
29#include <netlink/utils.h>
30#include <netlink/route/qdisc.h>
31#include <netlink/route/qdisc-modules.h>
32#include <netlink/route/sch/sfq.h>
33
34/** @cond SKIP */
35#define SCH_SFQ_ATTR_QUANTUM	0x01
36#define SCH_SFQ_ATTR_PERTURB	0x02
37#define SCH_SFQ_ATTR_LIMIT	0x04
38#define SCH_SFQ_ATTR_DIVISOR	0x08
39#define SCH_SFQ_ATTR_FLOWS	0x10
40/** @endcond */
41
42static inline struct rtnl_sfq *sfq_qdisc(struct rtnl_qdisc *qdisc)
43{
44	return (struct rtnl_sfq *) qdisc->q_subdata;
45}
46
47static inline struct rtnl_sfq *sfq_alloc(struct rtnl_qdisc *qdisc)
48{
49	if (!qdisc->q_subdata)
50		qdisc->q_subdata = calloc(1, sizeof(struct rtnl_sfq));
51
52	return sfq_qdisc(qdisc);
53}
54
55static int sfq_msg_parser(struct rtnl_qdisc *qdisc)
56{
57	struct rtnl_sfq *sfq;
58	struct tc_sfq_qopt *opts;
59
60	if (!(qdisc->ce_mask & TCA_ATTR_OPTS))
61		return 0;
62
63	if (qdisc->q_opts->d_size < sizeof(*opts))
64		return -NLE_INVAL;
65
66	sfq = sfq_alloc(qdisc);
67	if (!sfq)
68		return -NLE_NOMEM;
69
70	opts = (struct tc_sfq_qopt *) qdisc->q_opts->d_data;
71
72	sfq->qs_quantum = opts->quantum;
73	sfq->qs_perturb = opts->perturb_period;
74	sfq->qs_limit = opts->limit;
75	sfq->qs_divisor = opts->divisor;
76	sfq->qs_flows = opts->flows;
77
78	sfq->qs_mask = (SCH_SFQ_ATTR_QUANTUM | SCH_SFQ_ATTR_PERTURB |
79			SCH_SFQ_ATTR_LIMIT | SCH_SFQ_ATTR_DIVISOR |
80			SCH_SFQ_ATTR_FLOWS);
81
82	return 0;
83}
84
85static void sfq_free_data(struct rtnl_qdisc *qdisc)
86{
87	free(qdisc->q_subdata);
88}
89
90static void sfq_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
91{
92	struct rtnl_sfq *sfq = sfq_qdisc(qdisc);
93
94	if (sfq)
95		nl_dump(p, " quantum %u perturb %us", sfq->qs_quantum,
96			nl_ticks2us(sfq->qs_perturb * nl_get_hz()));
97}
98
99static void sfq_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
100{
101	struct rtnl_sfq *sfq = sfq_qdisc(qdisc);
102
103	if (sfq)
104		nl_dump(p, "limit %u divisor %u",
105			sfq->qs_limit, sfq->qs_divisor);
106}
107
108static struct nl_msg *sfq_get_opts(struct rtnl_qdisc *qdisc)
109{
110	struct rtnl_sfq *sfq;
111	struct tc_sfq_qopt opts;
112	struct nl_msg *msg;
113
114	sfq = sfq_qdisc(qdisc);
115	if (!sfq)
116		return NULL;
117
118	msg = nlmsg_alloc();
119	if (!msg)
120		goto errout;
121
122	memset(&opts, 0, sizeof(opts));
123	opts.quantum = sfq->qs_quantum;
124	opts.perturb_period = sfq->qs_perturb;
125	opts.limit = sfq->qs_limit;
126
127	if (nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD) < 0)
128		goto errout;
129
130	return msg;
131errout:
132	nlmsg_free(msg);
133	return NULL;
134}
135
136/**
137 * @name Attribute Access
138 * @{
139 */
140
141/**
142 * Set quantum of SFQ qdisc.
143 * @arg qdisc		SFQ qdisc to be modified.
144 * @arg quantum		New quantum in bytes.
145 * @return 0 on success or a negative error code.
146 */
147int rtnl_sfq_set_quantum(struct rtnl_qdisc *qdisc, int quantum)
148{
149	struct rtnl_sfq *sfq;
150
151	sfq = sfq_alloc(qdisc);
152	if (!sfq)
153		return -NLE_NOMEM;
154
155	sfq->qs_quantum = quantum;
156	sfq->qs_mask |= SCH_SFQ_ATTR_QUANTUM;
157
158	return 0;
159}
160
161/**
162 * Get quantum of SFQ qdisc.
163 * @arg qdisc		SFQ qdisc.
164 * @return Quantum in bytes or a negative error code.
165 */
166int rtnl_sfq_get_quantum(struct rtnl_qdisc *qdisc)
167{
168	struct rtnl_sfq *sfq;
169
170	sfq = sfq_qdisc(qdisc);
171	if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_QUANTUM)
172		return sfq->qs_quantum;
173	else
174		return -NLE_NOATTR;
175}
176
177/**
178 * Set limit of SFQ qdisc.
179 * @arg qdisc		SFQ qdisc to be modified.
180 * @arg limit		New limit in number of packets.
181 * @return 0 on success or a negative error code.
182 */
183int rtnl_sfq_set_limit(struct rtnl_qdisc *qdisc, int limit)
184{
185	struct rtnl_sfq *sfq;
186
187	sfq = sfq_alloc(qdisc);
188	if (!sfq)
189		return -NLE_NOMEM;
190
191	sfq->qs_limit = limit;
192	sfq->qs_mask |= SCH_SFQ_ATTR_LIMIT;
193
194	return 0;
195}
196
197/**
198 * Get limit of SFQ qdisc.
199 * @arg qdisc		SFQ qdisc.
200 * @return Limit or a negative error code.
201 */
202int rtnl_sfq_get_limit(struct rtnl_qdisc *qdisc)
203{
204	struct rtnl_sfq *sfq;
205
206	sfq = sfq_qdisc(qdisc);
207	if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_LIMIT)
208		return sfq->qs_limit;
209	else
210		return -NLE_NOATTR;
211}
212
213/**
214 * Set perturbation interval of SFQ qdisc.
215 * @arg qdisc		SFQ qdisc to be modified.
216 * @arg perturb		New perturbation interval in seconds.
217 * @note A value of 0 disables perturbation altogether.
218 * @return 0 on success or a negative error code.
219 */
220int rtnl_sfq_set_perturb(struct rtnl_qdisc *qdisc, int perturb)
221{
222	struct rtnl_sfq *sfq;
223
224	sfq = sfq_alloc(qdisc);
225	if (!sfq)
226		return -NLE_NOMEM;
227
228	sfq->qs_perturb = perturb;
229	sfq->qs_mask |= SCH_SFQ_ATTR_PERTURB;
230
231	return 0;
232}
233
234/**
235 * Get perturbation interval of SFQ qdisc.
236 * @arg qdisc		SFQ qdisc.
237 * @return Perturbation interval in seconds or a negative error code.
238 */
239int rtnl_sfq_get_perturb(struct rtnl_qdisc *qdisc)
240{
241	struct rtnl_sfq *sfq;
242
243	sfq = sfq_qdisc(qdisc);
244	if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_PERTURB)
245		return sfq->qs_perturb;
246	else
247		return -NLE_NOATTR;
248}
249
250/**
251 * Get divisor of SFQ qdisc.
252 * @arg qdisc		SFQ qdisc.
253 * @return Divisor in number of entries or a negative error code.
254 */
255int rtnl_sfq_get_divisor(struct rtnl_qdisc *qdisc)
256{
257	struct rtnl_sfq *sfq;
258
259	sfq = sfq_qdisc(qdisc);
260	if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_DIVISOR)
261		return sfq->qs_divisor;
262	else
263		return -NLE_NOATTR;
264}
265
266/** @} */
267
268static struct rtnl_qdisc_ops sfq_ops = {
269	.qo_kind		= "sfq",
270	.qo_msg_parser		= sfq_msg_parser,
271	.qo_free_data		= sfq_free_data,
272	.qo_dump = {
273	    [NL_DUMP_LINE]	= sfq_dump_line,
274	    [NL_DUMP_DETAILS]	= sfq_dump_details,
275	},
276	.qo_get_opts		= sfq_get_opts,
277};
278
279static void __init sfq_init(void)
280{
281	rtnl_qdisc_register(&sfq_ops);
282}
283
284static void __exit sfq_exit(void)
285{
286	rtnl_qdisc_unregister(&sfq_ops);
287}
288
289/** @} */
290