+ for (i = 0; i < MLX5_MAX_MODIFY_NUM; ++i)
+ if (sh->mdh_ipools[i])
+ mlx5_ipool_destroy(sh->mdh_ipools[i]);
+}
+
+/*
+ * Check if dynamic flex parser for eCPRI already exists.
+ *
+ * @param dev
+ * Pointer to Ethernet device structure.
+ *
+ * @return
+ * true on exists, false on not.
+ */
+bool
+mlx5_flex_parser_ecpri_exist(struct rte_eth_dev *dev)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_ecpri_parser_profile *prf = &priv->sh->ecpri_parser;
+
+ return !!prf->obj;
+}
+
+/*
+ * Allocation of a flex parser for eCPRI. Once created, this parser related
+ * resources will be held until the device is closed.
+ *
+ * @param dev
+ * Pointer to Ethernet device structure.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_flex_parser_ecpri_alloc(struct rte_eth_dev *dev)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_ecpri_parser_profile *prf = &priv->sh->ecpri_parser;
+ struct mlx5_devx_graph_node_attr node = {
+ .modify_field_select = 0,
+ };
+ uint32_t ids[8];
+ int ret;
+
+ if (!priv->sh->cdev->config.hca_attr.parse_graph_flex_node) {
+ DRV_LOG(ERR, "Dynamic flex parser is not supported "
+ "for device %s.", priv->dev_data->name);
+ return -ENOTSUP;
+ }
+ node.header_length_mode = MLX5_GRAPH_NODE_LEN_FIXED;
+ /* 8 bytes now: 4B common header + 4B message body header. */
+ node.header_length_base_value = 0x8;
+ /* After MAC layer: Ether / VLAN. */
+ node.in[0].arc_parse_graph_node = MLX5_GRAPH_ARC_NODE_MAC;
+ /* Type of compared condition should be 0xAEFE in the L2 layer. */
+ node.in[0].compare_condition_value = RTE_ETHER_TYPE_ECPRI;
+ /* Sample #0: type in common header. */
+ node.sample[0].flow_match_sample_en = 1;
+ /* Fixed offset. */
+ node.sample[0].flow_match_sample_offset_mode = 0x0;
+ /* Only the 2nd byte will be used. */
+ node.sample[0].flow_match_sample_field_base_offset = 0x0;
+ /* Sample #1: message payload. */
+ node.sample[1].flow_match_sample_en = 1;
+ /* Fixed offset. */
+ node.sample[1].flow_match_sample_offset_mode = 0x0;
+ /*
+ * Only the first two bytes will be used right now, and its offset will
+ * start after the common header that with the length of a DW(u32).
+ */
+ node.sample[1].flow_match_sample_field_base_offset = sizeof(uint32_t);
+ prf->obj = mlx5_devx_cmd_create_flex_parser(priv->sh->cdev->ctx, &node);
+ if (!prf->obj) {
+ DRV_LOG(ERR, "Failed to create flex parser node object.");
+ return (rte_errno == 0) ? -ENODEV : -rte_errno;
+ }
+ prf->num = 2;
+ ret = mlx5_devx_cmd_query_parse_samples(prf->obj, ids, prf->num);
+ if (ret) {
+ DRV_LOG(ERR, "Failed to query sample IDs.");
+ return (rte_errno == 0) ? -ENODEV : -rte_errno;
+ }
+ prf->offset[0] = 0x0;
+ prf->offset[1] = sizeof(uint32_t);
+ prf->ids[0] = ids[0];
+ prf->ids[1] = ids[1];
+ return 0;
+}
+
+/*
+ * Destroy the flex parser node, including the parser itself, input / output
+ * arcs and DW samples. Resources could be reused then.
+ *
+ * @param dev
+ * Pointer to Ethernet device structure.
+ */
+static void
+mlx5_flex_parser_ecpri_release(struct rte_eth_dev *dev)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_ecpri_parser_profile *prf = &priv->sh->ecpri_parser;
+
+ if (prf->obj)
+ mlx5_devx_cmd_destroy(prf->obj);
+ prf->obj = NULL;
+}
+
+uint32_t
+mlx5_get_supported_sw_parsing_offloads(const struct mlx5_hca_attr *attr)
+{
+ uint32_t sw_parsing_offloads = 0;
+
+ if (attr->swp) {
+ sw_parsing_offloads |= MLX5_SW_PARSING_CAP;
+ if (attr->swp_csum)
+ sw_parsing_offloads |= MLX5_SW_PARSING_CSUM_CAP;
+
+ if (attr->swp_lso)
+ sw_parsing_offloads |= MLX5_SW_PARSING_TSO_CAP;
+ }
+ return sw_parsing_offloads;
+}
+
+uint32_t
+mlx5_get_supported_tunneling_offloads(const struct mlx5_hca_attr *attr)
+{
+ uint32_t tn_offloads = 0;
+
+ if (attr->tunnel_stateless_vxlan)
+ tn_offloads |= MLX5_TUNNELED_OFFLOADS_VXLAN_CAP;
+ if (attr->tunnel_stateless_gre)
+ tn_offloads |= MLX5_TUNNELED_OFFLOADS_GRE_CAP;
+ if (attr->tunnel_stateless_geneve_rx)
+ tn_offloads |= MLX5_TUNNELED_OFFLOADS_GENEVE_CAP;
+ return tn_offloads;
+}
+
+/* Fill all fields of UAR structure. */
+static int
+mlx5_rxtx_uars_prepare(struct mlx5_dev_ctx_shared *sh)
+{
+ int ret;
+
+ ret = mlx5_devx_uar_prepare(sh->cdev, &sh->tx_uar);
+ if (ret) {
+ DRV_LOG(ERR, "Failed to prepare Tx DevX UAR.");
+ return -rte_errno;
+ }
+ MLX5_ASSERT(sh->tx_uar.obj);
+ MLX5_ASSERT(mlx5_os_get_devx_uar_base_addr(sh->tx_uar.obj));
+ ret = mlx5_devx_uar_prepare(sh->cdev, &sh->rx_uar);
+ if (ret) {
+ DRV_LOG(ERR, "Failed to prepare Rx DevX UAR.");
+ mlx5_devx_uar_release(&sh->tx_uar);
+ return -rte_errno;
+ }
+ MLX5_ASSERT(sh->rx_uar.obj);
+ MLX5_ASSERT(mlx5_os_get_devx_uar_base_addr(sh->rx_uar.obj));
+ return 0;
+}
+
+static void
+mlx5_rxtx_uars_release(struct mlx5_dev_ctx_shared *sh)
+{
+ mlx5_devx_uar_release(&sh->rx_uar);
+ mlx5_devx_uar_release(&sh->tx_uar);
+}
+
+/**
+ * rte_mempool_walk() callback to unregister Rx mempools.
+ * It used when implicit mempool registration is disabled.
+ *
+ * @param mp
+ * The mempool being walked.
+ * @param arg
+ * Pointer to the device shared context.
+ */
+static void
+mlx5_dev_ctx_shared_rx_mempool_unregister_cb(struct rte_mempool *mp, void *arg)
+{
+ struct mlx5_dev_ctx_shared *sh = arg;
+
+ mlx5_dev_mempool_unregister(sh->cdev, mp);
+}
+
+/**
+ * Callback used when implicit mempool registration is disabled
+ * in order to track Rx mempool destruction.
+ *
+ * @param event
+ * Mempool life cycle event.
+ * @param mp
+ * An Rx mempool registered explicitly when the port is started.
+ * @param arg
+ * Pointer to a device shared context.
+ */
+static void
+mlx5_dev_ctx_shared_rx_mempool_event_cb(enum rte_mempool_event event,
+ struct rte_mempool *mp, void *arg)
+{
+ struct mlx5_dev_ctx_shared *sh = arg;
+
+ if (event == RTE_MEMPOOL_EVENT_DESTROY)
+ mlx5_dev_mempool_unregister(sh->cdev, mp);
+}
+
+int
+mlx5_dev_ctx_shared_mempool_subscribe(struct rte_eth_dev *dev)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_dev_ctx_shared *sh = priv->sh;
+ int ret;
+
+ /* Check if we only need to track Rx mempool destruction. */
+ if (!sh->cdev->config.mr_mempool_reg_en) {
+ ret = rte_mempool_event_callback_register
+ (mlx5_dev_ctx_shared_rx_mempool_event_cb, sh);
+ return ret == 0 || rte_errno == EEXIST ? 0 : ret;
+ }
+ return mlx5_dev_mempool_subscribe(sh->cdev);
+}
+
+/**
+ * Set up multiple TISs with different affinities according to
+ * number of bonding ports
+ *
+ * @param priv
+ * Pointer of shared context.
+ *
+ * @return
+ * Zero on success, -1 otherwise.
+ */
+static int
+mlx5_setup_tis(struct mlx5_dev_ctx_shared *sh)
+{
+ int i;
+ struct mlx5_devx_lag_context lag_ctx = { 0 };
+ struct mlx5_devx_tis_attr tis_attr = { 0 };
+
+ tis_attr.transport_domain = sh->td->id;
+ if (sh->bond.n_port) {
+ if (!mlx5_devx_cmd_query_lag(sh->cdev->ctx, &lag_ctx)) {
+ sh->lag.tx_remap_affinity[0] =
+ lag_ctx.tx_remap_affinity_1;
+ sh->lag.tx_remap_affinity[1] =
+ lag_ctx.tx_remap_affinity_2;
+ sh->lag.affinity_mode = lag_ctx.port_select_mode;
+ } else {
+ DRV_LOG(ERR, "Failed to query lag affinity.");
+ return -1;
+ }
+ if (sh->lag.affinity_mode == MLX5_LAG_MODE_TIS) {
+ for (i = 0; i < sh->bond.n_port; i++) {
+ tis_attr.lag_tx_port_affinity =
+ MLX5_IFC_LAG_MAP_TIS_AFFINITY(i,
+ sh->bond.n_port);
+ sh->tis[i] = mlx5_devx_cmd_create_tis(sh->cdev->ctx,
+ &tis_attr);
+ if (!sh->tis[i]) {
+ DRV_LOG(ERR, "Failed to TIS %d/%d for bonding device"
+ " %s.", i, sh->bond.n_port,
+ sh->ibdev_name);
+ return -1;
+ }
+ }
+ DRV_LOG(DEBUG, "LAG number of ports : %d, affinity_1 & 2 : pf%d & %d.\n",
+ sh->bond.n_port, lag_ctx.tx_remap_affinity_1,
+ lag_ctx.tx_remap_affinity_2);
+ return 0;
+ }
+ if (sh->lag.affinity_mode == MLX5_LAG_MODE_HASH)
+ DRV_LOG(INFO, "Device %s enabled HW hash based LAG.",
+ sh->ibdev_name);
+ }
+ tis_attr.lag_tx_port_affinity = 0;
+ sh->tis[0] = mlx5_devx_cmd_create_tis(sh->cdev->ctx, &tis_attr);
+ if (!sh->tis[0]) {
+ DRV_LOG(ERR, "Failed to TIS 0 for bonding device"
+ " %s.", sh->ibdev_name);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Verify and store value for share device argument.
+ *
+ * @param[in] key
+ * Key argument to verify.
+ * @param[in] val
+ * Value associated with key.
+ * @param opaque
+ * User data.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+mlx5_dev_args_check_handler(const char *key, const char *val, void *opaque)
+{
+ struct mlx5_sh_config *config = opaque;
+ signed long tmp;
+
+ errno = 0;
+ tmp = strtol(val, NULL, 0);
+ if (errno) {
+ rte_errno = errno;
+ DRV_LOG(WARNING, "%s: \"%s\" is not a valid integer", key, val);
+ return -rte_errno;
+ }
+ if (tmp < 0 && strcmp(MLX5_TX_PP, key) && strcmp(MLX5_TX_SKEW, key)) {
+ /* Negative values are acceptable for some keys only. */
+ rte_errno = EINVAL;
+ DRV_LOG(WARNING, "%s: invalid negative value \"%s\"", key, val);
+ return -rte_errno;
+ }
+ if (strcmp(MLX5_TX_PP, key) == 0) {
+ unsigned long mod = tmp >= 0 ? tmp : -tmp;
+
+ if (!mod) {
+ DRV_LOG(ERR, "Zero Tx packet pacing parameter.");
+ rte_errno = EINVAL;
+ return -rte_errno;
+ }
+ config->tx_pp = tmp;
+ } else if (strcmp(MLX5_TX_SKEW, key) == 0) {
+ config->tx_skew = tmp;
+ } else if (strcmp(MLX5_L3_VXLAN_EN, key) == 0) {
+ config->l3_vxlan_en = !!tmp;
+ } else if (strcmp(MLX5_VF_NL_EN, key) == 0) {
+ config->vf_nl_en = !!tmp;
+ } else if (strcmp(MLX5_DV_ESW_EN, key) == 0) {
+ config->dv_esw_en = !!tmp;
+ } else if (strcmp(MLX5_DV_FLOW_EN, key) == 0) {
+ if (tmp > 2) {
+ DRV_LOG(ERR, "Invalid %s parameter.", key);
+ rte_errno = EINVAL;
+ return -rte_errno;
+ }
+ config->dv_flow_en = tmp;
+ } else if (strcmp(MLX5_DV_XMETA_EN, key) == 0) {
+ if (tmp != MLX5_XMETA_MODE_LEGACY &&
+ tmp != MLX5_XMETA_MODE_META16 &&
+ tmp != MLX5_XMETA_MODE_META32 &&
+ tmp != MLX5_XMETA_MODE_MISS_INFO) {
+ DRV_LOG(ERR, "Invalid extensive metadata parameter.");
+ rte_errno = EINVAL;
+ return -rte_errno;
+ }
+ if (tmp != MLX5_XMETA_MODE_MISS_INFO)
+ config->dv_xmeta_en = tmp;
+ else
+ config->dv_miss_info = 1;
+ } else if (strcmp(MLX5_LACP_BY_USER, key) == 0) {
+ config->lacp_by_user = !!tmp;
+ } else if (strcmp(MLX5_RECLAIM_MEM, key) == 0) {
+ if (tmp != MLX5_RCM_NONE &&
+ tmp != MLX5_RCM_LIGHT &&
+ tmp != MLX5_RCM_AGGR) {
+ DRV_LOG(ERR, "Unrecognize %s: \"%s\"", key, val);
+ rte_errno = EINVAL;
+ return -rte_errno;
+ }
+ config->reclaim_mode = tmp;
+ } else if (strcmp(MLX5_DECAP_EN, key) == 0) {
+ config->decap_en = !!tmp;
+ } else if (strcmp(MLX5_ALLOW_DUPLICATE_PATTERN, key) == 0) {
+ config->allow_duplicate_pattern = !!tmp;
+ }
+ return 0;
+}
+
+/**
+ * Parse user device parameters and adjust them according to device
+ * capabilities.
+ *
+ * @param sh
+ * Pointer to shared device context.
+ * @param mkvlist
+ * Pointer to mlx5 kvargs control, can be NULL if there is no devargs.
+ * @param config
+ * Pointer to shared device configuration structure.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+mlx5_shared_dev_ctx_args_config(struct mlx5_dev_ctx_shared *sh,
+ struct mlx5_kvargs_ctrl *mkvlist,
+ struct mlx5_sh_config *config)
+{
+ const char **params = (const char *[]){
+ MLX5_TX_PP,
+ MLX5_TX_SKEW,
+ MLX5_L3_VXLAN_EN,
+ MLX5_VF_NL_EN,
+ MLX5_DV_ESW_EN,
+ MLX5_DV_FLOW_EN,
+ MLX5_DV_XMETA_EN,
+ MLX5_LACP_BY_USER,
+ MLX5_RECLAIM_MEM,
+ MLX5_DECAP_EN,
+ MLX5_ALLOW_DUPLICATE_PATTERN,
+ NULL,
+ };
+ int ret = 0;
+
+ /* Default configuration. */
+ memset(config, 0, sizeof(*config));
+ config->vf_nl_en = 1;
+ config->dv_esw_en = 1;
+ config->dv_flow_en = 1;
+ config->decap_en = 1;
+ config->allow_duplicate_pattern = 1;
+ if (mkvlist != NULL) {
+ /* Process parameters. */
+ ret = mlx5_kvargs_process(mkvlist, params,
+ mlx5_dev_args_check_handler, config);
+ if (ret) {
+ DRV_LOG(ERR, "Failed to process device arguments: %s",
+ strerror(rte_errno));
+ return -rte_errno;
+ }
+ }
+ /* Adjust parameters according to device capabilities. */
+ if (config->dv_flow_en && !sh->dev_cap.dv_flow_en) {
+ DRV_LOG(WARNING, "DV flow is not supported.");
+ config->dv_flow_en = 0;
+ }
+ if (config->dv_esw_en && !sh->dev_cap.dv_esw_en) {
+ DRV_LOG(DEBUG, "E-Switch DV flow is not supported.");
+ config->dv_esw_en = 0;
+ }
+ if (config->dv_esw_en && !config->dv_flow_en) {
+ DRV_LOG(DEBUG,
+ "E-Switch DV flow is supported only when DV flow is enabled.");
+ config->dv_esw_en = 0;
+ }
+ if (config->dv_miss_info && config->dv_esw_en)
+ config->dv_xmeta_en = MLX5_XMETA_MODE_META16;
+ if (!config->dv_esw_en &&
+ config->dv_xmeta_en != MLX5_XMETA_MODE_LEGACY) {
+ DRV_LOG(WARNING,
+ "Metadata mode %u is not supported (no E-Switch).",
+ config->dv_xmeta_en);
+ config->dv_xmeta_en = MLX5_XMETA_MODE_LEGACY;
+ }
+ if (config->tx_pp && !sh->dev_cap.txpp_en) {
+ DRV_LOG(ERR, "Packet pacing is not supported.");
+ rte_errno = ENODEV;
+ return -rte_errno;
+ }
+ if (!config->tx_pp && config->tx_skew) {
+ DRV_LOG(WARNING,
+ "\"tx_skew\" doesn't affect without \"tx_pp\".");
+ }
+ /*
+ * If HW has bug working with tunnel packet decapsulation and scatter
+ * FCS, and decapsulation is needed, clear the hw_fcs_strip bit.
+ * Then RTE_ETH_RX_OFFLOAD_KEEP_CRC bit will not be set anymore.
+ */
+ if (sh->dev_cap.scatter_fcs_w_decap_disable && sh->config.decap_en)
+ config->hw_fcs_strip = 0;
+ else
+ config->hw_fcs_strip = sh->dev_cap.hw_fcs_strip;
+ DRV_LOG(DEBUG, "FCS stripping configuration is %ssupported",
+ (config->hw_fcs_strip ? "" : "not "));
+ DRV_LOG(DEBUG, "\"tx_pp\" is %d.", config->tx_pp);
+ DRV_LOG(DEBUG, "\"tx_skew\" is %d.", config->tx_skew);
+ DRV_LOG(DEBUG, "\"reclaim_mode\" is %u.", config->reclaim_mode);
+ DRV_LOG(DEBUG, "\"dv_esw_en\" is %u.", config->dv_esw_en);
+ DRV_LOG(DEBUG, "\"dv_flow_en\" is %u.", config->dv_flow_en);
+ DRV_LOG(DEBUG, "\"dv_xmeta_en\" is %u.", config->dv_xmeta_en);
+ DRV_LOG(DEBUG, "\"dv_miss_info\" is %u.", config->dv_miss_info);
+ DRV_LOG(DEBUG, "\"l3_vxlan_en\" is %u.", config->l3_vxlan_en);
+ DRV_LOG(DEBUG, "\"vf_nl_en\" is %u.", config->vf_nl_en);
+ DRV_LOG(DEBUG, "\"lacp_by_user\" is %u.", config->lacp_by_user);
+ DRV_LOG(DEBUG, "\"decap_en\" is %u.", config->decap_en);
+ DRV_LOG(DEBUG, "\"allow_duplicate_pattern\" is %u.",
+ config->allow_duplicate_pattern);
+ return 0;
+}
+
+/**
+ * Configure realtime timestamp format.
+ *
+ * @param sh
+ * Pointer to mlx5_dev_ctx_shared object.
+ * @param hca_attr
+ * Pointer to DevX HCA capabilities structure.
+ */
+void
+mlx5_rt_timestamp_config(struct mlx5_dev_ctx_shared *sh,
+ struct mlx5_hca_attr *hca_attr)
+{
+ uint32_t dw_cnt = MLX5_ST_SZ_DW(register_mtutc);
+ uint32_t reg[dw_cnt];
+ int ret = ENOTSUP;
+
+ if (hca_attr->access_register_user)
+ ret = mlx5_devx_cmd_register_read(sh->cdev->ctx,
+ MLX5_REGISTER_ID_MTUTC, 0,
+ reg, dw_cnt);
+ if (!ret) {
+ uint32_t ts_mode;
+
+ /* MTUTC register is read successfully. */
+ ts_mode = MLX5_GET(register_mtutc, reg, time_stamp_mode);
+ if (ts_mode == MLX5_MTUTC_TIMESTAMP_MODE_REAL_TIME)
+ sh->dev_cap.rt_timestamp = 1;
+ } else {
+ /* Kernel does not support register reading. */
+ if (hca_attr->dev_freq_khz == (NS_PER_S / MS_PER_S))
+ sh->dev_cap.rt_timestamp = 1;
+ }