@@ -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``
@@ -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
@@ -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,
@@ -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,
@@ -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.
*
@@ -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,