get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 122952,
    "url": "http://patches.dpdk.org/api/patches/122952/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20230202124951.2915770-2-tduszynski@marvell.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20230202124951.2915770-2-tduszynski@marvell.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230202124951.2915770-2-tduszynski@marvell.com",
    "date": "2023-02-02T12:49:48",
    "name": "[v9,1/4] lib: add generic support for reading PMU events",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "49cd294da45befe361b9a906367971c9d028f327",
    "submitter": {
        "id": 2215,
        "url": "http://patches.dpdk.org/api/people/2215/?format=api",
        "name": "Tomasz Duszynski",
        "email": "tduszynski@marvell.com"
    },
    "delegate": {
        "id": 24651,
        "url": "http://patches.dpdk.org/api/users/24651/?format=api",
        "username": "dmarchand",
        "first_name": "David",
        "last_name": "Marchand",
        "email": "david.marchand@redhat.com"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20230202124951.2915770-2-tduszynski@marvell.com/mbox/",
    "series": [
        {
            "id": 26761,
            "url": "http://patches.dpdk.org/api/series/26761/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=26761",
            "date": "2023-02-02T12:49:47",
            "name": "add support for self monitoring",
            "version": 9,
            "mbox": "http://patches.dpdk.org/series/26761/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/122952/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/122952/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 9D51341BAE;\n\tThu,  2 Feb 2023 13:50:09 +0100 (CET)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 271F942DAA;\n\tThu,  2 Feb 2023 13:50:09 +0100 (CET)",
            "from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com\n [67.231.156.173])\n by mails.dpdk.org (Postfix) with ESMTP id DB5F442DAA\n for <dev@dpdk.org>; Thu,  2 Feb 2023 13:50:06 +0100 (CET)",
            "from pps.filterd (m0045851.ppops.net [127.0.0.1])\n by mx0b-0016f401.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id\n 3127Kep2007927; Thu, 2 Feb 2023 04:50:03 -0800",
            "from dc5-exch02.marvell.com ([199.233.59.182])\n by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 3nfjrj832e-1\n (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT);\n Thu, 02 Feb 2023 04:50:03 -0800",
            "from DC5-EXCH01.marvell.com (10.69.176.38) by DC5-EXCH02.marvell.com\n (10.69.176.39) with Microsoft SMTP Server (TLS) id 15.0.1497.42;\n Thu, 2 Feb 2023 04:50:01 -0800",
            "from maili.marvell.com (10.69.176.80) by DC5-EXCH01.marvell.com\n (10.69.176.38) with Microsoft SMTP Server id 15.0.1497.42 via Frontend\n Transport; Thu, 2 Feb 2023 04:50:01 -0800",
            "from cavium-DT10.. (unknown [10.28.34.39])\n by maili.marvell.com (Postfix) with ESMTP id E69535B6926;\n Thu,  2 Feb 2023 04:49:57 -0800 (PST)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com;\n h=from : to : cc :\n subject : date : message-id : in-reply-to : references : mime-version :\n content-type : content-transfer-encoding; s=pfpt0220;\n bh=JvBedDQv4E9DNBHmqU9nSuR5kaCrI00Jw4LB4a1+GsQ=;\n b=TF1lQ9Yo9F/UiRTl/N2DcU9+glqN85UVigSmbmPSPumuu8NEaVO1JXBwsqnPRSwAnpBv\n 2eDWyGGuz3Nmi1494MWK9cKKcX1NWCFW/n0uOt0uZo5yshkvOCV1vVRZpqq2GLqmLYmZ\n OK3+s1VPfInLy7mEJDXTGJbC4aZiKnSJd45HnpDGw8BssSXAc56DdkDi63tPAXQqgBi5\n +k4BR7GoadePHw2iYf5p6jvA8irBdqY3UZBEFZcTurEAMejPWg8OuwjIY9pHZi19fzqy\n nhU8yiekje/GOs9Z/L9stGYTjSRVnc6CuVmBiCsjCouUP8MoeDW+jct8KFT13LVqWy7i Uw==",
        "From": "Tomasz Duszynski <tduszynski@marvell.com>",
        "To": "<dev@dpdk.org>, Thomas Monjalon <thomas@monjalon.net>, Tomasz Duszynski\n <tduszynski@marvell.com>",
        "CC": "<roretzla@linux.microsoft.com>, <Ruifeng.Wang@arm.com>,\n <bruce.richardson@intel.com>, <jerinj@marvell.com>,\n <mattias.ronnblom@ericsson.com>, <mb@smartsharesystems.com>,\n <zhoumin@loongson.cn>",
        "Subject": "[PATCH v9 1/4] lib: add generic support for reading PMU events",
        "Date": "Thu, 2 Feb 2023 13:49:48 +0100",
        "Message-ID": "<20230202124951.2915770-2-tduszynski@marvell.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20230202124951.2915770-1-tduszynski@marvell.com>",
        "References": "<20230202094358.2838758-1-tduszynski@marvell.com>\n <20230202124951.2915770-1-tduszynski@marvell.com>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"UTF-8\"",
        "Content-Transfer-Encoding": "8bit",
        "X-Proofpoint-ORIG-GUID": "HqUMJBHLAhKI-i5wVAwNbyrssSiUH1Aw",
        "X-Proofpoint-GUID": "HqUMJBHLAhKI-i5wVAwNbyrssSiUH1Aw",
        "X-Proofpoint-Virus-Version": "vendor=baseguard\n engine=ICAP:2.0.219,Aquarius:18.0.930,Hydra:6.0.562,FMLib:17.11.122.1\n definitions=2023-02-02_04,2023-02-02_01,2022-06-22_01",
        "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": "Add support for programming PMU counters and reading their values\nin runtime bypassing kernel completely.\n\nThis is especially useful in cases where CPU cores are isolated\n(nohz_full) i.e run dedicated tasks. In such cases one cannot use\nstandard perf utility without sacrificing latency and performance.\n\nSigned-off-by: Tomasz Duszynski <tduszynski@marvell.com>\nAcked-by: Morten Brørup <mb@smartsharesystems.com>\n---\n MAINTAINERS                            |   5 +\n app/test/meson.build                   |   1 +\n app/test/test_pmu.c                    |  55 +++\n doc/api/doxy-api-index.md              |   3 +-\n doc/api/doxy-api.conf.in               |   1 +\n doc/guides/prog_guide/profile_app.rst  |   8 +\n doc/guides/rel_notes/release_23_03.rst |   9 +\n lib/meson.build                        |   1 +\n lib/pmu/meson.build                    |  13 +\n lib/pmu/pmu_private.h                  |  29 ++\n lib/pmu/rte_pmu.c                      | 464 +++++++++++++++++++++++++\n lib/pmu/rte_pmu.h                      | 205 +++++++++++\n lib/pmu/version.map                    |  20 ++\n 13 files changed, 813 insertions(+), 1 deletion(-)\n create mode 100644 app/test/test_pmu.c\n create mode 100644 lib/pmu/meson.build\n create mode 100644 lib/pmu/pmu_private.h\n create mode 100644 lib/pmu/rte_pmu.c\n create mode 100644 lib/pmu/rte_pmu.h\n create mode 100644 lib/pmu/version.map",
    "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex 9a0f416d2e..9f13eafd95 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -1697,6 +1697,11 @@ M: Nithin Dabilpuram <ndabilpuram@marvell.com>\n M: Pavan Nikhilesh <pbhagavatula@marvell.com>\n F: lib/node/\n \n+PMU - EXPERIMENTAL\n+M: Tomasz Duszynski <tduszynski@marvell.com>\n+F: lib/pmu/\n+F: app/test/test_pmu*\n+\n \n Test Applications\n -----------------\ndiff --git a/app/test/meson.build b/app/test/meson.build\nindex f34d19e3c3..7b6b69dcf1 100644\n--- a/app/test/meson.build\n+++ b/app/test/meson.build\n@@ -111,6 +111,7 @@ test_sources = files(\n         'test_reciprocal_division_perf.c',\n         'test_red.c',\n         'test_pie.c',\n+        'test_pmu.c',\n         'test_reorder.c',\n         'test_rib.c',\n         'test_rib6.c',\ndiff --git a/app/test/test_pmu.c b/app/test/test_pmu.c\nnew file mode 100644\nindex 0000000000..a9bfb1a427\n--- /dev/null\n+++ b/app/test/test_pmu.c\n@@ -0,0 +1,55 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2023 Marvell International Ltd.\n+ */\n+\n+#include \"test.h\"\n+\n+#ifndef RTE_EXEC_ENV_LINUX\n+\n+static int\n+test_pmu(void)\n+{\n+\tprintf(\"pmu_autotest only supported on Linux, skipping test\\n\");\n+\treturn TEST_SKIPPED;\n+}\n+\n+#else\n+\n+#include <rte_pmu.h>\n+\n+static int\n+test_pmu_read(void)\n+{\n+\tint tries = 10, event = -1;\n+\tuint64_t val = 0;\n+\n+\tif (rte_pmu_init() < 0)\n+\t\treturn TEST_FAILED;\n+\n+\twhile (tries--)\n+\t\tval += rte_pmu_read(event);\n+\n+\trte_pmu_fini();\n+\n+\treturn val ? TEST_SUCCESS : TEST_FAILED;\n+}\n+\n+static struct unit_test_suite pmu_tests = {\n+\t.suite_name = \"pmu autotest\",\n+\t.setup = NULL,\n+\t.teardown = NULL,\n+\t.unit_test_cases = {\n+\t\tTEST_CASE(test_pmu_read),\n+\t\tTEST_CASES_END()\n+\t}\n+};\n+\n+static int\n+test_pmu(void)\n+{\n+\treturn unit_test_suite_runner(&pmu_tests);\n+}\n+\n+#endif /* RTE_EXEC_ENV_LINUX */\n+\n+REGISTER_TEST_COMMAND(pmu_autotest, test_pmu);\ndiff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md\nindex de488c7abf..7f1938f92f 100644\n--- a/doc/api/doxy-api-index.md\n+++ b/doc/api/doxy-api-index.md\n@@ -222,7 +222,8 @@ The public API headers are grouped by topics:\n   [log](@ref rte_log.h),\n   [errno](@ref rte_errno.h),\n   [trace](@ref rte_trace.h),\n-  [trace_point](@ref rte_trace_point.h)\n+  [trace_point](@ref rte_trace_point.h),\n+  [pmu](@ref rte_pmu.h)\n \n - **misc**:\n   [EAL config](@ref rte_eal.h),\ndiff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in\nindex f0886c3bd1..920e615996 100644\n--- a/doc/api/doxy-api.conf.in\n+++ b/doc/api/doxy-api.conf.in\n@@ -63,6 +63,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \\\n                           @TOPDIR@/lib/pci \\\n                           @TOPDIR@/lib/pdump \\\n                           @TOPDIR@/lib/pipeline \\\n+                          @TOPDIR@/lib/pmu \\\n                           @TOPDIR@/lib/port \\\n                           @TOPDIR@/lib/power \\\n                           @TOPDIR@/lib/rawdev \\\ndiff --git a/doc/guides/prog_guide/profile_app.rst b/doc/guides/prog_guide/profile_app.rst\nindex 14292d4c25..a8b501fe0c 100644\n--- a/doc/guides/prog_guide/profile_app.rst\n+++ b/doc/guides/prog_guide/profile_app.rst\n@@ -7,6 +7,14 @@ Profile Your Application\n The following sections describe methods of profiling DPDK applications on\n different architectures.\n \n+Performance counter based profiling\n+-----------------------------------\n+\n+Majority of architectures support some sort hardware measurement unit which provides a set of\n+programmable counters that monitor specific events. There are different tools which can gather\n+that information, perf being an example here. Though in some scenarios, eg. when CPU cores are\n+isolated (nohz_full) and run dedicated tasks, using perf is less than ideal. In such cases one can\n+read specific events directly from application via ``rte_pmu_read()``.\n \n Profiling on x86\n ----------------\ndiff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst\nindex 73f5d94e14..733541d56c 100644\n--- a/doc/guides/rel_notes/release_23_03.rst\n+++ b/doc/guides/rel_notes/release_23_03.rst\n@@ -55,10 +55,19 @@ New Features\n      Also, make sure to start the actual text at the margin.\n      =======================================================\n \n+* **Added PMU library.**\n+\n+  Added a new PMU (performance measurement unit) library which allows applications\n+  to perform self monitoring activities without depending on external utilities like perf.\n+  After integration with :doc:`../prog_guide/trace_lib` data gathered from hardware counters\n+  can be stored in CTF format for further analysis.\n+\n * **Updated AMD axgbe driver.**\n \n   * Added multi-process support.\n \n+* **Added multi-process support for axgbe PMD.**\n+\n * **Updated Corigine nfp driver.**\n \n   * Added support for meter options.\ndiff --git a/lib/meson.build b/lib/meson.build\nindex a90fee31b7..7132131b5c 100644\n--- a/lib/meson.build\n+++ b/lib/meson.build\n@@ -11,6 +11,7 @@\n libraries = [\n         'kvargs', # eal depends on kvargs\n         'telemetry', # basic info querying\n+        'pmu',\n         'eal', # everything depends on eal\n         'ring',\n         'rcu', # rcu depends on ring\ndiff --git a/lib/pmu/meson.build b/lib/pmu/meson.build\nnew file mode 100644\nindex 0000000000..a4160b494e\n--- /dev/null\n+++ b/lib/pmu/meson.build\n@@ -0,0 +1,13 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(C) 2023 Marvell International Ltd.\n+\n+if not is_linux\n+    build = false\n+    reason = 'only supported on Linux'\n+    subdir_done()\n+endif\n+\n+includes = [global_inc]\n+\n+sources = files('rte_pmu.c')\n+headers = files('rte_pmu.h')\ndiff --git a/lib/pmu/pmu_private.h b/lib/pmu/pmu_private.h\nnew file mode 100644\nindex 0000000000..849549b125\n--- /dev/null\n+++ b/lib/pmu/pmu_private.h\n@@ -0,0 +1,29 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Marvell\n+ */\n+\n+#ifndef _PMU_PRIVATE_H_\n+#define _PMU_PRIVATE_H_\n+\n+/**\n+ * Architecture specific PMU init callback.\n+ *\n+ * @return\n+ *   0 in case of success, negative value otherwise.\n+ */\n+int\n+pmu_arch_init(void);\n+\n+/**\n+ * Architecture specific PMU cleanup callback.\n+ */\n+void\n+pmu_arch_fini(void);\n+\n+/**\n+ * Apply architecture specific settings to config before passing it to syscall.\n+ */\n+void\n+pmu_arch_fixup_config(uint64_t config[3]);\n+\n+#endif /* _PMU_PRIVATE_H_ */\ndiff --git a/lib/pmu/rte_pmu.c b/lib/pmu/rte_pmu.c\nnew file mode 100644\nindex 0000000000..4cf3161155\n--- /dev/null\n+++ b/lib/pmu/rte_pmu.c\n@@ -0,0 +1,464 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2023 Marvell International Ltd.\n+ */\n+\n+#include <ctype.h>\n+#include <dirent.h>\n+#include <errno.h>\n+#include <regex.h>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <sys/ioctl.h>\n+#include <sys/mman.h>\n+#include <sys/queue.h>\n+#include <sys/syscall.h>\n+#include <unistd.h>\n+\n+#include <rte_atomic.h>\n+#include <rte_per_lcore.h>\n+#include <rte_pmu.h>\n+#include <rte_spinlock.h>\n+#include <rte_tailq.h>\n+\n+#include \"pmu_private.h\"\n+\n+#define EVENT_SOURCE_DEVICES_PATH \"/sys/bus/event_source/devices\"\n+\n+#ifndef GENMASK_ULL\n+#define GENMASK_ULL(h, l) ((~0ULL - (1ULL << (l)) + 1) & (~0ULL >> ((64 - 1 - (h)))))\n+#endif\n+\n+#ifndef FIELD_PREP\n+#define FIELD_PREP(m, v) (((uint64_t)(v) << (__builtin_ffsll(m) - 1)) & (m))\n+#endif\n+\n+RTE_DEFINE_PER_LCORE(struct rte_pmu_event_group, _event_group);\n+struct rte_pmu rte_pmu;\n+\n+/*\n+ * Following __rte_weak functions provide default no-op. Architectures should override them if\n+ * necessary.\n+ */\n+\n+int\n+__rte_weak pmu_arch_init(void)\n+{\n+\treturn 0;\n+}\n+\n+void\n+__rte_weak pmu_arch_fini(void)\n+{\n+}\n+\n+void\n+__rte_weak pmu_arch_fixup_config(uint64_t __rte_unused config[3])\n+{\n+}\n+\n+static int\n+get_term_format(const char *name, int *num, uint64_t *mask)\n+{\n+\tchar *config = NULL;\n+\tchar path[PATH_MAX];\n+\tint high, low, ret;\n+\tFILE *fp;\n+\n+\t/* quiesce -Wmaybe-uninitialized warning */\n+\t*num = 0;\n+\t*mask = 0;\n+\n+\tsnprintf(path, sizeof(path), EVENT_SOURCE_DEVICES_PATH \"/%s/format/%s\", rte_pmu.name, name);\n+\tfp = fopen(path, \"r\");\n+\tif (fp == NULL)\n+\t\treturn -errno;\n+\n+\terrno = 0;\n+\tret = fscanf(fp, \"%m[^:]:%d-%d\", &config, &low, &high);\n+\tif (ret < 2) {\n+\t\tret = -ENODATA;\n+\t\tgoto out;\n+\t}\n+\tif (errno) {\n+\t\tret = -errno;\n+\t\tgoto out;\n+\t}\n+\n+\tif (ret == 2)\n+\t\thigh = low;\n+\n+\t*mask = GENMASK_ULL(high, low);\n+\t/* Last digit should be [012]. If last digit is missing 0 is implied. */\n+\t*num = config[strlen(config) - 1];\n+\t*num = isdigit(*num) ? *num - '0' : 0;\n+\n+\tret = 0;\n+out:\n+\tfree(config);\n+\tfclose(fp);\n+\n+\treturn ret;\n+}\n+\n+static int\n+parse_event(char *buf, uint64_t config[3])\n+{\n+\tchar *token, *term;\n+\tint num, ret, val;\n+\tuint64_t mask;\n+\n+\tconfig[0] = config[1] = config[2] = 0;\n+\n+\ttoken = strtok(buf, \",\");\n+\twhile (token) {\n+\t\terrno = 0;\n+\t\t/* <term>=<value> */\n+\t\tret = sscanf(token, \"%m[^=]=%i\", &term, &val);\n+\t\tif (ret < 1)\n+\t\t\treturn -ENODATA;\n+\t\tif (errno)\n+\t\t\treturn -errno;\n+\t\tif (ret == 1)\n+\t\t\tval = 1;\n+\n+\t\tret = get_term_format(term, &num, &mask);\n+\t\tfree(term);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tconfig[num] |= FIELD_PREP(mask, val);\n+\t\ttoken = strtok(NULL, \",\");\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+get_event_config(const char *name, uint64_t config[3])\n+{\n+\tchar path[PATH_MAX], buf[BUFSIZ];\n+\tFILE *fp;\n+\tint ret;\n+\n+\tsnprintf(path, sizeof(path), EVENT_SOURCE_DEVICES_PATH \"/%s/events/%s\", rte_pmu.name, name);\n+\tfp = fopen(path, \"r\");\n+\tif (fp == NULL)\n+\t\treturn -errno;\n+\n+\tret = fread(buf, 1, sizeof(buf), fp);\n+\tif (ret == 0) {\n+\t\tfclose(fp);\n+\n+\t\treturn -EINVAL;\n+\t}\n+\tfclose(fp);\n+\tbuf[ret] = '\\0';\n+\n+\treturn parse_event(buf, config);\n+}\n+\n+static int\n+do_perf_event_open(uint64_t config[3], int group_fd)\n+{\n+\tstruct perf_event_attr attr = {\n+\t\t.size = sizeof(struct perf_event_attr),\n+\t\t.type = PERF_TYPE_RAW,\n+\t\t.exclude_kernel = 1,\n+\t\t.exclude_hv = 1,\n+\t\t.disabled = 1,\n+\t};\n+\n+\tpmu_arch_fixup_config(config);\n+\n+\tattr.config = config[0];\n+\tattr.config1 = config[1];\n+\tattr.config2 = config[2];\n+\n+\treturn syscall(SYS_perf_event_open, &attr, 0, -1, group_fd, 0);\n+}\n+\n+static int\n+open_events(struct rte_pmu_event_group *group)\n+{\n+\tstruct rte_pmu_event *event;\n+\tuint64_t config[3];\n+\tint num = 0, ret;\n+\n+\t/* group leader gets created first, with fd = -1 */\n+\tgroup->fds[0] = -1;\n+\n+\tTAILQ_FOREACH(event, &rte_pmu.event_list, next) {\n+\t\tret = get_event_config(event->name, config);\n+\t\tif (ret)\n+\t\t\tcontinue;\n+\n+\t\tret = do_perf_event_open(config, group->fds[0]);\n+\t\tif (ret == -1) {\n+\t\t\tret = -errno;\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tgroup->fds[event->index] = ret;\n+\t\tnum++;\n+\t}\n+\n+\treturn 0;\n+out:\n+\tfor (--num; num >= 0; num--) {\n+\t\tclose(group->fds[num]);\n+\t\tgroup->fds[num] = -1;\n+\t}\n+\n+\n+\treturn ret;\n+}\n+\n+static int\n+mmap_events(struct rte_pmu_event_group *group)\n+{\n+\tlong page_size = sysconf(_SC_PAGE_SIZE);\n+\tunsigned int i;\n+\tvoid *addr;\n+\tint ret;\n+\n+\tfor (i = 0; i < rte_pmu.num_group_events; i++) {\n+\t\taddr = mmap(0, page_size, PROT_READ, MAP_SHARED, group->fds[i], 0);\n+\t\tif (addr == MAP_FAILED) {\n+\t\t\tret = -errno;\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tgroup->mmap_pages[i] = addr;\n+\t}\n+\n+\treturn 0;\n+out:\n+\tfor (; i; i--) {\n+\t\tmunmap(group->mmap_pages[i - 1], page_size);\n+\t\tgroup->mmap_pages[i - 1] = NULL;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static void\n+cleanup_events(struct rte_pmu_event_group *group)\n+{\n+\tunsigned int i;\n+\n+\tif (group->fds[0] != -1)\n+\t\tioctl(group->fds[0], PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);\n+\n+\tfor (i = 0; i < rte_pmu.num_group_events; i++) {\n+\t\tif (group->mmap_pages[i]) {\n+\t\t\tmunmap(group->mmap_pages[i], sysconf(_SC_PAGE_SIZE));\n+\t\t\tgroup->mmap_pages[i] = NULL;\n+\t\t}\n+\n+\t\tif (group->fds[i] != -1) {\n+\t\t\tclose(group->fds[i]);\n+\t\t\tgroup->fds[i] = -1;\n+\t\t}\n+\t}\n+\n+\tgroup->enabled = false;\n+}\n+\n+int __rte_noinline\n+rte_pmu_enable_group(void)\n+{\n+\tstruct rte_pmu_event_group *group = &RTE_PER_LCORE(_event_group);\n+\tint ret;\n+\n+\tif (rte_pmu.num_group_events == 0)\n+\t\treturn -ENODEV;\n+\n+\tret = open_events(group);\n+\tif (ret)\n+\t\tgoto out;\n+\n+\tret = mmap_events(group);\n+\tif (ret)\n+\t\tgoto out;\n+\n+\tif (ioctl(group->fds[0], PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {\n+\t\tret = -errno;\n+\t\tgoto out;\n+\t}\n+\n+\tif (ioctl(group->fds[0], PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {\n+\t\tret = -errno;\n+\t\tgoto out;\n+\t}\n+\n+\trte_spinlock_lock(&rte_pmu.lock);\n+\tTAILQ_INSERT_TAIL(&rte_pmu.event_group_list, group, next);\n+\trte_spinlock_unlock(&rte_pmu.lock);\n+\tgroup->enabled = true;\n+\n+\treturn 0;\n+\n+out:\n+\tcleanup_events(group);\n+\n+\treturn ret;\n+}\n+\n+static int\n+scan_pmus(void)\n+{\n+\tchar path[PATH_MAX];\n+\tstruct dirent *dent;\n+\tconst char *name;\n+\tDIR *dirp;\n+\n+\tdirp = opendir(EVENT_SOURCE_DEVICES_PATH);\n+\tif (dirp == NULL)\n+\t\treturn -errno;\n+\n+\twhile ((dent = readdir(dirp))) {\n+\t\tname = dent->d_name;\n+\t\tif (name[0] == '.')\n+\t\t\tcontinue;\n+\n+\t\t/* sysfs entry should either contain cpus or be a cpu */\n+\t\tif (!strcmp(name, \"cpu\"))\n+\t\t\tbreak;\n+\n+\t\tsnprintf(path, sizeof(path), EVENT_SOURCE_DEVICES_PATH \"/%s/cpus\", name);\n+\t\tif (access(path, F_OK) == 0)\n+\t\t\tbreak;\n+\t}\n+\n+\tif (dent) {\n+\t\trte_pmu.name = strdup(name);\n+\t\tif (rte_pmu.name == NULL) {\n+\t\t\tclosedir(dirp);\n+\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\t}\n+\n+\tclosedir(dirp);\n+\n+\treturn rte_pmu.name ? 0 : -ENODEV;\n+}\n+\n+static struct rte_pmu_event *\n+new_event(const char *name)\n+{\n+\tstruct rte_pmu_event *event;\n+\n+\tevent = calloc(1, sizeof(*event));\n+\tif (event == NULL)\n+\t\tgoto out;\n+\n+\tevent->name = strdup(name);\n+\tif (event->name == NULL) {\n+\t\tfree(event);\n+\t\tevent = NULL;\n+\t}\n+\n+out:\n+\treturn event;\n+}\n+\n+static void\n+free_event(struct rte_pmu_event *event)\n+{\n+\tfree(event->name);\n+\tfree(event);\n+}\n+\n+int\n+rte_pmu_add_event(const char *name)\n+{\n+\tstruct rte_pmu_event *event;\n+\tchar path[PATH_MAX];\n+\n+\tif (rte_pmu.name == NULL)\n+\t\treturn -ENODEV;\n+\n+\tif (rte_pmu.num_group_events + 1 >= MAX_NUM_GROUP_EVENTS)\n+\t\treturn -ENOSPC;\n+\n+\tsnprintf(path, sizeof(path), EVENT_SOURCE_DEVICES_PATH \"/%s/events/%s\", rte_pmu.name, name);\n+\tif (access(path, R_OK))\n+\t\treturn -ENODEV;\n+\n+\tTAILQ_FOREACH(event, &rte_pmu.event_list, next) {\n+\t\tif (!strcmp(event->name, name))\n+\t\t\treturn event->index;\n+\t\tcontinue;\n+\t}\n+\n+\tevent = new_event(name);\n+\tif (event == NULL)\n+\t\treturn -ENOMEM;\n+\n+\tevent->index = rte_pmu.num_group_events++;\n+\tTAILQ_INSERT_TAIL(&rte_pmu.event_list, event, next);\n+\n+\treturn event->index;\n+}\n+\n+int\n+rte_pmu_init(void)\n+{\n+\tint ret;\n+\n+\t/* Allow calling init from multiple contexts within a single thread. This simplifies\n+\t * resource management a bit e.g in case fast-path tracepoint has already been enabled\n+\t * via command line but application doesn't care enough and performs init/fini again.\n+\t */\n+\tif (rte_pmu.initialized) {\n+\t\trte_pmu.initialized++;\n+\t\treturn 0;\n+\t}\n+\n+\tret = scan_pmus();\n+\tif (ret)\n+\t\tgoto out;\n+\n+\tret = pmu_arch_init();\n+\tif (ret)\n+\t\tgoto out;\n+\n+\tTAILQ_INIT(&rte_pmu.event_list);\n+\tTAILQ_INIT(&rte_pmu.event_group_list);\n+\trte_spinlock_init(&rte_pmu.lock);\n+\trte_pmu.initialized = 1;\n+\n+\treturn 0;\n+out:\n+\tfree(rte_pmu.name);\n+\trte_pmu.name = NULL;\n+\n+\treturn ret;\n+}\n+\n+void\n+rte_pmu_fini(void)\n+{\n+\tstruct rte_pmu_event_group *group, *tmp_group;\n+\tstruct rte_pmu_event *event, *tmp_event;\n+\n+\t/* cleanup once init count drops to zero */\n+\tif (!rte_pmu.initialized || --rte_pmu.initialized)\n+\t\treturn;\n+\n+\tRTE_TAILQ_FOREACH_SAFE(event, &rte_pmu.event_list, next, tmp_event) {\n+\t\tTAILQ_REMOVE(&rte_pmu.event_list, event, next);\n+\t\tfree_event(event);\n+\t}\n+\n+\tRTE_TAILQ_FOREACH_SAFE(group, &rte_pmu.event_group_list, next, tmp_group) {\n+\t\tTAILQ_REMOVE(&rte_pmu.event_group_list, group, next);\n+\t\tcleanup_events(group);\n+\t}\n+\n+\tpmu_arch_fini();\n+\tfree(rte_pmu.name);\n+\trte_pmu.name = NULL;\n+\trte_pmu.num_group_events = 0;\n+}\ndiff --git a/lib/pmu/rte_pmu.h b/lib/pmu/rte_pmu.h\nnew file mode 100644\nindex 0000000000..e360375a0c\n--- /dev/null\n+++ b/lib/pmu/rte_pmu.h\n@@ -0,0 +1,205 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Marvell\n+ */\n+\n+#ifndef _RTE_PMU_H_\n+#define _RTE_PMU_H_\n+\n+/**\n+ * @file\n+ *\n+ * PMU event tracing operations\n+ *\n+ * This file defines generic API and types necessary to setup PMU and\n+ * read selected counters in runtime.\n+ */\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+#include <linux/perf_event.h>\n+\n+#include <rte_atomic.h>\n+#include <rte_branch_prediction.h>\n+#include <rte_common.h>\n+#include <rte_compat.h>\n+#include <rte_spinlock.h>\n+\n+/** Maximum number of events in a group */\n+#define MAX_NUM_GROUP_EVENTS 8\n+\n+/**\n+ * A structure describing a group of events.\n+ */\n+struct rte_pmu_event_group {\n+\tstruct perf_event_mmap_page *mmap_pages[MAX_NUM_GROUP_EVENTS]; /**< array of user pages */\n+\tint fds[MAX_NUM_GROUP_EVENTS]; /**< array of event descriptors */\n+\tbool enabled; /**< true if group was enabled on particular lcore */\n+\tTAILQ_ENTRY(rte_pmu_event_group) next; /**< list entry */\n+} __rte_cache_aligned;\n+\n+/**\n+ * A structure describing an event.\n+ */\n+struct rte_pmu_event {\n+\tchar *name; /**< name of an event */\n+\tunsigned int index; /**< event index into fds/mmap_pages */\n+\tTAILQ_ENTRY(rte_pmu_event) next; /**< list entry */\n+};\n+\n+/**\n+ * A PMU state container.\n+ */\n+struct rte_pmu {\n+\tchar *name; /**< name of core PMU listed under /sys/bus/event_source/devices */\n+\trte_spinlock_t lock; /**< serialize access to event group list */\n+\tTAILQ_HEAD(, rte_pmu_event_group) event_group_list; /**< list of event groups */\n+\tunsigned int num_group_events; /**< number of events in a group */\n+\tTAILQ_HEAD(, rte_pmu_event) event_list; /**< list of matching events */\n+\tunsigned int initialized; /**< initialization counter */\n+};\n+\n+/** lcore event group */\n+RTE_DECLARE_PER_LCORE(struct rte_pmu_event_group, _event_group);\n+\n+/** PMU state container */\n+extern struct rte_pmu rte_pmu;\n+\n+/** Each architecture supporting PMU needs to provide its own version */\n+#ifndef rte_pmu_pmc_read\n+#define rte_pmu_pmc_read(index) ({ 0; })\n+#endif\n+\n+/**\n+ * @internal\n+ *\n+ * Read PMU counter.\n+ *\n+ * @param pc\n+ *   Pointer to the mmapped user page.\n+ * @return\n+ *   Counter value read from hardware.\n+ */\n+__rte_internal\n+static __rte_always_inline uint64_t\n+rte_pmu_read_userpage(struct perf_event_mmap_page *pc)\n+{\n+\tuint64_t width, offset;\n+\tuint32_t seq, index;\n+\tint64_t pmc;\n+\n+\tfor (;;) {\n+\t\tseq = pc->lock;\n+\t\trte_compiler_barrier();\n+\t\tindex = pc->index;\n+\t\toffset = pc->offset;\n+\t\twidth = pc->pmc_width;\n+\n+\t\t/* index set to 0 means that particular counter cannot be used */\n+\t\tif (likely(pc->cap_user_rdpmc && index)) {\n+\t\t\tpmc = rte_pmu_pmc_read(index - 1);\n+\t\t\tpmc <<= 64 - width;\n+\t\t\tpmc >>= 64 - width;\n+\t\t\toffset += pmc;\n+\t\t}\n+\n+\t\trte_compiler_barrier();\n+\n+\t\tif (likely(pc->lock == seq))\n+\t\t\treturn offset;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * @internal\n+ *\n+ * Enable group of events on the calling lcore.\n+ *\n+ * @return\n+ *   0 in case of success, negative value otherwise.\n+ */\n+__rte_internal\n+int\n+rte_pmu_enable_group(void);\n+\n+/**\n+ * @warning\n+ * @b EXPERIMENTAL: this API may change without prior notice\n+ *\n+ * Initialize PMU library.\n+ *\n+ * @return\n+ *   0 in case of success, negative value otherwise.\n+ */\n+__rte_experimental\n+int\n+rte_pmu_init(void);\n+\n+/**\n+ * @warning\n+ * @b EXPERIMENTAL: this API may change without prior notice\n+ *\n+ * Finalize PMU library. This should be called after PMU counters are no longer being read.\n+ */\n+__rte_experimental\n+void\n+rte_pmu_fini(void);\n+\n+/**\n+ * @warning\n+ * @b EXPERIMENTAL: this API may change without prior notice\n+ *\n+ * Add event to the group of enabled events.\n+ *\n+ * @param name\n+ *   Name of an event listed under /sys/bus/event_source/devices/pmu/events.\n+ * @return\n+ *   Event index in case of success, negative value otherwise.\n+ */\n+__rte_experimental\n+int\n+rte_pmu_add_event(const char *name);\n+\n+/**\n+ * @warning\n+ * @b EXPERIMENTAL: this API may change without prior notice\n+ *\n+ * Read hardware counter configured to count occurrences of an event.\n+ *\n+ * @param index\n+ *   Index of an event to be read.\n+ * @return\n+ *   Event value read from register. In case of errors or lack of support\n+ *   0 is returned. In other words, stream of zeros in a trace file\n+ *   indicates problem with reading particular PMU event register.\n+ */\n+__rte_experimental\n+static __rte_always_inline uint64_t\n+rte_pmu_read(unsigned int index)\n+{\n+\tstruct rte_pmu_event_group *group = &RTE_PER_LCORE(_event_group);\n+\tint ret;\n+\n+\tif (unlikely(!rte_pmu.initialized))\n+\t\treturn 0;\n+\n+\tif (unlikely(!group->enabled)) {\n+\t\tret = rte_pmu_enable_group();\n+\t\tif (ret)\n+\t\t\treturn 0;\n+\t}\n+\n+\tif (unlikely(index >= rte_pmu.num_group_events))\n+\t\treturn 0;\n+\n+\treturn rte_pmu_read_userpage(group->mmap_pages[index]);\n+}\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif /* _RTE_PMU_H_ */\ndiff --git a/lib/pmu/version.map b/lib/pmu/version.map\nnew file mode 100644\nindex 0000000000..50fb0f354e\n--- /dev/null\n+++ b/lib/pmu/version.map\n@@ -0,0 +1,20 @@\n+DPDK_23 {\n+\tlocal: *;\n+};\n+\n+EXPERIMENTAL {\n+\tglobal:\n+\n+\tper_lcore__event_group;\n+\trte_pmu;\n+\trte_pmu_add_event;\n+\trte_pmu_fini;\n+\trte_pmu_init;\n+\trte_pmu_read;\n+};\n+\n+INTERNAL {\n+\tglobal:\n+\n+\trte_pmu_enable_group;\n+};\n",
    "prefixes": [
        "v9",
        "1/4"
    ]
}