Imported Upstream version 16.07.2
[deb_dpdk.git] / drivers / net / enic / enic_clsf.c
1 /*
2  * Copyright 2008-2014 Cisco Systems, Inc.  All rights reserved.
3  * Copyright 2007 Nuova Systems, Inc.  All rights reserved.
4  *
5  * Copyright (c) 2014, Cisco Systems, Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34
35 #include <libgen.h>
36
37 #include <rte_ethdev.h>
38 #include <rte_malloc.h>
39 #include <rte_hash.h>
40 #include <rte_byteorder.h>
41
42 #include "enic_compat.h"
43 #include "enic.h"
44 #include "wq_enet_desc.h"
45 #include "rq_enet_desc.h"
46 #include "cq_enet_desc.h"
47 #include "vnic_enet.h"
48 #include "vnic_dev.h"
49 #include "vnic_wq.h"
50 #include "vnic_rq.h"
51 #include "vnic_cq.h"
52 #include "vnic_intr.h"
53 #include "vnic_nic.h"
54
55 #ifdef RTE_MACHINE_CPUFLAG_SSE4_2
56 #include <rte_hash_crc.h>
57 #define DEFAULT_HASH_FUNC       rte_hash_crc
58 #else
59 #include <rte_jhash.h>
60 #define DEFAULT_HASH_FUNC       rte_jhash
61 #endif
62
63 #define ENICPMD_CLSF_HASH_ENTRIES       ENICPMD_FDIR_MAX
64
65 void enic_fdir_stats_get(struct enic *enic, struct rte_eth_fdir_stats *stats)
66 {
67         *stats = enic->fdir.stats;
68 }
69
70 int enic_fdir_del_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
71 {
72         int32_t pos;
73         struct enic_fdir_node *key;
74         /* See if the key is in the table */
75         pos = rte_hash_del_key(enic->fdir.hash, params);
76         switch (pos) {
77         case -EINVAL:
78         case -ENOENT:
79                 enic->fdir.stats.f_remove++;
80                 return -EINVAL;
81         default:
82                 /* The entry is present in the table */
83                 key = enic->fdir.nodes[pos];
84
85                 /* Delete the filter */
86                 vnic_dev_classifier(enic->vdev, CLSF_DEL,
87                         &key->fltr_id, NULL);
88                 rte_free(key);
89                 enic->fdir.nodes[pos] = NULL;
90                 enic->fdir.stats.free++;
91                 enic->fdir.stats.remove++;
92                 break;
93         }
94         return 0;
95 }
96
97 int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
98 {
99         struct enic_fdir_node *key;
100         struct filter fltr = {0};
101         int32_t pos;
102         u8 do_free = 0;
103         u16 old_fltr_id = 0;
104         u32 flowtype_supported;
105         u16 flex_bytes;
106         u16 queue;
107
108         flowtype_supported = (
109                 (RTE_ETH_FLOW_NONFRAG_IPV4_TCP == params->input.flow_type) ||
110                 (RTE_ETH_FLOW_NONFRAG_IPV4_UDP == params->input.flow_type));
111
112         flex_bytes = ((params->input.flow_ext.flexbytes[1] << 8 & 0xFF00) |
113                 (params->input.flow_ext.flexbytes[0] & 0xFF));
114
115         if (!enic->fdir.hash ||
116                 (params->input.flow_ext.vlan_tci & 0xFFF) ||
117                 !flowtype_supported || flex_bytes ||
118                 params->action.behavior /* drop */) {
119                 enic->fdir.stats.f_add++;
120                 return -ENOTSUP;
121         }
122
123         /* Get the enicpmd RQ from the DPDK Rx queue */
124         queue = enic_sop_rq(params->action.rx_queue);
125
126         /* See if the key is already there in the table */
127         pos = rte_hash_del_key(enic->fdir.hash, params);
128         switch (pos) {
129         case -EINVAL:
130                 enic->fdir.stats.f_add++;
131                 return -EINVAL;
132         case -ENOENT:
133                 /* Add a new classifier entry */
134                 if (!enic->fdir.stats.free) {
135                         enic->fdir.stats.f_add++;
136                         return -ENOSPC;
137                 }
138                 key = rte_zmalloc("enic_fdir_node",
139                                   sizeof(struct enic_fdir_node), 0);
140                 if (!key) {
141                         enic->fdir.stats.f_add++;
142                         return -ENOMEM;
143                 }
144                 break;
145         default:
146                 /* The entry is already present in the table.
147                  * Check if there is a change in queue
148                  */
149                 key = enic->fdir.nodes[pos];
150                 enic->fdir.nodes[pos] = NULL;
151                 if (unlikely(key->rq_index == queue)) {
152                         /* Nothing to be done */
153                         enic->fdir.stats.f_add++;
154                         pos = rte_hash_add_key(enic->fdir.hash, params);
155                         if (pos < 0) {
156                                 dev_err(enic, "Add hash key failed\n");
157                                 return pos;
158                         }
159                         enic->fdir.nodes[pos] = key;
160                         dev_warning(enic,
161                                 "FDIR rule is already present\n");
162                         return 0;
163                 }
164
165                 if (likely(enic->fdir.stats.free)) {
166                         /* Add the filter and then delete the old one.
167                          * This is to avoid packets from going into the
168                          * default queue during the window between
169                          * delete and add
170                          */
171                         do_free = 1;
172                         old_fltr_id = key->fltr_id;
173                 } else {
174                         /* No free slots in the classifier.
175                          * Delete the filter and add the modified one later
176                          */
177                         vnic_dev_classifier(enic->vdev, CLSF_DEL,
178                                 &key->fltr_id, NULL);
179                         enic->fdir.stats.free++;
180                 }
181
182                 break;
183         }
184
185         key->filter = *params;
186         key->rq_index = queue;
187
188         fltr.type = FILTER_IPV4_5TUPLE;
189         fltr.u.ipv4.src_addr = rte_be_to_cpu_32(
190                 params->input.flow.ip4_flow.src_ip);
191         fltr.u.ipv4.dst_addr = rte_be_to_cpu_32(
192                 params->input.flow.ip4_flow.dst_ip);
193         fltr.u.ipv4.src_port = rte_be_to_cpu_16(
194                 params->input.flow.udp4_flow.src_port);
195         fltr.u.ipv4.dst_port = rte_be_to_cpu_16(
196                 params->input.flow.udp4_flow.dst_port);
197
198         if (RTE_ETH_FLOW_NONFRAG_IPV4_TCP == params->input.flow_type)
199                 fltr.u.ipv4.protocol = PROTO_TCP;
200         else
201                 fltr.u.ipv4.protocol = PROTO_UDP;
202
203         fltr.u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
204
205         if (!vnic_dev_classifier(enic->vdev, CLSF_ADD, &queue, &fltr)) {
206                 key->fltr_id = queue;
207         } else {
208                 dev_err(enic, "Add classifier entry failed\n");
209                 enic->fdir.stats.f_add++;
210                 rte_free(key);
211                 return -1;
212         }
213
214         if (do_free)
215                 vnic_dev_classifier(enic->vdev, CLSF_DEL, &old_fltr_id, NULL);
216         else{
217                 enic->fdir.stats.free--;
218                 enic->fdir.stats.add++;
219         }
220
221         pos = rte_hash_add_key(enic->fdir.hash, params);
222         if (pos < 0) {
223                 enic->fdir.stats.f_add++;
224                 dev_err(enic, "Add hash key failed\n");
225                 return pos;
226         }
227
228         enic->fdir.nodes[pos] = key;
229         return 0;
230 }
231
232 void enic_clsf_destroy(struct enic *enic)
233 {
234         u32 index;
235         struct enic_fdir_node *key;
236         /* delete classifier entries */
237         for (index = 0; index < ENICPMD_FDIR_MAX; index++) {
238                 key = enic->fdir.nodes[index];
239                 if (key) {
240                         vnic_dev_classifier(enic->vdev, CLSF_DEL,
241                                 &key->fltr_id, NULL);
242                         rte_free(key);
243                         enic->fdir.nodes[index] = NULL;
244                 }
245         }
246
247         if (enic->fdir.hash) {
248                 rte_hash_free(enic->fdir.hash);
249                 enic->fdir.hash = NULL;
250         }
251 }
252
253 int enic_clsf_init(struct enic *enic)
254 {
255         char clsf_name[RTE_HASH_NAMESIZE];
256         struct rte_hash_parameters hash_params = {
257                 .name = clsf_name,
258                 .entries = ENICPMD_CLSF_HASH_ENTRIES,
259                 .key_len = sizeof(struct rte_eth_fdir_filter),
260                 .hash_func = DEFAULT_HASH_FUNC,
261                 .hash_func_init_val = 0,
262                 .socket_id = SOCKET_ID_ANY,
263         };
264         snprintf(clsf_name, RTE_HASH_NAMESIZE, "enic_clsf_%s", enic->bdf_name);
265         enic->fdir.hash = rte_hash_create(&hash_params);
266         memset(&enic->fdir.stats, 0, sizeof(enic->fdir.stats));
267         enic->fdir.stats.free = ENICPMD_FDIR_MAX;
268         return NULL == enic->fdir.hash;
269 }