Show a patch.

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

{
    "id": 73557,
    "url": "https://patches.dpdk.org/api/patches/73557/",
    "web_url": "https://patches.dpdk.org/patch/73557/",
    "project": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/projects/1/",
        "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"
    },
    "msgid": "<20200708213946.30108-5-andreyv@mellanox.com>",
    "date": "2020-07-08T21:39:43",
    "name": "[v2,4/6] net/mlx5: shared action PMD",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "5d0f547948361acfec980c2032f24b670b7100e8",
    "submitter": {
        "id": 1809,
        "url": "https://patches.dpdk.org/api/people/1809/",
        "name": "Andrey Vesnovaty",
        "email": "andreyv@mellanox.com"
    },
    "delegate": {
        "id": 319,
        "url": "https://patches.dpdk.org/api/users/319/",
        "username": "fyigit",
        "first_name": "Ferruh",
        "last_name": "Yigit",
        "email": "ferruh.yigit@intel.com"
    },
    "mbox": "https://patches.dpdk.org/patch/73557/mbox/",
    "series": [
        {
            "id": 10899,
            "url": "https://patches.dpdk.org/api/series/10899/",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=10899",
            "date": "2020-07-08T21:39:39",
            "name": "add flow shared action API + PMD",
            "version": 2,
            "mbox": "https://patches.dpdk.org/series/10899/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/73557/comments/",
    "check": "warning",
    "checks": "https://patches.dpdk.org/api/patches/73557/checks/",
    "tags": {},
    "headers": {
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "Return-Path": "<dev-bounces@dpdk.org>",
        "Message-Id": "<20200708213946.30108-5-andreyv@mellanox.com>",
        "X-Mailer": "git-send-email 2.26.2",
        "To": "dev@dpdk.org",
        "Cc": "jer@marvell.com, jerinjacobk@gmail.com, thomas@monjalon.net,\n ferruh.yigit@intel.com, stephen@networkplumber.org,\n bruce.richardson@intel.com, orika@mellanox.com,\n viacheslavo@mellanox.com, andrey.vesnovaty@gmail.com,\n Matan Azrad <matan@mellanox.com>, Shahaf Shuler <shahafs@mellanox.com>",
        "X-BeenThere": "dev@dpdk.org",
        "Received": [
            "from dpdk.org (dpdk.org [92.243.14.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 13AB2A0526;\n\tWed,  8 Jul 2020 23:40:23 +0200 (CEST)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id F1A301E535;\n\tWed,  8 Jul 2020 23:40:03 +0200 (CEST)",
            "from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129])\n by dpdk.org (Postfix) with ESMTP id D4A4E1DFF0\n for <dev@dpdk.org>; Wed,  8 Jul 2020 23:39:57 +0200 (CEST)",
            "from Internal Mail-Server by MTLPINE1 (envelope-from\n andreyv@mellanox.com) with SMTP; 9 Jul 2020 00:39:54 +0300",
            "from r-arch-host11.mtr.labs.mlnx. (r-arch-host11.mtr.labs.mlnx\n [10.213.43.60])\n by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id 068LdrB5032740;\n Thu, 9 Jul 2020 00:39:54 +0300"
        ],
        "Subject": "[dpdk-dev] [PATCH v2 4/6] net/mlx5: shared action PMD",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "In-Reply-To": "<20200708213946.30108-1-andreyv@mellanox.com>",
        "Precedence": "list",
        "From": "Andrey Vesnovaty <andreyv@mellanox.com>",
        "Content-Transfer-Encoding": "8bit",
        "MIME-Version": "1.0",
        "References": "<20200702120511.16315-1-andreyv@mellanox.com>\n <20200708213946.30108-1-andreyv@mellanox.com>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>",
        "Errors-To": "dev-bounces@dpdk.org",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Date": "Thu,  9 Jul 2020 00:39:43 +0300",
        "X-Mailman-Version": "2.1.15",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>"
    },
    "content": "Implement rte_flow shared action API for mlx5 PMD.\nHandle shared action on flow create/destroy.\n\nSigned-off-by: Andrey Vesnovaty <andreyv@mellanox.com>\n---\n drivers/net/mlx5/mlx5.c      |   1 +\n drivers/net/mlx5/mlx5.h      |   2 +\n drivers/net/mlx5/mlx5_defs.h |   3 +\n drivers/net/mlx5/mlx5_flow.c | 492 ++++++++++++++++++++++++++++++++---\n drivers/net/mlx5/mlx5_flow.h |  83 ++++++\n 5 files changed, 549 insertions(+), 32 deletions(-)",
    "diff": "diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c\nindex 86b7671b4d..ea6af3775b 100644\n--- a/drivers/net/mlx5/mlx5.c\n+++ b/drivers/net/mlx5/mlx5.c\n@@ -1147,6 +1147,7 @@ mlx5_dev_close(struct rte_eth_dev *dev)\n \t * then this will return directly without any action.\n \t */\n \tmlx5_flow_list_flush(dev, &priv->flows, true);\n+\tmlx5_shared_action_flush(dev);\n \tmlx5_flow_meter_flush(dev, NULL);\n \t/* Free the intermediate buffers for flow creation. */\n \tmlx5_flow_free_intermediate(dev);\ndiff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h\nindex 46e66eb1c6..4add24bd79 100644\n--- a/drivers/net/mlx5/mlx5.h\n+++ b/drivers/net/mlx5/mlx5.h\n@@ -680,6 +680,8 @@ struct mlx5_priv {\n \tuint8_t fdb_def_rule; /* Whether fdb jump to table 1 is configured. */\n \tstruct mlx5_mp_id mp_id; /* ID of a multi-process process */\n \tLIST_HEAD(fdir, mlx5_fdir_flow) fdir_flows; /* fdir flows. */\n+\tLIST_HEAD(shared_action, rte_flow_shared_action) shared_actions;\n+\t/* shared actions */\n };\n \n #define PORT_ID(priv) ((priv)->dev_data->port_id)\ndiff --git a/drivers/net/mlx5/mlx5_defs.h b/drivers/net/mlx5/mlx5_defs.h\nindex 260f584298..dc56c77b59 100644\n--- a/drivers/net/mlx5/mlx5_defs.h\n+++ b/drivers/net/mlx5/mlx5_defs.h\n@@ -180,6 +180,9 @@\n #define MLX5_HAIRPIN_QUEUE_STRIDE 6\n #define MLX5_HAIRPIN_JUMBO_LOG_SIZE (14 + 2)\n \n+/* Maximum number of shared actions supported by rte_flow */\n+#define MLX5_MAX_SHARED_ACTIONS 1\n+\n /* Definition of static_assert found in /usr/include/assert.h */\n #ifndef HAVE_STATIC_ASSERT\n #define static_assert _Static_assert\ndiff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c\nindex 4a031a523b..5947b4b1ff 100644\n--- a/drivers/net/mlx5/mlx5_flow.c\n+++ b/drivers/net/mlx5/mlx5_flow.c\n@@ -231,6 +231,25 @@ static const struct rte_flow_expand_node mlx5_support_expansion[] = {\n \t},\n };\n \n+static struct rte_flow_shared_action *\n+mlx5_shared_action_create(struct rte_eth_dev *dev,\n+\t\t\t  const struct rte_flow_action *action,\n+\t\t\t  struct rte_flow_error *error);\n+static int mlx5_shared_action_destroy\n+\t\t\t\t(struct rte_eth_dev *dev,\n+\t\t\t\t struct rte_flow_shared_action *shared_action,\n+\t\t\t\t struct rte_flow_error *error);\n+static int mlx5_shared_action_update\n+\t\t\t\t(struct rte_eth_dev *dev,\n+\t\t\t\t struct rte_flow_shared_action *shared_action,\n+\t\t\t\t const struct rte_flow_action *action,\n+\t\t\t\t struct rte_flow_error *error);\n+static int mlx5_shared_action_query\n+\t\t\t\t(struct rte_eth_dev *dev,\n+\t\t\t\t const struct rte_flow_shared_action *action,\n+\t\t\t\t void *data,\n+\t\t\t\t struct rte_flow_error *error);\n+\n static const struct rte_flow_ops mlx5_flow_ops = {\n \t.validate = mlx5_flow_validate,\n \t.create = mlx5_flow_create,\n@@ -240,6 +259,10 @@ static const struct rte_flow_ops mlx5_flow_ops = {\n \t.query = mlx5_flow_query,\n \t.dev_dump = mlx5_flow_dev_dump,\n \t.get_aged_flows = mlx5_flow_get_aged_flows,\n+\t.shared_action_create = mlx5_shared_action_create,\n+\t.shared_action_destroy = mlx5_shared_action_destroy,\n+\t.shared_action_update = mlx5_shared_action_update,\n+\t.shared_action_query = mlx5_shared_action_query,\n };\n \n /* Convert FDIR request to Generic flow. */\n@@ -1117,16 +1140,10 @@ mlx5_flow_validate_action_queue(const struct rte_flow_action *action,\n /*\n  * Validate the rss action.\n  *\n- * @param[in] action\n- *   Pointer to the queue action.\n- * @param[in] action_flags\n- *   Bit-fields that holds the actions detected until now.\n  * @param[in] dev\n  *   Pointer to the Ethernet device structure.\n- * @param[in] attr\n- *   Attributes of flow that includes this action.\n- * @param[in] item_flags\n- *   Items that were detected.\n+ * @param[in] action\n+ *   Pointer to the queue action.\n  * @param[out] error\n  *   Pointer to error structure.\n  *\n@@ -1134,23 +1151,14 @@ mlx5_flow_validate_action_queue(const struct rte_flow_action *action,\n  *   0 on success, a negative errno value otherwise and rte_errno is set.\n  */\n int\n-mlx5_flow_validate_action_rss(const struct rte_flow_action *action,\n-\t\t\t      uint64_t action_flags,\n-\t\t\t      struct rte_eth_dev *dev,\n-\t\t\t      const struct rte_flow_attr *attr,\n-\t\t\t      uint64_t item_flags,\n-\t\t\t      struct rte_flow_error *error)\n+mlx5_validate_action_rss(struct rte_eth_dev *dev,\n+\t\t\t const struct rte_flow_action *action,\n+\t\t\t struct rte_flow_error *error)\n {\n \tstruct mlx5_priv *priv = dev->data->dev_private;\n \tconst struct rte_flow_action_rss *rss = action->conf;\n-\tint tunnel = !!(item_flags & MLX5_FLOW_LAYER_TUNNEL);\n \tunsigned int i;\n \n-\tif (action_flags & MLX5_FLOW_FATE_ACTIONS)\n-\t\treturn rte_flow_error_set(error, EINVAL,\n-\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION, NULL,\n-\t\t\t\t\t  \"can't have 2 fate actions\"\n-\t\t\t\t\t  \" in same flow\");\n \tif (rss->func != RTE_ETH_HASH_FUNCTION_DEFAULT &&\n \t    rss->func != RTE_ETH_HASH_FUNCTION_TOEPLITZ)\n \t\treturn rte_flow_error_set(error, ENOTSUP,\n@@ -1196,15 +1204,17 @@ mlx5_flow_validate_action_rss(const struct rte_flow_action *action,\n \tif ((rss->types & (ETH_RSS_L3_SRC_ONLY | ETH_RSS_L3_DST_ONLY)) &&\n \t    !(rss->types & ETH_RSS_IP))\n \t\treturn rte_flow_error_set(error, EINVAL,\n-\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,\n-\t\t\t\t\t  \"L3 partial RSS requested but L3 RSS\"\n-\t\t\t\t\t  \" type not specified\");\n+\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION_CONF,\n+\t\t\t\t\t  NULL,\n+\t\t\t\t\t  \"L3 partial RSS requested but L3 \"\n+\t\t\t\t\t  \"RSS type not specified\");\n \tif ((rss->types & (ETH_RSS_L4_SRC_ONLY | ETH_RSS_L4_DST_ONLY)) &&\n \t    !(rss->types & (ETH_RSS_UDP | ETH_RSS_TCP)))\n \t\treturn rte_flow_error_set(error, EINVAL,\n-\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,\n-\t\t\t\t\t  \"L4 partial RSS requested but L4 RSS\"\n-\t\t\t\t\t  \" type not specified\");\n+\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION_CONF,\n+\t\t\t\t\t  NULL,\n+\t\t\t\t\t  \"L4 partial RSS requested but L4 \"\n+\t\t\t\t\t  \"RSS type not specified\");\n \tif (!priv->rxqs_n)\n \t\treturn rte_flow_error_set(error, EINVAL,\n \t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION_CONF,\n@@ -1221,17 +1231,62 @@ mlx5_flow_validate_action_rss(const struct rte_flow_action *action,\n \t\t\t\t &rss->queue[i], \"queue index out of range\");\n \t\tif (!(*priv->rxqs)[rss->queue[i]])\n \t\t\treturn rte_flow_error_set\n-\t\t\t\t(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION_CONF,\n+\t\t\t\t(error, EINVAL,\n+\t\t\t\t RTE_FLOW_ERROR_TYPE_ACTION_CONF,\n \t\t\t\t &rss->queue[i], \"queue is not configured\");\n \t}\n+\treturn 0;\n+}\n+\n+/*\n+ * Validate the rss action.\n+ *\n+ * @param[in] action\n+ *   Pointer to the queue action.\n+ * @param[in] action_flags\n+ *   Bit-fields that holds the actions detected until now.\n+ * @param[in] dev\n+ *   Pointer to the Ethernet device structure.\n+ * @param[in] attr\n+ *   Attributes of flow that includes this action.\n+ * @param[in] item_flags\n+ *   Items that were detected.\n+ * @param[out] error\n+ *   Pointer to error structure.\n+ *\n+ * @return\n+ *   0 on success, a negative errno value otherwise and rte_errno is set.\n+ */\n+int\n+mlx5_flow_validate_action_rss(const struct rte_flow_action *action,\n+\t\t\t      uint64_t action_flags,\n+\t\t\t      struct rte_eth_dev *dev,\n+\t\t\t      const struct rte_flow_attr *attr,\n+\t\t\t      uint64_t item_flags,\n+\t\t\t      struct rte_flow_error *error)\n+{\n+\tconst struct rte_flow_action_rss *rss = action->conf;\n+\tint tunnel = !!(item_flags & MLX5_FLOW_LAYER_TUNNEL);\n+\tint ret;\n+\n+\tif (action_flags & MLX5_FLOW_FATE_ACTIONS)\n+\t\treturn rte_flow_error_set(error, EINVAL,\n+\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION, NULL,\n+\t\t\t\t\t  \"can't have 2 fate actions\"\n+\t\t\t\t\t  \" in same flow\");\n+\tret = mlx5_validate_action_rss(dev, action, error);\n+\tif (ret)\n+\t\treturn ret;\n \tif (attr->egress)\n \t\treturn rte_flow_error_set(error, ENOTSUP,\n-\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ATTR_EGRESS, NULL,\n+\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ATTR_EGRESS,\n+\t\t\t\t\t  NULL,\n \t\t\t\t\t  \"rss action not supported for \"\n \t\t\t\t\t  \"egress\");\n \tif (rss->level > 1 &&  !tunnel)\n \t\treturn rte_flow_error_set(error, EINVAL,\n-\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,\n+\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION_CONF,\n+\t\t\t\t\t  NULL,\n \t\t\t\t\t  \"inner RSS is not supported for \"\n \t\t\t\t\t  \"non-tunnel flows\");\n \treturn 0;\n@@ -2739,6 +2794,131 @@ flow_get_rss_action(const struct rte_flow_action actions[])\n \treturn NULL;\n }\n \n+/* maps shared action to translated non shared in some actions array */\n+struct mlx5_translated_shared_action {\n+\tstruct rte_flow_shared_action *action; /**< Shared action */\n+\tint index; /**< Index in related array of rte_flow_action */\n+};\n+\n+/**\n+ * Translates actions of type RTE_FLOW_ACTION_TYPE_SHARED to related\n+ * non shared action if translation possible.\n+ * This functionality used to run same execution path for both shared & non\n+ * shared actions on flow create. All necessary  preparations for shared\n+ * action handling should be preformed on *shared* actions list returned by\n+ * from this call.\n+ *\n+ * @param[in] actions\n+ *   List of actions to translate.\n+ * @param[out] shared\n+ *   List to store translated shared actions.\n+ * @param[in, out] shared_n\n+ *   Size of *shared* array. On return should be updated with number of shared\n+ *   actions retrieved from the *actions* list.\n+ * @param[out] translated_actions\n+ *   List of actions where all shared actions were translated to non shared\n+ *   if possible. NULL if no translation took place.\n+ * @param[out] error\n+ *   Pointer to the error structure.\n+ *\n+ * @return\n+ *   0 on success, a negative errno value otherwise and rte_errno is set.\n+ */\n+static int\n+flow_shared_actions_translate(const struct rte_flow_action actions[],\n+\tstruct mlx5_translated_shared_action *shared,\n+\tint *shared_n,\n+\tstruct rte_flow_action **translated_actions,\n+\tstruct rte_flow_error *error)\n+{\n+\tstruct rte_flow_action *translated = NULL;\n+\tint n;\n+\tint copied_n = 0;\n+\tstruct mlx5_translated_shared_action *shared_end = NULL;\n+\n+\tfor (n = 0; actions[n].type != RTE_FLOW_ACTION_TYPE_END; n++) {\n+\t\tif (actions[n].type != RTE_FLOW_ACTION_TYPE_SHARED)\n+\t\t\tcontinue;\n+\t\tif (copied_n == *shared_n) {\n+\t\t\treturn rte_flow_error_set\n+\t\t\t\t(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION_NUM,\n+\t\t\t\t NULL, \"too many shared actions\");\n+\t\t}\n+\t\trte_memcpy(&shared[copied_n].action, &actions[n].conf,\n+\t\t\t   sizeof(actions[n].conf));\n+\t\tshared[copied_n].index = n;\n+\t\tcopied_n++;\n+\t}\n+\tn++;\n+\t*shared_n = copied_n;\n+\tif (!copied_n)\n+\t\treturn 0;\n+\ttranslated = rte_calloc(__func__, n, sizeof(struct rte_flow_action), 0);\n+\trte_memcpy(translated, actions, n * sizeof(struct rte_flow_action));\n+\tfor (shared_end = shared + copied_n; shared < shared_end; shared++) {\n+\t\tconst struct rte_flow_shared_action *shared_action;\n+\n+\t\tshared_action = shared->action;\n+\t\tswitch (shared_action->type) {\n+\t\tcase MLX5_FLOW_ACTION_SHARED_RSS:\n+\t\t\ttranslated[shared->index].type =\n+\t\t\t\tRTE_FLOW_ACTION_TYPE_RSS;\n+\t\t\ttranslated[shared->index].conf =\n+\t\t\t\t&shared_action->rss.origin;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\trte_free(translated);\n+\t\t\treturn rte_flow_error_set\n+\t\t\t\t(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,\n+\t\t\t\t NULL, \"invalid shared action type\");\n+\t\t}\n+\t}\n+\t*translated_actions = translated;\n+\treturn 0;\n+}\n+\n+/**\n+ * Get Shared RSS action from the action list.\n+ *\n+ * @param[in] shared\n+ *   Pointer to the list of actions.\n+ * @param[in] shared_n\n+ *   Actions list length.\n+ *\n+ * @return\n+ *   Pointer to the MLX5 RSS action if exist, else return NULL.\n+ */\n+static struct mlx5_shared_action_rss *\n+flow_get_shared_rss_action(struct mlx5_translated_shared_action *shared,\n+\t\t\t   int shared_n)\n+{\n+\tstruct mlx5_translated_shared_action *shared_end;\n+\n+\tfor (shared_end = shared + shared_n; shared < shared_end; shared++) {\n+\t\tstruct rte_flow_shared_action *shared_action;\n+\n+\t\tshared_action = shared->action;\n+\t\tswitch (shared_action->type) {\n+\t\tcase MLX5_FLOW_ACTION_SHARED_RSS:\n+\t\t\trte_atomic32_inc(&shared_action->refcnt);\n+\t\t\treturn &shared_action->rss;\n+\t\tdefault:\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\treturn NULL;\n+}\n+\n+struct rte_flow_shared_action *\n+mlx5_flow_get_shared_rss(struct rte_flow *flow)\n+{\n+\tif (flow->shared_rss)\n+\t\treturn container_of(flow->shared_rss,\n+\t\t\t\t    struct rte_flow_shared_action, rss);\n+\telse\n+\t\treturn NULL;\n+}\n+\n static unsigned int\n find_graph_root(const struct rte_flow_item pattern[], uint32_t rss_level)\n {\n@@ -4328,13 +4508,16 @@ static uint32_t\n flow_list_create(struct rte_eth_dev *dev, uint32_t *list,\n \t\t const struct rte_flow_attr *attr,\n \t\t const struct rte_flow_item items[],\n-\t\t const struct rte_flow_action actions[],\n+\t\t const struct rte_flow_action original_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+\tstruct mlx5_translated_shared_action\n+\t\tshared_actions[MLX5_MAX_SHARED_ACTIONS];\n+\tint shared_actions_n = MLX5_MAX_SHARED_ACTIONS;\n \tunion {\n \t\tstruct rte_flow_expand_rss buf;\n \t\tuint8_t buffer[2048];\n@@ -4354,14 +4537,23 @@ flow_list_create(struct rte_eth_dev *dev, uint32_t *list,\n \tstruct rte_flow_expand_rss *buf = &expand_buffer.buf;\n \tstruct mlx5_flow_rss_desc *rss_desc = &((struct mlx5_flow_rss_desc *)\n \t\t\t\t\t      priv->rss_desc)[!!priv->flow_idx];\n-\tconst struct rte_flow_action *p_actions_rx = actions;\n+\tconst struct rte_flow_action *p_actions_rx;\n \tuint32_t i;\n \tuint32_t idx = 0;\n \tint hairpin_flow;\n \tuint32_t hairpin_id = 0;\n \tstruct rte_flow_attr attr_tx = { .priority = 0 };\n-\tint ret;\n+\tconst struct rte_flow_action *actions;\n+\tstruct rte_flow_action *translated_actions = NULL;\n+\tint ret = flow_shared_actions_translate(original_actions,\n+\t\t\t\t\t\tshared_actions,\n+\t\t\t\t\t\t&shared_actions_n,\n+\t\t\t\t\t\t&translated_actions, error);\n \n+\tif (ret < 0)\n+\t\treturn 0;\n+\tactions = (translated_actions) ? translated_actions : original_actions;\n+\tp_actions_rx = actions;\n \thairpin_flow = flow_check_hairpin_split(dev, attr, actions);\n \tret = flow_drv_validate(dev, attr, items, p_actions_rx,\n \t\t\t\texternal, hairpin_flow, error);\n@@ -4413,6 +4605,8 @@ flow_list_create(struct rte_eth_dev *dev, uint32_t *list,\n \t\tbuf->entries = 1;\n \t\tbuf->entry[0].pattern = (void *)(uintptr_t)items;\n \t}\n+\tflow->shared_rss = flow_get_shared_rss_action(shared_actions,\n+\t\t\t\t\t\t      shared_actions_n);\n \t/*\n \t * Record the start index when there is a nested call. All sub-flows\n \t * need to be translated before another calling.\n@@ -4484,6 +4678,7 @@ flow_list_create(struct rte_eth_dev *dev, uint32_t *list,\n \t\tILIST_INSERT(priv->sh->ipool[MLX5_IPOOL_RTE_FLOW], list, idx,\n \t\t\t     flow, next);\n \tflow_rxq_flags_set(dev, flow);\n+\trte_free(translated_actions);\n \t/* Nested flow creation index recovery. */\n \tpriv->flow_idx = priv->flow_nested_idx;\n \tif (priv->flow_nested_idx)\n@@ -4498,6 +4693,7 @@ flow_list_create(struct rte_eth_dev *dev, uint32_t *list,\n \trte_errno = ret; /* Restore rte_errno. */\n error_before_flow:\n \tret = rte_errno;\n+\trte_free(translated_actions);\n \tif (hairpin_id)\n \t\tmlx5_flow_id_release(priv->sh->flow_id_pool,\n \t\t\t\t     hairpin_id);\n@@ -6296,3 +6492,235 @@ mlx5_flow_get_aged_flows(struct rte_eth_dev *dev, void **contexts,\n \t\t dev->data->port_id);\n \treturn -ENOTSUP;\n }\n+\n+/**\n+ * Retrieve driver ops struct.\n+ *\n+ * @param[in] dev\n+ *   Pointer to the dev structure.\n+ * @param[in] error_message\n+ *   Error message to set if driver ops struct not found.\n+ * @param[out] error\n+ *   Perform verbose error reporting if not NULL. Initialized in case of\n+ *   error only.\n+ *\n+ * @return\n+ *   Pointer to driver ops on success, otherwise NULL and rte_errno is set.\n+ */\n+static const struct mlx5_flow_driver_ops *\n+flow_drv_dv_ops_get(struct rte_eth_dev *dev,\n+\t\t    const char *error_message,\n+\t\t    struct rte_flow_error *error)\n+{\n+\tstruct rte_flow_attr attr = { .transfer = 0 };\n+\n+\tif (flow_get_drv_type(dev, &attr) != MLX5_FLOW_TYPE_DV) {\n+\t\trte_flow_error_set(error, ENOTSUP,\n+\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION,\n+\t\t\t\t\t  NULL, error_message);\n+\t\tDRV_LOG(ERR, \"port %u %s.\", dev->data->port_id, error_message);\n+\t\treturn NULL;\n+\t}\n+\n+\treturn flow_get_drv_ops(MLX5_FLOW_TYPE_DV);\n+}\n+\n+/* Wrapper for driver action_validate op callback */\n+static int\n+flow_drv_action_validate(struct rte_eth_dev *dev,\n+\t\t\t const struct rte_flow_action *action,\n+\t\t\t struct rte_flow_error *error)\n+{\n+\tconst struct mlx5_flow_driver_ops *fops = flow_drv_dv_ops_get(dev,\n+\t\t\"action registration unsupported\", error);\n+\treturn (fops) ? fops->action_validate(dev, action, error) : -rte_errno;\n+}\n+\n+/* Wrapper for driver action_create op callback */\n+static struct rte_flow_shared_action *\n+flow_drv_action_create(struct rte_eth_dev *dev,\n+\t\t      const struct rte_flow_action *action,\n+\t\t      struct rte_flow_error *error)\n+{\n+\tconst struct mlx5_flow_driver_ops *fops = flow_drv_dv_ops_get(dev,\n+\t\t\"action registration unsupported\", error);\n+\treturn (fops) ? fops->action_create(dev, action, error) : NULL;\n+}\n+\n+/**\n+ * Destroys the shared action by handle.\n+ *\n+ * @param dev\n+ *   Pointer to Ethernet device structure.\n+ * @param[in] action\n+ *   Handle for the shared action to be destroyed.\n+ * @param[out] error\n+ *   Perform verbose error reporting if not NULL. PMDs initialize this\n+ *   structure in case of error only.\n+ *\n+ * @return\n+ *   0 on success, a negative errno value otherwise and rte_errno is set.\n+ *\n+ * @note: wrapper for driver action_create op callback.\n+ */\n+static int\n+mlx5_shared_action_destroy(struct rte_eth_dev *dev,\n+\t\t\t   struct rte_flow_shared_action *action,\n+\t\t\t   struct rte_flow_error *error)\n+{\n+\tconst struct mlx5_flow_driver_ops *fops = flow_drv_dv_ops_get(dev,\n+\t\t\"action registration unsupported\", error);\n+\treturn (fops) ? fops->action_destroy(dev, action, error) : -rte_errno;\n+}\n+\n+/* Wrapper for driver action_destroy op callback */\n+static int\n+flow_drv_action_update(struct rte_eth_dev *dev,\n+\t\t       struct rte_flow_shared_action *action,\n+\t\t       const void *action_conf,\n+\t\t       struct rte_flow_error *error)\n+{\n+\tconst struct mlx5_flow_driver_ops *fops = flow_drv_dv_ops_get(dev,\n+\t\t\"action registration unsupported\", error);\n+\treturn (fops) ? fops->action_update(dev, action,\n+\t\t\t\t\t    action_conf, error)\n+\t\t      : -rte_errno;\n+}\n+\n+/**\n+ * Create shared action for reuse in multiple flow rules.\n+ *\n+ * @param dev\n+ *   Pointer to Ethernet device structure.\n+ * @param[in] action\n+ *   Action configuration for shared action creation.\n+ * @param[out] error\n+ *   Perform verbose error reporting if not NULL. PMDs initialize this\n+ *   structure in case of error only.\n+ * @return\n+ *   A valid handle in case of success, NULL otherwise and rte_errno is set.\n+ */\n+static struct rte_flow_shared_action *\n+mlx5_shared_action_create(struct rte_eth_dev *dev,\n+\t\t\tconst struct rte_flow_action *action,\n+\t\t\tstruct rte_flow_error *error)\n+{\n+\tif (flow_drv_action_validate(dev, action, error))\n+\t\treturn NULL;\n+\treturn flow_drv_action_create(dev, action, error);\n+}\n+\n+/**\n+ * Updates inplace the shared action configuration pointed by *action* handle\n+ * with the configuration provided as *update* argument.\n+ * The update of the shared action configuration effects all flow rules reusing\n+ * the action via handle.\n+ *\n+ * @param dev\n+ *   Pointer to Ethernet device structure.\n+ * @param[in] action\n+ *   Handle for the shared action to be updated.\n+ * @param[in] update\n+ *   Action specification used to modify the action pointed by handle.\n+ *   *update* should be of same type with the action pointed by the *action*\n+ *   handle argument, otherwise considered as invalid.\n+ * @param[out] error\n+ *   Perform verbose error reporting if not NULL. PMDs initialize this\n+ *   structure in case of error only.\n+ *\n+ * @return\n+ *   0 on success, a negative errno value otherwise and rte_errno is set.\n+ */\n+static int\n+mlx5_shared_action_update(struct rte_eth_dev *dev,\n+\t\tstruct rte_flow_shared_action *shared_action,\n+\t\tconst struct rte_flow_action *action,\n+\t\tstruct rte_flow_error *error)\n+{\n+\tint ret;\n+\n+\tswitch (shared_action->type) {\n+\tcase MLX5_FLOW_ACTION_SHARED_RSS:\n+\t\tif (action->type != RTE_FLOW_ACTION_TYPE_RSS) {\n+\t\t\treturn rte_flow_error_set(error, EINVAL,\n+\t\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION,\n+\t\t\t\t\t\t  NULL,\n+\t\t\t\t\t\t  \"update action type invalid\");\n+\t\t}\n+\t\tret = flow_drv_action_validate(dev, action, error);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\treturn flow_drv_action_update(dev, shared_action, action->conf,\n+\t\t\t\t\t      error);\n+\tdefault:\n+\t\treturn rte_flow_error_set(error, ENOTSUP,\n+\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION,\n+\t\t\t\t\t  NULL,\n+\t\t\t\t\t  \"action type not supported\");\n+\t}\n+}\n+\n+/**\n+ * Query the shared action by handle.\n+ *\n+ * This function allows retrieving action-specific data such as counters.\n+ * Data is gathered by special action which may be present/referenced in\n+ * more than one flow rule definition.\n+ *\n+ * \\see RTE_FLOW_ACTION_TYPE_COUNT\n+ *\n+ * @param dev\n+ *   Pointer to Ethernet device structure.\n+ * @param[in] action\n+ *   Handle for the shared action to query.\n+ * @param[in, out] data\n+ *   Pointer to storage for the associated query data type.\n+ * @param[out] error\n+ *   Perform verbose error reporting if not NULL. PMDs initialize this\n+ *   structure in case of error only.\n+ *\n+ * @return\n+ *   0 on success, a negative errno value otherwise and rte_errno is set.\n+ */\n+static int\n+mlx5_shared_action_query(struct rte_eth_dev *dev,\n+\t\t\t const struct rte_flow_shared_action *action,\n+\t\t\t void *data,\n+\t\t\t struct rte_flow_error *error)\n+{\n+\t(void)dev;\n+\tswitch (action->type) {\n+\tcase MLX5_FLOW_ACTION_SHARED_RSS:\n+\t\t*((int32_t *)data) = rte_atomic32_read(&action->refcnt);\n+\t\treturn 0;\n+\tdefault:\n+\t\treturn rte_flow_error_set(error, ENOTSUP,\n+\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION,\n+\t\t\t\t\t  NULL,\n+\t\t\t\t\t  \"action type not supported\");\n+\t}\n+}\n+\n+/**\n+ * Destroy all shared actions.\n+ *\n+ * @param dev\n+ *   Pointer to Ethernet device.\n+ *\n+ * @return\n+ *   0 on success, a negative errno value otherwise and rte_errno is set.\n+ */\n+int\n+mlx5_shared_action_flush(struct rte_eth_dev *dev)\n+{\n+\tstruct rte_flow_error error;\n+\tstruct mlx5_priv *priv = dev->data->dev_private;\n+\tstruct rte_flow_shared_action *action;\n+\tint ret = 0;\n+\n+\twhile (!LIST_EMPTY(&priv->shared_actions)) {\n+\t\taction = LIST_FIRST(&priv->shared_actions);\n+\t\tret = mlx5_shared_action_destroy(dev, action, &error);\n+\t}\n+\treturn ret;\n+}\ndiff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h\nindex 50ec741157..fec47c4372 100644\n--- a/drivers/net/mlx5/mlx5_flow.h\n+++ b/drivers/net/mlx5/mlx5_flow.h\n@@ -202,6 +202,7 @@ enum mlx5_feature_name {\n #define MLX5_FLOW_ACTION_SET_IPV6_DSCP (1ull << 33)\n #define MLX5_FLOW_ACTION_AGE (1ull << 34)\n #define MLX5_FLOW_ACTION_DEFAULT_MISS (1ull << 35)\n+#define MLX5_FLOW_ACTION_SHARED_RSS (1ull << 36)\n \n #define MLX5_FLOW_FATE_ACTIONS \\\n \t(MLX5_FLOW_ACTION_DROP | MLX5_FLOW_ACTION_QUEUE | \\\n@@ -821,6 +822,7 @@ struct mlx5_fdir_flow {\n /* Flow structure. */\n struct rte_flow {\n \tILIST_ENTRY(uint32_t)next; /**< Index to the next flow structure. */\n+\tstruct mlx5_shared_action_rss *shared_rss; /** < Shred RSS action. */\n \tuint32_t dev_handles;\n \t/**< Device flow handles that are part of the flow. */\n \tuint32_t drv_type:2; /**< Driver type. */\n@@ -834,6 +836,62 @@ struct rte_flow {\n \tuint16_t meter; /**< Holds flow meter id. */\n } __rte_packed;\n \n+/*\n+ * Define list of valid combinations of RX Hash fields\n+ * (see enum ibv_rx_hash_fields).\n+ */\n+#define MLX5_RSS_HASH_IPV4 (IBV_RX_HASH_SRC_IPV4 | IBV_RX_HASH_DST_IPV4)\n+#define MLX5_RSS_HASH_IPV4_TCP \\\n+\t(MLX5_RSS_HASH_IPV4 | \\\n+\t IBV_RX_HASH_SRC_PORT_TCP | IBV_RX_HASH_SRC_PORT_TCP)\n+#define MLX5_RSS_HASH_IPV4_UDP \\\n+\t(MLX5_RSS_HASH_IPV4 | \\\n+\t IBV_RX_HASH_SRC_PORT_UDP | IBV_RX_HASH_SRC_PORT_UDP)\n+#define MLX5_RSS_HASH_IPV6 (IBV_RX_HASH_SRC_IPV6 | IBV_RX_HASH_DST_IPV6)\n+#define MLX5_RSS_HASH_IPV6_TCP \\\n+\t(MLX5_RSS_HASH_IPV6 | \\\n+\t IBV_RX_HASH_SRC_PORT_TCP | IBV_RX_HASH_SRC_PORT_TCP)\n+#define MLX5_RSS_HASH_IPV6_UDP \\\n+\t(MLX5_RSS_HASH_IPV6 | \\\n+\t IBV_RX_HASH_SRC_PORT_UDP | IBV_RX_HASH_SRC_PORT_UDP)\n+#define MLX5_RSS_HASH_NONE 0ULL\n+\n+/* array of valid combinations of RX Hash fields for RSS */\n+static const uint64_t mlx5_rss_hash_fields[] = {\n+\tMLX5_RSS_HASH_IPV4,\n+\tMLX5_RSS_HASH_IPV4_TCP,\n+\tMLX5_RSS_HASH_IPV4_UDP,\n+\tMLX5_RSS_HASH_IPV6,\n+\tMLX5_RSS_HASH_IPV6_TCP,\n+\tMLX5_RSS_HASH_IPV6_UDP,\n+\tMLX5_RSS_HASH_NONE,\n+};\n+\n+#define MLX5_RSS_HASH_FIELDS_LEN RTE_DIM(mlx5_rss_hash_fields)\n+\n+/* Shared RSS action structure */\n+struct mlx5_shared_action_rss {\n+\tstruct rte_flow_action_rss origin; /**< Original rte RSS action. */\n+\tuint8_t key[MLX5_RSS_HASH_KEY_LEN]; /**< RSS hash key. */\n+\tuint16_t *queue; /**< Queue indices to use. */\n+\tuint32_t hrxq[MLX5_RSS_HASH_FIELDS_LEN];\n+\t/**< Hash RX queue indexes mapped to mlx5_rss_hash_fields */\n+\tuint32_t hrxq_tunnel[MLX5_RSS_HASH_FIELDS_LEN];\n+\t/**< Hash RX queue indexes for tunneled RSS */\n+};\n+\n+struct rte_flow_shared_action {\n+\tLIST_ENTRY(rte_flow_shared_action) next;\n+\t\t/**< Pointer to the next element. */\n+\trte_atomic32_t refcnt;\n+\tuint64_t type;\n+\t\t/**< Shared action type (see MLX5_FLOW_ACTION_SHARED_*). */\n+\tunion {\n+\t\tstruct mlx5_shared_action_rss rss;\n+\t\t\t/**< Shared RSS action. */\n+\t};\n+};\n+\n typedef int (*mlx5_flow_validate_t)(struct rte_eth_dev *dev,\n \t\t\t\t    const struct rte_flow_attr *attr,\n \t\t\t\t    const struct rte_flow_item items[],\n@@ -888,6 +946,22 @@ typedef int (*mlx5_flow_get_aged_flows_t)\n \t\t\t\t\t void **context,\n \t\t\t\t\t uint32_t nb_contexts,\n \t\t\t\t\t struct rte_flow_error *error);\n+typedef int (*mlx5_flow_action_validate_t)(struct rte_eth_dev *dev,\n+\t\t\t\t\t   const struct rte_flow_action *action,\n+\t\t\t\t\t   struct rte_flow_error *error);\n+typedef struct rte_flow_shared_action *(*mlx5_flow_action_create_t)\n+\t\t\t\t(struct rte_eth_dev *dev,\n+\t\t\t\t const struct rte_flow_action *action,\n+\t\t\t\t struct rte_flow_error *error);\n+typedef int (*mlx5_flow_action_destroy_t)\n+\t\t\t\t(struct rte_eth_dev *dev,\n+\t\t\t\t struct rte_flow_shared_action *action,\n+\t\t\t\t struct rte_flow_error *error);\n+typedef int (*mlx5_flow_action_update_t)\n+\t\t\t(struct rte_eth_dev *dev,\n+\t\t\t struct rte_flow_shared_action *action,\n+\t\t\t const void *action_conf,\n+\t\t\t struct rte_flow_error *error);\n struct mlx5_flow_driver_ops {\n \tmlx5_flow_validate_t validate;\n \tmlx5_flow_prepare_t prepare;\n@@ -904,6 +978,10 @@ struct mlx5_flow_driver_ops {\n \tmlx5_flow_counter_free_t counter_free;\n \tmlx5_flow_counter_query_t counter_query;\n \tmlx5_flow_get_aged_flows_t get_aged_flows;\n+\tmlx5_flow_action_validate_t action_validate;\n+\tmlx5_flow_action_create_t action_create;\n+\tmlx5_flow_action_destroy_t action_destroy;\n+\tmlx5_flow_action_update_t action_update;\n };\n \n /* mlx5_flow.c */\n@@ -928,6 +1006,9 @@ int mlx5_flow_get_reg_id(struct rte_eth_dev *dev,\n const struct rte_flow_action *mlx5_flow_find_action\n \t\t\t\t\t(const struct rte_flow_action *actions,\n \t\t\t\t\t enum rte_flow_action_type action);\n+int mlx5_validate_action_rss(struct rte_eth_dev *dev,\n+\t\t\t     const struct rte_flow_action *action,\n+\t\t\t     struct rte_flow_error *error);\n int mlx5_flow_validate_action_count(struct rte_eth_dev *dev,\n \t\t\t\t    const struct rte_flow_attr *attr,\n \t\t\t\t    struct rte_flow_error *error);\n@@ -1040,4 +1121,6 @@ int mlx5_flow_destroy_policer_rules(struct rte_eth_dev *dev,\n \t\t\t\t    const struct rte_flow_attr *attr);\n int mlx5_flow_meter_flush(struct rte_eth_dev *dev,\n \t\t\t  struct rte_mtr_error *error);\n+struct rte_flow_shared_action *mlx5_flow_get_shared_rss(struct rte_flow *flow);\n+int mlx5_shared_action_flush(struct rte_eth_dev *dev);\n #endif /* RTE_PMD_MLX5_FLOW_H_ */\n",
    "prefixes": [
        "v2",
        "4/6"
    ]
}