Add support for GENEVE option fields modification. Only fields configured in parser creation can be modified.
Signed-off-by: Michael Baum <michae...@nvidia.com> --- doc/guides/nics/mlx5.rst | 4 + doc/guides/rel_notes/release_24_03.rst | 3 + drivers/net/mlx5/mlx5_flow.h | 21 +++++ drivers/net/mlx5/mlx5_flow_dv.c | 78 ++++++++++++++++- drivers/net/mlx5/mlx5_flow_geneve.c | 117 +++++++++++++++++++++++++ drivers/net/mlx5/mlx5_flow_hw.c | 71 ++++++++++----- 6 files changed, 268 insertions(+), 26 deletions(-) diff --git a/doc/guides/nics/mlx5.rst b/doc/guides/nics/mlx5.rst index fceb5bd58b..85820d7931 100644 --- a/doc/guides/nics/mlx5.rst +++ b/doc/guides/nics/mlx5.rst @@ -582,6 +582,10 @@ Limitations - Modification of GENEVE Network ID's is not supported when configured ``FLEX_PARSER_PROFILE_ENABLE`` supports Geneve TLV options. See :ref:`mlx5_firmware_config` for more flex parser information. + - Modification of GENEVE TLV option fields is supported only for HW steering. + Only DWs configured in :ref:`parser creation <geneve_parser_api>` can be modified, + 'type' and 'class' fields can be modified when ``match_on_class_mode=2``. + - Modification of GENEVE TLV option data supports one DW per action. - Encapsulation levels are not supported, can modify outermost header fields only. - Offsets cannot skip past the boundary of a field. - If the field type is ``RTE_FLOW_FIELD_MAC_TYPE`` diff --git a/doc/guides/rel_notes/release_24_03.rst b/doc/guides/rel_notes/release_24_03.rst index 8a99d6bfa4..f8d87c8a3c 100644 --- a/doc/guides/rel_notes/release_24_03.rst +++ b/doc/guides/rel_notes/release_24_03.rst @@ -60,6 +60,9 @@ New Features * Added HW steering support for ``RTE_FLOW_ITEM_TYPE_GENEVE`` flow item. * Added HW steering support for ``RTE_FLOW_ITEM_TYPE_GENEVE_OPT`` flow item. * Added HW steering support for modify field ``RTE_FLOW_FIELD_GENEVE_VNI`` flow action. + * Added HW steering support for modify field ``RTE_FLOW_FIELD_GENEVE_OPT_TYPE`` flow action. + * Added HW steering support for modify field ``RTE_FLOW_FIELD_GENEVE_OPT_CLASS`` flow action. + * Added HW steering support for modify field ``RTE_FLOW_FIELD_GENEVE_OPT_DATA`` flow action. Removed Items diff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h index 808f364c6c..65fe5be2fd 100644 --- a/drivers/net/mlx5/mlx5_flow.h +++ b/drivers/net/mlx5/mlx5_flow.h @@ -1805,6 +1805,25 @@ mlx5_get_geneve_hl_data(const void *dr_ctx, uint8_t type, uint16_t class, struct mlx5_hl_data ** const hl_dws, bool *ok_bit_on_class); +/** + * Get modify field ID for single DW inside configured GENEVE TLV option. + * + * @param[in] dr_ctx + * Pointer to HW steering DR context. + * @param[in] type + * GENEVE TLV option type. + * @param[in] class + * GENEVE TLV option class. + * @param[in] dw_offset + * Offset of DW inside the option. + * + * @return + * Modify field ID on success, negative errno otherwise and rte_errno is set. + */ +int +mlx5_get_geneve_option_modify_field_id(const void *dr_ctx, uint8_t type, + uint16_t class, uint8_t dw_offset); + void * mlx5_geneve_tlv_parser_create(uint16_t port_id, const struct rte_pmd_mlx5_geneve_tlv tlv_list[], @@ -1813,6 +1832,8 @@ int mlx5_geneve_tlv_parser_destroy(void *handle); int mlx5_flow_geneve_tlv_option_validate(struct mlx5_priv *priv, const struct rte_flow_item *geneve_opt, struct rte_flow_error *error); +int mlx5_geneve_opt_modi_field_get(struct mlx5_priv *priv, + const struct rte_flow_action_modify_data *data); struct mlx5_geneve_tlv_options_mng; int mlx5_geneve_tlv_option_register(struct mlx5_priv *priv, diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c index bb3d7ddc3c..2a7ee4e91f 100644 --- a/drivers/net/mlx5/mlx5_flow_dv.c +++ b/drivers/net/mlx5/mlx5_flow_dv.c @@ -1446,6 +1446,21 @@ mlx5_mpls_modi_field_get(const struct rte_flow_action_modify_data *data) return MLX5_MODI_IN_MPLS_LABEL_0 + data->tag_index; } +static __rte_always_inline int +flow_geneve_opt_modi_field_get(struct mlx5_priv *priv, + const struct rte_flow_action_modify_data *data) +{ +#ifdef HAVE_MLX5_HWS_SUPPORT + return mlx5_geneve_opt_modi_field_get(priv, data); +#else + (void)priv; + (void)data; + DRV_LOG(ERR, "GENEVE option modification is not supported."); + rte_errno = ENOTSUP; + return -rte_errno; +#endif +} + static void mlx5_modify_flex_item(const struct rte_eth_dev *dev, const struct mlx5_flex_item *flex, @@ -1579,9 +1594,11 @@ mlx5_flow_field_id_to_modify_info const struct rte_flow_attr *attr, struct rte_flow_error *error) { struct mlx5_priv *priv = dev->data->dev_private; + enum mlx5_modification_field modi_id; uint32_t idx = 0; uint32_t off_be = 0; uint32_t length = 0; + switch ((int)data->field) { case RTE_FLOW_FIELD_START: /* not supported yet */ @@ -1892,6 +1909,48 @@ mlx5_flow_field_id_to_modify_info else info[idx].offset = off_be; break; + case RTE_FLOW_FIELD_GENEVE_OPT_TYPE: + MLX5_ASSERT(data->offset + width <= 8); + modi_id = flow_geneve_opt_modi_field_get(priv, data); + if (modi_id < 0) + return; + /* Type is on bits 16-8 of GENEVE option header (DW0). */ + off_be = 32 - (16 + data->offset + width); + info[idx] = (struct field_modify_info){4, 0, modi_id}; + if (mask) + mask[idx] = flow_modify_info_mask_32(width, off_be); + else + info[idx].offset = off_be; + break; + case RTE_FLOW_FIELD_GENEVE_OPT_CLASS: + MLX5_ASSERT(data->offset + width <= 16); + modi_id = flow_geneve_opt_modi_field_get(priv, data); + if (modi_id < 0) + return; + /* Class is on bits 31-16 of GENEVE option header (DW0). */ + off_be = 32 - (data->offset + width); + info[idx] = (struct field_modify_info){4, 0, modi_id}; + if (mask) + mask[idx] = flow_modify_info_mask_32(width, off_be); + else + info[idx].offset = off_be; + break; + case RTE_FLOW_FIELD_GENEVE_OPT_DATA: + if ((data->offset % 32) + width > 32) { + DRV_LOG(ERR, "Geneve TLV option data is per DW."); + return; + } + modi_id = flow_geneve_opt_modi_field_get(priv, data); + if (modi_id < 0) + return; + /* Use offset inside DW. */ + off_be = 32 - ((data->offset % 32) + width); + info[idx] = (struct field_modify_info){4, 0, modi_id}; + if (mask) + mask[idx] = flow_modify_info_mask_32(width, off_be); + else + info[idx].offset = off_be; + break; case RTE_FLOW_FIELD_GTP_TEID: MLX5_ASSERT(data->offset + width <= 32); off_be = 32 - (data->offset + width); @@ -1905,8 +1964,8 @@ mlx5_flow_field_id_to_modify_info case RTE_FLOW_FIELD_MPLS: MLX5_ASSERT(data->offset + width <= 32); off_be = 32 - (data->offset + width); - info[idx] = (struct field_modify_info){4, 0, - mlx5_mpls_modi_field_get(data)}; + modi_id = mlx5_mpls_modi_field_get(data); + info[idx] = (struct field_modify_info){4, 0, modi_id}; if (mask) mask[idx] = flow_modify_info_mask_32(width, off_be); else @@ -5388,6 +5447,21 @@ flow_dv_validate_action_modify_field(struct rte_eth_dev *dev, RTE_FLOW_ERROR_TYPE_ACTION, action, "modifications of the GENEVE Network" " Identifier is not supported"); + if (dst_data->field == RTE_FLOW_FIELD_GENEVE_OPT_TYPE || + src_data->field == RTE_FLOW_FIELD_GENEVE_OPT_TYPE) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ACTION, action, + "modifications of the GENEVE option type is not supported"); + if (dst_data->field == RTE_FLOW_FIELD_GENEVE_OPT_CLASS || + src_data->field == RTE_FLOW_FIELD_GENEVE_OPT_CLASS) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ACTION, action, + "modifications of the GENEVE option class is not supported"); + if (dst_data->field == RTE_FLOW_FIELD_GENEVE_OPT_DATA || + src_data->field == RTE_FLOW_FIELD_GENEVE_OPT_DATA) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ACTION, action, + "modifications of the GENEVE option data is not supported"); if (dst_data->field == RTE_FLOW_FIELD_MPLS || src_data->field == RTE_FLOW_FIELD_MPLS) return rte_flow_error_set(error, ENOTSUP, diff --git a/drivers/net/mlx5/mlx5_flow_geneve.c b/drivers/net/mlx5/mlx5_flow_geneve.c index f3ee414d02..d5847a60e9 100644 --- a/drivers/net/mlx5/mlx5_flow_geneve.c +++ b/drivers/net/mlx5/mlx5_flow_geneve.c @@ -254,6 +254,123 @@ mlx5_geneve_tlv_options_unregister(struct mlx5_priv *priv, mng->nb_options = 0; } +/** + * Get single DW resource from given option. + * + * @param option + * Pointer to single GENEVE TLV option. + * @param offset + * Offset of DW related to option start. + * + * @return + * DW resource on success, NULL otherwise and rte_errno is set. + */ +static struct mlx5_geneve_tlv_resource * +mlx5_geneve_tlv_option_get_resource_by_offset(struct mlx5_geneve_tlv_option *option, + uint8_t offset) +{ + uint8_t i; + + for (i = 0; option->resources[i].obj != NULL; ++i) { + if (option->resources[i].offset < offset) + continue; + if (option->resources[i].offset == offset) + return &option->resources[i]; + break; + } + DRV_LOG(ERR, "The DW in offset %u wasn't configured.", offset); + rte_errno = EINVAL; + return NULL; +} + +int +mlx5_get_geneve_option_modify_field_id(const void *dr_ctx, uint8_t type, + uint16_t class, uint8_t dw_offset) +{ + uint16_t port_id; + + MLX5_ETH_FOREACH_DEV(port_id, NULL) { + struct mlx5_priv *priv; + struct mlx5_geneve_tlv_option *option; + struct mlx5_geneve_tlv_resource *resource; + + priv = rte_eth_devices[port_id].data->dev_private; + if (priv->dr_ctx != dr_ctx) + continue; + /* Find specific option inside list. */ + option = mlx5_geneve_tlv_option_get(priv, type, class); + if (option == NULL) + return -rte_errno; + /* Find specific FW object inside option resources. */ + resource = mlx5_geneve_tlv_option_get_resource_by_offset(option, + dw_offset); + if (resource == NULL) + return -rte_errno; + return resource->modify_field; + } + DRV_LOG(ERR, "DR CTX %p doesn't belong to any DPDK port.", dr_ctx); + rte_errno = EINVAL; + return -rte_errno; +} + +/** + * Get modify field ID for single DW inside configured GENEVE TLV option. + * + * @param[in] priv + * Pointer to port's private data. + * @param[in] data + * Pointer to modify field data structure. + * + * @return + * Modify field ID on success, negative errno otherwise and rte_errno is set. + */ +int +mlx5_geneve_opt_modi_field_get(struct mlx5_priv *priv, + const struct rte_flow_action_modify_data *data) +{ + uint16_t class = data->class_id; + uint8_t type = data->type; + struct mlx5_geneve_tlv_option *option; + struct mlx5_geneve_tlv_resource *resource; + uint8_t offset; + + option = mlx5_geneve_tlv_option_get(priv, type, class); + if (option == NULL) + return -rte_errno; + switch (data->field) { + case RTE_FLOW_FIELD_GENEVE_OPT_TYPE: + case RTE_FLOW_FIELD_GENEVE_OPT_CLASS: + if (!option->match_data[0].dw_mask) { + DRV_LOG(ERR, "DW0 isn't configured"); + rte_errno = EINVAL; + return -rte_errno; + } + resource = &option->resources[0]; + MLX5_ASSERT(resource->offset == 0); + break; + case RTE_FLOW_FIELD_GENEVE_OPT_DATA: + /* + * Convert offset twice: + * - First conversion from bit offset to DW offset. + * - Second conversion is to be related to data start instead + * of option start. + */ + offset = (data->offset >> 5) + 1; + resource = mlx5_geneve_tlv_option_get_resource_by_offset(option, + offset); + break; + default: + DRV_LOG(ERR, + "Field ID %u doesn't describe GENEVE option header.", + data->field); + rte_errno = EINVAL; + return -rte_errno; + } + if (resource == NULL) + return -rte_errno; + return resource->modify_field; +} + /** * Create single GENEVE TLV option sample. * diff --git a/drivers/net/mlx5/mlx5_flow_hw.c b/drivers/net/mlx5/mlx5_flow_hw.c index 22ac4e0a7c..692bbe063e 100644 --- a/drivers/net/mlx5/mlx5_flow_hw.c +++ b/drivers/net/mlx5/mlx5_flow_hw.c @@ -1254,10 +1254,12 @@ flow_hw_modify_field_compile(struct rte_eth_dev *dev, else value = rte_cpu_to_be_32(value); item.spec = &value; - } else if (conf->dst.field == RTE_FLOW_FIELD_GTP_PSC_QFI) { + } else if (conf->dst.field == RTE_FLOW_FIELD_GTP_PSC_QFI || + conf->dst.field == RTE_FLOW_FIELD_GENEVE_OPT_TYPE) { /* - * QFI is passed as an uint8_t integer, but it is accessed through - * a 2nd least significant byte of a 32-bit field in modify header command. + * Both QFI and Geneve option type are passed as an uint8_t integer, + * but it is accessed through a 2nd least significant byte of a 32-bit + * field in modify header command. */ value = *(const uint8_t *)item.spec; value = rte_cpu_to_be_32(value << 8); @@ -2825,12 +2827,14 @@ flow_hw_modify_field_construct(struct mlx5_hw_q_job *job, *value_p = rte_cpu_to_be_32(*value_p << 16); else *value_p = rte_cpu_to_be_32(*value_p); - } else if (mhdr_action->dst.field == RTE_FLOW_FIELD_GTP_PSC_QFI) { + } else if (mhdr_action->dst.field == RTE_FLOW_FIELD_GTP_PSC_QFI || + mhdr_action->dst.field == RTE_FLOW_FIELD_GENEVE_OPT_TYPE) { uint32_t tmp; /* - * QFI is passed as an uint8_t integer, but it is accessed through - * a 2nd least significant byte of a 32-bit field in modify header command. + * Both QFI and Geneve option type are passed as an uint8_t integer, + * but it is accessed through a 2nd least significant byte of a 32-bit + * field in modify header command. */ tmp = values[0]; value_p = (unaligned_uint32_t *)values; @@ -4944,6 +4948,14 @@ flow_hw_modify_field_is_used(const struct rte_flow_action_modify_field *action, return action->src.field == field || action->dst.field == field; } +static bool +flow_hw_modify_field_is_geneve_opt(enum rte_flow_field_id field) +{ + return field == RTE_FLOW_FIELD_GENEVE_OPT_TYPE || + field == RTE_FLOW_FIELD_GENEVE_OPT_CLASS || + field == RTE_FLOW_FIELD_GENEVE_OPT_DATA; +} + static int flow_hw_validate_action_modify_field(struct rte_eth_dev *dev, const struct rte_flow_action *action, @@ -4977,15 +4989,17 @@ flow_hw_validate_action_modify_field(struct rte_eth_dev *dev, ret = flow_validate_modify_field_level(&action_conf->dst, error); if (ret) return ret; - if (action_conf->dst.tag_index && - !flow_modify_field_support_tag_array(action_conf->dst.field)) - return rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ACTION, action, - "destination tag index is not supported"); - if (action_conf->dst.class_id) - return rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ACTION, action, - "destination class id is not supported"); + if (!flow_hw_modify_field_is_geneve_opt(action_conf->dst.field)) { + if (action_conf->dst.tag_index && + !flow_modify_field_support_tag_array(action_conf->dst.field)) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, action, + "destination tag index is not supported"); + if (action_conf->dst.class_id) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, action, + "destination class id is not supported"); + } if (mask_conf->dst.level != UINT8_MAX) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, action, @@ -5000,15 +5014,17 @@ flow_hw_validate_action_modify_field(struct rte_eth_dev *dev, "destination field mask and template are not equal"); if (action_conf->src.field != RTE_FLOW_FIELD_POINTER && action_conf->src.field != RTE_FLOW_FIELD_VALUE) { - if (action_conf->src.tag_index && - !flow_modify_field_support_tag_array(action_conf->src.field)) - return rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ACTION, action, - "source tag index is not supported"); - if (action_conf->src.class_id) - return rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ACTION, action, - "source class id is not supported"); + if (!flow_hw_modify_field_is_geneve_opt(action_conf->src.field)) { + if (action_conf->src.tag_index && + !flow_modify_field_support_tag_array(action_conf->src.field)) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, action, + "source tag index is not supported"); + if (action_conf->src.class_id) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, action, + "source class id is not supported"); + } if (mask_conf->src.level != UINT8_MAX) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, action, @@ -5059,6 +5075,13 @@ flow_hw_validate_action_modify_field(struct rte_eth_dev *dev, return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, action, "modifying Geneve VNI is not supported when GENEVE opt is supported"); + if (priv->tlv_options == NULL && + (flow_hw_modify_field_is_used(action_conf, RTE_FLOW_FIELD_GENEVE_OPT_TYPE) || + flow_hw_modify_field_is_used(action_conf, RTE_FLOW_FIELD_GENEVE_OPT_CLASS) || + flow_hw_modify_field_is_used(action_conf, RTE_FLOW_FIELD_GENEVE_OPT_DATA))) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, action, + "modifying Geneve TLV option is supported only after parser configuration"); /* Due to HW bug, tunnel MPLS header is read only. */ if (action_conf->dst.field == RTE_FLOW_FIELD_MPLS) return rte_flow_error_set(error, EINVAL, -- 2.25.1