b3de09f13425f39bc0afbc70e8966dcc36d49206
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlx5 / core / en_tc.c
1 /*
2  * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  */
32
33 #include <net/flow_dissector.h>
34 #include <net/pkt_cls.h>
35 #include <net/tc_act/tc_gact.h>
36 #include <net/tc_act/tc_skbedit.h>
37 #include <linux/mlx5/fs.h>
38 #include <linux/mlx5/device.h>
39 #include <linux/rhashtable.h>
40 #include "en.h"
41 #include "en_tc.h"
42
43 struct mlx5e_tc_flow {
44         struct rhash_head       node;
45         u64                     cookie;
46         struct mlx5_flow_rule   *rule;
47 };
48
49 #define MLX5E_TC_FLOW_TABLE_NUM_ENTRIES 1024
50 #define MLX5E_TC_FLOW_TABLE_NUM_GROUPS 4
51
52 static struct mlx5_flow_rule *mlx5e_tc_add_flow(struct mlx5e_priv *priv,
53                                                 u32 *match_c, u32 *match_v,
54                                                 u32 action, u32 flow_tag)
55 {
56         struct mlx5_flow_destination dest = {
57                 .type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE,
58                 {.ft = priv->fts.vlan.t},
59         };
60         struct mlx5_flow_rule *rule;
61         bool table_created = false;
62
63         if (IS_ERR_OR_NULL(priv->fts.tc.t)) {
64                 priv->fts.tc.t =
65                         mlx5_create_auto_grouped_flow_table(priv->fts.ns, 0,
66                                                             MLX5E_TC_FLOW_TABLE_NUM_ENTRIES,
67                                                             MLX5E_TC_FLOW_TABLE_NUM_GROUPS);
68                 if (IS_ERR(priv->fts.tc.t)) {
69                         netdev_err(priv->netdev,
70                                    "Failed to create tc offload table\n");
71                         return ERR_CAST(priv->fts.tc.t);
72                 }
73
74                 table_created = true;
75         }
76
77         rule = mlx5_add_flow_rule(priv->fts.tc.t, MLX5_MATCH_OUTER_HEADERS,
78                                   match_c, match_v,
79                                   action, flow_tag,
80                                   action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST ? &dest : NULL);
81
82         if (IS_ERR(rule) && table_created) {
83                 mlx5_destroy_flow_table(priv->fts.tc.t);
84                 priv->fts.tc.t = NULL;
85         }
86
87         return rule;
88 }
89
90 static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
91                               struct mlx5_flow_rule *rule)
92 {
93         mlx5_del_flow_rule(rule);
94
95         if (!mlx5e_tc_num_filters(priv)) {
96                 mlx5_destroy_flow_table(priv->fts.tc.t);
97                 priv->fts.tc.t = NULL;
98         }
99 }
100
101 static int parse_cls_flower(struct mlx5e_priv *priv,
102                             u32 *match_c, u32 *match_v,
103                             struct tc_cls_flower_offload *f)
104 {
105         void *headers_c = MLX5_ADDR_OF(fte_match_param, match_c, outer_headers);
106         void *headers_v = MLX5_ADDR_OF(fte_match_param, match_v, outer_headers);
107         u16 addr_type = 0;
108         u8 ip_proto = 0;
109
110         if (f->dissector->used_keys &
111             ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
112               BIT(FLOW_DISSECTOR_KEY_BASIC) |
113               BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
114               BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
115               BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
116               BIT(FLOW_DISSECTOR_KEY_PORTS))) {
117                 netdev_warn(priv->netdev, "Unsupported key used: 0x%x\n",
118                             f->dissector->used_keys);
119                 return -EOPNOTSUPP;
120         }
121
122         if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
123                 struct flow_dissector_key_control *key =
124                         skb_flow_dissector_target(f->dissector,
125                                                   FLOW_DISSECTOR_KEY_BASIC,
126                                                   f->key);
127                 addr_type = key->addr_type;
128         }
129
130         if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
131                 struct flow_dissector_key_basic *key =
132                         skb_flow_dissector_target(f->dissector,
133                                                   FLOW_DISSECTOR_KEY_BASIC,
134                                                   f->key);
135                 struct flow_dissector_key_basic *mask =
136                         skb_flow_dissector_target(f->dissector,
137                                                   FLOW_DISSECTOR_KEY_BASIC,
138                                                   f->mask);
139                 ip_proto = key->ip_proto;
140
141                 MLX5_SET(fte_match_set_lyr_2_4, headers_c, ethertype,
142                          ntohs(mask->n_proto));
143                 MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype,
144                          ntohs(key->n_proto));
145
146                 MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol,
147                          mask->ip_proto);
148                 MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol,
149                          key->ip_proto);
150         }
151
152         if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
153                 struct flow_dissector_key_eth_addrs *key =
154                         skb_flow_dissector_target(f->dissector,
155                                                   FLOW_DISSECTOR_KEY_ETH_ADDRS,
156                                                   f->key);
157                 struct flow_dissector_key_eth_addrs *mask =
158                         skb_flow_dissector_target(f->dissector,
159                                                   FLOW_DISSECTOR_KEY_ETH_ADDRS,
160                                                   f->mask);
161
162                 ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
163                                              dmac_47_16),
164                                 mask->dst);
165                 ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
166                                              dmac_47_16),
167                                 key->dst);
168
169                 ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
170                                              smac_47_16),
171                                 mask->src);
172                 ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
173                                              smac_47_16),
174                                 key->src);
175         }
176
177         if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
178                 struct flow_dissector_key_ipv4_addrs *key =
179                         skb_flow_dissector_target(f->dissector,
180                                                   FLOW_DISSECTOR_KEY_IPV4_ADDRS,
181                                                   f->key);
182                 struct flow_dissector_key_ipv4_addrs *mask =
183                         skb_flow_dissector_target(f->dissector,
184                                                   FLOW_DISSECTOR_KEY_IPV4_ADDRS,
185                                                   f->mask);
186
187                 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
188                                     src_ipv4_src_ipv6.ipv4_layout.ipv4),
189                        &mask->src, sizeof(mask->src));
190                 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
191                                     src_ipv4_src_ipv6.ipv4_layout.ipv4),
192                        &key->src, sizeof(key->src));
193                 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
194                                     dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
195                        &mask->dst, sizeof(mask->dst));
196                 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
197                                     dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
198                        &key->dst, sizeof(key->dst));
199         }
200
201         if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
202                 struct flow_dissector_key_ipv6_addrs *key =
203                         skb_flow_dissector_target(f->dissector,
204                                                   FLOW_DISSECTOR_KEY_IPV6_ADDRS,
205                                                   f->key);
206                 struct flow_dissector_key_ipv6_addrs *mask =
207                         skb_flow_dissector_target(f->dissector,
208                                                   FLOW_DISSECTOR_KEY_IPV6_ADDRS,
209                                                   f->mask);
210
211                 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
212                                     src_ipv4_src_ipv6.ipv6_layout.ipv6),
213                        &mask->src, sizeof(mask->src));
214                 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
215                                     src_ipv4_src_ipv6.ipv6_layout.ipv6),
216                        &key->src, sizeof(key->src));
217
218                 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
219                                     dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
220                        &mask->dst, sizeof(mask->dst));
221                 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
222                                     dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
223                        &key->dst, sizeof(key->dst));
224         }
225
226         if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
227                 struct flow_dissector_key_ports *key =
228                         skb_flow_dissector_target(f->dissector,
229                                                   FLOW_DISSECTOR_KEY_PORTS,
230                                                   f->key);
231                 struct flow_dissector_key_ports *mask =
232                         skb_flow_dissector_target(f->dissector,
233                                                   FLOW_DISSECTOR_KEY_PORTS,
234                                                   f->mask);
235                 switch (ip_proto) {
236                 case IPPROTO_TCP:
237                         MLX5_SET(fte_match_set_lyr_2_4, headers_c,
238                                  tcp_sport, ntohs(mask->src));
239                         MLX5_SET(fte_match_set_lyr_2_4, headers_v,
240                                  tcp_sport, ntohs(key->src));
241
242                         MLX5_SET(fte_match_set_lyr_2_4, headers_c,
243                                  tcp_dport, ntohs(mask->dst));
244                         MLX5_SET(fte_match_set_lyr_2_4, headers_v,
245                                  tcp_dport, ntohs(key->dst));
246                         break;
247
248                 case IPPROTO_UDP:
249                         MLX5_SET(fte_match_set_lyr_2_4, headers_c,
250                                  udp_sport, ntohs(mask->src));
251                         MLX5_SET(fte_match_set_lyr_2_4, headers_v,
252                                  udp_sport, ntohs(key->src));
253
254                         MLX5_SET(fte_match_set_lyr_2_4, headers_c,
255                                  udp_dport, ntohs(mask->dst));
256                         MLX5_SET(fte_match_set_lyr_2_4, headers_v,
257                                  udp_dport, ntohs(key->dst));
258                         break;
259                 default:
260                         netdev_err(priv->netdev,
261                                    "Only UDP and TCP transport are supported\n");
262                         return -EINVAL;
263                 }
264         }
265
266         return 0;
267 }
268
269 static int parse_tc_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
270                             u32 *action, u32 *flow_tag)
271 {
272         const struct tc_action *a;
273
274         if (tc_no_actions(exts))
275                 return -EINVAL;
276
277         *flow_tag = MLX5_FS_DEFAULT_FLOW_TAG;
278         *action = 0;
279
280         tc_for_each_action(a, exts) {
281                 /* Only support a single action per rule */
282                 if (*action)
283                         return -EINVAL;
284
285                 if (is_tcf_gact_shot(a)) {
286                         *action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
287                         continue;
288                 }
289
290                 if (is_tcf_skbedit_mark(a)) {
291                         u32 mark = tcf_skbedit_mark(a);
292
293                         if (mark & ~MLX5E_TC_FLOW_ID_MASK) {
294                                 netdev_warn(priv->netdev, "Bad flow mark - only 16 bit is supported: 0x%x\n",
295                                             mark);
296                                 return -EINVAL;
297                         }
298
299                         *flow_tag = mark;
300                         *action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
301                         continue;
302                 }
303
304                 return -EINVAL;
305         }
306
307         return 0;
308 }
309
310 int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol,
311                            struct tc_cls_flower_offload *f)
312 {
313         struct mlx5e_tc_flow_table *tc = &priv->fts.tc;
314         u32 *match_c;
315         u32 *match_v;
316         int err = 0;
317         u32 flow_tag;
318         u32 action;
319         struct mlx5e_tc_flow *flow;
320         struct mlx5_flow_rule *old = NULL;
321
322         flow = rhashtable_lookup_fast(&tc->ht, &f->cookie,
323                                       tc->ht_params);
324         if (flow)
325                 old = flow->rule;
326         else
327                 flow = kzalloc(sizeof(*flow), GFP_KERNEL);
328
329         match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
330         match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
331         if (!match_c || !match_v || !flow) {
332                 err = -ENOMEM;
333                 goto err_free;
334         }
335
336         flow->cookie = f->cookie;
337
338         err = parse_cls_flower(priv, match_c, match_v, f);
339         if (err < 0)
340                 goto err_free;
341
342         err = parse_tc_actions(priv, f->exts, &action, &flow_tag);
343         if (err < 0)
344                 goto err_free;
345
346         err = rhashtable_insert_fast(&tc->ht, &flow->node,
347                                      tc->ht_params);
348         if (err)
349                 goto err_free;
350
351         flow->rule = mlx5e_tc_add_flow(priv, match_c, match_v, action,
352                                        flow_tag);
353         if (IS_ERR(flow->rule)) {
354                 err = PTR_ERR(flow->rule);
355                 goto err_hash_del;
356         }
357
358         if (old)
359                 mlx5e_tc_del_flow(priv, old);
360
361         goto out;
362
363 err_hash_del:
364         rhashtable_remove_fast(&tc->ht, &flow->node, tc->ht_params);
365
366 err_free:
367         if (!old)
368                 kfree(flow);
369 out:
370         kfree(match_c);
371         kfree(match_v);
372         return err;
373 }
374
375 int mlx5e_delete_flower(struct mlx5e_priv *priv,
376                         struct tc_cls_flower_offload *f)
377 {
378         struct mlx5e_tc_flow *flow;
379         struct mlx5e_tc_flow_table *tc = &priv->fts.tc;
380
381         flow = rhashtable_lookup_fast(&tc->ht, &f->cookie,
382                                       tc->ht_params);
383         if (!flow)
384                 return -EINVAL;
385
386         rhashtable_remove_fast(&tc->ht, &flow->node, tc->ht_params);
387
388         mlx5e_tc_del_flow(priv, flow->rule);
389
390         kfree(flow);
391
392         return 0;
393 }
394
395 static const struct rhashtable_params mlx5e_tc_flow_ht_params = {
396         .head_offset = offsetof(struct mlx5e_tc_flow, node),
397         .key_offset = offsetof(struct mlx5e_tc_flow, cookie),
398         .key_len = sizeof(((struct mlx5e_tc_flow *)0)->cookie),
399         .automatic_shrinking = true,
400 };
401
402 int mlx5e_tc_init(struct mlx5e_priv *priv)
403 {
404         struct mlx5e_tc_flow_table *tc = &priv->fts.tc;
405
406         tc->ht_params = mlx5e_tc_flow_ht_params;
407         return rhashtable_init(&tc->ht, &tc->ht_params);
408 }
409
410 static void _mlx5e_tc_del_flow(void *ptr, void *arg)
411 {
412         struct mlx5e_tc_flow *flow = ptr;
413         struct mlx5e_priv *priv = arg;
414
415         mlx5e_tc_del_flow(priv, flow->rule);
416         kfree(flow);
417 }
418
419 void mlx5e_tc_cleanup(struct mlx5e_priv *priv)
420 {
421         struct mlx5e_tc_flow_table *tc = &priv->fts.tc;
422
423         rhashtable_free_and_destroy(&tc->ht, _mlx5e_tc_del_flow, priv);
424
425         if (!IS_ERR_OR_NULL(priv->fts.tc.t)) {
426                 mlx5_destroy_flow_table(priv->fts.tc.t);
427                 priv->fts.tc.t = NULL;
428         }
429 }