get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 132076,
    "url": "http://patches.dpdk.org/api/patches/132076/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20230928073056.359356-2-mattias.ronnblom@ericsson.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": "<20230928073056.359356-2-mattias.ronnblom@ericsson.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230928073056.359356-2-mattias.ronnblom@ericsson.com",
    "date": "2023-09-28T07:30:54",
    "name": "[v5,1/3] lib: introduce dispatcher library",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "aec19838def5ae6551edf614d99bb13df0bd5137",
    "submitter": {
        "id": 1077,
        "url": "http://patches.dpdk.org/api/people/1077/?format=api",
        "name": "Mattias Rönnblom",
        "email": "mattias.ronnblom@ericsson.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/20230928073056.359356-2-mattias.ronnblom@ericsson.com/mbox/",
    "series": [
        {
            "id": 29667,
            "url": "http://patches.dpdk.org/api/series/29667/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=29667",
            "date": "2023-09-28T07:30:53",
            "name": "Add dispatcher library",
            "version": 5,
            "mbox": "http://patches.dpdk.org/series/29667/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/132076/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/132076/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 317784265D;\n\tThu, 28 Sep 2023 09:36:33 +0200 (CEST)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 1C4DC402EB;\n\tThu, 28 Sep 2023 09:36:33 +0200 (CEST)",
            "from EUR03-DBA-obe.outbound.protection.outlook.com\n (mail-dbaeur03on2065.outbound.protection.outlook.com [40.107.104.65])\n by mails.dpdk.org (Postfix) with ESMTP id 3F573402DE;\n Thu, 28 Sep 2023 09:36:28 +0200 (CEST)",
            "from AM0PR01CA0130.eurprd01.prod.exchangelabs.com\n (2603:10a6:208:168::35) by VI1PR07MB6333.eurprd07.prod.outlook.com\n (2603:10a6:800:133::8) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6813.28; Thu, 28 Sep\n 2023 07:36:26 +0000",
            "from AM4PEPF00027A67.eurprd04.prod.outlook.com\n (2603:10a6:208:168:cafe::44) by AM0PR01CA0130.outlook.office365.com\n (2603:10a6:208:168::35) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6838.21 via Frontend\n Transport; Thu, 28 Sep 2023 07:36:25 +0000",
            "from oa.msg.ericsson.com (192.176.1.74) by\n AM4PEPF00027A67.mail.protection.outlook.com (10.167.16.84) with Microsoft\n SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id\n 15.20.6838.14 via Frontend Transport; Thu, 28 Sep 2023 07:36:25 +0000",
            "from ESESSMB503.ericsson.se (153.88.183.164) by\n ESESBMB504.ericsson.se (153.88.183.171) with Microsoft SMTP Server\n (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id\n 15.1.2507.32; Thu, 28 Sep 2023 09:36:25 +0200",
            "from seliicinfr00049.seli.gic.ericsson.se (153.88.183.153) by\n smtp.internal.ericsson.com (153.88.183.191) with Microsoft SMTP Server id\n 15.1.2507.32 via Frontend Transport; Thu, 28 Sep 2023 09:36:25 +0200",
            "from breslau.. (seliicwb00002.seli.gic.ericsson.se [10.156.25.100])\n by seliicinfr00049.seli.gic.ericsson.se (Postfix) with ESMTP id\n 32355380061; Thu, 28 Sep 2023 09:36:25 +0200 (CEST)"
        ],
        "ARC-Seal": "i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;\n b=jbCjbMlJVQ/g/tEjMpgLva1OOBvHfbz0QnTh2gLrqANSexRyfDB+fS/mGtCeLBHi/2mJ8jNcoGjz8jAFrbg+0NHK0PlJekDCpegF8b9GbjOw0mSRN6V7FSaVPycGoeicrPnvk/uexp5wqVNPY3ByiDTJHmnlh5PDNhRSvwxXxyiKhjBy6/sW942xwnwJRsTdL8RVL5NOOS3lGvTrFLoUZGJSe9nlw5Q5iGVGBXlt0nx0Ln4JdDAOpX/K1B1sBsvXzcRR52WNI5a8z6hEkfppbqOz6r9b94MulXxZnB5dsYBrZw0zQSg6M6LkegnNY/0MhLChSrNa7XQdQExMK2DStw==",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n s=arcselector9901;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;\n bh=EQXUJH6og1Z+BTUMbu+HIj4bToyd4Kgl5HjTz7p/D54=;\n b=WAyorReAuXurGzJysvP7YU31pm8xJzKftq2vWVUUzpKi1kStZMYsLvjcfrlMtq5x1a1juJze/4isrDqLHhcl7pXiJzmpR247PhIyB1kzl15EUgVklxZSi/veCN3sUMbfqyKwldyq3WTmaPp0cGx0/9qY+10vqs0hxcMaqf+M0xDl8twyUb7tmH7mmzBOcKlK7Qvqzh3Au/xoyzxrKjhqZAMq/DHogipIAnFtb1iKtYVCi8QMGooafVlCgv2AhbZjWobtdGJ8QvBFzYgw3Sv4CiGwcwilvlrMnL91EO8R0HRYEhMwKQaW1p8JTG2gyhDDmyo3xr6WOwQGEuFAClEnbA==",
        "ARC-Authentication-Results": "i=1; mx.microsoft.com 1; spf=pass (sender ip is\n 192.176.1.74) smtp.rcpttodomain=dpdk.org smtp.mailfrom=ericsson.com;\n dmarc=pass (p=reject sp=reject pct=100) action=none header.from=ericsson.com;\n dkim=none (message not signed); arc=none",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=ericsson.com;\n s=selector1;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=EQXUJH6og1Z+BTUMbu+HIj4bToyd4Kgl5HjTz7p/D54=;\n b=K0jeRwdwqF3dZyFR0IEhIWtqHPaa8zVHPiHOjJP3Y6dREyYQyn6zweLEfTBoYCus13TTs0aHwnldKzcjwXYuZ8XH/cjOr9lWNf6iIAoy3J+ABQsnjHWD1n8B4PsAUjl57EtwKBy+i5yDFFjiUs98xLLDbG2YWMOpKQcg9w1qyq8=",
        "X-MS-Exchange-Authentication-Results": "spf=pass (sender IP is 192.176.1.74)\n smtp.mailfrom=ericsson.com; dkim=none (message not signed)\n header.d=none;dmarc=pass action=none header.from=ericsson.com;",
        "Received-SPF": "Pass (protection.outlook.com: domain of ericsson.com designates\n 192.176.1.74 as permitted sender)\n receiver=protection.outlook.com;\n client-ip=192.176.1.74; helo=oa.msg.ericsson.com; pr=C",
        "From": "=?utf-8?q?Mattias_R=C3=B6nnblom?= <mattias.ronnblom@ericsson.com>",
        "To": "<dev@dpdk.org>",
        "CC": "Jerin Jacob <jerinj@marvell.com>, <techboard@dpdk.org>,\n <harry.van.haaren@intel.com>, <hofors@lysator.liu.se>,\n Peter Nilsson <peter.j.nilsson@ericsson.com>,\n Heng Wang <heng.wang@ericsson.com>,\n \"Naga Harish K S V\" <s.v.naga.harish.k@intel.com>,\n Pavan Nikhilesh <pbhagavatula@marvell.com>,\n Gujjar Abhinandan S <abhinandan.gujjar@intel.com>,\n Erik Gabriel Carrillo <erik.g.carrillo@intel.com>,\n Shijith Thotton <sthotton@marvell.com>,\n \"Hemant Agrawal\" <hemant.agrawal@nxp.com>,\n Sachin Saxena <sachin.saxena@oss.nxp.com>,  Liang Ma <liangma@liangbit.com>,\n Peter Mccarthy <peter.mccarthy@intel.com>, Zhirun Yan <zhirun.yan@intel.com>,\n =?utf-8?q?Mattias_R=C3=B6nnblom?= <mattias.ronnblom@ericsson.com>",
        "Subject": "[PATCH v5 1/3] lib: introduce dispatcher library",
        "Date": "Thu, 28 Sep 2023 09:30:54 +0200",
        "Message-ID": "<20230928073056.359356-2-mattias.ronnblom@ericsson.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20230928073056.359356-1-mattias.ronnblom@ericsson.com>",
        "References": "<20230922073825.351453-2-mattias.ronnblom@ericsson.com>\n <20230928073056.359356-1-mattias.ronnblom@ericsson.com>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"UTF-8\"",
        "Content-Transfer-Encoding": "8bit",
        "X-EOPAttributedMessage": "0",
        "X-MS-PublicTrafficType": "Email",
        "X-MS-TrafficTypeDiagnostic": "AM4PEPF00027A67:EE_|VI1PR07MB6333:EE_",
        "X-MS-Office365-Filtering-Correlation-Id": "579a2439-9eee-4042-1db9-08dbbff59f09",
        "X-LD-Processed": "92e84ceb-fbfd-47ab-be52-080c6b87953f,ExtAddr",
        "X-MS-Exchange-SenderADCheck": "1",
        "X-MS-Exchange-AntiSpam-Relay": "0",
        "X-Microsoft-Antispam": "BCL:0;",
        "X-Microsoft-Antispam-Message-Info": "\n +iGVORhweuotwmBpyPuWu9vAWnKSAyDBm/1A0LyLjl/zb/OXopkPeoc7CeI25V2RqFxJ6BwhWLXFriczvVeXzzCpjrn5Rhsc1sV4WbQ0Dj++hqv7nwtlmJOceFOzZqIPPSCra4h42tuDMlCUge5t0dlUHB7stp7Tn14rIWUs3GSHsEg85fFhSCX2vFRZxK4Spp03r+lZ6pjraUGpc/vv7cAAUEhG68ztkziO13CLsi3MlJrlirzblJw6Ad66RQoiVhETubNqfuJDITFhWGkFEG1iUrDIeZLzfMTXrFoa8g4d0AB2WzcWg2XVqgsNFvas5gUk3sWa1kF+ha41FWgsIijqME+e3xeW+SNairzRtYtT9CkhnBM1GjClb0lt9Toy9YiH19SmnBN56GSxkC+d14E84Cle3ALuxAT4E/Q/7LxTT8Kyo9EKi/ApQ+JLEJa98pAlOoFwRfUO2IzUSDDl1jlOW9xt/vIxxuuhYZwFPuc/J7sU64A1+B6Fx2BB5T5LlK9jHStI94BKZFmbEIge0WWM5Zt/qHkjYcy0ElEENdt2/HeW1ziddeThKIfBhJNI5uFw4jN7sGJjO8v6uYdFXZJ59vnAQaNLvExRnGWjorM0BS/T4oQKRKCaLSo0ho9JMIU4LHbtExBjdXqQch3Ed/HKawL9UPWizRIxBlMhHWQRFDCcEPTScGjHdGDAGvp80xmpzjGKoH/GODdLCOWhY7CotGFvCr2UJ5QYqvaadTjjYDGpuM8yT7HIhHggp8OQu5cwcYGeufXgGKYlC70NF6q96dmsDHcajOF8xlnuV5U=",
        "X-Forefront-Antispam-Report": "CIP:192.176.1.74; CTRY:SE; LANG:en; SCL:1; SRV:;\n IPV:NLI; SFV:NSPM; H:oa.msg.ericsson.com; PTR:office365.se.ericsson.net;\n CAT:NONE;\n SFS:(13230031)(4636009)(396003)(39860400002)(136003)(376002)(346002)(230922051799003)(64100799003)(186009)(1800799009)(82310400011)(451199024)(40470700004)(36840700001)(46966006)(7636003)(40460700003)(6666004)(107886003)(82960400001)(1076003)(83380400001)(356005)(82740400003)(86362001)(36860700001)(47076005)(36756003)(66574015)(6266002)(2616005)(26005)(336012)(40480700001)(2906002)(30864003)(8676002)(4326008)(8936002)(41300700001)(7416002)(70206006)(70586007)(6916009)(54906003)(5660300002)(316002)(478600001);\n DIR:OUT; SFP:1101;",
        "X-OriginatorOrg": "ericsson.com",
        "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "28 Sep 2023 07:36:25.8499 (UTC)",
        "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n 579a2439-9eee-4042-1db9-08dbbff59f09",
        "X-MS-Exchange-CrossTenant-Id": "92e84ceb-fbfd-47ab-be52-080c6b87953f",
        "X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp": "\n TenantId=92e84ceb-fbfd-47ab-be52-080c6b87953f; Ip=[192.176.1.74];\n Helo=[oa.msg.ericsson.com]",
        "X-MS-Exchange-CrossTenant-AuthSource": "\n AM4PEPF00027A67.eurprd04.prod.outlook.com",
        "X-MS-Exchange-CrossTenant-AuthAs": "Anonymous",
        "X-MS-Exchange-CrossTenant-FromEntityHeader": "HybridOnPrem",
        "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "VI1PR07MB6333",
        "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": "The purpose of the dispatcher library is to help reduce coupling in an\nEventdev-based DPDK application.\n\nIn addition, the dispatcher also provides a convenient and flexible\nway for the application to use service cores for application-level\nprocessing.\n\nSigned-off-by: Mattias Rönnblom <mattias.ronnblom@ericsson.com>\nTested-by: Peter Nilsson <peter.j.nilsson@ericsson.com>\nReviewed-by: Heng Wang <heng.wang@ericsson.com>\n\n--\n\nPATCH v5:\n o Move from using an integer id to a pointer to reference a dispatcher\n   instance, to simplify the API.\n o Fix bug where dispatcher stats retrieval function erroneously depended\n   on the user-supplied stats buffer being all-zero.\n\nPATCH v4:\n o Fix bugs in handler and finalizer unregistration. (Naga Harish)\n o Return -EINVAL in cases where NULL pointers were provided in\n   calls requiring non-NULL pointers. (Naga Harish)\n o Add experimental warning for the whole API. (Jerin Jacob)\n\nPATCH v3:\n o To underline its optional character and since it does not provide\n   hardware abstraction, the event dispatcher is now a separate\n   library.\n o Change name from rte_event_dispatcher -> rte_dispatcher, to make it\n   shorter and to avoid the rte_event_* namespace.\n\nPATCH v2:\n o Add dequeue batch count statistic.\n o Add statistics reset function to API.\n o Clarify MT safety guarantees (or lack thereof) in the API documentation.\n o Change loop variable type in evd_lcore_get_handler_by_id() to uint16_t,\n   to be consistent with similar loops elsewhere in the dispatcher.\n o Fix variable names in finalizer unregister function.\n\nPATCH:\n o Change prefix from RED to EVD, to avoid confusion with random\n   early detection.\n\nRFC v4:\n o Move handlers to per-lcore data structures.\n o Introduce mechanism which rearranges handlers so that often-used\n   handlers tend to be tried first.\n o Terminate dispatch loop in case all events are delivered.\n o To avoid the dispatcher's service function hogging the CPU, process\n   only one batch per call.\n o Have service function return -EAGAIN if no work is performed.\n o Events delivered in the process function is no longer marked 'const',\n   since modifying them may be useful for the application and cause\n   no difficulties for the dispatcher.\n o Various minor API documentation improvements.\n\nRFC v3:\n o Add stats_get() function to the version.map file.\n---\n MAINTAINERS                     |   3 +\n doc/api/doxy-api-index.md       |   1 +\n doc/api/doxy-api.conf.in        |   1 +\n lib/dispatcher/meson.build      |  17 +\n lib/dispatcher/rte_dispatcher.c | 708 ++++++++++++++++++++++++++++++++\n lib/dispatcher/rte_dispatcher.h | 468 +++++++++++++++++++++\n lib/dispatcher/version.map      |  20 +\n lib/meson.build                 |   2 +\n 8 files changed, 1220 insertions(+)\n create mode 100644 lib/dispatcher/meson.build\n create mode 100644 lib/dispatcher/rte_dispatcher.c\n create mode 100644 lib/dispatcher/rte_dispatcher.h\n create mode 100644 lib/dispatcher/version.map",
    "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex a926155f26..6704cd5b2c 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -1726,6 +1726,9 @@ M: Nithin Dabilpuram <ndabilpuram@marvell.com>\n M: Pavan Nikhilesh <pbhagavatula@marvell.com>\n F: lib/node/\n \n+Dispatcher - EXPERIMENTAL\n+M: Mattias Rönnblom <mattias.ronnblom@ericsson.com>\n+F: lib/dispatcher/\n \n Test Applications\n -----------------\ndiff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md\nindex fdeda13932..7d0cad9fed 100644\n--- a/doc/api/doxy-api-index.md\n+++ b/doc/api/doxy-api-index.md\n@@ -155,6 +155,7 @@ The public API headers are grouped by topics:\n \n - **classification**\n   [reorder](@ref rte_reorder.h),\n+  [dispatcher](@ref rte_dispatcher.h),\n   [distributor](@ref rte_distributor.h),\n   [EFD](@ref rte_efd.h),\n   [ACL](@ref rte_acl.h),\ndiff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in\nindex a88accd907..59c679e621 100644\n--- a/doc/api/doxy-api.conf.in\n+++ b/doc/api/doxy-api.conf.in\n@@ -34,6 +34,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \\\n                           @TOPDIR@/lib/cmdline \\\n                           @TOPDIR@/lib/compressdev \\\n                           @TOPDIR@/lib/cryptodev \\\n+                          @TOPDIR@/lib/dispatcher \\\n                           @TOPDIR@/lib/distributor \\\n                           @TOPDIR@/lib/dmadev \\\n                           @TOPDIR@/lib/efd \\\ndiff --git a/lib/dispatcher/meson.build b/lib/dispatcher/meson.build\nnew file mode 100644\nindex 0000000000..c6054a3a5d\n--- /dev/null\n+++ b/lib/dispatcher/meson.build\n@@ -0,0 +1,17 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(c) 2023 Ericsson AB\n+\n+if is_windows\n+    build = false\n+    reason = 'not supported on Windows'\n+    subdir_done()\n+endif\n+\n+sources = files(\n+        'rte_dispatcher.c',\n+)\n+headers = files(\n+        'rte_dispatcher.h',\n+)\n+\n+deps += ['eventdev']\ndiff --git a/lib/dispatcher/rte_dispatcher.c b/lib/dispatcher/rte_dispatcher.c\nnew file mode 100644\nindex 0000000000..0e69db2b9b\n--- /dev/null\n+++ b/lib/dispatcher/rte_dispatcher.c\n@@ -0,0 +1,708 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Ericsson AB\n+ */\n+\n+#include <stdbool.h>\n+#include <stdint.h>\n+\n+#include <rte_branch_prediction.h>\n+#include <rte_common.h>\n+#include <rte_lcore.h>\n+#include <rte_random.h>\n+#include <rte_service_component.h>\n+\n+#include \"eventdev_pmd.h\"\n+\n+#include <rte_dispatcher.h>\n+\n+#define EVD_MAX_PORTS_PER_LCORE 4\n+#define EVD_MAX_HANDLERS 32\n+#define EVD_MAX_FINALIZERS 16\n+#define EVD_AVG_PRIO_INTERVAL 2000\n+#define EVD_SERVICE_NAME \"dispatcher\"\n+\n+struct rte_dispatcher_lcore_port {\n+\tuint8_t port_id;\n+\tuint16_t batch_size;\n+\tuint64_t timeout;\n+};\n+\n+struct rte_dispatcher_handler {\n+\tint id;\n+\trte_dispatcher_match_t match_fun;\n+\tvoid *match_data;\n+\trte_dispatcher_process_t process_fun;\n+\tvoid *process_data;\n+};\n+\n+struct rte_dispatcher_finalizer {\n+\tint id;\n+\trte_dispatcher_finalize_t finalize_fun;\n+\tvoid *finalize_data;\n+};\n+\n+struct rte_dispatcher_lcore {\n+\tuint8_t num_ports;\n+\tuint16_t num_handlers;\n+\tint32_t prio_count;\n+\tstruct rte_dispatcher_lcore_port ports[EVD_MAX_PORTS_PER_LCORE];\n+\tstruct rte_dispatcher_handler handlers[EVD_MAX_HANDLERS];\n+\tstruct rte_dispatcher_stats stats;\n+} __rte_cache_aligned;\n+\n+struct rte_dispatcher {\n+\tuint8_t event_dev_id;\n+\tint socket_id;\n+\tuint32_t service_id;\n+\tstruct rte_dispatcher_lcore lcores[RTE_MAX_LCORE];\n+\tuint16_t num_finalizers;\n+\tstruct rte_dispatcher_finalizer finalizers[EVD_MAX_FINALIZERS];\n+};\n+\n+static int\n+evd_lookup_handler_idx(struct rte_dispatcher_lcore *lcore,\n+\t\t       const struct rte_event *event)\n+{\n+\tuint16_t i;\n+\n+\tfor (i = 0; i < lcore->num_handlers; i++) {\n+\t\tstruct rte_dispatcher_handler *handler =\n+\t\t\t&lcore->handlers[i];\n+\n+\t\tif (handler->match_fun(event, handler->match_data))\n+\t\t\treturn i;\n+\t}\n+\n+\treturn -1;\n+}\n+\n+static void\n+evd_prioritize_handler(struct rte_dispatcher_lcore *lcore,\n+\t\t       int handler_idx)\n+{\n+\tstruct rte_dispatcher_handler tmp;\n+\n+\tif (handler_idx == 0)\n+\t\treturn;\n+\n+\t/* Let the lucky handler \"bubble\" up the list */\n+\n+\ttmp = lcore->handlers[handler_idx - 1];\n+\n+\tlcore->handlers[handler_idx - 1] = lcore->handlers[handler_idx];\n+\n+\tlcore->handlers[handler_idx] = tmp;\n+}\n+\n+static inline void\n+evd_consider_prioritize_handler(struct rte_dispatcher_lcore *lcore,\n+\t\t\t\tint handler_idx, uint16_t handler_events)\n+{\n+\tlcore->prio_count -= handler_events;\n+\n+\tif (unlikely(lcore->prio_count <= 0)) {\n+\t\tevd_prioritize_handler(lcore, handler_idx);\n+\n+\t\t/*\n+\t\t * Randomize the interval in the unlikely case\n+\t\t * the traffic follow some very strict pattern.\n+\t\t */\n+\t\tlcore->prio_count =\n+\t\t\trte_rand_max(EVD_AVG_PRIO_INTERVAL) +\n+\t\t\tEVD_AVG_PRIO_INTERVAL / 2;\n+\t}\n+}\n+\n+static inline void\n+evd_dispatch_events(struct rte_dispatcher *dispatcher,\n+\t\t    struct rte_dispatcher_lcore *lcore,\n+\t\t    struct rte_dispatcher_lcore_port *port,\n+\t\t    struct rte_event *events, uint16_t num_events)\n+{\n+\tint i;\n+\tstruct rte_event bursts[EVD_MAX_HANDLERS][num_events];\n+\tuint16_t burst_lens[EVD_MAX_HANDLERS] = { 0 };\n+\tuint16_t drop_count = 0;\n+\tuint16_t dispatch_count;\n+\tuint16_t dispatched = 0;\n+\n+\tfor (i = 0; i < num_events; i++) {\n+\t\tstruct rte_event *event = &events[i];\n+\t\tint handler_idx;\n+\n+\t\thandler_idx = evd_lookup_handler_idx(lcore, event);\n+\n+\t\tif (unlikely(handler_idx < 0)) {\n+\t\t\tdrop_count++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tbursts[handler_idx][burst_lens[handler_idx]] = *event;\n+\t\tburst_lens[handler_idx]++;\n+\t}\n+\n+\tdispatch_count = num_events - drop_count;\n+\n+\tfor (i = 0; i < lcore->num_handlers &&\n+\t\t dispatched < dispatch_count; i++) {\n+\t\tstruct rte_dispatcher_handler *handler =\n+\t\t\t&lcore->handlers[i];\n+\t\tuint16_t len = burst_lens[i];\n+\n+\t\tif (len == 0)\n+\t\t\tcontinue;\n+\n+\t\thandler->process_fun(dispatcher->event_dev_id, port->port_id,\n+\t\t\t\t     bursts[i], len, handler->process_data);\n+\n+\t\tdispatched += len;\n+\n+\t\t/*\n+\t\t * Safe, since any reshuffling will only involve\n+\t\t * already-processed handlers.\n+\t\t */\n+\t\tevd_consider_prioritize_handler(lcore, i, len);\n+\t}\n+\n+\tlcore->stats.ev_batch_count++;\n+\tlcore->stats.ev_dispatch_count += dispatch_count;\n+\tlcore->stats.ev_drop_count += drop_count;\n+\n+\tfor (i = 0; i < dispatcher->num_finalizers; i++) {\n+\t\tstruct rte_dispatcher_finalizer *finalizer =\n+\t\t\t&dispatcher->finalizers[i];\n+\n+\t\tfinalizer->finalize_fun(dispatcher->event_dev_id,\n+\t\t\t\t\tport->port_id,\n+\t\t\t\t\tfinalizer->finalize_data);\n+\t}\n+}\n+\n+static __rte_always_inline uint16_t\n+evd_port_dequeue(struct rte_dispatcher *dispatcher,\n+\t\t struct rte_dispatcher_lcore *lcore,\n+\t\t struct rte_dispatcher_lcore_port *port)\n+{\n+\tuint16_t batch_size = port->batch_size;\n+\tstruct rte_event events[batch_size];\n+\tuint16_t n;\n+\n+\tn = rte_event_dequeue_burst(dispatcher->event_dev_id, port->port_id,\n+\t\t\t\t    events, batch_size, port->timeout);\n+\n+\tif (likely(n > 0))\n+\t\tevd_dispatch_events(dispatcher, lcore, port, events, n);\n+\n+\tlcore->stats.poll_count++;\n+\n+\treturn n;\n+}\n+\n+static __rte_always_inline uint16_t\n+evd_lcore_process(struct rte_dispatcher *dispatcher,\n+\t\t  struct rte_dispatcher_lcore *lcore)\n+{\n+\tuint16_t i;\n+\tuint16_t event_count = 0;\n+\n+\tfor (i = 0; i < lcore->num_ports; i++) {\n+\t\tstruct rte_dispatcher_lcore_port *port =\n+\t\t\t&lcore->ports[i];\n+\n+\t\tevent_count += evd_port_dequeue(dispatcher, lcore, port);\n+\t}\n+\n+\treturn event_count;\n+}\n+\n+static int32_t\n+evd_process(void *userdata)\n+{\n+\tstruct rte_dispatcher *dispatcher = userdata;\n+\tunsigned int lcore_id = rte_lcore_id();\n+\tstruct rte_dispatcher_lcore *lcore =\n+\t\t&dispatcher->lcores[lcore_id];\n+\tuint64_t event_count;\n+\n+\tevent_count = evd_lcore_process(dispatcher, lcore);\n+\n+\tif (unlikely(event_count == 0))\n+\t\treturn -EAGAIN;\n+\n+\treturn 0;\n+}\n+\n+static int\n+evd_service_register(struct rte_dispatcher *dispatcher)\n+{\n+\tstruct rte_service_spec service = {\n+\t\t.callback = evd_process,\n+\t\t.callback_userdata = dispatcher,\n+\t\t.capabilities = RTE_SERVICE_CAP_MT_SAFE,\n+\t\t.socket_id = dispatcher->socket_id\n+\t};\n+\tint rc;\n+\n+\tsnprintf(service.name, sizeof(service.name), EVD_SERVICE_NAME);\n+\n+\trc = rte_service_component_register(&service, &dispatcher->service_id);\n+\n+\tif (rc)\n+\t\tRTE_EDEV_LOG_ERR(\"Registration of dispatcher service \"\n+\t\t\t\t \"%s failed with error code %d\\n\",\n+\t\t\t\t service.name, rc);\n+\n+\treturn rc;\n+}\n+\n+static int\n+evd_service_unregister(struct rte_dispatcher *dispatcher)\n+{\n+\tint rc;\n+\n+\trc = rte_service_component_unregister(dispatcher->service_id);\n+\n+\tif (rc)\n+\t\tRTE_EDEV_LOG_ERR(\"Unregistration of dispatcher service \"\n+\t\t\t\t \"failed with error code %d\\n\", rc);\n+\n+\treturn rc;\n+}\n+\n+struct rte_dispatcher *\n+rte_dispatcher_create(uint8_t event_dev_id)\n+{\n+\tint socket_id;\n+\tstruct rte_dispatcher *dispatcher;\n+\tint rc;\n+\n+\tsocket_id = rte_event_dev_socket_id(event_dev_id);\n+\n+\tdispatcher =\n+\t\trte_malloc_socket(\"dispatcher\", sizeof(struct rte_dispatcher),\n+\t\t\t\t  RTE_CACHE_LINE_SIZE, socket_id);\n+\n+\tif (dispatcher == NULL) {\n+\t\tRTE_EDEV_LOG_ERR(\"Unable to allocate memory for dispatcher\\n\");\n+\t\trte_errno = ENOMEM;\n+\t\treturn NULL;\n+\t}\n+\n+\t*dispatcher = (struct rte_dispatcher) {\n+\t\t.event_dev_id = event_dev_id,\n+\t\t.socket_id = socket_id\n+\t};\n+\n+\trc = evd_service_register(dispatcher);\n+\n+\tif (rc < 0) {\n+\t\trte_free(dispatcher);\n+\t\trte_errno = -rc;\n+\t\treturn NULL;\n+\t}\n+\n+\treturn dispatcher;\n+}\n+\n+int\n+rte_dispatcher_free(struct rte_dispatcher *dispatcher)\n+{\n+\tint rc;\n+\n+\tif (dispatcher == NULL)\n+\t\treturn 0;\n+\n+\trc = evd_service_unregister(dispatcher);\n+\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trte_free(dispatcher);\n+\n+\treturn 0;\n+}\n+\n+uint32_t\n+rte_dispatcher_service_id_get(const struct rte_dispatcher *dispatcher)\n+{\n+\treturn dispatcher->service_id;\n+}\n+\n+static int\n+lcore_port_index(struct rte_dispatcher_lcore *lcore,\n+\t\t uint8_t event_port_id)\n+{\n+\tuint16_t i;\n+\n+\tfor (i = 0; i < lcore->num_ports; i++) {\n+\t\tstruct rte_dispatcher_lcore_port *port =\n+\t\t\t&lcore->ports[i];\n+\n+\t\tif (port->port_id == event_port_id)\n+\t\t\treturn i;\n+\t}\n+\n+\treturn -1;\n+}\n+\n+int\n+rte_dispatcher_bind_port_to_lcore(struct rte_dispatcher *dispatcher,\n+\t\t\t\t  uint8_t event_port_id, uint16_t batch_size,\n+\t\t\t\t  uint64_t timeout, unsigned int lcore_id)\n+{\n+\tstruct rte_dispatcher_lcore *lcore;\n+\tstruct rte_dispatcher_lcore_port *port;\n+\n+\tlcore =\t&dispatcher->lcores[lcore_id];\n+\n+\tif (lcore->num_ports == EVD_MAX_PORTS_PER_LCORE)\n+\t\treturn -ENOMEM;\n+\n+\tif (lcore_port_index(lcore, event_port_id) >= 0)\n+\t\treturn -EEXIST;\n+\n+\tport = &lcore->ports[lcore->num_ports];\n+\n+\t*port = (struct rte_dispatcher_lcore_port) {\n+\t\t.port_id = event_port_id,\n+\t\t.batch_size = batch_size,\n+\t\t.timeout = timeout\n+\t};\n+\n+\tlcore->num_ports++;\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_dispatcher_unbind_port_from_lcore(struct rte_dispatcher *dispatcher,\n+\t\t\t\t      uint8_t event_port_id,\n+\t\t\t\t      unsigned int lcore_id)\n+{\n+\tstruct rte_dispatcher_lcore *lcore;\n+\tint port_idx;\n+\tstruct rte_dispatcher_lcore_port *port;\n+\tstruct rte_dispatcher_lcore_port *last;\n+\n+\tlcore =\t&dispatcher->lcores[lcore_id];\n+\n+\tport_idx = lcore_port_index(lcore, event_port_id);\n+\n+\tif (port_idx < 0)\n+\t\treturn -ENOENT;\n+\n+\tport = &lcore->ports[port_idx];\n+\tlast = &lcore->ports[lcore->num_ports - 1];\n+\n+\tif (port != last)\n+\t\t*port = *last;\n+\n+\tlcore->num_ports--;\n+\n+\treturn 0;\n+}\n+\n+static struct rte_dispatcher_handler*\n+evd_lcore_get_handler_by_id(struct rte_dispatcher_lcore *lcore,\n+\t\t\t    int handler_id)\n+{\n+\tuint16_t i;\n+\n+\tfor (i = 0; i < lcore->num_handlers; i++) {\n+\t\tstruct rte_dispatcher_handler *handler =\n+\t\t\t&lcore->handlers[i];\n+\n+\t\tif (handler->id == handler_id)\n+\t\t\treturn handler;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static int\n+evd_alloc_handler_id(struct rte_dispatcher *dispatcher)\n+{\n+\tint handler_id = 0;\n+\tstruct rte_dispatcher_lcore *reference_lcore =\n+\t\t&dispatcher->lcores[0];\n+\n+\tif (reference_lcore->num_handlers == EVD_MAX_HANDLERS)\n+\t\treturn -1;\n+\n+\twhile (evd_lcore_get_handler_by_id(reference_lcore, handler_id) != NULL)\n+\t\thandler_id++;\n+\n+\treturn handler_id;\n+}\n+\n+static void\n+evd_lcore_install_handler(struct rte_dispatcher_lcore *lcore,\n+\t\t    const struct rte_dispatcher_handler *handler)\n+{\n+\tint handler_idx = lcore->num_handlers;\n+\n+\tlcore->handlers[handler_idx] = *handler;\n+\tlcore->num_handlers++;\n+}\n+\n+static void\n+evd_install_handler(struct rte_dispatcher *dispatcher,\n+\t\t    const struct rte_dispatcher_handler *handler)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < RTE_MAX_LCORE; i++) {\n+\t\tstruct rte_dispatcher_lcore *lcore =\n+\t\t\t&dispatcher->lcores[i];\n+\t\tevd_lcore_install_handler(lcore, handler);\n+\t}\n+}\n+\n+int\n+rte_dispatcher_register(struct rte_dispatcher *dispatcher,\n+\t\t\trte_dispatcher_match_t match_fun, void *match_data,\n+\t\t\trte_dispatcher_process_t process_fun,\n+\t\t\tvoid *process_data)\n+{\n+\tstruct rte_dispatcher_handler handler = {\n+\t\t.match_fun = match_fun,\n+\t\t.match_data = match_data,\n+\t\t.process_fun = process_fun,\n+\t\t.process_data = process_data\n+\t};\n+\n+\thandler.id = evd_alloc_handler_id(dispatcher);\n+\n+\tif (handler.id < 0)\n+\t\treturn -ENOMEM;\n+\n+\tevd_install_handler(dispatcher, &handler);\n+\n+\treturn handler.id;\n+}\n+\n+static int\n+evd_lcore_uninstall_handler(struct rte_dispatcher_lcore *lcore,\n+\t\t\t    int handler_id)\n+{\n+\tstruct rte_dispatcher_handler *unreg_handler;\n+\tint handler_idx;\n+\tuint16_t last_idx;\n+\n+\tunreg_handler = evd_lcore_get_handler_by_id(lcore, handler_id);\n+\n+\tif (unreg_handler == NULL) {\n+\t\tRTE_EDEV_LOG_ERR(\"Invalid handler id %d\\n\", handler_id);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\thandler_idx = unreg_handler - &lcore->handlers[0];\n+\n+\tlast_idx = lcore->num_handlers - 1;\n+\n+\tif (handler_idx != last_idx) {\n+\t\t/* move all handlers to maintain handler order */\n+\t\tint n = last_idx - handler_idx;\n+\t\tmemmove(unreg_handler, unreg_handler + 1,\n+\t\t\tsizeof(struct rte_dispatcher_handler) * n);\n+\t}\n+\n+\tlcore->num_handlers--;\n+\n+\treturn 0;\n+}\n+\n+static int\n+evd_uninstall_handler(struct rte_dispatcher *dispatcher,\n+\t\t      int handler_id)\n+{\n+\tunsigned int lcore_id;\n+\n+\tfor (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {\n+\t\tstruct rte_dispatcher_lcore *lcore =\n+\t\t\t&dispatcher->lcores[lcore_id];\n+\t\tint rc;\n+\n+\t\trc = evd_lcore_uninstall_handler(lcore, handler_id);\n+\n+\t\tif (rc < 0)\n+\t\t\treturn rc;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_dispatcher_unregister(struct rte_dispatcher *dispatcher, int handler_id)\n+{\n+\tint rc;\n+\n+\trc = evd_uninstall_handler(dispatcher, handler_id);\n+\n+\treturn rc;\n+}\n+\n+static struct rte_dispatcher_finalizer*\n+evd_get_finalizer_by_id(struct rte_dispatcher *dispatcher,\n+\t\t       int handler_id)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < dispatcher->num_finalizers; i++) {\n+\t\tstruct rte_dispatcher_finalizer *finalizer =\n+\t\t\t&dispatcher->finalizers[i];\n+\n+\t\tif (finalizer->id == handler_id)\n+\t\t\treturn finalizer;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static int\n+evd_alloc_finalizer_id(struct rte_dispatcher *dispatcher)\n+{\n+\tint finalizer_id = 0;\n+\n+\twhile (evd_get_finalizer_by_id(dispatcher, finalizer_id) != NULL)\n+\t\tfinalizer_id++;\n+\n+\treturn finalizer_id;\n+}\n+\n+static struct rte_dispatcher_finalizer *\n+evd_alloc_finalizer(struct rte_dispatcher *dispatcher)\n+{\n+\tint finalizer_idx;\n+\tstruct rte_dispatcher_finalizer *finalizer;\n+\n+\tif (dispatcher->num_finalizers == EVD_MAX_FINALIZERS)\n+\t\treturn NULL;\n+\n+\tfinalizer_idx = dispatcher->num_finalizers;\n+\tfinalizer = &dispatcher->finalizers[finalizer_idx];\n+\n+\tfinalizer->id = evd_alloc_finalizer_id(dispatcher);\n+\n+\tdispatcher->num_finalizers++;\n+\n+\treturn finalizer;\n+}\n+\n+int\n+rte_dispatcher_finalize_register(struct rte_dispatcher *dispatcher,\n+\t\t\t\t rte_dispatcher_finalize_t finalize_fun,\n+\t\t\t\t void *finalize_data)\n+{\n+\tstruct rte_dispatcher_finalizer *finalizer;\n+\n+\tfinalizer = evd_alloc_finalizer(dispatcher);\n+\n+\tif (finalizer == NULL)\n+\t\treturn -ENOMEM;\n+\n+\tfinalizer->finalize_fun = finalize_fun;\n+\tfinalizer->finalize_data = finalize_data;\n+\n+\treturn finalizer->id;\n+}\n+\n+int\n+rte_dispatcher_finalize_unregister(struct rte_dispatcher *dispatcher,\n+\t\t\t\t   int finalizer_id)\n+{\n+\tstruct rte_dispatcher_finalizer *unreg_finalizer;\n+\tint finalizer_idx;\n+\tuint16_t last_idx;\n+\n+\tunreg_finalizer = evd_get_finalizer_by_id(dispatcher, finalizer_id);\n+\n+\tif (unreg_finalizer == NULL) {\n+\t\tRTE_EDEV_LOG_ERR(\"Invalid finalizer id %d\\n\", finalizer_id);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tfinalizer_idx = unreg_finalizer - &dispatcher->finalizers[0];\n+\n+\tlast_idx = dispatcher->num_finalizers - 1;\n+\n+\tif (finalizer_idx != last_idx) {\n+\t\t/* move all finalizers to maintain order */\n+\t\tint n = last_idx - finalizer_idx;\n+\t\tmemmove(unreg_finalizer, unreg_finalizer + 1,\n+\t\t\tsizeof(struct rte_dispatcher_finalizer) * n);\n+\t}\n+\n+\tdispatcher->num_finalizers--;\n+\n+\treturn 0;\n+}\n+\n+static int\n+evd_set_service_runstate(struct rte_dispatcher *dispatcher, int state)\n+{\n+\tint rc;\n+\n+\trc = rte_service_component_runstate_set(dispatcher->service_id,\n+\t\t\t\t\t\tstate);\n+\n+\tif (rc != 0) {\n+\t\tRTE_EDEV_LOG_ERR(\"Unexpected error %d occurred while setting \"\n+\t\t\t\t \"service component run state to %d\\n\", rc,\n+\t\t\t\t state);\n+\t\tRTE_ASSERT(0);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_dispatcher_start(struct rte_dispatcher *dispatcher)\n+{\n+\treturn evd_set_service_runstate(dispatcher, 1);\n+}\n+\n+int\n+rte_dispatcher_stop(struct rte_dispatcher *dispatcher)\n+{\n+\treturn evd_set_service_runstate(dispatcher, 0);\n+}\n+\n+static void\n+evd_aggregate_stats(struct rte_dispatcher_stats *result,\n+\t\t    const struct rte_dispatcher_stats *part)\n+{\n+\tresult->poll_count += part->poll_count;\n+\tresult->ev_batch_count += part->ev_batch_count;\n+\tresult->ev_dispatch_count += part->ev_dispatch_count;\n+\tresult->ev_drop_count += part->ev_drop_count;\n+}\n+\n+void\n+rte_dispatcher_stats_get(const struct rte_dispatcher *dispatcher,\n+\t\t\t struct rte_dispatcher_stats *stats)\n+{\n+\tunsigned int lcore_id;\n+\n+\t*stats = (struct rte_dispatcher_stats) {};\n+\n+\tfor (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {\n+\t\tconst struct rte_dispatcher_lcore *lcore =\n+\t\t\t&dispatcher->lcores[lcore_id];\n+\n+\t\tevd_aggregate_stats(stats, &lcore->stats);\n+\t}\n+}\n+\n+void\n+rte_dispatcher_stats_reset(struct rte_dispatcher *dispatcher)\n+{\n+\tunsigned int lcore_id;\n+\n+\tfor (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {\n+\t\tstruct rte_dispatcher_lcore *lcore =\n+\t\t\t&dispatcher->lcores[lcore_id];\n+\n+\t\tlcore->stats = (struct rte_dispatcher_stats) {};\n+\t}\n+}\ndiff --git a/lib/dispatcher/rte_dispatcher.h b/lib/dispatcher/rte_dispatcher.h\nnew file mode 100644\nindex 0000000000..0387316d7b\n--- /dev/null\n+++ b/lib/dispatcher/rte_dispatcher.h\n@@ -0,0 +1,468 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Ericsson AB\n+ */\n+\n+#ifndef __RTE_DISPATCHER_H__\n+#define __RTE_DISPATCHER_H__\n+\n+/**\n+ * @file\n+ *\n+ * RTE Dispatcher\n+ *\n+ * @warning\n+ * @b EXPERIMENTAL:\n+ * All functions in this file may be changed or removed without prior notice.\n+ *\n+ * The purpose of the dispatcher is to help decouple different parts\n+ * of an application (e.g., modules), sharing the same underlying\n+ * event device.\n+ */\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+#include <rte_eventdev.h>\n+\n+/**\n+ * Function prototype for match callbacks.\n+ *\n+ * Match callbacks are used by an application to decide how the\n+ * dispatcher distributes events to different parts of the\n+ * application.\n+ *\n+ * The application is not expected to process the event at the point\n+ * of the match call. Such matters should be deferred to the process\n+ * callback invocation.\n+ *\n+ * The match callback may be used as an opportunity to prefetch data.\n+ *\n+ * @param event\n+ *  Pointer to event\n+ *\n+ * @param cb_data\n+ *  The pointer supplied by the application in\n+ *  rte_dispatcher_register().\n+ *\n+ * @return\n+ *   Returns true in case this events should be delivered (via\n+ *   the process callback), and false otherwise.\n+ */\n+typedef bool\n+(*rte_dispatcher_match_t)(const struct rte_event *event, void *cb_data);\n+\n+/**\n+ * Function prototype for process callbacks.\n+ *\n+ * The process callbacks are used by the dispatcher to deliver\n+ * events for processing.\n+ *\n+ * @param event_dev_id\n+ *  The originating event device id.\n+ *\n+ * @param event_port_id\n+ *  The originating event port.\n+ *\n+ * @param events\n+ *  Pointer to an array of events.\n+ *\n+ * @param num\n+ *  The number of events in the @p events array.\n+ *\n+ * @param cb_data\n+ *  The pointer supplied by the application in\n+ *  rte_dispatcher_register().\n+ */\n+\n+typedef void\n+(*rte_dispatcher_process_t)(uint8_t event_dev_id, uint8_t event_port_id,\n+\t\t\t\t  struct rte_event *events, uint16_t num,\n+\t\t\t\t  void *cb_data);\n+\n+/**\n+ * Function prototype for finalize callbacks.\n+ *\n+ * The finalize callbacks are used by the dispatcher to notify the\n+ * application it has delivered all events from a particular batch\n+ * dequeued from the event device.\n+ *\n+ * @param event_dev_id\n+ *  The originating event device id.\n+ *\n+ * @param event_port_id\n+ *  The originating event port.\n+ *\n+ * @param cb_data\n+ *  The pointer supplied by the application in\n+ *  rte_dispatcher_finalize_register().\n+ */\n+\n+typedef void\n+(*rte_dispatcher_finalize_t)(uint8_t event_dev_id, uint8_t event_port_id,\n+\t\t\t\t   void *cb_data);\n+\n+/**\n+ * Dispatcher statistics\n+ */\n+struct rte_dispatcher_stats {\n+\tuint64_t poll_count;\n+\t/**< Number of event dequeue calls made toward the event device. */\n+\tuint64_t ev_batch_count;\n+\t/**< Number of non-empty event batches dequeued from event device.*/\n+\tuint64_t ev_dispatch_count;\n+\t/**< Number of events dispatched to a handler.*/\n+\tuint64_t ev_drop_count;\n+\t/**< Number of events dropped because no handler was found. */\n+};\n+\n+/**\n+ * Create a dispatcher with the specified id.\n+ *\n+ * @param event_dev_id\n+ *  The identifier of the event device from which this dispatcher\n+ *  will dequeue events.\n+ *\n+ * @return\n+ *   A pointer to a new dispatcher instance, or NULL on failure, in which\n+ *   case rte_errno is set.\n+ */\n+__rte_experimental\n+struct rte_dispatcher *\n+rte_dispatcher_create(uint8_t event_dev_id);\n+\n+/**\n+ * Free a dispatcher.\n+ *\n+ * @param dispatcher\n+ *  The dispatcher instance.\n+ *\n+ * @return\n+ *  - 0: Success\n+ *  - <0: Error code on failure\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_free(struct rte_dispatcher *dispatcher);\n+\n+/**\n+ * Retrieve the service identifier of a dispatcher.\n+ *\n+ * @param dispatcher\n+ *  The dispatcher instance.\n+ *\n+ * @return\n+ *  The dispatcher service's id.\n+ */\n+__rte_experimental\n+uint32_t\n+rte_dispatcher_service_id_get(const struct rte_dispatcher *dispatcher);\n+\n+/**\n+ * Binds an event device port to a specific lcore on the specified\n+ * dispatcher.\n+ *\n+ * This function configures the event port id to be used by the event\n+ * dispatcher service, if run on the specified lcore.\n+ *\n+ * Multiple event device ports may be bound to the same lcore. A\n+ * particular port must not be bound to more than one lcore.\n+ *\n+ * If the dispatcher service is mapped (with rte_service_map_lcore_set())\n+ * to a lcore to which no ports are bound, the service function will be a\n+ * no-operation.\n+ *\n+ * This function may be called by any thread (including unregistered\n+ * non-EAL threads), but not while the dispatcher is running on lcore\n+ * specified by @c lcore_id.\n+ *\n+ * @param dispatcher\n+ *  The dispatcher instance.\n+ *\n+ * @param event_port_id\n+ *  The event device port identifier.\n+ *\n+ * @param batch_size\n+ *  The batch size to use in rte_event_dequeue_burst(), for the\n+ *  configured event device port and lcore.\n+ *\n+ * @param timeout\n+ *  The timeout parameter to use in rte_event_dequeue_burst(), for the\n+ *  configured event device port and lcore.\n+ *\n+ * @param lcore_id\n+ *  The lcore by which this event port will be used.\n+ *\n+ * @return\n+ *  - 0: Success\n+ *  - -ENOMEM: Unable to allocate sufficient resources.\n+ *  - -EEXISTS: Event port is already configured.\n+ *  - -EINVAL: Invalid arguments.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_bind_port_to_lcore(struct rte_dispatcher *dispatcher,\n+\t\t\t\t  uint8_t event_port_id, uint16_t batch_size,\n+\t\t\t\t  uint64_t timeout, unsigned int lcore_id);\n+\n+/**\n+ * Unbind an event device port from a specific lcore.\n+ *\n+ * This function may be called by any thread (including unregistered\n+ * non-EAL threads), but not while the dispatcher is running on\n+ * lcore specified by @c lcore_id.\n+ *\n+ * @param dispatcher\n+ *  The dispatcher instance.\n+ *\n+ * @param event_port_id\n+ *  The event device port identifier.\n+ *\n+ * @param lcore_id\n+ *  The lcore which was using this event port.\n+ *\n+ * @return\n+ *  - 0: Success\n+ *  - -ENOENT: Event port id not bound to this @c lcore_id.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_unbind_port_from_lcore(struct rte_dispatcher *dispatcher,\n+\t\t\t\t      uint8_t event_port_id,\n+\t\t\t\t      unsigned int lcore_id);\n+\n+/**\n+ * Register an event handler.\n+ *\n+ * The match callback function is used to select if a particular event\n+ * should be delivered, using the corresponding process callback\n+ * function.\n+ *\n+ * The reason for having two distinct steps is to allow the dispatcher\n+ * to deliver all events as a batch. This in turn will cause\n+ * processing of a particular kind of events to happen in a\n+ * back-to-back manner, improving cache locality.\n+ *\n+ * The list of handler callback functions is shared among all lcores,\n+ * but will only be executed on lcores which has an eventdev port\n+ * bound to them, and which are running the dispatcher service.\n+ *\n+ * An event is delivered to at most one handler. Events where no\n+ * handler is found are dropped.\n+ *\n+ * The application must not depend on the order of which the match\n+ * functions are invoked.\n+ *\n+ * Ordering of events is not guaranteed to be maintained between\n+ * different deliver callbacks. For example, suppose there are two\n+ * callbacks registered, matching different subsets of events arriving\n+ * on an atomic queue. A batch of events [ev0, ev1, ev2] are dequeued\n+ * on a particular port, all pertaining to the same flow. The match\n+ * callback for registration A returns true for ev0 and ev2, and the\n+ * matching function for registration B for ev1. In that scenario, the\n+ * dispatcher may choose to deliver first [ev0, ev2] using A's deliver\n+ * function, and then [ev1] to B - or vice versa.\n+ *\n+ * rte_dispatcher_register() may be called by any thread\n+ * (including unregistered non-EAL threads), but not while the event\n+ * dispatcher is running on any service lcore.\n+ *\n+ * @param dispatcher\n+ *  The dispatcher instance.\n+ *\n+ * @param match_fun\n+ *  The match callback function.\n+ *\n+ * @param match_cb_data\n+ *  A pointer to some application-specific opaque data (or NULL),\n+ *  which is supplied back to the application when match_fun is\n+ *  called.\n+ *\n+ * @param process_fun\n+ *  The process callback function.\n+ *\n+ * @param process_cb_data\n+ *  A pointer to some application-specific opaque data (or NULL),\n+ *  which is supplied back to the application when process_fun is\n+ *  called.\n+ *\n+ * @return\n+ *  - >= 0: The identifier for this registration.\n+ *  - -ENOMEM: Unable to allocate sufficient resources.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_register(struct rte_dispatcher *dispatcher,\n+\t\t\trte_dispatcher_match_t match_fun, void *match_cb_data,\n+\t\t\trte_dispatcher_process_t process_fun,\n+\t\t\tvoid *process_cb_data);\n+\n+/**\n+ * Unregister an event handler.\n+ *\n+ * This function may be called by any thread (including unregistered\n+ * non-EAL threads), but not while the dispatcher is running on\n+ * any service lcore.\n+ *\n+ * @param dispatcher\n+ *  The dispatcher instance.\n+ *\n+ * @param handler_id\n+ *  The handler registration id returned by the original\n+ *  rte_dispatcher_register() call.\n+ *\n+ * @return\n+ *  - 0: Success\n+ *  - -EINVAL: The @c handler_id parameter was invalid.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_unregister(struct rte_dispatcher *dispatcher, int handler_id);\n+\n+/**\n+ * Register a finalize callback function.\n+ *\n+ * An application may optionally install one or more finalize\n+ * callbacks.\n+ *\n+ * All finalize callbacks are invoked by the dispatcher when a\n+ * complete batch of events (retrieve using rte_event_dequeue_burst())\n+ * have been delivered to the application (or have been dropped).\n+ *\n+ * The finalize callback is not tied to any particular handler.\n+ *\n+ * The finalize callback provides an opportunity for the application\n+ * to do per-batch processing. One case where this may be useful is if\n+ * an event output buffer is used, and is shared among several\n+ * handlers. In such a case, proper output buffer flushing may be\n+ * assured using a finalize callback.\n+ *\n+ * rte_dispatcher_finalize_register() may be called by any thread\n+ * (including unregistered non-EAL threads), but not while the\n+ * dispatcher is running on any service lcore.\n+ *\n+ * @param dispatcher\n+ *  The dispatcher instance.\n+ *\n+ * @param finalize_fun\n+ *  The function called after completing the processing of a\n+ *  dequeue batch.\n+ *\n+ * @param finalize_data\n+ *  A pointer to some application-specific opaque data (or NULL),\n+ *  which is supplied back to the application when @c finalize_fun is\n+ *  called.\n+ *\n+ * @return\n+ *  - >= 0: The identifier for this registration.\n+ *  - -ENOMEM: Unable to allocate sufficient resources.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_finalize_register(struct rte_dispatcher *dispatcher,\n+\t\t\t\t rte_dispatcher_finalize_t finalize_fun,\n+\t\t\t\t void *finalize_data);\n+\n+/**\n+ * Unregister a finalize callback.\n+ *\n+ * This function may be called by any thread (including unregistered\n+ * non-EAL threads), but not while the dispatcher is running on\n+ * any service lcore.\n+ *\n+ * @param dispatcher\n+ *  The dispatcher instance.\n+ *\n+ * @param reg_id\n+ *  The finalize registration id returned by the original\n+ *  rte_dispatcher_finalize_register() call.\n+ *\n+ * @return\n+ *  - 0: Success\n+ *  - -EINVAL: The @c reg_id parameter was invalid.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_finalize_unregister(struct rte_dispatcher *dispatcher,\n+\t\t\t\t   int reg_id);\n+\n+/**\n+ * Start a dispatcher instance.\n+ *\n+ * Enables the dispatcher service.\n+ *\n+ * The underlying event device must have been started prior to calling\n+ * rte_dispatcher_start().\n+ *\n+ * For the dispatcher to actually perform work (i.e., dispatch\n+ * events), its service must have been mapped to one or more service\n+ * lcores, and its service run state set to '1'. A dispatcher's\n+ * service is retrieved using rte_dispatcher_service_id_get().\n+ *\n+ * Each service lcore to which the dispatcher is mapped should\n+ * have at least one event port configured. Such configuration is\n+ * performed by calling rte_dispatcher_bind_port_to_lcore(), prior to\n+ * starting the dispatcher.\n+ *\n+ * @param dispatcher\n+ *  The dispatcher instance.\n+ *\n+ * @return\n+ *  - 0: Success\n+ *  - <0: Error code on failure\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_start(struct rte_dispatcher *dispatcher);\n+\n+/**\n+ * Stop a running dispatcher instance.\n+ *\n+ * Disables the dispatcher service.\n+ *\n+ * @param dispatcher\n+ *  The dispatcher instance.\n+ *\n+ * @return\n+ *  - 0: Success\n+ *  - -EINVAL: Invalid @c id.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_stop(struct rte_dispatcher *dispatcher);\n+\n+/**\n+ * Retrieve statistics for a dispatcher instance.\n+ *\n+ * This function is MT safe and may be called by any thread\n+ * (including unregistered non-EAL threads).\n+ *\n+ * @param dispatcher\n+ *  The dispatcher instance.\n+ * @param[out] stats\n+ *   A pointer to a structure to fill with statistics.\n+ */\n+__rte_experimental\n+void\n+rte_dispatcher_stats_get(const struct rte_dispatcher *dispatcher,\n+\t\t\t struct rte_dispatcher_stats *stats);\n+\n+/**\n+ * Reset statistics for a dispatcher instance.\n+ *\n+ * This function may be called by any thread (including unregistered\n+ * non-EAL threads), but may not produce the correct result if the\n+ * dispatcher is running on any service lcore.\n+ *\n+ * @param dispatcher\n+ *  The dispatcher instance.\n+ */\n+__rte_experimental\n+void\n+rte_dispatcher_stats_reset(struct rte_dispatcher *dispatcher);\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif /* __RTE_DISPATCHER__ */\ndiff --git a/lib/dispatcher/version.map b/lib/dispatcher/version.map\nnew file mode 100644\nindex 0000000000..8f9ad96522\n--- /dev/null\n+++ b/lib/dispatcher/version.map\n@@ -0,0 +1,20 @@\n+EXPERIMENTAL {\n+\tglobal:\n+\n+\t# added in 23.11\n+\trte_dispatcher_create;\n+\trte_dispatcher_free;\n+\trte_dispatcher_service_id_get;\n+\trte_dispatcher_bind_port_to_lcore;\n+\trte_dispatcher_unbind_port_from_lcore;\n+\trte_dispatcher_register;\n+\trte_dispatcher_unregister;\n+\trte_dispatcher_finalize_register;\n+\trte_dispatcher_finalize_unregister;\n+\trte_dispatcher_start;\n+\trte_dispatcher_stop;\n+\trte_dispatcher_stats_get;\n+\trte_dispatcher_stats_reset;\n+\n+\tlocal: *;\n+};\ndiff --git a/lib/meson.build b/lib/meson.build\nindex 099b0ed18a..3093b338d2 100644\n--- a/lib/meson.build\n+++ b/lib/meson.build\n@@ -35,6 +35,7 @@ libraries = [\n         'distributor',\n         'efd',\n         'eventdev',\n+        'dispatcher', # dispatcher depends on eventdev\n         'gpudev',\n         'gro',\n         'gso',\n@@ -81,6 +82,7 @@ optional_libs = [\n         'cfgfile',\n         'compressdev',\n         'cryptodev',\n+        'dispatcher',\n         'distributor',\n         'dmadev',\n         'efd',\n",
    "prefixes": [
        "v5",
        "1/3"
    ]
}