a390117ed34c7442006f890e8dbe1834b2888b9f
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx5 / core / eswitch_offloads.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 <linux/etherdevice.h>
34 #include <linux/mlx5/driver.h>
35 #include <linux/mlx5/mlx5_ifc.h>
36 #include <linux/mlx5/vport.h>
37 #include <linux/mlx5/fs.h>
38 #include "mlx5_core.h"
39 #include "eswitch.h"
40
41 enum {
42         FDB_FAST_PATH = 0,
43         FDB_SLOW_PATH
44 };
45
46 struct mlx5_flow_handle *
47 mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
48                                 struct mlx5_flow_spec *spec,
49                                 struct mlx5_esw_flow_attr *attr)
50 {
51         struct mlx5_flow_destination dest[2] = {};
52         struct mlx5_flow_act flow_act = {0};
53         struct mlx5_fc *counter = NULL;
54         struct mlx5_flow_handle *rule;
55         void *misc;
56         int i = 0;
57
58         if (esw->mode != SRIOV_OFFLOADS)
59                 return ERR_PTR(-EOPNOTSUPP);
60
61         flow_act.action = attr->action;
62
63         if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
64                 dest[i].type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
65                 dest[i].vport_num = attr->out_rep->vport;
66                 i++;
67         }
68         if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
69                 counter = mlx5_fc_create(esw->dev, true);
70                 if (IS_ERR(counter))
71                         return ERR_CAST(counter);
72                 dest[i].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
73                 dest[i].counter = counter;
74                 i++;
75         }
76
77         misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
78         MLX5_SET(fte_match_set_misc, misc, source_port, attr->in_rep->vport);
79
80         misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
81         MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
82
83         spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS |
84                                       MLX5_MATCH_MISC_PARAMETERS;
85
86         rule = mlx5_add_flow_rules((struct mlx5_flow_table *)esw->fdb_table.fdb,
87                                    spec, &flow_act, dest, i);
88         if (IS_ERR(rule))
89                 mlx5_fc_destroy(esw->dev, counter);
90
91         return rule;
92 }
93
94 static int esw_set_global_vlan_pop(struct mlx5_eswitch *esw, u8 val)
95 {
96         struct mlx5_eswitch_rep *rep;
97         int vf_vport, err = 0;
98
99         esw_debug(esw->dev, "%s applying global %s policy\n", __func__, val ? "pop" : "none");
100         for (vf_vport = 1; vf_vport < esw->enabled_vports; vf_vport++) {
101                 rep = &esw->offloads.vport_reps[vf_vport];
102                 if (!rep->valid)
103                         continue;
104
105                 err = __mlx5_eswitch_set_vport_vlan(esw, rep->vport, 0, 0, val);
106                 if (err)
107                         goto out;
108         }
109
110 out:
111         return err;
112 }
113
114 static struct mlx5_eswitch_rep *
115 esw_vlan_action_get_vport(struct mlx5_esw_flow_attr *attr, bool push, bool pop)
116 {
117         struct mlx5_eswitch_rep *in_rep, *out_rep, *vport = NULL;
118
119         in_rep  = attr->in_rep;
120         out_rep = attr->out_rep;
121
122         if (push)
123                 vport = in_rep;
124         else if (pop)
125                 vport = out_rep;
126         else
127                 vport = in_rep;
128
129         return vport;
130 }
131
132 static int esw_add_vlan_action_check(struct mlx5_esw_flow_attr *attr,
133                                      bool push, bool pop, bool fwd)
134 {
135         struct mlx5_eswitch_rep *in_rep, *out_rep;
136
137         if ((push || pop) && !fwd)
138                 goto out_notsupp;
139
140         in_rep  = attr->in_rep;
141         out_rep = attr->out_rep;
142
143         if (push && in_rep->vport == FDB_UPLINK_VPORT)
144                 goto out_notsupp;
145
146         if (pop && out_rep->vport == FDB_UPLINK_VPORT)
147                 goto out_notsupp;
148
149         /* vport has vlan push configured, can't offload VF --> wire rules w.o it */
150         if (!push && !pop && fwd)
151                 if (in_rep->vlan && out_rep->vport == FDB_UPLINK_VPORT)
152                         goto out_notsupp;
153
154         /* protects against (1) setting rules with different vlans to push and
155          * (2) setting rules w.o vlans (attr->vlan = 0) && w. vlans to push (!= 0)
156          */
157         if (push && in_rep->vlan_refcount && (in_rep->vlan != attr->vlan))
158                 goto out_notsupp;
159
160         return 0;
161
162 out_notsupp:
163         return -ENOTSUPP;
164 }
165
166 int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw,
167                                  struct mlx5_esw_flow_attr *attr)
168 {
169         struct offloads_fdb *offloads = &esw->fdb_table.offloads;
170         struct mlx5_eswitch_rep *vport = NULL;
171         bool push, pop, fwd;
172         int err = 0;
173
174         push = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH);
175         pop  = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP);
176         fwd  = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST);
177
178         err = esw_add_vlan_action_check(attr, push, pop, fwd);
179         if (err)
180                 return err;
181
182         attr->vlan_handled = false;
183
184         vport = esw_vlan_action_get_vport(attr, push, pop);
185
186         if (!push && !pop && fwd) {
187                 /* tracks VF --> wire rules without vlan push action */
188                 if (attr->out_rep->vport == FDB_UPLINK_VPORT) {
189                         vport->vlan_refcount++;
190                         attr->vlan_handled = true;
191                 }
192
193                 return 0;
194         }
195
196         if (!push && !pop)
197                 return 0;
198
199         if (!(offloads->vlan_push_pop_refcount)) {
200                 /* it's the 1st vlan rule, apply global vlan pop policy */
201                 err = esw_set_global_vlan_pop(esw, SET_VLAN_STRIP);
202                 if (err)
203                         goto out;
204         }
205         offloads->vlan_push_pop_refcount++;
206
207         if (push) {
208                 if (vport->vlan_refcount)
209                         goto skip_set_push;
210
211                 err = __mlx5_eswitch_set_vport_vlan(esw, vport->vport, attr->vlan, 0,
212                                                     SET_VLAN_INSERT | SET_VLAN_STRIP);
213                 if (err)
214                         goto out;
215                 vport->vlan = attr->vlan;
216 skip_set_push:
217                 vport->vlan_refcount++;
218         }
219 out:
220         if (!err)
221                 attr->vlan_handled = true;
222         return err;
223 }
224
225 int mlx5_eswitch_del_vlan_action(struct mlx5_eswitch *esw,
226                                  struct mlx5_esw_flow_attr *attr)
227 {
228         struct offloads_fdb *offloads = &esw->fdb_table.offloads;
229         struct mlx5_eswitch_rep *vport = NULL;
230         bool push, pop, fwd;
231         int err = 0;
232
233         if (!attr->vlan_handled)
234                 return 0;
235
236         push = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH);
237         pop  = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP);
238         fwd  = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST);
239
240         vport = esw_vlan_action_get_vport(attr, push, pop);
241
242         if (!push && !pop && fwd) {
243                 /* tracks VF --> wire rules without vlan push action */
244                 if (attr->out_rep->vport == FDB_UPLINK_VPORT)
245                         vport->vlan_refcount--;
246
247                 return 0;
248         }
249
250         if (push) {
251                 vport->vlan_refcount--;
252                 if (vport->vlan_refcount)
253                         goto skip_unset_push;
254
255                 vport->vlan = 0;
256                 err = __mlx5_eswitch_set_vport_vlan(esw, vport->vport,
257                                                     0, 0, SET_VLAN_STRIP);
258                 if (err)
259                         goto out;
260         }
261
262 skip_unset_push:
263         offloads->vlan_push_pop_refcount--;
264         if (offloads->vlan_push_pop_refcount)
265                 return 0;
266
267         /* no more vlan rules, stop global vlan pop policy */
268         err = esw_set_global_vlan_pop(esw, 0);
269
270 out:
271         return err;
272 }
273
274 static struct mlx5_flow_handle *
275 mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *esw, int vport, u32 sqn)
276 {
277         struct mlx5_flow_act flow_act = {0};
278         struct mlx5_flow_destination dest;
279         struct mlx5_flow_handle *flow_rule;
280         struct mlx5_flow_spec *spec;
281         void *misc;
282
283         spec = mlx5_vzalloc(sizeof(*spec));
284         if (!spec) {
285                 esw_warn(esw->dev, "FDB: Failed to alloc match parameters\n");
286                 flow_rule = ERR_PTR(-ENOMEM);
287                 goto out;
288         }
289
290         misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
291         MLX5_SET(fte_match_set_misc, misc, source_sqn, sqn);
292         MLX5_SET(fte_match_set_misc, misc, source_port, 0x0); /* source vport is 0 */
293
294         misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
295         MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_sqn);
296         MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
297
298         spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;
299         dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
300         dest.vport_num = vport;
301         flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
302
303         flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.fdb, spec,
304                                         &flow_act, &dest, 1);
305         if (IS_ERR(flow_rule))
306                 esw_warn(esw->dev, "FDB: Failed to add send to vport rule err %ld\n", PTR_ERR(flow_rule));
307 out:
308         kvfree(spec);
309         return flow_rule;
310 }
311
312 void mlx5_eswitch_sqs2vport_stop(struct mlx5_eswitch *esw,
313                                  struct mlx5_eswitch_rep *rep)
314 {
315         struct mlx5_esw_sq *esw_sq, *tmp;
316
317         if (esw->mode != SRIOV_OFFLOADS)
318                 return;
319
320         list_for_each_entry_safe(esw_sq, tmp, &rep->vport_sqs_list, list) {
321                 mlx5_del_flow_rules(esw_sq->send_to_vport_rule);
322                 list_del(&esw_sq->list);
323                 kfree(esw_sq);
324         }
325 }
326
327 int mlx5_eswitch_sqs2vport_start(struct mlx5_eswitch *esw,
328                                  struct mlx5_eswitch_rep *rep,
329                                  u16 *sqns_array, int sqns_num)
330 {
331         struct mlx5_flow_handle *flow_rule;
332         struct mlx5_esw_sq *esw_sq;
333         int err;
334         int i;
335
336         if (esw->mode != SRIOV_OFFLOADS)
337                 return 0;
338
339         for (i = 0; i < sqns_num; i++) {
340                 esw_sq = kzalloc(sizeof(*esw_sq), GFP_KERNEL);
341                 if (!esw_sq) {
342                         err = -ENOMEM;
343                         goto out_err;
344                 }
345
346                 /* Add re-inject rule to the PF/representor sqs */
347                 flow_rule = mlx5_eswitch_add_send_to_vport_rule(esw,
348                                                                 rep->vport,
349                                                                 sqns_array[i]);
350                 if (IS_ERR(flow_rule)) {
351                         err = PTR_ERR(flow_rule);
352                         kfree(esw_sq);
353                         goto out_err;
354                 }
355                 esw_sq->send_to_vport_rule = flow_rule;
356                 list_add(&esw_sq->list, &rep->vport_sqs_list);
357         }
358         return 0;
359
360 out_err:
361         mlx5_eswitch_sqs2vport_stop(esw, rep);
362         return err;
363 }
364
365 static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw)
366 {
367         struct mlx5_flow_act flow_act = {0};
368         struct mlx5_flow_destination dest;
369         struct mlx5_flow_handle *flow_rule = NULL;
370         struct mlx5_flow_spec *spec;
371         int err = 0;
372
373         spec = mlx5_vzalloc(sizeof(*spec));
374         if (!spec) {
375                 esw_warn(esw->dev, "FDB: Failed to alloc match parameters\n");
376                 err = -ENOMEM;
377                 goto out;
378         }
379
380         dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
381         dest.vport_num = 0;
382         flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
383
384         flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.fdb, spec,
385                                         &flow_act, &dest, 1);
386         if (IS_ERR(flow_rule)) {
387                 err = PTR_ERR(flow_rule);
388                 esw_warn(esw->dev,  "FDB: Failed to add miss flow rule err %d\n", err);
389                 goto out;
390         }
391
392         esw->fdb_table.offloads.miss_rule = flow_rule;
393 out:
394         kvfree(spec);
395         return err;
396 }
397
398 #define MAX_PF_SQ 256
399 #define ESW_OFFLOADS_NUM_ENTRIES (1 << 13) /* 8K */
400 #define ESW_OFFLOADS_NUM_GROUPS  4
401
402 static int esw_create_offloads_fdb_table(struct mlx5_eswitch *esw, int nvports)
403 {
404         int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
405         struct mlx5_core_dev *dev = esw->dev;
406         struct mlx5_flow_namespace *root_ns;
407         struct mlx5_flow_table *fdb = NULL;
408         struct mlx5_flow_group *g;
409         u32 *flow_group_in;
410         void *match_criteria;
411         int table_size, ix, err = 0;
412
413         flow_group_in = mlx5_vzalloc(inlen);
414         if (!flow_group_in)
415                 return -ENOMEM;
416
417         root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
418         if (!root_ns) {
419                 esw_warn(dev, "Failed to get FDB flow namespace\n");
420                 goto ns_err;
421         }
422
423         esw_debug(dev, "Create offloads FDB table, log_max_size(%d)\n",
424                   MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
425
426         fdb = mlx5_create_auto_grouped_flow_table(root_ns, FDB_FAST_PATH,
427                                                   ESW_OFFLOADS_NUM_ENTRIES,
428                                                   ESW_OFFLOADS_NUM_GROUPS, 0,
429                                                   0);
430         if (IS_ERR(fdb)) {
431                 err = PTR_ERR(fdb);
432                 esw_warn(dev, "Failed to create Fast path FDB Table err %d\n", err);
433                 goto fast_fdb_err;
434         }
435         esw->fdb_table.fdb = fdb;
436
437         table_size = nvports + MAX_PF_SQ + 1;
438         fdb = mlx5_create_flow_table(root_ns, FDB_SLOW_PATH, table_size, 0, 0);
439         if (IS_ERR(fdb)) {
440                 err = PTR_ERR(fdb);
441                 esw_warn(dev, "Failed to create slow path FDB Table err %d\n", err);
442                 goto slow_fdb_err;
443         }
444         esw->fdb_table.offloads.fdb = fdb;
445
446         /* create send-to-vport group */
447         memset(flow_group_in, 0, inlen);
448         MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
449                  MLX5_MATCH_MISC_PARAMETERS);
450
451         match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
452
453         MLX5_SET_TO_ONES(fte_match_param, match_criteria, misc_parameters.source_sqn);
454         MLX5_SET_TO_ONES(fte_match_param, match_criteria, misc_parameters.source_port);
455
456         ix = nvports + MAX_PF_SQ;
457         MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
458         MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, ix - 1);
459
460         g = mlx5_create_flow_group(fdb, flow_group_in);
461         if (IS_ERR(g)) {
462                 err = PTR_ERR(g);
463                 esw_warn(dev, "Failed to create send-to-vport flow group err(%d)\n", err);
464                 goto send_vport_err;
465         }
466         esw->fdb_table.offloads.send_to_vport_grp = g;
467
468         /* create miss group */
469         memset(flow_group_in, 0, inlen);
470         MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, 0);
471
472         MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, ix);
473         MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, ix + 1);
474
475         g = mlx5_create_flow_group(fdb, flow_group_in);
476         if (IS_ERR(g)) {
477                 err = PTR_ERR(g);
478                 esw_warn(dev, "Failed to create miss flow group err(%d)\n", err);
479                 goto miss_err;
480         }
481         esw->fdb_table.offloads.miss_grp = g;
482
483         err = esw_add_fdb_miss_rule(esw);
484         if (err)
485                 goto miss_rule_err;
486
487         return 0;
488
489 miss_rule_err:
490         mlx5_destroy_flow_group(esw->fdb_table.offloads.miss_grp);
491 miss_err:
492         mlx5_destroy_flow_group(esw->fdb_table.offloads.send_to_vport_grp);
493 send_vport_err:
494         mlx5_destroy_flow_table(esw->fdb_table.offloads.fdb);
495 slow_fdb_err:
496         mlx5_destroy_flow_table(esw->fdb_table.fdb);
497 fast_fdb_err:
498 ns_err:
499         kvfree(flow_group_in);
500         return err;
501 }
502
503 static void esw_destroy_offloads_fdb_table(struct mlx5_eswitch *esw)
504 {
505         if (!esw->fdb_table.fdb)
506                 return;
507
508         esw_debug(esw->dev, "Destroy offloads FDB Table\n");
509         mlx5_del_flow_rules(esw->fdb_table.offloads.miss_rule);
510         mlx5_destroy_flow_group(esw->fdb_table.offloads.send_to_vport_grp);
511         mlx5_destroy_flow_group(esw->fdb_table.offloads.miss_grp);
512
513         mlx5_destroy_flow_table(esw->fdb_table.offloads.fdb);
514         mlx5_destroy_flow_table(esw->fdb_table.fdb);
515 }
516
517 static int esw_create_offloads_table(struct mlx5_eswitch *esw)
518 {
519         struct mlx5_flow_namespace *ns;
520         struct mlx5_flow_table *ft_offloads;
521         struct mlx5_core_dev *dev = esw->dev;
522         int err = 0;
523
524         ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_OFFLOADS);
525         if (!ns) {
526                 esw_warn(esw->dev, "Failed to get offloads flow namespace\n");
527                 return -ENOMEM;
528         }
529
530         ft_offloads = mlx5_create_flow_table(ns, 0, dev->priv.sriov.num_vfs + 2, 0, 0);
531         if (IS_ERR(ft_offloads)) {
532                 err = PTR_ERR(ft_offloads);
533                 esw_warn(esw->dev, "Failed to create offloads table, err %d\n", err);
534                 return err;
535         }
536
537         esw->offloads.ft_offloads = ft_offloads;
538         return 0;
539 }
540
541 static void esw_destroy_offloads_table(struct mlx5_eswitch *esw)
542 {
543         struct mlx5_esw_offload *offloads = &esw->offloads;
544
545         mlx5_destroy_flow_table(offloads->ft_offloads);
546 }
547
548 static int esw_create_vport_rx_group(struct mlx5_eswitch *esw)
549 {
550         int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
551         struct mlx5_flow_group *g;
552         struct mlx5_priv *priv = &esw->dev->priv;
553         u32 *flow_group_in;
554         void *match_criteria, *misc;
555         int err = 0;
556         int nvports = priv->sriov.num_vfs + 2;
557
558         flow_group_in = mlx5_vzalloc(inlen);
559         if (!flow_group_in)
560                 return -ENOMEM;
561
562         /* create vport rx group */
563         memset(flow_group_in, 0, inlen);
564         MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
565                  MLX5_MATCH_MISC_PARAMETERS);
566
567         match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
568         misc = MLX5_ADDR_OF(fte_match_param, match_criteria, misc_parameters);
569         MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
570
571         MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
572         MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, nvports - 1);
573
574         g = mlx5_create_flow_group(esw->offloads.ft_offloads, flow_group_in);
575
576         if (IS_ERR(g)) {
577                 err = PTR_ERR(g);
578                 mlx5_core_warn(esw->dev, "Failed to create vport rx group err %d\n", err);
579                 goto out;
580         }
581
582         esw->offloads.vport_rx_group = g;
583 out:
584         kfree(flow_group_in);
585         return err;
586 }
587
588 static void esw_destroy_vport_rx_group(struct mlx5_eswitch *esw)
589 {
590         mlx5_destroy_flow_group(esw->offloads.vport_rx_group);
591 }
592
593 struct mlx5_flow_handle *
594 mlx5_eswitch_create_vport_rx_rule(struct mlx5_eswitch *esw, int vport, u32 tirn)
595 {
596         struct mlx5_flow_act flow_act = {0};
597         struct mlx5_flow_destination dest;
598         struct mlx5_flow_handle *flow_rule;
599         struct mlx5_flow_spec *spec;
600         void *misc;
601
602         spec = mlx5_vzalloc(sizeof(*spec));
603         if (!spec) {
604                 esw_warn(esw->dev, "Failed to alloc match parameters\n");
605                 flow_rule = ERR_PTR(-ENOMEM);
606                 goto out;
607         }
608
609         misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
610         MLX5_SET(fte_match_set_misc, misc, source_port, vport);
611
612         misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
613         MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
614
615         spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;
616         dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR;
617         dest.tir_num = tirn;
618
619         flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
620         flow_rule = mlx5_add_flow_rules(esw->offloads.ft_offloads, spec,
621                                        &flow_act, &dest, 1);
622         if (IS_ERR(flow_rule)) {
623                 esw_warn(esw->dev, "fs offloads: Failed to add vport rx rule err %ld\n", PTR_ERR(flow_rule));
624                 goto out;
625         }
626
627 out:
628         kvfree(spec);
629         return flow_rule;
630 }
631
632 static int esw_offloads_start(struct mlx5_eswitch *esw)
633 {
634         int err, err1, num_vfs = esw->dev->priv.sriov.num_vfs;
635
636         if (esw->mode != SRIOV_LEGACY) {
637                 esw_warn(esw->dev, "Can't set offloads mode, SRIOV legacy not enabled\n");
638                 return -EINVAL;
639         }
640
641         mlx5_eswitch_disable_sriov(esw);
642         err = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_OFFLOADS);
643         if (err) {
644                 esw_warn(esw->dev, "Failed setting eswitch to offloads, err %d\n", err);
645                 err1 = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_LEGACY);
646                 if (err1)
647                         esw_warn(esw->dev, "Failed setting eswitch back to legacy, err %d\n", err);
648         }
649         return err;
650 }
651
652 int esw_offloads_init(struct mlx5_eswitch *esw, int nvports)
653 {
654         struct mlx5_eswitch_rep *rep;
655         int vport;
656         int err;
657
658         err = esw_create_offloads_fdb_table(esw, nvports);
659         if (err)
660                 return err;
661
662         err = esw_create_offloads_table(esw);
663         if (err)
664                 goto create_ft_err;
665
666         err = esw_create_vport_rx_group(esw);
667         if (err)
668                 goto create_fg_err;
669
670         for (vport = 0; vport < nvports; vport++) {
671                 rep = &esw->offloads.vport_reps[vport];
672                 if (!rep->valid)
673                         continue;
674
675                 err = rep->load(esw, rep);
676                 if (err)
677                         goto err_reps;
678         }
679         return 0;
680
681 err_reps:
682         for (vport--; vport >= 0; vport--) {
683                 rep = &esw->offloads.vport_reps[vport];
684                 if (!rep->valid)
685                         continue;
686                 rep->unload(esw, rep);
687         }
688         esw_destroy_vport_rx_group(esw);
689
690 create_fg_err:
691         esw_destroy_offloads_table(esw);
692
693 create_ft_err:
694         esw_destroy_offloads_fdb_table(esw);
695         return err;
696 }
697
698 static int esw_offloads_stop(struct mlx5_eswitch *esw)
699 {
700         int err, err1, num_vfs = esw->dev->priv.sriov.num_vfs;
701
702         mlx5_eswitch_disable_sriov(esw);
703         err = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_LEGACY);
704         if (err) {
705                 esw_warn(esw->dev, "Failed setting eswitch to legacy, err %d\n", err);
706                 err1 = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_OFFLOADS);
707                 if (err1)
708                         esw_warn(esw->dev, "Failed setting eswitch back to offloads, err %d\n", err);
709         }
710
711         return err;
712 }
713
714 void esw_offloads_cleanup(struct mlx5_eswitch *esw, int nvports)
715 {
716         struct mlx5_eswitch_rep *rep;
717         int vport;
718
719         for (vport = 0; vport < nvports; vport++) {
720                 rep = &esw->offloads.vport_reps[vport];
721                 if (!rep->valid)
722                         continue;
723                 rep->unload(esw, rep);
724         }
725
726         esw_destroy_vport_rx_group(esw);
727         esw_destroy_offloads_table(esw);
728         esw_destroy_offloads_fdb_table(esw);
729 }
730
731 static int esw_mode_from_devlink(u16 mode, u16 *mlx5_mode)
732 {
733         switch (mode) {
734         case DEVLINK_ESWITCH_MODE_LEGACY:
735                 *mlx5_mode = SRIOV_LEGACY;
736                 break;
737         case DEVLINK_ESWITCH_MODE_SWITCHDEV:
738                 *mlx5_mode = SRIOV_OFFLOADS;
739                 break;
740         default:
741                 return -EINVAL;
742         }
743
744         return 0;
745 }
746
747 static int esw_mode_to_devlink(u16 mlx5_mode, u16 *mode)
748 {
749         switch (mlx5_mode) {
750         case SRIOV_LEGACY:
751                 *mode = DEVLINK_ESWITCH_MODE_LEGACY;
752                 break;
753         case SRIOV_OFFLOADS:
754                 *mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
755                 break;
756         default:
757                 return -EINVAL;
758         }
759
760         return 0;
761 }
762
763 int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode)
764 {
765         struct mlx5_core_dev *dev;
766         u16 cur_mlx5_mode, mlx5_mode = 0;
767
768         dev = devlink_priv(devlink);
769
770         if (!MLX5_CAP_GEN(dev, vport_group_manager))
771                 return -EOPNOTSUPP;
772
773         cur_mlx5_mode = dev->priv.eswitch->mode;
774
775         if (cur_mlx5_mode == SRIOV_NONE)
776                 return -EOPNOTSUPP;
777
778         if (esw_mode_from_devlink(mode, &mlx5_mode))
779                 return -EINVAL;
780
781         if (cur_mlx5_mode == mlx5_mode)
782                 return 0;
783
784         if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV)
785                 return esw_offloads_start(dev->priv.eswitch);
786         else if (mode == DEVLINK_ESWITCH_MODE_LEGACY)
787                 return esw_offloads_stop(dev->priv.eswitch);
788         else
789                 return -EINVAL;
790 }
791
792 int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)
793 {
794         struct mlx5_core_dev *dev;
795
796         dev = devlink_priv(devlink);
797
798         if (!MLX5_CAP_GEN(dev, vport_group_manager))
799                 return -EOPNOTSUPP;
800
801         if (dev->priv.eswitch->mode == SRIOV_NONE)
802                 return -EOPNOTSUPP;
803
804         return esw_mode_to_devlink(dev->priv.eswitch->mode, mode);
805 }
806
807 void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw,
808                                      int vport_index,
809                                      struct mlx5_eswitch_rep *__rep)
810 {
811         struct mlx5_esw_offload *offloads = &esw->offloads;
812         struct mlx5_eswitch_rep *rep;
813
814         rep = &offloads->vport_reps[vport_index];
815
816         memset(rep, 0, sizeof(*rep));
817
818         rep->load   = __rep->load;
819         rep->unload = __rep->unload;
820         rep->vport  = __rep->vport;
821         rep->priv_data = __rep->priv_data;
822         ether_addr_copy(rep->hw_id, __rep->hw_id);
823
824         INIT_LIST_HEAD(&rep->vport_sqs_list);
825         rep->valid = true;
826 }
827
828 void mlx5_eswitch_unregister_vport_rep(struct mlx5_eswitch *esw,
829                                        int vport_index)
830 {
831         struct mlx5_esw_offload *offloads = &esw->offloads;
832         struct mlx5_eswitch_rep *rep;
833
834         rep = &offloads->vport_reps[vport_index];
835
836         if (esw->mode == SRIOV_OFFLOADS && esw->vports[vport_index].enabled)
837                 rep->unload(esw, rep);
838
839         rep->valid = false;
840 }