net/mlx5: fix meter suffix flow
[dpdk.git] / drivers / net / mlx5 / mlx5_flow.c
index da3e47a..970123b 100644 (file)
@@ -236,6 +236,7 @@ static const struct rte_flow_ops mlx5_flow_ops = {
        .flush = mlx5_flow_flush,
        .isolate = mlx5_flow_isolate,
        .query = mlx5_flow_query,
+       .dev_dump = mlx5_flow_dev_dump,
 };
 
 /* Convert FDIR request to Generic flow. */
@@ -318,6 +319,10 @@ static struct mlx5_flow_tunnel_info tunnels_info[] = {
                .tunnel = MLX5_FLOW_LAYER_IPV6_ENCAP,
                .ptype = RTE_PTYPE_TUNNEL_IP,
        },
+       {
+               .tunnel = MLX5_FLOW_LAYER_GTP,
+               .ptype = RTE_PTYPE_TUNNEL_GTPU,
+       },
 };
 
 /**
@@ -364,7 +369,15 @@ mlx5_flow_get_reg_id(struct rte_eth_dev *dev,
        case MLX5_METADATA_TX:
                return REG_A;
        case MLX5_METADATA_FDB:
-               return REG_C_0;
+               switch (config->dv_xmeta_en) {
+               case MLX5_XMETA_MODE_LEGACY:
+                       return REG_NONE;
+               case MLX5_XMETA_MODE_META16:
+                       return REG_C_0;
+               case MLX5_XMETA_MODE_META32:
+                       return REG_C_1;
+               }
+               break;
        case MLX5_FLOW_MARK:
                switch (config->dv_xmeta_en) {
                case MLX5_XMETA_MODE_LEGACY:
@@ -1142,6 +1155,18 @@ mlx5_flow_validate_action_rss(const struct rte_flow_action *action,
                                          &rss->types,
                                          "some RSS protocols are not"
                                          " supported");
+       if ((rss->types & (ETH_RSS_L3_SRC_ONLY | ETH_RSS_L3_DST_ONLY)) &&
+           !(rss->types & ETH_RSS_IP))
+               return rte_flow_error_set(error, EINVAL,
+                                         RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,
+                                         "L3 partial RSS requested but L3 RSS"
+                                         " type not specified");
+       if ((rss->types & (ETH_RSS_L4_SRC_ONLY | ETH_RSS_L4_DST_ONLY)) &&
+           !(rss->types & (ETH_RSS_UDP | ETH_RSS_TCP)))
+               return rte_flow_error_set(error, EINVAL,
+                                         RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,
+                                         "L4 partial RSS requested but L4 RSS"
+                                         " type not specified");
        if (!priv->rxqs_n)
                return rte_flow_error_set(error, EINVAL,
                                          RTE_FLOW_ERROR_TYPE_ACTION_CONF,
@@ -1151,6 +1176,11 @@ mlx5_flow_validate_action_rss(const struct rte_flow_action *action,
                                          RTE_FLOW_ERROR_TYPE_ACTION_CONF,
                                          NULL, "No queues configured");
        for (i = 0; i != rss->queue_num; ++i) {
+               if (rss->queue[i] >= priv->rxqs_n)
+                       return rte_flow_error_set
+                               (error, EINVAL,
+                                RTE_FLOW_ERROR_TYPE_ACTION_CONF,
+                                &rss->queue[i], "queue index out of range");
                if (!(*priv->rxqs)[rss->queue[i]])
                        return rte_flow_error_set
                                (error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION_CONF,
@@ -1985,8 +2015,8 @@ mlx5_flow_validate_item_gre_key(const struct rte_flow_item *item,
        const rte_be32_t *mask = item->mask;
        int ret = 0;
        rte_be32_t gre_key_default_mask = RTE_BE32(UINT32_MAX);
-       const struct rte_flow_item_gre *gre_spec = gre_item->spec;
-       const struct rte_flow_item_gre *gre_mask = gre_item->mask;
+       const struct rte_flow_item_gre *gre_spec;
+       const struct rte_flow_item_gre *gre_mask;
 
        if (item_flags & MLX5_FLOW_LAYER_GRE_KEY)
                return rte_flow_error_set(error, ENOTSUP,
@@ -2000,8 +2030,10 @@ mlx5_flow_validate_item_gre_key(const struct rte_flow_item *item,
                return rte_flow_error_set(error, ENOTSUP,
                                          RTE_FLOW_ERROR_TYPE_ITEM, item,
                                          "GRE key following a wrong item");
+       gre_mask = gre_item->mask;
        if (!gre_mask)
                gre_mask = &rte_flow_item_gre_mask;
+       gre_spec = gre_item->spec;
        if (gre_spec && (gre_mask->c_rsvd0_ver & RTE_BE16(0x2000)) &&
                         !(gre_spec->c_rsvd0_ver & RTE_BE16(0x2000)))
                return rte_flow_error_set(error, EINVAL,
@@ -2328,27 +2360,6 @@ flow_mreg_split_qrss_release(struct rte_eth_dev *dev,
                        flow_qrss_free_id(dev, dev_flow->qrss_id);
 }
 
-/**
- * Release meter prefix suffix flow match id.
- *
- * @param dev
- *   Pointer to Ethernet device.
- * @param flow
- *   Flow to release id's from.
- */
-static void
-flow_meter_split_id_release(struct rte_eth_dev *dev,
-                           struct rte_flow *flow)
-{
-       struct mlx5_flow *dev_flow;
-       struct mlx5_priv *priv = dev->data->dev_private;
-
-       LIST_FOREACH(dev_flow, &flow->dev_flows, next)
-               if (dev_flow->qrss_id)
-                       mlx5_flow_id_release(priv->sh->flow_id_pool,
-                                            dev_flow->mtr_flow_id);
-}
-
 static int
 flow_null_validate(struct rte_eth_dev *dev __rte_unused,
                   const struct rte_flow_attr *attr __rte_unused,
@@ -2639,7 +2650,6 @@ flow_drv_destroy(struct rte_eth_dev *dev, struct rte_flow *flow)
        enum mlx5_flow_drv_type type = flow->drv_type;
 
        flow_mreg_split_qrss_release(dev, flow);
-       flow_meter_split_id_release(dev, flow);
        assert(type > MLX5_FLOW_TYPE_MIN && type < MLX5_FLOW_TYPE_MAX);
        fops = flow_get_drv_ops(type);
        fops->destroy(dev, flow);
@@ -2831,6 +2841,8 @@ flow_check_hairpin_split(struct rte_eth_dev *dev,
                switch (actions->type) {
                case RTE_FLOW_ACTION_TYPE_QUEUE:
                        queue = actions->conf;
+                       if (queue == NULL)
+                               return 0;
                        if (mlx5_rxq_get_type(dev, queue->index) !=
                            MLX5_RXQ_TYPE_HAIRPIN)
                                return 0;
@@ -2839,6 +2851,8 @@ flow_check_hairpin_split(struct rte_eth_dev *dev,
                        break;
                case RTE_FLOW_ACTION_TYPE_RSS:
                        rss = actions->conf;
+                       if (rss == NULL || rss->queue_num == 0)
+                               return 0;
                        if (mlx5_rxq_get_type(dev, rss->queue[0]) !=
                            MLX5_RXQ_TYPE_HAIRPIN)
                                return 0;
@@ -2950,16 +2964,16 @@ flow_mreg_add_copy_action(struct rte_eth_dev *dev, uint32_t mark_id,
        mcp_res = (void *)mlx5_hlist_lookup(priv->mreg_cp_tbl, mark_id);
        if (mcp_res) {
                /* For non-default rule. */
-               if (mark_id)
+               if (mark_id != MLX5_DEFAULT_COPY_ID)
                        mcp_res->refcnt++;
-               assert(mark_id || mcp_res->refcnt == 1);
+               assert(mark_id != MLX5_DEFAULT_COPY_ID || mcp_res->refcnt == 1);
                return mcp_res;
        }
        /* Provide the full width of FLAG specific value. */
        if (mark_id == (priv->sh->dv_regc0_mask & MLX5_FLOW_MARK_DEFAULT))
                tag_spec.data = MLX5_FLOW_MARK_DEFAULT;
        /* Build a new flow. */
-       if (mark_id) {
+       if (mark_id != MLX5_DEFAULT_COPY_ID) {
                items[0] = (struct rte_flow_item){
                        .type = MLX5_RTE_FLOW_ITEM_TYPE_TAG,
                        .spec = &tag_spec,
@@ -3057,7 +3071,7 @@ flow_mreg_del_copy_action(struct rte_eth_dev *dev,
        }
        /*
         * We do not check availability of metadata registers here,
-        * because copy resources are allocated in this case.
+        * because copy resources are not allocated in this case.
         */
        if (--mcp_res->refcnt)
                return;
@@ -3136,7 +3150,8 @@ flow_mreg_del_default_copy_action(struct rte_eth_dev *dev)
        /* Check if default flow is registered. */
        if (!priv->mreg_cp_tbl)
                return;
-       mcp_res = (void *)mlx5_hlist_lookup(priv->mreg_cp_tbl, 0ULL);
+       mcp_res = (void *)mlx5_hlist_lookup(priv->mreg_cp_tbl,
+                                           MLX5_DEFAULT_COPY_ID);
        if (!mcp_res)
                return;
        assert(mcp_res->flow);
@@ -3169,7 +3184,7 @@ flow_mreg_add_default_copy_action(struct rte_eth_dev *dev,
            !mlx5_flow_ext_mreg_supported(dev) ||
            !priv->sh->dv_regc0_mask)
                return 0;
-       mcp_res = flow_mreg_add_copy_action(dev, 0, error);
+       mcp_res = flow_mreg_add_copy_action(dev, MLX5_DEFAULT_COPY_ID, error);
        if (!mcp_res)
                return -rte_errno;
        return 0;
@@ -3473,7 +3488,6 @@ flow_meter_split_prep(struct rte_eth_dev *dev,
                 struct rte_flow_action actions_sfx[],
                 struct rte_flow_action actions_pre[])
 {
-       struct mlx5_priv *priv = dev->data->dev_private;
        struct rte_flow_action *tag_action;
        struct mlx5_rte_flow_action_set_tag *set_tag;
        struct rte_flow_error error;
@@ -3538,7 +3552,10 @@ flow_meter_split_prep(struct rte_eth_dev *dev,
        /* Set the tag. */
        set_tag = (void *)actions_pre;
        set_tag->id = mlx5_flow_get_reg_id(dev, MLX5_MTR_SFX, 0, &error);
-       mlx5_flow_id_get(priv->sh->flow_id_pool, &tag_id);
+       /*
+        * Get the id from the qrss_pool to make qrss share the id with meter.
+        */
+       tag_id = flow_qrss_get_id(dev);
        set_tag->data = rte_cpu_to_be_32(tag_id);
        tag_action->conf = set_tag;
        return tag_id;
@@ -3595,7 +3612,7 @@ flow_mreg_split_qrss_prep(struct rte_eth_dev *dev,
        struct mlx5_rte_flow_action_set_tag *set_tag;
        struct rte_flow_action_jump *jump;
        const int qrss_idx = qrss - actions;
-       uint32_t flow_id;
+       uint32_t flow_id = 0;
        int ret = 0;
 
        /*
@@ -3605,43 +3622,50 @@ flow_mreg_split_qrss_prep(struct rte_eth_dev *dev,
         * As a result, there will be one more action.
         */
        ++actions_n;
+       memcpy(split_actions, actions, sizeof(*split_actions) * actions_n);
+       set_tag = (void *)(split_actions + actions_n);
        /*
-        * Allocate the new subflow ID. This one is unique within
-        * device and not shared with representors. Otherwise,
-        * we would have to resolve multi-thread access synch
-        * issue. Each flow on the shared device is appended
-        * with source vport identifier, so the resulting
-        * flows will be unique in the shared (by master and
-        * representors) domain even if they have coinciding
-        * IDs.
+        * If tag action is not set to void(it means we are not the meter
+        * suffix flow), add the tag action. Since meter suffix flow already
+        * has the tag added.
         */
-       flow_id = flow_qrss_get_id(dev);
-       if (!flow_id)
-               return rte_flow_error_set(error, ENOMEM,
-                                         RTE_FLOW_ERROR_TYPE_ACTION,
-                                         NULL, "can't allocate id "
-                                         "for split Q/RSS subflow");
-       /* Internal SET_TAG action to set flow ID. */
-       set_tag = (void *)(split_actions + actions_n);
-       *set_tag = (struct mlx5_rte_flow_action_set_tag){
-               .data = flow_id,
-       };
-       ret = mlx5_flow_get_reg_id(dev, MLX5_COPY_MARK, 0, error);
-       if (ret < 0)
-               return ret;
-       set_tag->id = ret;
+       if (split_actions[qrss_idx].type != RTE_FLOW_ACTION_TYPE_VOID) {
+               /*
+                * Allocate the new subflow ID. This one is unique within
+                * device and not shared with representors. Otherwise,
+                * we would have to resolve multi-thread access synch
+                * issue. Each flow on the shared device is appended
+                * with source vport identifier, so the resulting
+                * flows will be unique in the shared (by master and
+                * representors) domain even if they have coinciding
+                * IDs.
+                */
+               flow_id = flow_qrss_get_id(dev);
+               if (!flow_id)
+                       return rte_flow_error_set(error, ENOMEM,
+                                                 RTE_FLOW_ERROR_TYPE_ACTION,
+                                                 NULL, "can't allocate id "
+                                                 "for split Q/RSS subflow");
+               /* Internal SET_TAG action to set flow ID. */
+               *set_tag = (struct mlx5_rte_flow_action_set_tag){
+                       .data = flow_id,
+               };
+               ret = mlx5_flow_get_reg_id(dev, MLX5_COPY_MARK, 0, error);
+               if (ret < 0)
+                       return ret;
+               set_tag->id = ret;
+               /* Construct new actions array. */
+               /* Replace QUEUE/RSS action. */
+               split_actions[qrss_idx] = (struct rte_flow_action){
+                       .type = MLX5_RTE_FLOW_ACTION_TYPE_TAG,
+                       .conf = set_tag,
+               };
+       }
        /* JUMP action to jump to mreg copy table (CP_TBL). */
        jump = (void *)(set_tag + 1);
        *jump = (struct rte_flow_action_jump){
                .group = MLX5_FLOW_MREG_CP_TABLE_GROUP,
        };
-       /* Construct new actions array. */
-       memcpy(split_actions, actions, sizeof(*split_actions) * actions_n);
-       /* Replace QUEUE/RSS action. */
-       split_actions[qrss_idx] = (struct rte_flow_action){
-               .type = MLX5_RTE_FLOW_ACTION_TYPE_TAG,
-               .conf = set_tag,
-       };
        split_actions[actions_n - 2] = (struct rte_flow_action){
                .type = RTE_FLOW_ACTION_TYPE_JUMP,
                .conf = jump,
@@ -3742,6 +3766,7 @@ flow_create_split_metadata(struct rte_eth_dev *dev,
        struct rte_flow_action *ext_actions = NULL;
        struct mlx5_flow *dev_flow = NULL;
        uint32_t qrss_id = 0;
+       int mtr_sfx = 0;
        size_t act_size;
        int actions_n;
        int ret;
@@ -3772,6 +3797,10 @@ flow_create_split_metadata(struct rte_eth_dev *dev,
                }
        }
        if (qrss) {
+               /* Check if it is in meter suffix table. */
+               mtr_sfx = attr->group == (attr->transfer ?
+                         (MLX5_FLOW_TABLE_LEVEL_SUFFIX - 1) :
+                         MLX5_FLOW_TABLE_LEVEL_SUFFIX);
                /*
                 * Q/RSS action on NIC Rx should be split in order to pass by
                 * the mreg copy table (RX_CP_TBL) and then it jumps to the
@@ -3786,6 +3815,16 @@ flow_create_split_metadata(struct rte_eth_dev *dev,
                                                  RTE_FLOW_ERROR_TYPE_ACTION,
                                                  NULL, "no memory to split "
                                                  "metadata flow");
+               /*
+                * If we are the suffix flow of meter, tag already exist.
+                * Set the tag action to void.
+                */
+               if (mtr_sfx)
+                       ext_actions[qrss - actions].type =
+                                               RTE_FLOW_ACTION_TYPE_VOID;
+               else
+                       ext_actions[qrss - actions].type =
+                                               MLX5_RTE_FLOW_ACTION_TYPE_TAG;
                /*
                 * Create the new actions list with removed Q/RSS action
                 * and appended set tag and jump to register copy table
@@ -3794,7 +3833,7 @@ flow_create_split_metadata(struct rte_eth_dev *dev,
                 */
                qrss_id = flow_mreg_split_qrss_prep(dev, ext_actions, actions,
                                                    qrss, actions_n, error);
-               if (!qrss_id) {
+               if (!mtr_sfx && !qrss_id) {
                        ret = -rte_errno;
                        goto exit;
                }
@@ -3824,7 +3863,7 @@ flow_create_split_metadata(struct rte_eth_dev *dev,
        if (ret < 0)
                goto exit;
        assert(dev_flow);
-       if (qrss_id) {
+       if (qrss) {
                const struct rte_flow_attr q_attr = {
                        .group = MLX5_FLOW_MREG_ACT_TABLE_GROUP,
                        .ingress = 1,
@@ -3855,22 +3894,35 @@ flow_create_split_metadata(struct rte_eth_dev *dev,
                        },
                };
                uint64_t hash_fields = dev_flow->hash_fields;
+
                /*
-                * Put unique id in prefix flow due to it is destroyed after
-                * prefix flow and id will be freed after there is no actual
-                * flows with this id and identifier reallocation becomes
-                * possible (for example, for other flows in other threads).
+                * Configure the tag item only if there is no meter subflow.
+                * Since tag is already marked in the meter suffix subflow
+                * we can just use the meter suffix items as is.
                 */
-               dev_flow->qrss_id = qrss_id;
-               qrss_id = 0;
+               if (qrss_id) {
+                       /* Not meter subflow. */
+                       assert(!mtr_sfx);
+                       /*
+                        * Put unique id in prefix flow due to it is destroyed
+                        * after suffix flow and id will be freed after there
+                        * is no actual flows with this id and identifier
+                        * reallocation becomes possible (for example, for
+                        * other flows in other threads).
+                        */
+                       dev_flow->qrss_id = qrss_id;
+                       qrss_id = 0;
+                       ret = mlx5_flow_get_reg_id(dev, MLX5_COPY_MARK, 0,
+                                                  error);
+                       if (ret < 0)
+                               goto exit;
+                       q_tag_spec.id = ret;
+               }
                dev_flow = NULL;
-               ret = mlx5_flow_get_reg_id(dev, MLX5_COPY_MARK, 0, error);
-               if (ret < 0)
-                       goto exit;
-               q_tag_spec.id = ret;
                /* Add suffix subflow to execute Q/RSS. */
                ret = flow_create_split_inner(dev, flow, &dev_flow,
-                                             &q_attr, q_items, q_actions,
+                                             &q_attr, mtr_sfx ? items :
+                                             q_items, q_actions,
                                              external, error);
                if (ret < 0)
                        goto exit;
@@ -3990,7 +4042,7 @@ flow_create_split_meter(struct rte_eth_dev *dev,
                        sfx_items++;
                }
                sfx_items->type = RTE_FLOW_ITEM_TYPE_END;
-               sfx_items -= METER_SUFFIX_ITEM;
+               sfx_items -= sfx_port_id_item ? 2 : 1;
                /* Setting the sfx group atrr. */
                sfx_attr.group = sfx_attr.transfer ?
                                (MLX5_FLOW_TABLE_LEVEL_SUFFIX - 1) :
@@ -5632,3 +5684,26 @@ mlx5_flow_discover_mreg_c(struct rte_eth_dev *dev)
                config->flow_mreg_c[n] = REG_NONE;
        return 0;
 }
+
+/**
+ * Dump flow raw hw data to file
+ *
+ * @param[in] dev
+ *    The pointer to Ethernet device.
+ * @param[in] file
+ *   A pointer to a file for output.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL. PMDs initialize this
+ *   structure in case of error only.
+ * @return
+ *   0 on success, a nagative value otherwise.
+ */
+int
+mlx5_flow_dev_dump(struct rte_eth_dev *dev,
+                  FILE *file,
+                  struct rte_flow_error *error __rte_unused)
+{
+       struct mlx5_priv *priv = dev->data->dev_private;
+
+       return mlx5_devx_cmd_flow_dump(priv->sh, file);
+}