1d8931847SRahul Lakkireddy /*
2d8931847SRahul Lakkireddy  * This file is part of the Chelsio T4 Ethernet driver for Linux.
3d8931847SRahul Lakkireddy  *
4d8931847SRahul Lakkireddy  * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
5d8931847SRahul Lakkireddy  *
6d8931847SRahul Lakkireddy  * This software is available to you under a choice of one of two
7d8931847SRahul Lakkireddy  * licenses.  You may choose to be licensed under the terms of the GNU
8d8931847SRahul Lakkireddy  * General Public License (GPL) Version 2, available from the file
9d8931847SRahul Lakkireddy  * COPYING in the main directory of this source tree, or the
10d8931847SRahul Lakkireddy  * OpenIB.org BSD license below:
11d8931847SRahul Lakkireddy  *
12d8931847SRahul Lakkireddy  *     Redistribution and use in source and binary forms, with or
13d8931847SRahul Lakkireddy  *     without modification, are permitted provided that the following
14d8931847SRahul Lakkireddy  *     conditions are met:
15d8931847SRahul Lakkireddy  *
16d8931847SRahul Lakkireddy  *      - Redistributions of source code must retain the above
17d8931847SRahul Lakkireddy  *        copyright notice, this list of conditions and the following
18d8931847SRahul Lakkireddy  *        disclaimer.
19d8931847SRahul Lakkireddy  *
20d8931847SRahul Lakkireddy  *      - Redistributions in binary form must reproduce the above
21d8931847SRahul Lakkireddy  *        copyright notice, this list of conditions and the following
22d8931847SRahul Lakkireddy  *        disclaimer in the documentation and/or other materials
23d8931847SRahul Lakkireddy  *        provided with the distribution.
24d8931847SRahul Lakkireddy  *
25d8931847SRahul Lakkireddy  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26d8931847SRahul Lakkireddy  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27d8931847SRahul Lakkireddy  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28d8931847SRahul Lakkireddy  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29d8931847SRahul Lakkireddy  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30d8931847SRahul Lakkireddy  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31d8931847SRahul Lakkireddy  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32d8931847SRahul Lakkireddy  * SOFTWARE.
33d8931847SRahul Lakkireddy  */
34d8931847SRahul Lakkireddy 
35b20ff726SRahul Lakkireddy #include <net/tc_act/tc_gact.h>
36b20ff726SRahul Lakkireddy #include <net/tc_act/tc_mirred.h>
37b20ff726SRahul Lakkireddy 
38d8931847SRahul Lakkireddy #include "cxgb4.h"
39d8931847SRahul Lakkireddy #include "cxgb4_tc_u32_parse.h"
40d8931847SRahul Lakkireddy #include "cxgb4_tc_u32.h"
41d8931847SRahul Lakkireddy 
42d8931847SRahul Lakkireddy /* Fill ch_filter_specification with parsed match value/mask pair. */
43d8931847SRahul Lakkireddy static int fill_match_fields(struct adapter *adap,
44d8931847SRahul Lakkireddy 			     struct ch_filter_specification *fs,
45d8931847SRahul Lakkireddy 			     struct tc_cls_u32_offload *cls,
46d8931847SRahul Lakkireddy 			     const struct cxgb4_match_field *entry,
47d8931847SRahul Lakkireddy 			     bool next_header)
48d8931847SRahul Lakkireddy {
49d8931847SRahul Lakkireddy 	unsigned int i, j;
50d8931847SRahul Lakkireddy 	u32 val, mask;
51d8931847SRahul Lakkireddy 	int off, err;
52d8931847SRahul Lakkireddy 	bool found;
53d8931847SRahul Lakkireddy 
54d8931847SRahul Lakkireddy 	for (i = 0; i < cls->knode.sel->nkeys; i++) {
55d8931847SRahul Lakkireddy 		off = cls->knode.sel->keys[i].off;
56d8931847SRahul Lakkireddy 		val = cls->knode.sel->keys[i].val;
57d8931847SRahul Lakkireddy 		mask = cls->knode.sel->keys[i].mask;
58d8931847SRahul Lakkireddy 
59d8931847SRahul Lakkireddy 		if (next_header) {
60d8931847SRahul Lakkireddy 			/* For next headers, parse only keys with offmask */
61d8931847SRahul Lakkireddy 			if (!cls->knode.sel->keys[i].offmask)
62d8931847SRahul Lakkireddy 				continue;
63d8931847SRahul Lakkireddy 		} else {
64d8931847SRahul Lakkireddy 			/* For the remaining, parse only keys without offmask */
65d8931847SRahul Lakkireddy 			if (cls->knode.sel->keys[i].offmask)
66d8931847SRahul Lakkireddy 				continue;
67d8931847SRahul Lakkireddy 		}
68d8931847SRahul Lakkireddy 
69d8931847SRahul Lakkireddy 		found = false;
70d8931847SRahul Lakkireddy 
71d8931847SRahul Lakkireddy 		for (j = 0; entry[j].val; j++) {
72d8931847SRahul Lakkireddy 			if (off == entry[j].off) {
73d8931847SRahul Lakkireddy 				found = true;
74d8931847SRahul Lakkireddy 				err = entry[j].val(fs, val, mask);
75d8931847SRahul Lakkireddy 				if (err)
76d8931847SRahul Lakkireddy 					return err;
77d8931847SRahul Lakkireddy 				break;
78d8931847SRahul Lakkireddy 			}
79d8931847SRahul Lakkireddy 		}
80d8931847SRahul Lakkireddy 
81d8931847SRahul Lakkireddy 		if (!found)
82d8931847SRahul Lakkireddy 			return -EINVAL;
83d8931847SRahul Lakkireddy 	}
84d8931847SRahul Lakkireddy 
85d8931847SRahul Lakkireddy 	return 0;
86d8931847SRahul Lakkireddy }
87d8931847SRahul Lakkireddy 
88b20ff726SRahul Lakkireddy /* Fill ch_filter_specification with parsed action. */
89b20ff726SRahul Lakkireddy static int fill_action_fields(struct adapter *adap,
90b20ff726SRahul Lakkireddy 			      struct ch_filter_specification *fs,
91b20ff726SRahul Lakkireddy 			      struct tc_cls_u32_offload *cls)
92b20ff726SRahul Lakkireddy {
93b20ff726SRahul Lakkireddy 	unsigned int num_actions = 0;
94b20ff726SRahul Lakkireddy 	const struct tc_action *a;
95b20ff726SRahul Lakkireddy 	struct tcf_exts *exts;
96b20ff726SRahul Lakkireddy 	LIST_HEAD(actions);
97b20ff726SRahul Lakkireddy 
98b20ff726SRahul Lakkireddy 	exts = cls->knode.exts;
99b20ff726SRahul Lakkireddy 	if (tc_no_actions(exts))
100b20ff726SRahul Lakkireddy 		return -EINVAL;
101b20ff726SRahul Lakkireddy 
102b20ff726SRahul Lakkireddy 	tcf_exts_to_list(exts, &actions);
103b20ff726SRahul Lakkireddy 	list_for_each_entry(a, &actions, list) {
104b20ff726SRahul Lakkireddy 		/* Don't allow more than one action per rule. */
105b20ff726SRahul Lakkireddy 		if (num_actions)
106b20ff726SRahul Lakkireddy 			return -EINVAL;
107b20ff726SRahul Lakkireddy 
108b20ff726SRahul Lakkireddy 		/* Drop in hardware. */
109b20ff726SRahul Lakkireddy 		if (is_tcf_gact_shot(a)) {
110b20ff726SRahul Lakkireddy 			fs->action = FILTER_DROP;
111b20ff726SRahul Lakkireddy 			num_actions++;
112b20ff726SRahul Lakkireddy 			continue;
113b20ff726SRahul Lakkireddy 		}
114b20ff726SRahul Lakkireddy 
115b20ff726SRahul Lakkireddy 		/* Re-direct to specified port in hardware. */
1165724b8b5SShmulik Ladkani 		if (is_tcf_mirred_egress_redirect(a)) {
117b20ff726SRahul Lakkireddy 			struct net_device *n_dev;
118b20ff726SRahul Lakkireddy 			unsigned int i, index;
119b20ff726SRahul Lakkireddy 			bool found = false;
120b20ff726SRahul Lakkireddy 
121b20ff726SRahul Lakkireddy 			index = tcf_mirred_ifindex(a);
122b20ff726SRahul Lakkireddy 			for_each_port(adap, i) {
123b20ff726SRahul Lakkireddy 				n_dev = adap->port[i];
124b20ff726SRahul Lakkireddy 				if (index == n_dev->ifindex) {
125b20ff726SRahul Lakkireddy 					fs->action = FILTER_SWITCH;
126b20ff726SRahul Lakkireddy 					fs->eport = i;
127b20ff726SRahul Lakkireddy 					found = true;
128b20ff726SRahul Lakkireddy 					break;
129b20ff726SRahul Lakkireddy 				}
130b20ff726SRahul Lakkireddy 			}
131b20ff726SRahul Lakkireddy 
132b20ff726SRahul Lakkireddy 			/* Interface doesn't belong to any port of
133b20ff726SRahul Lakkireddy 			 * the underlying hardware.
134b20ff726SRahul Lakkireddy 			 */
135b20ff726SRahul Lakkireddy 			if (!found)
136b20ff726SRahul Lakkireddy 				return -EINVAL;
137b20ff726SRahul Lakkireddy 
138b20ff726SRahul Lakkireddy 			num_actions++;
139b20ff726SRahul Lakkireddy 			continue;
140b20ff726SRahul Lakkireddy 		}
141b20ff726SRahul Lakkireddy 
142b20ff726SRahul Lakkireddy 		/* Un-supported action. */
143b20ff726SRahul Lakkireddy 		return -EINVAL;
144b20ff726SRahul Lakkireddy 	}
145b20ff726SRahul Lakkireddy 
146b20ff726SRahul Lakkireddy 	return 0;
147b20ff726SRahul Lakkireddy }
148b20ff726SRahul Lakkireddy 
149d8931847SRahul Lakkireddy int cxgb4_config_knode(struct net_device *dev, __be16 protocol,
150d8931847SRahul Lakkireddy 		       struct tc_cls_u32_offload *cls)
151d8931847SRahul Lakkireddy {
152d8931847SRahul Lakkireddy 	const struct cxgb4_match_field *start, *link_start = NULL;
153d8931847SRahul Lakkireddy 	struct adapter *adapter = netdev2adap(dev);
154d8931847SRahul Lakkireddy 	struct ch_filter_specification fs;
155d8931847SRahul Lakkireddy 	struct cxgb4_tc_u32_table *t;
156d8931847SRahul Lakkireddy 	struct cxgb4_link *link;
157d8931847SRahul Lakkireddy 	unsigned int filter_id;
158d8931847SRahul Lakkireddy 	u32 uhtid, link_uhtid;
159d8931847SRahul Lakkireddy 	bool is_ipv6 = false;
160d8931847SRahul Lakkireddy 	int ret;
161d8931847SRahul Lakkireddy 
162d8931847SRahul Lakkireddy 	if (!can_tc_u32_offload(dev))
163d8931847SRahul Lakkireddy 		return -EOPNOTSUPP;
164d8931847SRahul Lakkireddy 
165d8931847SRahul Lakkireddy 	if (protocol != htons(ETH_P_IP) && protocol != htons(ETH_P_IPV6))
166d8931847SRahul Lakkireddy 		return -EOPNOTSUPP;
167d8931847SRahul Lakkireddy 
168d8931847SRahul Lakkireddy 	/* Fetch the location to insert the filter. */
169d8931847SRahul Lakkireddy 	filter_id = cls->knode.handle & 0xFFFFF;
170d8931847SRahul Lakkireddy 
171d8931847SRahul Lakkireddy 	if (filter_id > adapter->tids.nftids) {
172d8931847SRahul Lakkireddy 		dev_err(adapter->pdev_dev,
173d8931847SRahul Lakkireddy 			"Location %d out of range for insertion. Max: %d\n",
174d8931847SRahul Lakkireddy 			filter_id, adapter->tids.nftids);
175d8931847SRahul Lakkireddy 		return -ERANGE;
176d8931847SRahul Lakkireddy 	}
177d8931847SRahul Lakkireddy 
178d8931847SRahul Lakkireddy 	t = adapter->tc_u32;
179d8931847SRahul Lakkireddy 	uhtid = TC_U32_USERHTID(cls->knode.handle);
180d8931847SRahul Lakkireddy 	link_uhtid = TC_U32_USERHTID(cls->knode.link_handle);
181d8931847SRahul Lakkireddy 
182d8931847SRahul Lakkireddy 	/* Ensure that uhtid is either root u32 (i.e. 0x800)
183d8931847SRahul Lakkireddy 	 * or a a valid linked bucket.
184d8931847SRahul Lakkireddy 	 */
185d8931847SRahul Lakkireddy 	if (uhtid != 0x800 && uhtid >= t->size)
186d8931847SRahul Lakkireddy 		return -EINVAL;
187d8931847SRahul Lakkireddy 
188d8931847SRahul Lakkireddy 	/* Ensure link handle uhtid is sane, if specified. */
189d8931847SRahul Lakkireddy 	if (link_uhtid >= t->size)
190d8931847SRahul Lakkireddy 		return -EINVAL;
191d8931847SRahul Lakkireddy 
192d8931847SRahul Lakkireddy 	memset(&fs, 0, sizeof(fs));
193d8931847SRahul Lakkireddy 
194d8931847SRahul Lakkireddy 	if (protocol == htons(ETH_P_IPV6)) {
195d8931847SRahul Lakkireddy 		start = cxgb4_ipv6_fields;
196d8931847SRahul Lakkireddy 		is_ipv6 = true;
197d8931847SRahul Lakkireddy 	} else {
198d8931847SRahul Lakkireddy 		start = cxgb4_ipv4_fields;
199d8931847SRahul Lakkireddy 		is_ipv6 = false;
200d8931847SRahul Lakkireddy 	}
201d8931847SRahul Lakkireddy 
202d8931847SRahul Lakkireddy 	if (uhtid != 0x800) {
203d8931847SRahul Lakkireddy 		/* Link must exist from root node before insertion. */
204d8931847SRahul Lakkireddy 		if (!t->table[uhtid - 1].link_handle)
205d8931847SRahul Lakkireddy 			return -EINVAL;
206d8931847SRahul Lakkireddy 
207d8931847SRahul Lakkireddy 		/* Link must have a valid supported next header. */
208d8931847SRahul Lakkireddy 		link_start = t->table[uhtid - 1].match_field;
209d8931847SRahul Lakkireddy 		if (!link_start)
210d8931847SRahul Lakkireddy 			return -EINVAL;
211d8931847SRahul Lakkireddy 	}
212d8931847SRahul Lakkireddy 
213d8931847SRahul Lakkireddy 	/* Parse links and record them for subsequent jumps to valid
214d8931847SRahul Lakkireddy 	 * next headers.
215d8931847SRahul Lakkireddy 	 */
216d8931847SRahul Lakkireddy 	if (link_uhtid) {
217d8931847SRahul Lakkireddy 		const struct cxgb4_next_header *next;
218d8931847SRahul Lakkireddy 		bool found = false;
219d8931847SRahul Lakkireddy 		unsigned int i, j;
220d8931847SRahul Lakkireddy 		u32 val, mask;
221d8931847SRahul Lakkireddy 		int off;
222d8931847SRahul Lakkireddy 
223d8931847SRahul Lakkireddy 		if (t->table[link_uhtid - 1].link_handle) {
224d8931847SRahul Lakkireddy 			dev_err(adapter->pdev_dev,
225d8931847SRahul Lakkireddy 				"Link handle exists for: 0x%x\n",
226d8931847SRahul Lakkireddy 				link_uhtid);
227d8931847SRahul Lakkireddy 			return -EINVAL;
228d8931847SRahul Lakkireddy 		}
229d8931847SRahul Lakkireddy 
230d8931847SRahul Lakkireddy 		next = is_ipv6 ? cxgb4_ipv6_jumps : cxgb4_ipv4_jumps;
231d8931847SRahul Lakkireddy 
232d8931847SRahul Lakkireddy 		/* Try to find matches that allow jumps to next header. */
233d8931847SRahul Lakkireddy 		for (i = 0; next[i].jump; i++) {
234d8931847SRahul Lakkireddy 			if (next[i].offoff != cls->knode.sel->offoff ||
235d8931847SRahul Lakkireddy 			    next[i].shift != cls->knode.sel->offshift ||
236d8931847SRahul Lakkireddy 			    next[i].mask != cls->knode.sel->offmask ||
237d8931847SRahul Lakkireddy 			    next[i].offset != cls->knode.sel->off)
238d8931847SRahul Lakkireddy 				continue;
239d8931847SRahul Lakkireddy 
240d8931847SRahul Lakkireddy 			/* Found a possible candidate.  Find a key that
241d8931847SRahul Lakkireddy 			 * matches the corresponding offset, value, and
242d8931847SRahul Lakkireddy 			 * mask to jump to next header.
243d8931847SRahul Lakkireddy 			 */
244d8931847SRahul Lakkireddy 			for (j = 0; j < cls->knode.sel->nkeys; j++) {
245d8931847SRahul Lakkireddy 				off = cls->knode.sel->keys[j].off;
246d8931847SRahul Lakkireddy 				val = cls->knode.sel->keys[j].val;
247d8931847SRahul Lakkireddy 				mask = cls->knode.sel->keys[j].mask;
248d8931847SRahul Lakkireddy 
249d8931847SRahul Lakkireddy 				if (next[i].match_off == off &&
250d8931847SRahul Lakkireddy 				    next[i].match_val == val &&
251d8931847SRahul Lakkireddy 				    next[i].match_mask == mask) {
252d8931847SRahul Lakkireddy 					found = true;
253d8931847SRahul Lakkireddy 					break;
254d8931847SRahul Lakkireddy 				}
255d8931847SRahul Lakkireddy 			}
256d8931847SRahul Lakkireddy 
257d8931847SRahul Lakkireddy 			if (!found)
258d8931847SRahul Lakkireddy 				continue; /* Try next candidate. */
259d8931847SRahul Lakkireddy 
260d8931847SRahul Lakkireddy 			/* Candidate to jump to next header found.
261d8931847SRahul Lakkireddy 			 * Translate all keys to internal specification
262d8931847SRahul Lakkireddy 			 * and store them in jump table. This spec is copied
263d8931847SRahul Lakkireddy 			 * later to set the actual filters.
264d8931847SRahul Lakkireddy 			 */
265d8931847SRahul Lakkireddy 			ret = fill_match_fields(adapter, &fs, cls,
266d8931847SRahul Lakkireddy 						start, false);
267d8931847SRahul Lakkireddy 			if (ret)
268d8931847SRahul Lakkireddy 				goto out;
269d8931847SRahul Lakkireddy 
270d8931847SRahul Lakkireddy 			link = &t->table[link_uhtid - 1];
271d8931847SRahul Lakkireddy 			link->match_field = next[i].jump;
272d8931847SRahul Lakkireddy 			link->link_handle = cls->knode.handle;
273d8931847SRahul Lakkireddy 			memcpy(&link->fs, &fs, sizeof(fs));
274d8931847SRahul Lakkireddy 			break;
275d8931847SRahul Lakkireddy 		}
276d8931847SRahul Lakkireddy 
277d8931847SRahul Lakkireddy 		/* No candidate found to jump to next header. */
278d8931847SRahul Lakkireddy 		if (!found)
279d8931847SRahul Lakkireddy 			return -EINVAL;
280d8931847SRahul Lakkireddy 
281d8931847SRahul Lakkireddy 		return 0;
282d8931847SRahul Lakkireddy 	}
283d8931847SRahul Lakkireddy 
284d8931847SRahul Lakkireddy 	/* Fill ch_filter_specification match fields to be shipped to hardware.
285d8931847SRahul Lakkireddy 	 * Copy the linked spec (if any) first.  And then update the spec as
286d8931847SRahul Lakkireddy 	 * needed.
287d8931847SRahul Lakkireddy 	 */
288d8931847SRahul Lakkireddy 	if (uhtid != 0x800 && t->table[uhtid - 1].link_handle) {
289d8931847SRahul Lakkireddy 		/* Copy linked ch_filter_specification */
290d8931847SRahul Lakkireddy 		memcpy(&fs, &t->table[uhtid - 1].fs, sizeof(fs));
291d8931847SRahul Lakkireddy 		ret = fill_match_fields(adapter, &fs, cls,
292d8931847SRahul Lakkireddy 					link_start, true);
293d8931847SRahul Lakkireddy 		if (ret)
294d8931847SRahul Lakkireddy 			goto out;
295d8931847SRahul Lakkireddy 	}
296d8931847SRahul Lakkireddy 
297d8931847SRahul Lakkireddy 	ret = fill_match_fields(adapter, &fs, cls, start, false);
298d8931847SRahul Lakkireddy 	if (ret)
299d8931847SRahul Lakkireddy 		goto out;
300d8931847SRahul Lakkireddy 
301b20ff726SRahul Lakkireddy 	/* Fill ch_filter_specification action fields to be shipped to
302b20ff726SRahul Lakkireddy 	 * hardware.
303b20ff726SRahul Lakkireddy 	 */
304b20ff726SRahul Lakkireddy 	ret = fill_action_fields(adapter, &fs, cls);
305b20ff726SRahul Lakkireddy 	if (ret)
306b20ff726SRahul Lakkireddy 		goto out;
307b20ff726SRahul Lakkireddy 
308d8931847SRahul Lakkireddy 	/* The filter spec has been completely built from the info
309d8931847SRahul Lakkireddy 	 * provided from u32.  We now set some default fields in the
310d8931847SRahul Lakkireddy 	 * spec for sanity.
311d8931847SRahul Lakkireddy 	 */
312d8931847SRahul Lakkireddy 
313d8931847SRahul Lakkireddy 	/* Match only packets coming from the ingress port where this
314d8931847SRahul Lakkireddy 	 * filter will be created.
315d8931847SRahul Lakkireddy 	 */
316d8931847SRahul Lakkireddy 	fs.val.iport = netdev2pinfo(dev)->port_id;
317d8931847SRahul Lakkireddy 	fs.mask.iport = ~0;
318d8931847SRahul Lakkireddy 
319d8931847SRahul Lakkireddy 	/* Enable filter hit counts. */
320d8931847SRahul Lakkireddy 	fs.hitcnts = 1;
321d8931847SRahul Lakkireddy 
322d8931847SRahul Lakkireddy 	/* Set type of filter - IPv6 or IPv4 */
323d8931847SRahul Lakkireddy 	fs.type = is_ipv6 ? 1 : 0;
324d8931847SRahul Lakkireddy 
325d8931847SRahul Lakkireddy 	/* Set the filter */
326d8931847SRahul Lakkireddy 	ret = cxgb4_set_filter(dev, filter_id, &fs);
327d8931847SRahul Lakkireddy 	if (ret)
328d8931847SRahul Lakkireddy 		goto out;
329d8931847SRahul Lakkireddy 
330d8931847SRahul Lakkireddy 	/* If this is a linked bucket, then set the corresponding
331d8931847SRahul Lakkireddy 	 * entry in the bitmap to mark it as belonging to this linked
332d8931847SRahul Lakkireddy 	 * bucket.
333d8931847SRahul Lakkireddy 	 */
334d8931847SRahul Lakkireddy 	if (uhtid != 0x800 && t->table[uhtid - 1].link_handle)
335d8931847SRahul Lakkireddy 		set_bit(filter_id, t->table[uhtid - 1].tid_map);
336d8931847SRahul Lakkireddy 
337d8931847SRahul Lakkireddy out:
338d8931847SRahul Lakkireddy 	return ret;
339d8931847SRahul Lakkireddy }
340d8931847SRahul Lakkireddy 
341d8931847SRahul Lakkireddy int cxgb4_delete_knode(struct net_device *dev, __be16 protocol,
342d8931847SRahul Lakkireddy 		       struct tc_cls_u32_offload *cls)
343d8931847SRahul Lakkireddy {
344d8931847SRahul Lakkireddy 	struct adapter *adapter = netdev2adap(dev);
345d8931847SRahul Lakkireddy 	unsigned int filter_id, max_tids, i, j;
346d8931847SRahul Lakkireddy 	struct cxgb4_link *link = NULL;
347d8931847SRahul Lakkireddy 	struct cxgb4_tc_u32_table *t;
348d8931847SRahul Lakkireddy 	u32 handle, uhtid;
349d8931847SRahul Lakkireddy 	int ret;
350d8931847SRahul Lakkireddy 
351d8931847SRahul Lakkireddy 	if (!can_tc_u32_offload(dev))
352d8931847SRahul Lakkireddy 		return -EOPNOTSUPP;
353d8931847SRahul Lakkireddy 
354d8931847SRahul Lakkireddy 	/* Fetch the location to delete the filter. */
355d8931847SRahul Lakkireddy 	filter_id = cls->knode.handle & 0xFFFFF;
356d8931847SRahul Lakkireddy 
357d8931847SRahul Lakkireddy 	if (filter_id > adapter->tids.nftids) {
358d8931847SRahul Lakkireddy 		dev_err(adapter->pdev_dev,
359d8931847SRahul Lakkireddy 			"Location %d out of range for deletion. Max: %d\n",
360d8931847SRahul Lakkireddy 			filter_id, adapter->tids.nftids);
361d8931847SRahul Lakkireddy 		return -ERANGE;
362d8931847SRahul Lakkireddy 	}
363d8931847SRahul Lakkireddy 
364d8931847SRahul Lakkireddy 	t = adapter->tc_u32;
365d8931847SRahul Lakkireddy 	handle = cls->knode.handle;
366d8931847SRahul Lakkireddy 	uhtid = TC_U32_USERHTID(cls->knode.handle);
367d8931847SRahul Lakkireddy 
368d8931847SRahul Lakkireddy 	/* Ensure that uhtid is either root u32 (i.e. 0x800)
369d8931847SRahul Lakkireddy 	 * or a a valid linked bucket.
370d8931847SRahul Lakkireddy 	 */
371d8931847SRahul Lakkireddy 	if (uhtid != 0x800 && uhtid >= t->size)
372d8931847SRahul Lakkireddy 		return -EINVAL;
373d8931847SRahul Lakkireddy 
374d8931847SRahul Lakkireddy 	/* Delete the specified filter */
375d8931847SRahul Lakkireddy 	if (uhtid != 0x800) {
376d8931847SRahul Lakkireddy 		link = &t->table[uhtid - 1];
377d8931847SRahul Lakkireddy 		if (!link->link_handle)
378d8931847SRahul Lakkireddy 			return -EINVAL;
379d8931847SRahul Lakkireddy 
380d8931847SRahul Lakkireddy 		if (!test_bit(filter_id, link->tid_map))
381d8931847SRahul Lakkireddy 			return -EINVAL;
382d8931847SRahul Lakkireddy 	}
383d8931847SRahul Lakkireddy 
384d8931847SRahul Lakkireddy 	ret = cxgb4_del_filter(dev, filter_id);
385d8931847SRahul Lakkireddy 	if (ret)
386d8931847SRahul Lakkireddy 		goto out;
387d8931847SRahul Lakkireddy 
388d8931847SRahul Lakkireddy 	if (link)
389d8931847SRahul Lakkireddy 		clear_bit(filter_id, link->tid_map);
390d8931847SRahul Lakkireddy 
391d8931847SRahul Lakkireddy 	/* If a link is being deleted, then delete all filters
392d8931847SRahul Lakkireddy 	 * associated with the link.
393d8931847SRahul Lakkireddy 	 */
394d8931847SRahul Lakkireddy 	max_tids = adapter->tids.nftids;
395d8931847SRahul Lakkireddy 	for (i = 0; i < t->size; i++) {
396d8931847SRahul Lakkireddy 		link = &t->table[i];
397d8931847SRahul Lakkireddy 
398d8931847SRahul Lakkireddy 		if (link->link_handle == handle) {
399d8931847SRahul Lakkireddy 			for (j = 0; j < max_tids; j++) {
400d8931847SRahul Lakkireddy 				if (!test_bit(j, link->tid_map))
401d8931847SRahul Lakkireddy 					continue;
402d8931847SRahul Lakkireddy 
403d8931847SRahul Lakkireddy 				ret = __cxgb4_del_filter(dev, j, NULL);
404d8931847SRahul Lakkireddy 				if (ret)
405d8931847SRahul Lakkireddy 					goto out;
406d8931847SRahul Lakkireddy 
407d8931847SRahul Lakkireddy 				clear_bit(j, link->tid_map);
408d8931847SRahul Lakkireddy 			}
409d8931847SRahul Lakkireddy 
410d8931847SRahul Lakkireddy 			/* Clear the link state */
411d8931847SRahul Lakkireddy 			link->match_field = NULL;
412d8931847SRahul Lakkireddy 			link->link_handle = 0;
413d8931847SRahul Lakkireddy 			memset(&link->fs, 0, sizeof(link->fs));
414d8931847SRahul Lakkireddy 			break;
415d8931847SRahul Lakkireddy 		}
416d8931847SRahul Lakkireddy 	}
417d8931847SRahul Lakkireddy 
418d8931847SRahul Lakkireddy out:
419d8931847SRahul Lakkireddy 	return ret;
420d8931847SRahul Lakkireddy }
421d8931847SRahul Lakkireddy 
422d8931847SRahul Lakkireddy void cxgb4_cleanup_tc_u32(struct adapter *adap)
423d8931847SRahul Lakkireddy {
424d8931847SRahul Lakkireddy 	struct cxgb4_tc_u32_table *t;
425d8931847SRahul Lakkireddy 	unsigned int i;
426d8931847SRahul Lakkireddy 
427d8931847SRahul Lakkireddy 	if (!adap->tc_u32)
428d8931847SRahul Lakkireddy 		return;
429d8931847SRahul Lakkireddy 
430d8931847SRahul Lakkireddy 	/* Free up all allocated memory. */
431d8931847SRahul Lakkireddy 	t = adap->tc_u32;
432d8931847SRahul Lakkireddy 	for (i = 0; i < t->size; i++) {
433d8931847SRahul Lakkireddy 		struct cxgb4_link *link = &t->table[i];
434d8931847SRahul Lakkireddy 
435d8931847SRahul Lakkireddy 		t4_free_mem(link->tid_map);
436d8931847SRahul Lakkireddy 	}
437d8931847SRahul Lakkireddy 	t4_free_mem(adap->tc_u32);
438d8931847SRahul Lakkireddy }
439d8931847SRahul Lakkireddy 
44045da1ca2SArjun V struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap)
441d8931847SRahul Lakkireddy {
44245da1ca2SArjun V 	unsigned int max_tids = adap->tids.nftids;
443d8931847SRahul Lakkireddy 	struct cxgb4_tc_u32_table *t;
444d8931847SRahul Lakkireddy 	unsigned int i;
445d8931847SRahul Lakkireddy 
44645da1ca2SArjun V 	if (!max_tids)
447d8931847SRahul Lakkireddy 		return NULL;
448d8931847SRahul Lakkireddy 
449d8931847SRahul Lakkireddy 	t = t4_alloc_mem(sizeof(*t) +
45045da1ca2SArjun V 			 (max_tids * sizeof(struct cxgb4_link)));
451d8931847SRahul Lakkireddy 	if (!t)
452d8931847SRahul Lakkireddy 		return NULL;
453d8931847SRahul Lakkireddy 
45445da1ca2SArjun V 	t->size = max_tids;
455d8931847SRahul Lakkireddy 
456d8931847SRahul Lakkireddy 	for (i = 0; i < t->size; i++) {
457d8931847SRahul Lakkireddy 		struct cxgb4_link *link = &t->table[i];
458d8931847SRahul Lakkireddy 		unsigned int bmap_size;
459d8931847SRahul Lakkireddy 
460d8931847SRahul Lakkireddy 		bmap_size = BITS_TO_LONGS(max_tids);
461d8931847SRahul Lakkireddy 		link->tid_map = t4_alloc_mem(sizeof(unsigned long) * bmap_size);
462d8931847SRahul Lakkireddy 		if (!link->tid_map)
463d8931847SRahul Lakkireddy 			goto out_no_mem;
464d8931847SRahul Lakkireddy 		bitmap_zero(link->tid_map, max_tids);
465d8931847SRahul Lakkireddy 	}
466d8931847SRahul Lakkireddy 
467d8931847SRahul Lakkireddy 	return t;
468d8931847SRahul Lakkireddy 
469d8931847SRahul Lakkireddy out_no_mem:
470d8931847SRahul Lakkireddy 	for (i = 0; i < t->size; i++) {
471d8931847SRahul Lakkireddy 		struct cxgb4_link *link = &t->table[i];
472d8931847SRahul Lakkireddy 
473d8931847SRahul Lakkireddy 		if (link->tid_map)
474d8931847SRahul Lakkireddy 			t4_free_mem(link->tid_map);
475d8931847SRahul Lakkireddy 	}
476d8931847SRahul Lakkireddy 
477d8931847SRahul Lakkireddy 	if (t)
478d8931847SRahul Lakkireddy 		t4_free_mem(t);
479d8931847SRahul Lakkireddy 
480d8931847SRahul Lakkireddy 	return NULL;
481d8931847SRahul Lakkireddy }
482