get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 81934,
    "url": "http://patches.dpdk.org/api/patches/81934/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20201023102410.20317-4-andreyv@nvidia.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": "<20201023102410.20317-4-andreyv@nvidia.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20201023102410.20317-4-andreyv@nvidia.com",
    "date": "2020-10-23T10:24:09",
    "name": "[v2,3/4] net/mlx5: shared action PMD",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": true,
    "hash": "ce36b6ec17a92eb484d2bee1bcccdae822ac34d1",
    "submitter": {
        "id": 1969,
        "url": "http://patches.dpdk.org/api/people/1969/?format=api",
        "name": "Andrey Vesnovaty",
        "email": "andreyv@nvidia.com"
    },
    "delegate": {
        "id": 3268,
        "url": "http://patches.dpdk.org/api/users/3268/?format=api",
        "username": "rasland",
        "first_name": "Raslan",
        "last_name": "Darawsheh",
        "email": "rasland@nvidia.com"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20201023102410.20317-4-andreyv@nvidia.com/mbox/",
    "series": [
        {
            "id": 13264,
            "url": "http://patches.dpdk.org/api/series/13264/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=13264",
            "date": "2020-10-23T10:24:06",
            "name": "Shared action RSS PMD impl",
            "version": 2,
            "mbox": "http://patches.dpdk.org/series/13264/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/81934/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/81934/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from dpdk.org (dpdk.org [92.243.14.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 5C0CBA04DE;\n\tFri, 23 Oct 2020 12:25:04 +0200 (CEST)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id D862BA956;\n\tFri, 23 Oct 2020 12:24:34 +0200 (CEST)",
            "from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129])\n by dpdk.org (Postfix) with ESMTP id 64849A94C\n for <dev@dpdk.org>; Fri, 23 Oct 2020 12:24:25 +0200 (CEST)",
            "from Internal Mail-Server by MTLPINE1 (envelope-from\n andreyv@nvidia.com) with SMTP; 23 Oct 2020 13:24:21 +0300",
            "from nvidia.com (r-arch-host11.mtr.labs.mlnx [10.213.43.60])\n by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id 09NAOGCx009185;\n Fri, 23 Oct 2020 13:24:21 +0300"
        ],
        "From": "Andrey Vesnovaty <andreyv@nvidia.com>",
        "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@nvidia.com, viacheslavo@nvidia.com,\n andrey.vesnovaty@gmail.com, mdr@ashroe.eu, nhorman@tuxdriver.com,\n ajit.khaparde@broadcom.com, samik.gupta@broadcom.com,\n Matan Azrad <matan@nvidia.com>, Shahaf Shuler <shahafs@nvidia.com>",
        "Date": "Fri, 23 Oct 2020 13:24:09 +0300",
        "Message-Id": "<20201023102410.20317-4-andreyv@nvidia.com>",
        "X-Mailer": "git-send-email 2.26.2",
        "In-Reply-To": "<20201023102410.20317-1-andreyv@nvidia.com>",
        "References": "<20201008121848.15330-1-andreyv@nvidia.com>\n <20201023102410.20317-1-andreyv@nvidia.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[dpdk-dev] [PATCH v2 3/4] net/mlx5: shared action PMD",
        "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 <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 <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "Handle shared action on flow validation/creation/destruction.\nmlx5 PMD translates shared action into a regular one before handling\nflow validation/creation. The shared action translation applied to\nutilize the same execution path for both shared and regular actions.\nThe current implementation supports shared action translation for shared\nRSS action only.\n\nRSS action validation split to validate shared RSS action on its creation\nin addition to action validation in flow validation/creation path.\n\nImplement rte_flow shared action API for mlx5 PMD, mostly forwarding\ncalls to flow driver operations (see struct mlx5_flow_driver_ops).\n\nSigned-off-by: Andrey Vesnovaty <andreyv@nvidia.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 | 499 +++++++++++++++++++++++++++++++++--\n drivers/net/mlx5/mlx5_flow.h |  86 ++++++\n 5 files changed, 563 insertions(+), 28 deletions(-)",
    "diff": "diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c\nindex e4ce9a9cb7..2484251b2f 100644\n--- a/drivers/net/mlx5/mlx5.c\n+++ b/drivers/net/mlx5/mlx5.c\n@@ -1401,6 +1401,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 9be3061165..658533de6f 100644\n--- a/drivers/net/mlx5/mlx5.h\n+++ b/drivers/net/mlx5/mlx5.h\n@@ -891,6 +891,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 0df47391ee..22e41df1eb 100644\n--- a/drivers/net/mlx5/mlx5_defs.h\n+++ b/drivers/net/mlx5/mlx5_defs.h\n@@ -197,6 +197,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 d7243a878b..6077685430 100644\n--- a/drivers/net/mlx5/mlx5_flow.c\n+++ b/drivers/net/mlx5/mlx5_flow.c\n@@ -548,6 +548,26 @@ static const struct mlx5_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_shared_action_conf *conf,\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@@ -557,6 +577,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@@ -1326,16 +1350,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@@ -1343,23 +1361,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@@ -1433,6 +1442,48 @@ mlx5_flow_validate_action_rss(const struct rte_flow_action *action,\n \t\t\t\t(error, EINVAL, 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@@ -3084,6 +3135,138 @@ 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\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+\tsize_t actions_size;\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+\tactions_size = sizeof(struct rte_flow_action) * n;\n+\ttranslated = mlx5_malloc(MLX5_MEM_ZERO, actions_size, 0, SOCKET_ID_ANY);\n+\tif (!translated) {\n+\t\trte_errno = ENOMEM;\n+\t\treturn -ENOMEM;\n+\t}\n+\tmemcpy(translated, actions, actions_size);\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_RTE_FLOW_ACTION_TYPE_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\tmlx5_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 exists, otherwise 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_RTE_FLOW_ACTION_TYPE_SHARED_RSS:\n+\t\t\t__atomic_add_fetch(&shared_action->refcnt, 1,\n+\t\t\t\t\t   __ATOMIC_RELAXED);\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@@ -5032,13 +5215,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 mlx5_flow_expand_rss buf;\n \t\tuint8_t buffer[2048];\n@@ -5058,27 +5244,38 @@ flow_list_create(struct rte_eth_dev *dev, uint32_t *list,\n \tstruct mlx5_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 \tstruct rte_flow_attr attr_factor = {0};\n-\tint ret;\n-\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\tMLX5_ASSERT(translated_actions == NULL);\n+\t\treturn 0;\n+\t}\n+\tactions = translated_actions ? translated_actions : original_actions;\n \tmemcpy((void *)&attr_factor, (const void *)attr, sizeof(*attr));\n \tif (external)\n \t\tattr_factor.group *= MLX5_FLOW_TABLE_FACTOR;\n+\tp_actions_rx = actions;\n \thairpin_flow = flow_check_hairpin_split(dev, &attr_factor, actions);\n \tret = flow_drv_validate(dev, &attr_factor, items, p_actions_rx,\n \t\t\t\texternal, hairpin_flow, error);\n \tif (ret < 0)\n-\t\treturn 0;\n+\t\tgoto error_before_hairpin_split;\n \tif (hairpin_flow > 0) {\n \t\tif (hairpin_flow > MLX5_MAX_SPLIT_ACTIONS) {\n \t\t\trte_errno = EINVAL;\n-\t\t\treturn 0;\n+\t\t\tgoto error_before_hairpin_split;\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@@ -5120,6 +5317,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@@ -5191,6 +5390,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@@ -5212,6 +5412,8 @@ flow_list_create(struct rte_eth_dev *dev, uint32_t *list,\n \tpriv->flow_idx = priv->flow_nested_idx;\n \tif (priv->flow_nested_idx)\n \t\tpriv->flow_nested_idx = 0;\n+error_before_hairpin_split:\n+\trte_free(translated_actions);\n \treturn 0;\n }\n \n@@ -5275,14 +5477,28 @@ int\n mlx5_flow_validate(struct rte_eth_dev *dev,\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   struct rte_flow_error *error)\n {\n \tint hairpin_flow;\n+\tstruct mlx5_translated_shared_action\n+\t\tshared_actions[MLX5_MAX_SHARED_ACTIONS];\n+\tint shared_actions_n = MLX5_MAX_SHARED_ACTIONS;\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)\n+\t\treturn ret;\n+\tactions = translated_actions ? translated_actions : original_actions;\n \thairpin_flow = flow_check_hairpin_split(dev, attr, actions);\n-\treturn flow_drv_validate(dev, attr, items, actions,\n+\tret = flow_drv_validate(dev, attr, items, actions,\n \t\t\t\ttrue, hairpin_flow, error);\n+\trte_free(translated_actions);\n+\treturn ret;\n }\n \n /**\n@@ -7081,3 +7297,230 @@ 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+/* 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_shared_action_conf *conf,\n+\t\t\t const struct rte_flow_action *action,\n+\t\t\t const struct mlx5_flow_driver_ops *fops,\n+\t\t\t struct rte_flow_error *error)\n+{\n+\tstatic const char err_msg[] = \"shared action validation unsupported\";\n+\n+\tif (!fops->action_validate) {\n+\t\tDRV_LOG(ERR, \"port %u %s.\", dev->data->port_id, err_msg);\n+\t\trte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION,\n+\t\t\t\t   NULL, err_msg);\n+\t\treturn -rte_errno;\n+\t}\n+\treturn fops->action_validate(dev, conf, action, error);\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+\tstatic const char err_msg[] = \"shared action destruction unsupported\";\n+\tstruct rte_flow_attr attr = { .transfer = 0 };\n+\tconst struct mlx5_flow_driver_ops *fops =\n+\t\t\tflow_get_drv_ops(flow_get_drv_type(dev, &attr));\n+\n+\tif (!fops->action_destroy) {\n+\t\tDRV_LOG(ERR, \"port %u %s.\", dev->data->port_id, err_msg);\n+\t\trte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION,\n+\t\t\t\t   NULL, err_msg);\n+\t\treturn -rte_errno;\n+\t}\n+\treturn fops->action_destroy(dev, action, error);\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       const struct mlx5_flow_driver_ops *fops,\n+\t\t       struct rte_flow_error *error)\n+{\n+\tstatic const char err_msg[] = \"shared action update unsupported\";\n+\n+\tif (!fops->action_update) {\n+\t\tDRV_LOG(ERR, \"port %u %s.\", dev->data->port_id, err_msg);\n+\t\trte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION,\n+\t\t\t\t   NULL, err_msg);\n+\t\treturn -rte_errno;\n+\t}\n+\treturn fops->action_update(dev, action, action_conf, error);\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\t  const struct rte_flow_shared_action_conf *conf,\n+\t\t\t  const struct rte_flow_action *action,\n+\t\t\t  struct rte_flow_error *error)\n+{\n+\tstatic const char err_msg[] = \"shared action creation unsupported\";\n+\tstruct rte_flow_attr attr = { .transfer = 0 };\n+\tconst struct mlx5_flow_driver_ops *fops =\n+\t\t\tflow_get_drv_ops(flow_get_drv_type(dev, &attr));\n+\n+\tif (flow_drv_action_validate(dev, conf, action, fops, error))\n+\t\treturn NULL;\n+\tif (!fops->action_create) {\n+\t\tDRV_LOG(ERR, \"port %u %s.\", dev->data->port_id, err_msg);\n+\t\trte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION,\n+\t\t\t\t   NULL, err_msg);\n+\t\treturn NULL;\n+\t}\n+\treturn fops->action_create(dev, conf, action, error);\n+}\n+\n+/**\n+ * Updates inplace the shared action configuration pointed by *action* handle\n+ * with the configuration provided as *action* 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] shared_action\n+ *   Handle for the shared action to be updated.\n+ * @param[in] action\n+ *   Action specification used to modify the action pointed by handle.\n+ *   *action* 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+\tstruct rte_flow_attr attr = { .transfer = 0 };\n+\tconst struct mlx5_flow_driver_ops *fops =\n+\t\t\tflow_get_drv_ops(flow_get_drv_type(dev, &attr));\n+\tint ret;\n+\n+\tswitch (shared_action->type) {\n+\tcase MLX5_RTE_FLOW_ACTION_TYPE_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, NULL, action, fops, 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      fops, 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_RTE_FLOW_ACTION_TYPE_SHARED_RSS:\n+\t\t__atomic_load(&action->refcnt, (uint32_t *)data,\n+\t\t\t      __ATOMIC_RELAXED);\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 b4be4769ef..7faab43fe6 100644\n--- a/drivers/net/mlx5/mlx5_flow.h\n+++ b/drivers/net/mlx5/mlx5_flow.h\n@@ -35,6 +35,7 @@ enum mlx5_rte_flow_action_type {\n \tMLX5_RTE_FLOW_ACTION_TYPE_MARK,\n \tMLX5_RTE_FLOW_ACTION_TYPE_COPY_MREG,\n \tMLX5_RTE_FLOW_ACTION_TYPE_DEFAULT_MISS,\n+\tMLX5_RTE_FLOW_ACTION_TYPE_SHARED_RSS,\n };\n \n /* Matches on selected register. */\n@@ -916,6 +917,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@@ -929,6 +931,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+\tuint32_t refcnt; /**< Atomically accessed 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@@ -983,6 +1041,25 @@ 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)\n+\t\t\t\t(struct rte_eth_dev *dev,\n+\t\t\t\t const struct rte_flow_shared_action_conf *conf,\n+\t\t\t\t const struct rte_flow_action *action,\n+\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_shared_action_conf *conf,\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@@ -999,6 +1076,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@@ -1024,6 +1105,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@@ -1145,4 +1229,6 @@ int mlx5_flow_destroy_policer_rules(struct rte_eth_dev *dev,\n int mlx5_flow_meter_flush(struct rte_eth_dev *dev,\n \t\t\t  struct rte_mtr_error *error);\n int mlx5_flow_dv_discover_counter_offset_support(struct rte_eth_dev *dev);\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",
        "3/4"
    ]
}