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;
51*27f78cb2SRahul 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;
231*27f78cb2SRahul 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++) {
245*27f78cb2SRahul Lakkireddy if (next[i].sel.offoff != cls->knode.sel->offoff ||
246*27f78cb2SRahul Lakkireddy next[i].sel.offshift != cls->knode.sel->offshift ||
247*27f78cb2SRahul Lakkireddy next[i].sel.offmask != cls->knode.sel->offmask ||
248*27f78cb2SRahul 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
260*27f78cb2SRahul Lakkireddy if (next[i].key.off == off &&
261*27f78cb2SRahul Lakkireddy next[i].key.val == val &&
262*27f78cb2SRahul 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