{0, 0, 0},
};
+static const struct rte_flow_item *
+mlx5_flow_find_tunnel_item(const struct rte_flow_item *item)
+{
+ for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
+ switch (item->type) {
+ default:
+ break;
+ case RTE_FLOW_ITEM_TYPE_VXLAN:
+ case RTE_FLOW_ITEM_TYPE_VXLAN_GPE:
+ case RTE_FLOW_ITEM_TYPE_GRE:
+ case RTE_FLOW_ITEM_TYPE_MPLS:
+ case RTE_FLOW_ITEM_TYPE_NVGRE:
+ case RTE_FLOW_ITEM_TYPE_GENEVE:
+ return item;
+ case RTE_FLOW_ITEM_TYPE_IPV4:
+ case RTE_FLOW_ITEM_TYPE_IPV6:
+ if (item[1].type == RTE_FLOW_ITEM_TYPE_IPV4 ||
+ item[1].type == RTE_FLOW_ITEM_TYPE_IPV6)
+ return item;
+ break;
+ }
+ }
+ return NULL;
+}
+
static void
mlx5_flow_tunnel_ip_check(const struct rte_flow_item *item __rte_unused,
uint8_t next_protocol, uint64_t *item_flags,
case RTE_FLOW_FIELD_TCP_ACK_NUM:
return 32;
case RTE_FLOW_FIELD_TCP_FLAGS:
- return 6;
+ return 9;
case RTE_FLOW_FIELD_UDP_PORT_SRC:
case RTE_FLOW_FIELD_UDP_PORT_DST:
return 16;
(32 - width));
break;
case RTE_FLOW_FIELD_TCP_FLAGS:
- info[idx] = (struct field_modify_info){1, 0,
+ info[idx] = (struct field_modify_info){2, 0,
MLX5_MODI_OUT_TCP_FLAGS};
if (mask)
- mask[idx] = 0x3f >> (6 - width);
+ mask[idx] = rte_cpu_to_be_16(0x1ff >> (9 - width));
break;
case RTE_FLOW_FIELD_UDP_PORT_SRC:
info[idx] = (struct field_modify_info){2, 0,
"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.
*
return ret;
}
+static uint16_t
+mlx5_flow_locate_proto_l3(const struct rte_flow_item **head,
+ const struct rte_flow_item *end)
+{
+ const struct rte_flow_item *item = *head;
+ uint16_t l3_protocol;
+
+ for (; item != end; item++) {
+ switch (item->type) {
+ default:
+ break;
+ case RTE_FLOW_ITEM_TYPE_IPV4:
+ l3_protocol = RTE_ETHER_TYPE_IPV4;
+ goto l3_ok;
+ case RTE_FLOW_ITEM_TYPE_IPV6:
+ l3_protocol = RTE_ETHER_TYPE_IPV6;
+ goto l3_ok;
+ case RTE_FLOW_ITEM_TYPE_ETH:
+ if (item->mask && item->spec) {
+ MLX5_ETHER_TYPE_FROM_HEADER(rte_flow_item_eth,
+ type, item,
+ l3_protocol);
+ if (l3_protocol == RTE_ETHER_TYPE_IPV4 ||
+ l3_protocol == RTE_ETHER_TYPE_IPV6)
+ goto l3_ok;
+ }
+ break;
+ case RTE_FLOW_ITEM_TYPE_VLAN:
+ if (item->mask && item->spec) {
+ MLX5_ETHER_TYPE_FROM_HEADER(rte_flow_item_vlan,
+ inner_type, item,
+ l3_protocol);
+ if (l3_protocol == RTE_ETHER_TYPE_IPV4 ||
+ l3_protocol == RTE_ETHER_TYPE_IPV6)
+ goto l3_ok;
+ }
+ break;
+ }
+ }
+ return 0;
+l3_ok:
+ *head = item;
+ return l3_protocol;
+}
+
+static uint8_t
+mlx5_flow_locate_proto_l4(const struct rte_flow_item **head,
+ const struct rte_flow_item *end)
+{
+ const struct rte_flow_item *item = *head;
+ uint8_t l4_protocol;
+
+ for (; item != end; item++) {
+ switch (item->type) {
+ default:
+ break;
+ case RTE_FLOW_ITEM_TYPE_TCP:
+ l4_protocol = IPPROTO_TCP;
+ goto l4_ok;
+ case RTE_FLOW_ITEM_TYPE_UDP:
+ l4_protocol = IPPROTO_UDP;
+ goto l4_ok;
+ case RTE_FLOW_ITEM_TYPE_IPV4:
+ if (item->mask && item->spec) {
+ const struct rte_flow_item_ipv4 *mask, *spec;
+
+ mask = (typeof(mask))item->mask;
+ spec = (typeof(spec))item->spec;
+ l4_protocol = mask->hdr.next_proto_id &
+ spec->hdr.next_proto_id;
+ if (l4_protocol == IPPROTO_TCP ||
+ l4_protocol == IPPROTO_UDP)
+ goto l4_ok;
+ }
+ break;
+ case RTE_FLOW_ITEM_TYPE_IPV6:
+ if (item->mask && item->spec) {
+ const struct rte_flow_item_ipv6 *mask, *spec;
+ mask = (typeof(mask))item->mask;
+ spec = (typeof(spec))item->spec;
+ l4_protocol = mask->hdr.proto & spec->hdr.proto;
+ if (l4_protocol == IPPROTO_TCP ||
+ l4_protocol == IPPROTO_UDP)
+ goto l4_ok;
+ }
+ break;
+ }
+ }
+ return 0;
+l4_ok:
+ *head = item;
+ return l4_protocol;
+}
+
+static int
+flow_dv_validate_item_integrity(struct rte_eth_dev *dev,
+ const struct rte_flow_item *rule_items,
+ const struct rte_flow_item *integrity_item,
+ struct rte_flow_error *error)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ const struct rte_flow_item *tunnel_item, *end_item, *item = rule_items;
+ const struct rte_flow_item_integrity *mask = (typeof(mask))
+ integrity_item->mask;
+ const struct rte_flow_item_integrity *spec = (typeof(spec))
+ integrity_item->spec;
+ uint32_t protocol;
+
+ if (!priv->config.hca_attr.pkt_integrity_match)
+ return rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ integrity_item,
+ "packet integrity integrity_item not supported");
+ if (!mask)
+ mask = &rte_flow_item_integrity_mask;
+ if (!mlx5_validate_integrity_item(mask))
+ return rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ integrity_item,
+ "unsupported integrity filter");
+ tunnel_item = mlx5_flow_find_tunnel_item(rule_items);
+ if (spec->level > 1) {
+ if (!tunnel_item)
+ return rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ integrity_item,
+ "missing tunnel item");
+ item = tunnel_item;
+ end_item = mlx5_find_end_item(tunnel_item);
+ } else {
+ end_item = tunnel_item ? tunnel_item :
+ mlx5_find_end_item(integrity_item);
+ }
+ if (mask->l3_ok || mask->ipv4_csum_ok) {
+ protocol = mlx5_flow_locate_proto_l3(&item, end_item);
+ if (!protocol)
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ integrity_item,
+ "missing L3 protocol");
+ }
+ if (mask->l4_ok || mask->l4_csum_ok) {
+ protocol = mlx5_flow_locate_proto_l4(&item, end_item);
+ if (!protocol)
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ integrity_item,
+ "missing L4 protocol");
+ }
+ return 0;
+}
+
/**
* Internal validation function. For validating both actions and items.
*
.fdb_def_rule = !!priv->fdb_def_rule,
};
const struct rte_eth_hairpin_conf *conf;
+ const struct rte_flow_item *rule_items = items;
bool def_policy = false;
if (items == NULL)
return ret;
last_item = MLX5_FLOW_LAYER_ECPRI;
break;
+ case RTE_FLOW_ITEM_TYPE_INTEGRITY:
+ if (item_flags & MLX5_FLOW_ITEM_INTEGRITY)
+ return rte_flow_error_set
+ (error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ NULL, "multiple integrity items not supported");
+ ret = flow_dv_validate_item_integrity(dev, rule_items,
+ items, error);
+ if (ret < 0)
+ 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;
default:
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ITEM,
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;
default:
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION,
}
}
+/*
+ * Add connection tracking status item to matcher
+ *
+ * @param[in] dev
+ * The devich to configure through.
+ * @param[in, out] matcher
+ * Flow matcher.
+ * @param[in, out] key
+ * Flow matcher value.
+ * @param[in] item
+ * Flow pattern to translate.
+ */
+static void
+flow_dv_translate_item_aso_ct(struct rte_eth_dev *dev,
+ void *matcher, void *key,
+ const struct rte_flow_item *item)
+{
+ uint32_t reg_value = 0;
+ int reg_id;
+ /* 8LSB 0b 11/0000/11, middle 4 bits are reserved. */
+ uint32_t reg_mask = 0;
+ const struct rte_flow_item_conntrack *spec = item->spec;
+ const struct rte_flow_item_conntrack *mask = item->mask;
+ uint32_t flags;
+ struct rte_flow_error error;
+
+ if (!mask)
+ mask = &rte_flow_item_conntrack_mask;
+ if (!spec || !mask->flags)
+ return;
+ flags = spec->flags & mask->flags;
+ /* The conflict should be checked in the validation. */
+ if (flags & RTE_FLOW_CONNTRACK_PKT_STATE_VALID)
+ reg_value |= MLX5_CT_SYNDROME_VALID;
+ if (flags & RTE_FLOW_CONNTRACK_PKT_STATE_CHANGED)
+ reg_value |= MLX5_CT_SYNDROME_STATE_CHANGE;
+ if (flags & RTE_FLOW_CONNTRACK_PKT_STATE_INVALID)
+ reg_value |= MLX5_CT_SYNDROME_INVALID;
+ if (flags & RTE_FLOW_CONNTRACK_PKT_STATE_DISABLED)
+ reg_value |= MLX5_CT_SYNDROME_TRAP;
+ if (flags & RTE_FLOW_CONNTRACK_PKT_STATE_BAD)
+ reg_value |= MLX5_CT_SYNDROME_BAD_PACKET;
+ if (mask->flags & (RTE_FLOW_CONNTRACK_PKT_STATE_VALID |
+ RTE_FLOW_CONNTRACK_PKT_STATE_INVALID |
+ RTE_FLOW_CONNTRACK_PKT_STATE_DISABLED))
+ reg_mask |= 0xc0;
+ if (mask->flags & RTE_FLOW_CONNTRACK_PKT_STATE_CHANGED)
+ reg_mask |= MLX5_CT_SYNDROME_STATE_CHANGE;
+ if (mask->flags & RTE_FLOW_CONNTRACK_PKT_STATE_BAD)
+ reg_mask |= MLX5_CT_SYNDROME_BAD_PACKET;
+ /* The REG_C_x value could be saved during startup. */
+ reg_id = mlx5_flow_get_reg_id(dev, MLX5_ASO_CONNTRACK, 0, &error);
+ if (reg_id == REG_NON)
+ return;
+ flow_dv_match_meta_reg(matcher, key, (enum modify_reg)reg_id,
+ reg_value, reg_mask);
+}
+
static uint32_t matcher_zero[MLX5_ST_SZ_DW(fte_match_param)] = { 0 };
#define HEADER_IS_ZERO(match_criteria, headers) \
return age_idx;
}
+static void
+flow_dv_translate_integrity_l4(const struct rte_flow_item_integrity *mask,
+ const struct rte_flow_item_integrity *value,
+ void *headers_m, void *headers_v)
+{
+ if (mask->l4_ok) {
+ /* application l4_ok filter aggregates all hardware l4 filters
+ * therefore hw l4_checksum_ok must be implicitly added here.
+ */
+ struct rte_flow_item_integrity local_item;
+
+ local_item.l4_csum_ok = 1;
+ MLX5_SET(fte_match_set_lyr_2_4, headers_m, l4_checksum_ok,
+ local_item.l4_csum_ok);
+ if (value->l4_ok) {
+ /* application l4_ok = 1 matches sets both hw flags
+ * l4_ok and l4_checksum_ok flags to 1.
+ */
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v,
+ l4_checksum_ok, local_item.l4_csum_ok);
+ MLX5_SET(fte_match_set_lyr_2_4, headers_m, l4_ok,
+ mask->l4_ok);
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v, l4_ok,
+ value->l4_ok);
+ } else {
+ /* application l4_ok = 0 matches on hw flag
+ * l4_checksum_ok = 0 only.
+ */
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v,
+ l4_checksum_ok, 0);
+ }
+ } else if (mask->l4_csum_ok) {
+ MLX5_SET(fte_match_set_lyr_2_4, headers_m, l4_checksum_ok,
+ mask->l4_csum_ok);
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v, ipv4_checksum_ok,
+ value->l4_csum_ok);
+ }
+}
+
+static void
+flow_dv_translate_integrity_l3(const struct rte_flow_item_integrity *mask,
+ const struct rte_flow_item_integrity *value,
+ void *headers_m, void *headers_v,
+ bool is_ipv4)
+{
+ if (mask->l3_ok) {
+ /* application l3_ok filter aggregates all hardware l3 filters
+ * therefore hw ipv4_checksum_ok must be implicitly added here.
+ */
+ struct rte_flow_item_integrity local_item;
+
+ local_item.ipv4_csum_ok = !!is_ipv4;
+ MLX5_SET(fte_match_set_lyr_2_4, headers_m, ipv4_checksum_ok,
+ local_item.ipv4_csum_ok);
+ if (value->l3_ok) {
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v,
+ ipv4_checksum_ok, local_item.ipv4_csum_ok);
+ MLX5_SET(fte_match_set_lyr_2_4, headers_m, l3_ok,
+ mask->l3_ok);
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v, l3_ok,
+ value->l3_ok);
+ } else {
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v,
+ ipv4_checksum_ok, 0);
+ }
+ } else if (mask->ipv4_csum_ok) {
+ MLX5_SET(fte_match_set_lyr_2_4, headers_m, ipv4_checksum_ok,
+ mask->ipv4_csum_ok);
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v, ipv4_checksum_ok,
+ value->ipv4_csum_ok);
+ }
+}
+
+static void
+flow_dv_translate_item_integrity(void *matcher, void *key,
+ const struct rte_flow_item *head_item,
+ const struct rte_flow_item *integrity_item)
+{
+ const struct rte_flow_item_integrity *mask = integrity_item->mask;
+ const struct rte_flow_item_integrity *value = integrity_item->spec;
+ const struct rte_flow_item *tunnel_item, *end_item, *item;
+ void *headers_m;
+ void *headers_v;
+ uint32_t l3_protocol;
+
+ if (!value)
+ return;
+ if (!mask)
+ mask = &rte_flow_item_integrity_mask;
+ if (value->level > 1) {
+ headers_m = MLX5_ADDR_OF(fte_match_param, matcher,
+ inner_headers);
+ headers_v = MLX5_ADDR_OF(fte_match_param, key, inner_headers);
+ } else {
+ headers_m = MLX5_ADDR_OF(fte_match_param, matcher,
+ outer_headers);
+ headers_v = MLX5_ADDR_OF(fte_match_param, key, outer_headers);
+ }
+ tunnel_item = mlx5_flow_find_tunnel_item(head_item);
+ if (value->level > 1) {
+ /* tunnel item was verified during the item validation */
+ item = tunnel_item;
+ end_item = mlx5_find_end_item(tunnel_item);
+ } else {
+ item = head_item;
+ end_item = tunnel_item ? tunnel_item :
+ mlx5_find_end_item(integrity_item);
+ }
+ l3_protocol = mask->l3_ok ?
+ mlx5_flow_locate_proto_l3(&item, end_item) : 0;
+ flow_dv_translate_integrity_l3(mask, value, headers_m, headers_v,
+ l3_protocol == RTE_ETHER_TYPE_IPV4);
+ flow_dv_translate_integrity_l4(mask, value, headers_m, headers_v);
+}
+
/**
* Prepares DV flow counter with aging configuration.
* Gets it by index when exists, creates a new one when doesn't.
return flow_dv_counter_get_by_idx(dev, flow->counter, NULL);
}
+/*
+ * Release an ASO CT action by its own device.
+ *
+ * @param[in] dev
+ * Pointer to the Ethernet device structure.
+ * @param[in] idx
+ * Index of ASO CT action to release.
+ *
+ * @return
+ * 0 when CT action was removed, otherwise the number of references.
+ */
+static inline int
+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_dev_idx(dev, idx);
+ enum mlx5_aso_ct_state state =
+ __atomic_load_n(&ct->state, __ATOMIC_RELAXED);
+
+ /* Cannot release when CT is in the ASO SQ. */
+ if (state == ASO_CONNTRACK_WAIT || state == ASO_CONNTRACK_QUERY)
+ return -1;
+ ret = __atomic_sub_fetch(&ct->refcnt, 1, __ATOMIC_RELAXED);
+ if (!ret) {
+ if (ct->dr_action_orig) {
+#ifdef HAVE_MLX5_DR_ACTION_ASO_CT
+ claim_zero(mlx5_glue->destroy_flow_action
+ (ct->dr_action_orig));
+#endif
+ ct->dr_action_orig = NULL;
+ }
+ if (ct->dr_action_rply) {
+#ifdef HAVE_MLX5_DR_ACTION_ASO_CT
+ claim_zero(mlx5_glue->destroy_flow_action
+ (ct->dr_action_rply));
+#endif
+ ct->dr_action_rply = NULL;
+ }
+ /* Clear the state to free, no need in 1st allocation. */
+ MLX5_ASO_CT_UPDATE_STATE(ct, ASO_CONNTRACK_FREE);
+ rte_spinlock_lock(&mng->ct_sl);
+ LIST_INSERT_HEAD(&mng->free_cts, ct, next);
+ rte_spinlock_unlock(&mng->ct_sl);
+ }
+ 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);
+}
+
+/*
+ * Resize the ASO CT pools array by 64 pools.
+ *
+ * @param[in] dev
+ * Pointer to the Ethernet device structure.
+ *
+ * @return
+ * 0 on success, otherwise negative errno value and rte_errno is set.
+ */
+static int
+flow_dv_aso_ct_pools_resize(struct rte_eth_dev *dev)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_aso_ct_pools_mng *mng = priv->sh->ct_mng;
+ void *old_pools = mng->pools;
+ /* Magic number now, need a macro. */
+ uint32_t resize = mng->n + 64;
+ uint32_t mem_size = sizeof(struct mlx5_aso_ct_pool *) * resize;
+ void *pools = mlx5_malloc(MLX5_MEM_ZERO, mem_size, 0, SOCKET_ID_ANY);
+
+ if (!pools) {
+ rte_errno = ENOMEM;
+ return -rte_errno;
+ }
+ rte_rwlock_write_lock(&mng->resize_rwl);
+ /* ASO SQ/QP was already initialized in the startup. */
+ if (old_pools) {
+ /* Realloc could be an alternative choice. */
+ rte_memcpy(pools, old_pools,
+ mng->n * sizeof(struct mlx5_aso_ct_pool *));
+ mlx5_free(old_pools);
+ }
+ mng->n = resize;
+ mng->pools = pools;
+ rte_rwlock_write_unlock(&mng->resize_rwl);
+ return 0;
+}
+
+/*
+ * Create and initialize a new ASO CT pool.
+ *
+ * @param[in] dev
+ * Pointer to the Ethernet device structure.
+ * @param[out] ct_free
+ * Where to put the pointer of a new CT action.
+ *
+ * @return
+ * The CT actions pool pointer and @p ct_free is set on success,
+ * NULL otherwise and rte_errno is set.
+ */
+static struct mlx5_aso_ct_pool *
+flow_dv_ct_pool_create(struct rte_eth_dev *dev,
+ struct mlx5_aso_ct_action **ct_free)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_aso_ct_pools_mng *mng = priv->sh->ct_mng;
+ struct mlx5_aso_ct_pool *pool = NULL;
+ struct mlx5_devx_obj *obj = NULL;
+ uint32_t i;
+ uint32_t log_obj_size = rte_log2_u32(MLX5_ASO_CT_ACTIONS_PER_POOL);
+
+ obj = mlx5_devx_cmd_create_conn_track_offload_obj(priv->sh->ctx,
+ priv->sh->pdn, log_obj_size);
+ if (!obj) {
+ rte_errno = ENODATA;
+ DRV_LOG(ERR, "Failed to create conn_track_offload_obj using DevX.");
+ return NULL;
+ }
+ pool = mlx5_malloc(MLX5_MEM_ZERO, sizeof(*pool), 0, SOCKET_ID_ANY);
+ if (!pool) {
+ rte_errno = ENOMEM;
+ claim_zero(mlx5_devx_cmd_destroy(obj));
+ return NULL;
+ }
+ pool->devx_obj = obj;
+ pool->index = mng->next;
+ /* Resize pools array if there is no room for the new pool in it. */
+ if (pool->index == mng->n && flow_dv_aso_ct_pools_resize(dev)) {
+ claim_zero(mlx5_devx_cmd_destroy(obj));
+ mlx5_free(pool);
+ return NULL;
+ }
+ mng->pools[pool->index] = pool;
+ mng->next++;
+ /* Assign the first action in the new pool, the rest go to free list. */
+ *ct_free = &pool->actions[0];
+ /* Lock outside, the list operation is safe here. */
+ for (i = 1; i < MLX5_ASO_CT_ACTIONS_PER_POOL; i++) {
+ /* refcnt is 0 when allocating the memory. */
+ pool->actions[i].offset = i;
+ LIST_INSERT_HEAD(&mng->free_cts, &pool->actions[i], next);
+ }
+ return pool;
+}
+
+/*
+ * Allocate a ASO CT action from free list.
+ *
+ * @param[in] dev
+ * Pointer to the Ethernet device structure.
+ * @param[out] error
+ * Pointer to the error structure.
+ *
+ * @return
+ * Index to ASO CT action on success, 0 otherwise and rte_errno is set.
+ */
+static uint32_t
+flow_dv_aso_ct_alloc(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_aso_ct_pools_mng *mng = priv->sh->ct_mng;
+ struct mlx5_aso_ct_action *ct = NULL;
+ struct mlx5_aso_ct_pool *pool;
+ uint8_t reg_c;
+ uint32_t ct_idx;
+
+ MLX5_ASSERT(mng);
+ if (!priv->config.devx) {
+ rte_errno = ENOTSUP;
+ return 0;
+ }
+ /* Get a free CT action, if no, a new pool will be created. */
+ rte_spinlock_lock(&mng->ct_sl);
+ ct = LIST_FIRST(&mng->free_cts);
+ if (ct) {
+ LIST_REMOVE(ct, next);
+ } else if (!flow_dv_ct_pool_create(dev, &ct)) {
+ rte_spinlock_unlock(&mng->ct_sl);
+ rte_flow_error_set(error, rte_errno, RTE_FLOW_ERROR_TYPE_ACTION,
+ NULL, "failed to create ASO CT pool");
+ return 0;
+ }
+ rte_spinlock_unlock(&mng->ct_sl);
+ pool = container_of(ct, struct mlx5_aso_ct_pool, actions[ct->offset]);
+ ct_idx = MLX5_MAKE_CT_IDX(pool->index, ct->offset);
+ /* 0: inactive, 1: created, 2+: used by flows. */
+ __atomic_store_n(&ct->refcnt, 1, __ATOMIC_RELAXED);
+ reg_c = mlx5_flow_get_reg_id(dev, MLX5_ASO_CONNTRACK, 0, error);
+ if (!ct->dr_action_orig) {
+#ifdef HAVE_MLX5_DR_ACTION_ASO_CT
+ ct->dr_action_orig = mlx5_glue->dv_create_flow_action_aso
+ (priv->sh->rx_domain, pool->devx_obj->obj,
+ ct->offset,
+ MLX5DV_DR_ACTION_FLAGS_ASO_CT_DIRECTION_INITIATOR,
+ reg_c - REG_C_0);
+#else
+ RTE_SET_USED(reg_c);
+#endif
+ if (!ct->dr_action_orig) {
+ 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 0;
+ }
+ }
+ if (!ct->dr_action_rply) {
+#ifdef HAVE_MLX5_DR_ACTION_ASO_CT
+ ct->dr_action_rply = mlx5_glue->dv_create_flow_action_aso
+ (priv->sh->rx_domain, pool->devx_obj->obj,
+ ct->offset,
+ MLX5DV_DR_ACTION_FLAGS_ASO_CT_DIRECTION_RESPONDER,
+ reg_c - REG_C_0);
+#endif
+ if (!ct->dr_action_rply) {
+ 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 0;
+ }
+ }
+ return ct_idx;
+}
+
+/*
+ * Create a conntrack object with context and actions by using ASO mechanism.
+ *
+ * @param[in] dev
+ * Pointer to rte_eth_dev structure.
+ * @param[in] pro
+ * Pointer to conntrack information profile.
+ * @param[out] error
+ * Pointer to the error structure.
+ *
+ * @return
+ * Index to conntrack object on success, 0 otherwise.
+ */
+static uint32_t
+flow_dv_translate_create_conntrack(struct rte_eth_dev *dev,
+ const struct rte_flow_action_conntrack *pro,
+ struct rte_flow_error *error)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_dev_ctx_shared *sh = priv->sh;
+ struct mlx5_aso_ct_action *ct;
+ uint32_t idx;
+
+ if (!sh->ct_aso_en)
+ return rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+ "Connection is not supported");
+ idx = flow_dv_aso_ct_alloc(dev, error);
+ if (!idx)
+ 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_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;
+}
+
/**
* Fill the flow with DV spec, lock free
* (mutex should be acquired by caller).
.skip_scale = dev_flow->skip_scale &
(1 << MLX5_SCALE_FLOW_GROUP_BIT),
};
+ const struct rte_flow_item *head_item = items;
if (!wks)
return rte_flow_error_set(error, ENOMEM,
int action_type = actions->type;
const struct rte_flow_action *found_action = NULL;
uint32_t jump_group = 0;
+ uint32_t owner_idx;
+ struct mlx5_aso_ct_action *ct;
if (!mlx5_flow_os_action_supported(action_type))
return rte_flow_error_set(error, ENOTSUP,
return -rte_errno;
action_flags |= MLX5_FLOW_ACTION_MODIFY_FIELD;
break;
+ case RTE_FLOW_ACTION_TYPE_CONNTRACK:
+ 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,
+ NULL,
+ "CT is unavailable.");
+ if (ct->is_original)
+ dev_flow->dv.actions[actions_n] =
+ ct->dr_action_orig;
+ else
+ dev_flow->dv.actions[actions_n] =
+ ct->dr_action_rply;
+ flow->indirect_type = MLX5_INDIRECT_ACTION_TYPE_CT;
+ flow->ct = owner_idx;
+ __atomic_fetch_add(&ct->refcnt, 1, __ATOMIC_RELAXED);
+ actions_n++;
+ action_flags |= MLX5_FLOW_ACTION_CT;
+ break;
case RTE_FLOW_ACTION_TYPE_END:
actions_end = true;
if (mhdr_res->actions_num) {
/* No other protocol should follow eCPRI layer. */
last_item = MLX5_FLOW_LAYER_ECPRI;
break;
+ case RTE_FLOW_ITEM_TYPE_INTEGRITY:
+ flow_dv_translate_item_integrity(match_mask,
+ match_value,
+ head_item, items);
+ break;
+ case RTE_FLOW_ITEM_TYPE_CONNTRACK:
+ flow_dv_translate_item_aso_ct(dev, match_mask,
+ match_value, items);
+ break;
default:
break;
}
mlx5_flow_meter_detach(priv, fm);
flow->meter = 0;
}
- if (flow->age)
+ /* Keep the current age handling by default. */
+ if (flow->indirect_type == MLX5_INDIRECT_ACTION_TYPE_CT && flow->ct)
+ flow_dv_aso_ct_release(dev, flow->ct);
+ else if (flow->age)
flow_dv_aso_age_release(dev, flow->age);
if (flow->geneve_tlv_option) {
flow_dv_geneve_tlv_option_resource_release(dev);
{
uint32_t idx = 0;
uint32_t ret = 0;
+ struct mlx5_priv *priv = dev->data->dev_private;
switch (action->type) {
case RTE_FLOW_ACTION_TYPE_RSS:
idx = (MLX5_INDIRECT_ACTION_TYPE_COUNT <<
MLX5_INDIRECT_ACTION_TYPE_OFFSET) | ret;
break;
+ case RTE_FLOW_ACTION_TYPE_CONNTRACK:
+ ret = flow_dv_translate_create_conntrack(dev, action->conf,
+ err);
+ idx = MLX5_INDIRECT_ACT_CT_GEN_IDX(PORT_ID(priv), ret);
+ break;
default:
rte_flow_error_set(err, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION,
NULL, "action type not supported");
DRV_LOG(DEBUG, "Indirect age action %" PRIu32 " was"
" released with references %d.", idx, ret);
return 0;
+ case MLX5_INDIRECT_ACTION_TYPE_CT:
+ ret = flow_dv_aso_ct_release(dev, idx);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ DRV_LOG(DEBUG, "Connection tracking object %u still "
+ "has references %d.", idx, ret);
+ return 0;
default:
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION,
return ret;
}
+/*
+ * Updates in place conntrack context or direction.
+ * Context update should be synchronized.
+ *
+ * @param[in] dev
+ * Pointer to the Ethernet device structure.
+ * @param[in] idx
+ * The conntrack object ID to be updated.
+ * @param[in] update
+ * Pointer to the structure of information to update.
+ * @param[out] error
+ * Perform verbose error reporting if not NULL. Initialized in case of
+ * error only.
+ *
+ * @return
+ * 0 on success, otherwise negative errno value.
+ */
+static int
+__flow_dv_action_ct_update(struct rte_eth_dev *dev, uint32_t idx,
+ const struct rte_flow_modify_conntrack *update,
+ struct rte_flow_error *error)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ 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;
+
+ 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,
+ NULL,
+ "CT object is inactive");
+ new_prf = &update->new_ct;
+ 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,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "Failed to send CT context update WQE");
+ /* Block until ready or a failure. */
+ ret = mlx5_aso_ct_available(priv->sh, ct);
+ if (ret)
+ rte_flow_error_set(error, rte_errno,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "Timeout to get the CT update");
+ }
+ return ret;
+}
+
/**
* Updates in place shared action configuration, lock free,
* (mutex should be acquired by caller).
case MLX5_INDIRECT_ACTION_TYPE_RSS:
action_conf = ((const struct rte_flow_action *)update)->conf;
return __flow_dv_action_rss_update(dev, idx, action_conf, err);
+ case MLX5_INDIRECT_ACTION_TYPE_CT:
+ return __flow_dv_action_ct_update(dev, idx, update, err);
default:
return rte_flow_error_set(err, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION,
uint32_t act_idx = (uint32_t)(uintptr_t)handle;
uint32_t type = act_idx >> MLX5_INDIRECT_ACTION_TYPE_OFFSET;
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:
return 0;
case MLX5_INDIRECT_ACTION_TYPE_COUNT:
return flow_dv_query_count(dev, idx, data, error);
+ case MLX5_INDIRECT_ACTION_TYPE_CT:
+ 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,
+ "CT object is inactive");
+ ((struct rte_flow_action_conntrack *)data)->peer_port =
+ ct->peer;
+ ((struct rte_flow_action_conntrack *)data)->is_original_dir =
+ ct->is_original;
+ if (mlx5_aso_ct_query_by_wqe(priv->sh, ct, data))
+ return rte_flow_error_set(error, EIO,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "Failed to query CT context");
+ return 0;
default:
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION, NULL,
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,