// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB /* Copyright (c) 2020 Mellanox Technologies Inc. All rights reserved. */ #include "mlx5_core.h" #include "eswitch.h" #include "helper.h" #include "ofld.h" static void esw_acl_egress_ofld_fwd2vport_destroy(struct mlx5_vport *vport) { if (!vport->egress.offloads.fwd_rule) return; mlx5_del_flow_rules(vport->egress.offloads.fwd_rule); vport->egress.offloads.fwd_rule = NULL; } static int esw_acl_egress_ofld_fwd2vport_create(struct mlx5_eswitch *esw, struct mlx5_vport *vport, struct mlx5_flow_destination *fwd_dest) { struct mlx5_flow_act flow_act = {}; int err = 0; esw_debug(esw->dev, "vport(%d) configure egress acl rule fwd2vport(%d)\n", vport->vport, fwd_dest->vport.num); /* Delete the old egress forward-to-vport rule if any */ esw_acl_egress_ofld_fwd2vport_destroy(vport); flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; vport->egress.offloads.fwd_rule = mlx5_add_flow_rules(vport->egress.acl, NULL, &flow_act, fwd_dest, 1); if (IS_ERR(vport->egress.offloads.fwd_rule)) { err = PTR_ERR(vport->egress.offloads.fwd_rule); esw_warn(esw->dev, "vport(%d) failed to add fwd2vport acl rule err(%d)\n", vport->vport, err); vport->egress.offloads.fwd_rule = NULL; } return err; } static int esw_acl_egress_ofld_rules_create(struct mlx5_eswitch *esw, struct mlx5_vport *vport, struct mlx5_flow_destination *fwd_dest) { int err = 0; int action; if (MLX5_CAP_GEN(esw->dev, prio_tag_required)) { /* For prio tag mode, there is only 1 FTEs: * 1) prio tag packets - pop the prio tag VLAN, allow * Unmatched traffic is allowed by default */ esw_debug(esw->dev, "vport[%d] configure prio tag egress rules\n", vport->vport); action = MLX5_FLOW_CONTEXT_ACTION_VLAN_POP; action |= fwd_dest ? MLX5_FLOW_CONTEXT_ACTION_FWD_DEST : MLX5_FLOW_CONTEXT_ACTION_ALLOW; /* prio tag vlan rule - pop it so vport receives untagged packets */ err = esw_egress_acl_vlan_create(esw, vport, fwd_dest, 0, action); if (err) goto prio_err; } if (fwd_dest) { err = esw_acl_egress_ofld_fwd2vport_create(esw, vport, fwd_dest); if (err) goto fwd_err; } return 0; fwd_err: esw_acl_egress_vlan_destroy(vport); prio_err: return err; } static void esw_acl_egress_ofld_rules_destroy(struct mlx5_vport *vport) { esw_acl_egress_vlan_destroy(vport); esw_acl_egress_ofld_fwd2vport_destroy(vport); } static int esw_acl_egress_ofld_groups_create(struct mlx5_eswitch *esw, struct mlx5_vport *vport) { int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); struct mlx5_flow_group *fwd_grp; u32 *flow_group_in; u32 flow_index = 0; int ret = 0; if (MLX5_CAP_GEN(esw->dev, prio_tag_required)) { ret = esw_acl_egress_vlan_grp_create(esw, vport); if (ret) return ret; flow_index++; } if (!mlx5_esw_acl_egress_fwd2vport_supported(esw)) goto out; flow_group_in = kvzalloc(inlen, GFP_KERNEL); if (!flow_group_in) { ret = -ENOMEM; goto fwd_grp_err; } /* This group holds 1 FTE to forward all packets to other vport * when bond vports is supported. */ MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, flow_index); MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, flow_index); fwd_grp = mlx5_create_flow_group(vport->egress.acl, flow_group_in); if (IS_ERR(fwd_grp)) { ret = PTR_ERR(fwd_grp); esw_warn(esw->dev, "Failed to create vport[%d] egress fwd2vport flow group, err(%d)\n", vport->vport, ret); kvfree(flow_group_in); goto fwd_grp_err; } vport->egress.offloads.fwd_grp = fwd_grp; kvfree(flow_group_in); return 0; fwd_grp_err: esw_acl_egress_vlan_grp_destroy(vport); out: return ret; } static void esw_acl_egress_ofld_groups_destroy(struct mlx5_vport *vport) { if (!IS_ERR_OR_NULL(vport->egress.offloads.fwd_grp)) { mlx5_destroy_flow_group(vport->egress.offloads.fwd_grp); vport->egress.offloads.fwd_grp = NULL; } esw_acl_egress_vlan_grp_destroy(vport); } int esw_acl_egress_ofld_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport) { int table_size = 0; int err; if (!mlx5_esw_acl_egress_fwd2vport_supported(esw) && !MLX5_CAP_GEN(esw->dev, prio_tag_required)) return 0; esw_acl_egress_ofld_rules_destroy(vport); if (mlx5_esw_acl_egress_fwd2vport_supported(esw)) table_size++; if (MLX5_CAP_GEN(esw->dev, prio_tag_required)) table_size++; vport->egress.acl = esw_acl_table_create(esw, vport->vport, MLX5_FLOW_NAMESPACE_ESW_EGRESS, table_size); if (IS_ERR_OR_NULL(vport->egress.acl)) { err = PTR_ERR(vport->egress.acl); vport->egress.acl = NULL; return err; } err = esw_acl_egress_ofld_groups_create(esw, vport); if (err) goto group_err; esw_debug(esw->dev, "vport[%d] configure egress rules\n", vport->vport); err = esw_acl_egress_ofld_rules_create(esw, vport, NULL); if (err) goto rules_err; return 0; rules_err: esw_acl_egress_ofld_groups_destroy(vport); group_err: esw_acl_egress_table_destroy(vport); return err; } void esw_acl_egress_ofld_cleanup(struct mlx5_vport *vport) { esw_acl_egress_ofld_rules_destroy(vport); esw_acl_egress_ofld_groups_destroy(vport); esw_acl_egress_table_destroy(vport); } int mlx5_esw_acl_egress_vport_bond(struct mlx5_eswitch *esw, u16 active_vport_num, u16 passive_vport_num) { struct mlx5_vport *passive_vport = mlx5_eswitch_get_vport(esw, passive_vport_num); struct mlx5_vport *active_vport = mlx5_eswitch_get_vport(esw, active_vport_num); struct mlx5_flow_destination fwd_dest = {}; if (IS_ERR(active_vport)) return PTR_ERR(active_vport); if (IS_ERR(passive_vport)) return PTR_ERR(passive_vport); /* Cleanup and recreate rules WITHOUT fwd2vport of active vport */ esw_acl_egress_ofld_rules_destroy(active_vport); esw_acl_egress_ofld_rules_create(esw, active_vport, NULL); /* Cleanup and recreate all rules + fwd2vport rule of passive vport to forward */ esw_acl_egress_ofld_rules_destroy(passive_vport); fwd_dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT; fwd_dest.vport.num = active_vport_num; fwd_dest.vport.vhca_id = MLX5_CAP_GEN(esw->dev, vhca_id); fwd_dest.vport.flags = MLX5_FLOW_DEST_VPORT_VHCA_ID; return esw_acl_egress_ofld_rules_create(esw, passive_vport, &fwd_dest); } int mlx5_esw_acl_egress_vport_unbond(struct mlx5_eswitch *esw, u16 vport_num) { struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num); if (IS_ERR(vport)) return PTR_ERR(vport); esw_acl_egress_ofld_rules_destroy(vport); return esw_acl_egress_ofld_rules_create(esw, vport, NULL); }