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"
3941ec03e5SRahul Lakkireddy #include "cxgb4_filter.h"
40d8931847SRahul Lakkireddy #include "cxgb4_tc_u32_parse.h"
41d8931847SRahul Lakkireddy #include "cxgb4_tc_u32.h"
42d8931847SRahul Lakkireddy 
43d8931847SRahul Lakkireddy /* Fill ch_filter_specification with parsed match value/mask pair. */
fill_match_fields(struct adapter * adap,struct ch_filter_specification * fs,struct tc_cls_u32_offload * cls,const struct cxgb4_match_field * entry,bool next_header)44d8931847SRahul Lakkireddy static int fill_match_fields(struct adapter *adap,
45d8931847SRahul Lakkireddy 			     struct ch_filter_specification *fs,
46d8931847SRahul Lakkireddy 			     struct tc_cls_u32_offload *cls,
47d8931847SRahul Lakkireddy 			     const struct cxgb4_match_field *entry,
48d8931847SRahul Lakkireddy 			     bool next_header)
49d8931847SRahul Lakkireddy {
50d8931847SRahul Lakkireddy 	unsigned int i, j;
5127f78cb2SRahul Lakkireddy 	__be32 val, mask;
52d8931847SRahul Lakkireddy 	int off, err;
53d8931847SRahul Lakkireddy 	bool found;
54d8931847SRahul Lakkireddy 
55d8931847SRahul Lakkireddy 	for (i = 0; i < cls->knode.sel->nkeys; i++) {
56d8931847SRahul Lakkireddy 		off = cls->knode.sel->keys[i].off;
57d8931847SRahul Lakkireddy 		val = cls->knode.sel->keys[i].val;
58d8931847SRahul Lakkireddy 		mask = cls->knode.sel->keys[i].mask;
59d8931847SRahul Lakkireddy 
60d8931847SRahul Lakkireddy 		if (next_header) {
61d8931847SRahul Lakkireddy 			/* For next headers, parse only keys with offmask */
62d8931847SRahul Lakkireddy 			if (!cls->knode.sel->keys[i].offmask)
63d8931847SRahul Lakkireddy 				continue;
64d8931847SRahul Lakkireddy 		} else {
65d8931847SRahul Lakkireddy 			/* For the remaining, parse only keys without offmask */
66d8931847SRahul Lakkireddy 			if (cls->knode.sel->keys[i].offmask)
67d8931847SRahul Lakkireddy 				continue;
68d8931847SRahul Lakkireddy 		}
69d8931847SRahul Lakkireddy 
70d8931847SRahul Lakkireddy 		found = false;
71d8931847SRahul Lakkireddy 
72d8931847SRahul Lakkireddy 		for (j = 0; entry[j].val; j++) {
73d8931847SRahul Lakkireddy 			if (off == entry[j].off) {
74d8931847SRahul Lakkireddy 				found = true;
75d8931847SRahul Lakkireddy 				err = entry[j].val(fs, val, mask);
76d8931847SRahul Lakkireddy 				if (err)
77d8931847SRahul Lakkireddy 					return err;
78d8931847SRahul Lakkireddy 				break;
79d8931847SRahul Lakkireddy 			}
80d8931847SRahul Lakkireddy 		}
81d8931847SRahul Lakkireddy 
82d8931847SRahul Lakkireddy 		if (!found)
83d8931847SRahul Lakkireddy 			return -EINVAL;
84d8931847SRahul Lakkireddy 	}
85d8931847SRahul Lakkireddy 
86d8931847SRahul Lakkireddy 	return 0;
87d8931847SRahul Lakkireddy }
88d8931847SRahul Lakkireddy 
89b20ff726SRahul Lakkireddy /* Fill ch_filter_specification with parsed action. */
fill_action_fields(struct adapter * adap,struct ch_filter_specification * fs,struct tc_cls_u32_offload * cls)90b20ff726SRahul Lakkireddy static int fill_action_fields(struct adapter *adap,
91b20ff726SRahul Lakkireddy 			      struct ch_filter_specification *fs,
92b20ff726SRahul Lakkireddy 			      struct tc_cls_u32_offload *cls)
93b20ff726SRahul Lakkireddy {
94b20ff726SRahul Lakkireddy 	unsigned int num_actions = 0;
95b20ff726SRahul Lakkireddy 	const struct tc_action *a;
96b20ff726SRahul Lakkireddy 	struct tcf_exts *exts;
97244cd96aSCong Wang 	int i;
98b20ff726SRahul Lakkireddy 
99b20ff726SRahul Lakkireddy 	exts = cls->knode.exts;
1003bcc0cecSJiri Pirko 	if (!tcf_exts_has_actions(exts))
101b20ff726SRahul Lakkireddy 		return -EINVAL;
102b20ff726SRahul Lakkireddy 
103244cd96aSCong Wang 	tcf_exts_for_each_action(i, a, exts) {
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)) {
1179f8a739eSCong Wang 			struct net_device *n_dev, *target_dev;
118b20ff726SRahul Lakkireddy 			bool found = false;
1199f8a739eSCong Wang 			unsigned int i;
120b20ff726SRahul Lakkireddy 
1219f8a739eSCong Wang 			target_dev = tcf_mirred_dev(a);
122b20ff726SRahul Lakkireddy 			for_each_port(adap, i) {
123b20ff726SRahul Lakkireddy 				n_dev = adap->port[i];
1249f8a739eSCong Wang 				if (target_dev == n_dev) {
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 
cxgb4_config_knode(struct net_device * dev,struct tc_cls_u32_offload * cls)1495fd9fc4eSJiri Pirko int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)
150d8931847SRahul Lakkireddy {
151d8931847SRahul Lakkireddy 	const struct cxgb4_match_field *start, *link_start = NULL;
15241ec03e5SRahul Lakkireddy 	struct netlink_ext_ack *extack = cls->common.extack;
153d8931847SRahul Lakkireddy 	struct adapter *adapter = netdev2adap(dev);
1545fd9fc4eSJiri Pirko 	__be16 protocol = cls->common.protocol;
155d8931847SRahul Lakkireddy 	struct ch_filter_specification fs;
156d8931847SRahul Lakkireddy 	struct cxgb4_tc_u32_table *t;
157d8931847SRahul Lakkireddy 	struct cxgb4_link *link;
158d8931847SRahul Lakkireddy 	u32 uhtid, link_uhtid;
159d8931847SRahul Lakkireddy 	bool is_ipv6 = false;
1608d174351SRahul Lakkireddy 	u8 inet_family;
1618d174351SRahul Lakkireddy 	int filter_id;
162d8931847SRahul Lakkireddy 	int ret;
163d8931847SRahul Lakkireddy 
164d8931847SRahul Lakkireddy 	if (!can_tc_u32_offload(dev))
165d8931847SRahul Lakkireddy 		return -EOPNOTSUPP;
166d8931847SRahul Lakkireddy 
167d8931847SRahul Lakkireddy 	if (protocol != htons(ETH_P_IP) && protocol != htons(ETH_P_IPV6))
168d8931847SRahul Lakkireddy 		return -EOPNOTSUPP;
169d8931847SRahul Lakkireddy 
1708d174351SRahul Lakkireddy 	inet_family = (protocol == htons(ETH_P_IPV6)) ? PF_INET6 : PF_INET;
171d8931847SRahul Lakkireddy 
1728d174351SRahul Lakkireddy 	/* Get a free filter entry TID, where we can insert this new
1738d174351SRahul Lakkireddy 	 * rule. Only insert rule if its prio doesn't conflict with
1748d174351SRahul Lakkireddy 	 * existing rules.
17541ec03e5SRahul Lakkireddy 	 */
1768d174351SRahul Lakkireddy 	filter_id = cxgb4_get_free_ftid(dev, inet_family, false,
1778d174351SRahul Lakkireddy 					TC_U32_NODE(cls->knode.handle));
1788d174351SRahul Lakkireddy 	if (filter_id < 0) {
17941ec03e5SRahul Lakkireddy 		NL_SET_ERR_MSG_MOD(extack,
18041ec03e5SRahul Lakkireddy 				   "No free LETCAM index available");
18141ec03e5SRahul Lakkireddy 		return -ENOMEM;
182d8931847SRahul Lakkireddy 	}
183d8931847SRahul Lakkireddy 
184d8931847SRahul Lakkireddy 	t = adapter->tc_u32;
185d8931847SRahul Lakkireddy 	uhtid = TC_U32_USERHTID(cls->knode.handle);
186d8931847SRahul Lakkireddy 	link_uhtid = TC_U32_USERHTID(cls->knode.link_handle);
187d8931847SRahul Lakkireddy 
188d8931847SRahul Lakkireddy 	/* Ensure that uhtid is either root u32 (i.e. 0x800)
189d8931847SRahul Lakkireddy 	 * or a a valid linked bucket.
190d8931847SRahul Lakkireddy 	 */
191d8931847SRahul Lakkireddy 	if (uhtid != 0x800 && uhtid >= t->size)
192d8931847SRahul Lakkireddy 		return -EINVAL;
193d8931847SRahul Lakkireddy 
194d8931847SRahul Lakkireddy 	/* Ensure link handle uhtid is sane, if specified. */
195d8931847SRahul Lakkireddy 	if (link_uhtid >= t->size)
196d8931847SRahul Lakkireddy 		return -EINVAL;
197d8931847SRahul Lakkireddy 
198d8931847SRahul Lakkireddy 	memset(&fs, 0, sizeof(fs));
199d8931847SRahul Lakkireddy 
200c2193999SShahjada Abul Husain 	if (filter_id < adapter->tids.nhpftids)
201c2193999SShahjada Abul Husain 		fs.prio = 1;
20241ec03e5SRahul Lakkireddy 	fs.tc_prio = cls->common.prio;
20341ec03e5SRahul Lakkireddy 	fs.tc_cookie = cls->knode.handle;
20441ec03e5SRahul Lakkireddy 
205d8931847SRahul Lakkireddy 	if (protocol == htons(ETH_P_IPV6)) {
206d8931847SRahul Lakkireddy 		start = cxgb4_ipv6_fields;
207d8931847SRahul Lakkireddy 		is_ipv6 = true;
208d8931847SRahul Lakkireddy 	} else {
209d8931847SRahul Lakkireddy 		start = cxgb4_ipv4_fields;
210d8931847SRahul Lakkireddy 		is_ipv6 = false;
211d8931847SRahul Lakkireddy 	}
212d8931847SRahul Lakkireddy 
213d8931847SRahul Lakkireddy 	if (uhtid != 0x800) {
214d8931847SRahul Lakkireddy 		/* Link must exist from root node before insertion. */
215d8931847SRahul Lakkireddy 		if (!t->table[uhtid - 1].link_handle)
216d8931847SRahul Lakkireddy 			return -EINVAL;
217d8931847SRahul Lakkireddy 
218d8931847SRahul Lakkireddy 		/* Link must have a valid supported next header. */
219d8931847SRahul Lakkireddy 		link_start = t->table[uhtid - 1].match_field;
220d8931847SRahul Lakkireddy 		if (!link_start)
221d8931847SRahul Lakkireddy 			return -EINVAL;
222d8931847SRahul Lakkireddy 	}
223d8931847SRahul Lakkireddy 
224d8931847SRahul Lakkireddy 	/* Parse links and record them for subsequent jumps to valid
225d8931847SRahul Lakkireddy 	 * next headers.
226d8931847SRahul Lakkireddy 	 */
227d8931847SRahul Lakkireddy 	if (link_uhtid) {
228d8931847SRahul Lakkireddy 		const struct cxgb4_next_header *next;
229d8931847SRahul Lakkireddy 		bool found = false;
230d8931847SRahul Lakkireddy 		unsigned int i, j;
23127f78cb2SRahul Lakkireddy 		__be32 val, mask;
232d8931847SRahul Lakkireddy 		int off;
233d8931847SRahul Lakkireddy 
234d8931847SRahul Lakkireddy 		if (t->table[link_uhtid - 1].link_handle) {
235d8931847SRahul Lakkireddy 			dev_err(adapter->pdev_dev,
236d8931847SRahul Lakkireddy 				"Link handle exists for: 0x%x\n",
237d8931847SRahul Lakkireddy 				link_uhtid);
238d8931847SRahul Lakkireddy 			return -EINVAL;
239d8931847SRahul Lakkireddy 		}
240d8931847SRahul Lakkireddy 
241d8931847SRahul Lakkireddy 		next = is_ipv6 ? cxgb4_ipv6_jumps : cxgb4_ipv4_jumps;
242d8931847SRahul Lakkireddy 
243d8931847SRahul Lakkireddy 		/* Try to find matches that allow jumps to next header. */
244d8931847SRahul Lakkireddy 		for (i = 0; next[i].jump; i++) {
24527f78cb2SRahul Lakkireddy 			if (next[i].sel.offoff != cls->knode.sel->offoff ||
24627f78cb2SRahul Lakkireddy 			    next[i].sel.offshift != cls->knode.sel->offshift ||
24727f78cb2SRahul Lakkireddy 			    next[i].sel.offmask != cls->knode.sel->offmask ||
24827f78cb2SRahul Lakkireddy 			    next[i].sel.off != cls->knode.sel->off)
249d8931847SRahul Lakkireddy 				continue;
250d8931847SRahul Lakkireddy 
251d8931847SRahul Lakkireddy 			/* Found a possible candidate.  Find a key that
252d8931847SRahul Lakkireddy 			 * matches the corresponding offset, value, and
253d8931847SRahul Lakkireddy 			 * mask to jump to next header.
254d8931847SRahul Lakkireddy 			 */
255d8931847SRahul Lakkireddy 			for (j = 0; j < cls->knode.sel->nkeys; j++) {
256d8931847SRahul Lakkireddy 				off = cls->knode.sel->keys[j].off;
257d8931847SRahul Lakkireddy 				val = cls->knode.sel->keys[j].val;
258d8931847SRahul Lakkireddy 				mask = cls->knode.sel->keys[j].mask;
259d8931847SRahul Lakkireddy 
26027f78cb2SRahul Lakkireddy 				if (next[i].key.off == off &&
26127f78cb2SRahul Lakkireddy 				    next[i].key.val == val &&
26227f78cb2SRahul Lakkireddy 				    next[i].key.mask == mask) {
263d8931847SRahul Lakkireddy 					found = true;
264d8931847SRahul Lakkireddy 					break;
265d8931847SRahul Lakkireddy 				}
266d8931847SRahul Lakkireddy 			}
267d8931847SRahul Lakkireddy 
268d8931847SRahul Lakkireddy 			if (!found)
269d8931847SRahul Lakkireddy 				continue; /* Try next candidate. */
270d8931847SRahul Lakkireddy 
271d8931847SRahul Lakkireddy 			/* Candidate to jump to next header found.
272d8931847SRahul Lakkireddy 			 * Translate all keys to internal specification
273d8931847SRahul Lakkireddy 			 * and store them in jump table. This spec is copied
274d8931847SRahul Lakkireddy 			 * later to set the actual filters.
275d8931847SRahul Lakkireddy 			 */
276d8931847SRahul Lakkireddy 			ret = fill_match_fields(adapter, &fs, cls,
277d8931847SRahul Lakkireddy 						start, false);
278d8931847SRahul Lakkireddy 			if (ret)
279d8931847SRahul Lakkireddy 				goto out;
280d8931847SRahul Lakkireddy 
281d8931847SRahul Lakkireddy 			link = &t->table[link_uhtid - 1];
282d8931847SRahul Lakkireddy 			link->match_field = next[i].jump;
283d8931847SRahul Lakkireddy 			link->link_handle = cls->knode.handle;
284d8931847SRahul Lakkireddy 			memcpy(&link->fs, &fs, sizeof(fs));
285d8931847SRahul Lakkireddy 			break;
286d8931847SRahul Lakkireddy 		}
287d8931847SRahul Lakkireddy 
288d8931847SRahul Lakkireddy 		/* No candidate found to jump to next header. */
289d8931847SRahul Lakkireddy 		if (!found)
290d8931847SRahul Lakkireddy 			return -EINVAL;
291d8931847SRahul Lakkireddy 
292d8931847SRahul Lakkireddy 		return 0;
293d8931847SRahul Lakkireddy 	}
294d8931847SRahul Lakkireddy 
295d8931847SRahul Lakkireddy 	/* Fill ch_filter_specification match fields to be shipped to hardware.
296d8931847SRahul Lakkireddy 	 * Copy the linked spec (if any) first.  And then update the spec as
297d8931847SRahul Lakkireddy 	 * needed.
298d8931847SRahul Lakkireddy 	 */
299d8931847SRahul Lakkireddy 	if (uhtid != 0x800 && t->table[uhtid - 1].link_handle) {
300d8931847SRahul Lakkireddy 		/* Copy linked ch_filter_specification */
301d8931847SRahul Lakkireddy 		memcpy(&fs, &t->table[uhtid - 1].fs, sizeof(fs));
302d8931847SRahul Lakkireddy 		ret = fill_match_fields(adapter, &fs, cls,
303d8931847SRahul Lakkireddy 					link_start, true);
304d8931847SRahul Lakkireddy 		if (ret)
305d8931847SRahul Lakkireddy 			goto out;
306d8931847SRahul Lakkireddy 	}
307d8931847SRahul Lakkireddy 
308d8931847SRahul Lakkireddy 	ret = fill_match_fields(adapter, &fs, cls, start, false);
309d8931847SRahul Lakkireddy 	if (ret)
310d8931847SRahul Lakkireddy 		goto out;
311d8931847SRahul Lakkireddy 
312b20ff726SRahul Lakkireddy 	/* Fill ch_filter_specification action fields to be shipped to
313b20ff726SRahul Lakkireddy 	 * hardware.
314b20ff726SRahul Lakkireddy 	 */
315b20ff726SRahul Lakkireddy 	ret = fill_action_fields(adapter, &fs, cls);
316b20ff726SRahul Lakkireddy 	if (ret)
317b20ff726SRahul Lakkireddy 		goto out;
318b20ff726SRahul Lakkireddy 
319d8931847SRahul Lakkireddy 	/* The filter spec has been completely built from the info
320d8931847SRahul Lakkireddy 	 * provided from u32.  We now set some default fields in the
321d8931847SRahul Lakkireddy 	 * spec for sanity.
322d8931847SRahul Lakkireddy 	 */
323d8931847SRahul Lakkireddy 
324d8931847SRahul Lakkireddy 	/* Match only packets coming from the ingress port where this
325d8931847SRahul Lakkireddy 	 * filter will be created.
326d8931847SRahul Lakkireddy 	 */
327d8931847SRahul Lakkireddy 	fs.val.iport = netdev2pinfo(dev)->port_id;
328d8931847SRahul Lakkireddy 	fs.mask.iport = ~0;
329d8931847SRahul Lakkireddy 
330d8931847SRahul Lakkireddy 	/* Enable filter hit counts. */
331d8931847SRahul Lakkireddy 	fs.hitcnts = 1;
332d8931847SRahul Lakkireddy 
333d8931847SRahul Lakkireddy 	/* Set type of filter - IPv6 or IPv4 */
334d8931847SRahul Lakkireddy 	fs.type = is_ipv6 ? 1 : 0;
335d8931847SRahul Lakkireddy 
336d8931847SRahul Lakkireddy 	/* Set the filter */
337d8931847SRahul Lakkireddy 	ret = cxgb4_set_filter(dev, filter_id, &fs);
338d8931847SRahul Lakkireddy 	if (ret)
339d8931847SRahul Lakkireddy 		goto out;
340d8931847SRahul Lakkireddy 
341d8931847SRahul Lakkireddy 	/* If this is a linked bucket, then set the corresponding
342d8931847SRahul Lakkireddy 	 * entry in the bitmap to mark it as belonging to this linked
343d8931847SRahul Lakkireddy 	 * bucket.
344d8931847SRahul Lakkireddy 	 */
345d8931847SRahul Lakkireddy 	if (uhtid != 0x800 && t->table[uhtid - 1].link_handle)
346d8931847SRahul Lakkireddy 		set_bit(filter_id, t->table[uhtid - 1].tid_map);
347d8931847SRahul Lakkireddy 
348d8931847SRahul Lakkireddy out:
349d8931847SRahul Lakkireddy 	return ret;
350d8931847SRahul Lakkireddy }
351d8931847SRahul Lakkireddy 
cxgb4_delete_knode(struct net_device * dev,struct tc_cls_u32_offload * cls)3525fd9fc4eSJiri Pirko int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)
353d8931847SRahul Lakkireddy {
354d8931847SRahul Lakkireddy 	struct adapter *adapter = netdev2adap(dev);
355d8931847SRahul Lakkireddy 	unsigned int filter_id, max_tids, i, j;
356d8931847SRahul Lakkireddy 	struct cxgb4_link *link = NULL;
357d8931847SRahul Lakkireddy 	struct cxgb4_tc_u32_table *t;
358c2193999SShahjada Abul Husain 	struct filter_entry *f;
3598d174351SRahul Lakkireddy 	bool found = false;
360d8931847SRahul Lakkireddy 	u32 handle, uhtid;
3618d174351SRahul Lakkireddy 	u8 nslots;
362d8931847SRahul Lakkireddy 	int ret;
363d8931847SRahul Lakkireddy 
364d8931847SRahul Lakkireddy 	if (!can_tc_u32_offload(dev))
365d8931847SRahul Lakkireddy 		return -EOPNOTSUPP;
366d8931847SRahul Lakkireddy 
367d8931847SRahul Lakkireddy 	/* Fetch the location to delete the filter. */
3688d174351SRahul Lakkireddy 	max_tids = adapter->tids.nhpftids + adapter->tids.nftids;
369c2193999SShahjada Abul Husain 
3708d174351SRahul Lakkireddy 	spin_lock_bh(&adapter->tids.ftid_lock);
3718d174351SRahul Lakkireddy 	filter_id = 0;
3728d174351SRahul Lakkireddy 	while (filter_id < max_tids) {
3738d174351SRahul Lakkireddy 		if (filter_id < adapter->tids.nhpftids) {
3748d174351SRahul Lakkireddy 			i = filter_id;
3758d174351SRahul Lakkireddy 			f = &adapter->tids.hpftid_tab[i];
3768d174351SRahul Lakkireddy 			if (f->valid && f->fs.tc_cookie == cls->knode.handle) {
3778d174351SRahul Lakkireddy 				found = true;
3788d174351SRahul Lakkireddy 				break;
3798d174351SRahul Lakkireddy 			}
380c2193999SShahjada Abul Husain 
3818d174351SRahul Lakkireddy 			i = find_next_bit(adapter->tids.hpftid_bmap,
3828d174351SRahul Lakkireddy 					  adapter->tids.nhpftids, i + 1);
3838d174351SRahul Lakkireddy 			if (i >= adapter->tids.nhpftids) {
3848d174351SRahul Lakkireddy 				filter_id = adapter->tids.nhpftids;
3858d174351SRahul Lakkireddy 				continue;
3868d174351SRahul Lakkireddy 			}
3878d174351SRahul Lakkireddy 
3888d174351SRahul Lakkireddy 			filter_id = i;
3898d174351SRahul Lakkireddy 		} else {
3908d174351SRahul Lakkireddy 			i = filter_id - adapter->tids.nhpftids;
3918d174351SRahul Lakkireddy 			f = &adapter->tids.ftid_tab[i];
3928d174351SRahul Lakkireddy 			if (f->valid && f->fs.tc_cookie == cls->knode.handle) {
3938d174351SRahul Lakkireddy 				found = true;
3948d174351SRahul Lakkireddy 				break;
3958d174351SRahul Lakkireddy 			}
3968d174351SRahul Lakkireddy 
3978d174351SRahul Lakkireddy 			i = find_next_bit(adapter->tids.ftid_bmap,
3988d174351SRahul Lakkireddy 					  adapter->tids.nftids, i + 1);
3998d174351SRahul Lakkireddy 			if (i >= adapter->tids.nftids)
4008d174351SRahul Lakkireddy 				break;
4018d174351SRahul Lakkireddy 
4028d174351SRahul Lakkireddy 			filter_id = i + adapter->tids.nhpftids;
4038d174351SRahul Lakkireddy 		}
4048d174351SRahul Lakkireddy 
4058d174351SRahul Lakkireddy 		nslots = 0;
4068d174351SRahul Lakkireddy 		if (f->fs.type) {
4078d174351SRahul Lakkireddy 			nslots++;
4088d174351SRahul Lakkireddy 			if (CHELSIO_CHIP_VERSION(adapter->params.chip) <
4098d174351SRahul Lakkireddy 			    CHELSIO_T6)
4108d174351SRahul Lakkireddy 				nslots += 2;
4118d174351SRahul Lakkireddy 		}
4128d174351SRahul Lakkireddy 
4138d174351SRahul Lakkireddy 		filter_id += nslots;
4148d174351SRahul Lakkireddy 	}
4158d174351SRahul Lakkireddy 	spin_unlock_bh(&adapter->tids.ftid_lock);
4168d174351SRahul Lakkireddy 
4178d174351SRahul Lakkireddy 	if (!found)
418d8931847SRahul Lakkireddy 		return -ERANGE;
419d8931847SRahul Lakkireddy 
420d8931847SRahul Lakkireddy 	t = adapter->tc_u32;
421d8931847SRahul Lakkireddy 	handle = cls->knode.handle;
422d8931847SRahul Lakkireddy 	uhtid = TC_U32_USERHTID(cls->knode.handle);
423d8931847SRahul Lakkireddy 
424d8931847SRahul Lakkireddy 	/* Ensure that uhtid is either root u32 (i.e. 0x800)
425d8931847SRahul Lakkireddy 	 * or a a valid linked bucket.
426d8931847SRahul Lakkireddy 	 */
427d8931847SRahul Lakkireddy 	if (uhtid != 0x800 && uhtid >= t->size)
428d8931847SRahul Lakkireddy 		return -EINVAL;
429d8931847SRahul Lakkireddy 
430d8931847SRahul Lakkireddy 	/* Delete the specified filter */
431d8931847SRahul Lakkireddy 	if (uhtid != 0x800) {
432d8931847SRahul Lakkireddy 		link = &t->table[uhtid - 1];
433d8931847SRahul Lakkireddy 		if (!link->link_handle)
434d8931847SRahul Lakkireddy 			return -EINVAL;
435d8931847SRahul Lakkireddy 
436d8931847SRahul Lakkireddy 		if (!test_bit(filter_id, link->tid_map))
437d8931847SRahul Lakkireddy 			return -EINVAL;
438d8931847SRahul Lakkireddy 	}
439d8931847SRahul Lakkireddy 
4403b0b3beeSKumar Sanghvi 	ret = cxgb4_del_filter(dev, filter_id, NULL);
441d8931847SRahul Lakkireddy 	if (ret)
442d8931847SRahul Lakkireddy 		goto out;
443d8931847SRahul Lakkireddy 
444d8931847SRahul Lakkireddy 	if (link)
445d8931847SRahul Lakkireddy 		clear_bit(filter_id, link->tid_map);
446d8931847SRahul Lakkireddy 
447d8931847SRahul Lakkireddy 	/* If a link is being deleted, then delete all filters
448d8931847SRahul Lakkireddy 	 * associated with the link.
449d8931847SRahul Lakkireddy 	 */
450d8931847SRahul Lakkireddy 	for (i = 0; i < t->size; i++) {
451d8931847SRahul Lakkireddy 		link = &t->table[i];
452d8931847SRahul Lakkireddy 
453d8931847SRahul Lakkireddy 		if (link->link_handle == handle) {
454d8931847SRahul Lakkireddy 			for (j = 0; j < max_tids; j++) {
455d8931847SRahul Lakkireddy 				if (!test_bit(j, link->tid_map))
456d8931847SRahul Lakkireddy 					continue;
457d8931847SRahul Lakkireddy 
4583b0b3beeSKumar Sanghvi 				ret = __cxgb4_del_filter(dev, j, NULL, NULL);
459d8931847SRahul Lakkireddy 				if (ret)
460d8931847SRahul Lakkireddy 					goto out;
461d8931847SRahul Lakkireddy 
462d8931847SRahul Lakkireddy 				clear_bit(j, link->tid_map);
463d8931847SRahul Lakkireddy 			}
464d8931847SRahul Lakkireddy 
465d8931847SRahul Lakkireddy 			/* Clear the link state */
466d8931847SRahul Lakkireddy 			link->match_field = NULL;
467d8931847SRahul Lakkireddy 			link->link_handle = 0;
468d8931847SRahul Lakkireddy 			memset(&link->fs, 0, sizeof(link->fs));
469d8931847SRahul Lakkireddy 			break;
470d8931847SRahul Lakkireddy 		}
471d8931847SRahul Lakkireddy 	}
472d8931847SRahul Lakkireddy 
473d8931847SRahul Lakkireddy out:
474d8931847SRahul Lakkireddy 	return ret;
475d8931847SRahul Lakkireddy }
476d8931847SRahul Lakkireddy 
cxgb4_cleanup_tc_u32(struct adapter * adap)477d8931847SRahul Lakkireddy void cxgb4_cleanup_tc_u32(struct adapter *adap)
478d8931847SRahul Lakkireddy {
479d8931847SRahul Lakkireddy 	struct cxgb4_tc_u32_table *t;
480d8931847SRahul Lakkireddy 	unsigned int i;
481d8931847SRahul Lakkireddy 
482d8931847SRahul Lakkireddy 	if (!adap->tc_u32)
483d8931847SRahul Lakkireddy 		return;
484d8931847SRahul Lakkireddy 
485d8931847SRahul Lakkireddy 	/* Free up all allocated memory. */
486d8931847SRahul Lakkireddy 	t = adap->tc_u32;
487d8931847SRahul Lakkireddy 	for (i = 0; i < t->size; i++) {
488d8931847SRahul Lakkireddy 		struct cxgb4_link *link = &t->table[i];
489d8931847SRahul Lakkireddy 
490752ade68SMichal Hocko 		kvfree(link->tid_map);
491d8931847SRahul Lakkireddy 	}
492752ade68SMichal Hocko 	kvfree(adap->tc_u32);
493d8931847SRahul Lakkireddy }
494d8931847SRahul Lakkireddy 
cxgb4_init_tc_u32(struct adapter * adap)49545da1ca2SArjun V struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap)
496d8931847SRahul Lakkireddy {
497c2193999SShahjada Abul Husain 	unsigned int max_tids = adap->tids.nftids + adap->tids.nhpftids;
498d8931847SRahul Lakkireddy 	struct cxgb4_tc_u32_table *t;
499d8931847SRahul Lakkireddy 	unsigned int i;
500d8931847SRahul Lakkireddy 
50145da1ca2SArjun V 	if (!max_tids)
502d8931847SRahul Lakkireddy 		return NULL;
503d8931847SRahul Lakkireddy 
504c829f5f5SGustavo A. R. Silva 	t = kvzalloc(struct_size(t, table, max_tids), GFP_KERNEL);
505d8931847SRahul Lakkireddy 	if (!t)
506d8931847SRahul Lakkireddy 		return NULL;
507d8931847SRahul Lakkireddy 
50845da1ca2SArjun V 	t->size = max_tids;
509d8931847SRahul Lakkireddy 
510d8931847SRahul Lakkireddy 	for (i = 0; i < t->size; i++) {
511d8931847SRahul Lakkireddy 		struct cxgb4_link *link = &t->table[i];
512d8931847SRahul Lakkireddy 		unsigned int bmap_size;
513d8931847SRahul Lakkireddy 
514d8931847SRahul Lakkireddy 		bmap_size = BITS_TO_LONGS(max_tids);
515778e1cddSKees Cook 		link->tid_map = kvcalloc(bmap_size, sizeof(unsigned long),
516778e1cddSKees Cook 					 GFP_KERNEL);
517d8931847SRahul Lakkireddy 		if (!link->tid_map)
518d8931847SRahul Lakkireddy 			goto out_no_mem;
519d8931847SRahul Lakkireddy 		bitmap_zero(link->tid_map, max_tids);
520d8931847SRahul Lakkireddy 	}
521d8931847SRahul Lakkireddy 
522d8931847SRahul Lakkireddy 	return t;
523d8931847SRahul Lakkireddy 
524d8931847SRahul Lakkireddy out_no_mem:
525d8931847SRahul Lakkireddy 	for (i = 0; i < t->size; i++) {
526d8931847SRahul Lakkireddy 		struct cxgb4_link *link = &t->table[i];
527752ade68SMichal Hocko 		kvfree(link->tid_map);
528d8931847SRahul Lakkireddy 	}
529752ade68SMichal Hocko 	kvfree(t);
530d8931847SRahul Lakkireddy 
531d8931847SRahul Lakkireddy 	return NULL;
532d8931847SRahul Lakkireddy }
533