1/*
2 **************************************************************************
3 * Copyright (c) 2014, The Linux Foundation. All rights reserved.
4 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all copies.
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
13 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 **************************************************************************
15 */
16
17#include "nss_qdisc.h"
18
19/*
20 * nss_blackhole private qdisc structure
21 */
22struct nss_blackhole_sched_data {
23	struct nss_qdisc nq;	/* Common base class for all nss qdiscs */
24	u8 set_default;		/* Flag to set qdisc as default qdisc for enqueue */
25};
26
27/*
28 * nss_blackhole_enqueue()
29 *	Enqueue API for nss blackhole qdisc.
30 */
31static int nss_blackhole_enqueue(struct sk_buff *skb, struct Qdisc *sch)
32{
33	return nss_qdisc_enqueue(skb, sch);
34}
35
36/*
37 * nss_blackhole_dequeue()
38 * 	Dequeue API for nss blackhole qdisc.
39 */
40static struct sk_buff *nss_blackhole_dequeue(struct Qdisc *sch)
41{
42	return nss_qdisc_dequeue(sch);
43}
44
45/*
46 * nss_blackhole_drop()
47 *	The following function drops a packet from HLOS queue.
48 *
49 * Note, this does not drop packets from queues in the NSS. We do not support that.
50 */
51static unsigned int nss_blackhole_drop(struct Qdisc *sch)
52{
53	nss_qdisc_info("%s: qdisc %x dropping\n", __func__, sch->handle);
54	return nss_qdisc_drop(sch);
55}
56
57/*
58 * nss_blackhole_reset()
59 *	Resets the nss blackhole qdisc.
60 */
61static void nss_blackhole_reset(struct Qdisc *sch)
62{
63	nss_qdisc_info("%s: qdisc %x resetting\n", __func__, sch->handle);
64	nss_qdisc_reset(sch);
65}
66
67/*
68 * nss_blackhole_destroy()
69 *	Destroys the nss blackhole qdisc.
70 */
71static void nss_blackhole_destroy(struct Qdisc *sch)
72{
73	struct nss_qdisc *nq = (struct nss_qdisc *)qdisc_priv(sch);
74
75	/*
76	 * Stop the polling of basic stats
77	 */
78	nss_qdisc_stop_basic_stats_polling(nq);
79
80	nss_qdisc_info("%s: destroying qdisc %x\n", __func__, sch->handle);
81	nss_qdisc_destroy(nq);
82}
83
84/*
85 * nss_blackhole policy structure
86 */
87static const struct nla_policy nss_blackhole_policy[TCA_NSSBLACKHOLE_MAX + 1] = {
88	[TCA_NSSBLACKHOLE_PARMS] = { .len = sizeof(struct tc_nssblackhole_qopt) },
89};
90
91/*
92 * nss_blackhole_change()
93 *	Function call used to configure the parameters of the nss blackhole qdisc.
94 */
95static int nss_blackhole_change(struct Qdisc *sch, struct nlattr *opt)
96{
97	struct nss_blackhole_sched_data *q;
98	struct nlattr *na[TCA_NSSBLACKHOLE_MAX + 1];
99	struct tc_nssblackhole_qopt *qopt;
100	int err;
101	struct nss_if_msg nim;
102
103	q = qdisc_priv(sch);
104
105	if (opt == NULL) {
106		return -EINVAL;
107	}
108
109	err = nla_parse_nested(na, TCA_NSSBLACKHOLE_MAX, opt, nss_blackhole_policy);
110	if (err < 0)
111		return err;
112
113	if (na[TCA_NSSBLACKHOLE_PARMS] == NULL)
114		return -EINVAL;
115
116	qopt = nla_data(na[TCA_NSSBLACKHOLE_PARMS]);
117
118	/*
119	 * Required for basic stats display
120	 */
121	sch->limit = 0;
122
123	q->set_default = qopt->set_default;
124	nss_qdisc_info("%s: qdisc set_default = %u\n", __func__, qopt->set_default);
125
126	/*
127	 * Underneath nss_bloackhole uses a fifo in the NSS. This is why we are sending down a configuration
128	 * message to a fifo node. There are no blackhole shaper in the NSS as yet.
129	 *
130	 * Note: We simply set the limit of fifo to zero to get the blackhole behavior.
131	 */
132	nim.msg.shaper_configure.config.msg.shaper_node_config.qos_tag = q->nq.qos_tag;
133	nim.msg.shaper_configure.config.msg.shaper_node_config.snc.fifo_param.limit = 0;
134	nim.msg.shaper_configure.config.msg.shaper_node_config.snc.fifo_param.drop_mode = NSS_SHAPER_FIFO_DROP_MODE_TAIL;
135	if (nss_qdisc_configure(&q->nq, &nim, NSS_SHAPER_CONFIG_TYPE_FIFO_CHANGE_PARAM) < 0) {
136		nss_qdisc_error("%s: qdisc %x configuration failed\n", __func__, sch->handle);
137		return -EINVAL;
138	}
139
140	/*
141	 * There is nothing we need to do if the qdisc is not
142	 * set as default qdisc.
143	 */
144	if (q->set_default == 0)
145		return 0;
146
147	/*
148	 * Set this qdisc to be the default qdisc for enqueuing packets.
149	 */
150	if (nss_qdisc_set_default(&q->nq) < 0) {
151		nss_qdisc_error("%s: qdisc %x set_default failed\n", __func__, sch->handle);
152		return -EINVAL;
153	}
154
155	nss_qdisc_info("%s: qdisc %x set as default\n", __func__, q->nq.qos_tag);
156	return 0;
157}
158
159/*
160 * nss_blackhole_init()
161 *	Initializes a nss blackhole qdisc.
162 */
163static int nss_blackhole_init(struct Qdisc *sch, struct nlattr *opt)
164{
165	struct nss_qdisc *nq = qdisc_priv(sch);
166
167	if (opt == NULL)
168		return -EINVAL;
169
170	nss_qdisc_info("%s: qdisc %x initializing\n", __func__, sch->handle);
171	nss_blackhole_reset(sch);
172
173	if (nss_qdisc_init(sch, nq, NSS_SHAPER_NODE_TYPE_FIFO, 0) < 0)
174		return -EINVAL;
175
176	nss_qdisc_info("%s: qdisc %x initialized with parent %x\n", __func__, sch->handle, sch->parent);
177	if (nss_blackhole_change(sch, opt) < 0) {
178		nss_qdisc_destroy(nq);
179		return -EINVAL;
180	}
181
182	/*
183	 * Start the stats polling timer
184	 */
185	nss_qdisc_start_basic_stats_polling(nq);
186
187	return 0;
188}
189
190/*
191 * nss_blackhole_dump()
192 *	Dumps qdisc parameters for nss blackhole.
193 */
194static int nss_blackhole_dump(struct Qdisc *sch, struct sk_buff *skb)
195{
196	struct nss_blackhole_sched_data *q;
197	struct nlattr *opts = NULL;
198	struct tc_nssblackhole_qopt opt;
199
200	nss_qdisc_info("%s: qdisc %x dumping!\n", __func__, sch->handle);
201
202	q = qdisc_priv(sch);
203	if (q == NULL) {
204		return -1;
205	}
206
207	opt.set_default = q->set_default;
208
209	opts = nla_nest_start(skb, TCA_OPTIONS);
210	if (opts == NULL) {
211		goto nla_put_failure;
212	}
213	if (nla_put(skb, TCA_NSSBLACKHOLE_PARMS, sizeof(opt), &opt))
214		goto nla_put_failure;
215
216	return nla_nest_end(skb, opts);
217
218nla_put_failure:
219	nla_nest_cancel(skb, opts);
220	return -EMSGSIZE;
221}
222
223/*
224 * nss_blackhole_peek()
225 *	Peeks the first packet in queue for this qdisc.
226 *
227 * Note: This just peeks at the first packet in what is present in HLOS. This does not
228 * perform an actual peak into the queue in the NSS. Given the async delay between
229 * the processors, there is less use in implementing this function.
230 */
231static struct sk_buff *nss_blackhole_peek(struct Qdisc *sch)
232{
233	nss_qdisc_info("%s: qdisc %x peeked\n", __func__, sch->handle);
234	return nss_qdisc_peek(sch);
235}
236
237/*
238 * Registration structure for nss blackhole qdisc
239 */
240struct Qdisc_ops nss_blackhole_qdisc_ops __read_mostly = {
241	.id		=	"nssblackhole",
242	.priv_size	=	sizeof(struct nss_blackhole_sched_data),
243	.enqueue	=	nss_blackhole_enqueue,
244	.dequeue	=	nss_blackhole_dequeue,
245	.peek		=	nss_blackhole_peek,
246	.drop		=	nss_blackhole_drop,
247	.init		=	nss_blackhole_init,
248	.reset		=	nss_blackhole_reset,
249	.destroy	=	nss_blackhole_destroy,
250	.change		=	nss_blackhole_change,
251	.dump		=	nss_blackhole_dump,
252	.owner		=	THIS_MODULE,
253};
254