net/mlx5: E-Switch, properly handle ingress tagged packets on VST
[platform/kernel/linux-starfive.git] / drivers / net / ethernet / mellanox / mlx5 / core / esw / acl / egress_lgcy.c
1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2020 Mellanox Technologies Inc. All rights reserved. */
3
4 #include "mlx5_core.h"
5 #include "eswitch.h"
6 #include "helper.h"
7 #include "lgcy.h"
8
9 static void esw_acl_egress_lgcy_rules_destroy(struct mlx5_vport *vport)
10 {
11         esw_acl_egress_vlan_destroy(vport);
12         if (!IS_ERR_OR_NULL(vport->egress.legacy.drop_rule)) {
13                 mlx5_del_flow_rules(vport->egress.legacy.drop_rule);
14                 vport->egress.legacy.drop_rule = NULL;
15         }
16 }
17
18 static int esw_acl_egress_lgcy_groups_create(struct mlx5_eswitch *esw,
19                                              struct mlx5_vport *vport)
20 {
21         int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
22         struct mlx5_core_dev *dev = esw->dev;
23         struct mlx5_flow_group *drop_grp;
24         u32 *flow_group_in;
25         int err = 0;
26
27         err = esw_acl_egress_vlan_grp_create(esw, vport);
28         if (err)
29                 return err;
30
31         flow_group_in = kvzalloc(inlen, GFP_KERNEL);
32         if (!flow_group_in) {
33                 err = -ENOMEM;
34                 goto alloc_err;
35         }
36
37         MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 1);
38         MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1);
39         drop_grp = mlx5_create_flow_group(vport->egress.acl, flow_group_in);
40         if (IS_ERR(drop_grp)) {
41                 err = PTR_ERR(drop_grp);
42                 esw_warn(dev, "Failed to create E-Switch vport[%d] egress drop flow group, err(%d)\n",
43                          vport->vport, err);
44                 goto drop_grp_err;
45         }
46
47         vport->egress.legacy.drop_grp = drop_grp;
48         kvfree(flow_group_in);
49         return 0;
50
51 drop_grp_err:
52         kvfree(flow_group_in);
53 alloc_err:
54         esw_acl_egress_vlan_grp_destroy(vport);
55         return err;
56 }
57
58 static void esw_acl_egress_lgcy_groups_destroy(struct mlx5_vport *vport)
59 {
60         if (!IS_ERR_OR_NULL(vport->egress.legacy.drop_grp)) {
61                 mlx5_destroy_flow_group(vport->egress.legacy.drop_grp);
62                 vport->egress.legacy.drop_grp = NULL;
63         }
64         esw_acl_egress_vlan_grp_destroy(vport);
65 }
66
67 int esw_acl_egress_lgcy_setup(struct mlx5_eswitch *esw,
68                               struct mlx5_vport *vport)
69 {
70         bool vst_mode_steering = esw_vst_mode_is_steering(esw);
71         struct mlx5_flow_destination drop_ctr_dst = {};
72         struct mlx5_flow_destination *dst = NULL;
73         struct mlx5_fc *drop_counter = NULL;
74         struct mlx5_flow_act flow_act = {};
75         /* The egress acl table contains 2 rules:
76          * 1)Allow traffic with vlan_tag=vst_vlan_id
77          * 2)Drop all other traffic.
78          */
79         int table_size = 2;
80         int dest_num = 0;
81         int actions_flag;
82         int err = 0;
83
84         if (vport->egress.legacy.drop_counter) {
85                 drop_counter = vport->egress.legacy.drop_counter;
86         } else if (MLX5_CAP_ESW_EGRESS_ACL(esw->dev, flow_counter)) {
87                 drop_counter = mlx5_fc_create(esw->dev, false);
88                 if (IS_ERR(drop_counter)) {
89                         esw_warn(esw->dev,
90                                  "vport[%d] configure egress drop rule counter err(%ld)\n",
91                                  vport->vport, PTR_ERR(drop_counter));
92                         drop_counter = NULL;
93                 }
94                 vport->egress.legacy.drop_counter = drop_counter;
95         }
96
97         esw_acl_egress_lgcy_rules_destroy(vport);
98
99         if (!vport->info.vlan && !vport->info.qos) {
100                 esw_acl_egress_lgcy_cleanup(esw, vport);
101                 return 0;
102         }
103
104         if (!vport->egress.acl) {
105                 vport->egress.acl = esw_acl_table_create(esw, vport,
106                                                          MLX5_FLOW_NAMESPACE_ESW_EGRESS,
107                                                          table_size);
108                 if (IS_ERR(vport->egress.acl)) {
109                         err = PTR_ERR(vport->egress.acl);
110                         vport->egress.acl = NULL;
111                         goto out;
112                 }
113
114                 err = esw_acl_egress_lgcy_groups_create(esw, vport);
115                 if (err)
116                         goto out;
117         }
118
119         esw_debug(esw->dev,
120                   "vport[%d] configure egress rules, vlan(%d) qos(%d)\n",
121                   vport->vport, vport->info.vlan, vport->info.qos);
122
123         /* Allowed vlan rule */
124         actions_flag = MLX5_FLOW_CONTEXT_ACTION_ALLOW;
125         if (vst_mode_steering)
126                 actions_flag |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
127         err = esw_egress_acl_vlan_create(esw, vport, NULL, vport->info.vlan,
128                                          actions_flag);
129         if (err)
130                 goto out;
131
132         flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
133
134         /* Attach egress drop flow counter */
135         if (drop_counter) {
136                 flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
137                 drop_ctr_dst.type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
138                 drop_ctr_dst.counter_id = mlx5_fc_id(drop_counter);
139                 dst = &drop_ctr_dst;
140                 dest_num++;
141         }
142         vport->egress.legacy.drop_rule =
143                 mlx5_add_flow_rules(vport->egress.acl, NULL,
144                                     &flow_act, dst, dest_num);
145         if (IS_ERR(vport->egress.legacy.drop_rule)) {
146                 err = PTR_ERR(vport->egress.legacy.drop_rule);
147                 esw_warn(esw->dev,
148                          "vport[%d] configure egress drop rule failed, err(%d)\n",
149                          vport->vport, err);
150                 vport->egress.legacy.drop_rule = NULL;
151                 goto out;
152         }
153
154         return err;
155
156 out:
157         esw_acl_egress_lgcy_cleanup(esw, vport);
158         return err;
159 }
160
161 void esw_acl_egress_lgcy_cleanup(struct mlx5_eswitch *esw,
162                                  struct mlx5_vport *vport)
163 {
164         if (IS_ERR_OR_NULL(vport->egress.acl))
165                 goto clean_drop_counter;
166
167         esw_debug(esw->dev, "Destroy vport[%d] E-Switch egress ACL\n", vport->vport);
168
169         esw_acl_egress_lgcy_rules_destroy(vport);
170         esw_acl_egress_lgcy_groups_destroy(vport);
171         esw_acl_egress_table_destroy(vport);
172
173 clean_drop_counter:
174         if (vport->egress.legacy.drop_counter) {
175                 mlx5_fc_destroy(esw->dev, vport->egress.legacy.drop_counter);
176                 vport->egress.legacy.drop_counter = NULL;
177         }
178 }