1 /*- 2 * Copyright (c) 2013-2017, Mellanox Technologies, Ltd. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include "opt_rss.h" 27 #include "opt_ratelimit.h" 28 29 #include <linux/types.h> 30 #include <linux/module.h> 31 #include <dev/mlx5/mlx5_ifc.h> 32 #include <dev/mlx5/device.h> 33 #include <dev/mlx5/fs.h> 34 35 #include <dev/mlx5/mlx5_core/fs_core.h> 36 #include <dev/mlx5/mlx5_core/mlx5_core.h> 37 38 int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev, 39 enum fs_ft_type type, 40 unsigned int id) 41 { 42 u32 in[MLX5_ST_SZ_DW(set_flow_table_root_in)] = {0}; 43 u32 out[MLX5_ST_SZ_DW(set_flow_table_root_out)] = {0}; 44 45 if (!dev) 46 return -EINVAL; 47 48 MLX5_SET(set_flow_table_root_in, in, opcode, 49 MLX5_CMD_OP_SET_FLOW_TABLE_ROOT); 50 MLX5_SET(set_flow_table_root_in, in, table_type, type); 51 MLX5_SET(set_flow_table_root_in, in, table_id, id); 52 53 return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); 54 } 55 56 int mlx5_cmd_fs_create_ft(struct mlx5_core_dev *dev, 57 u16 vport, enum fs_ft_type type, unsigned int level, 58 unsigned int log_size, const char *name, unsigned int *table_id) 59 { 60 u32 in[MLX5_ST_SZ_DW(create_flow_table_in)] = {0}; 61 u32 out[MLX5_ST_SZ_DW(create_flow_table_out)] = {0}; 62 int err; 63 64 if (!dev) 65 return -EINVAL; 66 67 MLX5_SET(create_flow_table_in, in, opcode, 68 MLX5_CMD_OP_CREATE_FLOW_TABLE); 69 70 MLX5_SET(create_flow_table_in, in, table_type, type); 71 MLX5_SET(create_flow_table_in, in, flow_table_context.level, level); 72 MLX5_SET(create_flow_table_in, in, flow_table_context.log_size, 73 log_size); 74 if (strstr(name, FS_REFORMAT_KEYWORD) != NULL) 75 MLX5_SET(create_flow_table_in, in, 76 flow_table_context.reformat_en, 1); 77 if (vport) { 78 MLX5_SET(create_flow_table_in, in, vport_number, vport); 79 MLX5_SET(create_flow_table_in, in, other_vport, 1); 80 } 81 82 err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); 83 if (!err) 84 *table_id = MLX5_GET(create_flow_table_out, out, table_id); 85 86 return err; 87 } 88 89 int mlx5_cmd_fs_destroy_ft(struct mlx5_core_dev *dev, 90 u16 vport, 91 enum fs_ft_type type, unsigned int table_id) 92 { 93 u32 in[MLX5_ST_SZ_DW(destroy_flow_table_in)] = {0}; 94 u32 out[MLX5_ST_SZ_DW(destroy_flow_table_out)] = {0}; 95 96 if (!dev) 97 return -EINVAL; 98 99 MLX5_SET(destroy_flow_table_in, in, opcode, 100 MLX5_CMD_OP_DESTROY_FLOW_TABLE); 101 MLX5_SET(destroy_flow_table_in, in, table_type, type); 102 MLX5_SET(destroy_flow_table_in, in, table_id, table_id); 103 if (vport) { 104 MLX5_SET(destroy_flow_table_in, in, vport_number, vport); 105 MLX5_SET(destroy_flow_table_in, in, other_vport, 1); 106 } 107 108 return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); 109 } 110 111 int mlx5_cmd_fs_create_fg(struct mlx5_core_dev *dev, 112 u32 *in, 113 u16 vport, 114 enum fs_ft_type type, unsigned int table_id, 115 unsigned int *group_id) 116 { 117 u32 out[MLX5_ST_SZ_DW(create_flow_group_out)] = {0}; 118 int err; 119 int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); 120 if (!dev) 121 return -EINVAL; 122 123 MLX5_SET(create_flow_group_in, in, opcode, 124 MLX5_CMD_OP_CREATE_FLOW_GROUP); 125 MLX5_SET(create_flow_group_in, in, table_type, type); 126 MLX5_SET(create_flow_group_in, in, table_id, table_id); 127 if (vport) { 128 MLX5_SET(create_flow_group_in, in, vport_number, vport); 129 MLX5_SET(create_flow_group_in, in, other_vport, 1); 130 } 131 132 err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out)); 133 if (!err) 134 *group_id = MLX5_GET(create_flow_group_out, out, group_id); 135 136 return err; 137 } 138 139 int mlx5_cmd_fs_destroy_fg(struct mlx5_core_dev *dev, 140 u16 vport, 141 enum fs_ft_type type, unsigned int table_id, 142 unsigned int group_id) 143 { 144 u32 in[MLX5_ST_SZ_DW(destroy_flow_group_in)] = {0}; 145 u32 out[MLX5_ST_SZ_DW(destroy_flow_group_out)] = {0}; 146 147 if (!dev) 148 return -EINVAL; 149 150 MLX5_SET(destroy_flow_group_in, in, opcode, 151 MLX5_CMD_OP_DESTROY_FLOW_GROUP); 152 MLX5_SET(destroy_flow_group_in, in, table_type, type); 153 MLX5_SET(destroy_flow_group_in, in, table_id, table_id); 154 MLX5_SET(destroy_flow_group_in, in, group_id, group_id); 155 if (vport) { 156 MLX5_SET(destroy_flow_group_in, in, vport_number, vport); 157 MLX5_SET(destroy_flow_group_in, in, other_vport, 1); 158 } 159 160 return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); 161 } 162 163 int mlx5_cmd_fs_set_fte(struct mlx5_core_dev *dev, 164 u16 vport, 165 enum fs_fte_status *fte_status, 166 u32 *match_val, 167 enum fs_ft_type type, unsigned int table_id, 168 unsigned int index, unsigned int group_id, 169 struct mlx5_flow_act *flow_act, 170 u32 sw_action, int dest_size, 171 struct list_head *dests) /* mlx5_flow_desination */ 172 { 173 u32 out[MLX5_ST_SZ_DW(set_fte_out)] = {0}; 174 u32 *in; 175 unsigned int inlen; 176 struct mlx5_flow_rule *dst; 177 void *in_flow_context; 178 void *in_match_value; 179 void *in_dests; 180 int err; 181 int opmod = 0; 182 int modify_mask = 0; 183 int atomic_mod_cap; 184 u32 prm_action = 0; 185 int count_list = 0; 186 187 if (sw_action != MLX5_FLOW_RULE_FWD_ACTION_DEST) 188 dest_size = 0; 189 190 if (sw_action & MLX5_FLOW_RULE_FWD_ACTION_ALLOW) 191 prm_action |= MLX5_FLOW_CONTEXT_ACTION_ALLOW; 192 193 if (sw_action & MLX5_FLOW_RULE_FWD_ACTION_DROP) 194 prm_action |= MLX5_FLOW_CONTEXT_ACTION_DROP; 195 196 if (sw_action & MLX5_FLOW_RULE_FWD_ACTION_DEST) 197 prm_action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; 198 199 if (flow_act->actions & MLX5_FLOW_ACT_ACTIONS_COUNT) { 200 prm_action |= MLX5_FLOW_CONTEXT_ACTION_COUNT; 201 count_list = 1; 202 } 203 204 inlen = MLX5_ST_SZ_BYTES(set_fte_in) + 205 (dest_size + count_list) * MLX5_ST_SZ_BYTES(dest_format_struct); 206 207 if (!dev) 208 return -EINVAL; 209 210 if (*fte_status & FS_FTE_STATUS_EXISTING) { 211 atomic_mod_cap = MLX5_CAP_FLOWTABLE(dev, 212 flow_table_properties_nic_receive. 213 flow_modify_en); 214 if (!atomic_mod_cap) 215 return -ENOTSUPP; 216 opmod = 1; 217 modify_mask = 1 << 218 MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST; 219 } 220 221 in = mlx5_vzalloc(inlen); 222 if (!in) { 223 mlx5_core_warn(dev, "failed to allocate inbox\n"); 224 return -ENOMEM; 225 } 226 227 MLX5_SET(set_fte_in, in, opcode, MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY); 228 MLX5_SET(set_fte_in, in, op_mod, opmod); 229 MLX5_SET(set_fte_in, in, modify_enable_mask, modify_mask); 230 MLX5_SET(set_fte_in, in, table_type, type); 231 MLX5_SET(set_fte_in, in, table_id, table_id); 232 MLX5_SET(set_fte_in, in, flow_index, index); 233 if (vport) { 234 MLX5_SET(set_fte_in, in, vport_number, vport); 235 MLX5_SET(set_fte_in, in, other_vport, 1); 236 } 237 238 in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context); 239 MLX5_SET(flow_context, in_flow_context, group_id, group_id); 240 if (flow_act->actions & MLX5_FLOW_ACT_ACTIONS_FLOW_TAG) 241 MLX5_SET(flow_context, in_flow_context, flow_tag, flow_act->flow_tag); 242 if (flow_act->actions & MLX5_FLOW_ACT_ACTIONS_MODIFY_HDR) { 243 MLX5_SET(flow_context, in_flow_context, modify_header_id, 244 flow_act->modify_hdr->id); 245 prm_action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; 246 } 247 if (flow_act->actions & MLX5_FLOW_ACT_ACTIONS_PACKET_REFORMAT) { 248 MLX5_SET(flow_context, in_flow_context, packet_reformat_id, 249 flow_act->pkt_reformat->id); 250 prm_action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT; 251 } 252 MLX5_SET(flow_context, in_flow_context, destination_list_size, 253 dest_size); 254 in_match_value = MLX5_ADDR_OF(flow_context, in_flow_context, 255 match_value); 256 memcpy(in_match_value, match_val, MLX5_ST_SZ_BYTES(fte_match_param)); 257 258 in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination); 259 260 if (dest_size) { 261 list_for_each_entry(dst, dests, base.list) { 262 unsigned int id; 263 264 MLX5_SET(dest_format_struct, in_dests, destination_type, 265 dst->dest_attr.type); 266 if (dst->dest_attr.type == 267 MLX5_FLOW_CONTEXT_DEST_TYPE_FLOW_TABLE) 268 id = dst->dest_attr.ft->id; 269 else 270 id = dst->dest_attr.tir_num; 271 MLX5_SET(dest_format_struct, in_dests, destination_id, id); 272 in_dests += MLX5_ST_SZ_BYTES(dest_format_struct); 273 } 274 } 275 276 if (flow_act->actions & MLX5_FLOW_ACT_ACTIONS_COUNT) { 277 MLX5_SET(dest_format_struct, in_dests, destination_id, 278 mlx5_fc_id(flow_act->counter)); 279 in_dests += MLX5_ST_SZ_BYTES(dest_format_struct); 280 MLX5_SET(flow_context, in_flow_context, flow_counter_list_size, 1); 281 } 282 283 MLX5_SET(flow_context, in_flow_context, action, prm_action); 284 err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out)); 285 if (!err) 286 *fte_status |= FS_FTE_STATUS_EXISTING; 287 288 kvfree(in); 289 290 return err; 291 } 292 293 int mlx5_cmd_fs_delete_fte(struct mlx5_core_dev *dev, 294 u16 vport, 295 enum fs_fte_status *fte_status, 296 enum fs_ft_type type, unsigned int table_id, 297 unsigned int index) 298 { 299 u32 in[MLX5_ST_SZ_DW(delete_fte_in)] = {0}; 300 u32 out[MLX5_ST_SZ_DW(delete_fte_out)] = {0}; 301 int err; 302 303 if (!(*fte_status & FS_FTE_STATUS_EXISTING)) 304 return 0; 305 306 if (!dev) 307 return -EINVAL; 308 309 MLX5_SET(delete_fte_in, in, opcode, MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY); 310 MLX5_SET(delete_fte_in, in, table_type, type); 311 MLX5_SET(delete_fte_in, in, table_id, table_id); 312 MLX5_SET(delete_fte_in, in, flow_index, index); 313 if (vport) { 314 MLX5_SET(delete_fte_in, in, vport_number, vport); 315 MLX5_SET(delete_fte_in, in, other_vport, 1); 316 } 317 318 err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); 319 if (!err) 320 *fte_status = 0; 321 322 return err; 323 } 324 325 int mlx5_cmd_modify_header_alloc(struct mlx5_core_dev *dev, 326 enum mlx5_flow_namespace_type namespace, 327 u8 num_actions, 328 void *modify_actions, 329 struct mlx5_modify_hdr *modify_hdr) 330 { 331 u32 out[MLX5_ST_SZ_DW(alloc_modify_header_context_out)] = {}; 332 int max_actions, actions_size, inlen, err; 333 void *actions_in; 334 u8 table_type; 335 u32 *in; 336 337 switch (namespace) { 338 case MLX5_FLOW_NAMESPACE_FDB: 339 max_actions = MLX5_CAP_ESW_FLOWTABLE_FDB(dev, max_modify_header_actions); 340 table_type = FS_FT_FDB; 341 break; 342 case MLX5_FLOW_NAMESPACE_KERNEL: 343 case MLX5_FLOW_NAMESPACE_BYPASS: 344 max_actions = MLX5_CAP_FLOWTABLE_NIC_RX(dev, max_modify_header_actions); 345 table_type = FS_FT_NIC_RX; 346 break; 347 case MLX5_FLOW_NAMESPACE_ESW_INGRESS: 348 max_actions = MLX5_CAP_ESW_INGRESS_ACL(dev, max_modify_header_actions); 349 table_type = FS_FT_ESW_INGRESS_ACL; 350 break; 351 default: 352 return -EOPNOTSUPP; 353 } 354 355 if (num_actions > max_actions) { 356 mlx5_core_warn(dev, "too many modify header actions %d, max supported %d\n", 357 num_actions, max_actions); 358 return -EOPNOTSUPP; 359 } 360 361 actions_size = MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto) * num_actions; 362 inlen = MLX5_ST_SZ_BYTES(alloc_modify_header_context_in) + actions_size; 363 364 in = kzalloc(inlen, GFP_KERNEL); 365 if (!in) 366 return -ENOMEM; 367 368 MLX5_SET(alloc_modify_header_context_in, in, opcode, 369 MLX5_CMD_OP_ALLOC_MODIFY_HEADER_CONTEXT); 370 MLX5_SET(alloc_modify_header_context_in, in, table_type, table_type); 371 MLX5_SET(alloc_modify_header_context_in, in, num_of_actions, num_actions); 372 373 actions_in = MLX5_ADDR_OF(alloc_modify_header_context_in, in, actions); 374 memcpy(actions_in, modify_actions, actions_size); 375 376 err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out)); 377 378 modify_hdr->id = MLX5_GET(alloc_modify_header_context_out, out, modify_header_id); 379 kfree(in); 380 381 return err; 382 } 383 384 void mlx5_cmd_modify_header_dealloc(struct mlx5_core_dev *dev, 385 struct mlx5_modify_hdr *modify_hdr) 386 { 387 u32 out[MLX5_ST_SZ_DW(dealloc_modify_header_context_out)] = {}; 388 u32 in[MLX5_ST_SZ_DW(dealloc_modify_header_context_in)] = {}; 389 390 MLX5_SET(dealloc_modify_header_context_in, in, opcode, 391 MLX5_CMD_OP_DEALLOC_MODIFY_HEADER_CONTEXT); 392 MLX5_SET(dealloc_modify_header_context_in, in, modify_header_id, 393 modify_hdr->id); 394 395 mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); 396 } 397 398 int mlx5_cmd_packet_reformat_alloc(struct mlx5_core_dev *dev, 399 struct mlx5_pkt_reformat_params *params, 400 enum mlx5_flow_namespace_type namespace, 401 struct mlx5_pkt_reformat *pkt_reformat) 402 { 403 u32 out[MLX5_ST_SZ_DW(alloc_packet_reformat_context_out)] = {}; 404 void *packet_reformat_context_in; 405 int max_encap_size; 406 void *reformat; 407 int inlen; 408 int err; 409 u32 *in; 410 411 if (namespace == MLX5_FLOW_NAMESPACE_FDB) 412 max_encap_size = MLX5_CAP_ESW(dev, max_encap_header_size); 413 else 414 max_encap_size = MLX5_CAP_FLOWTABLE(dev, max_encap_header_size); 415 416 if (params->size > max_encap_size) { 417 mlx5_core_warn(dev, "encap size %zd too big, max supported is %d\n", 418 params->size, max_encap_size); 419 return -EINVAL; 420 } 421 422 in = kzalloc(MLX5_ST_SZ_BYTES(alloc_packet_reformat_context_in) + 423 params->size, GFP_KERNEL); 424 if (!in) 425 return -ENOMEM; 426 427 packet_reformat_context_in = MLX5_ADDR_OF(alloc_packet_reformat_context_in, 428 in, packet_reformat_context); 429 reformat = MLX5_ADDR_OF(packet_reformat_context_in, 430 packet_reformat_context_in, 431 reformat_data); 432 inlen = reformat - (void *)in + params->size; 433 434 MLX5_SET(alloc_packet_reformat_context_in, in, opcode, 435 MLX5_CMD_OP_ALLOC_PACKET_REFORMAT_CONTEXT); 436 MLX5_SET(packet_reformat_context_in, packet_reformat_context_in, 437 reformat_data_size, params->size); 438 MLX5_SET(packet_reformat_context_in, packet_reformat_context_in, 439 reformat_type, params->type); 440 MLX5_SET(packet_reformat_context_in, packet_reformat_context_in, 441 reformat_param_0, params->param_0); 442 MLX5_SET(packet_reformat_context_in, packet_reformat_context_in, 443 reformat_param_1, params->param_1); 444 if (params->data && params->size) 445 memcpy(reformat, params->data, params->size); 446 447 err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out)); 448 449 pkt_reformat->id = MLX5_GET(alloc_packet_reformat_context_out, 450 out, packet_reformat_id); 451 kfree(in); 452 453 return err; 454 } 455 456 void mlx5_cmd_packet_reformat_dealloc(struct mlx5_core_dev *dev, 457 struct mlx5_pkt_reformat *pkt_reformat) 458 { 459 u32 out[MLX5_ST_SZ_DW(dealloc_packet_reformat_context_out)] = {}; 460 u32 in[MLX5_ST_SZ_DW(dealloc_packet_reformat_context_in)] = {}; 461 462 MLX5_SET(dealloc_packet_reformat_context_in, in, opcode, 463 MLX5_CMD_OP_DEALLOC_PACKET_REFORMAT_CONTEXT); 464 MLX5_SET(dealloc_packet_reformat_context_in, in, packet_reformat_id, 465 pkt_reformat->id); 466 467 mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); 468 } 469