| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| /* Copyright (c) 2019 Mellanox Technologies. */ |
| |
| #include "dr_types.h" |
| |
| int mlx5dr_cmd_query_esw_vport_context(struct mlx5_core_dev *mdev, |
| bool other_vport, |
| u16 vport_number, |
| u64 *icm_address_rx, |
| u64 *icm_address_tx) |
| { |
| u32 out[MLX5_ST_SZ_DW(query_esw_vport_context_out)] = {}; |
| u32 in[MLX5_ST_SZ_DW(query_esw_vport_context_in)] = {}; |
| int err; |
| |
| MLX5_SET(query_esw_vport_context_in, in, opcode, |
| MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT); |
| MLX5_SET(query_esw_vport_context_in, in, other_vport, other_vport); |
| MLX5_SET(query_esw_vport_context_in, in, vport_number, vport_number); |
| |
| err = mlx5_cmd_exec_inout(mdev, query_esw_vport_context, in, out); |
| if (err) |
| return err; |
| |
| *icm_address_rx = |
| MLX5_GET64(query_esw_vport_context_out, out, |
| esw_vport_context.sw_steering_vport_icm_address_rx); |
| *icm_address_tx = |
| MLX5_GET64(query_esw_vport_context_out, out, |
| esw_vport_context.sw_steering_vport_icm_address_tx); |
| return 0; |
| } |
| |
| int mlx5dr_cmd_query_gvmi(struct mlx5_core_dev *mdev, bool other_vport, |
| u16 vport_number, u16 *gvmi) |
| { |
| u32 in[MLX5_ST_SZ_DW(query_hca_cap_in)] = {}; |
| int out_size; |
| void *out; |
| int err; |
| |
| out_size = MLX5_ST_SZ_BYTES(query_hca_cap_out); |
| out = kzalloc(out_size, GFP_KERNEL); |
| if (!out) |
| return -ENOMEM; |
| |
| MLX5_SET(query_hca_cap_in, in, opcode, MLX5_CMD_OP_QUERY_HCA_CAP); |
| MLX5_SET(query_hca_cap_in, in, other_function, other_vport); |
| MLX5_SET(query_hca_cap_in, in, function_id, vport_number); |
| MLX5_SET(query_hca_cap_in, in, op_mod, |
| MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE << 1 | |
| HCA_CAP_OPMOD_GET_CUR); |
| |
| err = mlx5_cmd_exec_inout(mdev, query_hca_cap, in, out); |
| if (err) { |
| kfree(out); |
| return err; |
| } |
| |
| *gvmi = MLX5_GET(query_hca_cap_out, out, capability.cmd_hca_cap.vhca_id); |
| |
| kfree(out); |
| return 0; |
| } |
| |
| int mlx5dr_cmd_query_esw_caps(struct mlx5_core_dev *mdev, |
| struct mlx5dr_esw_caps *caps) |
| { |
| caps->drop_icm_address_rx = |
| MLX5_CAP64_ESW_FLOWTABLE(mdev, |
| sw_steering_fdb_action_drop_icm_address_rx); |
| caps->drop_icm_address_tx = |
| MLX5_CAP64_ESW_FLOWTABLE(mdev, |
| sw_steering_fdb_action_drop_icm_address_tx); |
| caps->uplink_icm_address_rx = |
| MLX5_CAP64_ESW_FLOWTABLE(mdev, |
| sw_steering_uplink_icm_address_rx); |
| caps->uplink_icm_address_tx = |
| MLX5_CAP64_ESW_FLOWTABLE(mdev, |
| sw_steering_uplink_icm_address_tx); |
| caps->sw_owner_v2 = MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, sw_owner_v2); |
| if (!caps->sw_owner_v2) |
| caps->sw_owner = MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, sw_owner); |
| |
| return 0; |
| } |
| |
| static int dr_cmd_query_nic_vport_roce_en(struct mlx5_core_dev *mdev, |
| u16 vport, bool *roce_en) |
| { |
| u32 out[MLX5_ST_SZ_DW(query_nic_vport_context_out)] = {}; |
| u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)] = {}; |
| int err; |
| |
| MLX5_SET(query_nic_vport_context_in, in, opcode, |
| MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT); |
| MLX5_SET(query_nic_vport_context_in, in, vport_number, vport); |
| MLX5_SET(query_nic_vport_context_in, in, other_vport, !!vport); |
| |
| err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); |
| if (err) |
| return err; |
| |
| *roce_en = MLX5_GET(query_nic_vport_context_out, out, |
| nic_vport_context.roce_en); |
| return 0; |
| } |
| |
| int mlx5dr_cmd_query_device(struct mlx5_core_dev *mdev, |
| struct mlx5dr_cmd_caps *caps) |
| { |
| bool roce_en; |
| int err; |
| |
| caps->prio_tag_required = MLX5_CAP_GEN(mdev, prio_tag_required); |
| caps->eswitch_manager = MLX5_CAP_GEN(mdev, eswitch_manager); |
| caps->gvmi = MLX5_CAP_GEN(mdev, vhca_id); |
| caps->flex_protocols = MLX5_CAP_GEN(mdev, flex_parser_protocols); |
| caps->sw_format_ver = MLX5_CAP_GEN(mdev, steering_format_version); |
| |
| if (MLX5_CAP_GEN(mdev, roce)) { |
| err = dr_cmd_query_nic_vport_roce_en(mdev, 0, &roce_en); |
| if (err) |
| return err; |
| |
| caps->roce_caps.roce_en = roce_en; |
| caps->roce_caps.fl_rc_qp_when_roce_disabled = |
| MLX5_CAP_ROCE(mdev, fl_rc_qp_when_roce_disabled); |
| caps->roce_caps.fl_rc_qp_when_roce_enabled = |
| MLX5_CAP_ROCE(mdev, fl_rc_qp_when_roce_enabled); |
| } |
| |
| caps->isolate_vl_tc = MLX5_CAP_GEN(mdev, isolate_vl_tc_new); |
| |
| if (caps->flex_protocols & MLX5_FLEX_PARSER_ICMP_V4_ENABLED) { |
| caps->flex_parser_id_icmp_dw0 = MLX5_CAP_GEN(mdev, flex_parser_id_icmp_dw0); |
| caps->flex_parser_id_icmp_dw1 = MLX5_CAP_GEN(mdev, flex_parser_id_icmp_dw1); |
| } |
| |
| if (caps->flex_protocols & MLX5_FLEX_PARSER_ICMP_V6_ENABLED) { |
| caps->flex_parser_id_icmpv6_dw0 = |
| MLX5_CAP_GEN(mdev, flex_parser_id_icmpv6_dw0); |
| caps->flex_parser_id_icmpv6_dw1 = |
| MLX5_CAP_GEN(mdev, flex_parser_id_icmpv6_dw1); |
| } |
| |
| if (caps->flex_protocols & MLX5_FLEX_PARSER_GENEVE_TLV_OPTION_0_ENABLED) |
| caps->flex_parser_id_geneve_tlv_option_0 = |
| MLX5_CAP_GEN(mdev, flex_parser_id_geneve_tlv_option_0); |
| |
| if (caps->flex_protocols & MLX5_FLEX_PARSER_MPLS_OVER_GRE_ENABLED) |
| caps->flex_parser_id_mpls_over_gre = |
| MLX5_CAP_GEN(mdev, flex_parser_id_outer_first_mpls_over_gre); |
| |
| if (caps->flex_protocols & mlx5_FLEX_PARSER_MPLS_OVER_UDP_ENABLED) |
| caps->flex_parser_id_mpls_over_udp = |
| MLX5_CAP_GEN(mdev, flex_parser_id_outer_first_mpls_over_udp_label); |
| |
| if (caps->flex_protocols & MLX5_FLEX_PARSER_GTPU_DW_0_ENABLED) |
| caps->flex_parser_id_gtpu_dw_0 = |
| MLX5_CAP_GEN(mdev, flex_parser_id_gtpu_dw_0); |
| |
| if (caps->flex_protocols & MLX5_FLEX_PARSER_GTPU_TEID_ENABLED) |
| caps->flex_parser_id_gtpu_teid = |
| MLX5_CAP_GEN(mdev, flex_parser_id_gtpu_teid); |
| |
| if (caps->flex_protocols & MLX5_FLEX_PARSER_GTPU_DW_2_ENABLED) |
| caps->flex_parser_id_gtpu_dw_2 = |
| MLX5_CAP_GEN(mdev, flex_parser_id_gtpu_dw_2); |
| |
| if (caps->flex_protocols & MLX5_FLEX_PARSER_GTPU_FIRST_EXT_DW_0_ENABLED) |
| caps->flex_parser_id_gtpu_first_ext_dw_0 = |
| MLX5_CAP_GEN(mdev, flex_parser_id_gtpu_first_ext_dw_0); |
| |
| caps->nic_rx_drop_address = |
| MLX5_CAP64_FLOWTABLE(mdev, sw_steering_nic_rx_action_drop_icm_address); |
| caps->nic_tx_drop_address = |
| MLX5_CAP64_FLOWTABLE(mdev, sw_steering_nic_tx_action_drop_icm_address); |
| caps->nic_tx_allow_address = |
| MLX5_CAP64_FLOWTABLE(mdev, sw_steering_nic_tx_action_allow_icm_address); |
| |
| caps->rx_sw_owner_v2 = MLX5_CAP_FLOWTABLE_NIC_RX(mdev, sw_owner_v2); |
| caps->tx_sw_owner_v2 = MLX5_CAP_FLOWTABLE_NIC_TX(mdev, sw_owner_v2); |
| |
| if (!caps->rx_sw_owner_v2) |
| caps->rx_sw_owner = MLX5_CAP_FLOWTABLE_NIC_RX(mdev, sw_owner); |
| if (!caps->tx_sw_owner_v2) |
| caps->tx_sw_owner = MLX5_CAP_FLOWTABLE_NIC_TX(mdev, sw_owner); |
| |
| caps->max_ft_level = MLX5_CAP_FLOWTABLE_NIC_RX(mdev, max_ft_level); |
| |
| caps->log_icm_size = MLX5_CAP_DEV_MEM(mdev, log_steering_sw_icm_size); |
| caps->hdr_modify_icm_addr = |
| MLX5_CAP64_DEV_MEM(mdev, header_modify_sw_icm_start_address); |
| |
| caps->roce_min_src_udp = MLX5_CAP_ROCE(mdev, r_roce_min_src_udp_port); |
| |
| return 0; |
| } |
| |
| int mlx5dr_cmd_query_flow_table(struct mlx5_core_dev *dev, |
| enum fs_flow_table_type type, |
| u32 table_id, |
| struct mlx5dr_cmd_query_flow_table_details *output) |
| { |
| u32 out[MLX5_ST_SZ_DW(query_flow_table_out)] = {}; |
| u32 in[MLX5_ST_SZ_DW(query_flow_table_in)] = {}; |
| int err; |
| |
| MLX5_SET(query_flow_table_in, in, opcode, |
| MLX5_CMD_OP_QUERY_FLOW_TABLE); |
| |
| MLX5_SET(query_flow_table_in, in, table_type, type); |
| MLX5_SET(query_flow_table_in, in, table_id, table_id); |
| |
| err = mlx5_cmd_exec_inout(dev, query_flow_table, in, out); |
| if (err) |
| return err; |
| |
| output->status = MLX5_GET(query_flow_table_out, out, status); |
| output->level = MLX5_GET(query_flow_table_out, out, flow_table_context.level); |
| |
| output->sw_owner_icm_root_1 = MLX5_GET64(query_flow_table_out, out, |
| flow_table_context.sw_owner_icm_root_1); |
| output->sw_owner_icm_root_0 = MLX5_GET64(query_flow_table_out, out, |
| flow_table_context.sw_owner_icm_root_0); |
| |
| return 0; |
| } |
| |
| int mlx5dr_cmd_query_flow_sampler(struct mlx5_core_dev *dev, |
| u32 sampler_id, |
| u64 *rx_icm_addr, |
| u64 *tx_icm_addr) |
| { |
| u32 out[MLX5_ST_SZ_DW(query_sampler_obj_out)] = {}; |
| u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {}; |
| void *attr; |
| int ret; |
| |
| MLX5_SET(general_obj_in_cmd_hdr, in, opcode, |
| MLX5_CMD_OP_QUERY_GENERAL_OBJECT); |
| MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, |
| MLX5_GENERAL_OBJECT_TYPES_SAMPLER); |
| MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, sampler_id); |
| |
| ret = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); |
| if (ret) |
| return ret; |
| |
| attr = MLX5_ADDR_OF(query_sampler_obj_out, out, sampler_object); |
| |
| *rx_icm_addr = MLX5_GET64(sampler_obj, attr, |
| sw_steering_icm_address_rx); |
| *tx_icm_addr = MLX5_GET64(sampler_obj, attr, |
| sw_steering_icm_address_tx); |
| |
| return 0; |
| } |
| |
| int mlx5dr_cmd_sync_steering(struct mlx5_core_dev *mdev) |
| { |
| u32 in[MLX5_ST_SZ_DW(sync_steering_in)] = {}; |
| |
| MLX5_SET(sync_steering_in, in, opcode, MLX5_CMD_OP_SYNC_STEERING); |
| |
| return mlx5_cmd_exec_in(mdev, sync_steering, in); |
| } |
| |
| int mlx5dr_cmd_set_fte_modify_and_vport(struct mlx5_core_dev *mdev, |
| u32 table_type, |
| u32 table_id, |
| u32 group_id, |
| u32 modify_header_id, |
| u32 vport_id) |
| { |
| u32 out[MLX5_ST_SZ_DW(set_fte_out)] = {}; |
| void *in_flow_context; |
| unsigned int inlen; |
| void *in_dests; |
| u32 *in; |
| int err; |
| |
| inlen = MLX5_ST_SZ_BYTES(set_fte_in) + |
| 1 * MLX5_ST_SZ_BYTES(dest_format_struct); /* One destination only */ |
| |
| in = kvzalloc(inlen, GFP_KERNEL); |
| if (!in) |
| return -ENOMEM; |
| |
| MLX5_SET(set_fte_in, in, opcode, MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY); |
| MLX5_SET(set_fte_in, in, table_type, table_type); |
| MLX5_SET(set_fte_in, in, table_id, table_id); |
| |
| in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context); |
| MLX5_SET(flow_context, in_flow_context, group_id, group_id); |
| MLX5_SET(flow_context, in_flow_context, modify_header_id, modify_header_id); |
| MLX5_SET(flow_context, in_flow_context, destination_list_size, 1); |
| MLX5_SET(flow_context, in_flow_context, action, |
| MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | |
| MLX5_FLOW_CONTEXT_ACTION_MOD_HDR); |
| |
| in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination); |
| MLX5_SET(dest_format_struct, in_dests, destination_type, |
| MLX5_FLOW_DESTINATION_TYPE_VPORT); |
| MLX5_SET(dest_format_struct, in_dests, destination_id, vport_id); |
| |
| err = mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out)); |
| kvfree(in); |
| |
| return err; |
| } |
| |
| int mlx5dr_cmd_del_flow_table_entry(struct mlx5_core_dev *mdev, |
| u32 table_type, |
| u32 table_id) |
| { |
| u32 in[MLX5_ST_SZ_DW(delete_fte_in)] = {}; |
| |
| MLX5_SET(delete_fte_in, in, opcode, MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY); |
| MLX5_SET(delete_fte_in, in, table_type, table_type); |
| MLX5_SET(delete_fte_in, in, table_id, table_id); |
| |
| return mlx5_cmd_exec_in(mdev, delete_fte, in); |
| } |
| |
| int mlx5dr_cmd_alloc_modify_header(struct mlx5_core_dev *mdev, |
| u32 table_type, |
| u8 num_of_actions, |
| u64 *actions, |
| u32 *modify_header_id) |
| { |
| u32 out[MLX5_ST_SZ_DW(alloc_modify_header_context_out)] = {}; |
| void *p_actions; |
| u32 inlen; |
| u32 *in; |
| int err; |
| |
| inlen = MLX5_ST_SZ_BYTES(alloc_modify_header_context_in) + |
| num_of_actions * sizeof(u64); |
| in = kvzalloc(inlen, GFP_KERNEL); |
| if (!in) |
| return -ENOMEM; |
| |
| MLX5_SET(alloc_modify_header_context_in, in, opcode, |
| MLX5_CMD_OP_ALLOC_MODIFY_HEADER_CONTEXT); |
| MLX5_SET(alloc_modify_header_context_in, in, table_type, table_type); |
| MLX5_SET(alloc_modify_header_context_in, in, num_of_actions, num_of_actions); |
| p_actions = MLX5_ADDR_OF(alloc_modify_header_context_in, in, actions); |
| memcpy(p_actions, actions, num_of_actions * sizeof(u64)); |
| |
| err = mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out)); |
| if (err) |
| goto out; |
| |
| *modify_header_id = MLX5_GET(alloc_modify_header_context_out, out, |
| modify_header_id); |
| out: |
| kvfree(in); |
| return err; |
| } |
| |
| int mlx5dr_cmd_dealloc_modify_header(struct mlx5_core_dev *mdev, |
| u32 modify_header_id) |
| { |
| u32 in[MLX5_ST_SZ_DW(dealloc_modify_header_context_in)] = {}; |
| |
| MLX5_SET(dealloc_modify_header_context_in, in, opcode, |
| MLX5_CMD_OP_DEALLOC_MODIFY_HEADER_CONTEXT); |
| MLX5_SET(dealloc_modify_header_context_in, in, modify_header_id, |
| modify_header_id); |
| |
| return mlx5_cmd_exec_in(mdev, dealloc_modify_header_context, in); |
| } |
| |
| int mlx5dr_cmd_create_empty_flow_group(struct mlx5_core_dev *mdev, |
| u32 table_type, |
| u32 table_id, |
| u32 *group_id) |
| { |
| u32 out[MLX5_ST_SZ_DW(create_flow_group_out)] = {}; |
| int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); |
| u32 *in; |
| int err; |
| |
| in = kvzalloc(inlen, GFP_KERNEL); |
| if (!in) |
| return -ENOMEM; |
| |
| MLX5_SET(create_flow_group_in, in, opcode, MLX5_CMD_OP_CREATE_FLOW_GROUP); |
| MLX5_SET(create_flow_group_in, in, table_type, table_type); |
| MLX5_SET(create_flow_group_in, in, table_id, table_id); |
| |
| err = mlx5_cmd_exec_inout(mdev, create_flow_group, in, out); |
| if (err) |
| goto out; |
| |
| *group_id = MLX5_GET(create_flow_group_out, out, group_id); |
| |
| out: |
| kvfree(in); |
| return err; |
| } |
| |
| int mlx5dr_cmd_destroy_flow_group(struct mlx5_core_dev *mdev, |
| u32 table_type, |
| u32 table_id, |
| u32 group_id) |
| { |
| u32 in[MLX5_ST_SZ_DW(destroy_flow_group_in)] = {}; |
| |
| MLX5_SET(destroy_flow_group_in, in, opcode, |
| MLX5_CMD_OP_DESTROY_FLOW_GROUP); |
| MLX5_SET(destroy_flow_group_in, in, table_type, table_type); |
| MLX5_SET(destroy_flow_group_in, in, table_id, table_id); |
| MLX5_SET(destroy_flow_group_in, in, group_id, group_id); |
| |
| return mlx5_cmd_exec_in(mdev, destroy_flow_group, in); |
| } |
| |
| int mlx5dr_cmd_create_flow_table(struct mlx5_core_dev *mdev, |
| struct mlx5dr_cmd_create_flow_table_attr *attr, |
| u64 *fdb_rx_icm_addr, |
| u32 *table_id) |
| { |
| u32 out[MLX5_ST_SZ_DW(create_flow_table_out)] = {}; |
| u32 in[MLX5_ST_SZ_DW(create_flow_table_in)] = {}; |
| void *ft_mdev; |
| int err; |
| |
| MLX5_SET(create_flow_table_in, in, opcode, MLX5_CMD_OP_CREATE_FLOW_TABLE); |
| MLX5_SET(create_flow_table_in, in, table_type, attr->table_type); |
| |
| ft_mdev = MLX5_ADDR_OF(create_flow_table_in, in, flow_table_context); |
| MLX5_SET(flow_table_context, ft_mdev, termination_table, attr->term_tbl); |
| MLX5_SET(flow_table_context, ft_mdev, sw_owner, attr->sw_owner); |
| MLX5_SET(flow_table_context, ft_mdev, level, attr->level); |
| |
| if (attr->sw_owner) { |
| /* icm_addr_0 used for FDB RX / NIC TX / NIC_RX |
| * icm_addr_1 used for FDB TX |
| */ |
| if (attr->table_type == MLX5_FLOW_TABLE_TYPE_NIC_RX) { |
| MLX5_SET64(flow_table_context, ft_mdev, |
| sw_owner_icm_root_0, attr->icm_addr_rx); |
| } else if (attr->table_type == MLX5_FLOW_TABLE_TYPE_NIC_TX) { |
| MLX5_SET64(flow_table_context, ft_mdev, |
| sw_owner_icm_root_0, attr->icm_addr_tx); |
| } else if (attr->table_type == MLX5_FLOW_TABLE_TYPE_FDB) { |
| MLX5_SET64(flow_table_context, ft_mdev, |
| sw_owner_icm_root_0, attr->icm_addr_rx); |
| MLX5_SET64(flow_table_context, ft_mdev, |
| sw_owner_icm_root_1, attr->icm_addr_tx); |
| } |
| } |
| |
| MLX5_SET(create_flow_table_in, in, flow_table_context.decap_en, |
| attr->decap_en); |
| MLX5_SET(create_flow_table_in, in, flow_table_context.reformat_en, |
| attr->reformat_en); |
| |
| err = mlx5_cmd_exec_inout(mdev, create_flow_table, in, out); |
| if (err) |
| return err; |
| |
| *table_id = MLX5_GET(create_flow_table_out, out, table_id); |
| if (!attr->sw_owner && attr->table_type == MLX5_FLOW_TABLE_TYPE_FDB && |
| fdb_rx_icm_addr) |
| *fdb_rx_icm_addr = |
| (u64)MLX5_GET(create_flow_table_out, out, icm_address_31_0) | |
| (u64)MLX5_GET(create_flow_table_out, out, icm_address_39_32) << 32 | |
| (u64)MLX5_GET(create_flow_table_out, out, icm_address_63_40) << 40; |
| |
| return 0; |
| } |
| |
| int mlx5dr_cmd_destroy_flow_table(struct mlx5_core_dev *mdev, |
| u32 table_id, |
| u32 table_type) |
| { |
| u32 in[MLX5_ST_SZ_DW(destroy_flow_table_in)] = {}; |
| |
| MLX5_SET(destroy_flow_table_in, in, opcode, |
| MLX5_CMD_OP_DESTROY_FLOW_TABLE); |
| MLX5_SET(destroy_flow_table_in, in, table_type, table_type); |
| MLX5_SET(destroy_flow_table_in, in, table_id, table_id); |
| |
| return mlx5_cmd_exec_in(mdev, destroy_flow_table, in); |
| } |
| |
| int mlx5dr_cmd_create_reformat_ctx(struct mlx5_core_dev *mdev, |
| enum mlx5_reformat_ctx_type rt, |
| u8 reformat_param_0, |
| u8 reformat_param_1, |
| size_t reformat_size, |
| void *reformat_data, |
| u32 *reformat_id) |
| { |
| u32 out[MLX5_ST_SZ_DW(alloc_packet_reformat_context_out)] = {}; |
| size_t inlen, cmd_data_sz, cmd_total_sz; |
| void *prctx; |
| void *pdata; |
| void *in; |
| int err; |
| |
| cmd_total_sz = MLX5_ST_SZ_BYTES(alloc_packet_reformat_context_in); |
| cmd_data_sz = MLX5_FLD_SZ_BYTES(alloc_packet_reformat_context_in, |
| packet_reformat_context.reformat_data); |
| inlen = ALIGN(cmd_total_sz + reformat_size - cmd_data_sz, 4); |
| in = kvzalloc(inlen, GFP_KERNEL); |
| if (!in) |
| return -ENOMEM; |
| |
| MLX5_SET(alloc_packet_reformat_context_in, in, opcode, |
| MLX5_CMD_OP_ALLOC_PACKET_REFORMAT_CONTEXT); |
| |
| prctx = MLX5_ADDR_OF(alloc_packet_reformat_context_in, in, packet_reformat_context); |
| pdata = MLX5_ADDR_OF(packet_reformat_context_in, prctx, reformat_data); |
| |
| MLX5_SET(packet_reformat_context_in, prctx, reformat_type, rt); |
| MLX5_SET(packet_reformat_context_in, prctx, reformat_param_0, reformat_param_0); |
| MLX5_SET(packet_reformat_context_in, prctx, reformat_param_1, reformat_param_1); |
| MLX5_SET(packet_reformat_context_in, prctx, reformat_data_size, reformat_size); |
| if (reformat_data && reformat_size) |
| memcpy(pdata, reformat_data, reformat_size); |
| |
| err = mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out)); |
| if (err) |
| return err; |
| |
| *reformat_id = MLX5_GET(alloc_packet_reformat_context_out, out, packet_reformat_id); |
| kvfree(in); |
| |
| return err; |
| } |
| |
| void mlx5dr_cmd_destroy_reformat_ctx(struct mlx5_core_dev *mdev, |
| u32 reformat_id) |
| { |
| u32 in[MLX5_ST_SZ_DW(dealloc_packet_reformat_context_in)] = {}; |
| |
| MLX5_SET(dealloc_packet_reformat_context_in, in, opcode, |
| MLX5_CMD_OP_DEALLOC_PACKET_REFORMAT_CONTEXT); |
| MLX5_SET(dealloc_packet_reformat_context_in, in, packet_reformat_id, |
| reformat_id); |
| |
| mlx5_cmd_exec_in(mdev, dealloc_packet_reformat_context, in); |
| } |
| |
| int mlx5dr_cmd_query_gid(struct mlx5_core_dev *mdev, u8 vhca_port_num, |
| u16 index, struct mlx5dr_cmd_gid_attr *attr) |
| { |
| u32 out[MLX5_ST_SZ_DW(query_roce_address_out)] = {}; |
| u32 in[MLX5_ST_SZ_DW(query_roce_address_in)] = {}; |
| int err; |
| |
| MLX5_SET(query_roce_address_in, in, opcode, |
| MLX5_CMD_OP_QUERY_ROCE_ADDRESS); |
| |
| MLX5_SET(query_roce_address_in, in, roce_address_index, index); |
| MLX5_SET(query_roce_address_in, in, vhca_port_num, vhca_port_num); |
| |
| err = mlx5_cmd_exec_inout(mdev, query_roce_address, in, out); |
| if (err) |
| return err; |
| |
| memcpy(&attr->gid, |
| MLX5_ADDR_OF(query_roce_address_out, |
| out, roce_address.source_l3_address), |
| sizeof(attr->gid)); |
| memcpy(attr->mac, |
| MLX5_ADDR_OF(query_roce_address_out, out, |
| roce_address.source_mac_47_32), |
| sizeof(attr->mac)); |
| |
| if (MLX5_GET(query_roce_address_out, out, |
| roce_address.roce_version) == MLX5_ROCE_VERSION_2) |
| attr->roce_ver = MLX5_ROCE_VERSION_2; |
| else |
| attr->roce_ver = MLX5_ROCE_VERSION_1; |
| |
| return 0; |
| } |
| |
| static int mlx5dr_cmd_set_extended_dest(struct mlx5_core_dev *dev, |
| struct mlx5dr_cmd_fte_info *fte, |
| bool *extended_dest) |
| { |
| int fw_log_max_fdb_encap_uplink = MLX5_CAP_ESW(dev, log_max_fdb_encap_uplink); |
| int num_fwd_destinations = 0; |
| int num_encap = 0; |
| int i; |
| |
| *extended_dest = false; |
| if (!(fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST)) |
| return 0; |
| for (i = 0; i < fte->dests_size; i++) { |
| if (fte->dest_arr[i].type == MLX5_FLOW_DESTINATION_TYPE_COUNTER) |
| continue; |
| if (fte->dest_arr[i].type == MLX5_FLOW_DESTINATION_TYPE_VPORT && |
| fte->dest_arr[i].vport.flags & MLX5_FLOW_DEST_VPORT_REFORMAT_ID) |
| num_encap++; |
| num_fwd_destinations++; |
| } |
| |
| if (num_fwd_destinations > 1 && num_encap > 0) |
| *extended_dest = true; |
| |
| if (*extended_dest && !fw_log_max_fdb_encap_uplink) { |
| mlx5_core_warn(dev, "FW does not support extended destination"); |
| return -EOPNOTSUPP; |
| } |
| if (num_encap > (1 << fw_log_max_fdb_encap_uplink)) { |
| mlx5_core_warn(dev, "FW does not support more than %d encaps", |
| 1 << fw_log_max_fdb_encap_uplink); |
| return -EOPNOTSUPP; |
| } |
| |
| return 0; |
| } |
| |
| int mlx5dr_cmd_set_fte(struct mlx5_core_dev *dev, |
| int opmod, int modify_mask, |
| struct mlx5dr_cmd_ft_info *ft, |
| u32 group_id, |
| struct mlx5dr_cmd_fte_info *fte) |
| { |
| u32 out[MLX5_ST_SZ_DW(set_fte_out)] = {}; |
| void *in_flow_context, *vlan; |
| bool extended_dest = false; |
| void *in_match_value; |
| unsigned int inlen; |
| int dst_cnt_size; |
| void *in_dests; |
| u32 *in; |
| int err; |
| int i; |
| |
| if (mlx5dr_cmd_set_extended_dest(dev, fte, &extended_dest)) |
| return -EOPNOTSUPP; |
| |
| if (!extended_dest) |
| dst_cnt_size = MLX5_ST_SZ_BYTES(dest_format_struct); |
| else |
| dst_cnt_size = MLX5_ST_SZ_BYTES(extended_dest_format); |
| |
| inlen = MLX5_ST_SZ_BYTES(set_fte_in) + fte->dests_size * dst_cnt_size; |
| in = kvzalloc(inlen, GFP_KERNEL); |
| if (!in) |
| return -ENOMEM; |
| |
| MLX5_SET(set_fte_in, in, opcode, MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY); |
| MLX5_SET(set_fte_in, in, op_mod, opmod); |
| MLX5_SET(set_fte_in, in, modify_enable_mask, modify_mask); |
| MLX5_SET(set_fte_in, in, table_type, ft->type); |
| MLX5_SET(set_fte_in, in, table_id, ft->id); |
| MLX5_SET(set_fte_in, in, flow_index, fte->index); |
| MLX5_SET(set_fte_in, in, ignore_flow_level, fte->ignore_flow_level); |
| if (ft->vport) { |
| MLX5_SET(set_fte_in, in, vport_number, ft->vport); |
| MLX5_SET(set_fte_in, in, other_vport, 1); |
| } |
| |
| in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context); |
| MLX5_SET(flow_context, in_flow_context, group_id, group_id); |
| |
| MLX5_SET(flow_context, in_flow_context, flow_tag, |
| fte->flow_context.flow_tag); |
| MLX5_SET(flow_context, in_flow_context, flow_source, |
| fte->flow_context.flow_source); |
| |
| MLX5_SET(flow_context, in_flow_context, extended_destination, |
| extended_dest); |
| if (extended_dest) { |
| u32 action; |
| |
| action = fte->action.action & |
| ~MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT; |
| MLX5_SET(flow_context, in_flow_context, action, action); |
| } else { |
| MLX5_SET(flow_context, in_flow_context, action, |
| fte->action.action); |
| if (fte->action.pkt_reformat) |
| MLX5_SET(flow_context, in_flow_context, packet_reformat_id, |
| fte->action.pkt_reformat->id); |
| } |
| if (fte->action.modify_hdr) |
| MLX5_SET(flow_context, in_flow_context, modify_header_id, |
| fte->action.modify_hdr->id); |
| |
| vlan = MLX5_ADDR_OF(flow_context, in_flow_context, push_vlan); |
| |
| MLX5_SET(vlan, vlan, ethtype, fte->action.vlan[0].ethtype); |
| MLX5_SET(vlan, vlan, vid, fte->action.vlan[0].vid); |
| MLX5_SET(vlan, vlan, prio, fte->action.vlan[0].prio); |
| |
| vlan = MLX5_ADDR_OF(flow_context, in_flow_context, push_vlan_2); |
| |
| MLX5_SET(vlan, vlan, ethtype, fte->action.vlan[1].ethtype); |
| MLX5_SET(vlan, vlan, vid, fte->action.vlan[1].vid); |
| MLX5_SET(vlan, vlan, prio, fte->action.vlan[1].prio); |
| |
| in_match_value = MLX5_ADDR_OF(flow_context, in_flow_context, |
| match_value); |
| memcpy(in_match_value, fte->val, sizeof(u32) * MLX5_ST_SZ_DW_MATCH_PARAM); |
| |
| in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination); |
| if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { |
| int list_size = 0; |
| |
| for (i = 0; i < fte->dests_size; i++) { |
| unsigned int id, type = fte->dest_arr[i].type; |
| |
| if (type == MLX5_FLOW_DESTINATION_TYPE_COUNTER) |
| continue; |
| |
| switch (type) { |
| case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE_NUM: |
| id = fte->dest_arr[i].ft_num; |
| type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; |
| break; |
| case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE: |
| id = fte->dest_arr[i].ft_id; |
| break; |
| case MLX5_FLOW_DESTINATION_TYPE_VPORT: |
| id = fte->dest_arr[i].vport.num; |
| MLX5_SET(dest_format_struct, in_dests, |
| destination_eswitch_owner_vhca_id_valid, |
| !!(fte->dest_arr[i].vport.flags & |
| MLX5_FLOW_DEST_VPORT_VHCA_ID)); |
| MLX5_SET(dest_format_struct, in_dests, |
| destination_eswitch_owner_vhca_id, |
| fte->dest_arr[i].vport.vhca_id); |
| if (extended_dest && (fte->dest_arr[i].vport.flags & |
| MLX5_FLOW_DEST_VPORT_REFORMAT_ID)) { |
| MLX5_SET(dest_format_struct, in_dests, |
| packet_reformat, |
| !!(fte->dest_arr[i].vport.flags & |
| MLX5_FLOW_DEST_VPORT_REFORMAT_ID)); |
| MLX5_SET(extended_dest_format, in_dests, |
| packet_reformat_id, |
| fte->dest_arr[i].vport.reformat_id); |
| } |
| break; |
| case MLX5_FLOW_DESTINATION_TYPE_FLOW_SAMPLER: |
| id = fte->dest_arr[i].sampler_id; |
| break; |
| default: |
| id = fte->dest_arr[i].tir_num; |
| } |
| |
| MLX5_SET(dest_format_struct, in_dests, destination_type, |
| type); |
| MLX5_SET(dest_format_struct, in_dests, destination_id, id); |
| in_dests += dst_cnt_size; |
| list_size++; |
| } |
| |
| MLX5_SET(flow_context, in_flow_context, destination_list_size, |
| list_size); |
| } |
| |
| if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { |
| int max_list_size = BIT(MLX5_CAP_FLOWTABLE_TYPE(dev, |
| log_max_flow_counter, |
| ft->type)); |
| int list_size = 0; |
| |
| for (i = 0; i < fte->dests_size; i++) { |
| if (fte->dest_arr[i].type != |
| MLX5_FLOW_DESTINATION_TYPE_COUNTER) |
| continue; |
| |
| MLX5_SET(flow_counter_list, in_dests, flow_counter_id, |
| fte->dest_arr[i].counter_id); |
| in_dests += dst_cnt_size; |
| list_size++; |
| } |
| if (list_size > max_list_size) { |
| err = -EINVAL; |
| goto err_out; |
| } |
| |
| MLX5_SET(flow_context, in_flow_context, flow_counter_list_size, |
| list_size); |
| } |
| |
| err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out)); |
| err_out: |
| kvfree(in); |
| return err; |
| } |