Merge remote-tracking branches 'asoc/topic/max9878', 'asoc/topic/max98927', 'asoc...
[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         /* per flow vlan pop/push is emulated, don't set that into the firmware */
62         flow_act.action = attr->action & ~(MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH | MLX5_FLOW_CONTEXT_ACTION_VLAN_POP);
63
64         if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
65                 dest[i].type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
66                 dest[i].vport_num = attr->out_rep->vport;
67                 i++;
68         }
69         if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
70                 counter = mlx5_fc_create(esw->dev, true);
71                 if (IS_ERR(counter))
72                         return ERR_CAST(counter);
73                 dest[i].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
74                 dest[i].counter = counter;
75                 i++;
76         }
77
78         misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
79         MLX5_SET(fte_match_set_misc, misc, source_port, attr->in_rep->vport);
80
81         misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
82         MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
83
84         spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS |
85                                       MLX5_MATCH_MISC_PARAMETERS;
86         if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DECAP)
87                 spec->match_criteria_enable |= MLX5_MATCH_INNER_HEADERS;
88
89         if (attr->encap)
90                 flow_act.encap_id = attr->encap->encap_id;
91
92         rule = mlx5_add_flow_rules((struct mlx5_flow_table *)esw->fdb_table.fdb,
93                                    spec, &flow_act, dest, i);
94         if (IS_ERR(rule))
95                 mlx5_fc_destroy(esw->dev, counter);
96         else
97                 esw->offloads.num_flows++;
98
99         return rule;
100 }
101
102 void
103 mlx5_eswitch_del_offloaded_rule(struct mlx5_eswitch *esw,
104                                 struct mlx5_flow_handle *rule,
105                                 struct mlx5_esw_flow_attr *attr)
106 {
107         struct mlx5_fc *counter = NULL;
108
109         if (!IS_ERR(rule)) {
110                 counter = mlx5_flow_rule_counter(rule);
111                 mlx5_del_flow_rules(rule);
112                 mlx5_fc_destroy(esw->dev, counter);
113                 esw->offloads.num_flows--;
114         }
115 }
116
117 static int esw_set_global_vlan_pop(struct mlx5_eswitch *esw, u8 val)
118 {
119         struct mlx5_eswitch_rep *rep;
120         int vf_vport, err = 0;
121
122         esw_debug(esw->dev, "%s applying global %s policy\n", __func__, val ? "pop" : "none");
123         for (vf_vport = 1; vf_vport < esw->enabled_vports; vf_vport++) {
124                 rep = &esw->offloads.vport_reps[vf_vport];
125                 if (!rep->valid)
126                         continue;
127
128                 err = __mlx5_eswitch_set_vport_vlan(esw, rep->vport, 0, 0, val);
129                 if (err)
130                         goto out;
131         }
132
133 out:
134         return err;
135 }
136
137 static struct mlx5_eswitch_rep *
138 esw_vlan_action_get_vport(struct mlx5_esw_flow_attr *attr, bool push, bool pop)
139 {
140         struct mlx5_eswitch_rep *in_rep, *out_rep, *vport = NULL;
141
142         in_rep  = attr->in_rep;
143         out_rep = attr->out_rep;
144
145         if (push)
146                 vport = in_rep;
147         else if (pop)
148                 vport = out_rep;
149         else
150                 vport = in_rep;
151
152         return vport;
153 }
154
155 static int esw_add_vlan_action_check(struct mlx5_esw_flow_attr *attr,
156                                      bool push, bool pop, bool fwd)
157 {
158         struct mlx5_eswitch_rep *in_rep, *out_rep;
159
160         if ((push || pop) && !fwd)
161                 goto out_notsupp;
162
163         in_rep  = attr->in_rep;
164         out_rep = attr->out_rep;
165
166         if (push && in_rep->vport == FDB_UPLINK_VPORT)
167                 goto out_notsupp;
168
169         if (pop && out_rep->vport == FDB_UPLINK_VPORT)
170                 goto out_notsupp;
171
172         /* vport has vlan push configured, can't offload VF --> wire rules w.o it */
173         if (!push && !pop && fwd)
174                 if (in_rep->vlan && out_rep->vport == FDB_UPLINK_VPORT)
175                         goto out_notsupp;
176
177         /* protects against (1) setting rules with different vlans to push and
178          * (2) setting rules w.o vlans (attr->vlan = 0) && w. vlans to push (!= 0)
179          */
180         if (push && in_rep->vlan_refcount && (in_rep->vlan != attr->vlan))
181                 goto out_notsupp;
182
183         return 0;
184
185 out_notsupp:
186         return -EOPNOTSUPP;
187 }
188
189 int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw,
190                                  struct mlx5_esw_flow_attr *attr)
191 {
192         struct offloads_fdb *offloads = &esw->fdb_table.offloads;
193         struct mlx5_eswitch_rep *vport = NULL;
194         bool push, pop, fwd;
195         int err = 0;
196
197         push = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH);
198         pop  = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP);
199         fwd  = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST);
200
201         err = esw_add_vlan_action_check(attr, push, pop, fwd);
202         if (err)
203                 return err;
204
205         attr->vlan_handled = false;
206
207         vport = esw_vlan_action_get_vport(attr, push, pop);
208
209         if (!push && !pop && fwd) {
210                 /* tracks VF --> wire rules without vlan push action */
211                 if (attr->out_rep->vport == FDB_UPLINK_VPORT) {
212                         vport->vlan_refcount++;
213                         attr->vlan_handled = true;
214                 }
215
216                 return 0;
217         }
218
219         if (!push && !pop)
220                 return 0;
221
222         if (!(offloads->vlan_push_pop_refcount)) {
223                 /* it's the 1st vlan rule, apply global vlan pop policy */
224                 err = esw_set_global_vlan_pop(esw, SET_VLAN_STRIP);
225                 if (err)
226                         goto out;
227         }
228         offloads->vlan_push_pop_refcount++;
229
230         if (push) {
231                 if (vport->vlan_refcount)
232                         goto skip_set_push;
233
234                 err = __mlx5_eswitch_set_vport_vlan(esw, vport->vport, attr->vlan, 0,
235                                                     SET_VLAN_INSERT | SET_VLAN_STRIP);
236                 if (err)
237                         goto out;
238                 vport->vlan = attr->vlan;
239 skip_set_push:
240                 vport->vlan_refcount++;
241         }
242 out:
243         if (!err)
244                 attr->vlan_handled = true;
245         return err;
246 }
247
248 int mlx5_eswitch_del_vlan_action(struct mlx5_eswitch *esw,
249                                  struct mlx5_esw_flow_attr *attr)
250 {
251         struct offloads_fdb *offloads = &esw->fdb_table.offloads;
252         struct mlx5_eswitch_rep *vport = NULL;
253         bool push, pop, fwd;
254         int err = 0;
255
256         if (!attr->vlan_handled)
257                 return 0;
258
259         push = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH);
260         pop  = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP);
261         fwd  = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST);
262
263         vport = esw_vlan_action_get_vport(attr, push, pop);
264
265         if (!push && !pop && fwd) {
266                 /* tracks VF --> wire rules without vlan push action */
267                 if (attr->out_rep->vport == FDB_UPLINK_VPORT)
268                         vport->vlan_refcount--;
269
270                 return 0;
271         }
272
273         if (push) {
274                 vport->vlan_refcount--;
275                 if (vport->vlan_refcount)
276                         goto skip_unset_push;
277
278                 vport->vlan = 0;
279                 err = __mlx5_eswitch_set_vport_vlan(esw, vport->vport,
280                                                     0, 0, SET_VLAN_STRIP);
281                 if (err)
282                         goto out;
283         }
284
285 skip_unset_push:
286         offloads->vlan_push_pop_refcount--;
287         if (offloads->vlan_push_pop_refcount)
288                 return 0;
289
290         /* no more vlan rules, stop global vlan pop policy */
291         err = esw_set_global_vlan_pop(esw, 0);
292
293 out:
294         return err;
295 }
296
297 static struct mlx5_flow_handle *
298 mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *esw, int vport, u32 sqn)
299 {
300         struct mlx5_flow_act flow_act = {0};
301         struct mlx5_flow_destination dest;
302         struct mlx5_flow_handle *flow_rule;
303         struct mlx5_flow_spec *spec;
304         void *misc;
305
306         spec = mlx5_vzalloc(sizeof(*spec));
307         if (!spec) {
308                 esw_warn(esw->dev, "FDB: Failed to alloc match parameters\n");
309                 flow_rule = ERR_PTR(-ENOMEM);
310                 goto out;
311         }
312
313         misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
314         MLX5_SET(fte_match_set_misc, misc, source_sqn, sqn);
315         MLX5_SET(fte_match_set_misc, misc, source_port, 0x0); /* source vport is 0 */
316
317         misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
318         MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_sqn);
319         MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
320
321         spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;
322         dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
323         dest.vport_num = vport;
324         flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
325
326         flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.fdb, spec,
327                                         &flow_act, &dest, 1);
328         if (IS_ERR(flow_rule))
329                 esw_warn(esw->dev, "FDB: Failed to add send to vport rule err %ld\n", PTR_ERR(flow_rule));
330 out:
331         kvfree(spec);
332         return flow_rule;
333 }
334
335 void mlx5_eswitch_sqs2vport_stop(struct mlx5_eswitch *esw,
336                                  struct mlx5_eswitch_rep *rep)
337 {
338         struct mlx5_esw_sq *esw_sq, *tmp;
339
340         if (esw->mode != SRIOV_OFFLOADS)
341                 return;
342
343         list_for_each_entry_safe(esw_sq, tmp, &rep->vport_sqs_list, list) {
344                 mlx5_del_flow_rules(esw_sq->send_to_vport_rule);
345                 list_del(&esw_sq->list);
346                 kfree(esw_sq);
347         }
348 }
349
350 int mlx5_eswitch_sqs2vport_start(struct mlx5_eswitch *esw,
351                                  struct mlx5_eswitch_rep *rep,
352                                  u16 *sqns_array, int sqns_num)
353 {
354         struct mlx5_flow_handle *flow_rule;
355         struct mlx5_esw_sq *esw_sq;
356         int err;
357         int i;
358
359         if (esw->mode != SRIOV_OFFLOADS)
360                 return 0;
361
362         for (i = 0; i < sqns_num; i++) {
363                 esw_sq = kzalloc(sizeof(*esw_sq), GFP_KERNEL);
364                 if (!esw_sq) {
365                         err = -ENOMEM;
366                         goto out_err;
367                 }
368
369                 /* Add re-inject rule to the PF/representor sqs */
370                 flow_rule = mlx5_eswitch_add_send_to_vport_rule(esw,
371                                                                 rep->vport,
372                                                                 sqns_array[i]);
373                 if (IS_ERR(flow_rule)) {
374                         err = PTR_ERR(flow_rule);
375                         kfree(esw_sq);
376                         goto out_err;
377                 }
378                 esw_sq->send_to_vport_rule = flow_rule;
379                 list_add(&esw_sq->list, &rep->vport_sqs_list);
380         }
381         return 0;
382
383 out_err:
384         mlx5_eswitch_sqs2vport_stop(esw, rep);
385         return err;
386 }
387
388 static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw)
389 {
390         struct mlx5_flow_act flow_act = {0};
391         struct mlx5_flow_destination dest;
392         struct mlx5_flow_handle *flow_rule = NULL;
393         struct mlx5_flow_spec *spec;
394         int err = 0;
395
396         spec = mlx5_vzalloc(sizeof(*spec));
397         if (!spec) {
398                 esw_warn(esw->dev, "FDB: Failed to alloc match parameters\n");
399                 err = -ENOMEM;
400                 goto out;
401         }
402
403         dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
404         dest.vport_num = 0;
405         flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
406
407         flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.fdb, spec,
408                                         &flow_act, &dest, 1);
409         if (IS_ERR(flow_rule)) {
410                 err = PTR_ERR(flow_rule);
411                 esw_warn(esw->dev,  "FDB: Failed to add miss flow rule err %d\n", err);
412                 goto out;
413         }
414
415         esw->fdb_table.offloads.miss_rule = flow_rule;
416 out:
417         kvfree(spec);
418         return err;
419 }
420
421 #define MAX_PF_SQ 256
422 #define ESW_OFFLOADS_NUM_GROUPS  4
423
424 static int esw_create_offloads_fdb_table(struct mlx5_eswitch *esw, int nvports)
425 {
426         int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
427         int table_size, ix, esw_size, err = 0;
428         struct mlx5_core_dev *dev = esw->dev;
429         struct mlx5_flow_namespace *root_ns;
430         struct mlx5_flow_table *fdb = NULL;
431         struct mlx5_flow_group *g;
432         u32 *flow_group_in;
433         void *match_criteria;
434         u32 flags = 0;
435
436         flow_group_in = mlx5_vzalloc(inlen);
437         if (!flow_group_in)
438                 return -ENOMEM;
439
440         root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
441         if (!root_ns) {
442                 esw_warn(dev, "Failed to get FDB flow namespace\n");
443                 err = -EOPNOTSUPP;
444                 goto ns_err;
445         }
446
447         esw_debug(dev, "Create offloads FDB table, min (max esw size(2^%d), max counters(%d)*groups(%d))\n",
448                   MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size),
449                   MLX5_CAP_GEN(dev, max_flow_counter), ESW_OFFLOADS_NUM_GROUPS);
450
451         esw_size = min_t(int, MLX5_CAP_GEN(dev, max_flow_counter) * ESW_OFFLOADS_NUM_GROUPS,
452                          1 << MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
453
454         if (MLX5_CAP_ESW_FLOWTABLE_FDB(dev, encap) &&
455             MLX5_CAP_ESW_FLOWTABLE_FDB(dev, decap))
456                 flags |= MLX5_FLOW_TABLE_TUNNEL_EN;
457
458         fdb = mlx5_create_auto_grouped_flow_table(root_ns, FDB_FAST_PATH,
459                                                   esw_size,
460                                                   ESW_OFFLOADS_NUM_GROUPS, 0,
461                                                   flags);
462         if (IS_ERR(fdb)) {
463                 err = PTR_ERR(fdb);
464                 esw_warn(dev, "Failed to create Fast path FDB Table err %d\n", err);
465                 goto fast_fdb_err;
466         }
467         esw->fdb_table.fdb = fdb;
468
469         table_size = nvports + MAX_PF_SQ + 1;
470         fdb = mlx5_create_flow_table(root_ns, FDB_SLOW_PATH, table_size, 0, 0);
471         if (IS_ERR(fdb)) {
472                 err = PTR_ERR(fdb);
473                 esw_warn(dev, "Failed to create slow path FDB Table err %d\n", err);
474                 goto slow_fdb_err;
475         }
476         esw->fdb_table.offloads.fdb = fdb;
477
478         /* create send-to-vport group */
479         memset(flow_group_in, 0, inlen);
480         MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
481                  MLX5_MATCH_MISC_PARAMETERS);
482
483         match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
484
485         MLX5_SET_TO_ONES(fte_match_param, match_criteria, misc_parameters.source_sqn);
486         MLX5_SET_TO_ONES(fte_match_param, match_criteria, misc_parameters.source_port);
487
488         ix = nvports + MAX_PF_SQ;
489         MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
490         MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, ix - 1);
491
492         g = mlx5_create_flow_group(fdb, flow_group_in);
493         if (IS_ERR(g)) {
494                 err = PTR_ERR(g);
495                 esw_warn(dev, "Failed to create send-to-vport flow group err(%d)\n", err);
496                 goto send_vport_err;
497         }
498         esw->fdb_table.offloads.send_to_vport_grp = g;
499
500         /* create miss group */
501         memset(flow_group_in, 0, inlen);
502         MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, 0);
503
504         MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, ix);
505         MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, ix + 1);
506
507         g = mlx5_create_flow_group(fdb, flow_group_in);
508         if (IS_ERR(g)) {
509                 err = PTR_ERR(g);
510                 esw_warn(dev, "Failed to create miss flow group err(%d)\n", err);
511                 goto miss_err;
512         }
513         esw->fdb_table.offloads.miss_grp = g;
514
515         err = esw_add_fdb_miss_rule(esw);
516         if (err)
517                 goto miss_rule_err;
518
519         return 0;
520
521 miss_rule_err:
522         mlx5_destroy_flow_group(esw->fdb_table.offloads.miss_grp);
523 miss_err:
524         mlx5_destroy_flow_group(esw->fdb_table.offloads.send_to_vport_grp);
525 send_vport_err:
526         mlx5_destroy_flow_table(esw->fdb_table.offloads.fdb);
527 slow_fdb_err:
528         mlx5_destroy_flow_table(esw->fdb_table.fdb);
529 fast_fdb_err:
530 ns_err:
531         kvfree(flow_group_in);
532         return err;
533 }
534
535 static void esw_destroy_offloads_fdb_table(struct mlx5_eswitch *esw)
536 {
537         if (!esw->fdb_table.fdb)
538                 return;
539
540         esw_debug(esw->dev, "Destroy offloads FDB Table\n");
541         mlx5_del_flow_rules(esw->fdb_table.offloads.miss_rule);
542         mlx5_destroy_flow_group(esw->fdb_table.offloads.send_to_vport_grp);
543         mlx5_destroy_flow_group(esw->fdb_table.offloads.miss_grp);
544
545         mlx5_destroy_flow_table(esw->fdb_table.offloads.fdb);
546         mlx5_destroy_flow_table(esw->fdb_table.fdb);
547 }
548
549 static int esw_create_offloads_table(struct mlx5_eswitch *esw)
550 {
551         struct mlx5_flow_namespace *ns;
552         struct mlx5_flow_table *ft_offloads;
553         struct mlx5_core_dev *dev = esw->dev;
554         int err = 0;
555
556         ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_OFFLOADS);
557         if (!ns) {
558                 esw_warn(esw->dev, "Failed to get offloads flow namespace\n");
559                 return -EOPNOTSUPP;
560         }
561
562         ft_offloads = mlx5_create_flow_table(ns, 0, dev->priv.sriov.num_vfs + 2, 0, 0);
563         if (IS_ERR(ft_offloads)) {
564                 err = PTR_ERR(ft_offloads);
565                 esw_warn(esw->dev, "Failed to create offloads table, err %d\n", err);
566                 return err;
567         }
568
569         esw->offloads.ft_offloads = ft_offloads;
570         return 0;
571 }
572
573 static void esw_destroy_offloads_table(struct mlx5_eswitch *esw)
574 {
575         struct mlx5_esw_offload *offloads = &esw->offloads;
576
577         mlx5_destroy_flow_table(offloads->ft_offloads);
578 }
579
580 static int esw_create_vport_rx_group(struct mlx5_eswitch *esw)
581 {
582         int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
583         struct mlx5_flow_group *g;
584         struct mlx5_priv *priv = &esw->dev->priv;
585         u32 *flow_group_in;
586         void *match_criteria, *misc;
587         int err = 0;
588         int nvports = priv->sriov.num_vfs + 2;
589
590         flow_group_in = mlx5_vzalloc(inlen);
591         if (!flow_group_in)
592                 return -ENOMEM;
593
594         /* create vport rx group */
595         memset(flow_group_in, 0, inlen);
596         MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
597                  MLX5_MATCH_MISC_PARAMETERS);
598
599         match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
600         misc = MLX5_ADDR_OF(fte_match_param, match_criteria, misc_parameters);
601         MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
602
603         MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
604         MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, nvports - 1);
605
606         g = mlx5_create_flow_group(esw->offloads.ft_offloads, flow_group_in);
607
608         if (IS_ERR(g)) {
609                 err = PTR_ERR(g);
610                 mlx5_core_warn(esw->dev, "Failed to create vport rx group err %d\n", err);
611                 goto out;
612         }
613
614         esw->offloads.vport_rx_group = g;
615 out:
616         kfree(flow_group_in);
617         return err;
618 }
619
620 static void esw_destroy_vport_rx_group(struct mlx5_eswitch *esw)
621 {
622         mlx5_destroy_flow_group(esw->offloads.vport_rx_group);
623 }
624
625 struct mlx5_flow_handle *
626 mlx5_eswitch_create_vport_rx_rule(struct mlx5_eswitch *esw, int vport, u32 tirn)
627 {
628         struct mlx5_flow_act flow_act = {0};
629         struct mlx5_flow_destination dest;
630         struct mlx5_flow_handle *flow_rule;
631         struct mlx5_flow_spec *spec;
632         void *misc;
633
634         spec = mlx5_vzalloc(sizeof(*spec));
635         if (!spec) {
636                 esw_warn(esw->dev, "Failed to alloc match parameters\n");
637                 flow_rule = ERR_PTR(-ENOMEM);
638                 goto out;
639         }
640
641         misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
642         MLX5_SET(fte_match_set_misc, misc, source_port, vport);
643
644         misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
645         MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
646
647         spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;
648         dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR;
649         dest.tir_num = tirn;
650
651         flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
652         flow_rule = mlx5_add_flow_rules(esw->offloads.ft_offloads, spec,
653                                        &flow_act, &dest, 1);
654         if (IS_ERR(flow_rule)) {
655                 esw_warn(esw->dev, "fs offloads: Failed to add vport rx rule err %ld\n", PTR_ERR(flow_rule));
656                 goto out;
657         }
658
659 out:
660         kvfree(spec);
661         return flow_rule;
662 }
663
664 static int esw_offloads_start(struct mlx5_eswitch *esw)
665 {
666         int err, err1, num_vfs = esw->dev->priv.sriov.num_vfs;
667
668         if (esw->mode != SRIOV_LEGACY) {
669                 esw_warn(esw->dev, "Can't set offloads mode, SRIOV legacy not enabled\n");
670                 return -EINVAL;
671         }
672
673         mlx5_eswitch_disable_sriov(esw);
674         err = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_OFFLOADS);
675         if (err) {
676                 esw_warn(esw->dev, "Failed setting eswitch to offloads, err %d\n", err);
677                 err1 = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_LEGACY);
678                 if (err1)
679                         esw_warn(esw->dev, "Failed setting eswitch back to legacy, err %d\n", err1);
680         }
681         if (esw->offloads.inline_mode == MLX5_INLINE_MODE_NONE) {
682                 if (mlx5_eswitch_inline_mode_get(esw,
683                                                  num_vfs,
684                                                  &esw->offloads.inline_mode)) {
685                         esw->offloads.inline_mode = MLX5_INLINE_MODE_L2;
686                         esw_warn(esw->dev, "Inline mode is different between vports\n");
687                 }
688         }
689         return err;
690 }
691
692 int esw_offloads_init(struct mlx5_eswitch *esw, int nvports)
693 {
694         struct mlx5_eswitch_rep *rep;
695         int vport;
696         int err;
697
698         /* disable PF RoCE so missed packets don't go through RoCE steering */
699         mlx5_dev_list_lock();
700         mlx5_remove_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
701         mlx5_dev_list_unlock();
702
703         err = esw_create_offloads_fdb_table(esw, nvports);
704         if (err)
705                 goto create_fdb_err;
706
707         err = esw_create_offloads_table(esw);
708         if (err)
709                 goto create_ft_err;
710
711         err = esw_create_vport_rx_group(esw);
712         if (err)
713                 goto create_fg_err;
714
715         for (vport = 0; vport < nvports; vport++) {
716                 rep = &esw->offloads.vport_reps[vport];
717                 if (!rep->valid)
718                         continue;
719
720                 err = rep->load(esw, rep);
721                 if (err)
722                         goto err_reps;
723         }
724
725         return 0;
726
727 err_reps:
728         for (vport--; vport >= 0; vport--) {
729                 rep = &esw->offloads.vport_reps[vport];
730                 if (!rep->valid)
731                         continue;
732                 rep->unload(esw, rep);
733         }
734         esw_destroy_vport_rx_group(esw);
735
736 create_fg_err:
737         esw_destroy_offloads_table(esw);
738
739 create_ft_err:
740         esw_destroy_offloads_fdb_table(esw);
741
742 create_fdb_err:
743         /* enable back PF RoCE */
744         mlx5_dev_list_lock();
745         mlx5_add_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
746         mlx5_dev_list_unlock();
747
748         return err;
749 }
750
751 static int esw_offloads_stop(struct mlx5_eswitch *esw)
752 {
753         int err, err1, num_vfs = esw->dev->priv.sriov.num_vfs;
754
755         mlx5_eswitch_disable_sriov(esw);
756         err = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_LEGACY);
757         if (err) {
758                 esw_warn(esw->dev, "Failed setting eswitch to legacy, err %d\n", err);
759                 err1 = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_OFFLOADS);
760                 if (err1)
761                         esw_warn(esw->dev, "Failed setting eswitch back to offloads, err %d\n", err);
762         }
763
764         /* enable back PF RoCE */
765         mlx5_dev_list_lock();
766         mlx5_add_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
767         mlx5_dev_list_unlock();
768
769         return err;
770 }
771
772 void esw_offloads_cleanup(struct mlx5_eswitch *esw, int nvports)
773 {
774         struct mlx5_eswitch_rep *rep;
775         int vport;
776
777         for (vport = 0; vport < nvports; vport++) {
778                 rep = &esw->offloads.vport_reps[vport];
779                 if (!rep->valid)
780                         continue;
781                 rep->unload(esw, rep);
782         }
783
784         esw_destroy_vport_rx_group(esw);
785         esw_destroy_offloads_table(esw);
786         esw_destroy_offloads_fdb_table(esw);
787 }
788
789 static int esw_mode_from_devlink(u16 mode, u16 *mlx5_mode)
790 {
791         switch (mode) {
792         case DEVLINK_ESWITCH_MODE_LEGACY:
793                 *mlx5_mode = SRIOV_LEGACY;
794                 break;
795         case DEVLINK_ESWITCH_MODE_SWITCHDEV:
796                 *mlx5_mode = SRIOV_OFFLOADS;
797                 break;
798         default:
799                 return -EINVAL;
800         }
801
802         return 0;
803 }
804
805 static int esw_mode_to_devlink(u16 mlx5_mode, u16 *mode)
806 {
807         switch (mlx5_mode) {
808         case SRIOV_LEGACY:
809                 *mode = DEVLINK_ESWITCH_MODE_LEGACY;
810                 break;
811         case SRIOV_OFFLOADS:
812                 *mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
813                 break;
814         default:
815                 return -EINVAL;
816         }
817
818         return 0;
819 }
820
821 static int esw_inline_mode_from_devlink(u8 mode, u8 *mlx5_mode)
822 {
823         switch (mode) {
824         case DEVLINK_ESWITCH_INLINE_MODE_NONE:
825                 *mlx5_mode = MLX5_INLINE_MODE_NONE;
826                 break;
827         case DEVLINK_ESWITCH_INLINE_MODE_LINK:
828                 *mlx5_mode = MLX5_INLINE_MODE_L2;
829                 break;
830         case DEVLINK_ESWITCH_INLINE_MODE_NETWORK:
831                 *mlx5_mode = MLX5_INLINE_MODE_IP;
832                 break;
833         case DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT:
834                 *mlx5_mode = MLX5_INLINE_MODE_TCP_UDP;
835                 break;
836         default:
837                 return -EINVAL;
838         }
839
840         return 0;
841 }
842
843 static int esw_inline_mode_to_devlink(u8 mlx5_mode, u8 *mode)
844 {
845         switch (mlx5_mode) {
846         case MLX5_INLINE_MODE_NONE:
847                 *mode = DEVLINK_ESWITCH_INLINE_MODE_NONE;
848                 break;
849         case MLX5_INLINE_MODE_L2:
850                 *mode = DEVLINK_ESWITCH_INLINE_MODE_LINK;
851                 break;
852         case MLX5_INLINE_MODE_IP:
853                 *mode = DEVLINK_ESWITCH_INLINE_MODE_NETWORK;
854                 break;
855         case MLX5_INLINE_MODE_TCP_UDP:
856                 *mode = DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT;
857                 break;
858         default:
859                 return -EINVAL;
860         }
861
862         return 0;
863 }
864
865 int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode)
866 {
867         struct mlx5_core_dev *dev;
868         u16 cur_mlx5_mode, mlx5_mode = 0;
869
870         dev = devlink_priv(devlink);
871
872         if (!MLX5_CAP_GEN(dev, vport_group_manager))
873                 return -EOPNOTSUPP;
874
875         cur_mlx5_mode = dev->priv.eswitch->mode;
876
877         if (cur_mlx5_mode == SRIOV_NONE)
878                 return -EOPNOTSUPP;
879
880         if (esw_mode_from_devlink(mode, &mlx5_mode))
881                 return -EINVAL;
882
883         if (cur_mlx5_mode == mlx5_mode)
884                 return 0;
885
886         if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV)
887                 return esw_offloads_start(dev->priv.eswitch);
888         else if (mode == DEVLINK_ESWITCH_MODE_LEGACY)
889                 return esw_offloads_stop(dev->priv.eswitch);
890         else
891                 return -EINVAL;
892 }
893
894 int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)
895 {
896         struct mlx5_core_dev *dev;
897
898         dev = devlink_priv(devlink);
899
900         if (!MLX5_CAP_GEN(dev, vport_group_manager))
901                 return -EOPNOTSUPP;
902
903         if (dev->priv.eswitch->mode == SRIOV_NONE)
904                 return -EOPNOTSUPP;
905
906         return esw_mode_to_devlink(dev->priv.eswitch->mode, mode);
907 }
908
909 int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode)
910 {
911         struct mlx5_core_dev *dev = devlink_priv(devlink);
912         struct mlx5_eswitch *esw = dev->priv.eswitch;
913         int num_vports = esw->enabled_vports;
914         int err;
915         int vport;
916         u8 mlx5_mode;
917
918         if (!MLX5_CAP_GEN(dev, vport_group_manager))
919                 return -EOPNOTSUPP;
920
921         if (esw->mode == SRIOV_NONE)
922                 return -EOPNOTSUPP;
923
924         if (MLX5_CAP_ETH(dev, wqe_inline_mode) !=
925             MLX5_CAP_INLINE_MODE_VPORT_CONTEXT)
926                 return -EOPNOTSUPP;
927
928         if (esw->offloads.num_flows > 0) {
929                 esw_warn(dev, "Can't set inline mode when flows are configured\n");
930                 return -EOPNOTSUPP;
931         }
932
933         err = esw_inline_mode_from_devlink(mode, &mlx5_mode);
934         if (err)
935                 goto out;
936
937         for (vport = 1; vport < num_vports; vport++) {
938                 err = mlx5_modify_nic_vport_min_inline(dev, vport, mlx5_mode);
939                 if (err) {
940                         esw_warn(dev, "Failed to set min inline on vport %d\n",
941                                  vport);
942                         goto revert_inline_mode;
943                 }
944         }
945
946         esw->offloads.inline_mode = mlx5_mode;
947         return 0;
948
949 revert_inline_mode:
950         while (--vport > 0)
951                 mlx5_modify_nic_vport_min_inline(dev,
952                                                  vport,
953                                                  esw->offloads.inline_mode);
954 out:
955         return err;
956 }
957
958 int mlx5_devlink_eswitch_inline_mode_get(struct devlink *devlink, u8 *mode)
959 {
960         struct mlx5_core_dev *dev = devlink_priv(devlink);
961         struct mlx5_eswitch *esw = dev->priv.eswitch;
962
963         if (!MLX5_CAP_GEN(dev, vport_group_manager))
964                 return -EOPNOTSUPP;
965
966         if (esw->mode == SRIOV_NONE)
967                 return -EOPNOTSUPP;
968
969         if (MLX5_CAP_ETH(dev, wqe_inline_mode) !=
970             MLX5_CAP_INLINE_MODE_VPORT_CONTEXT)
971                 return -EOPNOTSUPP;
972
973         return esw_inline_mode_to_devlink(esw->offloads.inline_mode, mode);
974 }
975
976 int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, int nvfs, u8 *mode)
977 {
978         struct mlx5_core_dev *dev = esw->dev;
979         int vport;
980         u8 prev_mlx5_mode, mlx5_mode = MLX5_INLINE_MODE_L2;
981
982         if (!MLX5_CAP_GEN(dev, vport_group_manager))
983                 return -EOPNOTSUPP;
984
985         if (esw->mode == SRIOV_NONE)
986                 return -EOPNOTSUPP;
987
988         if (MLX5_CAP_ETH(dev, wqe_inline_mode) !=
989             MLX5_CAP_INLINE_MODE_VPORT_CONTEXT)
990                 return -EOPNOTSUPP;
991
992         for (vport = 1; vport <= nvfs; vport++) {
993                 mlx5_query_nic_vport_min_inline(dev, vport, &mlx5_mode);
994                 if (vport > 1 && prev_mlx5_mode != mlx5_mode)
995                         return -EINVAL;
996                 prev_mlx5_mode = mlx5_mode;
997         }
998
999         *mode = mlx5_mode;
1000         return 0;
1001 }
1002
1003 void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw,
1004                                      int vport_index,
1005                                      struct mlx5_eswitch_rep *__rep)
1006 {
1007         struct mlx5_esw_offload *offloads = &esw->offloads;
1008         struct mlx5_eswitch_rep *rep;
1009
1010         rep = &offloads->vport_reps[vport_index];
1011
1012         memset(rep, 0, sizeof(*rep));
1013
1014         rep->load   = __rep->load;
1015         rep->unload = __rep->unload;
1016         rep->vport  = __rep->vport;
1017         rep->netdev = __rep->netdev;
1018         ether_addr_copy(rep->hw_id, __rep->hw_id);
1019
1020         INIT_LIST_HEAD(&rep->vport_sqs_list);
1021         rep->valid = true;
1022 }
1023
1024 void mlx5_eswitch_unregister_vport_rep(struct mlx5_eswitch *esw,
1025                                        int vport_index)
1026 {
1027         struct mlx5_esw_offload *offloads = &esw->offloads;
1028         struct mlx5_eswitch_rep *rep;
1029
1030         rep = &offloads->vport_reps[vport_index];
1031
1032         if (esw->mode == SRIOV_OFFLOADS && esw->vports[vport_index].enabled)
1033                 rep->unload(esw, rep);
1034
1035         rep->valid = false;
1036 }
1037
1038 struct net_device *mlx5_eswitch_get_uplink_netdev(struct mlx5_eswitch *esw)
1039 {
1040 #define UPLINK_REP_INDEX 0
1041         struct mlx5_esw_offload *offloads = &esw->offloads;
1042         struct mlx5_eswitch_rep *rep;
1043
1044         rep = &offloads->vport_reps[UPLINK_REP_INDEX];
1045         return rep->netdev;
1046 }