get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 131150,
    "url": "http://patchwork.dpdk.org/api/patches/131150/?format=api",
    "web_url": "http://patchwork.dpdk.org/project/dpdk/patch/20230904130313.327809-2-mattias.ronnblom@ericsson.com/",
    "project": {
        "id": 1,
        "url": "http://patchwork.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": "<20230904130313.327809-2-mattias.ronnblom@ericsson.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230904130313.327809-2-mattias.ronnblom@ericsson.com",
    "date": "2023-09-04T13:03:11",
    "name": "[v3,1/3] lib: introduce dispatcher library",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "0e51b27b71a3d9c88b5a368fa8e86e006b3ca5a8",
    "submitter": {
        "id": 1077,
        "url": "http://patchwork.dpdk.org/api/people/1077/?format=api",
        "name": "Mattias Rönnblom",
        "email": "mattias.ronnblom@ericsson.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patchwork.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patchwork.dpdk.org/project/dpdk/patch/20230904130313.327809-2-mattias.ronnblom@ericsson.com/mbox/",
    "series": [
        {
            "id": 29419,
            "url": "http://patchwork.dpdk.org/api/series/29419/?format=api",
            "web_url": "http://patchwork.dpdk.org/project/dpdk/list/?series=29419",
            "date": "2023-09-04T13:03:10",
            "name": "Add dispatcher library",
            "version": 3,
            "mbox": "http://patchwork.dpdk.org/series/29419/mbox/"
        }
    ],
    "comments": "http://patchwork.dpdk.org/api/patches/131150/comments/",
    "check": "warning",
    "checks": "http://patchwork.dpdk.org/api/patches/131150/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 4B744424F0;\n\tMon,  4 Sep 2023 15:09:05 +0200 (CEST)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 972C94067B;\n\tMon,  4 Sep 2023 15:09:04 +0200 (CEST)",
            "from EUR04-HE1-obe.outbound.protection.outlook.com\n (mail-he1eur04on2059.outbound.protection.outlook.com [40.107.7.59])\n by mails.dpdk.org (Postfix) with ESMTP id C80AD4067C;\n Mon,  4 Sep 2023 15:09:02 +0200 (CEST)",
            "from AM6PR04CA0050.eurprd04.prod.outlook.com (2603:10a6:20b:f0::27)\n by VI1PR07MB6272.eurprd07.prod.outlook.com (2603:10a6:800:136::9)\n with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6745.30; Mon, 4 Sep\n 2023 13:09:00 +0000",
            "from AM1PEPF000252DC.eurprd07.prod.outlook.com\n (2603:10a6:20b:f0:cafe::2c) by AM6PR04CA0050.outlook.office365.com\n (2603:10a6:20b:f0::27) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6745.32 via Frontend\n Transport; Mon, 4 Sep 2023 13:08:59 +0000",
            "from oa.msg.ericsson.com (192.176.1.74) by\n AM1PEPF000252DC.mail.protection.outlook.com (10.167.16.54) with Microsoft\n SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id\n 15.20.6768.25 via Frontend Transport; Mon, 4 Sep 2023 13:08:59 +0000",
            "from ESESSMB501.ericsson.se (153.88.183.162) 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; Mon, 4 Sep 2023 15:08:59 +0200",
            "from seliicinfr00049.seli.gic.ericsson.se (153.88.183.153) by\n smtp.internal.ericsson.com (153.88.183.189) with Microsoft SMTP Server id\n 15.1.2507.32 via Frontend Transport; Mon, 4 Sep 2023 15:08:59 +0200",
            "from breslau.. (seliicwb00002.seli.gic.ericsson.se [10.156.25.100])\n by seliicinfr00049.seli.gic.ericsson.se (Postfix) with ESMTP id\n 2A7EB380061; Mon,  4 Sep 2023 15:08:59 +0200 (CEST)"
        ],
        "ARC-Seal": "i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;\n b=R+PDqTJ9FVmmVKTWTBHp6mKy/FLyg1NhkiR2gTVq3uwV5PF+hcQZSGq0oKShGCtqHvdtzSdj9CB5fphlvg2Gk9xJPSjpcNhSmmNehHCewzq3P8ScmeeYQ2VY5gWw6M8WlhjcYdcN+krVmFNJS94iOvnL7dSr/34aEWnNiBUwXfgd8W3ZXaVUg4JeAaKR+RaxLwn/o10dxP3m1wrrMEBV1rRaC1J9kLVKkdfl6RK86+v0kI7v9U0pv6a3Ax6h8cNAwajCZTbp+RlXnrhMMdoR929euYdhe+TkUF6lKkbdOmH344JlqGCGpWVGngrxq//8P9RtZ9tVvszbXr0rYKq9Iw==",
        "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=0jkrKsR+/S7K2G4VaQ4qj4v8CAO2N0qfJ9zCQHr3kE4=;\n b=TsvZ0UOrTsGom4VDoFx2uohUEXN8iLHlZkXW7n4TY6MbycoOWffqtaE8sGXQ/dxwZrLdULKJoaxuFBgrq2PrWtX+lfwcvGDcbDusASW3HHJTNgiA8yfOO1CFE7bZZgwNOgvZsqP8tO3AJ2toOLm/mkVbuBja9cDvHJ4AnOMGvqQqW05MxFbk9uDuOno+qoDSQ110nFW0TdYon185ET0NoLCYMJHmX775RoKbu3/eimUeXJ1x/+k4RVc1i9i0cz2xtppYDHGDE1UTQc+kYhJ06OrTsF49iagJIkdYVQoFUmK3rBPt3g8S6wsXluPEG+CUpS5LflZCTW4lbenn9YHkzw==",
        "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=0jkrKsR+/S7K2G4VaQ4qj4v8CAO2N0qfJ9zCQHr3kE4=;\n b=pn+Gj1yRMGWw3ge+Afiuhc1NwsQJ4OcxU0PFckVUIzY1kmMm9U8wBoiX3pyXLubnCX3YtD4kZK59rjDNjrEoClAB+sfcnepXrRznxUo4yDZZl6amyXPJDDDaLuDUJPmC2+sHZyezV3x+kkiU3QHs4JTnMipycmq7PCHsJ4tXBYY=",
        "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 v3 1/3] lib: introduce dispatcher library",
        "Date": "Mon, 4 Sep 2023 15:03:11 +0200",
        "Message-ID": "<20230904130313.327809-2-mattias.ronnblom@ericsson.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20230904130313.327809-1-mattias.ronnblom@ericsson.com>",
        "References": "<20230616074041.159675-2-mattias.ronnblom@ericsson.com>\n <20230904130313.327809-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": "AM1PEPF000252DC:EE_|VI1PR07MB6272:EE_",
        "X-MS-Office365-Filtering-Correlation-Id": "7ef266eb-ddfb-4418-bb44-08dbad481a8d",
        "X-MS-Exchange-SenderADCheck": "1",
        "X-MS-Exchange-AntiSpam-Relay": "0",
        "X-Microsoft-Antispam": "BCL:0;",
        "X-Microsoft-Antispam-Message-Info": "\n 4uH+ifK3pDNs+6jBIwUfmz5lCeTsL4BaMpuDNgG8qQovs3mzXjUxG7vWSwsh8K6wS73MgP8TdJ2eslIdPTf8kAbX32dlR25wtgF9gPhmLUKZK7GiqrdE5V+MqU8nHAquF2sDjYD8jVLkppvwbbYyzE7POwhkhQAp8Ng9TAVaiI4vgXrH0poJSnWqMEdCLvuZWPYIqKJrUvr43oio2mjbEsHOoKlYp7XgBQFPZeblf8W++ZQlWrtjVKf7cdLq0iw+LTfyrJ94wf4LpS1ai2mfurJ8LPvva6cW+J6E18wf4WSlvDH1VHG8YtCMYuaouueWdm99edS+rtozaek9bQa6/kMpgOsY1S7izH7VSc32r/t0zg7AXJ0yCYitwAL4o2UfGgHgowmFWNv3+7vPYOF/zlJOR2rvyXIL2yCRMDmcjUg/+LLz1Pi8itKvID5HIjI9zffUnnHK1/3xvm80cLcyBUVJa4euqPrggMJb7q2gWzfhcOEVpBIBb4+k7fqarGpYpWOTilPd7qZQlcB6p3Pp85UU+OQf5h+czUlmvnO2x1IeAg07rZHjsvV7mbbrXUSEIvOr0dRmiDHxX5RVohNhic3NxE3N6aXW4OvpLOmUL/f1K1b5J5VzHzXyDuHLemQqsSfEwrSaOVguSKLGDsZsUO53bUTXbtENQBWwBAqivTS+agkjbO8EKP4tj++bhI8PpchpmCJTB/hvDdBMCl0tACx3nTHZrHpY4ektiQNE2f3JpvevzQheIh2VnTrmDyB27tALGKilFLiCuXka8wyE/wqPTyI0zdCUETlEIBXeCac=",
        "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)(39860400002)(136003)(376002)(346002)(396003)(451199024)(186009)(1800799009)(82310400011)(36840700001)(40470700004)(46966006)(316002)(86362001)(36756003)(6916009)(54906003)(2906002)(70206006)(70586007)(40480700001)(8936002)(5660300002)(4326008)(8676002)(41300700001)(40460700003)(107886003)(36860700001)(2616005)(83380400001)(26005)(336012)(1076003)(47076005)(6266002)(30864003)(66574015)(7416002)(478600001)(7636003)(82960400001)(356005)(82740400003)(6666004);\n DIR:OUT; SFP:1101;",
        "X-OriginatorOrg": "ericsson.com",
        "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "04 Sep 2023 13:08:59.7335 (UTC)",
        "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n 7ef266eb-ddfb-4418-bb44-08dbad481a8d",
        "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 AM1PEPF000252DC.eurprd07.prod.outlook.com",
        "X-MS-Exchange-CrossTenant-AuthAs": "Anonymous",
        "X-MS-Exchange-CrossTenant-FromEntityHeader": "HybridOnPrem",
        "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "VI1PR07MB6272",
        "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 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 lib/dispatcher/meson.build      |  17 +\n lib/dispatcher/rte_dispatcher.c | 791 ++++++++++++++++++++++++++++++++\n lib/dispatcher/rte_dispatcher.h | 480 +++++++++++++++++++\n lib/dispatcher/version.map      |  20 +\n lib/meson.build                 |   2 +\n 6 files changed, 1313 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/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..3319fe09f2\n--- /dev/null\n+++ b/lib/dispatcher/rte_dispatcher.c\n@@ -0,0 +1,791 @@\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+\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 id;\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 struct rte_dispatcher *dispatchers[UINT8_MAX];\n+\n+static bool\n+evd_has_dispatcher(uint8_t id)\n+{\n+\treturn dispatchers[id] != NULL;\n+}\n+\n+static struct rte_dispatcher *\n+evd_get_dispatcher(uint8_t id)\n+{\n+\treturn dispatchers[id];\n+}\n+\n+static void\n+evd_set_dispatcher(uint8_t id, struct rte_dispatcher *dispatcher)\n+{\n+\tdispatchers[id] = dispatcher;\n+}\n+\n+#define EVD_VALID_ID_OR_RET_EINVAL(id)\t\t\t\t\t\\\n+\tdo {\t\t\t\t\t\t\t\t\\\n+\t\tif (unlikely(!evd_has_dispatcher(id))) {\t\t\\\n+\t\t\tRTE_EDEV_LOG_ERR(\"Invalid dispatcher id %d\\n\", id); \\\n+\t\t\treturn -EINVAL;\t\t\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\\\n+\t} while (0)\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, RTE_SERVICE_NAME_MAX - 1, \"evd_%d\",\n+\t\t dispatcher->id);\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+int\n+rte_dispatcher_create(uint8_t id, uint8_t event_dev_id)\n+{\n+\tint socket_id;\n+\tstruct rte_dispatcher *dispatcher;\n+\tint rc;\n+\n+\tif (evd_has_dispatcher(id)) {\n+\t\tRTE_EDEV_LOG_ERR(\"Dispatcher with id %d already exists\\n\",\n+\t\t\t\t id);\n+\t\treturn -EEXIST;\n+\t}\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\treturn -ENOMEM;\n+\t}\n+\n+\t*dispatcher = (struct rte_dispatcher) {\n+\t\t.id = id,\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\treturn rc;\n+\t}\n+\n+\tevd_set_dispatcher(id, dispatcher);\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_dispatcher_free(uint8_t id)\n+{\n+\tstruct rte_dispatcher *dispatcher;\n+\tint rc;\n+\n+\tEVD_VALID_ID_OR_RET_EINVAL(id);\n+\tdispatcher = evd_get_dispatcher(id);\n+\n+\trc = evd_service_unregister(dispatcher);\n+\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tevd_set_dispatcher(id, NULL);\n+\n+\trte_free(dispatcher);\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_dispatcher_service_id_get(uint8_t id, uint32_t *service_id)\n+{\n+\tstruct rte_dispatcher *dispatcher;\n+\n+\tEVD_VALID_ID_OR_RET_EINVAL(id);\n+\tdispatcher = evd_get_dispatcher(id);\n+\n+\t*service_id = dispatcher->service_id;\n+\n+\treturn 0;\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(uint8_t id, uint8_t event_port_id,\n+\t\t\t\t\tuint16_t batch_size, uint64_t timeout,\n+\t\t\t\t\tunsigned int lcore_id)\n+{\n+\tstruct rte_dispatcher *dispatcher;\n+\tstruct rte_dispatcher_lcore *lcore;\n+\tstruct rte_dispatcher_lcore_port *port;\n+\n+\tEVD_VALID_ID_OR_RET_EINVAL(id);\n+\tdispatcher = evd_get_dispatcher(id);\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(uint8_t id, uint8_t event_port_id,\n+\t\t\t\t\t    unsigned int lcore_id)\n+{\n+\tstruct rte_dispatcher *dispatcher;\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+\tEVD_VALID_ID_OR_RET_EINVAL(id);\n+\tdispatcher = evd_get_dispatcher(id);\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(uint8_t id,\n+\t\t\t      rte_dispatcher_match_t match_fun,\n+\t\t\t      void *match_data,\n+\t\t\t      rte_dispatcher_process_t process_fun,\n+\t\t\t      void *process_data)\n+{\n+\tstruct rte_dispatcher *dispatcher;\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+\tEVD_VALID_ID_OR_RET_EINVAL(id);\n+\tdispatcher = evd_get_dispatcher(id);\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\treturn -EINVAL;\n+\n+\thandler_idx = &lcore->handlers[0] - unreg_handler;\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(uint8_t id, int handler_id)\n+{\n+\tstruct rte_dispatcher *dispatcher;\n+\tint rc;\n+\n+\tEVD_VALID_ID_OR_RET_EINVAL(id);\n+\tdispatcher = evd_get_dispatcher(id);\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(uint8_t id,\n+\t\t\t      rte_dispatcher_finalize_t finalize_fun,\n+\t\t\t      void *finalize_data)\n+{\n+\tstruct rte_dispatcher *dispatcher;\n+\tstruct rte_dispatcher_finalizer *finalizer;\n+\n+\tEVD_VALID_ID_OR_RET_EINVAL(id);\n+\tdispatcher = evd_get_dispatcher(id);\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(uint8_t id, int handler_id)\n+{\n+\tstruct rte_dispatcher *dispatcher;\n+\tstruct rte_dispatcher_finalizer *unreg_finalizer;\n+\tint finalizer_idx;\n+\tuint16_t last_idx;\n+\n+\tEVD_VALID_ID_OR_RET_EINVAL(id);\n+\tdispatcher = evd_get_dispatcher(id);\n+\n+\tunreg_finalizer = evd_get_finalizer_by_id(dispatcher, handler_id);\n+\n+\tif (unreg_finalizer == NULL)\n+\t\treturn -EINVAL;\n+\n+\tfinalizer_idx = &dispatcher->finalizers[0] - unreg_finalizer;\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(uint8_t id, int state)\n+{\n+\tstruct rte_dispatcher *dispatcher;\n+\tint rc;\n+\n+\tEVD_VALID_ID_OR_RET_EINVAL(id);\n+\tdispatcher = evd_get_dispatcher(id);\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(uint8_t id)\n+{\n+\treturn evd_set_service_runstate(id, 1);\n+}\n+\n+int\n+rte_dispatcher_stop(uint8_t id)\n+{\n+\treturn evd_set_service_runstate(id, 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+int\n+rte_dispatcher_stats_get(uint8_t id,\n+\t\t\t       struct rte_dispatcher_stats *stats)\n+{\n+\tstruct rte_dispatcher *dispatcher;\n+\tunsigned int lcore_id;\n+\n+\tEVD_VALID_ID_OR_RET_EINVAL(id);\n+\tdispatcher = evd_get_dispatcher(id);\n+\n+\t*stats = (struct rte_dispatcher_stats) {};\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\tevd_aggregate_stats(stats, &lcore->stats);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_dispatcher_stats_reset(uint8_t id)\n+{\n+\tstruct rte_dispatcher *dispatcher;\n+\tunsigned int lcore_id;\n+\n+\tEVD_VALID_ID_OR_RET_EINVAL(id);\n+\tdispatcher = evd_get_dispatcher(id);\n+\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+\n+\treturn 0;\n+\n+}\ndiff --git a/lib/dispatcher/rte_dispatcher.h b/lib/dispatcher/rte_dispatcher.h\nnew file mode 100644\nindex 0000000000..6712687a08\n--- /dev/null\n+++ b/lib/dispatcher/rte_dispatcher.h\n@@ -0,0 +1,480 @@\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+ * 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 id\n+ *  An application-specified, unique (across all dispatcher\n+ *  instances) identifier.\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+ *   - 0: Success\n+ *   - <0: Error code on failure\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_create(uint8_t id, uint8_t event_dev_id);\n+\n+/**\n+ * Free a dispatcher.\n+ *\n+ * @param id\n+ *  The dispatcher identifier.\n+ *\n+ * @return\n+ *  - 0: Success\n+ *  - <0: Error code on failure\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_free(uint8_t id);\n+\n+/**\n+ * Retrieve the service identifier of a dispatcher.\n+ *\n+ * @param id\n+ *  The dispatcher identifier.\n+ *\n+ * @param [out] service_id\n+ *  A pointer to a caller-supplied buffer where the dispatcher's\n+ *  service id will be stored.\n+ *\n+ * @return\n+ *  - 0: Success\n+ *  - <0: Error code on failure.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_service_id_get(uint8_t id, uint32_t *service_id);\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 id\n+ *  The dispatcher identifier.\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(uint8_t id, uint8_t event_port_id,\n+\t\t\t\t\tuint16_t batch_size, uint64_t timeout,\n+\t\t\t\t\tunsigned 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 id\n+ *  The dispatcher identifier.\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+ *  - -EINVAL: Invalid @c id.\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(uint8_t id, uint8_t event_port_id,\n+\t\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 id\n+ *  The dispatcher identifier.\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(uint8_t id,\n+\t\t\t      rte_dispatcher_match_t match_fun,\n+\t\t\t      void *match_cb_data,\n+\t\t\t      rte_dispatcher_process_t process_fun,\n+\t\t\t      void *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 id\n+ *  The dispatcher identifier.\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 id and/or the @c handler_id parameter was invalid.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_unregister(uint8_t id, 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 id\n+ *  The dispatcher identifier.\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(uint8_t id,\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 id\n+ *  The dispatcher identifier.\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 id and/or the @c reg_id parameter was invalid.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_finalize_unregister(uint8_t id, 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 id\n+ *  The dispatcher identifier.\n+ *\n+ * @return\n+ *  - 0: Success\n+ *  - -EINVAL: Invalid @c id.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_start(uint8_t id);\n+\n+/**\n+ * Stop a running dispatcher instance.\n+ *\n+ * Disables the dispatcher service.\n+ *\n+ * @param id\n+ *  The dispatcher identifier.\n+ *\n+ * @return\n+ *  - 0: Success\n+ *  - -EINVAL: Invalid @c id.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_stop(uint8_t id);\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 id\n+ *  The dispatcher identifier.\n+ * @param[out] stats\n+ *   A pointer to a structure to fill with statistics.\n+ * @return\n+ *  - 0: Success\n+ *  - -EINVAL: The @c id parameter was invalid.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_stats_get(uint8_t id,\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 id\n+ *  The dispatcher identifier.\n+ *\n+ * @return\n+ *  - 0: Success\n+ *  - -EINVAL: The @c id parameter was invalid.\n+ */\n+__rte_experimental\n+int\n+rte_dispatcher_stats_reset(uint8_t id);\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": [
        "v3",
        "1/3"
    ]
}