get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 120259,
    "url": "http://patches.dpdk.org/api/patches/120259/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20221129092821.1304853-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": "<20221129092821.1304853-2-tduszynski@marvell.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20221129092821.1304853-2-tduszynski@marvell.com",
    "date": "2022-11-29T09:28:18",
    "name": "[v3,1/4] eal: add generic support for reading PMU events",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "054424fbbf52ccee53a86039e4a564f8e730a1f9",
    "submitter": {
        "id": 2215,
        "url": "http://patches.dpdk.org/api/people/2215/?format=api",
        "name": "Tomasz Duszynski",
        "email": "tduszynski@marvell.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20221129092821.1304853-2-tduszynski@marvell.com/mbox/",
    "series": [
        {
            "id": 25916,
            "url": "http://patches.dpdk.org/api/series/25916/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=25916",
            "date": "2022-11-29T09:28:17",
            "name": "add support for self monitoring",
            "version": 3,
            "mbox": "http://patches.dpdk.org/series/25916/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/120259/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/120259/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 CA89CA0093;\n\tTue, 29 Nov 2022 10:28:40 +0100 (CET)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 732F342B8C;\n\tTue, 29 Nov 2022 10:28:38 +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 5F6B642686\n for <dev@dpdk.org>; Tue, 29 Nov 2022 10:28:36 +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 2AT743Qq020463; Tue, 29 Nov 2022 01:28:35 -0800",
            "from dc5-exch02.marvell.com ([199.233.59.182])\n by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 3m3k6wamrr-1\n (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT);\n Tue, 29 Nov 2022 01:28:35 -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.18;\n Tue, 29 Nov 2022 01:28:33 -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.2 via Frontend\n Transport; Tue, 29 Nov 2022 01:28:32 -0800",
            "from localhost.localdomain (unknown [10.28.34.39])\n by maili.marvell.com (Postfix) with ESMTP id 867BD3F7051;\n Tue, 29 Nov 2022 01:28:31 -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-transfer-encoding : content-type; s=pfpt0220;\n bh=FDO/YPDxPhaIv0p13KbgpdcCApmLmOomRNBUAaDy0Mo=;\n b=lKLNAVShYsiUc8bTQVUcQNEjX2v63tWehqzzSiDcnBN2T2/wLbFuoduUVElhpc0vEw2e\n RSfDMh4NrWgkm1Ce85PH2AvJei7TAjtGZrcg+QgcGNxBVTapU49h5fuVkOwVatYwOaqW\n 2LSKEf9V17DfaYdQTznPDo2mL90vELv4onLte8U6SZWOrIF/tLlMA3E0CZ3lUYc0P+Rq\n PQMiTEuArIJPWRgdrqs7vfnCWR2jaf7tzlNPH3AwJYsOpbFE26F/0Pe9yP+Llc5alZrX\n t5avcJs+M9nwfJG936xuUmaQPHk9ofB/f/GoP4ZZLNeRKOtEiPeJhC0be0Zqc/z6CZLD PQ==",
        "From": "Tomasz Duszynski <tduszynski@marvell.com>",
        "To": "<dev@dpdk.org>",
        "CC": "<thomas@monjalon.net>, <jerinj@marvell.com>, Tomasz Duszynski\n <tduszynski@marvell.com>",
        "Subject": "[PATCH v3 1/4] eal: add generic support for reading PMU events",
        "Date": "Tue, 29 Nov 2022 10:28:18 +0100",
        "Message-ID": "<20221129092821.1304853-2-tduszynski@marvell.com>",
        "X-Mailer": "git-send-email 2.25.1",
        "In-Reply-To": "<20221129092821.1304853-1-tduszynski@marvell.com>",
        "References": "<20221121121121.3917194-1-tduszynski@marvell.com>\n <20221129092821.1304853-1-tduszynski@marvell.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Content-Type": "text/plain",
        "X-Proofpoint-GUID": "2cRRK6VwA1r31q7cRu8c5g5c5vHmcqtf",
        "X-Proofpoint-ORIG-GUID": "2cRRK6VwA1r31q7cRu8c5g5c5vHmcqtf",
        "X-Proofpoint-Virus-Version": "vendor=baseguard\n engine=ICAP:2.0.219,Aquarius:18.0.895,Hydra:6.0.545,FMLib:17.11.122.1\n definitions=2022-11-29_06,2022-11-28_02,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>\n---\n app/test/meson.build                  |   1 +\n app/test/test_pmu.c                   |  41 +++\n doc/guides/prog_guide/profile_app.rst |   8 +\n lib/eal/common/meson.build            |   3 +\n lib/eal/common/pmu_private.h          |  41 +++\n lib/eal/common/rte_pmu.c              | 457 ++++++++++++++++++++++++++\n lib/eal/include/meson.build           |   1 +\n lib/eal/include/rte_pmu.h             | 204 ++++++++++++\n lib/eal/linux/eal.c                   |   4 +\n lib/eal/version.map                   |   6 +\n 10 files changed, 766 insertions(+)\n create mode 100644 app/test/test_pmu.c\n create mode 100644 lib/eal/common/pmu_private.h\n create mode 100644 lib/eal/common/rte_pmu.c\n create mode 100644 lib/eal/include/rte_pmu.h",
    "diff": "diff --git a/app/test/meson.build b/app/test/meson.build\nindex f34d19e3c3..93b3300309 100644\n--- a/app/test/meson.build\n+++ b/app/test/meson.build\n@@ -143,6 +143,7 @@ test_sources = files(\n         'test_timer_racecond.c',\n         'test_timer_secondary.c',\n         'test_ticketlock.c',\n+        'test_pmu.c',\n         'test_trace.c',\n         'test_trace_register.c',\n         'test_trace_perf.c',\ndiff --git a/app/test/test_pmu.c b/app/test/test_pmu.c\nnew file mode 100644\nindex 0000000000..fd331af9ee\n--- /dev/null\n+++ b/app/test/test_pmu.c\n@@ -0,0 +1,41 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2022 Marvell International Ltd.\n+ */\n+\n+#include <rte_pmu.h>\n+\n+#include \"test.h\"\n+\n+static int\n+test_pmu_read(void)\n+{\n+\tuint64_t val = 0;\n+\tint tries = 10;\n+\tint event = -1;\n+\n+\twhile (tries--)\n+\t\tval += rte_pmu_read(event);\n+\n+\tif (val == 0)\n+\t\treturn TEST_FAILED;\n+\n+\treturn TEST_SUCCESS;\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+REGISTER_TEST_COMMAND(pmu_autotest, test_pmu);\ndiff --git a/doc/guides/prog_guide/profile_app.rst b/doc/guides/prog_guide/profile_app.rst\nindex bd6700ef85..8fc1b20cab 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/lib/eal/common/meson.build b/lib/eal/common/meson.build\nindex 917758cc65..d6d05b56f3 100644\n--- a/lib/eal/common/meson.build\n+++ b/lib/eal/common/meson.build\n@@ -38,6 +38,9 @@ sources += files(\n         'rte_service.c',\n         'rte_version.c',\n )\n+if is_linux\n+    sources += files('rte_pmu.c')\n+endif\n if is_linux or is_windows\n     sources += files('eal_common_dynmem.c')\n endif\ndiff --git a/lib/eal/common/pmu_private.h b/lib/eal/common/pmu_private.h\nnew file mode 100644\nindex 0000000000..cade4245e6\n--- /dev/null\n+++ b/lib/eal/common/pmu_private.h\n@@ -0,0 +1,41 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2022 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+/**\n+ * Initialize PMU tracing internals.\n+ */\n+void\n+eal_pmu_init(void);\n+\n+/**\n+ * Cleanup PMU internals.\n+ */\n+void\n+eal_pmu_fini(void);\n+\n+#endif /* _PMU_PRIVATE_H_ */\ndiff --git a/lib/eal/common/rte_pmu.c b/lib/eal/common/rte_pmu.c\nnew file mode 100644\nindex 0000000000..6763005903\n--- /dev/null\n+++ b/lib/eal/common/rte_pmu.c\n@@ -0,0 +1,457 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2022 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_eal_paging.h>\n+#include <rte_malloc.h>\n+#include <rte_pmu.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+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 config[3])\n+{\n+\tRTE_SET_USED(config);\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)\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)\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 lcore_id, 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, rte_gettid(), rte_lcore_to_cpu_id(lcore_id),\n+\t\t       group_fd, 0);\n+}\n+\n+static int\n+open_events(int lcore_id)\n+{\n+\tstruct rte_pmu_event_group *group = &rte_pmu->group[lcore_id];\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\tRTE_LOG(ERR, EAL, \"failed to get %s event config\\n\", event->name);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tret = do_perf_event_open(config, lcore_id, group->fds[0]);\n+\t\tif (ret == -1) {\n+\t\t\tif (errno == EOPNOTSUPP)\n+\t\t\t\tRTE_LOG(ERR, EAL, \"64 bit counters not supported\\n\");\n+\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(int lcore_id)\n+{\n+\tstruct rte_pmu_event_group *group = &rte_pmu->group[lcore_id];\n+\tvoid *addr;\n+\tint ret, i;\n+\n+\tfor (i = 0; i < rte_pmu->num_group_events; i++) {\n+\t\taddr = mmap(0, rte_mem_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], rte_mem_page_size());\n+\t\tgroup->mmap_pages[i - 1] = NULL;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static void\n+cleanup_events(int lcore_id)\n+{\n+\tstruct rte_pmu_event_group *group = &rte_pmu->group[lcore_id];\n+\tint i;\n+\n+\tif (!group->fds)\n+\t\treturn;\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], rte_mem_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+\trte_free(group->mmap_pages);\n+\trte_free(group->fds);\n+\n+\tgroup->mmap_pages = NULL;\n+\tgroup->fds = NULL;\n+\tgroup->enabled = false;\n+}\n+\n+int __rte_noinline\n+rte_pmu_enable_group(int lcore_id)\n+{\n+\tstruct rte_pmu_event_group *group = &rte_pmu->group[lcore_id];\n+\tint ret;\n+\n+\tif (rte_pmu->num_group_events == 0) {\n+\t\tRTE_LOG(DEBUG, EAL, \"no matching PMU events\\n\");\n+\n+\t\treturn 0;\n+\t}\n+\n+\tgroup->fds = rte_calloc(NULL, rte_pmu->num_group_events, sizeof(*group->fds), 0);\n+\tif (!group->fds) {\n+\t\tRTE_LOG(ERR, EAL, \"failed to alloc descriptor memory\\n\");\n+\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tgroup->mmap_pages = rte_calloc(NULL, rte_pmu->num_group_events, sizeof(*group->mmap_pages), 0);\n+\tif (!group->mmap_pages) {\n+\t\tRTE_LOG(ERR, EAL, \"failed to alloc userpage memory\\n\");\n+\n+\t\tret = -ENOMEM;\n+\t\tgoto out;\n+\t}\n+\n+\tret = open_events(lcore_id);\n+\tif (ret) {\n+\t\tRTE_LOG(ERR, EAL, \"failed to open events on lcore-worker-%d\\n\", lcore_id);\n+\t\tgoto out;\n+\t}\n+\n+\tret = mmap_events(lcore_id);\n+\tif (ret) {\n+\t\tRTE_LOG(ERR, EAL, \"failed to map events on lcore-worker-%d\\n\", lcore_id);\n+\t\tgoto out;\n+\t}\n+\n+\tif (ioctl(group->fds[0], PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {\n+\t\tRTE_LOG(ERR, EAL, \"failed to enable events on lcore-worker-%d\\n\", lcore_id);\n+\n+\t\tret = -errno;\n+\t\tgoto out;\n+\t}\n+\n+\treturn 0;\n+\n+out:\n+\tcleanup_events(lcore_id);\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)\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+\tclosedir(dirp);\n+\n+\tif (dent) {\n+\t\trte_pmu->name = strdup(name);\n+\t\tif (!rte_pmu->name)\n+\t\t\treturn -ENOMEM;\n+\t}\n+\n+\treturn rte_pmu->name ? 0 : -ENODEV;\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+\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 = rte_calloc(NULL, 1, sizeof(*event), 0);\n+\tif (!event)\n+\t\treturn -ENOMEM;\n+\n+\tevent->name = strdup(name);\n+\tif (!event->name) {\n+\t\trte_free(event);\n+\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tevent->index = rte_pmu->num_group_events++;\n+\tTAILQ_INSERT_TAIL(&rte_pmu->event_list, event, next);\n+\n+\tRTE_LOG(DEBUG, EAL, \"%s even added at index %d\\n\", name, event->index);\n+\n+\treturn event->index;\n+}\n+\n+void\n+eal_pmu_init(void)\n+{\n+\tint ret;\n+\n+\trte_pmu = rte_calloc(NULL, 1, sizeof(*rte_pmu), RTE_CACHE_LINE_SIZE);\n+\tif (!rte_pmu) {\n+\t\tRTE_LOG(ERR, EAL, \"failed to alloc PMU\\n\");\n+\n+\t\treturn;\n+\t}\n+\n+\tTAILQ_INIT(&rte_pmu->event_list);\n+\n+\tret = scan_pmus();\n+\tif (ret) {\n+\t\tRTE_LOG(ERR, EAL, \"failed to find core pmu\\n\");\n+\t\tgoto out;\n+\t}\n+\n+\tret = pmu_arch_init();\n+\tif (ret) {\n+\t\tRTE_LOG(ERR, EAL, \"failed to setup arch for PMU\\n\");\n+\t\tgoto out;\n+\t}\n+\n+\treturn;\n+out:\n+\tfree(rte_pmu->name);\n+\trte_free(rte_pmu);\n+}\n+\n+void\n+eal_pmu_fini(void)\n+{\n+\tstruct rte_pmu_event *event, *tmp;\n+\tint lcore_id;\n+\n+\tRTE_TAILQ_FOREACH_SAFE(event, &rte_pmu->event_list, next, tmp) {\n+\t\tTAILQ_REMOVE(&rte_pmu->event_list, event, next);\n+\t\tfree(event->name);\n+\t\trte_free(event);\n+\t}\n+\n+\tRTE_LCORE_FOREACH_WORKER(lcore_id)\n+\t\tcleanup_events(lcore_id);\n+\n+\tpmu_arch_fini();\n+\tfree(rte_pmu->name);\n+\trte_free(rte_pmu);\n+}\ndiff --git a/lib/eal/include/meson.build b/lib/eal/include/meson.build\nindex cfcd40aaed..3bf830adee 100644\n--- a/lib/eal/include/meson.build\n+++ b/lib/eal/include/meson.build\n@@ -36,6 +36,7 @@ headers += files(\n         'rte_pci_dev_features.h',\n         'rte_per_lcore.h',\n         'rte_pflock.h',\n+        'rte_pmu.h',\n         'rte_random.h',\n         'rte_reciprocal.h',\n         'rte_seqcount.h',\ndiff --git a/lib/eal/include/rte_pmu.h b/lib/eal/include/rte_pmu.h\nnew file mode 100644\nindex 0000000000..e4b4f6b052\n--- /dev/null\n+++ b/lib/eal/include/rte_pmu.h\n@@ -0,0 +1,204 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2022 Marvell\n+ */\n+\n+#ifndef _RTE_PMU_H_\n+#define _RTE_PMU_H_\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+#include <rte_common.h>\n+#include <rte_compat.h>\n+\n+#ifdef RTE_EXEC_ENV_LINUX\n+\n+#include <linux/perf_event.h>\n+\n+#include <rte_atomic.h>\n+#include <rte_branch_prediction.h>\n+#include <rte_lcore.h>\n+#include <rte_log.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+/**\n+ * A structure describing a group of events.\n+ */\n+struct rte_pmu_event_group {\n+\tint *fds; /**< array of event descriptors */\n+\tvoid **mmap_pages; /**< array of pointers to mmapped perf_event_attr structures */\n+\tbool enabled; /**< true if group was enabled on particular lcore */\n+};\n+\n+/**\n+ * A structure describing an event.\n+ */\n+struct rte_pmu_event {\n+\tchar *name; /** name of an event */\n+\tint 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+\tstruct rte_pmu_event_group group[RTE_MAX_LCORE]; /**< per lcore event group data */\n+\tint num_group_events; /**< number of events in a group */\n+\tTAILQ_HEAD(, rte_pmu_event) event_list; /**< list of matching events */\n+};\n+\n+/** Pointer to the 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 offset, width, pmc = 0;\n+\tuint32_t seq, index;\n+\tint tries = 100;\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\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}\n+\n+\t\trte_compiler_barrier();\n+\n+\t\tif (likely(pc->lock == seq))\n+\t\t\treturn pmc + offset;\n+\n+\t\tif (--tries == 0) {\n+\t\t\tRTE_LOG(DEBUG, EAL, \"failed to get perf_event_mmap_page lock\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * @internal\n+ *\n+ * Enable group of events for a given lcore.\n+ *\n+ * @param lcore_id\n+ *   The identifier of the lcore.\n+ * @return\n+ *   0 in case of success, negative value otherwise.\n+ */\n+__rte_internal\n+int\n+rte_pmu_enable_group(int lcore_id);\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(int index)\n+{\n+\tint lcore_id = rte_lcore_id();\n+\tstruct rte_pmu_event_group *group;\n+\tint ret;\n+\n+\tif (!rte_pmu)\n+\t\treturn 0;\n+\n+\tgroup = &rte_pmu->group[lcore_id];\n+\tif (!group->enabled) {\n+\t\tret = rte_pmu_enable_group(lcore_id);\n+\t\tif (ret)\n+\t\t\treturn 0;\n+\n+\t\tgroup->enabled = true;\n+\t}\n+\n+\tif (index < 0 || index >= rte_pmu->num_group_events)\n+\t\treturn 0;\n+\n+\treturn rte_pmu_read_userpage((struct perf_event_mmap_page *)group->mmap_pages[index]);\n+}\n+\n+#else /* !RTE_EXEC_ENV_LINUX */\n+\n+__rte_experimental\n+static int __rte_unused\n+rte_pmu_add_event(__rte_unused const char *name)\n+{\n+\treturn -1;\n+}\n+\n+__rte_experimental\n+static __rte_always_inline uint64_t\n+rte_pmu_read(__rte_unused int index)\n+{\n+\treturn 0;\n+}\n+\n+#endif /* RTE_EXEC_ENV_LINUX */\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif /* _RTE_PMU_H_ */\ndiff --git a/lib/eal/linux/eal.c b/lib/eal/linux/eal.c\nindex 8c118d0d9f..751a13b597 100644\n--- a/lib/eal/linux/eal.c\n+++ b/lib/eal/linux/eal.c\n@@ -53,6 +53,7 @@\n #include \"eal_options.h\"\n #include \"eal_vfio.h\"\n #include \"hotplug_mp.h\"\n+#include \"pmu_private.h\"\n \n #define MEMSIZE_IF_NO_HUGE_PAGE (64ULL * 1024ULL * 1024ULL)\n \n@@ -1206,6 +1207,8 @@ rte_eal_init(int argc, char **argv)\n \t\treturn -1;\n \t}\n \n+\teal_pmu_init();\n+\n \tif (rte_eal_tailqs_init() < 0) {\n \t\trte_eal_init_alert(\"Cannot init tail queues for objects\");\n \t\trte_errno = EFAULT;\n@@ -1372,6 +1375,7 @@ rte_eal_cleanup(void)\n \teal_bus_cleanup();\n \trte_trace_save();\n \teal_trace_fini();\n+\teal_pmu_fini();\n \t/* after this point, any DPDK pointers will become dangling */\n \trte_eal_memory_detach();\n \teal_mp_dev_hotplug_cleanup();\ndiff --git a/lib/eal/version.map b/lib/eal/version.map\nindex 7ad12a7dc9..9225f46f67 100644\n--- a/lib/eal/version.map\n+++ b/lib/eal/version.map\n@@ -440,6 +440,11 @@ EXPERIMENTAL {\n \trte_thread_detach;\n \trte_thread_equal;\n \trte_thread_join;\n+\n+\t# added in 23.03\n+\trte_pmu; # WINDOWS_NO_EXPORT\n+\trte_pmu_add_event; # WINDOWS_NO_EXPORT\n+\trte_pmu_read; # WINDOWS_NO_EXPORT\n };\n \n INTERNAL {\n@@ -483,4 +488,5 @@ INTERNAL {\n \trte_mem_map;\n \trte_mem_page_size;\n \trte_mem_unmap;\n+\trte_pmu_enable_group;\n };\n",
    "prefixes": [
        "v3",
        "1/4"
    ]
}