"specified range not supported");
}
+/*
+ * Validate ASO CT item.
+ *
+ * @param[in] dev
+ * Pointer to the rte_eth_dev structure.
+ * @param[in] item
+ * Item specification.
+ * @param[in] item_flags
+ * Pointer to bit-fields that holds the items detected until now.
+ * @param[out] error
+ * Pointer to error structure.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+flow_dv_validate_item_aso_ct(struct rte_eth_dev *dev,
+ const struct rte_flow_item *item,
+ uint64_t *item_flags,
+ struct rte_flow_error *error)
+{
+ const struct rte_flow_item_conntrack *spec = item->spec;
+ const struct rte_flow_item_conntrack *mask = item->mask;
+ RTE_SET_USED(dev);
+ uint32_t flags;
+
+ if (*item_flags & MLX5_FLOW_LAYER_ASO_CT)
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+ "Only one CT is supported");
+ if (!mask)
+ mask = &rte_flow_item_conntrack_mask;
+ flags = spec->flags & mask->flags;
+ if ((flags & RTE_FLOW_CONNTRACK_PKT_STATE_VALID) &&
+ ((flags & RTE_FLOW_CONNTRACK_PKT_STATE_INVALID) ||
+ (flags & RTE_FLOW_CONNTRACK_PKT_STATE_BAD) ||
+ (flags & RTE_FLOW_CONNTRACK_PKT_STATE_DISABLED)))
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+ "Conflict status bits");
+ /* State change also needs to be considered. */
+ *item_flags |= MLX5_FLOW_LAYER_ASO_CT;
+ return 0;
+}
+
/**
* Validate the pop VLAN action.
*
return 0;
}
+/*
+ * Validate the ASO CT action.
+ *
+ * @param[in] dev
+ * Pointer to the rte_eth_dev structure.
+ * @param[in] action_flags
+ * Holds the actions detected until now.
+ * @param[in] item_flags
+ * The items found in this flow rule.
+ * @param[in] attr
+ * Pointer to flow attributes.
+ * @param[out] error
+ * Pointer to error structure.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+flow_dv_validate_action_aso_ct(struct rte_eth_dev *dev,
+ uint64_t action_flags,
+ uint64_t item_flags,
+ const struct rte_flow_attr *attr,
+ struct rte_flow_error *error)
+{
+ RTE_SET_USED(dev);
+
+ if (attr->group == 0 && !attr->transfer)
+ return rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "Only support non-root table");
+ if (action_flags & MLX5_FLOW_FATE_ACTIONS)
+ return rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+ "CT cannot follow a fate action");
+ if ((action_flags & MLX5_FLOW_ACTION_METER) ||
+ (action_flags & MLX5_FLOW_ACTION_AGE))
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+ "Only one ASO action is supported");
+ if (action_flags & MLX5_FLOW_ACTION_ENCAP)
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+ "Encap cannot exist before CT");
+ if (!(item_flags & MLX5_FLOW_LAYER_OUTER_L4_TCP))
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "Not a outer TCP packet");
+ return 0;
+}
+
/**
* Match encap_decap resource.
*
uint32_t rw_act_num = 0;
uint64_t is_root;
const struct mlx5_flow_tunnel *tunnel;
+ enum mlx5_tof_rule_type tof_rule_type;
struct flow_grp_info grp_info = {
.external = !!external,
.transfer = !!attr->transfer,
.fdb_def_rule = !!priv->fdb_def_rule,
+ .std_tbl_fix = true,
};
const struct rte_eth_hairpin_conf *conf;
const struct rte_flow_item *rule_items = items;
if (items == NULL)
return -1;
- if (is_flow_tunnel_match_rule(dev, attr, items, actions)) {
- tunnel = flow_items_to_tunnel(items);
- action_flags |= MLX5_FLOW_ACTION_TUNNEL_MATCH |
- MLX5_FLOW_ACTION_DECAP;
- } else if (is_flow_tunnel_steer_rule(dev, attr, items, actions)) {
- tunnel = flow_actions_to_tunnel(actions);
- action_flags |= MLX5_FLOW_ACTION_TUNNEL_SET;
- } else {
- tunnel = NULL;
+ tunnel = is_tunnel_offload_active(dev) ?
+ mlx5_get_tof(items, actions, &tof_rule_type) : NULL;
+ if (tunnel) {
+ if (priv->representor)
+ return rte_flow_error_set
+ (error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL, "decap not supported for VF representor");
+ if (tof_rule_type == MLX5_TUNNEL_OFFLOAD_SET_RULE)
+ action_flags |= MLX5_FLOW_ACTION_TUNNEL_SET;
+ else if (tof_rule_type == MLX5_TUNNEL_OFFLOAD_MATCH_RULE)
+ action_flags |= MLX5_FLOW_ACTION_TUNNEL_MATCH |
+ MLX5_FLOW_ACTION_DECAP;
+ grp_info.std_tbl_fix = tunnel_use_standard_attr_group_translate
+ (dev, attr, tunnel, tof_rule_type);
}
- if (tunnel && priv->representor)
- return rte_flow_error_set(error, ENOTSUP,
- RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
- "decap not supported "
- "for VF representor");
- grp_info.std_tbl_fix = tunnel_use_standard_attr_group_translate
- (dev, tunnel, attr, items, actions);
ret = flow_dv_validate_attributes(dev, tunnel, attr, &grp_info, error);
if (ret < 0)
return ret;
RTE_FLOW_ERROR_TYPE_ITEM,
NULL, "item not supported");
switch (type) {
- case MLX5_RTE_FLOW_ITEM_TYPE_TUNNEL:
- if (items[0].type != (typeof(items[0].type))
- MLX5_RTE_FLOW_ITEM_TYPE_TUNNEL)
- return rte_flow_error_set
- (error, EINVAL,
- RTE_FLOW_ERROR_TYPE_ITEM,
- NULL, "MLX5 private items "
- "must be the first");
- break;
case RTE_FLOW_ITEM_TYPE_VOID:
break;
case RTE_FLOW_ITEM_TYPE_PORT_ID:
return ret;
last_item = MLX5_FLOW_ITEM_INTEGRITY;
break;
+ case RTE_FLOW_ITEM_TYPE_CONNTRACK:
+ ret = flow_dv_validate_item_aso_ct(dev, items,
+ &item_flags, error);
+ if (ret < 0)
+ return ret;
+ break;
+ case MLX5_RTE_FLOW_ITEM_TYPE_TUNNEL:
+ /* tunnel offload item was processed before
+ * list it here as a supported type
+ */
+ break;
default:
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ITEM,
action_flags |= MLX5_FLOW_ACTION_SAMPLE;
++actions_n;
break;
- case MLX5_RTE_FLOW_ACTION_TYPE_TUNNEL_SET:
- if (actions[0].type != (typeof(actions[0].type))
- MLX5_RTE_FLOW_ACTION_TYPE_TUNNEL_SET)
- return rte_flow_error_set
- (error, EINVAL,
- RTE_FLOW_ERROR_TYPE_ACTION,
- NULL, "MLX5 private action "
- "must be the first");
-
- action_flags |= MLX5_FLOW_ACTION_TUNNEL_SET;
- break;
case RTE_FLOW_ACTION_TYPE_MODIFY_FIELD:
ret = flow_dv_validate_action_modify_field(dev,
action_flags,
action_flags |= MLX5_FLOW_ACTION_MODIFY_FIELD;
rw_act_num += ret;
break;
+ case RTE_FLOW_ACTION_TYPE_CONNTRACK:
+ ret = flow_dv_validate_action_aso_ct(dev, action_flags,
+ item_flags, attr,
+ error);
+ if (ret < 0)
+ return ret;
+ action_flags |= MLX5_FLOW_ACTION_CT;
+ break;
+ case MLX5_RTE_FLOW_ACTION_TYPE_TUNNEL_SET:
+ /* tunnel offload action was processed before
+ * list it here as a supported type
+ */
+ break;
default:
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION,
}
/**
- * Create a age action using ASO mechanism.
+ * Initialize flow ASO age parameters.
*
* @param[in] dev
* Pointer to rte_eth_dev structure.
- * @param[in] age
- * Pointer to the aging action configuration.
- * @param[out] error
- * Pointer to the error structure.
+ * @param[in] age_idx
+ * Index of ASO age action.
+ * @param[in] context
+ * Pointer to flow counter age context.
+ * @param[in] timeout
+ * Aging timeout in seconds.
*
- * @return
- * Index to flow counter on success, 0 otherwise.
*/
-static uint32_t
-flow_dv_translate_create_aso_age(struct rte_eth_dev *dev,
- const struct rte_flow_action_age *age,
- struct rte_flow_error *error)
+static void
+flow_dv_aso_age_params_init(struct rte_eth_dev *dev,
+ uint32_t age_idx,
+ void *context,
+ uint32_t timeout)
{
- uint32_t age_idx = 0;
struct mlx5_aso_age_action *aso_age;
- age_idx = flow_dv_aso_age_alloc(dev, error);
- if (!age_idx)
- return 0;
aso_age = flow_aso_age_get_by_idx(dev, age_idx);
- aso_age->age_params.context = age->context;
- aso_age->age_params.timeout = age->timeout;
+ MLX5_ASSERT(aso_age);
+ aso_age->age_params.context = context;
+ aso_age->age_params.timeout = timeout;
aso_age->age_params.port_id = dev->data->port_id;
__atomic_store_n(&aso_age->age_params.sec_since_last_hit, 0,
__ATOMIC_RELAXED);
__atomic_store_n(&aso_age->age_params.state, AGE_CANDIDATE,
__ATOMIC_RELAXED);
- return age_idx;
}
static void
}
/*
- * Release an ASO CT action.
+ * Release an ASO CT action by its own device.
*
* @param[in] dev
* Pointer to the Ethernet device structure.
* 0 when CT action was removed, otherwise the number of references.
*/
static inline int
-flow_dv_aso_ct_release(struct rte_eth_dev *dev, uint32_t idx)
+flow_dv_aso_ct_dev_release(struct rte_eth_dev *dev, uint32_t idx)
{
struct mlx5_priv *priv = dev->data->dev_private;
struct mlx5_aso_ct_pools_mng *mng = priv->sh->ct_mng;
uint32_t ret;
- struct mlx5_aso_ct_action *ct = flow_aso_ct_get_by_idx(dev, idx);
+ struct mlx5_aso_ct_action *ct = flow_aso_ct_get_by_dev_idx(dev, idx);
enum mlx5_aso_ct_state state =
__atomic_load_n(&ct->state, __ATOMIC_RELAXED);
LIST_INSERT_HEAD(&mng->free_cts, ct, next);
rte_spinlock_unlock(&mng->ct_sl);
}
- return ret;
+ return (int)ret;
+}
+
+static inline int
+flow_dv_aso_ct_release(struct rte_eth_dev *dev, uint32_t own_idx)
+{
+ uint16_t owner = (uint16_t)MLX5_INDIRECT_ACT_CT_GET_OWNER(own_idx);
+ uint32_t idx = MLX5_INDIRECT_ACT_CT_GET_IDX(own_idx);
+ struct rte_eth_dev *owndev = &rte_eth_devices[owner];
+ RTE_SET_USED(dev);
+
+ MLX5_ASSERT(owner < RTE_MAX_ETHPORTS);
+ if (dev->data->dev_started != 1)
+ return -1;
+ return flow_dv_aso_ct_dev_release(owndev, idx);
}
/*
RTE_SET_USED(reg_c);
#endif
if (!ct->dr_action_orig) {
- flow_dv_aso_ct_release(dev, ct_idx);
+ flow_dv_aso_ct_dev_release(dev, ct_idx);
rte_flow_error_set(error, rte_errno,
RTE_FLOW_ERROR_TYPE_ACTION, NULL,
"failed to create ASO CT action");
reg_c - REG_C_0);
#endif
if (!ct->dr_action_rply) {
- flow_dv_aso_ct_release(dev, ct_idx);
+ flow_dv_aso_ct_dev_release(dev, ct_idx);
rte_flow_error_set(error, rte_errno,
RTE_FLOW_ERROR_TYPE_ACTION, NULL,
"failed to create ASO CT action");
return rte_flow_error_set(error, rte_errno,
RTE_FLOW_ERROR_TYPE_ACTION, NULL,
"Failed to allocate CT object");
- ct = flow_aso_ct_get_by_idx(dev, idx);
+ ct = flow_aso_ct_get_by_dev_idx(dev, idx);
if (mlx5_aso_ct_update_by_wqe(sh, ct, pro))
return rte_flow_error_set(error, EBUSY,
RTE_FLOW_ERROR_TYPE_ACTION, NULL,
"Failed to update CT");
ct->is_original = !!pro->is_original_dir;
+ ct->peer = pro->peer_port;
return idx;
}
int tmp_actions_n = 0;
uint32_t table;
int ret = 0;
- const struct mlx5_flow_tunnel *tunnel;
+ const struct mlx5_flow_tunnel *tunnel = NULL;
struct flow_grp_info grp_info = {
.external = !!dev_flow->external,
.transfer = !!attr->transfer,
.fdb_def_rule = !!priv->fdb_def_rule,
.skip_scale = dev_flow->skip_scale &
(1 << MLX5_SCALE_FLOW_GROUP_BIT),
+ .std_tbl_fix = true,
};
const struct rte_flow_item *head_item = items;
MLX5DV_FLOW_TABLE_TYPE_NIC_RX;
/* update normal path action resource into last index of array */
sample_act = &mdest_res.sample_act[MLX5_MAX_DEST_NUM - 1];
- tunnel = is_flow_tunnel_match_rule(dev, attr, items, actions) ?
- flow_items_to_tunnel(items) :
- is_flow_tunnel_steer_rule(dev, attr, items, actions) ?
- flow_actions_to_tunnel(actions) :
- dev_flow->tunnel ? dev_flow->tunnel : NULL;
+ if (is_tunnel_offload_active(dev)) {
+ if (dev_flow->tunnel) {
+ RTE_VERIFY(dev_flow->tof_type ==
+ MLX5_TUNNEL_OFFLOAD_MISS_RULE);
+ tunnel = dev_flow->tunnel;
+ } else {
+ tunnel = mlx5_get_tof(items, actions,
+ &dev_flow->tof_type);
+ dev_flow->tunnel = tunnel;
+ }
+ grp_info.std_tbl_fix = tunnel_use_standard_attr_group_translate
+ (dev, attr, tunnel, dev_flow->tof_type);
+ }
mhdr_res->ft_type = attr->egress ? MLX5DV_FLOW_TABLE_TYPE_NIC_TX :
MLX5DV_FLOW_TABLE_TYPE_NIC_RX;
- grp_info.std_tbl_fix = tunnel_use_standard_attr_group_translate
- (dev, tunnel, attr, items, actions);
ret = mlx5_flow_group_to_table(dev, tunnel, attr->group, &table,
&grp_info, error);
if (ret)
mhdr_res->ft_type = MLX5DV_FLOW_TABLE_TYPE_FDB;
/* number of actions must be set to 0 in case of dirty stack. */
mhdr_res->actions_num = 0;
- if (is_flow_tunnel_match_rule(dev, attr, items, actions)) {
+ if (is_flow_tunnel_match_rule(dev_flow->tof_type)) {
/*
* do not add decap action if match rule drops packet
* HW rejects rules with decap & drop
int action_type = actions->type;
const struct rte_flow_action *found_action = NULL;
uint32_t jump_group = 0;
- uint32_t ct_idx;
+ uint32_t owner_idx;
struct mlx5_aso_ct_action *ct;
if (!mlx5_flow_os_action_supported(action_type))
action_flags |= MLX5_FLOW_ACTION_MODIFY_FIELD;
break;
case RTE_FLOW_ACTION_TYPE_CONNTRACK:
- ct_idx = (uint32_t)(uintptr_t)action->conf;
- ct = flow_aso_ct_get_by_idx(dev, ct_idx);
+ owner_idx = (uint32_t)(uintptr_t)action->conf;
+ ct = flow_aso_ct_get_by_idx(dev, owner_idx);
+ if (!ct)
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION,
+ NULL,
+ "Failed to get CT object.");
if (mlx5_aso_ct_available(priv->sh, ct))
return rte_flow_error_set(error, rte_errno,
RTE_FLOW_ERROR_TYPE_ACTION,
dev_flow->dv.actions[actions_n] =
ct->dr_action_rply;
flow->indirect_type = MLX5_INDIRECT_ACTION_TYPE_CT;
- flow->ct = ct_idx;
+ flow->ct = owner_idx;
__atomic_fetch_add(&ct->refcnt, 1, __ATOMIC_RELAXED);
actions_n++;
action_flags |= MLX5_FLOW_ACTION_CT;
if ((non_shared_age &&
count && !count->shared) ||
!(priv->sh->flow_hit_aso_en &&
- attr->group)) {
+ (attr->group || attr->transfer))) {
/* Creates age by counters. */
cnt_act = flow_dv_prepare_counter
(dev, dev_flow,
break;
}
if (!flow->age && non_shared_age) {
- flow->age =
- flow_dv_translate_create_aso_age
- (dev,
- non_shared_age,
- error);
+ flow->age = flow_dv_aso_age_alloc
+ (dev, error);
if (!flow->age)
- return rte_flow_error_set
- (error, rte_errno,
- RTE_FLOW_ERROR_TYPE_ACTION,
- NULL,
- "can't create ASO age action");
+ return -rte_errno;
+ flow_dv_aso_age_params_init
+ (dev, flow->age,
+ non_shared_age->context ?
+ non_shared_age->context :
+ (void *)(uintptr_t)
+ (dev_flow->flow_idx),
+ non_shared_age->timeout);
}
age_act = flow_aso_age_get_by_idx(dev,
flow->age);
const struct rte_flow_action *action,
struct rte_flow_error *err)
{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ uint32_t age_idx = 0;
uint32_t idx = 0;
uint32_t ret = 0;
MLX5_INDIRECT_ACTION_TYPE_OFFSET) | ret;
break;
case RTE_FLOW_ACTION_TYPE_AGE:
- ret = flow_dv_translate_create_aso_age(dev, action->conf, err);
- idx = (MLX5_INDIRECT_ACTION_TYPE_AGE <<
- MLX5_INDIRECT_ACTION_TYPE_OFFSET) | ret;
- if (ret) {
- struct mlx5_aso_age_action *aso_age =
- flow_aso_age_get_by_idx(dev, ret);
-
- if (!aso_age->age_params.context)
- aso_age->age_params.context =
- (void *)(uintptr_t)idx;
+ age_idx = flow_dv_aso_age_alloc(dev, err);
+ if (!age_idx) {
+ ret = -rte_errno;
+ break;
}
+ idx = (MLX5_INDIRECT_ACTION_TYPE_AGE <<
+ MLX5_INDIRECT_ACTION_TYPE_OFFSET) | age_idx;
+ flow_dv_aso_age_params_init(dev, age_idx,
+ ((const struct rte_flow_action_age *)
+ action->conf)->context ?
+ ((const struct rte_flow_action_age *)
+ action->conf)->context :
+ (void *)(uintptr_t)idx,
+ ((const struct rte_flow_action_age *)
+ action->conf)->timeout);
+ ret = age_idx;
break;
case RTE_FLOW_ACTION_TYPE_COUNT:
ret = flow_dv_translate_create_counter(dev, NULL, NULL, NULL);
case RTE_FLOW_ACTION_TYPE_CONNTRACK:
ret = flow_dv_translate_create_conntrack(dev, action->conf,
err);
- idx = (MLX5_INDIRECT_ACTION_TYPE_CT <<
- MLX5_INDIRECT_ACTION_TYPE_OFFSET) | ret;
+ idx = MLX5_INDIRECT_ACT_CT_GEN_IDX(PORT_ID(priv), ret);
break;
default:
rte_flow_error_set(err, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION,
return 0;
case MLX5_INDIRECT_ACTION_TYPE_CT:
ret = flow_dv_aso_ct_release(dev, idx);
- if (ret)
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
DRV_LOG(DEBUG, "Connection tracking object %u still "
"has references %d.", idx, ret);
return 0;
struct mlx5_aso_ct_action *ct;
const struct rte_flow_action_conntrack *new_prf;
int ret = 0;
+ uint16_t owner = (uint16_t)MLX5_INDIRECT_ACT_CT_GET_OWNER(idx);
+ uint32_t dev_idx;
- ct = flow_aso_ct_get_by_idx(dev, idx);
+ if (PORT_ID(priv) != owner)
+ return rte_flow_error_set(error, EACCES,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "CT object owned by another port");
+ dev_idx = MLX5_INDIRECT_ACT_CT_GET_IDX(idx);
+ ct = flow_aso_ct_get_by_dev_idx(dev, dev_idx);
if (!ct->refcnt)
return rte_flow_error_set(error, ENOMEM,
RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
if (update->direction)
ct->is_original = !!new_prf->is_original_dir;
if (update->state) {
+ /* Only validate the profile when it needs to be updated. */
+ ret = mlx5_validate_action_ct(dev, new_prf, error);
+ if (ret)
+ return ret;
ret = mlx5_aso_ct_update_by_wqe(priv->sh, ct, new_prf);
if (ret)
return rte_flow_error_set(error, EIO,
uint32_t idx = act_idx & ((1u << MLX5_INDIRECT_ACTION_TYPE_OFFSET) - 1);
struct mlx5_priv *priv = dev->data->dev_private;
struct mlx5_aso_ct_action *ct;
+ uint16_t owner;
+ uint32_t dev_idx;
switch (type) {
case MLX5_INDIRECT_ACTION_TYPE_AGE:
case MLX5_INDIRECT_ACTION_TYPE_COUNT:
return flow_dv_query_count(dev, idx, data, error);
case MLX5_INDIRECT_ACTION_TYPE_CT:
- ct = flow_aso_ct_get_by_idx(dev, idx);
+ owner = (uint16_t)MLX5_INDIRECT_ACT_CT_GET_OWNER(idx);
+ if (owner != PORT_ID(priv))
+ return rte_flow_error_set(error, EACCES,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "CT object owned by another port");
+ dev_idx = MLX5_INDIRECT_ACT_CT_GET_IDX(idx);
+ ct = flow_aso_ct_get_by_dev_idx(dev, dev_idx);
+ MLX5_ASSERT(ct);
if (!ct->refcnt)
return rte_flow_error_set(error, EFAULT,
RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
NULL,
"Mix shared and indirect counter is not supported");
return flow_dv_validate_action_count(dev, true, 0, err);
+ case RTE_FLOW_ACTION_TYPE_CONNTRACK:
+ if (!priv->sh->ct_aso_en)
+ return rte_flow_error_set(err, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "ASO CT is not supported");
+ return mlx5_validate_action_ct(dev, action->conf, err);
default:
return rte_flow_error_set(err, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION,