get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 94501,
    "url": "https://patches.dpdk.org/api/patches/94501/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/20210618134032.1922012-20-andrew.rybchenko@oktetlabs.ru/",
    "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": "<20210618134032.1922012-20-andrew.rybchenko@oktetlabs.ru>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20210618134032.1922012-20-andrew.rybchenko@oktetlabs.ru",
    "date": "2021-06-18T13:40:31",
    "name": "[v3,19/20] net/sfc: support flow action COUNT in transfer rules",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "75ae8a36a505fd51801cd9211ba555985e7684fe",
    "submitter": {
        "id": 2013,
        "url": "https://patches.dpdk.org/api/people/2013/?format=api",
        "name": "Andrew Rybchenko",
        "email": "Andrew.Rybchenko@oktetlabs.ru"
    },
    "delegate": {
        "id": 24651,
        "url": "https://patches.dpdk.org/api/users/24651/?format=api",
        "username": "dmarchand",
        "first_name": "David",
        "last_name": "Marchand",
        "email": "david.marchand@redhat.com"
    },
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/20210618134032.1922012-20-andrew.rybchenko@oktetlabs.ru/mbox/",
    "series": [
        {
            "id": 17397,
            "url": "https://patches.dpdk.org/api/series/17397/?format=api",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=17397",
            "date": "2021-06-18T13:40:12",
            "name": "net/sfc: support flow API COUNT action",
            "version": 3,
            "mbox": "https://patches.dpdk.org/series/17397/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/94501/comments/",
    "check": "warning",
    "checks": "https://patches.dpdk.org/api/patches/94501/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 6B17DA0C46;\n\tFri, 18 Jun 2021 15:43:14 +0200 (CEST)",
            "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 51F594117A;\n\tFri, 18 Jun 2021 15:41:39 +0200 (CEST)",
            "from shelob.oktetlabs.ru (shelob.oktetlabs.ru [91.220.146.113])\n by mails.dpdk.org (Postfix) with ESMTP id 8EAD8410ED\n for <dev@dpdk.org>; Fri, 18 Jun 2021 15:41:38 +0200 (CEST)",
            "by shelob.oktetlabs.ru (Postfix, from userid 122)\n id 5C60C7F51B; Fri, 18 Jun 2021 16:41:38 +0300 (MSK)",
            "from aros.oktetlabs.ru (aros.oktetlabs.ru [192.168.38.17])\n by shelob.oktetlabs.ru (Postfix) with ESMTP id 26C907F6A4;\n Fri, 18 Jun 2021 16:40:36 +0300 (MSK)"
        ],
        "X-Spam-Checker-Version": "SpamAssassin 3.4.2 (2018-09-13) on shelob.oktetlabs.ru",
        "X-Spam-Level": "",
        "X-Spam-Status": "No, score=0.8 required=5.0 tests=ALL_TRUSTED,\n DKIM_ADSP_DISCARD,\n URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.2",
        "DKIM-Filter": "OpenDKIM Filter v2.11.0 shelob.oktetlabs.ru 26C907F6A4",
        "Authentication-Results": "shelob.oktetlabs.ru/26C907F6A4; dkim=none;\n dkim-atps=neutral",
        "From": "Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>",
        "To": "dev@dpdk.org",
        "Cc": "David Marchand <david.marchand@redhat.com>,\n Igor Romanov <igor.romanov@oktetlabs.ru>,\n Andy Moreton <amoreton@xilinx.com>, Ivan Malov <ivan.malov@oktetlabs.ru>",
        "Date": "Fri, 18 Jun 2021 16:40:31 +0300",
        "Message-Id": "<20210618134032.1922012-20-andrew.rybchenko@oktetlabs.ru>",
        "X-Mailer": "git-send-email 2.30.2",
        "In-Reply-To": "<20210618134032.1922012-1-andrew.rybchenko@oktetlabs.ru>",
        "References": "<20210527152510.1551026-1-andrew.rybchenko@oktetlabs.ru>\n <20210618134032.1922012-1-andrew.rybchenko@oktetlabs.ru>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[dpdk-dev] [PATCH v3 19/20] net/sfc: support flow action COUNT in\n transfer rules",
        "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",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "From: Igor Romanov <igor.romanov@oktetlabs.ru>\n\nFor now, a rule may have only one dedicated counter, shared counters\nare not supported.\n\nHW delivers (or \"streams\") counter readings using special packets.\nThe driver creates a dedicated Rx queue to receive such packets\nand requests that HW start \"streaming\" the readings to it.\n\nThe counter queue is polled periodically, and the first available\nservice core is used for that. Hence, the user has to specify at least\none service core for counters to work. Such a core is shared by all\nMAE-capable devices managed by sfc driver.\n\nSigned-off-by: Igor Romanov <igor.romanov@oktetlabs.ru>\nSigned-off-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>\nReviewed-by: Andy Moreton <amoreton@xilinx.com>\nReviewed-by: Ivan Malov <ivan.malov@oktetlabs.ru>\n---\n doc/guides/nics/sfc_efx.rst            |   2 +\n doc/guides/rel_notes/release_21_08.rst |   6 +\n drivers/net/sfc/meson.build            |  10 +\n drivers/net/sfc/sfc_flow.c             |   7 +\n drivers/net/sfc/sfc_mae.c              | 231 +++++++++-\n drivers/net/sfc/sfc_mae.h              |  60 +++\n drivers/net/sfc/sfc_mae_counter.c      | 578 +++++++++++++++++++++++++\n drivers/net/sfc/sfc_mae_counter.h      |  11 +\n drivers/net/sfc/sfc_stats.h            |  80 ++++\n drivers/net/sfc/sfc_tweak.h            |   9 +\n 10 files changed, 989 insertions(+), 5 deletions(-)\n create mode 100644 drivers/net/sfc/sfc_stats.h",
    "diff": "diff --git a/doc/guides/nics/sfc_efx.rst b/doc/guides/nics/sfc_efx.rst\nindex cf1269cc03..bd08118da7 100644\n--- a/doc/guides/nics/sfc_efx.rst\n+++ b/doc/guides/nics/sfc_efx.rst\n@@ -240,6 +240,8 @@ Supported actions (***transfer*** rules):\n \n - PORT_ID\n \n+- COUNT\n+\n - DROP\n \n Validating flow rules depends on the firmware variant.\ndiff --git a/doc/guides/rel_notes/release_21_08.rst b/doc/guides/rel_notes/release_21_08.rst\nindex a6ecfdf3ce..75688304da 100644\n--- a/doc/guides/rel_notes/release_21_08.rst\n+++ b/doc/guides/rel_notes/release_21_08.rst\n@@ -55,6 +55,12 @@ New Features\n      Also, make sure to start the actual text at the margin.\n      =======================================================\n \n+* **Updated Solarflare network PMD.**\n+\n+  Updated the Solarflare ``sfc_efx`` driver with changes including:\n+\n+  * Added COUNT action support for SN1000 NICs\n+\n \n Removed Items\n -------------\ndiff --git a/drivers/net/sfc/meson.build b/drivers/net/sfc/meson.build\nindex f8880f740a..32b58e3d76 100644\n--- a/drivers/net/sfc/meson.build\n+++ b/drivers/net/sfc/meson.build\n@@ -39,6 +39,16 @@ foreach flag: extra_flags\n     endif\n endforeach\n \n+# for clang 32-bit compiles we need libatomic for 64-bit atomic ops\n+if cc.get_id() == 'clang' and dpdk_conf.get('RTE_ARCH_64') == false\n+    ext_deps += cc.find_library('atomic')\n+endif\n+\n+# for gcc compiles we need -latomic for 128-bit atomic ops\n+if cc.get_id() == 'gcc'\n+    ext_deps += cc.find_library('atomic')\n+endif\n+\n deps += ['common_sfc_efx', 'bus_pci']\n sources = files(\n         'sfc_ethdev.c',\ndiff --git a/drivers/net/sfc/sfc_flow.c b/drivers/net/sfc/sfc_flow.c\nindex 2db8af1759..1294dbd3a7 100644\n--- a/drivers/net/sfc/sfc_flow.c\n+++ b/drivers/net/sfc/sfc_flow.c\n@@ -24,6 +24,7 @@\n #include \"sfc_flow.h\"\n #include \"sfc_log.h\"\n #include \"sfc_dp_rx.h\"\n+#include \"sfc_mae_counter.h\"\n \n struct sfc_flow_ops_by_spec {\n \tsfc_flow_parse_cb_t\t*parse;\n@@ -2854,6 +2855,12 @@ sfc_flow_stop(struct sfc_adapter *sa)\n \t\tefx_rx_scale_context_free(sa->nic, rss->dummy_rss_context);\n \t\trss->dummy_rss_context = EFX_RSS_CONTEXT_DEFAULT;\n \t}\n+\n+\t/*\n+\t * MAE counter service is not stopped on flow rule remove to avoid\n+\t * extra work. Make sure that it is stopped here.\n+\t */\n+\tsfc_mae_counter_stop(sa);\n }\n \n int\ndiff --git a/drivers/net/sfc/sfc_mae.c b/drivers/net/sfc/sfc_mae.c\nindex 8ffcf72d88..c3efd5b407 100644\n--- a/drivers/net/sfc/sfc_mae.c\n+++ b/drivers/net/sfc/sfc_mae.c\n@@ -19,6 +19,7 @@\n #include \"sfc_mae_counter.h\"\n #include \"sfc_log.h\"\n #include \"sfc_switch.h\"\n+#include \"sfc_service.h\"\n \n static int\n sfc_mae_assign_entity_mport(struct sfc_adapter *sa,\n@@ -30,6 +31,19 @@ sfc_mae_assign_entity_mport(struct sfc_adapter *sa,\n \t\t\t\t\t      mportp);\n }\n \n+static int\n+sfc_mae_counter_registry_init(struct sfc_mae_counter_registry *registry,\n+\t\t\t      uint32_t nb_counters_max)\n+{\n+\treturn sfc_mae_counters_init(&registry->counters, nb_counters_max);\n+}\n+\n+static void\n+sfc_mae_counter_registry_fini(struct sfc_mae_counter_registry *registry)\n+{\n+\tsfc_mae_counters_fini(&registry->counters);\n+}\n+\n int\n sfc_mae_attach(struct sfc_adapter *sa)\n {\n@@ -59,6 +73,15 @@ sfc_mae_attach(struct sfc_adapter *sa)\n \tif (rc != 0)\n \t\tgoto fail_mae_get_limits;\n \n+\tsfc_log_init(sa, \"init MAE counter registry\");\n+\trc = sfc_mae_counter_registry_init(&mae->counter_registry,\n+\t\t\t\t\t   limits.eml_max_n_counters);\n+\tif (rc != 0) {\n+\t\tsfc_err(sa, \"failed to init MAE counters registry for %u entries: %s\",\n+\t\t\tlimits.eml_max_n_counters, rte_strerror(rc));\n+\t\tgoto fail_counter_registry_init;\n+\t}\n+\n \tsfc_log_init(sa, \"assign entity MPORT\");\n \trc = sfc_mae_assign_entity_mport(sa, &entity_mport);\n \tif (rc != 0)\n@@ -107,6 +130,9 @@ sfc_mae_attach(struct sfc_adapter *sa)\n fail_mae_assign_switch_port:\n fail_mae_assign_switch_domain:\n fail_mae_assign_entity_mport:\n+\tsfc_mae_counter_registry_fini(&mae->counter_registry);\n+\n+fail_counter_registry_init:\n fail_mae_get_limits:\n \tefx_mae_fini(sa->nic);\n \n@@ -131,6 +157,7 @@ sfc_mae_detach(struct sfc_adapter *sa)\n \t\treturn;\n \n \trte_free(mae->bounce_eh.buf);\n+\tsfc_mae_counter_registry_fini(&mae->counter_registry);\n \n \tefx_mae_fini(sa->nic);\n \n@@ -480,9 +507,72 @@ sfc_mae_encap_header_disable(struct sfc_adapter *sa,\n \t--(fw_rsrc->refcnt);\n }\n \n+static int\n+sfc_mae_counters_enable(struct sfc_adapter *sa,\n+\t\t\tstruct sfc_mae_counter_id *counters,\n+\t\t\tunsigned int n_counters,\n+\t\t\tefx_mae_actions_t *action_set_spec)\n+{\n+\tint rc;\n+\n+\tsfc_log_init(sa, \"entry\");\n+\n+\tif (n_counters == 0) {\n+\t\tsfc_log_init(sa, \"no counters - skip\");\n+\t\treturn 0;\n+\t}\n+\n+\tSFC_ASSERT(sfc_adapter_is_locked(sa));\n+\tSFC_ASSERT(n_counters == 1);\n+\n+\trc = sfc_mae_counter_enable(sa, &counters[0]);\n+\tif (rc != 0) {\n+\t\tsfc_err(sa, \"failed to enable MAE counter %u: %s\",\n+\t\t\tcounters[0].mae_id.id, rte_strerror(rc));\n+\t\tgoto fail_counter_add;\n+\t}\n+\n+\trc = efx_mae_action_set_fill_in_counter_id(action_set_spec,\n+\t\t\t\t\t\t   &counters[0].mae_id);\n+\tif (rc != 0) {\n+\t\tsfc_err(sa, \"failed to fill in MAE counter %u in action set: %s\",\n+\t\t\tcounters[0].mae_id.id, rte_strerror(rc));\n+\t\tgoto fail_fill_in_id;\n+\t}\n+\n+\treturn 0;\n+\n+fail_fill_in_id:\n+\t(void)sfc_mae_counter_disable(sa, &counters[0]);\n+\n+fail_counter_add:\n+\tsfc_log_init(sa, \"failed: %s\", rte_strerror(rc));\n+\treturn rc;\n+}\n+\n+static int\n+sfc_mae_counters_disable(struct sfc_adapter *sa,\n+\t\t\t struct sfc_mae_counter_id *counters,\n+\t\t\t unsigned int n_counters)\n+{\n+\tif (n_counters == 0)\n+\t\treturn 0;\n+\n+\tSFC_ASSERT(sfc_adapter_is_locked(sa));\n+\tSFC_ASSERT(n_counters == 1);\n+\n+\tif (counters[0].mae_id.id == EFX_MAE_RSRC_ID_INVALID) {\n+\t\tsfc_err(sa, \"failed to disable: already disabled\");\n+\t\treturn EALREADY;\n+\t}\n+\n+\treturn sfc_mae_counter_disable(sa, &counters[0]);\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_encap_header *encap_header,\n+\t\t\t  unsigned int n_count,\n \t\t\t  const efx_mae_actions_t *spec)\n {\n \tstruct sfc_mae_action_set *action_set;\n@@ -491,7 +581,12 @@ sfc_mae_action_set_attach(struct sfc_adapter *sa,\n \tSFC_ASSERT(sfc_adapter_is_locked(sa));\n \n \tTAILQ_FOREACH(action_set, &mae->action_sets, entries) {\n+\t\t/*\n+\t\t * Shared counters are not supported, hence action sets with\n+\t\t * COUNT are not attachable.\n+\t\t */\n \t\tif (action_set->encap_header == encap_header &&\n+\t\t    n_count == 0 &&\n \t\t    efx_mae_action_set_specs_equal(action_set->spec, spec)) {\n \t\t\tsfc_dbg(sa, \"attaching to action_set=%p\", action_set);\n \t\t\t++(action_set->refcnt);\n@@ -504,18 +599,52 @@ sfc_mae_action_set_attach(struct sfc_adapter *sa,\n \n static int\n sfc_mae_action_set_add(struct sfc_adapter *sa,\n+\t\t       const struct rte_flow_action actions[],\n \t\t       efx_mae_actions_t *spec,\n \t\t       struct sfc_mae_encap_header *encap_header,\n+\t\t       unsigned int n_counters,\n \t\t       struct sfc_mae_action_set **action_setp)\n {\n \tstruct sfc_mae_action_set *action_set;\n \tstruct sfc_mae *mae = &sa->mae;\n+\tunsigned int i;\n \n \tSFC_ASSERT(sfc_adapter_is_locked(sa));\n \n \taction_set = rte_zmalloc(\"sfc_mae_action_set\", sizeof(*action_set), 0);\n-\tif (action_set == NULL)\n+\tif (action_set == NULL) {\n+\t\tsfc_err(sa, \"failed to alloc action set\");\n \t\treturn ENOMEM;\n+\t}\n+\n+\tif (n_counters > 0) {\n+\t\tconst struct rte_flow_action *action;\n+\n+\t\taction_set->counters = rte_malloc(\"sfc_mae_counter_ids\",\n+\t\t\tsizeof(action_set->counters[0]) * n_counters, 0);\n+\t\tif (action_set->counters == NULL) {\n+\t\t\trte_free(action_set);\n+\t\t\tsfc_err(sa, \"failed to alloc counters\");\n+\t\t\treturn ENOMEM;\n+\t\t}\n+\n+\t\tfor (action = actions, i = 0;\n+\t\t     action->type != RTE_FLOW_ACTION_TYPE_END && i < n_counters;\n+\t\t     ++action) {\n+\t\t\tconst struct rte_flow_action_count *conf;\n+\n+\t\t\tif (action->type != RTE_FLOW_ACTION_TYPE_COUNT)\n+\t\t\t\tcontinue;\n+\n+\t\t\tconf = action->conf;\n+\n+\t\t\taction_set->counters[i].mae_id.id =\n+\t\t\t\tEFX_MAE_RSRC_ID_INVALID;\n+\t\t\taction_set->counters[i].rte_id = conf->id;\n+\t\t\ti++;\n+\t\t}\n+\t\taction_set->n_counters = n_counters;\n+\t}\n \n \taction_set->refcnt = 1;\n \taction_set->spec = spec;\n@@ -555,6 +684,12 @@ sfc_mae_action_set_del(struct sfc_adapter *sa,\n \n \tefx_mae_action_set_spec_fini(sa->nic, action_set->spec);\n \tsfc_mae_encap_header_del(sa, action_set->encap_header);\n+\tif (action_set->n_counters > 0) {\n+\t\tSFC_ASSERT(action_set->n_counters == 1);\n+\t\tSFC_ASSERT(action_set->counters[0].mae_id.id ==\n+\t\t\t   EFX_MAE_RSRC_ID_INVALID);\n+\t\trte_free(action_set->counters);\n+\t}\n \tTAILQ_REMOVE(&mae->action_sets, action_set, entries);\n \trte_free(action_set);\n \n@@ -566,6 +701,7 @@ sfc_mae_action_set_enable(struct sfc_adapter *sa,\n \t\t\t  struct sfc_mae_action_set *action_set)\n {\n \tstruct sfc_mae_encap_header *encap_header = action_set->encap_header;\n+\tstruct sfc_mae_counter_id *counters = action_set->counters;\n \tstruct sfc_mae_fw_rsrc *fw_rsrc = &action_set->fw_rsrc;\n \tint rc;\n \n@@ -580,14 +716,26 @@ sfc_mae_action_set_enable(struct sfc_adapter *sa,\n \t\tif (rc != 0)\n \t\t\treturn rc;\n \n-\t\trc = efx_mae_action_set_alloc(sa->nic, action_set->spec,\n-\t\t\t\t\t      &fw_rsrc->aset_id);\n+\t\trc = sfc_mae_counters_enable(sa, counters,\n+\t\t\t\t\t     action_set->n_counters,\n+\t\t\t\t\t     action_set->spec);\n \t\tif (rc != 0) {\n+\t\t\tsfc_err(sa, \"failed to enable %u MAE counters: %s\",\n+\t\t\t\taction_set->n_counters, rte_strerror(rc));\n+\n \t\t\tsfc_mae_encap_header_disable(sa, encap_header);\n+\t\t\treturn rc;\n+\t\t}\n \n+\t\trc = efx_mae_action_set_alloc(sa->nic, action_set->spec,\n+\t\t\t\t\t      &fw_rsrc->aset_id);\n+\t\tif (rc != 0) {\n \t\t\tsfc_err(sa, \"failed to enable action_set=%p: %s\",\n \t\t\t\taction_set, strerror(rc));\n \n+\t\t\t(void)sfc_mae_counters_disable(sa, counters,\n+\t\t\t\t\t\t       action_set->n_counters);\n+\t\t\tsfc_mae_encap_header_disable(sa, encap_header);\n \t\t\treturn rc;\n \t\t}\n \n@@ -627,6 +775,13 @@ sfc_mae_action_set_disable(struct sfc_adapter *sa,\n \t\t}\n \t\tfw_rsrc->aset_id.id = EFX_MAE_RSRC_ID_INVALID;\n \n+\t\trc = sfc_mae_counters_disable(sa, action_set->counters,\n+\t\t\t\t\t      action_set->n_counters);\n+\t\tif (rc != 0) {\n+\t\t\tsfc_err(sa, \"failed to disable %u MAE counters: %s\",\n+\t\t\t\taction_set->n_counters, rte_strerror(rc));\n+\t\t}\n+\n \t\tsfc_mae_encap_header_disable(sa, action_set->encap_header);\n \t}\n \n@@ -2508,6 +2663,48 @@ sfc_mae_rule_parse_action_mark(const struct rte_flow_action_mark *conf,\n \treturn efx_mae_action_set_populate_mark(spec, conf->id);\n }\n \n+static int\n+sfc_mae_rule_parse_action_count(struct sfc_adapter *sa,\n+\t\t\t\tconst struct rte_flow_action_count *conf,\n+\t\t\t\tefx_mae_actions_t *spec)\n+{\n+\tint rc;\n+\n+\tif (conf->shared) {\n+\t\trc = ENOTSUP;\n+\t\tgoto fail_counter_shared;\n+\t}\n+\n+\tif ((sa->counter_rxq.state & SFC_COUNTER_RXQ_INITIALIZED) == 0) {\n+\t\tsfc_err(sa,\n+\t\t\t\"counter queue is not configured for COUNT action\");\n+\t\trc = EINVAL;\n+\t\tgoto fail_counter_queue_uninit;\n+\t}\n+\n+\tif (sfc_get_service_lcore(SOCKET_ID_ANY) == RTE_MAX_LCORE) {\n+\t\trc = EINVAL;\n+\t\tgoto fail_no_service_core;\n+\t}\n+\n+\trc = efx_mae_action_set_populate_count(spec);\n+\tif (rc != 0) {\n+\t\tsfc_err(sa,\n+\t\t\t\"failed to populate counters in MAE action set: %s\",\n+\t\t\trte_strerror(rc));\n+\t\tgoto fail_populate_count;\n+\t}\n+\n+\treturn 0;\n+\n+fail_populate_count:\n+fail_no_service_core:\n+fail_counter_queue_uninit:\n+fail_counter_shared:\n+\n+\treturn rc;\n+}\n+\n static int\n sfc_mae_rule_parse_action_phy_port(struct sfc_adapter *sa,\n \t\t\t\t   const struct rte_flow_action_phy_port *conf,\n@@ -2623,6 +2820,11 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,\n \t\t\t\t\t\t\t   spec, error);\n \t\tcustom_error = B_TRUE;\n \t\tbreak;\n+\tcase RTE_FLOW_ACTION_TYPE_COUNT:\n+\t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_COUNT,\n+\t\t\t\t       bundle->actions_mask);\n+\t\trc = sfc_mae_rule_parse_action_count(sa, action->conf, spec);\n+\t\tbreak;\n \tcase RTE_FLOW_ACTION_TYPE_FLAG:\n \t\tSFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_FLAG,\n \t\t\t\t       bundle->actions_mask);\n@@ -2708,6 +2910,7 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,\n \tconst struct rte_flow_action *action;\n \tstruct sfc_mae *mae = &sa->mae;\n \tefx_mae_actions_t *spec;\n+\tunsigned int n_count;\n \tint rc;\n \n \trte_errno = 0;\n@@ -2745,15 +2948,22 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,\n \tif (rc != 0)\n \t\tgoto fail_process_encap_header;\n \n+\tn_count = efx_mae_action_set_get_nb_count(spec);\n+\tif (n_count > 1) {\n+\t\trc = ENOTSUP;\n+\t\tsfc_err(sa, \"too many count actions requested: %u\", n_count);\n+\t\tgoto fail_nb_count;\n+\t}\n+\n \tspec_mae->action_set = sfc_mae_action_set_attach(sa, encap_header,\n-\t\t\t\t\t\t\t spec);\n+\t\t\t\t\t\t\t n_count, spec);\n \tif (spec_mae->action_set != NULL) {\n \t\tsfc_mae_encap_header_del(sa, encap_header);\n \t\tefx_mae_action_set_spec_fini(sa->nic, spec);\n \t\treturn 0;\n \t}\n \n-\trc = sfc_mae_action_set_add(sa, spec, encap_header,\n+\trc = sfc_mae_action_set_add(sa, actions, spec, encap_header, n_count,\n \t\t\t\t    &spec_mae->action_set);\n \tif (rc != 0)\n \t\tgoto fail_action_set_add;\n@@ -2761,6 +2971,7 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,\n \treturn 0;\n \n fail_action_set_add:\n+fail_nb_count:\n \tsfc_mae_encap_header_del(sa, encap_header);\n \n fail_process_encap_header:\n@@ -2915,6 +3126,15 @@ sfc_mae_flow_insert(struct sfc_adapter *sa,\n \tif (rc != 0)\n \t\tgoto fail_action_set_enable;\n \n+\tif (action_set->n_counters > 0) {\n+\t\trc = sfc_mae_counter_start(sa);\n+\t\tif (rc != 0) {\n+\t\t\tsfc_err(sa, \"failed to start MAE counters support: %s\",\n+\t\t\t\trte_strerror(rc));\n+\t\t\tgoto fail_mae_counter_start;\n+\t\t}\n+\t}\n+\n \trc = efx_mae_action_rule_insert(sa->nic, spec_mae->match_spec,\n \t\t\t\t\tNULL, &fw_rsrc->aset_id,\n \t\t\t\t\t&spec_mae->rule_id);\n@@ -2927,6 +3147,7 @@ sfc_mae_flow_insert(struct sfc_adapter *sa,\n \treturn 0;\n \n fail_action_rule_insert:\n+fail_mae_counter_start:\n \tsfc_mae_action_set_disable(sa, action_set);\n \n fail_action_set_enable:\ndiff --git a/drivers/net/sfc/sfc_mae.h b/drivers/net/sfc/sfc_mae.h\nindex 9740e54e49..2cc4334890 100644\n--- a/drivers/net/sfc/sfc_mae.h\n+++ b/drivers/net/sfc/sfc_mae.h\n@@ -16,6 +16,8 @@\n \n #include \"efx.h\"\n \n+#include \"sfc_stats.h\"\n+\n #ifdef __cplusplus\n extern \"C\" {\n #endif\n@@ -54,10 +56,20 @@ struct sfc_mae_encap_header {\n \n TAILQ_HEAD(sfc_mae_encap_headers, sfc_mae_encap_header);\n \n+/* Counter ID */\n+struct sfc_mae_counter_id {\n+\t/* ID of a counter in MAE */\n+\tefx_counter_t\t\t\tmae_id;\n+\t/* ID of a counter in RTE */\n+\tuint32_t\t\t\trte_id;\n+};\n+\n /** Action set registry entry */\n struct sfc_mae_action_set {\n \tTAILQ_ENTRY(sfc_mae_action_set)\tentries;\n \tunsigned int\t\t\trefcnt;\n+\tstruct sfc_mae_counter_id\t*counters;\n+\tuint32_t\t\t\tn_counters;\n \tefx_mae_actions_t\t\t*spec;\n \tstruct sfc_mae_encap_header\t*encap_header;\n \tstruct sfc_mae_fw_rsrc\t\tfw_rsrc;\n@@ -83,6 +95,50 @@ struct sfc_mae_bounce_eh {\n \tefx_tunnel_protocol_t\t\ttype;\n };\n \n+/** Counter collection entry */\n+struct sfc_mae_counter {\n+\tbool\t\t\t\tinuse;\n+\tuint32_t\t\t\tgeneration_count;\n+\tunion sfc_pkts_bytes\t\tvalue;\n+\tunion sfc_pkts_bytes\t\treset;\n+};\n+\n+struct sfc_mae_counters_xstats {\n+\tuint64_t\t\t\tnot_inuse_update;\n+\tuint64_t\t\t\trealloc_update;\n+};\n+\n+struct sfc_mae_counters {\n+\t/** An array of all MAE counters */\n+\tstruct sfc_mae_counter\t\t*mae_counters;\n+\t/** Extra statistics for counters */\n+\tstruct sfc_mae_counters_xstats\txstats;\n+\t/** Count of all MAE counters */\n+\tunsigned int\t\t\tn_mae_counters;\n+};\n+\n+struct sfc_mae_counter_registry {\n+\t/* Common counter information */\n+\t/** Counters collection */\n+\tstruct sfc_mae_counters\t\tcounters;\n+\n+\t/* Information used by counter update service */\n+\t/** Callback to get packets from RxQ */\n+\teth_rx_burst_t\t\t\trx_pkt_burst;\n+\t/** Data for the callback to get packets */\n+\tstruct sfc_dp_rxq\t\t*rx_dp;\n+\t/** Number of buffers pushed to the RxQ */\n+\tunsigned int\t\t\tpushed_n_buffers;\n+\t/** Are credits used by counter stream */\n+\tbool\t\t\t\tuse_credits;\n+\n+\t/* Information used by configuration routines */\n+\t/** Counter service core ID */\n+\tuint32_t\t\t\tservice_core_id;\n+\t/** Counter service ID */\n+\tuint32_t\t\t\tservice_id;\n+};\n+\n struct sfc_mae {\n \t/** Assigned switch domain identifier */\n \tuint16_t\t\t\tswitch_domain_id;\n@@ -104,6 +160,10 @@ struct sfc_mae {\n \tstruct sfc_mae_action_sets\taction_sets;\n \t/** Encap. header bounce buffer */\n \tstruct sfc_mae_bounce_eh\tbounce_eh;\n+\t/** Flag indicating whether counter-only RxQ is running */\n+\tbool\t\t\t\tcounter_rxq_running;\n+\t/** Counter registry */\n+\tstruct sfc_mae_counter_registry\tcounter_registry;\n };\n \n struct sfc_adapter;\ndiff --git a/drivers/net/sfc/sfc_mae_counter.c b/drivers/net/sfc/sfc_mae_counter.c\nindex c7646cf7b1..b0cb8157aa 100644\n--- a/drivers/net/sfc/sfc_mae_counter.c\n+++ b/drivers/net/sfc/sfc_mae_counter.c\n@@ -4,8 +4,10 @@\n  */\n \n #include <rte_common.h>\n+#include <rte_service_component.h>\n \n #include \"efx.h\"\n+#include \"efx_regs_counters_pkt_format.h\"\n \n #include \"sfc_ev.h\"\n #include \"sfc.h\"\n@@ -49,6 +51,520 @@ sfc_mae_counter_rxq_required(struct sfc_adapter *sa)\n \treturn true;\n }\n \n+int\n+sfc_mae_counter_enable(struct sfc_adapter *sa,\n+\t\t       struct sfc_mae_counter_id *counterp)\n+{\n+\tstruct sfc_mae_counter_registry *reg = &sa->mae.counter_registry;\n+\tstruct sfc_mae_counters *counters = &reg->counters;\n+\tstruct sfc_mae_counter *p;\n+\tefx_counter_t mae_counter;\n+\tuint32_t generation_count;\n+\tuint32_t unused;\n+\tint rc;\n+\n+\t/*\n+\t * The actual count of counters allocated is ignored since a failure\n+\t * to allocate a single counter is indicated by non-zero return code.\n+\t */\n+\trc = efx_mae_counters_alloc(sa->nic, 1, &unused, &mae_counter,\n+\t\t\t\t    &generation_count);\n+\tif (rc != 0) {\n+\t\tsfc_err(sa, \"failed to alloc MAE counter: %s\",\n+\t\t\trte_strerror(rc));\n+\t\tgoto fail_mae_counter_alloc;\n+\t}\n+\n+\tif (mae_counter.id >= counters->n_mae_counters) {\n+\t\t/*\n+\t\t * ID of a counter is expected to be within the range\n+\t\t * between 0 and the maximum count of counters to always\n+\t\t * fit into a pre-allocated array size of maximum counter ID.\n+\t\t */\n+\t\tsfc_err(sa, \"MAE counter ID is out of expected range\");\n+\t\trc = EFAULT;\n+\t\tgoto fail_counter_id_range;\n+\t}\n+\n+\tcounterp->mae_id = mae_counter;\n+\n+\tp = &counters->mae_counters[mae_counter.id];\n+\n+\t/*\n+\t * Ordering is relaxed since it is the only operation on counter value.\n+\t * And it does not depend on different stores/loads in other threads.\n+\t * Paired with relaxed ordering in counter increment.\n+\t */\n+\t__atomic_store(&p->reset.pkts_bytes.int128,\n+\t\t       &p->value.pkts_bytes.int128, __ATOMIC_RELAXED);\n+\tp->generation_count = generation_count;\n+\n+\t/*\n+\t * The flag is set at the very end of add operation and reset\n+\t * at the beginning of delete operation. Release ordering is\n+\t * paired with acquire ordering on load in counter increment operation.\n+\t */\n+\t__atomic_store_n(&p->inuse, true, __ATOMIC_RELEASE);\n+\n+\tsfc_info(sa, \"enabled MAE counter #%u with reset pkts=%\" PRIu64\n+\t\t \" bytes=%\" PRIu64, mae_counter.id,\n+\t\t p->reset.pkts, p->reset.bytes);\n+\n+\treturn 0;\n+\n+fail_counter_id_range:\n+\t(void)efx_mae_counters_free(sa->nic, 1, &unused, &mae_counter, NULL);\n+\n+fail_mae_counter_alloc:\n+\tsfc_log_init(sa, \"failed: %s\", rte_strerror(rc));\n+\treturn rc;\n+}\n+\n+int\n+sfc_mae_counter_disable(struct sfc_adapter *sa,\n+\t\t\tstruct sfc_mae_counter_id *counter)\n+{\n+\tstruct sfc_mae_counter_registry *reg = &sa->mae.counter_registry;\n+\tstruct sfc_mae_counters *counters = &reg->counters;\n+\tstruct sfc_mae_counter *p;\n+\tuint32_t unused;\n+\tint rc;\n+\n+\tif (counter->mae_id.id == EFX_MAE_RSRC_ID_INVALID)\n+\t\treturn 0;\n+\n+\tSFC_ASSERT(counter->mae_id.id < counters->n_mae_counters);\n+\t/*\n+\t * The flag is set at the very end of add operation and reset\n+\t * at the beginning of delete operation. Release ordering is\n+\t * paired with acquire ordering on load in counter increment operation.\n+\t */\n+\tp = &counters->mae_counters[counter->mae_id.id];\n+\t__atomic_store_n(&p->inuse, false, __ATOMIC_RELEASE);\n+\n+\trc = efx_mae_counters_free(sa->nic, 1, &unused, &counter->mae_id, NULL);\n+\tif (rc != 0)\n+\t\tsfc_err(sa, \"failed to free MAE counter %u: %s\",\n+\t\t\tcounter->mae_id.id, rte_strerror(rc));\n+\n+\tsfc_info(sa, \"disabled MAE counter #%u with reset pkts=%\" PRIu64\n+\t\t \" bytes=%\" PRIu64, counter->mae_id.id,\n+\t\t p->reset.pkts, p->reset.bytes);\n+\n+\t/*\n+\t * Do this regardless of what efx_mae_counters_free() return value is.\n+\t * If there's some error, the resulting resource leakage is bad, but\n+\t * nothing sensible can be done in this case.\n+\t */\n+\tcounter->mae_id.id = EFX_MAE_RSRC_ID_INVALID;\n+\n+\treturn rc;\n+}\n+\n+static void\n+sfc_mae_counter_increment(struct sfc_adapter *sa,\n+\t\t\t  struct sfc_mae_counters *counters,\n+\t\t\t  uint32_t mae_counter_id,\n+\t\t\t  uint32_t generation_count,\n+\t\t\t  uint64_t pkts, uint64_t bytes)\n+{\n+\tstruct sfc_mae_counter *p = &counters->mae_counters[mae_counter_id];\n+\tstruct sfc_mae_counters_xstats *xstats = &counters->xstats;\n+\tunion sfc_pkts_bytes cnt_val;\n+\tbool inuse;\n+\n+\t/*\n+\t * Acquire ordering is paired with release ordering in counter add\n+\t * and delete operations.\n+\t */\n+\t__atomic_load(&p->inuse, &inuse, __ATOMIC_ACQUIRE);\n+\tif (!inuse) {\n+\t\t/*\n+\t\t * Two possible cases include:\n+\t\t * 1) Counter is just allocated. Too early counter update\n+\t\t *    cannot be processed properly.\n+\t\t * 2) Stale update of freed and not reallocated counter.\n+\t\t *    There is no point in processing that update.\n+\t\t */\n+\t\txstats->not_inuse_update++;\n+\t\treturn;\n+\t}\n+\n+\tif (unlikely(generation_count < p->generation_count)) {\n+\t\t/*\n+\t\t * It is a stale update for the reallocated counter\n+\t\t * (i.e., freed and the same ID allocated again).\n+\t\t */\n+\t\txstats->realloc_update++;\n+\t\treturn;\n+\t}\n+\n+\tcnt_val.pkts = p->value.pkts + pkts;\n+\tcnt_val.bytes = p->value.bytes + bytes;\n+\n+\t/*\n+\t * Ordering is relaxed since it is the only operation on counter value.\n+\t * And it does not depend on different stores/loads in other threads.\n+\t * Paired with relaxed ordering on counter reset.\n+\t */\n+\t__atomic_store(&p->value.pkts_bytes,\n+\t\t       &cnt_val.pkts_bytes, __ATOMIC_RELAXED);\n+\n+\tsfc_info(sa, \"update MAE counter #%u: pkts+%\" PRIu64 \"=%\" PRIu64\n+\t\t \", bytes+%\" PRIu64 \"=%\" PRIu64, mae_counter_id,\n+\t\t pkts, cnt_val.pkts, bytes, cnt_val.bytes);\n+}\n+\n+static void\n+sfc_mae_parse_counter_packet(struct sfc_adapter *sa,\n+\t\t\t     struct sfc_mae_counter_registry *counter_registry,\n+\t\t\t     const struct rte_mbuf *m)\n+{\n+\tuint32_t generation_count;\n+\tconst efx_xword_t *hdr;\n+\tconst efx_oword_t *counters_data;\n+\tunsigned int version;\n+\tunsigned int id;\n+\tunsigned int header_offset;\n+\tunsigned int payload_offset;\n+\tunsigned int counter_count;\n+\tunsigned int required_len;\n+\tunsigned int i;\n+\n+\tif (unlikely(m->nb_segs != 1)) {\n+\t\tsfc_err(sa, \"unexpectedly scattered MAE counters packet (%u segments)\",\n+\t\t\tm->nb_segs);\n+\t\treturn;\n+\t}\n+\n+\tif (unlikely(m->data_len < ER_RX_SL_PACKETISER_HEADER_WORD_SIZE)) {\n+\t\tsfc_err(sa, \"too short MAE counters packet (%u bytes)\",\n+\t\t\tm->data_len);\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * The generation count is located in the Rx prefix in the USER_MARK\n+\t * field which is written into hash.fdir.hi field of an mbuf. See\n+\t * SF-123581-TC SmartNIC Datapath Offloads section 4.7.5 Counters.\n+\t */\n+\tgeneration_count = m->hash.fdir.hi;\n+\n+\thdr = rte_pktmbuf_mtod(m, const efx_xword_t *);\n+\n+\tversion = EFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_VERSION);\n+\tif (unlikely(version != ERF_SC_PACKETISER_HEADER_VERSION_2)) {\n+\t\tsfc_err(sa, \"unexpected MAE counters packet version %u\",\n+\t\t\tversion);\n+\t\treturn;\n+\t}\n+\n+\tid = EFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_IDENTIFIER);\n+\tif (unlikely(id != ERF_SC_PACKETISER_HEADER_IDENTIFIER_AR)) {\n+\t\tsfc_err(sa, \"unexpected MAE counters source identifier %u\", id);\n+\t\treturn;\n+\t}\n+\n+\t/* Packet layout definitions assume fixed header offset in fact */\n+\theader_offset =\n+\t\tEFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_HEADER_OFFSET);\n+\tif (unlikely(header_offset !=\n+\t\t     ERF_SC_PACKETISER_HEADER_HEADER_OFFSET_DEFAULT)) {\n+\t\tsfc_err(sa, \"unexpected MAE counters packet header offset %u\",\n+\t\t\theader_offset);\n+\t\treturn;\n+\t}\n+\n+\tpayload_offset =\n+\t\tEFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_PAYLOAD_OFFSET);\n+\n+\tcounter_count = EFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_COUNT);\n+\n+\trequired_len = payload_offset +\n+\t\t\tcounter_count * sizeof(counters_data[0]);\n+\tif (unlikely(required_len > m->data_len)) {\n+\t\tsfc_err(sa, \"truncated MAE counters packet: %u counters, packet length is %u vs %u required\",\n+\t\t\tcounter_count, m->data_len, required_len);\n+\t\t/*\n+\t\t * In theory it is possible process available counters data,\n+\t\t * but such condition is really unexpected and it is\n+\t\t * better to treat entire packet as corrupted.\n+\t\t */\n+\t\treturn;\n+\t}\n+\n+\t/* Ensure that counters data is 32-bit aligned */\n+\tif (unlikely(payload_offset % sizeof(uint32_t) != 0)) {\n+\t\tsfc_err(sa, \"unsupported MAE counters payload offset %u, must be 32-bit aligned\",\n+\t\t\tpayload_offset);\n+\t\treturn;\n+\t}\n+\tRTE_BUILD_BUG_ON(sizeof(counters_data[0]) !=\n+\t\t\tER_RX_SL_PACKETISER_PAYLOAD_WORD_SIZE);\n+\n+\tcounters_data =\n+\t\trte_pktmbuf_mtod_offset(m, const efx_oword_t *, payload_offset);\n+\n+\tsfc_info(sa, \"update %u MAE counters with gc=%u\",\n+\t\t counter_count, generation_count);\n+\n+\tfor (i = 0; i < counter_count; ++i) {\n+\t\tuint32_t packet_count_lo;\n+\t\tuint32_t packet_count_hi;\n+\t\tuint32_t byte_count_lo;\n+\t\tuint32_t byte_count_hi;\n+\n+\t\t/*\n+\t\t * Use 32-bit field accessors below since counters data\n+\t\t * is not 64-bit aligned.\n+\t\t * 32-bit alignment is checked above taking into account\n+\t\t * that start of packet data is 32-bit aligned\n+\t\t * (cache-line size aligned in fact).\n+\t\t */\n+\t\tpacket_count_lo =\n+\t\t\tEFX_OWORD_FIELD32(counters_data[i],\n+\t\t\t\tERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_LO);\n+\t\tpacket_count_hi =\n+\t\t\tEFX_OWORD_FIELD32(counters_data[i],\n+\t\t\t\tERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_HI);\n+\t\tbyte_count_lo =\n+\t\t\tEFX_OWORD_FIELD32(counters_data[i],\n+\t\t\t\tERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_LO);\n+\t\tbyte_count_hi =\n+\t\t\tEFX_OWORD_FIELD32(counters_data[i],\n+\t\t\t\tERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_HI);\n+\t\tsfc_mae_counter_increment(sa,\n+\t\t\t&counter_registry->counters,\n+\t\t\tEFX_OWORD_FIELD32(counters_data[i],\n+\t\t\t\tERF_SC_PACKETISER_PAYLOAD_COUNTER_INDEX),\n+\t\t\tgeneration_count,\n+\t\t\t(uint64_t)packet_count_lo |\n+\t\t\t((uint64_t)packet_count_hi <<\n+\t\t\t ERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_LO_WIDTH),\n+\t\t\t(uint64_t)byte_count_lo |\n+\t\t\t((uint64_t)byte_count_hi <<\n+\t\t\t ERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_LO_WIDTH));\n+\t}\n+}\n+\n+static int32_t\n+sfc_mae_counter_routine(void *arg)\n+{\n+\tstruct sfc_adapter *sa = arg;\n+\tstruct sfc_mae_counter_registry *counter_registry =\n+\t\t&sa->mae.counter_registry;\n+\tstruct rte_mbuf *mbufs[SFC_MAE_COUNTER_RX_BURST];\n+\tunsigned int pushed_diff;\n+\tunsigned int pushed;\n+\tunsigned int i;\n+\tuint16_t n;\n+\tint rc;\n+\n+\tn = counter_registry->rx_pkt_burst(counter_registry->rx_dp, mbufs,\n+\t\t\t\t\t   SFC_MAE_COUNTER_RX_BURST);\n+\n+\tfor (i = 0; i < n; i++)\n+\t\tsfc_mae_parse_counter_packet(sa, counter_registry, mbufs[i]);\n+\n+\trte_pktmbuf_free_bulk(mbufs, n);\n+\n+\tif (!counter_registry->use_credits)\n+\t\treturn 0;\n+\n+\tpushed = sfc_rx_get_pushed(sa, counter_registry->rx_dp);\n+\tpushed_diff = pushed - counter_registry->pushed_n_buffers;\n+\n+\tif (pushed_diff >= SFC_COUNTER_RXQ_REFILL_LEVEL) {\n+\t\trc = efx_mae_counters_stream_give_credits(sa->nic, pushed_diff);\n+\t\tif (rc == 0) {\n+\t\t\tcounter_registry->pushed_n_buffers = pushed;\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t * FIXME: counters might be important for the\n+\t\t\t * application. Handle the error in order to recover\n+\t\t\t * from the failure\n+\t\t\t */\n+\t\t\tSFC_GENERIC_LOG(DEBUG, \"Give credits failed: %s\",\n+\t\t\t\t\trte_strerror(rc));\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void\n+sfc_mae_counter_service_unregister(struct sfc_adapter *sa)\n+{\n+\tstruct sfc_mae_counter_registry *registry =\n+\t\t&sa->mae.counter_registry;\n+\tconst unsigned int wait_ms = 10000;\n+\tunsigned int i;\n+\n+\trte_service_runstate_set(registry->service_id, 0);\n+\trte_service_component_runstate_set(registry->service_id, 0);\n+\n+\t/*\n+\t * Wait for the counter routine to finish the last iteration.\n+\t * Give up on timeout.\n+\t */\n+\tfor (i = 0; i < wait_ms; i++) {\n+\t\tif (rte_service_may_be_active(registry->service_id) == 0)\n+\t\t\tbreak;\n+\n+\t\trte_delay_ms(1);\n+\t}\n+\tif (i == wait_ms)\n+\t\tsfc_warn(sa, \"failed to wait for counter service to stop\");\n+\n+\trte_service_map_lcore_set(registry->service_id,\n+\t\t\t\t  registry->service_core_id, 0);\n+\n+\trte_service_component_unregister(registry->service_id);\n+}\n+\n+static struct sfc_rxq_info *\n+sfc_counter_rxq_info_get(struct sfc_adapter *sa)\n+{\n+\treturn &sfc_sa2shared(sa)->rxq_info[sa->counter_rxq.sw_index];\n+}\n+\n+static int\n+sfc_mae_counter_service_register(struct sfc_adapter *sa,\n+\t\t\t\t uint32_t counter_stream_flags)\n+{\n+\tstruct rte_service_spec service;\n+\tchar counter_service_name[sizeof(service.name)] = \"counter_service\";\n+\tstruct sfc_mae_counter_registry *counter_registry =\n+\t\t&sa->mae.counter_registry;\n+\tuint32_t cid;\n+\tuint32_t sid;\n+\tint rc;\n+\n+\tsfc_log_init(sa, \"entry\");\n+\n+\t/* Prepare service info */\n+\tmemset(&service, 0, sizeof(service));\n+\trte_strscpy(service.name, counter_service_name, sizeof(service.name));\n+\tservice.socket_id = sa->socket_id;\n+\tservice.callback = sfc_mae_counter_routine;\n+\tservice.callback_userdata = sa;\n+\tcounter_registry->rx_pkt_burst = sa->eth_dev->rx_pkt_burst;\n+\tcounter_registry->rx_dp = sfc_counter_rxq_info_get(sa)->dp;\n+\tcounter_registry->pushed_n_buffers = 0;\n+\tcounter_registry->use_credits = counter_stream_flags &\n+\t\tEFX_MAE_COUNTERS_STREAM_OUT_USES_CREDITS;\n+\n+\tcid = sfc_get_service_lcore(sa->socket_id);\n+\tif (cid == RTE_MAX_LCORE && sa->socket_id != SOCKET_ID_ANY) {\n+\t\t/* Warn and try to allocate on any NUMA node */\n+\t\tsfc_warn(sa,\n+\t\t\t\"failed to get service lcore for counter service at socket %d\",\n+\t\t\tsa->socket_id);\n+\n+\t\tcid = sfc_get_service_lcore(SOCKET_ID_ANY);\n+\t}\n+\tif (cid == RTE_MAX_LCORE) {\n+\t\trc = ENOTSUP;\n+\t\tsfc_err(sa, \"failed to get service lcore for counter service\");\n+\t\tgoto fail_get_service_lcore;\n+\t}\n+\n+\t/* Service core may be in \"stopped\" state, start it */\n+\trc = rte_service_lcore_start(cid);\n+\tif (rc != 0 && rc != -EALREADY) {\n+\t\tsfc_err(sa, \"failed to start service core for counter service: %s\",\n+\t\t\trte_strerror(-rc));\n+\t\trc = ENOTSUP;\n+\t\tgoto fail_start_core;\n+\t}\n+\n+\t/* Register counter service */\n+\trc = rte_service_component_register(&service, &sid);\n+\tif (rc != 0) {\n+\t\trc = ENOEXEC;\n+\t\tsfc_err(sa, \"failed to register counter service component\");\n+\t\tgoto fail_register;\n+\t}\n+\n+\t/* Map the service with the service core */\n+\trc = rte_service_map_lcore_set(sid, cid, 1);\n+\tif (rc != 0) {\n+\t\trc = -rc;\n+\t\tsfc_err(sa, \"failed to map lcore for counter service: %s\",\n+\t\t\trte_strerror(rc));\n+\t\tgoto fail_map_lcore;\n+\t}\n+\n+\t/* Run the service */\n+\trc = rte_service_component_runstate_set(sid, 1);\n+\tif (rc < 0) {\n+\t\trc = -rc;\n+\t\tsfc_err(sa, \"failed to run counter service component: %s\",\n+\t\t\trte_strerror(rc));\n+\t\tgoto fail_component_runstate_set;\n+\t}\n+\trc = rte_service_runstate_set(sid, 1);\n+\tif (rc < 0) {\n+\t\trc = -rc;\n+\t\tsfc_err(sa, \"failed to run counter service\");\n+\t\tgoto fail_runstate_set;\n+\t}\n+\n+\tcounter_registry->service_core_id = cid;\n+\tcounter_registry->service_id = sid;\n+\n+\tsfc_log_init(sa, \"done\");\n+\n+\treturn 0;\n+\n+fail_runstate_set:\n+\trte_service_component_runstate_set(sid, 0);\n+\n+fail_component_runstate_set:\n+\trte_service_map_lcore_set(sid, cid, 0);\n+\n+fail_map_lcore:\n+\trte_service_component_unregister(sid);\n+\n+fail_register:\n+fail_start_core:\n+fail_get_service_lcore:\n+\tsfc_log_init(sa, \"failed: %s\", rte_strerror(rc));\n+\n+\treturn rc;\n+}\n+\n+int\n+sfc_mae_counters_init(struct sfc_mae_counters *counters,\n+\t\t      uint32_t nb_counters_max)\n+{\n+\tint rc;\n+\n+\tSFC_GENERIC_LOG(DEBUG, \"%s: entry\", __func__);\n+\n+\tcounters->mae_counters = rte_zmalloc(\"sfc_mae_counters\",\n+\t\tsizeof(*counters->mae_counters) * nb_counters_max, 0);\n+\tif (counters->mae_counters == NULL) {\n+\t\trc = ENOMEM;\n+\t\tSFC_GENERIC_LOG(ERR, \"%s: failed: %s\", __func__,\n+\t\t\t\trte_strerror(rc));\n+\t\treturn rc;\n+\t}\n+\n+\tcounters->n_mae_counters = nb_counters_max;\n+\n+\tSFC_GENERIC_LOG(DEBUG, \"%s: done\", __func__);\n+\n+\treturn 0;\n+}\n+\n+void\n+sfc_mae_counters_fini(struct sfc_mae_counters *counters)\n+{\n+\trte_free(counters->mae_counters);\n+\tcounters->mae_counters = NULL;\n+}\n+\n int\n sfc_mae_counter_rxq_attach(struct sfc_adapter *sa)\n {\n@@ -215,3 +731,65 @@ sfc_mae_counter_rxq_fini(struct sfc_adapter *sa)\n \n \tsfc_log_init(sa, \"done\");\n }\n+\n+void\n+sfc_mae_counter_stop(struct sfc_adapter *sa)\n+{\n+\tstruct sfc_mae *mae = &sa->mae;\n+\n+\tsfc_log_init(sa, \"entry\");\n+\n+\tif (!mae->counter_rxq_running) {\n+\t\tsfc_log_init(sa, \"counter queue is not running - skip\");\n+\t\treturn;\n+\t}\n+\n+\tsfc_mae_counter_service_unregister(sa);\n+\tefx_mae_counters_stream_stop(sa->nic, sa->counter_rxq.sw_index, NULL);\n+\n+\tmae->counter_rxq_running = false;\n+\n+\tsfc_log_init(sa, \"done\");\n+}\n+\n+int\n+sfc_mae_counter_start(struct sfc_adapter *sa)\n+{\n+\tstruct sfc_mae *mae = &sa->mae;\n+\tuint32_t flags;\n+\tint rc;\n+\n+\tSFC_ASSERT(sa->counter_rxq.state & SFC_COUNTER_RXQ_ATTACHED);\n+\n+\tif (mae->counter_rxq_running)\n+\t\treturn 0;\n+\n+\tsfc_log_init(sa, \"entry\");\n+\n+\trc = efx_mae_counters_stream_start(sa->nic, sa->counter_rxq.sw_index,\n+\t\t\t\t\t   SFC_MAE_COUNTER_STREAM_PACKET_SIZE,\n+\t\t\t\t\t   0 /* No flags required */, &flags);\n+\tif (rc != 0) {\n+\t\tsfc_err(sa, \"failed to start MAE counters stream: %s\",\n+\t\t\trte_strerror(rc));\n+\t\tgoto fail_counter_stream;\n+\t}\n+\n+\tsfc_log_init(sa, \"stream start flags: 0x%x\", flags);\n+\n+\trc = sfc_mae_counter_service_register(sa, flags);\n+\tif (rc != 0)\n+\t\tgoto fail_service_register;\n+\n+\tmae->counter_rxq_running = true;\n+\n+\treturn 0;\n+\n+fail_service_register:\n+\tefx_mae_counters_stream_stop(sa->nic, sa->counter_rxq.sw_index, NULL);\n+\n+fail_counter_stream:\n+\tsfc_log_init(sa, \"failed: %s\", rte_strerror(rc));\n+\n+\treturn rc;\n+}\ndiff --git a/drivers/net/sfc/sfc_mae_counter.h b/drivers/net/sfc/sfc_mae_counter.h\nindex f16d64a999..f61a6b59cb 100644\n--- a/drivers/net/sfc/sfc_mae_counter.h\n+++ b/drivers/net/sfc/sfc_mae_counter.h\n@@ -38,6 +38,17 @@ void sfc_mae_counter_rxq_detach(struct sfc_adapter *sa);\n int sfc_mae_counter_rxq_init(struct sfc_adapter *sa);\n void sfc_mae_counter_rxq_fini(struct sfc_adapter *sa);\n \n+int sfc_mae_counters_init(struct sfc_mae_counters *counters,\n+\t\t\t  uint32_t nb_counters_max);\n+void sfc_mae_counters_fini(struct sfc_mae_counters *counters);\n+int sfc_mae_counter_enable(struct sfc_adapter *sa,\n+\t\t\t   struct sfc_mae_counter_id *counterp);\n+int sfc_mae_counter_disable(struct sfc_adapter *sa,\n+\t\t\t    struct sfc_mae_counter_id *counter);\n+\n+int sfc_mae_counter_start(struct sfc_adapter *sa);\n+void sfc_mae_counter_stop(struct sfc_adapter *sa);\n+\n #ifdef __cplusplus\n }\n #endif\ndiff --git a/drivers/net/sfc/sfc_stats.h b/drivers/net/sfc/sfc_stats.h\nnew file mode 100644\nindex 0000000000..2d7ab71f14\n--- /dev/null\n+++ b/drivers/net/sfc/sfc_stats.h\n@@ -0,0 +1,80 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ *\n+ * Copyright(c) 2019-2021 Xilinx, Inc.\n+ * Copyright(c) 2019 Solarflare Communications Inc.\n+ *\n+ * This software was jointly developed between OKTET Labs (under contract\n+ * for Solarflare) and Solarflare Communications, Inc.\n+ */\n+\n+#ifndef _SFC_STATS_H\n+#define _SFC_STATS_H\n+\n+#include <stdint.h>\n+\n+#include <rte_atomic.h>\n+\n+#include \"sfc_tweak.h\"\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+/**\n+ * 64-bit packets and bytes counters covered by 128-bit integer\n+ * in order to do atomic updates to guarantee consistency if\n+ * required.\n+ */\n+union sfc_pkts_bytes {\n+\tRTE_STD_C11\n+\tstruct {\n+\t\tuint64_t\t\tpkts;\n+\t\tuint64_t\t\tbytes;\n+\t};\n+\trte_int128_t\t\t\tpkts_bytes;\n+};\n+\n+/**\n+ * Update packets and bytes counters atomically in assumption that\n+ * the counter is written on one core only.\n+ */\n+static inline void\n+sfc_pkts_bytes_add(union sfc_pkts_bytes *st, uint64_t pkts, uint64_t bytes)\n+{\n+#if SFC_SW_STATS_ATOMIC\n+\tunion sfc_pkts_bytes result;\n+\n+\t/* Stats are written on single core only, so just load values */\n+\tresult.pkts = st->pkts + pkts;\n+\tresult.bytes = st->bytes + bytes;\n+\n+\t/*\n+\t * Store the result atomically to guarantee that the reader\n+\t * core sees both counter updates together.\n+\t */\n+\t__atomic_store_n(&st->pkts_bytes.int128, result.pkts_bytes.int128,\n+\t\t\t __ATOMIC_RELEASE);\n+#else\n+\tst->pkts += pkts;\n+\tst->bytes += bytes;\n+#endif\n+}\n+\n+/**\n+ * Get an atomic copy of a packets and bytes counters.\n+ */\n+static inline void\n+sfc_pkts_bytes_get(const union sfc_pkts_bytes *st, union sfc_pkts_bytes *result)\n+{\n+#if SFC_SW_STATS_ATOMIC\n+\tresult->pkts_bytes.int128 = __atomic_load_n(&st->pkts_bytes.int128,\n+\t\t\t\t\t\t    __ATOMIC_ACQUIRE);\n+#else\n+\t*result = *st;\n+#endif\n+}\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+#endif /* _SFC_STATS_H */\ndiff --git a/drivers/net/sfc/sfc_tweak.h b/drivers/net/sfc/sfc_tweak.h\nindex f2d8701421..d09c7a3125 100644\n--- a/drivers/net/sfc/sfc_tweak.h\n+++ b/drivers/net/sfc/sfc_tweak.h\n@@ -42,4 +42,13 @@\n  */\n #define SFC_RXD_WAIT_TIMEOUT_NS_DEF\t(200U * 1000)\n \n+/**\n+ * Ideally reading packet and byte counters together should return\n+ * consistent values. I.e. a number of bytes corresponds to a number of\n+ * packets. Since counters are updated in one thread and queried in\n+ * another it requires either locking or atomics which are very\n+ * expensive from performance point of view. So, disable it by default.\n+ */\n+#define SFC_SW_STATS_ATOMIC\t\t0\n+\n #endif /* _SFC_TWEAK_H_ */\n",
    "prefixes": [
        "v3",
        "19/20"
    ]
}