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

Reply via email to