get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 131992,
    "url": "https://patches.dpdk.org/api/patches/131992/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/20230927103605.6022-1-ivan.malov@arknetworks.am/",
    "project": {
        "id": 1,
        "url": "https://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": "<20230927103605.6022-1-ivan.malov@arknetworks.am>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230927103605.6022-1-ivan.malov@arknetworks.am",
    "date": "2023-09-27T10:36:05",
    "name": "net/sfc: support packet replay in transfer flows",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": true,
    "hash": "5581417686129226145849863bdf5d09fb70bfab",
    "submitter": {
        "id": 2962,
        "url": "https://patches.dpdk.org/api/people/2962/?format=api",
        "name": "Ivan Malov",
        "email": "ivan.malov@arknetworks.am"
    },
    "delegate": {
        "id": 319,
        "url": "https://patches.dpdk.org/api/users/319/?format=api",
        "username": "fyigit",
        "first_name": "Ferruh",
        "last_name": "Yigit",
        "email": "ferruh.yigit@amd.com"
    },
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/20230927103605.6022-1-ivan.malov@arknetworks.am/mbox/",
    "series": [
        {
            "id": 29652,
            "url": "https://patches.dpdk.org/api/series/29652/?format=api",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=29652",
            "date": "2023-09-27T10:36:05",
            "name": "net/sfc: support packet replay in transfer flows",
            "version": 1,
            "mbox": "https://patches.dpdk.org/series/29652/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/131992/comments/",
    "check": "warning",
    "checks": "https://patches.dpdk.org/api/patches/131992/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 mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 35C0842651;\n\tWed, 27 Sep 2023 12:36:15 +0200 (CEST)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id B3A0740277;\n\tWed, 27 Sep 2023 12:36:14 +0200 (CEST)",
            "from agw.arknetworks.am (agw.arknetworks.am [79.141.165.80])\n by mails.dpdk.org (Postfix) with ESMTP id 4AEAC40271\n for <dev@dpdk.org>; Wed, 27 Sep 2023 12:36:13 +0200 (CEST)",
            "from localhost.localdomain (unknown [78.109.75.132])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by agw.arknetworks.am (Postfix) with ESMTPSA id 750A7E0E24;\n Wed, 27 Sep 2023 14:36:12 +0400 (+04)"
        ],
        "DKIM-Filter": "OpenDKIM Filter v2.11.0 agw.arknetworks.am 750A7E0E24",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=simple/simple; d=arknetworks.am;\n s=default; t=1695810972;\n bh=yXttJAU55q91mmlmM96V19I1PcxntntFZfmUyuibEK0=;\n h=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n b=pFes5MAMP+5pj0XHWw1hn4j12LYsPZPwtKHCoaqjMRxPjcrbKcIReAcQ6xr2paVWw\n dEcZslWX3vuWaxh9wTafYtgQL8ccbIm6931wQDjit6tolR8Rlr9CtIAQM0Re7t0oFr\n n1DbeN6gNnkab6p5Ly/oinN271HyXtwmY80Lzq/L5aekt5RhCMBU3dxOhcm4SgHTRi\n 4lZC2LIP4BKPlpdisSAjMtDd5q+CJzVFVk+LjhCjZs6Q0tV01P3hwdQZSPbIWfTK0F\n ZCTffNuAOLx8ETRlYpX0DxaGGyrjzR1A7Zd4JUkweS0o5U/OtRz1reF0theq6+r/1s\n 8t4+vQ2cmjTzw==",
        "From": "Ivan Malov <ivan.malov@arknetworks.am>",
        "To": "dev@dpdk.org",
        "Cc": "Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>,\n Ferruh Yigit <ferruh.yigit@amd.com>, Andy Moreton <andy.moreton@amd.com>",
        "Subject": "[PATCH] net/sfc: support packet replay in transfer flows",
        "Date": "Wed, 27 Sep 2023 14:36:05 +0400",
        "Message-Id": "<20230927103605.6022-1-ivan.malov@arknetworks.am>",
        "X-Mailer": "git-send-email 2.39.2",
        "In-Reply-To": "<20230810182820.6365-1-ivan.malov@arknetworks.am>",
        "References": "<20230810182820.6365-1-ivan.malov@arknetworks.am>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "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"
    },
    "content": "Packet replay enables users to leverage multiple counters in\none flow and allows to request delivery to multiple ports.\n\nA given flow rule may use either one inline count action\nand multiple indirect counters or just multiple indirect\ncounters. The inline count action (if any) must come\nbefore the first delivery action or before the first\nindirect count action, whichever comes earlier.\n\nThese are some testpmd examples of supported\nmulti-count and mirroring use cases:\n\nflow create 0 transfer pattern represented_port ethdev_port_id is 0 / end \\\n actions port_representor port_id 0 / port_representor port_id 1 / end\n\nor\n\nflow indirect_action 0 create action_id 239 transfer action count / end\n\nflow create 0 transfer pattern represented_port ethdev_port_id is 0 / end \\\n actions count / port_representor port_id 0 / indirect 239 / \\\n port_representor port_id 1 / end\n\nor\n\nflow indirect_action 0 create action_id 239 transfer action count / end\n\nflow create 0 transfer pattern represented_port ethdev_port_id is 0 / end \\\n actions indirect 239 / port_representor port_id 0 / indirect 239 / \\\n port_representor port_id 1 / end\n\nand the likes.\n\nSigned-off-by: Ivan Malov <ivan.malov@arknetworks.am>\nReviewed-by: Andy Moreton <andy.moreton@amd.com>\n---\nRFC: https://patches.dpdk.org/project/dpdk/list/?series=29397\n\n doc/guides/rel_notes/release_23_11.rst |   2 +\n drivers/common/sfc_efx/base/efx.h      |  32 +\n drivers/common/sfc_efx/base/efx_mae.c  | 175 +++++\n drivers/common/sfc_efx/version.map     |   3 +\n drivers/net/sfc/sfc_mae.c              | 858 +++++++++++++++++++++----\n drivers/net/sfc/sfc_mae.h              |  37 ++\n 6 files changed, 969 insertions(+), 138 deletions(-)",
    "diff": "diff --git a/doc/guides/rel_notes/release_23_11.rst b/doc/guides/rel_notes/release_23_11.rst\nindex dd10110fff..066495c622 100644\n--- a/doc/guides/rel_notes/release_23_11.rst\n+++ b/doc/guides/rel_notes/release_23_11.rst\n@@ -59,6 +59,8 @@ New Features\n \n   * Added support for transfer flow action INDIRECT with subtype VXLAN_ENCAP.\n \n+  * Supported packet replay (multi-count / multi-delivery) in transfer flows.\n+\n \n Removed Items\n -------------\ndiff --git a/drivers/common/sfc_efx/base/efx.h b/drivers/common/sfc_efx/base/efx.h\nindex b4d8cfe9d8..3312c2fa8f 100644\n--- a/drivers/common/sfc_efx/base/efx.h\n+++ b/drivers/common/sfc_efx/base/efx.h\n@@ -5327,6 +5327,38 @@ efx_table_entry_delete(\n \t__in_bcount(data_size)\t\tuint8_t *entry_datap,\n \t__in\t\t\t\tunsigned int data_size);\n \n+/*\n+ * Clone the given MAE action set specification\n+ * and drop actions COUNT and DELIVER from it.\n+ */\n+LIBEFX_API\n+extern\t__checkReturn\t\tefx_rc_t\n+efx_mae_action_set_replay(\n+\t__in\t\t\tefx_nic_t *enp,\n+\t__in\t\t\tconst efx_mae_actions_t *spec_orig,\n+\t__out\t\t\tefx_mae_actions_t **spec_clonep);\n+\n+/*\n+ * The actual limit may be lower than this.\n+ * This define merely limits the number of\n+ * entries in a single allocation request.\n+ */\n+#define EFX_MAE_ACTION_SET_LIST_MAX_NENTRIES\t254\n+\n+LIBEFX_API\n+extern\t__checkReturn\t\tefx_rc_t\n+efx_mae_action_set_list_alloc(\n+\t__in\t\t\tefx_nic_t *enp,\n+\t__in\t\t\tunsigned int n_asets,\n+\t__in_ecount(n_asets)\tconst efx_mae_aset_id_t *aset_ids,\n+\t__out\t\t\tefx_mae_aset_list_id_t *aset_list_idp);\n+\n+LIBEFX_API\n+extern\t__checkReturn\t\tefx_rc_t\n+efx_mae_action_set_list_free(\n+\t__in\t\t\tefx_nic_t *enp,\n+\t__in\t\t\tconst efx_mae_aset_list_id_t *aset_list_idp);\n+\n #ifdef\t__cplusplus\n }\n #endif\ndiff --git a/drivers/common/sfc_efx/base/efx_mae.c b/drivers/common/sfc_efx/base/efx_mae.c\nindex 0d7b24d351..9ae136dcce 100644\n--- a/drivers/common/sfc_efx/base/efx_mae.c\n+++ b/drivers/common/sfc_efx/base/efx_mae.c\n@@ -4273,4 +4273,179 @@ efx_mae_read_mport_journal(\n \treturn (rc);\n }\n \n+\t__checkReturn\t\tefx_rc_t\n+efx_mae_action_set_replay(\n+\t__in\t\t\tefx_nic_t *enp,\n+\t__in\t\t\tconst efx_mae_actions_t *spec_orig,\n+\t__out\t\t\tefx_mae_actions_t **spec_clonep)\n+{\n+\tconst efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);\n+\tefx_mae_actions_t *spec_clone;\n+\tefx_rc_t rc;\n+\n+\tEFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*spec_clone), spec_clone);\n+\tif (spec_clone == NULL) {\n+\t\trc = ENOMEM;\n+\t\tgoto fail1;\n+\t}\n+\n+\t*spec_clone = *spec_orig;\n+\n+\tspec_clone->ema_rsrc.emar_counter_id.id = EFX_MAE_RSRC_ID_INVALID;\n+\tspec_clone->ema_actions &= ~(1U << EFX_MAE_ACTION_COUNT);\n+\tspec_clone->ema_n_count_actions = 0;\n+\n+\t(void)efx_mae_mport_invalid(&spec_clone->ema_deliver_mport);\n+\tspec_clone->ema_actions &= ~(1U << EFX_MAE_ACTION_DELIVER);\n+\n+\t*spec_clonep = spec_clone;\n+\n+\treturn (0);\n+\n+fail1:\n+\tEFSYS_PROBE1(fail1, efx_rc_t, rc);\n+\treturn (rc);\n+}\n+\n+\t__checkReturn\t\tefx_rc_t\n+efx_mae_action_set_list_alloc(\n+\t__in\t\t\tefx_nic_t *enp,\n+\t__in\t\t\tunsigned int n_asets,\n+\t__in_ecount(n_asets)\tconst efx_mae_aset_id_t *aset_ids,\n+\t__out\t\t\tefx_mae_aset_list_id_t *aset_list_idp)\n+{\n+\tconst efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);\n+\tEFX_MCDI_DECLARE_BUF(payload,\n+\t    MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LENMAX_MCDI2,\n+\t    MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN);\n+\tefx_mae_aset_list_id_t aset_list_id;\n+\tefx_mcdi_req_t req;\n+\tefx_rc_t rc;\n+\n+\tEFX_STATIC_ASSERT(EFX_MAE_ACTION_SET_LIST_MAX_NENTRIES ==\n+\t    MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS_MAXNUM_MCDI2);\n+\n+\tEFX_STATIC_ASSERT(EFX_MAE_RSRC_ID_INVALID ==\n+\t    MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL);\n+\n+\tEFX_STATIC_ASSERT(sizeof (aset_list_idp->id) ==\n+\t    MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ASL_ID_LEN);\n+\n+\tif (encp->enc_mae_supported == B_FALSE) {\n+\t\trc = ENOTSUP;\n+\t\tgoto fail1;\n+\t}\n+\n+\tif (MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LEN(n_asets) >\n+\t    MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LENMAX_MCDI2) {\n+\t\trc = EINVAL;\n+\t\tgoto fail2;\n+\t}\n+\n+\treq.emr_cmd = MC_CMD_MAE_ACTION_SET_LIST_ALLOC;\n+\treq.emr_in_buf = payload;\n+\treq.emr_in_length = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LEN(n_asets);\n+\treq.emr_out_buf = payload;\n+\treq.emr_out_length = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN;\n+\n+\tMCDI_IN_SET_DWORD(req,\n+\t    MAE_ACTION_SET_LIST_ALLOC_IN_COUNT, n_asets);\n+\n+\tmemcpy(MCDI_IN2(req, uint8_t, MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS),\n+\t    aset_ids, n_asets * sizeof (*aset_ids));\n+\n+\tefx_mcdi_execute(enp, &req);\n+\n+\tif (req.emr_rc != 0) {\n+\t\trc = req.emr_rc;\n+\t\tgoto fail3;\n+\t}\n+\n+\tif (req.emr_out_length_used < MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN) {\n+\t\trc = EMSGSIZE;\n+\t\tgoto fail4;\n+\t}\n+\n+\taset_list_id.id =\n+\t    MCDI_OUT_DWORD(req, MAE_ACTION_SET_LIST_ALLOC_OUT_ASL_ID);\n+\tif (aset_list_id.id == EFX_MAE_RSRC_ID_INVALID) {\n+\t\trc = ENOENT;\n+\t\tgoto fail5;\n+\t}\n+\n+\taset_list_idp->id = aset_list_id.id;\n+\n+\treturn (0);\n+\n+fail5:\n+\tEFSYS_PROBE(fail5);\n+fail4:\n+\tEFSYS_PROBE(fail4);\n+fail3:\n+\tEFSYS_PROBE(fail3);\n+fail2:\n+\tEFSYS_PROBE(fail2);\n+fail1:\n+\tEFSYS_PROBE1(fail1, efx_rc_t, rc);\n+\treturn (rc);\n+}\n+\n+\t__checkReturn\t\tefx_rc_t\n+efx_mae_action_set_list_free(\n+\t__in\t\t\tefx_nic_t *enp,\n+\t__in\t\t\tconst efx_mae_aset_list_id_t *aset_list_idp)\n+{\n+\tconst efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);\n+\tEFX_MCDI_DECLARE_BUF(payload,\n+\t    MC_CMD_MAE_ACTION_SET_LIST_FREE_IN_LEN(1),\n+\t    MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LEN(1));\n+\tefx_mcdi_req_t req;\n+\tefx_rc_t rc;\n+\n+\tif (encp->enc_mae_supported == B_FALSE) {\n+\t\trc = ENOTSUP;\n+\t\tgoto fail1;\n+\t}\n+\n+\treq.emr_cmd = MC_CMD_MAE_ACTION_SET_LIST_FREE;\n+\treq.emr_in_buf = payload;\n+\treq.emr_in_length = MC_CMD_MAE_ACTION_SET_LIST_FREE_IN_LEN(1);\n+\treq.emr_out_buf = payload;\n+\treq.emr_out_length = MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LEN(1);\n+\n+\tMCDI_IN_SET_DWORD(req,\n+\t    MAE_ACTION_SET_LIST_FREE_IN_ASL_ID, aset_list_idp->id);\n+\n+\tefx_mcdi_execute(enp, &req);\n+\n+\tif (req.emr_rc != 0) {\n+\t\trc = req.emr_rc;\n+\t\tgoto fail2;\n+\t}\n+\n+\tif (req.emr_out_length_used < MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LENMIN) {\n+\t\trc = EMSGSIZE;\n+\t\tgoto fail3;\n+\t}\n+\n+\tif (MCDI_OUT_DWORD(req, MAE_ACTION_SET_LIST_FREE_OUT_FREED_ASL_ID) !=\n+\t    aset_list_idp->id) {\n+\t\t/* Firmware failed to free the action set list. */\n+\t\trc = EAGAIN;\n+\t\tgoto fail4;\n+\t}\n+\n+\treturn (0);\n+\n+fail4:\n+\tEFSYS_PROBE(fail4);\n+fail3:\n+\tEFSYS_PROBE(fail3);\n+fail2:\n+\tEFSYS_PROBE(fail2);\n+fail1:\n+\tEFSYS_PROBE1(fail1, efx_rc_t, rc);\n+\treturn (rc);\n+}\n+\n #endif /* EFSYS_OPT_MAE */\ndiff --git a/drivers/common/sfc_efx/version.map b/drivers/common/sfc_efx/version.map\nindex 43e8e52ab9..b2b90f5512 100644\n--- a/drivers/common/sfc_efx/version.map\n+++ b/drivers/common/sfc_efx/version.map\n@@ -97,6 +97,8 @@ INTERNAL {\n \tefx_mae_action_set_fill_in_src_mac_id;\n \tefx_mae_action_set_free;\n \tefx_mae_action_set_get_nb_count;\n+\tefx_mae_action_set_list_alloc;\n+\tefx_mae_action_set_list_free;\n \tefx_mae_action_set_populate_count;\n \tefx_mae_action_set_populate_decap;\n \tefx_mae_action_set_populate_decr_ip_ttl;\n@@ -111,6 +113,7 @@ INTERNAL {\n \tefx_mae_action_set_populate_set_src_mac;\n \tefx_mae_action_set_populate_vlan_pop;\n \tefx_mae_action_set_populate_vlan_push;\n+\tefx_mae_action_set_replay;\n \tefx_mae_action_set_spec_fini;\n \tefx_mae_action_set_spec_init;\n \tefx_mae_action_set_specs_equal;\ndiff --git a/drivers/net/sfc/sfc_mae.c b/drivers/net/sfc/sfc_mae.c\nindex d4c76a2c63..e5ec0ae49d 100644\n--- a/drivers/net/sfc/sfc_mae.c\n+++ b/drivers/net/sfc/sfc_mae.c\n@@ -220,6 +220,33 @@ sfc_mae_attach(struct sfc_adapter *sa)\n \t\t\tgoto fail_mae_alloc_bounce_eh;\n \t\t}\n \n+\t\tsfc_log_init(sa, \"allocate bounce action set pointer array\");\n+\t\tmae->bounce_aset_ptrs = rte_calloc(\"sfc_mae_bounce_aset_ptrs\",\n+\t\t\t\t\tEFX_MAE_ACTION_SET_LIST_MAX_NENTRIES,\n+\t\t\t\t\tsizeof(*mae->bounce_aset_ptrs), 0);\n+\t\tif (mae->bounce_aset_ptrs == NULL) {\n+\t\t\trc = ENOMEM;\n+\t\t\tgoto fail_mae_alloc_bounce_aset_ptrs;\n+\t\t}\n+\n+\t\tsfc_log_init(sa, \"allocate bounce action set contexts\");\n+\t\tmae->bounce_aset_ctxs = rte_calloc(\"sfc_mae_bounce_aset_ctxs\",\n+\t\t\t\t\tEFX_MAE_ACTION_SET_LIST_MAX_NENTRIES,\n+\t\t\t\t\tsizeof(*mae->bounce_aset_ctxs), 0);\n+\t\tif (mae->bounce_aset_ctxs == NULL) {\n+\t\t\trc = ENOMEM;\n+\t\t\tgoto fail_mae_alloc_bounce_aset_ctxs;\n+\t\t}\n+\n+\t\tsfc_log_init(sa, \"allocate bounce action set ID array\");\n+\t\tmae->bounce_aset_ids = rte_calloc(\"sfc_mae_bounce_aset_ids\",\n+\t\t\t\t\tEFX_MAE_ACTION_SET_LIST_MAX_NENTRIES,\n+\t\t\t\t\tsizeof(*mae->bounce_aset_ids), 0);\n+\t\tif (mae->bounce_aset_ids == NULL) {\n+\t\t\trc = ENOMEM;\n+\t\t\tgoto fail_mae_alloc_bounce_aset_ids;\n+\t\t}\n+\n \t\tmae->nb_outer_rule_prios_max = limits.eml_max_n_outer_prios;\n \t\tmae->nb_action_rule_prios_max = limits.eml_max_n_action_prios;\n \t\tmae->encap_types_supported = limits.eml_encap_types_supported;\n@@ -230,6 +257,7 @@ sfc_mae_attach(struct sfc_adapter *sa)\n \tTAILQ_INIT(&mae->encap_headers);\n \tTAILQ_INIT(&mae->counters);\n \tTAILQ_INIT(&mae->action_sets);\n+\tTAILQ_INIT(&mae->action_set_lists);\n \tTAILQ_INIT(&mae->action_rules);\n \n \tif (encp->enc_mae_admin)\n@@ -241,6 +269,15 @@ sfc_mae_attach(struct sfc_adapter *sa)\n \n \treturn 0;\n \n+fail_mae_alloc_bounce_aset_ids:\n+\trte_free(mae->bounce_aset_ctxs);\n+\n+fail_mae_alloc_bounce_aset_ctxs:\n+\trte_free(mae->bounce_aset_ptrs);\n+\n+fail_mae_alloc_bounce_aset_ptrs:\n+\trte_free(mae->bounce_eh.buf);\n+\n fail_mae_alloc_bounce_eh:\n fail_mae_assign_switch_port:\n fail_mae_assign_switch_domain:\n@@ -274,6 +311,9 @@ sfc_mae_detach(struct sfc_adapter *sa)\n \tif (status_prev != SFC_MAE_STATUS_ADMIN)\n \t\treturn;\n \n+\trte_free(mae->bounce_aset_ids);\n+\trte_free(mae->bounce_aset_ctxs);\n+\trte_free(mae->bounce_aset_ptrs);\n \trte_free(mae->bounce_eh.buf);\n \tsfc_mae_counter_registry_fini(&mae->counter_registry);\n \n@@ -1036,15 +1076,6 @@ sfc_mae_counter_disable(struct sfc_adapter *sa, struct sfc_mae_counter *counter)\n \t--(fw_rsrc->refcnt);\n }\n \n-struct sfc_mae_aset_ctx {\n-\tstruct sfc_mae_encap_header\t*encap_header;\n-\tstruct sfc_mae_counter\t\t*counter;\n-\tstruct sfc_mae_mac_addr\t\t*dst_mac;\n-\tstruct sfc_mae_mac_addr\t\t*src_mac;\n-\n-\tefx_mae_actions_t\t\t*spec;\n-};\n-\n static struct sfc_mae_action_set *\n sfc_mae_action_set_attach(struct sfc_adapter *sa,\n \t\t\t  const struct sfc_mae_aset_ctx *ctx)\n@@ -1272,9 +1303,222 @@ sfc_mae_action_set_disable(struct sfc_adapter *sa,\n \t--(fw_rsrc->refcnt);\n }\n \n+static struct sfc_mae_action_set_list *\n+sfc_mae_action_set_list_attach(struct sfc_adapter *sa)\n+{\n+\tstruct sfc_mae_action_set_list *action_set_list;\n+\tstruct sfc_mae *mae = &sa->mae;\n+\n+\tSFC_ASSERT(sfc_adapter_is_locked(sa));\n+\n+\tTAILQ_FOREACH(action_set_list, &mae->action_set_lists, entries) {\n+\t\tif (action_set_list->nb_action_sets != mae->nb_bounce_asets)\n+\t\t\tcontinue;\n+\n+\t\tif (memcmp(action_set_list->action_sets, mae->bounce_aset_ptrs,\n+\t\t\t   sizeof(struct sfc_mae_action_set *) *\n+\t\t\t   mae->nb_bounce_asets) == 0) {\n+\t\t\tsfc_dbg(sa, \"attaching to action_set_list=%p\",\n+\t\t\t\taction_set_list);\n+\t\t\t++(action_set_list->refcnt);\n+\t\t\treturn action_set_list;\n+\t\t}\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static int\n+sfc_mae_action_set_list_add(struct sfc_adapter *sa,\n+\t\t\t    struct sfc_mae_action_set_list **action_set_listp)\n+{\n+\tstruct sfc_mae_action_set_list *action_set_list;\n+\tstruct sfc_mae *mae = &sa->mae;\n+\n+\tSFC_ASSERT(sfc_adapter_is_locked(sa));\n+\n+\taction_set_list = rte_zmalloc(\"sfc_mae_action_set_list\",\n+\t\t\t\t      sizeof(*action_set_list), 0);\n+\tif (action_set_list == NULL) {\n+\t\tsfc_err(sa, \"failed to allocate action set list\");\n+\t\treturn ENOMEM;\n+\t}\n+\n+\taction_set_list->refcnt = 1;\n+\taction_set_list->nb_action_sets = mae->nb_bounce_asets;\n+\taction_set_list->fw_rsrc.aset_list_id.id = EFX_MAE_RSRC_ID_INVALID;\n+\n+\taction_set_list->action_sets =\n+\t\trte_calloc(\"sfc_mae_action_set_list_action_sets\",\n+\t\t\t   sizeof(struct sfc_mae_action_set *),\n+\t\t\t   action_set_list->nb_action_sets, 0);\n+\tif (action_set_list->action_sets == NULL) {\n+\t\tsfc_err(sa, \"failed to allocate action set list\");\n+\t\trte_free(action_set_list);\n+\t\treturn ENOMEM;\n+\t}\n+\n+\trte_memcpy(action_set_list->action_sets, mae->bounce_aset_ptrs,\n+\t\t   sizeof(struct sfc_mae_action_set *) *\n+\t\t   action_set_list->nb_action_sets);\n+\n+\tTAILQ_INSERT_TAIL(&mae->action_set_lists, action_set_list, entries);\n+\n+\t*action_set_listp = action_set_list;\n+\n+\tsfc_dbg(sa, \"added action_set_list=%p\", action_set_list);\n+\n+\treturn 0;\n+}\n+\n+static void\n+sfc_mae_action_set_list_del(struct sfc_adapter *sa,\n+\t\t\t    struct sfc_mae_action_set_list *action_set_list)\n+{\n+\tstruct sfc_mae *mae = &sa->mae;\n+\tunsigned int i;\n+\n+\tif (action_set_list == NULL)\n+\t\treturn;\n+\n+\tSFC_ASSERT(sfc_adapter_is_locked(sa));\n+\tSFC_ASSERT(action_set_list->refcnt != 0);\n+\n+\t--(action_set_list->refcnt);\n+\n+\tif (action_set_list->refcnt != 0)\n+\t\treturn;\n+\n+\tif (action_set_list->fw_rsrc.aset_list_id.id !=\n+\t    EFX_MAE_RSRC_ID_INVALID || action_set_list->fw_rsrc.refcnt != 0) {\n+\t\tsfc_err(sa, \"deleting action_set_list=%p abandons its FW resource: ASL_ID=0x%08x, refcnt=%u\",\n+\t\t\taction_set_list,\n+\t\t\taction_set_list->fw_rsrc.aset_list_id.id,\n+\t\t\taction_set_list->fw_rsrc.refcnt);\n+\t}\n+\n+\tfor (i = 0; i < action_set_list->nb_action_sets; ++i)\n+\t\tsfc_mae_action_set_del(sa, action_set_list->action_sets[i]);\n+\n+\tTAILQ_REMOVE(&mae->action_set_lists, action_set_list, entries);\n+\trte_free(action_set_list->action_sets);\n+\trte_free(action_set_list);\n+\n+\tsfc_dbg(sa, \"deleted action_set_list=%p\", action_set_list);\n+}\n+\n+static int\n+sfc_mae_action_set_list_enable(struct sfc_adapter *sa,\n+\t\t\t       struct sfc_mae_action_set_list *action_set_list)\n+{\n+\tstruct sfc_mae_fw_rsrc *fw_rsrc;\n+\tunsigned int i;\n+\tunsigned int j;\n+\tint rc;\n+\n+\tif (action_set_list == NULL)\n+\t\treturn 0;\n+\n+\tSFC_ASSERT(sfc_adapter_is_locked(sa));\n+\n+\tfw_rsrc = &action_set_list->fw_rsrc;\n+\n+\tif (fw_rsrc->refcnt == 0) {\n+\t\tstruct sfc_mae *mae = &sa->mae;\n+\n+\t\tSFC_ASSERT(fw_rsrc->aset_list_id.id == EFX_MAE_RSRC_ID_INVALID);\n+\n+\t\tfor (i = 0; i < action_set_list->nb_action_sets; ++i) {\n+\t\t\tconst struct sfc_mae_fw_rsrc *as_fw_rsrc;\n+\n+\t\t\trc = sfc_mae_action_set_enable(sa,\n+\t\t\t\t\t\taction_set_list->action_sets[i]);\n+\t\t\tif (rc != 0)\n+\t\t\t\tgoto fail_action_set_enable;\n+\n+\t\t\tas_fw_rsrc = &action_set_list->action_sets[i]->fw_rsrc;\n+\t\t\tmae->bounce_aset_ids[i].id = as_fw_rsrc->aset_id.id;\n+\t\t}\n+\n+\t\trc = efx_mae_action_set_list_alloc(sa->nic,\n+\t\t\t\t\t\taction_set_list->nb_action_sets,\n+\t\t\t\t\t\tmae->bounce_aset_ids,\n+\t\t\t\t\t\t&fw_rsrc->aset_list_id);\n+\t\tif (rc != 0) {\n+\t\t\tsfc_err(sa, \"failed to enable action_set_list=%p: %s\",\n+\t\t\t\taction_set_list, strerror(rc));\n+\t\t\tgoto fail_action_set_list_alloc;\n+\t\t}\n+\n+\t\tsfc_dbg(sa, \"enabled action_set_list=%p: ASL_ID=0x%08x\",\n+\t\t\taction_set_list, fw_rsrc->aset_list_id.id);\n+\t}\n+\n+\t++(fw_rsrc->refcnt);\n+\n+\treturn 0;\n+\n+fail_action_set_list_alloc:\n+fail_action_set_enable:\n+\tfor (j = 0; j < i; ++j)\n+\t\tsfc_mae_action_set_disable(sa, action_set_list->action_sets[j]);\n+\n+\treturn rc;\n+}\n+\n+static void\n+sfc_mae_action_set_list_disable(struct sfc_adapter *sa,\n+\t\t\t\tstruct sfc_mae_action_set_list *action_set_list)\n+{\n+\tstruct sfc_mae_fw_rsrc *fw_rsrc;\n+\tint rc;\n+\n+\tif (action_set_list == NULL)\n+\t\treturn;\n+\n+\tSFC_ASSERT(sfc_adapter_is_locked(sa));\n+\n+\tfw_rsrc = &action_set_list->fw_rsrc;\n+\n+\tif (fw_rsrc->aset_list_id.id == EFX_MAE_RSRC_ID_INVALID ||\n+\t    fw_rsrc->refcnt == 0) {\n+\t\tsfc_err(sa, \"failed to disable action_set_list=%p: already disabled; ASL_ID=0x%08x, refcnt=%u\",\n+\t\t\taction_set_list, fw_rsrc->aset_list_id.id,\n+\t\t\tfw_rsrc->refcnt);\n+\t\treturn;\n+\t}\n+\n+\tif (fw_rsrc->refcnt == 1) {\n+\t\tunsigned int i;\n+\n+\t\trc = efx_mae_action_set_list_free(sa->nic,\n+\t\t\t\t\t\t  &fw_rsrc->aset_list_id);\n+\t\tif (rc == 0) {\n+\t\t\tsfc_dbg(sa, \"disabled action_set_list=%p with ASL_ID=0x%08x\",\n+\t\t\t\taction_set_list, fw_rsrc->aset_list_id.id);\n+\t\t} else {\n+\t\t\tsfc_err(sa, \"failed to disable action_set_list=%p with ASL_ID=0x%08x: %s\",\n+\t\t\t\taction_set_list, fw_rsrc->aset_list_id.id,\n+\t\t\t\tstrerror(rc));\n+\t\t}\n+\t\tfw_rsrc->aset_list_id.id = EFX_MAE_RSRC_ID_INVALID;\n+\n+\t\tfor (i = 0; i < action_set_list->nb_action_sets; ++i) {\n+\t\t\tsfc_mae_action_set_disable(sa,\n+\t\t\t\t\taction_set_list->action_sets[i]);\n+\t\t}\n+\t}\n+\n+\t--(fw_rsrc->refcnt);\n+}\n+\n struct sfc_mae_action_rule_ctx {\n \tstruct sfc_mae_outer_rule\t*outer_rule;\n+\t/*\n+\t * When action_set_list != NULL, action_set is NULL, and vice versa.\n+\t */\n \tstruct sfc_mae_action_set\t*action_set;\n+\tstruct sfc_mae_action_set_list\t*action_set_list;\n \tefx_mae_match_spec_t\t\t*match_spec;\n \tuint32_t\t\t\tct_mark;\n };\n@@ -1305,6 +1549,7 @@ sfc_mae_action_rule_attach(struct sfc_adapter *sa,\n \n \t\tif (rule->outer_rule != ctx->outer_rule ||\n \t\t    rule->action_set != ctx->action_set ||\n+\t\t    rule->action_set_list != ctx->action_set_list ||\n \t\t    !!rule->ct_mark != !!ctx->ct_mark)\n \t\t\tcontinue;\n \n@@ -1380,6 +1625,7 @@ sfc_mae_action_rule_add(struct sfc_adapter *sa,\n \n \trule->outer_rule = ctx->outer_rule;\n \trule->action_set = ctx->action_set;\n+\trule->action_set_list = ctx->action_set_list;\n \trule->match_spec = ctx->match_spec;\n \n \trule->fw_rsrc.rule_id.id = EFX_MAE_RSRC_ID_INVALID;\n@@ -1416,6 +1662,7 @@ sfc_mae_action_rule_del(struct sfc_adapter *sa,\n \t}\n \n \tefx_mae_match_spec_fini(sa->nic, rule->match_spec);\n+\tsfc_mae_action_set_list_del(sa, rule->action_set_list);\n \tsfc_mae_action_set_del(sa, rule->action_set);\n \tsfc_mae_outer_rule_del(sa, rule->outer_rule);\n \n@@ -1429,6 +1676,8 @@ static int\n sfc_mae_action_rule_enable(struct sfc_adapter *sa,\n \t\t\t   struct sfc_mae_action_rule *rule)\n {\n+\tconst efx_mae_aset_list_id_t *asl_idp = NULL;\n+\tconst efx_mae_aset_id_t *as_idp = NULL;\n \tstruct sfc_mae_fw_rsrc *fw_rsrc;\n \tint rc;\n \n@@ -1447,9 +1696,18 @@ sfc_mae_action_rule_enable(struct sfc_adapter *sa,\n \tif (rc != 0)\n \t\tgoto fail_action_set_enable;\n \n-\trc = efx_mae_action_rule_insert(sa->nic, rule->match_spec, NULL,\n-\t\t\t\t\t&rule->action_set->fw_rsrc.aset_id,\n-\t\t\t\t\t&fw_rsrc->rule_id);\n+\trc = sfc_mae_action_set_list_enable(sa, rule->action_set_list);\n+\tif (rc != 0)\n+\t\tgoto fail_action_set_list_enable;\n+\n+\tif (rule->action_set_list != NULL)\n+\t\tasl_idp = &rule->action_set_list->fw_rsrc.aset_list_id;\n+\n+\tif (rule->action_set != NULL)\n+\t\tas_idp = &rule->action_set->fw_rsrc.aset_id;\n+\n+\trc = efx_mae_action_rule_insert(sa->nic, rule->match_spec, asl_idp,\n+\t\t\t\t\tas_idp, &fw_rsrc->rule_id);\n \tif (rc != 0) {\n \t\tsfc_err(sa, \"failed to enable action_rule=%p: %s\",\n \t\t\trule, strerror(rc));\n@@ -1467,6 +1725,9 @@ sfc_mae_action_rule_enable(struct sfc_adapter *sa,\n \treturn 0;\n \n fail_action_rule_insert:\n+\tsfc_mae_action_set_list_disable(sa, rule->action_set_list);\n+\n+fail_action_set_list_enable:\n \tsfc_mae_action_set_disable(sa, rule->action_set);\n \n fail_action_set_enable:\n@@ -1505,6 +1766,8 @@ sfc_mae_action_rule_disable(struct sfc_adapter *sa,\n \n \t\tfw_rsrc->rule_id.id = EFX_MAE_RSRC_ID_INVALID;\n \n+\t\tsfc_mae_action_set_list_disable(sa, rule->action_set_list);\n+\n \t\tsfc_mae_action_set_disable(sa, rule->action_set);\n \n \t\tsfc_mae_outer_rule_disable(sa, rule->outer_rule,\n@@ -4198,7 +4461,7 @@ sfc_mae_rule_parse_action_count(struct sfc_adapter *sa,\n }\n \n static int\n-sfc_mae_rule_parse_action_indirect(struct sfc_adapter *sa,\n+sfc_mae_rule_parse_action_indirect(struct sfc_adapter *sa, bool replayable_only,\n \t\t\t\t   const struct rte_flow_action_handle *handle,\n \t\t\t\t   enum sfc_ft_rule_type ft_rule_type,\n \t\t\t\t   struct sfc_mae_aset_ctx *ctx,\n@@ -4209,8 +4472,24 @@ sfc_mae_rule_parse_action_indirect(struct sfc_adapter *sa,\n \n \tTAILQ_FOREACH(entry, &sa->flow_indir_actions, entries) {\n \t\tif (entry == handle) {\n+\t\t\tbool replayable = false;\n+\n \t\t\tsfc_dbg(sa, \"attaching to indirect_action=%p\", entry);\n \n+\t\t\tswitch (entry->type) {\n+\t\t\tcase RTE_FLOW_ACTION_TYPE_COUNT:\n+\t\t\t\treplayable = true;\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tif (replayable_only && !replayable) {\n+\t\t\t\treturn rte_flow_error_set(error, EINVAL,\n+\t\t\t\t  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,\n+\t\t\t\t  \"the indirect action handle cannot be used\");\n+\t\t\t}\n+\n \t\t\tswitch (entry->type) {\n \t\t\tcase RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP:\n \t\t\t\tif (ctx->encap_header != NULL) {\n@@ -4230,17 +4509,21 @@ sfc_mae_rule_parse_action_indirect(struct sfc_adapter *sa,\n \t\t\t\t++(ctx->encap_header->refcnt);\n \t\t\t\tbreak;\n \t\t\tcase RTE_FLOW_ACTION_TYPE_COUNT:\n+\t\t\t\tif (!replayable_only && ctx->counter != NULL) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * Signal the caller to \"replay\" the action\n+\t\t\t\t\t * set context and re-invoke this function.\n+\t\t\t\t\t */\n+\t\t\t\t\treturn EEXIST;\n+\t\t\t\t}\n+\n \t\t\t\tif (ft_rule_type != SFC_FT_RULE_NONE) {\n \t\t\t\t\treturn rte_flow_error_set(error, EINVAL,\n \t\t\t\t\t  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,\n \t\t\t\t\t  \"cannot use indirect count action in tunnel model\");\n \t\t\t\t}\n \n-\t\t\t\tif (ctx->counter != NULL) {\n-\t\t\t\t\treturn rte_flow_error_set(error, EINVAL,\n-\t\t\t\t\t  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,\n-\t\t\t\t\t  \"cannot have multiple actions COUNT in one flow\");\n-\t\t\t\t}\n+\t\t\t\tSFC_ASSERT(ctx->counter == NULL);\n \n \t\t\t\trc = efx_mae_action_set_populate_count(ctx->spec);\n \t\t\t\tif (rc != 0) {\n@@ -4416,31 +4699,255 @@ static const char * const action_names[] = {\n \t[RTE_FLOW_ACTION_TYPE_JUMP] = \"JUMP\",\n };\n \n+static void sfc_mae_bounce_eh_invalidate(struct sfc_mae_bounce_eh *bounce_eh);\n+\n+static int sfc_mae_process_encap_header(struct sfc_adapter *sa,\n+\t\t\t\tconst struct sfc_mae_bounce_eh *bounce_eh,\n+\t\t\t\tstruct sfc_mae_encap_header **encap_headerp);\n+\n+static int\n+sfc_mae_aset_ctx_replay(struct sfc_adapter *sa, struct sfc_mae_aset_ctx **ctxp)\n+{\n+\tconst struct sfc_mae_aset_ctx *ctx_cur;\n+\tstruct sfc_mae_aset_ctx *ctx_new;\n+\tstruct sfc_mae *mae = &sa->mae;\n+\tint rc;\n+\n+\tRTE_BUILD_BUG_ON(EFX_MAE_ACTION_SET_LIST_MAX_NENTRIES == 0);\n+\n+\t/* Check the number of complete action set contexts. */\n+\tif (mae->nb_bounce_asets >= (EFX_MAE_ACTION_SET_LIST_MAX_NENTRIES - 1))\n+\t\treturn ENOSPC;\n+\n+\tctx_cur = &mae->bounce_aset_ctxs[mae->nb_bounce_asets];\n+\n+\t++(mae->nb_bounce_asets);\n+\n+\tctx_new = &mae->bounce_aset_ctxs[mae->nb_bounce_asets];\n+\n+\t*ctx_new = *ctx_cur;\n+\tctx_new->counter = NULL;\n+\tctx_new->fate_set = false;\n+\n+\t/*\n+\t * This clones the action set specification and drops\n+\t * actions COUNT and DELIVER from the clone so that\n+\t * such can be added to it by later action parsing.\n+\t */\n+\trc = efx_mae_action_set_replay(sa->nic, ctx_cur->spec, &ctx_new->spec);\n+\tif (rc != 0)\n+\t\treturn rc;\n+\n+\t*ctxp = ctx_new;\n+\n+\treturn 0;\n+}\n+\n+static int\n+sfc_mae_rule_parse_action_rc(struct sfc_adapter *sa,\n+\t\t\t     struct sfc_mae_actions_bundle *bundle,\n+\t\t\t     const struct rte_flow_action *action,\n+\t\t\t     struct rte_flow_error *error,\n+\t\t\t     int rc, bool custom_error)\n+{\n+\tif (rc == 0) {\n+\t\tbundle->actions_mask |= (1ULL << action->type);\n+\t} else if (!custom_error) {\n+\t\tif (action->type < RTE_DIM(action_names)) {\n+\t\t\tconst char *action_name = action_names[action->type];\n+\n+\t\t\tif (action_name != NULL) {\n+\t\t\t\tsfc_err(sa, \"action %s was rejected: %s\",\n+\t\t\t\t\taction_name, strerror(rc));\n+\t\t\t}\n+\t\t}\n+\t\trc = rte_flow_error_set(error, rc, RTE_FLOW_ERROR_TYPE_ACTION,\n+\t\t\t\tNULL, \"Failed to request the action\");\n+\t}\n+\n+\treturn rc;\n+}\n+\n+static int\n+sfc_mae_rule_parse_action_replayable(struct sfc_adapter *sa,\n+\t\t\t\t     const struct rte_flow *flow,\n+\t\t\t\t     struct sfc_mae_actions_bundle *bundle,\n+\t\t\t\t     const struct rte_flow_action *action,\n+\t\t\t\t     struct sfc_mae_aset_ctx *ctx,\n+\t\t\t\t     struct rte_flow_error *error)\n+{\n+\tconst struct sfc_flow_spec_mae *spec_mae = &flow->spec.mae;\n+\tefx_mae_actions_t *spec = ctx->spec;\n+\tunsigned int switch_port_type_mask;\n+\tbool custom_error = false;\n+\tbool new_fate_set = false;\n+\tbool need_replay = false;\n+\tint rc;\n+\n+\t/*\n+\t * Decide whether the current action set context is\n+\t * complete. If yes, \"replay\" it = go to a new one.\n+\t */\n+\tswitch (action->type) {\n+\tcase RTE_FLOW_ACTION_TYPE_INDIRECT:\n+\t\tif (ctx->fate_set || ctx->counter != NULL)\n+\t\t\tneed_replay = true;\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_PF:\n+\tcase RTE_FLOW_ACTION_TYPE_VF:\n+\tcase RTE_FLOW_ACTION_TYPE_PORT_ID:\n+\tcase RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR:\n+\tcase RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT:\n+\t\t/* FALLTHROUGH */\n+\tcase RTE_FLOW_ACTION_TYPE_DROP:\n+\t\tif (ctx->fate_set)\n+\t\t\tneed_replay = true;\n+\n+\t\tnew_fate_set = true;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn rte_flow_error_set(error, ENOTSUP,\n+\t\t\t\tRTE_FLOW_ERROR_TYPE_ACTION, NULL,\n+\t\t\t\t\"Unsupported action\");\n+\t}\n+\n+\tif (need_replay) {\n+\t\tif (spec_mae->ft_rule_type != SFC_FT_RULE_NONE) {\n+\t\t\treturn rte_flow_error_set(error, EINVAL,\n+\t\t\t\tRTE_FLOW_ERROR_TYPE_ACTION, NULL,\n+\t\t\t\t\"no support for packet replay in tunnel offload\");\n+\t\t}\n+\n+\t\tif (!ctx->fate_set) {\n+\t\t\t/*\n+\t\t\t * With regard to replayable actions, the current action\n+\t\t\t * set is only needed to hold one of the counters.\n+\t\t\t * That is, it does not have a fate action, so\n+\t\t\t * add one to suppress undesired delivery.\n+\t\t\t */\n+\t\t\trc = efx_mae_action_set_populate_drop(spec);\n+\t\t\tif (rc != 0) {\n+\t\t\t\treturn rte_flow_error_set(error, rc,\n+\t\t\t\t\tRTE_FLOW_ERROR_TYPE_ACTION, NULL,\n+\t\t\t\t\t\"failed to auto-add action DROP\");\n+\t\t\t}\n+\t\t}\n+\n+\t\trc = sfc_mae_aset_ctx_replay(sa, &ctx);\n+\t\tif (rc != 0) {\n+\t\t\treturn rte_flow_error_set(error, rc,\n+\t\t\t\tRTE_FLOW_ERROR_TYPE_ACTION, NULL,\n+\t\t\t\t\"failed to replay the action set\");\n+\t\t}\n+\n+\t\tspec = ctx->spec;\n+\t}\n+\n+\tctx->fate_set = new_fate_set;\n+\n+\tswitch (action->type) {\n+\tcase RTE_FLOW_ACTION_TYPE_INDIRECT:\n+\t\trc = sfc_mae_rule_parse_action_indirect(sa, true, action->conf,\n+\t\t\t\t\t\t\tspec_mae->ft_rule_type,\n+\t\t\t\t\t\t\tctx, error);\n+\t\tcustom_error = true;\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_PF:\n+\t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_PF,\n+\t\t\t\t       bundle->actions_mask);\n+\t\trc = sfc_mae_rule_parse_action_pf_vf(sa, NULL, spec);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_VF:\n+\t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_VF,\n+\t\t\t\t       bundle->actions_mask);\n+\t\trc = sfc_mae_rule_parse_action_pf_vf(sa, action->conf, spec);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_PORT_ID:\n+\t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_PORT_ID,\n+\t\t\t\t       bundle->actions_mask);\n+\t\trc = sfc_mae_rule_parse_action_port_id(sa, action->conf, spec);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR:\n+\t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR,\n+\t\t\t\t       bundle->actions_mask);\n+\n+\t\tswitch_port_type_mask = 1U << SFC_MAE_SWITCH_PORT_INDEPENDENT;\n+\n+\t\tif (flow->internal) {\n+\t\t\tswitch_port_type_mask |=\n+\t\t\t\t\t1U << SFC_MAE_SWITCH_PORT_REPRESENTOR;\n+\t\t}\n+\n+\t\trc = sfc_mae_rule_parse_action_port_representor(sa,\n+\t\t\t\taction->conf, switch_port_type_mask, spec);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT:\n+\t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT,\n+\t\t\t\t       bundle->actions_mask);\n+\t\trc = sfc_mae_rule_parse_action_represented_port(sa,\n+\t\t\t\taction->conf, spec);\n+\t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_DROP:\n+\t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_DROP,\n+\t\t\t\t       bundle->actions_mask);\n+\t\trc = efx_mae_action_set_populate_drop(spec);\n+\t\tbreak;\n+\tdefault:\n+\t\tSFC_ASSERT(B_FALSE);\n+\t\tbreak;\n+\t}\n+\n+\treturn sfc_mae_rule_parse_action_rc(sa, bundle, action, error,\n+\t\t\t\t\t    rc, custom_error);\n+}\n+\n static int\n sfc_mae_rule_parse_action(struct sfc_adapter *sa,\n \t\t\t  const struct rte_flow_action *action,\n \t\t\t  struct rte_flow *flow, bool ct,\n \t\t\t  struct sfc_mae_actions_bundle *bundle,\n-\t\t\t  struct sfc_mae_aset_ctx *ctx,\n \t\t\t  struct rte_flow_error *error)\n {\n \tstruct sfc_flow_spec_mae *spec_mae = &flow->spec.mae;\n \tconst struct sfc_mae_outer_rule *outer_rule = spec_mae->outer_rule;\n \tefx_counter_type_t mae_counter_type = EFX_COUNTER_TYPE_ACTION;\n \tconst uint64_t rx_metadata = sa->negotiated_rx_metadata;\n-\tstruct sfc_mae_counter **counterp = &ctx->counter;\n-\tefx_mae_actions_t *spec = ctx->spec;\n-\tefx_mae_actions_t *spec_ptr = spec;\n-\tunsigned int switch_port_type_mask;\n+\tstruct sfc_mae_counter **counterp;\n+\tbool non_replayable_found = true;\n+\tstruct sfc_mae *mae = &sa->mae;\n+\tstruct sfc_mae_aset_ctx *ctx;\n+\tefx_mae_actions_t *spec_ptr;\n \tbool custom_error = B_FALSE;\n+\tefx_mae_actions_t *spec;\n \tint rc = 0;\n \n+\t/* Check the number of complete action set contexts. */\n+\tif (mae->nb_bounce_asets > (EFX_MAE_ACTION_SET_LIST_MAX_NENTRIES - 1)) {\n+\t\treturn sfc_mae_rule_parse_action_rc(sa, bundle, action, error,\n+\t\t\t\t\t\t    ENOSPC, custom_error);\n+\t}\n+\n+\tctx = &mae->bounce_aset_ctxs[mae->nb_bounce_asets];\n+\tcounterp = &ctx->counter;\n+\tspec = ctx->spec;\n+\tspec_ptr = spec;\n+\n \tif (ct) {\n \t\tmae_counter_type = EFX_COUNTER_TYPE_CONNTRACK;\n \t\tcounterp = &spec_mae->ct_counter;\n \t\tspec_ptr = NULL;\n \t}\n \n+\tif (mae->nb_bounce_asets != 0 || ctx->fate_set) {\n+\t\t/*\n+\t\t * When at least one delivery action has been encountered,\n+\t\t * non-replayable actions (packet edits, for instance)\n+\t\t * will be turned down.\n+\t\t */\n+\t\treturn sfc_mae_rule_parse_action_replayable(sa, flow, bundle,\n+\t\t\t\t\t\t\t    action, ctx, error);\n+\t}\n+\n \tswitch (action->type) {\n \tcase RTE_FLOW_ACTION_TYPE_VXLAN_DECAP:\n \t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_VXLAN_DECAP,\n@@ -4516,10 +5023,18 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,\n \tcase RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP:\n \t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP,\n \t\t\t\t       bundle->actions_mask);\n-\t\trc = sfc_mae_rule_parse_action_vxlan_encap(&sa->mae,\n-\t\t\t\t\t\t\t   action->conf,\n+\n+\t\t/* Cleanup after previous encap. header bounce buffer usage. */\n+\t\tsfc_mae_bounce_eh_invalidate(&mae->bounce_eh);\n+\n+\t\trc = sfc_mae_rule_parse_action_vxlan_encap(mae, action->conf,\n \t\t\t\t\t\t\t   spec, error);\n-\t\tcustom_error = B_TRUE;\n+\t\tif (rc == 0) {\n+\t\t\trc = sfc_mae_process_encap_header(sa, &mae->bounce_eh,\n+\t\t\t\t\t\t\t  &ctx->encap_header);\n+\t\t} else {\n+\t\t\tcustom_error = true;\n+\t\t}\n \t\tbreak;\n \tcase RTE_FLOW_ACTION_TYPE_COUNT:\n \t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_COUNT,\n@@ -4531,9 +5046,13 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,\n \tcase RTE_FLOW_ACTION_TYPE_INDIRECT:\n \t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_INDIRECT,\n \t\t\t\t       bundle->actions_mask);\n-\t\trc = sfc_mae_rule_parse_action_indirect(sa, action->conf,\n+\t\trc = sfc_mae_rule_parse_action_indirect(sa, false, action->conf,\n \t\t\t\t\t\t\tspec_mae->ft_rule_type,\n \t\t\t\t\t\t\tctx, error);\n+\t\tif (rc == EEXIST) {\n+\t\t\t/* Handle the action as a replayable one below. */\n+\t\t\tnon_replayable_found = false;\n+\t\t}\n \t\tcustom_error = B_TRUE;\n \t\tbreak;\n \tcase RTE_FLOW_ACTION_TYPE_FLAG:\n@@ -4564,46 +5083,6 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,\n \t\t\tcustom_error = B_TRUE;\n \t\t}\n \t\tbreak;\n-\tcase RTE_FLOW_ACTION_TYPE_PF:\n-\t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_PF,\n-\t\t\t\t       bundle->actions_mask);\n-\t\trc = sfc_mae_rule_parse_action_pf_vf(sa, NULL, spec);\n-\t\tbreak;\n-\tcase RTE_FLOW_ACTION_TYPE_VF:\n-\t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_VF,\n-\t\t\t\t       bundle->actions_mask);\n-\t\trc = sfc_mae_rule_parse_action_pf_vf(sa, action->conf, spec);\n-\t\tbreak;\n-\tcase RTE_FLOW_ACTION_TYPE_PORT_ID:\n-\t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_PORT_ID,\n-\t\t\t\t       bundle->actions_mask);\n-\t\trc = sfc_mae_rule_parse_action_port_id(sa, action->conf, spec);\n-\t\tbreak;\n-\tcase RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR:\n-\t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR,\n-\t\t\t\t       bundle->actions_mask);\n-\n-\t\tswitch_port_type_mask = 1U << SFC_MAE_SWITCH_PORT_INDEPENDENT;\n-\n-\t\tif (flow->internal) {\n-\t\t\tswitch_port_type_mask |=\n-\t\t\t\t\t1U << SFC_MAE_SWITCH_PORT_REPRESENTOR;\n-\t\t}\n-\n-\t\trc = sfc_mae_rule_parse_action_port_representor(sa,\n-\t\t\t\taction->conf, switch_port_type_mask, spec);\n-\t\tbreak;\n-\tcase RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT:\n-\t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT,\n-\t\t\t\t       bundle->actions_mask);\n-\t\trc = sfc_mae_rule_parse_action_represented_port(sa,\n-\t\t\t\taction->conf, spec);\n-\t\tbreak;\n-\tcase RTE_FLOW_ACTION_TYPE_DROP:\n-\t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_DROP,\n-\t\t\t\t       bundle->actions_mask);\n-\t\trc = efx_mae_action_set_populate_drop(spec);\n-\t\tbreak;\n \tcase RTE_FLOW_ACTION_TYPE_JUMP:\n \t\tif (spec_mae->ft_rule_type == SFC_FT_RULE_TUNNEL) {\n \t\t\t/* Workaround. See sfc_flow_parse_rte_to_mae() */\n@@ -4611,27 +5090,16 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,\n \t\t}\n \t\t/* FALLTHROUGH */\n \tdefault:\n-\t\treturn rte_flow_error_set(error, ENOTSUP,\n-\t\t\t\tRTE_FLOW_ERROR_TYPE_ACTION, NULL,\n-\t\t\t\t\"Unsupported action\");\n+\t\tnon_replayable_found = false;\n \t}\n \n-\tif (rc == 0) {\n-\t\tbundle->actions_mask |= (1ULL << action->type);\n-\t} else if (!custom_error) {\n-\t\tif (action->type < RTE_DIM(action_names)) {\n-\t\t\tconst char *action_name = action_names[action->type];\n-\n-\t\t\tif (action_name != NULL) {\n-\t\t\t\tsfc_err(sa, \"action %s was rejected: %s\",\n-\t\t\t\t\taction_name, strerror(rc));\n-\t\t\t}\n-\t\t}\n-\t\trc = rte_flow_error_set(error, rc, RTE_FLOW_ERROR_TYPE_ACTION,\n-\t\t\t\tNULL, \"Failed to request the action\");\n+\tif (non_replayable_found) {\n+\t\treturn sfc_mae_rule_parse_action_rc(sa, bundle, action, error,\n+\t\t\t\t\t\t    rc, custom_error);\n \t}\n \n-\treturn rc;\n+\treturn sfc_mae_rule_parse_action_replayable(sa, flow, bundle,\n+\t\t\t\t\t\t    action, ctx, error);\n }\n \n static void\n@@ -4657,6 +5125,78 @@ sfc_mae_process_encap_header(struct sfc_adapter *sa,\n \treturn sfc_mae_encap_header_add(sa, bounce_eh, encap_headerp);\n }\n \n+static int\n+sfc_mae_rule_process_replay(struct sfc_adapter *sa,\n+\t\t\t    struct sfc_mae_action_rule_ctx *action_rule_ctx)\n+{\n+\tstruct sfc_mae_action_set *base_aset;\n+\tstruct sfc_mae_action_set **asetp;\n+\tstruct sfc_mae *mae = &sa->mae;\n+\tstruct sfc_mae_aset_ctx *ctx;\n+\tunsigned int i;\n+\tunsigned int j;\n+\tint rc;\n+\n+\tif (mae->nb_bounce_asets == 1)\n+\t\treturn 0;\n+\n+\tmae->bounce_aset_ptrs[0] = action_rule_ctx->action_set;\n+\tbase_aset = mae->bounce_aset_ptrs[0];\n+\n+\tfor (i = 1; i < mae->nb_bounce_asets; ++i) {\n+\t\tasetp = &mae->bounce_aset_ptrs[i];\n+\t\tctx = &mae->bounce_aset_ctxs[i];\n+\n+\t\t*asetp = sfc_mae_action_set_attach(sa, ctx);\n+\t\tif (*asetp != NULL) {\n+\t\t\tefx_mae_action_set_spec_fini(sa->nic, ctx->spec);\n+\t\t\tsfc_mae_counter_del(sa, ctx->counter);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\trc = sfc_mae_action_set_add(sa, ctx, asetp);\n+\t\tif (rc != 0)\n+\t\t\tgoto fail_action_set_add;\n+\n+\t\tif (base_aset->encap_header != NULL)\n+\t\t\t++(base_aset->encap_header->refcnt);\n+\n+\t\tif (base_aset->dst_mac_addr != NULL)\n+\t\t\t++(base_aset->dst_mac_addr->refcnt);\n+\n+\t\tif (base_aset->src_mac_addr != NULL)\n+\t\t\t++(base_aset->src_mac_addr->refcnt);\n+\t}\n+\n+\taction_rule_ctx->action_set_list = sfc_mae_action_set_list_attach(sa);\n+\tif (action_rule_ctx->action_set_list != NULL) {\n+\t\tfor (i = 0; i < mae->nb_bounce_asets; ++i)\n+\t\t\tsfc_mae_action_set_del(sa, mae->bounce_aset_ptrs[i]);\n+\t} else {\n+\t\trc = sfc_mae_action_set_list_add(sa,\n+\t\t\t\t\t&action_rule_ctx->action_set_list);\n+\t\tif (rc != 0)\n+\t\t\tgoto fail_action_set_list_add;\n+\t}\n+\n+\taction_rule_ctx->action_set = NULL;\n+\n+\treturn 0;\n+\n+fail_action_set_list_add:\n+fail_action_set_add:\n+\tfor (j = i; j < mae->nb_bounce_asets; ++j) {\n+\t\tctx = &mae->bounce_aset_ctxs[j];\n+\t\tefx_mae_action_set_spec_fini(sa->nic, ctx->spec);\n+\t\tsfc_mae_counter_del(sa, ctx->counter);\n+\t}\n+\n+\twhile (--i > 0)\n+\t\tsfc_mae_action_set_del(sa, mae->bounce_aset_ptrs[i]);\n+\n+\treturn rc;\n+}\n+\n static int\n sfc_mae_rule_parse_actions(struct sfc_adapter *sa,\n \t\t\t   const struct rte_flow_action actions[],\n@@ -4668,8 +5208,9 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,\n \tstruct sfc_mae_actions_bundle bundle = {0};\n \tbool ct = (action_rule_ctx->ct_mark != 0);\n \tconst struct rte_flow_action *action;\n-\tstruct sfc_mae_aset_ctx ctx = {0};\n+\tstruct sfc_mae_aset_ctx *last_ctx;\n \tstruct sfc_mae *mae = &sa->mae;\n+\tstruct sfc_mae_aset_ctx *ctx;\n \tint rc;\n \n \trte_errno = 0;\n@@ -4680,7 +5221,18 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,\n \t\t\t\t\"NULL actions\");\n \t}\n \n-\trc = efx_mae_action_set_spec_init(sa->nic, &ctx.spec);\n+\t/*\n+\t * Cleanup after action parsing of the previous flow.\n+\t *\n+\t * This particular variable always points at the\n+\t * 1st (base) action set context, which can hold\n+\t * both non-replayable and replayable actions.\n+\t */\n+\tctx = &mae->bounce_aset_ctxs[0];\n+\tmemset(ctx, 0, sizeof(*ctx));\n+\tmae->nb_bounce_asets = 0;\n+\n+\trc = efx_mae_action_set_spec_init(sa->nic, &ctx->spec);\n \tif (rc != 0)\n \t\tgoto fail_action_set_spec_init;\n \n@@ -4688,7 +5240,7 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,\n \t\tbool have_user_action_count = false;\n \n \t\t/* TUNNEL rules don't decapsulate packets. SWITCH rules do. */\n-\t\trc = efx_mae_action_set_populate_decap(ctx.spec);\n+\t\trc = efx_mae_action_set_populate_decap(ctx->spec);\n \t\tif (rc != 0)\n \t\t\tgoto fail_enforce_ft_decap;\n \n@@ -4708,63 +5260,62 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,\n \t\t\t * packets hitting this rule contribute to the tunnel's\n \t\t\t * total number of hits. See sfc_mae_counter_get().\n \t\t\t */\n-\t\t\trc = efx_mae_action_set_populate_count(ctx.spec);\n+\t\t\trc = efx_mae_action_set_populate_count(ctx->spec);\n \t\t\tif (rc != 0)\n \t\t\t\tgoto fail_enforce_ft_count;\n \n-\t\t\trc = sfc_mae_counter_add(sa, NULL, &ctx.counter);\n+\t\t\trc = sfc_mae_counter_add(sa, NULL, &ctx->counter);\n \t\t\tif (rc != 0)\n \t\t\t\tgoto fail_enforce_ft_count;\n \t\t}\n \t}\n \n-\t/* Cleanup after previous encap. header bounce buffer usage. */\n-\tsfc_mae_bounce_eh_invalidate(&mae->bounce_eh);\n-\n \tfor (action = actions;\n \t     action->type != RTE_FLOW_ACTION_TYPE_END; ++action) {\n-\t\trc = sfc_mae_actions_bundle_sync(action, &bundle, spec_mae,\n-\t\t\t\t\t\t ctx.spec, ct, error);\n-\t\tif (rc != 0)\n-\t\t\tgoto fail_rule_parse_action;\n+\t\tif (mae->nb_bounce_asets == 0) {\n+\t\t\trc = sfc_mae_actions_bundle_sync(action, &bundle,\n+\t\t\t\t\t\t\t spec_mae, ctx->spec,\n+\t\t\t\t\t\t\t ct, error);\n+\t\t\tif (rc != 0)\n+\t\t\t\tgoto fail_rule_parse_action;\n+\t\t}\n \n \t\trc = sfc_mae_rule_parse_action(sa, action, flow, ct,\n-\t\t\t\t\t       &bundle, &ctx, error);\n+\t\t\t\t\t       &bundle, error);\n \t\tif (rc != 0)\n \t\t\tgoto fail_rule_parse_action;\n \t}\n \n-\trc = sfc_mae_actions_bundle_sync(action, &bundle, spec_mae,\n-\t\t\t\t\t ctx.spec, ct, error);\n-\tif (rc != 0)\n-\t\tgoto fail_rule_parse_action;\n-\n-\trc = sfc_mae_process_encap_header(sa, &mae->bounce_eh,\n-\t\t\t\t\t  &ctx.encap_header);\n-\tif (rc != 0)\n-\t\tgoto fail_process_encap_header;\n+\tif (mae->nb_bounce_asets == 0) {\n+\t\trc = sfc_mae_actions_bundle_sync(action, &bundle, spec_mae,\n+\t\t\t\t\t\t ctx->spec, ct, error);\n+\t\tif (rc != 0)\n+\t\t\tgoto fail_rule_parse_action;\n+\t}\n \n \tswitch (spec_mae->ft_rule_type) {\n \tcase SFC_FT_RULE_NONE:\n \t\tbreak;\n \tcase SFC_FT_RULE_TUNNEL:\n \t\t/* Workaround. See sfc_flow_parse_rte_to_mae() */\n-\t\trc = sfc_mae_rule_parse_action_pf_vf(sa, NULL, ctx.spec);\n+\t\trc = sfc_mae_rule_parse_action_pf_vf(sa, NULL, ctx->spec);\n \t\tif (rc != 0)\n \t\t\tgoto fail_workaround_tunnel_delivery;\n \n-\t\tif (ctx.counter != NULL)\n-\t\t\t(ctx.counter)->ft_ctx = spec_mae->ft_ctx;\n+\t\tif (ctx->counter != NULL)\n+\t\t\t(ctx->counter)->ft_ctx = spec_mae->ft_ctx;\n+\n+\t\tctx->fate_set = true;\n \t\tbreak;\n \tcase SFC_FT_RULE_SWITCH:\n \t\t/*\n \t\t * Packets that go to the rule's AR have FT mark set (from\n \t\t * the TUNNEL rule OR's RECIRC_ID). Reset the mark to zero.\n \t\t */\n-\t\tefx_mae_action_set_populate_mark_reset(ctx.spec);\n+\t\tefx_mae_action_set_populate_mark_reset(ctx->spec);\n \n-\t\tif (ctx.counter != NULL) {\n-\t\t\t(ctx.counter)->ft_switch_hit_counter =\n+\t\tif (ctx->counter != NULL) {\n+\t\t\t(ctx->counter)->ft_switch_hit_counter =\n \t\t\t\t&spec_mae->ft_ctx->switch_hit_counter;\n \t\t} else if (sfc_mae_counter_stream_enabled(sa)) {\n \t\t\tSFC_ASSERT(ct);\n@@ -4777,48 +5328,53 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,\n \t\tSFC_ASSERT(B_FALSE);\n \t}\n \n-\t/*\n-\t * A DPDK flow entry must specify a fate action, which the parser\n-\t * converts into a DELIVER action in a libefx action set. An\n-\t * attempt to replace the action in the action set should\n-\t * fail. If it succeeds then report an error, as the\n-\t * parsed flow entry did not contain a fate action.\n-\t */\n-\trc = efx_mae_action_set_populate_drop(ctx.spec);\n-\tif (rc == 0) {\n+\tSFC_ASSERT(mae->nb_bounce_asets < EFX_MAE_ACTION_SET_LIST_MAX_NENTRIES);\n+\tlast_ctx = &mae->bounce_aset_ctxs[mae->nb_bounce_asets];\n+\t++(mae->nb_bounce_asets);\n+\n+\tif (!last_ctx->fate_set) {\n \t\trc = rte_flow_error_set(error, EINVAL,\n \t\t\t\t\tRTE_FLOW_ERROR_TYPE_ACTION, NULL,\n \t\t\t\t\t\"no fate action found\");\n \t\tgoto fail_check_fate_action;\n \t}\n \n-\taction_rule_ctx->action_set = sfc_mae_action_set_attach(sa, &ctx);\n+\taction_rule_ctx->action_set = sfc_mae_action_set_attach(sa, ctx);\n \tif (action_rule_ctx->action_set != NULL) {\n-\t\tsfc_mae_counter_del(sa, ctx.counter);\n-\t\tsfc_mae_mac_addr_del(sa, ctx.src_mac);\n-\t\tsfc_mae_mac_addr_del(sa, ctx.dst_mac);\n-\t\tsfc_mae_encap_header_del(sa, ctx.encap_header);\n-\t\tefx_mae_action_set_spec_fini(sa->nic, ctx.spec);\n-\t\treturn 0;\n+\t\tsfc_mae_counter_del(sa, ctx->counter);\n+\t\tsfc_mae_mac_addr_del(sa, ctx->src_mac);\n+\t\tsfc_mae_mac_addr_del(sa, ctx->dst_mac);\n+\t\tsfc_mae_encap_header_del(sa, ctx->encap_header);\n+\t\tefx_mae_action_set_spec_fini(sa->nic, ctx->spec);\n+\t} else {\n+\t\trc = sfc_mae_action_set_add(sa, ctx,\n+\t\t\t\t\t    &action_rule_ctx->action_set);\n+\t\tif (rc != 0)\n+\t\t\tgoto fail_action_set_add;\n \t}\n \n-\trc = sfc_mae_action_set_add(sa, &ctx, &action_rule_ctx->action_set);\n+\tmemset(ctx, 0, sizeof(*ctx));\n+\n+\trc = sfc_mae_rule_process_replay(sa, action_rule_ctx);\n \tif (rc != 0)\n-\t\tgoto fail_action_set_add;\n+\t\tgoto fail_rule_parse_replay;\n \n \treturn 0;\n \n+fail_rule_parse_replay:\n+\tsfc_mae_action_set_del(sa, action_rule_ctx->action_set);\n+\n fail_action_set_add:\n fail_check_fate_action:\n fail_workaround_tunnel_delivery:\n-\tsfc_mae_encap_header_del(sa, ctx.encap_header);\n-\n-fail_process_encap_header:\n fail_rule_parse_action:\n-\tsfc_mae_counter_del(sa, ctx.counter);\n-\tsfc_mae_mac_addr_del(sa, ctx.src_mac);\n-\tsfc_mae_mac_addr_del(sa, ctx.dst_mac);\n-\tefx_mae_action_set_spec_fini(sa->nic, ctx.spec);\n+\tsfc_mae_encap_header_del(sa, ctx->encap_header);\n+\tsfc_mae_counter_del(sa, ctx->counter);\n+\tsfc_mae_mac_addr_del(sa, ctx->src_mac);\n+\tsfc_mae_mac_addr_del(sa, ctx->dst_mac);\n+\n+\tif (ctx->spec != NULL)\n+\t\tefx_mae_action_set_spec_fini(sa->nic, ctx->spec);\n \n fail_enforce_ft_count:\n fail_enforce_ft_decap:\n@@ -4875,6 +5431,7 @@ sfc_mae_rule_parse(struct sfc_adapter *sa, const struct rte_flow_item pattern[],\n \t\t\t\t\terror);\n \tif (rc == 0) {\n \t\tefx_mae_match_spec_fini(sa->nic, ctx.match_spec);\n+\t\tsfc_mae_action_set_list_del(sa, ctx.action_set_list);\n \t\tsfc_mae_action_set_del(sa, ctx.action_set);\n \t\tsfc_mae_outer_rule_del(sa, ctx.outer_rule);\n \t} else if (rc == -ENOENT) {\n@@ -4902,6 +5459,7 @@ sfc_mae_rule_parse(struct sfc_adapter *sa, const struct rte_flow_item pattern[],\n \tif (ctx.match_spec != NULL)\n \t\tefx_mae_match_spec_fini(sa->nic, ctx.match_spec);\n \n+\tsfc_mae_action_set_list_del(sa, ctx.action_set_list);\n \tsfc_mae_action_set_del(sa, ctx.action_set);\n \tsfc_mae_outer_rule_del(sa, ctx.outer_rule);\n \n@@ -5120,6 +5678,7 @@ sfc_mae_query_counter(struct sfc_adapter *sa,\n \tconst struct rte_flow_action_count *conf = action->conf;\n \tstruct sfc_mae_counter *counters[1 /* action rule counter */ +\n \t\t\t\t\t 1 /* conntrack counter */];\n+\tstruct sfc_mae_counter *counter;\n \tunsigned int i;\n \tint rc;\n \n@@ -5137,7 +5696,7 @@ sfc_mae_query_counter(struct sfc_adapter *sa,\n \tcounters[1] = spec->ct_counter;\n \n \tfor (i = 0; i < RTE_DIM(counters); ++i) {\n-\t\tstruct sfc_mae_counter *counter = counters[i];\n+\t\tcounter = counters[i];\n \n \t\tif (counter == NULL)\n \t\t\tcontinue;\n@@ -5155,6 +5714,29 @@ sfc_mae_query_counter(struct sfc_adapter *sa,\n \t\t}\n \t}\n \n+\tif (action_rule == NULL || action_rule->action_set_list == NULL)\n+\t\tgoto exit;\n+\n+\tfor (i = 0; i < action_rule->action_set_list->nb_action_sets; ++i) {\n+\t\tcounter = action_rule->action_set_list->action_sets[i]->counter;\n+\n+\t\tif (counter == NULL || counter->indirect)\n+\t\t\tcontinue;\n+\n+\t\tif (conf == NULL ||\n+\t\t    (counter->rte_id_valid && conf->id == counter->rte_id)) {\n+\t\t\trc = sfc_mae_counter_get(sa, counter, data);\n+\t\t\tif (rc != 0) {\n+\t\t\t\treturn rte_flow_error_set(error, EINVAL,\n+\t\t\t\t\tRTE_FLOW_ERROR_TYPE_ACTION, action,\n+\t\t\t\t\t\"Queried flow rule counter action is invalid\");\n+\t\t\t}\n+\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+exit:\n \treturn rte_flow_error_set(error, ENOENT,\n \t\t\t\t  RTE_FLOW_ERROR_TYPE_ACTION, action,\n \t\t\t\t  \"no such flow rule action or such count ID\");\ndiff --git a/drivers/net/sfc/sfc_mae.h b/drivers/net/sfc/sfc_mae.h\nindex 7f4c3324bd..d5509b0582 100644\n--- a/drivers/net/sfc/sfc_mae.h\n+++ b/drivers/net/sfc/sfc_mae.h\n@@ -27,6 +27,7 @@ struct sfc_mae_fw_rsrc {\n struct sfc_mae_fw_rsrc {\n \tunsigned int\t\t\trefcnt;\n \tunion {\n+\t\tefx_mae_aset_list_id_t\taset_list_id;\n \t\tefx_counter_t\t\tcounter_id;\n \t\tefx_mae_aset_id_t\taset_id;\n \t\tefx_mae_rule_id_t\trule_id;\n@@ -106,12 +107,27 @@ struct sfc_mae_action_set {\n \n TAILQ_HEAD(sfc_mae_action_sets, sfc_mae_action_set);\n \n+/** Action set list registry entry */\n+struct sfc_mae_action_set_list {\n+\tTAILQ_ENTRY(sfc_mae_action_set_list)\tentries;\n+\tunsigned int\t\t\t\trefcnt;\n+\tunsigned int\t\t\t\tnb_action_sets;\n+\tstruct sfc_mae_action_set\t\t**action_sets;\n+\tstruct sfc_mae_fw_rsrc\t\t\tfw_rsrc;\n+};\n+\n+TAILQ_HEAD(sfc_mae_action_set_lists, sfc_mae_action_set_list);\n+\n /** Action rule registry entry */\n struct sfc_mae_action_rule {\n \tTAILQ_ENTRY(sfc_mae_action_rule)\tentries;\n \tuint32_t\t\t\t\tct_mark;\n \tstruct sfc_mae_outer_rule\t\t*outer_rule;\n+\t/*\n+\t * When action_set_list != NULL, action_set is NULL, and vice versa.\n+\t */\n \tstruct sfc_mae_action_set\t\t*action_set;\n+\tstruct sfc_mae_action_set_list\t\t*action_set_list;\n \tefx_mae_match_spec_t\t\t\t*match_spec;\n \tstruct sfc_mae_fw_rsrc\t\t\tfw_rsrc;\n \tunsigned int\t\t\t\trefcnt;\n@@ -205,6 +221,18 @@ struct sfc_mae_counter_registry {\n \t} polling;\n };\n \n+/* Entry format for the action parsing bounce buffer */\n+struct sfc_mae_aset_ctx {\n+\tstruct sfc_mae_encap_header\t*encap_header;\n+\tstruct sfc_mae_counter\t\t*counter;\n+\tstruct sfc_mae_mac_addr\t\t*dst_mac;\n+\tstruct sfc_mae_mac_addr\t\t*src_mac;\n+\n+\tbool\t\t\t\tfate_set;\n+\n+\tefx_mae_actions_t\t\t*spec;\n+};\n+\n struct sfc_mae {\n \t/** Assigned switch domain identifier */\n \tuint16_t\t\t\tswitch_domain_id;\n@@ -226,10 +254,19 @@ struct sfc_mae {\n \tstruct sfc_mae_mac_addrs\tmac_addrs;\n \t/** Action set registry */\n \tstruct sfc_mae_action_sets\taction_sets;\n+\t/** Action set list registry */\n+\tstruct sfc_mae_action_set_lists\taction_set_lists;\n \t/** Action rule registry */\n \tstruct sfc_mae_action_rules\taction_rules;\n \t/** Encap. header bounce buffer */\n \tstruct sfc_mae_bounce_eh\tbounce_eh;\n+\t/**\n+\t * Action parsing bounce buffers\n+\t */\n+\tstruct sfc_mae_action_set\t**bounce_aset_ptrs;\n+\tstruct sfc_mae_aset_ctx\t\t*bounce_aset_ctxs;\n+\tefx_mae_aset_id_t\t\t*bounce_aset_ids;\n+\tunsigned int\t\t\tnb_bounce_asets;\n \t/** Flag indicating whether counter-only RxQ is running */\n \tbool\t\t\t\tcounter_rxq_running;\n \t/** Counter record registry */\n",
    "prefixes": []
}