net/mlx5/linux: fix missed Rx packet stats
[dpdk.git] / drivers / net / mlx5 / mlx5_flow_dv.c
index f93191a..70e8d0b 100644 (file)
@@ -2623,6 +2623,51 @@ flow_dv_validate_item_ipv6_frag_ext(const struct rte_flow_item *item,
                                  "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.
  *
@@ -3442,6 +3487,57 @@ flow_dv_validate_action_raw_encap_decap
        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.
  *
@@ -6873,6 +6969,12 @@ flow_dv_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr,
                                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,
@@ -7441,6 +7543,14 @@ flow_dv_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr,
                        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,
@@ -9615,6 +9725,64 @@ flow_dv_translate_item_ecpri(struct rte_eth_dev *dev, void *matcher,
        }
 }
 
+/*
+ * 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)                                     \
@@ -11514,6 +11682,286 @@ flow_dv_prepare_counter(struct rte_eth_dev *dev,
        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).
@@ -11669,6 +12117,8 @@ flow_dv_translate(struct rte_eth_dev *dev,
                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,
@@ -12122,6 +12572,31 @@ flow_dv_translate(struct rte_eth_dev *dev,
                                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) {
@@ -12439,6 +12914,10 @@ flow_dv_translate(struct rte_eth_dev *dev,
                                                         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;
                }
@@ -13292,7 +13771,10 @@ flow_dv_destroy(struct rte_eth_dev *dev, struct rte_flow *flow)
                        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);
@@ -13721,6 +14203,7 @@ flow_dv_action_create(struct rte_eth_dev *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:
@@ -13746,6 +14229,11 @@ flow_dv_action_create(struct rte_eth_dev *dev,
                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");
@@ -13808,6 +14296,14 @@ flow_dv_action_destroy(struct rte_eth_dev *dev,
                        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,
@@ -13882,6 +14378,72 @@ __flow_dv_action_rss_update(struct rte_eth_dev *dev, uint32_t idx,
        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).
@@ -13917,6 +14479,8 @@ flow_dv_action_update(struct rte_eth_dev *dev,
        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,
@@ -14538,6 +15102,10 @@ flow_dv_action_query(struct rte_eth_dev *dev,
        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:
@@ -14553,6 +15121,31 @@ flow_dv_action_query(struct rte_eth_dev *dev,
                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,
@@ -15728,6 +16321,12 @@ flow_dv_action_validate(struct rte_eth_dev *dev,
                                                  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,