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