cd504197855b9ded1652e3305c08b0c46e10c084
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlx5 / core / en_arfs.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 "en.h"
34 #include <linux/mlx5/fs.h>
35
36 static void arfs_destroy_table(struct arfs_table *arfs_t)
37 {
38         mlx5_del_flow_rule(arfs_t->default_rule);
39         mlx5e_destroy_flow_table(&arfs_t->ft);
40 }
41
42 void mlx5e_arfs_destroy_tables(struct mlx5e_priv *priv)
43 {
44         int i;
45
46         if (!(priv->netdev->hw_features & NETIF_F_NTUPLE))
47                 return;
48         for (i = 0; i < ARFS_NUM_TYPES; i++) {
49                 if (!IS_ERR_OR_NULL(priv->fs.arfs.arfs_tables[i].ft.t))
50                         arfs_destroy_table(&priv->fs.arfs.arfs_tables[i]);
51         }
52 }
53
54 static int arfs_add_default_rule(struct mlx5e_priv *priv,
55                                  enum arfs_type type)
56 {
57         struct arfs_table *arfs_t = &priv->fs.arfs.arfs_tables[type];
58         struct mlx5_flow_destination dest;
59         u8 match_criteria_enable = 0;
60         u32 *tirn = priv->indir_tirn;
61         u32 *match_criteria;
62         u32 *match_value;
63         int err = 0;
64
65         match_value     = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
66         match_criteria  = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
67         if (!match_value || !match_criteria) {
68                 netdev_err(priv->netdev, "%s: alloc failed\n", __func__);
69                 err = -ENOMEM;
70                 goto out;
71         }
72
73         dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR;
74         switch (type) {
75         case ARFS_IPV4_TCP:
76                 dest.tir_num = tirn[MLX5E_TT_IPV4_TCP];
77                 break;
78         case ARFS_IPV4_UDP:
79                 dest.tir_num = tirn[MLX5E_TT_IPV4_UDP];
80                 break;
81         case ARFS_IPV6_TCP:
82                 dest.tir_num = tirn[MLX5E_TT_IPV6_TCP];
83                 break;
84         case ARFS_IPV6_UDP:
85                 dest.tir_num = tirn[MLX5E_TT_IPV6_UDP];
86                 break;
87         default:
88                 err = -EINVAL;
89                 goto out;
90         }
91
92         arfs_t->default_rule = mlx5_add_flow_rule(arfs_t->ft.t, match_criteria_enable,
93                                                   match_criteria, match_value,
94                                                   MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
95                                                   MLX5_FS_DEFAULT_FLOW_TAG,
96                                                   &dest);
97         if (IS_ERR(arfs_t->default_rule)) {
98                 err = PTR_ERR(arfs_t->default_rule);
99                 arfs_t->default_rule = NULL;
100                 netdev_err(priv->netdev, "%s: add rule failed, arfs type=%d\n",
101                            __func__, type);
102         }
103 out:
104         kvfree(match_criteria);
105         kvfree(match_value);
106         return err;
107 }
108
109 #define MLX5E_ARFS_NUM_GROUPS   2
110 #define MLX5E_ARFS_GROUP1_SIZE  BIT(12)
111 #define MLX5E_ARFS_GROUP2_SIZE  BIT(0)
112 #define MLX5E_ARFS_TABLE_SIZE   (MLX5E_ARFS_GROUP1_SIZE +\
113                                  MLX5E_ARFS_GROUP2_SIZE)
114 static int arfs_create_groups(struct mlx5e_flow_table *ft,
115                               enum  arfs_type type)
116 {
117         int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
118         void *outer_headers_c;
119         int ix = 0;
120         u32 *in;
121         int err;
122         u8 *mc;
123
124         ft->g = kcalloc(MLX5E_ARFS_NUM_GROUPS,
125                         sizeof(*ft->g), GFP_KERNEL);
126         in = mlx5_vzalloc(inlen);
127         if  (!in || !ft->g) {
128                 kvfree(ft->g);
129                 kvfree(in);
130                 return -ENOMEM;
131         }
132
133         mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
134         outer_headers_c = MLX5_ADDR_OF(fte_match_param, mc,
135                                        outer_headers);
136         MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, ethertype);
137         switch (type) {
138         case ARFS_IPV4_TCP:
139         case ARFS_IPV6_TCP:
140                 MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, tcp_dport);
141                 MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, tcp_sport);
142                 break;
143         case ARFS_IPV4_UDP:
144         case ARFS_IPV6_UDP:
145                 MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, udp_dport);
146                 MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, udp_sport);
147                 break;
148         default:
149                 err = -EINVAL;
150                 goto out;
151         }
152
153         switch (type) {
154         case ARFS_IPV4_TCP:
155         case ARFS_IPV4_UDP:
156                 MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c,
157                                  src_ipv4_src_ipv6.ipv4_layout.ipv4);
158                 MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c,
159                                  dst_ipv4_dst_ipv6.ipv4_layout.ipv4);
160                 break;
161         case ARFS_IPV6_TCP:
162         case ARFS_IPV6_UDP:
163                 memset(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c,
164                                     src_ipv4_src_ipv6.ipv6_layout.ipv6),
165                        0xff, 16);
166                 memset(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c,
167                                     dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
168                        0xff, 16);
169                 break;
170         default:
171                 err = -EINVAL;
172                 goto out;
173         }
174
175         MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
176         MLX5_SET_CFG(in, start_flow_index, ix);
177         ix += MLX5E_ARFS_GROUP1_SIZE;
178         MLX5_SET_CFG(in, end_flow_index, ix - 1);
179         ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
180         if (IS_ERR(ft->g[ft->num_groups]))
181                 goto err;
182         ft->num_groups++;
183
184         memset(in, 0, inlen);
185         MLX5_SET_CFG(in, start_flow_index, ix);
186         ix += MLX5E_ARFS_GROUP2_SIZE;
187         MLX5_SET_CFG(in, end_flow_index, ix - 1);
188         ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
189         if (IS_ERR(ft->g[ft->num_groups]))
190                 goto err;
191         ft->num_groups++;
192
193         kvfree(in);
194         return 0;
195
196 err:
197         err = PTR_ERR(ft->g[ft->num_groups]);
198         ft->g[ft->num_groups] = NULL;
199 out:
200         kvfree(in);
201
202         return err;
203 }
204
205 static int arfs_create_table(struct mlx5e_priv *priv,
206                              enum arfs_type type)
207 {
208         struct mlx5e_arfs_tables *arfs = &priv->fs.arfs;
209         struct mlx5e_flow_table *ft = &arfs->arfs_tables[type].ft;
210         int err;
211
212         ft->t = mlx5_create_flow_table(priv->fs.ns, MLX5E_NIC_PRIO,
213                                        MLX5E_ARFS_TABLE_SIZE, MLX5E_ARFS_FT_LEVEL);
214         if (IS_ERR(ft->t)) {
215                 err = PTR_ERR(ft->t);
216                 ft->t = NULL;
217                 return err;
218         }
219
220         err = arfs_create_groups(ft, type);
221         if (err)
222                 goto err;
223
224         err = arfs_add_default_rule(priv, type);
225         if (err)
226                 goto err;
227
228         return 0;
229 err:
230         mlx5e_destroy_flow_table(ft);
231         return err;
232 }
233
234 int mlx5e_arfs_create_tables(struct mlx5e_priv *priv)
235 {
236         int err = 0;
237         int i;
238
239         if (!(priv->netdev->hw_features & NETIF_F_NTUPLE))
240                 return 0;
241
242         for (i = 0; i < ARFS_NUM_TYPES; i++) {
243                 err = arfs_create_table(priv, i);
244                 if (err)
245                         goto err;
246         }
247         return 0;
248 err:
249         mlx5e_arfs_destroy_tables(priv);
250         return err;
251 }