get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/61246/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 61246,
    "url": "http://patches.dpdk.org/api/patches/61246/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/1571130263-120863-14-git-send-email-orika@mellanox.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<1571130263-120863-14-git-send-email-orika@mellanox.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/1571130263-120863-14-git-send-email-orika@mellanox.com",
    "date": "2019-10-15T09:04:21",
    "name": "[v3,13/14] net/mlx5: split hairpin flows",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "20f4ab9419e27355e75c0b26d0f363aca69c10ae",
    "submitter": {
        "id": 795,
        "url": "http://patches.dpdk.org/api/people/795/?format=api",
        "name": "Ori Kam",
        "email": "orika@mellanox.com"
    },
    "delegate": {
        "id": 319,
        "url": "http://patches.dpdk.org/api/users/319/?format=api",
        "username": "fyigit",
        "first_name": "Ferruh",
        "last_name": "Yigit",
        "email": "ferruh.yigit@amd.com"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/1571130263-120863-14-git-send-email-orika@mellanox.com/mbox/",
    "series": [
        {
            "id": 6855,
            "url": "http://patches.dpdk.org/api/series/6855/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=6855",
            "date": "2019-10-15T09:04:08",
            "name": "add hairpin feature",
            "version": 3,
            "mbox": "http://patches.dpdk.org/series/6855/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/61246/comments/",
    "check": "fail",
    "checks": "http://patches.dpdk.org/api/patches/61246/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@dpdk.org",
        "Delivered-To": "patchwork@dpdk.org",
        "Received": [
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 85AB11EA5D;\n\tTue, 15 Oct 2019 11:07:17 +0200 (CEST)",
            "from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129])\n\tby dpdk.org (Postfix) with ESMTP id 3C5071BF8D\n\tfor <dev@dpdk.org>; Tue, 15 Oct 2019 11:07:16 +0200 (CEST)",
            "from Internal Mail-Server by MTLPINE1 (envelope-from\n\torika@mellanox.com)\n\twith ESMTPS (AES256-SHA encrypted); 15 Oct 2019 11:07:12 +0200",
            "from pegasus04.mtr.labs.mlnx. (pegasus04.mtr.labs.mlnx\n\t[10.210.16.126])\n\tby labmailer.mlnx (8.13.8/8.13.8) with ESMTP id x9F94hco029891;\n\tTue, 15 Oct 2019 12:07:12 +0300"
        ],
        "From": "Ori Kam <orika@mellanox.com>",
        "To": "Matan Azrad <matan@mellanox.com>, Shahaf Shuler <shahafs@mellanox.com>, \n\tViacheslav Ovsiienko <viacheslavo@mellanox.com>",
        "Cc": "dev@dpdk.org, orika@mellanox.com, jingjing.wu@intel.com,\n\tstephen@networkplumber.org",
        "Date": "Tue, 15 Oct 2019 09:04:21 +0000",
        "Message-Id": "<1571130263-120863-14-git-send-email-orika@mellanox.com>",
        "X-Mailer": "git-send-email 1.8.3.1",
        "In-Reply-To": "<1571130263-120863-1-git-send-email-orika@mellanox.com>",
        "References": "<1569479349-36962-1-git-send-email-orika@mellanox.com>\n\t<1571130263-120863-1-git-send-email-orika@mellanox.com>",
        "Subject": "[dpdk-dev] [PATCH v3 13/14] net/mlx5: split hairpin flows",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "Since the encap action is not supported in RX, we need to split the\nhairpin flow into RX and TX.\n\nSigned-off-by: Ori Kam <orika@mellanox.com>\nAcked-by: Viacheslav Ovsiienko <viacheslavo@mellanox.com>\n\n---\n drivers/net/mlx5/mlx5.c            |  10 ++\n drivers/net/mlx5/mlx5.h            |  10 ++\n drivers/net/mlx5/mlx5_flow.c       | 281 +++++++++++++++++++++++++++++++++++--\n drivers/net/mlx5/mlx5_flow.h       |  14 +-\n drivers/net/mlx5/mlx5_flow_dv.c    |  10 +-\n drivers/net/mlx5/mlx5_flow_verbs.c |  11 +-\n drivers/net/mlx5/mlx5_rxq.c        |  26 ++++\n drivers/net/mlx5/mlx5_rxtx.h       |   2 +\n 8 files changed, 334 insertions(+), 30 deletions(-)",
    "diff": "diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c\nindex 0c3239c..bd9c203 100644\n--- a/drivers/net/mlx5/mlx5.c\n+++ b/drivers/net/mlx5/mlx5.c\n@@ -530,6 +530,12 @@ struct mlx5_flow_id_pool *\n \t\t\tgoto error;\n \t\t}\n \t}\n+\tsh->flow_id_pool = mlx5_flow_id_pool_alloc();\n+\tif (!sh->flow_id_pool) {\n+\t\tDRV_LOG(ERR, \"can't create flow id pool\");\n+\t\terr = ENOMEM;\n+\t\tgoto error;\n+\t}\n #endif /* HAVE_IBV_FLOW_DV_SUPPORT */\n \t/*\n \t * Once the device is added to the list of memory event\n@@ -569,6 +575,8 @@ struct mlx5_flow_id_pool *\n \t\tclaim_zero(mlx5_glue->dealloc_pd(sh->pd));\n \tif (sh->ctx)\n \t\tclaim_zero(mlx5_glue->close_device(sh->ctx));\n+\tif (sh->flow_id_pool)\n+\t\tmlx5_flow_id_pool_release(sh->flow_id_pool);\n \trte_free(sh);\n \tassert(err > 0);\n \trte_errno = err;\n@@ -631,6 +639,8 @@ struct mlx5_flow_id_pool *\n \t\tclaim_zero(mlx5_devx_cmd_destroy(sh->td));\n \tif (sh->ctx)\n \t\tclaim_zero(mlx5_glue->close_device(sh->ctx));\n+\tif (sh->flow_id_pool)\n+\t\tmlx5_flow_id_pool_release(sh->flow_id_pool);\n \trte_free(sh);\n exit:\n \tpthread_mutex_unlock(&mlx5_ibv_list_mutex);\ndiff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h\nindex 8e86bcf..5f40a39 100644\n--- a/drivers/net/mlx5/mlx5.h\n+++ b/drivers/net/mlx5/mlx5.h\n@@ -574,6 +574,15 @@ struct mlx5_devx_dbr_page {\n \tuint64_t dbr_bitmap[MLX5_DBR_BITMAP_SIZE];\n };\n \n+/* ID generation structure. */\n+struct mlx5_flow_id_pool {\n+\tuint32_t *free_arr; /**< Pointer to the a array of free values. */\n+\tuint32_t base_index;\n+\t/**< The next index that can be used without any free elements. */\n+\tuint32_t *curr; /**< Pointer to the index to pop. */\n+\tuint32_t *last; /**< Pointer to the last element in the empty arrray. */\n+};\n+\n /*\n  * Shared Infiniband device context for Master/Representors\n  * which belong to same IB device with multiple IB ports.\n@@ -632,6 +641,7 @@ struct mlx5_ibv_shared {\n \tstruct mlx5dv_devx_cmd_comp *devx_comp; /* DEVX async comp obj. */\n \tstruct mlx5_devx_obj *tis; /* TIS object. */\n \tstruct mlx5_devx_obj *td; /* Transport domain. */\n+\tstruct mlx5_flow_id_pool *flow_id_pool; /* Flow ID pool. */\n \tstruct mlx5_ibv_shared_port port[]; /* per device port data array. */\n };\n \ndiff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c\nindex b6dc105..bb13857 100644\n--- a/drivers/net/mlx5/mlx5_flow.c\n+++ b/drivers/net/mlx5/mlx5_flow.c\n@@ -606,7 +606,7 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,\n {\n \tstruct mlx5_priv *priv = dev->data->dev_private;\n \tstruct rte_flow *flow = dev_flow->flow;\n-\tconst int mark = !!(flow->actions &\n+\tconst int mark = !!(dev_flow->actions &\n \t\t\t    (MLX5_FLOW_ACTION_FLAG | MLX5_FLOW_ACTION_MARK));\n \tconst int tunnel = !!(dev_flow->layers & MLX5_FLOW_LAYER_TUNNEL);\n \tunsigned int i;\n@@ -669,7 +669,7 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,\n {\n \tstruct mlx5_priv *priv = dev->data->dev_private;\n \tstruct rte_flow *flow = dev_flow->flow;\n-\tconst int mark = !!(flow->actions &\n+\tconst int mark = !!(dev_flow->actions &\n \t\t\t    (MLX5_FLOW_ACTION_FLAG | MLX5_FLOW_ACTION_MARK));\n \tconst int tunnel = !!(dev_flow->layers & MLX5_FLOW_LAYER_TUNNEL);\n \tunsigned int i;\n@@ -2438,6 +2438,210 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,\n }\n \n /**\n+ * Check if the flow should be splited due to hairpin.\n+ * The reason for the split is that in current HW we can't\n+ * support encap on Rx, so if a flow have encap we move it\n+ * to Tx.\n+ *\n+ * @param dev\n+ *   Pointer to Ethernet device.\n+ * @param[in] attr\n+ *   Flow rule attributes.\n+ * @param[in] actions\n+ *   Associated actions (list terminated by the END action).\n+ *\n+ * @return\n+ *   > 0 the number of actions and the flow should be split,\n+ *   0 when no split required.\n+ */\n+static int\n+flow_check_hairpin_split(struct rte_eth_dev *dev,\n+\t\t\t const struct rte_flow_attr *attr,\n+\t\t\t const struct rte_flow_action actions[])\n+{\n+\tint queue_action = 0;\n+\tint action_n = 0;\n+\tint encap = 0;\n+\tconst struct rte_flow_action_queue *queue;\n+\tconst struct rte_flow_action_rss *rss;\n+\tconst struct rte_flow_action_raw_encap *raw_encap;\n+\n+\tif (!attr->ingress)\n+\t\treturn 0;\n+\tfor (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {\n+\t\tswitch (actions->type) {\n+\t\tcase RTE_FLOW_ACTION_TYPE_QUEUE:\n+\t\t\tqueue = actions->conf;\n+\t\t\tif (mlx5_rxq_get_type(dev, queue->index) !=\n+\t\t\t    MLX5_RXQ_TYPE_HAIRPIN)\n+\t\t\t\treturn 0;\n+\t\t\tqueue_action = 1;\n+\t\t\taction_n++;\n+\t\t\tbreak;\n+\t\tcase RTE_FLOW_ACTION_TYPE_RSS:\n+\t\t\trss = actions->conf;\n+\t\t\tif (mlx5_rxq_get_type(dev, rss->queue[0]) !=\n+\t\t\t    MLX5_RXQ_TYPE_HAIRPIN)\n+\t\t\t\treturn 0;\n+\t\t\tqueue_action = 1;\n+\t\t\taction_n++;\n+\t\t\tbreak;\n+\t\tcase RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP:\n+\t\tcase RTE_FLOW_ACTION_TYPE_NVGRE_ENCAP:\n+\t\t\tencap = 1;\n+\t\t\taction_n++;\n+\t\t\tbreak;\n+\t\tcase RTE_FLOW_ACTION_TYPE_RAW_ENCAP:\n+\t\t\traw_encap = actions->conf;\n+\t\t\tif (raw_encap->size >\n+\t\t\t    (sizeof(struct rte_flow_item_eth) +\n+\t\t\t     sizeof(struct rte_flow_item_ipv4)))\n+\t\t\t\tencap = 1;\n+\t\t\taction_n++;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\taction_n++;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tif (encap == 1 && queue_action)\n+\t\treturn action_n;\n+\treturn 0;\n+}\n+\n+#define MLX5_MAX_SPLIT_ACTIONS 24\n+#define MLX5_MAX_SPLIT_ITEMS 24\n+\n+/**\n+ * Split the hairpin flow.\n+ * Since HW can't support encap on Rx we move the encap to Tx.\n+ * If the count action is after the encap then we also\n+ * move the count action. in this case the count will also measure\n+ * the outer bytes.\n+ *\n+ * @param dev\n+ *   Pointer to Ethernet device.\n+ * @param[in] actions\n+ *   Associated actions (list terminated by the END action).\n+ * @param[out] actions_rx\n+ *   Rx flow actions.\n+ * @param[out] actions_tx\n+ *   Tx flow actions..\n+ * @param[out] pattern_tx\n+ *   The pattern items for the Tx flow.\n+ * @param[out] flow_id\n+ *   The flow ID connected to this flow.\n+ *\n+ * @return\n+ *   0 on success.\n+ */\n+static int\n+flow_hairpin_split(struct rte_eth_dev *dev,\n+\t\t   const struct rte_flow_action actions[],\n+\t\t   struct rte_flow_action actions_rx[],\n+\t\t   struct rte_flow_action actions_tx[],\n+\t\t   struct rte_flow_item pattern_tx[],\n+\t\t   uint32_t *flow_id)\n+{\n+\tstruct mlx5_priv *priv = dev->data->dev_private;\n+\tconst struct rte_flow_action_raw_encap *raw_encap;\n+\tconst struct rte_flow_action_raw_decap *raw_decap;\n+\tstruct mlx5_rte_flow_action_set_tag *set_tag;\n+\tstruct rte_flow_action *tag_action;\n+\tstruct mlx5_rte_flow_item_tag *tag_item;\n+\tstruct rte_flow_item *item;\n+\tchar *addr;\n+\tstruct rte_flow_error error;\n+\tint encap = 0;\n+\n+\tmlx5_flow_id_get(priv->sh->flow_id_pool, flow_id);\n+\tfor (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {\n+\t\tswitch (actions->type) {\n+\t\tcase RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP:\n+\t\tcase RTE_FLOW_ACTION_TYPE_NVGRE_ENCAP:\n+\t\t\trte_memcpy(actions_tx, actions,\n+\t\t\t       sizeof(struct rte_flow_action));\n+\t\t\tactions_tx++;\n+\t\t\tbreak;\n+\t\tcase RTE_FLOW_ACTION_TYPE_COUNT:\n+\t\t\tif (encap) {\n+\t\t\t\trte_memcpy(actions_tx, actions,\n+\t\t\t\t\t   sizeof(struct rte_flow_action));\n+\t\t\t\tactions_tx++;\n+\t\t\t} else {\n+\t\t\t\trte_memcpy(actions_rx, actions,\n+\t\t\t\t\t   sizeof(struct rte_flow_action));\n+\t\t\t\tactions_rx++;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tcase RTE_FLOW_ACTION_TYPE_RAW_ENCAP:\n+\t\t\traw_encap = actions->conf;\n+\t\t\tif (raw_encap->size >\n+\t\t\t    (sizeof(struct rte_flow_item_eth) +\n+\t\t\t     sizeof(struct rte_flow_item_ipv4))) {\n+\t\t\t\tmemcpy(actions_tx, actions,\n+\t\t\t\t       sizeof(struct rte_flow_action));\n+\t\t\t\tactions_tx++;\n+\t\t\t\tencap = 1;\n+\t\t\t} else {\n+\t\t\t\trte_memcpy(actions_rx, actions,\n+\t\t\t\t\t   sizeof(struct rte_flow_action));\n+\t\t\t\tactions_rx++;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tcase RTE_FLOW_ACTION_TYPE_RAW_DECAP:\n+\t\t\traw_decap = actions->conf;\n+\t\t\tif (raw_decap->size <\n+\t\t\t    (sizeof(struct rte_flow_item_eth) +\n+\t\t\t     sizeof(struct rte_flow_item_ipv4))) {\n+\t\t\t\tmemcpy(actions_tx, actions,\n+\t\t\t\t       sizeof(struct rte_flow_action));\n+\t\t\t\tactions_tx++;\n+\t\t\t} else {\n+\t\t\t\trte_memcpy(actions_rx, actions,\n+\t\t\t\t\t   sizeof(struct rte_flow_action));\n+\t\t\t\tactions_rx++;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\trte_memcpy(actions_rx, actions,\n+\t\t\t\t   sizeof(struct rte_flow_action));\n+\t\t\tactions_rx++;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\t/* Add set meta action and end action for the Rx flow. */\n+\ttag_action = actions_rx;\n+\ttag_action->type = MLX5_RTE_FLOW_ACTION_TYPE_TAG;\n+\tactions_rx++;\n+\trte_memcpy(actions_rx, actions, sizeof(struct rte_flow_action));\n+\tactions_rx++;\n+\tset_tag = (void *)actions_rx;\n+\tset_tag->id = flow_get_reg_id(dev, MLX5_HAIRPIN_RX, 0, &error);\n+\tset_tag->data = rte_cpu_to_be_32(*flow_id);\n+\ttag_action->conf = set_tag;\n+\t/* Create Tx item list. */\n+\trte_memcpy(actions_tx, actions, sizeof(struct rte_flow_action));\n+\taddr = (void *)&pattern_tx[2];\n+\titem = pattern_tx;\n+\titem->type = MLX5_RTE_FLOW_ITEM_TYPE_TAG;\n+\ttag_item = (void *)addr;\n+\ttag_item->data = rte_cpu_to_be_32(*flow_id);\n+\ttag_item->id = flow_get_reg_id(dev, MLX5_HAIRPIN_TX, 0, &error);\n+\titem->spec = tag_item;\n+\taddr += sizeof(struct mlx5_rte_flow_item_tag);\n+\ttag_item = (void *)addr;\n+\ttag_item->data = UINT32_MAX;\n+\ttag_item->id = UINT16_MAX;\n+\titem->mask = tag_item;\n+\taddr += sizeof(struct mlx5_rte_flow_item_tag);\n+\titem->last = NULL;\n+\titem++;\n+\titem->type = RTE_FLOW_ITEM_TYPE_END;\n+\treturn 0;\n+}\n+\n+/**\n  * Create a flow and add it to @p list.\n  *\n  * @param dev\n@@ -2465,6 +2669,7 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,\n \t\t const struct rte_flow_action actions[],\n \t\t bool external, struct rte_flow_error *error)\n {\n+\tstruct mlx5_priv *priv = dev->data->dev_private;\n \tstruct rte_flow *flow = NULL;\n \tstruct mlx5_flow *dev_flow;\n \tconst struct rte_flow_action_rss *rss;\n@@ -2472,16 +2677,44 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,\n \t\tstruct rte_flow_expand_rss buf;\n \t\tuint8_t buffer[2048];\n \t} expand_buffer;\n+\tunion {\n+\t\tstruct rte_flow_action actions[MLX5_MAX_SPLIT_ACTIONS];\n+\t\tuint8_t buffer[2048];\n+\t} actions_rx;\n+\tunion {\n+\t\tstruct rte_flow_action actions[MLX5_MAX_SPLIT_ACTIONS];\n+\t\tuint8_t buffer[2048];\n+\t} actions_hairpin_tx;\n+\tunion {\n+\t\tstruct rte_flow_item items[MLX5_MAX_SPLIT_ITEMS];\n+\t\tuint8_t buffer[2048];\n+\t} items_tx;\n \tstruct rte_flow_expand_rss *buf = &expand_buffer.buf;\n+\tconst struct rte_flow_action *p_actions_rx = actions;\n \tint ret;\n \tuint32_t i;\n \tuint32_t flow_size;\n+\tint hairpin_flow = 0;\n+\tuint32_t hairpin_id = 0;\n+\tstruct rte_flow_attr attr_tx = { .priority = 0 };\n \n-\tret = flow_drv_validate(dev, attr, items, actions, external, error);\n+\thairpin_flow = flow_check_hairpin_split(dev, attr, actions);\n+\tif (hairpin_flow > 0) {\n+\t\tif (hairpin_flow > MLX5_MAX_SPLIT_ACTIONS) {\n+\t\t\trte_errno = EINVAL;\n+\t\t\treturn NULL;\n+\t\t}\n+\t\tflow_hairpin_split(dev, actions, actions_rx.actions,\n+\t\t\t\t   actions_hairpin_tx.actions, items_tx.items,\n+\t\t\t\t   &hairpin_id);\n+\t\tp_actions_rx = actions_rx.actions;\n+\t}\n+\tret = flow_drv_validate(dev, attr, items, p_actions_rx, external,\n+\t\t\t\terror);\n \tif (ret < 0)\n-\t\treturn NULL;\n+\t\tgoto error_before_flow;\n \tflow_size = sizeof(struct rte_flow);\n-\trss = flow_get_rss_action(actions);\n+\trss = flow_get_rss_action(p_actions_rx);\n \tif (rss)\n \t\tflow_size += RTE_ALIGN_CEIL(rss->queue_num * sizeof(uint16_t),\n \t\t\t\t\t    sizeof(void *));\n@@ -2490,11 +2723,13 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,\n \tflow = rte_calloc(__func__, 1, flow_size, 0);\n \tif (!flow) {\n \t\trte_errno = ENOMEM;\n-\t\treturn NULL;\n+\t\tgoto error_before_flow;\n \t}\n \tflow->drv_type = flow_get_drv_type(dev, attr);\n \tflow->ingress = attr->ingress;\n \tflow->transfer = attr->transfer;\n+\tif (hairpin_id != 0)\n+\t\tflow->hairpin_flow_id = hairpin_id;\n \tassert(flow->drv_type > MLX5_FLOW_TYPE_MIN &&\n \t       flow->drv_type < MLX5_FLOW_TYPE_MAX);\n \tflow->queue = (void *)(flow + 1);\n@@ -2515,7 +2750,7 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,\n \t}\n \tfor (i = 0; i < buf->entries; ++i) {\n \t\tdev_flow = flow_drv_prepare(flow, attr, buf->entry[i].pattern,\n-\t\t\t\t\t    actions, error);\n+\t\t\t\t\t    p_actions_rx, error);\n \t\tif (!dev_flow)\n \t\t\tgoto error;\n \t\tdev_flow->flow = flow;\n@@ -2523,7 +2758,24 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,\n \t\tLIST_INSERT_HEAD(&flow->dev_flows, dev_flow, next);\n \t\tret = flow_drv_translate(dev, dev_flow, attr,\n \t\t\t\t\t buf->entry[i].pattern,\n-\t\t\t\t\t actions, error);\n+\t\t\t\t\t p_actions_rx, error);\n+\t\tif (ret < 0)\n+\t\t\tgoto error;\n+\t}\n+\t/* Create the tx flow. */\n+\tif (hairpin_flow) {\n+\t\tattr_tx.group = MLX5_HAIRPIN_TX_TABLE;\n+\t\tattr_tx.ingress = 0;\n+\t\tattr_tx.egress = 1;\n+\t\tdev_flow = flow_drv_prepare(flow, &attr_tx, items_tx.items,\n+\t\t\t\t\t    actions_hairpin_tx.actions, error);\n+\t\tif (!dev_flow)\n+\t\t\tgoto error;\n+\t\tdev_flow->flow = flow;\n+\t\tLIST_INSERT_HEAD(&flow->dev_flows, dev_flow, next);\n+\t\tret = flow_drv_translate(dev, dev_flow, &attr_tx,\n+\t\t\t\t\t items_tx.items,\n+\t\t\t\t\t actions_hairpin_tx.actions, error);\n \t\tif (ret < 0)\n \t\t\tgoto error;\n \t}\n@@ -2535,8 +2787,16 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,\n \tTAILQ_INSERT_TAIL(list, flow, next);\n \tflow_rxq_flags_set(dev, flow);\n \treturn flow;\n+error_before_flow:\n+\tif (hairpin_id)\n+\t\tmlx5_flow_id_release(priv->sh->flow_id_pool,\n+\t\t\t\t     hairpin_id);\n+\treturn NULL;\n error:\n \tret = rte_errno; /* Save rte_errno before cleanup. */\n+\tif (flow->hairpin_flow_id)\n+\t\tmlx5_flow_id_release(priv->sh->flow_id_pool,\n+\t\t\t\t     flow->hairpin_flow_id);\n \tassert(flow);\n \tflow_drv_destroy(dev, flow);\n \trte_free(flow);\n@@ -2626,12 +2886,17 @@ struct rte_flow *\n flow_list_destroy(struct rte_eth_dev *dev, struct mlx5_flows *list,\n \t\t  struct rte_flow *flow)\n {\n+\tstruct mlx5_priv *priv = dev->data->dev_private;\n+\n \t/*\n \t * Update RX queue flags only if port is started, otherwise it is\n \t * already clean.\n \t */\n \tif (dev->data->dev_started)\n \t\tflow_rxq_flags_trim(dev, flow);\n+\tif (flow->hairpin_flow_id)\n+\t\tmlx5_flow_id_release(priv->sh->flow_id_pool,\n+\t\t\t\t     flow->hairpin_flow_id);\n \tflow_drv_destroy(dev, flow);\n \tTAILQ_REMOVE(list, flow, next);\n \trte_free(flow->fdir);\ndiff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h\nindex bb67380..90a289e 100644\n--- a/drivers/net/mlx5/mlx5_flow.h\n+++ b/drivers/net/mlx5/mlx5_flow.h\n@@ -434,6 +434,8 @@ struct mlx5_flow {\n \tstruct rte_flow *flow; /**< Pointer to the main flow. */\n \tuint64_t layers;\n \t/**< Bit-fields of present layers, see MLX5_FLOW_LAYER_*. */\n+\tuint64_t actions;\n+\t/**< Bit-fields of detected actions, see MLX5_FLOW_ACTION_*. */\n \tunion {\n #ifdef HAVE_IBV_FLOW_DV_SUPPORT\n \t\tstruct mlx5_flow_dv dv;\n@@ -455,12 +457,11 @@ struct rte_flow {\n \tuint16_t (*queue)[]; /**< Destination queues to redirect traffic to. */\n \tLIST_HEAD(dev_flows, mlx5_flow) dev_flows;\n \t/**< Device flows that are part of the flow. */\n-\tuint64_t actions;\n-\t/**< Bit-fields of detected actions, see MLX5_FLOW_ACTION_*. */\n \tstruct mlx5_fdir *fdir; /**< Pointer to associated FDIR if any. */\n \tuint8_t ingress; /**< 1 if the flow is ingress. */\n \tuint32_t group; /**< The group index. */\n \tuint8_t transfer; /**< 1 if the flow is E-Switch flow. */\n+\tuint32_t hairpin_flow_id; /**< The flow id used for hairpin. */\n };\n \n typedef int (*mlx5_flow_validate_t)(struct rte_eth_dev *dev,\n@@ -504,15 +505,6 @@ struct mlx5_flow_driver_ops {\n #define MLX5_CNT_CONTAINER_UNUSED(sh, batch, thread) (&(sh)->cmng.ccont \\\n \t[(~((sh)->cmng.mhi[batch] >> (thread)) & 0x1) * 2 + (batch)])\n \n-/* ID generation structure. */\n-struct mlx5_flow_id_pool {\n-\tuint32_t *free_arr; /**< Pointer to the a array of free values. */\n-\tuint32_t base_index;\n-\t/**< The next index that can be used without any free elements. */\n-\tuint32_t *curr; /**< Pointer to the index to pop. */\n-\tuint32_t *last; /**< Pointer to the last element in the empty arrray. */\n-};\n-\n /* mlx5_flow.c */\n \n struct mlx5_flow_id_pool *mlx5_flow_id_pool_alloc(void);\ndiff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c\nindex c7a3f6b..367e632 100644\n--- a/drivers/net/mlx5/mlx5_flow_dv.c\n+++ b/drivers/net/mlx5/mlx5_flow_dv.c\n@@ -5763,7 +5763,7 @@ struct field_modify_info modify_tcp[] = {\n \t\t\tmodify_action_position = actions_n++;\n \t}\n \tdev_flow->dv.actions_n = actions_n;\n-\tflow->actions = action_flags;\n+\tdev_flow->actions = action_flags;\n \tfor (; items->type != RTE_FLOW_ITEM_TYPE_END; items++) {\n \t\tint tunnel = !!(item_flags & MLX5_FLOW_LAYER_TUNNEL);\n \t\tint item_type = items->type;\n@@ -5985,7 +5985,7 @@ struct field_modify_info modify_tcp[] = {\n \tLIST_FOREACH(dev_flow, &flow->dev_flows, next) {\n \t\tdv = &dev_flow->dv;\n \t\tn = dv->actions_n;\n-\t\tif (flow->actions & MLX5_FLOW_ACTION_DROP) {\n+\t\tif (dev_flow->actions & MLX5_FLOW_ACTION_DROP) {\n \t\t\tif (flow->transfer) {\n \t\t\t\tdv->actions[n++] = priv->sh->esw_drop_action;\n \t\t\t} else {\n@@ -6000,7 +6000,7 @@ struct field_modify_info modify_tcp[] = {\n \t\t\t\t}\n \t\t\t\tdv->actions[n++] = dv->hrxq->action;\n \t\t\t}\n-\t\t} else if (flow->actions &\n+\t\t} else if (dev_flow->actions &\n \t\t\t   (MLX5_FLOW_ACTION_QUEUE | MLX5_FLOW_ACTION_RSS)) {\n \t\t\tstruct mlx5_hrxq *hrxq;\n \n@@ -6056,7 +6056,7 @@ struct field_modify_info modify_tcp[] = {\n \tLIST_FOREACH(dev_flow, &flow->dev_flows, next) {\n \t\tstruct mlx5_flow_dv *dv = &dev_flow->dv;\n \t\tif (dv->hrxq) {\n-\t\t\tif (flow->actions & MLX5_FLOW_ACTION_DROP)\n+\t\t\tif (dev_flow->actions & MLX5_FLOW_ACTION_DROP)\n \t\t\t\tmlx5_hrxq_drop_release(dev);\n \t\t\telse\n \t\t\t\tmlx5_hrxq_release(dev, dv->hrxq);\n@@ -6290,7 +6290,7 @@ struct field_modify_info modify_tcp[] = {\n \t\t\tdv->flow = NULL;\n \t\t}\n \t\tif (dv->hrxq) {\n-\t\t\tif (flow->actions & MLX5_FLOW_ACTION_DROP)\n+\t\t\tif (dev_flow->actions & MLX5_FLOW_ACTION_DROP)\n \t\t\t\tmlx5_hrxq_drop_release(dev);\n \t\t\telse\n \t\t\t\tmlx5_hrxq_release(dev, dv->hrxq);\ndiff --git a/drivers/net/mlx5/mlx5_flow_verbs.c b/drivers/net/mlx5/mlx5_flow_verbs.c\nindex 23110f2..fd27f6c 100644\n--- a/drivers/net/mlx5/mlx5_flow_verbs.c\n+++ b/drivers/net/mlx5/mlx5_flow_verbs.c\n@@ -191,7 +191,7 @@\n {\n #if defined(HAVE_IBV_DEVICE_COUNTERS_SET_V42) || \\\n \tdefined(HAVE_IBV_DEVICE_COUNTERS_SET_V45)\n-\tif (flow->actions & MLX5_FLOW_ACTION_COUNT) {\n+\tif (flow->counter->cs) {\n \t\tstruct rte_flow_query_count *qc = data;\n \t\tuint64_t counters[2] = {0, 0};\n #if defined(HAVE_IBV_DEVICE_COUNTERS_SET_V42)\n@@ -1410,7 +1410,6 @@\n \t\t     const struct rte_flow_action actions[],\n \t\t     struct rte_flow_error *error)\n {\n-\tstruct rte_flow *flow = dev_flow->flow;\n \tuint64_t item_flags = 0;\n \tuint64_t action_flags = 0;\n \tuint64_t priority = attr->priority;\n@@ -1460,7 +1459,7 @@\n \t\t\t\t\t\t  \"action not supported\");\n \t\t}\n \t}\n-\tflow->actions = action_flags;\n+\tdev_flow->actions = action_flags;\n \tfor (; items->type != RTE_FLOW_ITEM_TYPE_END; items++) {\n \t\tint tunnel = !!(item_flags & MLX5_FLOW_LAYER_TUNNEL);\n \n@@ -1592,7 +1591,7 @@\n \t\t\tverbs->flow = NULL;\n \t\t}\n \t\tif (verbs->hrxq) {\n-\t\t\tif (flow->actions & MLX5_FLOW_ACTION_DROP)\n+\t\t\tif (dev_flow->actions & MLX5_FLOW_ACTION_DROP)\n \t\t\t\tmlx5_hrxq_drop_release(dev);\n \t\t\telse\n \t\t\t\tmlx5_hrxq_release(dev, verbs->hrxq);\n@@ -1656,7 +1655,7 @@\n \n \tLIST_FOREACH(dev_flow, &flow->dev_flows, next) {\n \t\tverbs = &dev_flow->verbs;\n-\t\tif (flow->actions & MLX5_FLOW_ACTION_DROP) {\n+\t\tif (dev_flow->actions & MLX5_FLOW_ACTION_DROP) {\n \t\t\tverbs->hrxq = mlx5_hrxq_drop_new(dev);\n \t\t\tif (!verbs->hrxq) {\n \t\t\t\trte_flow_error_set\n@@ -1717,7 +1716,7 @@\n \tLIST_FOREACH(dev_flow, &flow->dev_flows, next) {\n \t\tverbs = &dev_flow->verbs;\n \t\tif (verbs->hrxq) {\n-\t\t\tif (flow->actions & MLX5_FLOW_ACTION_DROP)\n+\t\t\tif (dev_flow->actions & MLX5_FLOW_ACTION_DROP)\n \t\t\t\tmlx5_hrxq_drop_release(dev);\n \t\t\telse\n \t\t\t\tmlx5_hrxq_release(dev, verbs->hrxq);\ndiff --git a/drivers/net/mlx5/mlx5_rxq.c b/drivers/net/mlx5/mlx5_rxq.c\nindex a8ff8b2..c39118a 100644\n--- a/drivers/net/mlx5/mlx5_rxq.c\n+++ b/drivers/net/mlx5/mlx5_rxq.c\n@@ -2097,6 +2097,32 @@ struct mlx5_rxq_ctrl *\n }\n \n /**\n+ * Get a Rx queue type.\n+ *\n+ * @param dev\n+ *   Pointer to Ethernet device.\n+ * @param idx\n+ *   Rx queue index.\n+ *\n+ * @return\n+ *   The Rx queue type.\n+ */\n+enum mlx5_rxq_type\n+mlx5_rxq_get_type(struct rte_eth_dev *dev, uint16_t idx)\n+{\n+\tstruct mlx5_priv *priv = dev->data->dev_private;\n+\tstruct mlx5_rxq_ctrl *rxq_ctrl = NULL;\n+\n+\tif ((*priv->rxqs)[idx]) {\n+\t\trxq_ctrl = container_of((*priv->rxqs)[idx],\n+\t\t\t\t\tstruct mlx5_rxq_ctrl,\n+\t\t\t\t\trxq);\n+\t\treturn rxq_ctrl->type;\n+\t}\n+\treturn MLX5_RXQ_TYPE_UNDEFINED;\n+}\n+\n+/**\n  * Create an indirection table.\n  *\n  * @param dev\ndiff --git a/drivers/net/mlx5/mlx5_rxtx.h b/drivers/net/mlx5/mlx5_rxtx.h\nindex 271b648..d4ba25f 100644\n--- a/drivers/net/mlx5/mlx5_rxtx.h\n+++ b/drivers/net/mlx5/mlx5_rxtx.h\n@@ -166,6 +166,7 @@ enum mlx5_rxq_obj_type {\n enum mlx5_rxq_type {\n \tMLX5_RXQ_TYPE_STANDARD, /* Standard Rx queue. */\n \tMLX5_RXQ_TYPE_HAIRPIN, /* Hairpin Rx queue. */\n+\tMLX5_RXQ_TYPE_UNDEFINED,\n };\n \n /* Verbs/DevX Rx queue elements. */\n@@ -406,6 +407,7 @@ struct mlx5_hrxq *mlx5_hrxq_get(struct rte_eth_dev *dev,\n \t\t\t\tconst uint16_t *queues, uint32_t queues_n);\n int mlx5_hrxq_release(struct rte_eth_dev *dev, struct mlx5_hrxq *hxrq);\n int mlx5_hrxq_verify(struct rte_eth_dev *dev);\n+enum mlx5_rxq_type mlx5_rxq_get_type(struct rte_eth_dev *dev, uint16_t idx);\n struct mlx5_hrxq *mlx5_hrxq_drop_new(struct rte_eth_dev *dev);\n void mlx5_hrxq_drop_release(struct rte_eth_dev *dev);\n uint64_t mlx5_get_rx_port_offloads(void);\n",
    "prefixes": [
        "v3",
        "13/14"
    ]
}