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