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 "ofld.h" 8 9 static void esw_acl_egress_ofld_fwd2vport_destroy(struct mlx5_vport *vport) 10 { 11 if (!vport->egress.offloads.fwd_rule) 12 return; 13 14 mlx5_del_flow_rules(vport->egress.offloads.fwd_rule); 15 vport->egress.offloads.fwd_rule = NULL; 16 } 17 18 static void esw_acl_egress_ofld_bounce_rule_destroy(struct mlx5_vport *vport) 19 { 20 if (!vport->egress.offloads.bounce_rule) 21 return; 22 23 mlx5_del_flow_rules(vport->egress.offloads.bounce_rule); 24 vport->egress.offloads.bounce_rule = NULL; 25 } 26 27 static int esw_acl_egress_ofld_fwd2vport_create(struct mlx5_eswitch *esw, 28 struct mlx5_vport *vport, 29 struct mlx5_flow_destination *fwd_dest) 30 { 31 struct mlx5_flow_act flow_act = {}; 32 int err = 0; 33 34 esw_debug(esw->dev, "vport(%d) configure egress acl rule fwd2vport(%d)\n", 35 vport->vport, fwd_dest->vport.num); 36 37 /* Delete the old egress forward-to-vport rule if any */ 38 esw_acl_egress_ofld_fwd2vport_destroy(vport); 39 40 flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; 41 42 vport->egress.offloads.fwd_rule = 43 mlx5_add_flow_rules(vport->egress.acl, NULL, 44 &flow_act, fwd_dest, 1); 45 if (IS_ERR(vport->egress.offloads.fwd_rule)) { 46 err = PTR_ERR(vport->egress.offloads.fwd_rule); 47 esw_warn(esw->dev, 48 "vport(%d) failed to add fwd2vport acl rule err(%d)\n", 49 vport->vport, err); 50 vport->egress.offloads.fwd_rule = NULL; 51 } 52 53 return err; 54 } 55 56 static int esw_acl_egress_ofld_rules_create(struct mlx5_eswitch *esw, 57 struct mlx5_vport *vport, 58 struct mlx5_flow_destination *fwd_dest) 59 { 60 int err = 0; 61 int action; 62 63 if (MLX5_CAP_GEN(esw->dev, prio_tag_required)) { 64 /* For prio tag mode, there is only 1 FTEs: 65 * 1) prio tag packets - pop the prio tag VLAN, allow 66 * Unmatched traffic is allowed by default 67 */ 68 esw_debug(esw->dev, 69 "vport[%d] configure prio tag egress rules\n", vport->vport); 70 71 action = MLX5_FLOW_CONTEXT_ACTION_VLAN_POP; 72 action |= fwd_dest ? MLX5_FLOW_CONTEXT_ACTION_FWD_DEST : 73 MLX5_FLOW_CONTEXT_ACTION_ALLOW; 74 75 /* prio tag vlan rule - pop it so vport receives untagged packets */ 76 err = esw_egress_acl_vlan_create(esw, vport, fwd_dest, 0, action); 77 if (err) 78 goto prio_err; 79 } 80 81 if (fwd_dest) { 82 err = esw_acl_egress_ofld_fwd2vport_create(esw, vport, fwd_dest); 83 if (err) 84 goto fwd_err; 85 } 86 87 return 0; 88 89 fwd_err: 90 esw_acl_egress_vlan_destroy(vport); 91 prio_err: 92 return err; 93 } 94 95 static void esw_acl_egress_ofld_rules_destroy(struct mlx5_vport *vport) 96 { 97 esw_acl_egress_vlan_destroy(vport); 98 esw_acl_egress_ofld_fwd2vport_destroy(vport); 99 esw_acl_egress_ofld_bounce_rule_destroy(vport); 100 } 101 102 static int esw_acl_egress_ofld_groups_create(struct mlx5_eswitch *esw, 103 struct mlx5_vport *vport) 104 { 105 int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); 106 struct mlx5_flow_group *fwd_grp; 107 u32 *flow_group_in; 108 u32 flow_index = 0; 109 int ret = 0; 110 111 if (MLX5_CAP_GEN(esw->dev, prio_tag_required)) { 112 ret = esw_acl_egress_vlan_grp_create(esw, vport); 113 if (ret) 114 return ret; 115 116 flow_index++; 117 } 118 119 if (!mlx5_esw_acl_egress_fwd2vport_supported(esw)) 120 goto out; 121 122 flow_group_in = kvzalloc(inlen, GFP_KERNEL); 123 if (!flow_group_in) { 124 ret = -ENOMEM; 125 goto fwd_grp_err; 126 } 127 128 /* This group holds 1 FTE to forward all packets to other vport 129 * when bond vports is supported. 130 */ 131 MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, flow_index); 132 MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, flow_index); 133 fwd_grp = mlx5_create_flow_group(vport->egress.acl, flow_group_in); 134 if (IS_ERR(fwd_grp)) { 135 ret = PTR_ERR(fwd_grp); 136 esw_warn(esw->dev, 137 "Failed to create vport[%d] egress fwd2vport flow group, err(%d)\n", 138 vport->vport, ret); 139 kvfree(flow_group_in); 140 goto fwd_grp_err; 141 } 142 vport->egress.offloads.fwd_grp = fwd_grp; 143 kvfree(flow_group_in); 144 return 0; 145 146 fwd_grp_err: 147 esw_acl_egress_vlan_grp_destroy(vport); 148 out: 149 return ret; 150 } 151 152 static void esw_acl_egress_ofld_groups_destroy(struct mlx5_vport *vport) 153 { 154 if (!IS_ERR_OR_NULL(vport->egress.offloads.fwd_grp)) { 155 mlx5_destroy_flow_group(vport->egress.offloads.fwd_grp); 156 vport->egress.offloads.fwd_grp = NULL; 157 } 158 159 if (!IS_ERR_OR_NULL(vport->egress.offloads.bounce_grp)) { 160 mlx5_destroy_flow_group(vport->egress.offloads.bounce_grp); 161 vport->egress.offloads.bounce_grp = NULL; 162 } 163 164 esw_acl_egress_vlan_grp_destroy(vport); 165 } 166 167 static bool esw_acl_egress_needed(struct mlx5_eswitch *esw, u16 vport_num) 168 { 169 return mlx5_eswitch_is_vf_vport(esw, vport_num) || mlx5_esw_is_sf_vport(esw, vport_num); 170 } 171 172 int esw_acl_egress_ofld_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport) 173 { 174 int table_size = 0; 175 int err; 176 177 if (!mlx5_esw_acl_egress_fwd2vport_supported(esw) && 178 !MLX5_CAP_GEN(esw->dev, prio_tag_required)) 179 return 0; 180 181 if (!esw_acl_egress_needed(esw, vport->vport)) 182 return 0; 183 184 esw_acl_egress_ofld_rules_destroy(vport); 185 186 if (mlx5_esw_acl_egress_fwd2vport_supported(esw)) 187 table_size++; 188 if (MLX5_CAP_GEN(esw->dev, prio_tag_required)) 189 table_size++; 190 vport->egress.acl = esw_acl_table_create(esw, vport, 191 MLX5_FLOW_NAMESPACE_ESW_EGRESS, table_size); 192 if (IS_ERR(vport->egress.acl)) { 193 err = PTR_ERR(vport->egress.acl); 194 vport->egress.acl = NULL; 195 return err; 196 } 197 198 err = esw_acl_egress_ofld_groups_create(esw, vport); 199 if (err) 200 goto group_err; 201 202 esw_debug(esw->dev, "vport[%d] configure egress rules\n", vport->vport); 203 204 err = esw_acl_egress_ofld_rules_create(esw, vport, NULL); 205 if (err) 206 goto rules_err; 207 208 return 0; 209 210 rules_err: 211 esw_acl_egress_ofld_groups_destroy(vport); 212 group_err: 213 esw_acl_egress_table_destroy(vport); 214 return err; 215 } 216 217 void esw_acl_egress_ofld_cleanup(struct mlx5_vport *vport) 218 { 219 esw_acl_egress_ofld_rules_destroy(vport); 220 esw_acl_egress_ofld_groups_destroy(vport); 221 esw_acl_egress_table_destroy(vport); 222 } 223 224 int mlx5_esw_acl_egress_vport_bond(struct mlx5_eswitch *esw, u16 active_vport_num, 225 u16 passive_vport_num) 226 { 227 struct mlx5_vport *passive_vport = mlx5_eswitch_get_vport(esw, passive_vport_num); 228 struct mlx5_vport *active_vport = mlx5_eswitch_get_vport(esw, active_vport_num); 229 struct mlx5_flow_destination fwd_dest = {}; 230 231 if (IS_ERR(active_vport)) 232 return PTR_ERR(active_vport); 233 if (IS_ERR(passive_vport)) 234 return PTR_ERR(passive_vport); 235 236 /* Cleanup and recreate rules WITHOUT fwd2vport of active vport */ 237 esw_acl_egress_ofld_rules_destroy(active_vport); 238 esw_acl_egress_ofld_rules_create(esw, active_vport, NULL); 239 240 /* Cleanup and recreate all rules + fwd2vport rule of passive vport to forward */ 241 esw_acl_egress_ofld_rules_destroy(passive_vport); 242 fwd_dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT; 243 fwd_dest.vport.num = active_vport_num; 244 fwd_dest.vport.vhca_id = MLX5_CAP_GEN(esw->dev, vhca_id); 245 fwd_dest.vport.flags = MLX5_FLOW_DEST_VPORT_VHCA_ID; 246 247 return esw_acl_egress_ofld_rules_create(esw, passive_vport, &fwd_dest); 248 } 249 250 int mlx5_esw_acl_egress_vport_unbond(struct mlx5_eswitch *esw, u16 vport_num) 251 { 252 struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num); 253 254 if (IS_ERR(vport)) 255 return PTR_ERR(vport); 256 257 esw_acl_egress_ofld_rules_destroy(vport); 258 return esw_acl_egress_ofld_rules_create(esw, vport, NULL); 259 } 260