1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) 2024 NVIDIA Corporation & Affiliates 3 */ 4 5 #include <rte_common.h> 6 #include <rte_flow.h> 7 8 #include "mlx5_malloc.h" 9 #include "mlx5.h" 10 #include "mlx5_defs.h" 11 #include "mlx5_flow.h" 12 #include "mlx5_rx.h" 13 14 #ifdef HAVE_MLX5_HWS_SUPPORT 15 16 /* 17 * Generate new actions lists for prefix and suffix flows. 18 * 19 * @param[in] dev 20 * Pointer to rte_eth_dev structure. 21 * @param[in] prefix_act 22 * Pointer to actions for prefix flow rule. 23 * @param[in] suffix_act 24 * Pointer to actions for suffix flow rule. 25 * @param[in] actions 26 * Pointer to the original actions list. 27 * @param[in] qrss 28 * Pointer to the action of QUEUE / RSS. 29 * @param[in] actions_n 30 * Number of the actions in the original list. 31 * @param[out] error 32 * Pointer to error structure. 33 * 34 * @return 35 * Positive prefix flow ID on success, zero on failure. 36 */ 37 static uint32_t 38 mlx5_flow_nta_split_qrss_actions_prep(struct rte_eth_dev *dev, 39 struct rte_flow_action *prefix_act, 40 struct rte_flow_action *suffix_act, 41 const struct rte_flow_action *actions, 42 const struct rte_flow_action *qrss, 43 int actions_n, 44 struct rte_flow_error *error) 45 { 46 struct mlx5_priv *priv = dev->data->dev_private; 47 struct mlx5_rte_flow_action_set_tag *set_tag; 48 struct rte_flow_action_jump *jump; 49 const int qrss_idx = qrss - actions; 50 uint32_t flow_id = 0; 51 int ret = 0; 52 53 /* Allocate the new subflow ID and used to be matched later. */ 54 mlx5_ipool_malloc(priv->sh->ipool[MLX5_IPOOL_RSS_EXPANTION_FLOW_ID], &flow_id); 55 if (!flow_id) { 56 rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_ACTION, NULL, 57 "can't allocate id for split Q/RSS subflow"); 58 return 0; 59 } 60 /* 61 * Given actions will be split 62 * - Replace QUEUE/RSS action with SET_TAG to set flow ID. 63 * - Add jump to mreg CP_TBL. 64 * As a result, there will be one more action. 65 */ 66 memcpy(prefix_act, actions, sizeof(struct rte_flow_action) * actions_n); 67 /* Count MLX5_RTE_FLOW_ACTION_TYPE_TAG. */ 68 actions_n++; 69 set_tag = (void *)(prefix_act + actions_n); 70 /* Reuse ASO reg, should always succeed. Consider to use REG_C_6. */ 71 ret = flow_hw_get_reg_id_by_domain(dev, RTE_FLOW_ITEM_TYPE_METER_COLOR, 72 MLX5DR_TABLE_TYPE_NIC_RX, 0); 73 MLX5_ASSERT(ret != (int)REG_NON); 74 set_tag->id = (enum modify_reg)ret; 75 /* Internal SET_TAG action to set flow ID. */ 76 set_tag->data = flow_id; 77 /* Construct new actions array and replace QUEUE/RSS action. */ 78 prefix_act[qrss_idx] = (struct rte_flow_action) { 79 .type = (enum rte_flow_action_type)MLX5_RTE_FLOW_ACTION_TYPE_TAG, 80 .conf = set_tag, 81 }; 82 /* JUMP action to jump to mreg copy table (CP_TBL). */ 83 jump = (void *)(set_tag + 1); 84 *jump = (struct rte_flow_action_jump) { 85 .group = MLX5_FLOW_MREG_CP_TABLE_GROUP, 86 }; 87 prefix_act[actions_n - 2] = (struct rte_flow_action) { 88 .type = RTE_FLOW_ACTION_TYPE_JUMP, 89 .conf = jump, 90 }; 91 prefix_act[actions_n - 1] = (struct rte_flow_action) { 92 .type = RTE_FLOW_ACTION_TYPE_END, 93 }; 94 /* Copy the suffix Q/RSS action, can also be indirect RSS. */ 95 suffix_act[0] = (struct rte_flow_action) { 96 .type = qrss->type, 97 .conf = qrss->conf, 98 }; 99 suffix_act[1] = (struct rte_flow_action) { 100 .type = RTE_FLOW_ACTION_TYPE_END, 101 }; 102 return flow_id; 103 } 104 105 /* 106 * Generate new attribute and items for suffix flows. 107 * 108 * @param[in] dev 109 * Pointer to rte_eth_dev structure. 110 * @param[in] split_attr 111 * Pointer to attribute for prefix flow rule. 112 * @param[in] split_items 113 * Pointer to actions for suffix flow rule. 114 * @param[in] qrss_id 115 * Prefix flow ID to match. 116 */ 117 static void 118 mlx5_flow_nta_split_qrss_items_prep(struct rte_eth_dev *dev, 119 struct rte_flow_attr *split_attr, 120 struct rte_flow_item *split_items, 121 uint32_t qrss_id) 122 { 123 struct mlx5_rte_flow_item_tag *q_tag_spec; 124 125 /* MLX5_FLOW_MREG_CP_TABLE_GROUP -> MLX5_FLOW_MREG_ACT_TABLE_GROUP(Q/RSS base) */ 126 split_attr->ingress = 1; 127 split_attr->group = MLX5_FLOW_MREG_ACT_TABLE_GROUP; 128 /* Only internal tag will be used, together with the item flags for RSS. */ 129 q_tag_spec = (void *)((char *)split_items + 2 * sizeof(struct rte_flow_item)); 130 split_items[0].type = (enum rte_flow_item_type)MLX5_RTE_FLOW_ITEM_TYPE_TAG; 131 split_items[0].spec = q_tag_spec; 132 split_items[1].type = RTE_FLOW_ITEM_TYPE_END; 133 q_tag_spec->data = qrss_id; 134 q_tag_spec->id = (enum modify_reg) 135 flow_hw_get_reg_id_by_domain(dev, RTE_FLOW_ITEM_TYPE_METER_COLOR, 136 MLX5DR_TABLE_TYPE_NIC_RX, 0); 137 MLX5_ASSERT(q_tag_spec->id != REG_NON); 138 } 139 140 /* 141 * Checking the split information and split the actions, items, attributes into 142 * prefix and suffix to connect the flows after passing the copy tables. 143 * 144 * @param[in] dev 145 * Pointer to rte_eth_dev structure. 146 * @param[in] attr 147 * Pointer to the flow attributes. 148 * @param[in] actions 149 * Pointer to the original actions list. 150 * @param[in] qrss 151 * Pointer to the action of QUEUE / RSS. 152 * @param[in] action_flags 153 * Holds the actions detected. 154 * @param[in] actions_n 155 * Number of original actions. 156 * @param[in] external 157 * This flow rule is created by request external to PMD. 158 * @param[out] res 159 * Pointer to the resource to store the split result. 160 * @param[out] error 161 * Pointer to error structure. 162 * 163 * @return 164 * - Positive 1 on succeed. 165 * - 0 on no split. 166 * - negative errno value on error. 167 */ 168 int 169 mlx5_flow_nta_split_metadata(struct rte_eth_dev *dev, 170 const struct rte_flow_attr *attr, 171 const struct rte_flow_action actions[], 172 const struct rte_flow_action *qrss, 173 uint64_t action_flags, 174 int actions_n, 175 bool external, 176 struct mlx5_flow_hw_split_resource *res, 177 struct rte_flow_error *error) 178 { 179 struct mlx5_priv *priv = dev->data->dev_private; 180 struct mlx5_sh_config *config = &priv->sh->config; 181 const struct rte_flow_action_queue *queue; 182 const struct rte_flow_action_rss *rss; 183 struct rte_flow_action *prfx_actions; 184 struct rte_flow_action *sfx_actions; 185 struct rte_flow_attr *sfx_attr; 186 struct rte_flow_item *sfx_items; 187 size_t pefx_act_size, sfx_act_size; 188 size_t attr_size, item_size; 189 size_t total_size; 190 uint32_t qrss_id; 191 192 /* 193 * The metadata copy flow should be created: 194 * 1. only on NIC Rx domain with Q / RSS 195 * 2. only when extended metadata mode is enabled 196 * 3. only on HWS, should always be "config->dv_flow_en == 2", this 197 * checking can be skipped 198 * Note: 199 * 1. Even if metadata is not enabled in the data-path, it can still 200 * be used to match on the Rx side. 201 * 2. The HWS Tx default copy rule or SQ rules already have the metadata 202 * copy on the root table. The user's rule will always be inserted 203 * and executed after the root table steering. 204 */ 205 if (config->dv_xmeta_en == MLX5_XMETA_MODE_LEGACY || attr->transfer || 206 attr->egress || !external || !qrss) 207 return 0; 208 if (action_flags & MLX5_FLOW_ACTION_QUEUE) { 209 queue = (const struct rte_flow_action_queue *)actions->conf; 210 if (mlx5_rxq_is_hairpin(dev, queue->index)) 211 return 0; 212 } else if (action_flags & MLX5_FLOW_ACTION_RSS) { 213 rss = (const struct rte_flow_action_rss *)actions->conf; 214 if (mlx5_rxq_is_hairpin(dev, rss->queue[0])) 215 return 0; 216 } 217 /* The prefix and suffix flows' actions. */ 218 pefx_act_size = sizeof(struct rte_flow_action) * (actions_n + 1) + 219 sizeof(struct rte_flow_action_set_tag) + 220 sizeof(struct rte_flow_action_jump); 221 sfx_act_size = sizeof(struct rte_flow_action) * 2; 222 /* The suffix attribute. */ 223 attr_size = sizeof(struct rte_flow_attr); 224 /* The suffix items - mlx5_tag + end. */ 225 item_size = sizeof(struct rte_flow_item) * 2 + 226 sizeof(struct mlx5_rte_flow_item_tag); 227 total_size = pefx_act_size + sfx_act_size + attr_size + item_size; 228 prfx_actions = mlx5_malloc(MLX5_MEM_ZERO, total_size, 0, SOCKET_ID_ANY); 229 if (!prfx_actions) 230 return rte_flow_error_set(error, ENOMEM, 231 RTE_FLOW_ERROR_TYPE_ACTION, 232 NULL, "no memory to split " 233 "metadata flow"); 234 sfx_actions = (void *)((char *)prfx_actions + pefx_act_size); 235 qrss_id = mlx5_flow_nta_split_qrss_actions_prep(dev, prfx_actions, 236 sfx_actions, actions, 237 qrss, actions_n, error); 238 if (!qrss_id) { 239 mlx5_free(prfx_actions); 240 return -rte_errno; 241 } 242 sfx_attr = (void *)((char *)sfx_actions + sfx_act_size); 243 sfx_items = (void *)((char *)sfx_attr + attr_size); 244 mlx5_flow_nta_split_qrss_items_prep(dev, sfx_attr, sfx_items, qrss_id); 245 res->prefix.actions = prfx_actions; 246 res->suffix.actions = sfx_actions; 247 res->suffix.items = sfx_items; 248 res->suffix.attr = sfx_attr; 249 res->buf_start = prfx_actions; 250 res->flow_idx = qrss_id; 251 return 1; 252 } 253 254 /* 255 * Release the buffer and flow ID. 256 * 257 * @param[in] dev 258 * Pointer to rte_eth_dev structure. 259 * @param[in] res 260 * Pointer to the resource to release. 261 */ 262 void 263 mlx5_flow_nta_split_resource_free(struct rte_eth_dev *dev, 264 struct mlx5_flow_hw_split_resource *res) 265 { 266 struct mlx5_priv *priv = dev->data->dev_private; 267 268 mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_RSS_EXPANTION_FLOW_ID], res->flow_idx); 269 mlx5_free(res->buf_start); 270 } 271 272 /* 273 * Callback functions for the metadata copy and mark / flag set flow. 274 * The create and remove cannot reuse the DV since the flow opaque and structure 275 * are different, and the action used to copy the metadata is also different. 276 */ 277 struct mlx5_list_entry * 278 flow_nta_mreg_create_cb(void *tool_ctx, void *cb_ctx) 279 { 280 struct rte_eth_dev *dev = tool_ctx; 281 struct mlx5_priv *priv = dev->data->dev_private; 282 struct mlx5_flow_cb_ctx *ctx = cb_ctx; 283 struct mlx5_flow_mreg_copy_resource *mcp_res; 284 struct rte_flow_error *error = ctx->error; 285 uint32_t idx = 0; 286 uint32_t mark_id = *(uint32_t *)(ctx->data); 287 struct rte_flow_attr attr = { 288 .group = MLX5_FLOW_MREG_CP_TABLE_GROUP, 289 .ingress = 1, 290 }; 291 struct mlx5_rte_flow_item_tag tag_spec = { 292 .id = REG_C_0, 293 .data = mark_id, 294 }; 295 struct mlx5_rte_flow_item_tag tag_mask = { 296 .data = priv->sh->dv_mark_mask, 297 }; 298 struct rte_flow_action_mark ftag = { 299 .id = mark_id, 300 }; 301 struct rte_flow_action_modify_field rx_meta = { 302 .operation = RTE_FLOW_MODIFY_SET, 303 .dst = { 304 .field = (enum rte_flow_field_id)MLX5_RTE_FLOW_FIELD_META_REG, 305 .tag_index = REG_B, 306 }, 307 .src = { 308 .field = (enum rte_flow_field_id)MLX5_RTE_FLOW_FIELD_META_REG, 309 .tag_index = REG_C_1, 310 }, 311 .width = 32, 312 }; 313 struct rte_flow_action_jump jump = { 314 .group = MLX5_FLOW_MREG_ACT_TABLE_GROUP, 315 }; 316 struct rte_flow_item items[2]; 317 struct rte_flow_action actions[4]; 318 319 /* Provide the full width of FLAG specific value. */ 320 if (mark_id == (priv->sh->dv_regc0_mask & MLX5_FLOW_MARK_DEFAULT)) 321 tag_spec.data = MLX5_FLOW_MARK_DEFAULT; 322 /* Build a new flow. */ 323 if (mark_id != MLX5_DEFAULT_COPY_ID) { 324 items[0] = (struct rte_flow_item) { 325 .type = (enum rte_flow_item_type)MLX5_RTE_FLOW_ITEM_TYPE_TAG, 326 .spec = &tag_spec, 327 .mask = &tag_mask, 328 }; 329 actions[0] = (struct rte_flow_action) { 330 .type = RTE_FLOW_ACTION_TYPE_MARK, 331 .conf = &ftag, 332 }; 333 } else { 334 /* Default rule, wildcard match with lowest priority. */ 335 attr.priority = MLX5_FLOW_LOWEST_PRIO_INDICATOR; 336 items[0] = (struct rte_flow_item) { 337 .type = RTE_FLOW_ITEM_TYPE_ETH, 338 }; 339 actions[0] = (struct rte_flow_action) { 340 .type = RTE_FLOW_ACTION_TYPE_VOID, 341 }; 342 } 343 /* (match REG 'tag') or all. */ 344 items[1].type = RTE_FLOW_ITEM_TYPE_END; 345 /* (Mark) or void + copy to Rx meta + jump to the MREG_ACT_TABLE_GROUP. */ 346 actions[1].type = RTE_FLOW_ACTION_TYPE_MODIFY_FIELD, 347 actions[1].conf = &rx_meta, 348 actions[2].type = RTE_FLOW_ACTION_TYPE_JUMP; 349 actions[2].conf = &jump; 350 actions[3].type = RTE_FLOW_ACTION_TYPE_END; 351 /* Build a new entry. */ 352 mcp_res = mlx5_ipool_zmalloc(priv->sh->ipool[MLX5_IPOOL_MCP], &idx); 353 if (!mcp_res) { 354 rte_errno = ENOMEM; 355 return NULL; 356 } 357 mcp_res->idx = idx; 358 mcp_res->mark_id = mark_id; 359 /* 360 * The copy flows are not included in any list. There 361 * ones are referenced from other flows and cannot 362 * be applied, removed, deleted in arbitrary order 363 * by list traversing. 364 */ 365 mcp_res->hw_flow = mlx5_flow_list_create(dev, MLX5_FLOW_TYPE_MCP, &attr, 366 items, actions, false, error); 367 if (!mcp_res->hw_flow) { 368 mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_MCP], idx); 369 return NULL; 370 } 371 return &mcp_res->hlist_ent; 372 } 373 374 void 375 flow_nta_mreg_remove_cb(void *tool_ctx, struct mlx5_list_entry *entry) 376 { 377 struct mlx5_flow_mreg_copy_resource *mcp_res = 378 container_of(entry, typeof(*mcp_res), hlist_ent); 379 struct rte_eth_dev *dev = tool_ctx; 380 struct mlx5_priv *priv = dev->data->dev_private; 381 382 MLX5_ASSERT(mcp_res->hw_flow); 383 mlx5_flow_list_destroy(dev, MLX5_FLOW_TYPE_MCP, mcp_res->hw_flow); 384 mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_MCP], mcp_res->idx); 385 } 386 387 /* 388 * Add a flow of copying flow metadata registers in RX_CP_TBL. 389 * @see flow_mreg_add_copy_action 390 * 391 * @param[in] dev 392 * Pointer to Ethernet device. 393 * @param[in] mark_id 394 * ID of MARK action, zero means default flow for META. 395 * @param[out] error 396 * Perform verbose error reporting if not NULL. 397 * 398 * @return 399 * Associated resource on success, NULL otherwise and rte_errno is set. 400 */ 401 static struct mlx5_flow_mreg_copy_resource * 402 mlx5_flow_nta_add_copy_action(struct rte_eth_dev *dev, 403 uint32_t mark_id, 404 struct rte_flow_error *error) 405 { 406 struct mlx5_priv *priv = dev->data->dev_private; 407 struct mlx5_list_entry *entry; 408 uint32_t specialize = 0; 409 struct mlx5_flow_cb_ctx ctx = { 410 .dev = dev, 411 .error = error, 412 .data = &mark_id, 413 .data2 = &specialize, 414 }; 415 416 /* Check if already registered. */ 417 MLX5_ASSERT(priv->sh->mreg_cp_tbl); 418 entry = mlx5_hlist_register(priv->sh->mreg_cp_tbl, mark_id, &ctx); 419 if (!entry) 420 return NULL; 421 return container_of(entry, struct mlx5_flow_mreg_copy_resource, hlist_ent); 422 } 423 424 /* 425 * Release flow in RX_CP_TBL. 426 * 427 * @param[in] dev 428 * Pointer to Ethernet device. 429 * @param[in] idx 430 * Index in the pool to store the copy flow. 431 */ 432 void 433 mlx5_flow_nta_del_copy_action(struct rte_eth_dev *dev, uint32_t idx) 434 { 435 struct mlx5_flow_mreg_copy_resource *mcp_res; 436 struct mlx5_priv *priv = dev->data->dev_private; 437 438 if (!idx) 439 return; 440 mcp_res = mlx5_ipool_get(priv->sh->ipool[MLX5_IPOOL_MCP], idx); 441 if (!mcp_res || !priv->sh->mreg_cp_tbl) 442 return; 443 MLX5_ASSERT(mcp_res->hw_flow); 444 mlx5_hlist_unregister(priv->sh->mreg_cp_tbl, &mcp_res->hlist_ent); 445 } 446 447 /* 448 * Remove the default copy action from RX_CP_TBL. 449 * @see flow_mreg_del_default_copy_action 450 * 451 * This functions is called in the mlx5_dev_start(). No thread safe 452 * is guaranteed. 453 * 454 * @param[in] dev 455 * Pointer to Ethernet device. 456 */ 457 void 458 mlx5_flow_nta_del_default_copy_action(struct rte_eth_dev *dev) 459 { 460 struct mlx5_list_entry *entry; 461 struct mlx5_priv *priv = dev->data->dev_private; 462 struct mlx5_flow_cb_ctx ctx; 463 uint32_t mark_id; 464 465 /* Check if default flow is registered. */ 466 if (!priv->sh->mreg_cp_tbl) 467 return; 468 mark_id = MLX5_DEFAULT_COPY_ID; 469 ctx.data = &mark_id; 470 entry = mlx5_hlist_lookup(priv->sh->mreg_cp_tbl, mark_id, &ctx); 471 if (!entry) 472 return; 473 mlx5_hlist_unregister(priv->sh->mreg_cp_tbl, entry); 474 } 475 476 /* 477 * Add the default copy action in RX_CP_TBL. 478 * 479 * This functions is called in the mlx5_dev_start(). No thread safe 480 * is guaranteed. 481 * @see flow_mreg_add_default_copy_action 482 * 483 * @param[in] dev 484 * Pointer to Ethernet device. 485 * @param[out] error 486 * Perform verbose error reporting if not NULL. 487 * 488 * @return 489 * 0 for success, negative value otherwise and rte_errno is set. 490 */ 491 int 492 mlx5_flow_nta_add_default_copy_action(struct rte_eth_dev *dev, 493 struct rte_flow_error *error) 494 { 495 struct mlx5_priv *priv = dev->data->dev_private; 496 struct mlx5_sh_config *config = &priv->sh->config; 497 struct mlx5_flow_mreg_copy_resource *mcp_res; 498 struct mlx5_flow_cb_ctx ctx; 499 uint32_t mark_id; 500 501 if (config->dv_xmeta_en == MLX5_XMETA_MODE_LEGACY || 502 !priv->sh->dv_regc0_mask) 503 return 0; 504 /* 505 * Add default mreg copy flow may be called multiple time, but 506 * only be called once in stop. Avoid register it twice. 507 */ 508 mark_id = MLX5_DEFAULT_COPY_ID; 509 ctx.data = &mark_id; 510 if (mlx5_hlist_lookup(priv->sh->mreg_cp_tbl, mark_id, &ctx)) 511 return 0; 512 mcp_res = mlx5_flow_nta_add_copy_action(dev, mark_id, error); 513 if (!mcp_res) 514 return -rte_errno; 515 return 0; 516 } 517 518 /* 519 * Add a flow of copying flow metadata registers in RX_CP_TBL. 520 * @see flow_mreg_update_copy_table 521 * 522 * @param[in] dev 523 * Pointer to Ethernet device. 524 * @param[out] idx 525 * Pointer to store the index of flow in the pool. 526 * @param[in] mark 527 * Pointer to mark or flag action. 528 * @param[in] action_flags 529 * Holds the actions detected. 530 * @param[out] error 531 * Perform verbose error reporting if not NULL. 532 * 533 * @return 534 * 0 on success, negative value otherwise and rte_errno is set. 535 */ 536 int 537 mlx5_flow_nta_update_copy_table(struct rte_eth_dev *dev, 538 uint32_t *idx, 539 const struct rte_flow_action *mark, 540 uint64_t action_flags, 541 struct rte_flow_error *error) 542 { 543 struct mlx5_priv *priv = dev->data->dev_private; 544 struct mlx5_sh_config *config = &priv->sh->config; 545 struct mlx5_flow_mreg_copy_resource *mcp_res; 546 const struct rte_flow_action_mark *mark_conf; 547 uint32_t mark_id; 548 549 if (config->dv_xmeta_en == MLX5_XMETA_MODE_LEGACY || 550 !priv->sh->dv_regc0_mask) 551 return 0; 552 /* Find MARK action. */ 553 if (action_flags & (MLX5_FLOW_ACTION_FLAG | MLX5_FLOW_ACTION_MARK)) { 554 if (mark) { 555 mark_conf = (const struct rte_flow_action_mark *)mark->conf; 556 mark_id = mark_conf->id; 557 } else { 558 mark_id = MLX5_FLOW_MARK_DEFAULT; 559 } 560 mcp_res = mlx5_flow_nta_add_copy_action(dev, mark_id, error); 561 if (!mcp_res) 562 return -rte_errno; 563 *idx = mcp_res->idx; 564 } 565 return 0; 566 } 567 568 #endif 569