| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| /* Copyright (c) 2019 Mellanox Technologies */ |
| |
| #include <linux/mlx5/vport.h> |
| #include <rdma/ib_verbs.h> |
| #include <net/addrconf.h> |
| |
| #include "lib/mlx5.h" |
| #include "eswitch.h" |
| #include "fs_core.h" |
| #include "rdma.h" |
| |
| static void mlx5_rdma_disable_roce_steering(struct mlx5_core_dev *dev) |
| { |
| struct mlx5_core_roce *roce = &dev->priv.roce; |
| |
| mlx5_del_flow_rules(roce->allow_rule); |
| mlx5_destroy_flow_group(roce->fg); |
| mlx5_destroy_flow_table(roce->ft); |
| } |
| |
| static int mlx5_rdma_enable_roce_steering(struct mlx5_core_dev *dev) |
| { |
| int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); |
| struct mlx5_core_roce *roce = &dev->priv.roce; |
| struct mlx5_flow_handle *flow_rule = NULL; |
| struct mlx5_flow_table_attr ft_attr = {}; |
| struct mlx5_flow_namespace *ns = NULL; |
| struct mlx5_flow_act flow_act = {}; |
| struct mlx5_flow_spec *spec; |
| struct mlx5_flow_table *ft; |
| struct mlx5_flow_group *fg; |
| void *match_criteria; |
| u32 *flow_group_in; |
| void *misc; |
| int err; |
| |
| if (!(MLX5_CAP_FLOWTABLE_RDMA_RX(dev, ft_support) && |
| MLX5_CAP_FLOWTABLE_RDMA_RX(dev, table_miss_action_domain))) |
| return -EOPNOTSUPP; |
| |
| flow_group_in = kvzalloc(inlen, GFP_KERNEL); |
| if (!flow_group_in) |
| return -ENOMEM; |
| spec = kvzalloc(sizeof(*spec), GFP_KERNEL); |
| if (!spec) { |
| kvfree(flow_group_in); |
| return -ENOMEM; |
| } |
| |
| ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_RDMA_RX_KERNEL); |
| if (!ns) { |
| mlx5_core_err(dev, "Failed to get RDMA RX namespace"); |
| err = -EOPNOTSUPP; |
| goto free; |
| } |
| |
| ft_attr.max_fte = 1; |
| ft = mlx5_create_flow_table(ns, &ft_attr); |
| if (IS_ERR(ft)) { |
| mlx5_core_err(dev, "Failed to create RDMA RX flow table"); |
| err = PTR_ERR(ft); |
| goto free; |
| } |
| |
| MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, |
| MLX5_MATCH_MISC_PARAMETERS); |
| match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, |
| match_criteria); |
| MLX5_SET_TO_ONES(fte_match_param, match_criteria, |
| misc_parameters.source_port); |
| |
| fg = mlx5_create_flow_group(ft, flow_group_in); |
| if (IS_ERR(fg)) { |
| err = PTR_ERR(fg); |
| mlx5_core_err(dev, "Failed to create RDMA RX flow group err(%d)\n", err); |
| goto destroy_flow_table; |
| } |
| |
| spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS; |
| misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, |
| misc_parameters); |
| MLX5_SET(fte_match_set_misc, misc, source_port, |
| dev->priv.eswitch->manager_vport); |
| misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, |
| misc_parameters); |
| MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port); |
| |
| flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW; |
| flow_rule = mlx5_add_flow_rules(ft, spec, &flow_act, NULL, 0); |
| if (IS_ERR(flow_rule)) { |
| err = PTR_ERR(flow_rule); |
| mlx5_core_err(dev, "Failed to add RoCE allow rule, err=%d\n", |
| err); |
| goto destroy_flow_group; |
| } |
| |
| kvfree(spec); |
| kvfree(flow_group_in); |
| roce->ft = ft; |
| roce->fg = fg; |
| roce->allow_rule = flow_rule; |
| |
| return 0; |
| |
| destroy_flow_group: |
| mlx5_destroy_flow_group(fg); |
| destroy_flow_table: |
| mlx5_destroy_flow_table(ft); |
| free: |
| kvfree(spec); |
| kvfree(flow_group_in); |
| return err; |
| } |
| |
| static void mlx5_rdma_del_roce_addr(struct mlx5_core_dev *dev) |
| { |
| mlx5_core_roce_gid_set(dev, 0, 0, 0, |
| NULL, NULL, false, 0, 1); |
| } |
| |
| static void mlx5_rdma_make_default_gid(struct mlx5_core_dev *dev, union ib_gid *gid) |
| { |
| u8 hw_id[ETH_ALEN]; |
| |
| mlx5_query_mac_address(dev, hw_id); |
| gid->global.subnet_prefix = cpu_to_be64(0xfe80000000000000LL); |
| addrconf_addr_eui48(&gid->raw[8], hw_id); |
| } |
| |
| static int mlx5_rdma_add_roce_addr(struct mlx5_core_dev *dev) |
| { |
| union ib_gid gid; |
| u8 mac[ETH_ALEN]; |
| |
| mlx5_rdma_make_default_gid(dev, &gid); |
| return mlx5_core_roce_gid_set(dev, 0, |
| MLX5_ROCE_VERSION_1, |
| 0, gid.raw, mac, |
| false, 0, 1); |
| } |
| |
| void mlx5_rdma_disable_roce(struct mlx5_core_dev *dev) |
| { |
| struct mlx5_core_roce *roce = &dev->priv.roce; |
| |
| if (!roce->ft) |
| return; |
| |
| mlx5_rdma_disable_roce_steering(dev); |
| mlx5_rdma_del_roce_addr(dev); |
| mlx5_nic_vport_disable_roce(dev); |
| } |
| |
| void mlx5_rdma_enable_roce(struct mlx5_core_dev *dev) |
| { |
| int err; |
| |
| if (!MLX5_CAP_GEN(dev, roce)) |
| return; |
| |
| err = mlx5_nic_vport_enable_roce(dev); |
| if (err) { |
| mlx5_core_err(dev, "Failed to enable RoCE: %d\n", err); |
| return; |
| } |
| |
| err = mlx5_rdma_add_roce_addr(dev); |
| if (err) { |
| mlx5_core_err(dev, "Failed to add RoCE address: %d\n", err); |
| goto disable_roce; |
| } |
| |
| err = mlx5_rdma_enable_roce_steering(dev); |
| if (err) { |
| mlx5_core_err(dev, "Failed to enable RoCE steering: %d\n", err); |
| goto del_roce_addr; |
| } |
| |
| return; |
| |
| del_roce_addr: |
| mlx5_rdma_del_roce_addr(dev); |
| disable_roce: |
| mlx5_nic_vport_disable_roce(dev); |
| } |