get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 132446,
    "url": "http://patchwork.dpdk.org/api/patches/132446/?format=api",
    "web_url": "http://patchwork.dpdk.org/project/dpdk/patch/20231009181711.440865-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": "<20231009181711.440865-2-mattias.ronnblom@ericsson.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20231009181711.440865-2-mattias.ronnblom@ericsson.com",
    "date": "2023-10-09T18:17:09",
    "name": "[v6,1/3] lib: introduce dispatcher library",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "c19c212fc73b162168cd6ae4ac50fb8b464186e7",
    "submitter": {
        "id": 1077,
        "url": "http://patchwork.dpdk.org/api/people/1077/?format=api",
        "name": "Mattias Rönnblom",
        "email": "mattias.ronnblom@ericsson.com"
    },
    "delegate": {
        "id": 24651,
        "url": "http://patchwork.dpdk.org/api/users/24651/?format=api",
        "username": "dmarchand",
        "first_name": "David",
        "last_name": "Marchand",
        "email": "david.marchand@redhat.com"
    },
    "mbox": "http://patchwork.dpdk.org/project/dpdk/patch/20231009181711.440865-2-mattias.ronnblom@ericsson.com/mbox/",
    "series": [
        {
            "id": 29781,
            "url": "http://patchwork.dpdk.org/api/series/29781/?format=api",
            "web_url": "http://patchwork.dpdk.org/project/dpdk/list/?series=29781",
            "date": "2023-10-09T18:17:08",
            "name": "Add dispatcher library",
            "version": 6,
            "mbox": "http://patchwork.dpdk.org/series/29781/mbox/"
        }
    ],
    "comments": "http://patchwork.dpdk.org/api/patches/132446/comments/",
    "check": "success",
    "checks": "http://patchwork.dpdk.org/api/patches/132446/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 6595642340;\n\tMon,  9 Oct 2023 20:22:41 +0200 (CEST)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 3D34A4068E;\n\tMon,  9 Oct 2023 20:22:37 +0200 (CEST)",
            "from EUR04-DB3-obe.outbound.protection.outlook.com\n (mail-db3eur04on2074.outbound.protection.outlook.com [40.107.6.74])\n by mails.dpdk.org (Postfix) with ESMTP id CB50840648;\n Mon,  9 Oct 2023 20:22:35 +0200 (CEST)",
            "from AM6PR01CA0040.eurprd01.prod.exchangelabs.com\n (2603:10a6:20b:e0::17) by AS4PR07MB9631.eurprd07.prod.outlook.com\n (2603:10a6:20b:4f4::5) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6863.36; Mon, 9 Oct\n 2023 18:22:32 +0000",
            "from AMS1EPF0000004A.eurprd04.prod.outlook.com\n (2603:10a6:20b:e0:cafe::22) by AM6PR01CA0040.outlook.office365.com\n (2603:10a6:20b:e0::17) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6863.35 via Frontend\n Transport; Mon, 9 Oct 2023 18:22:32 +0000",
            "from oa.msg.ericsson.com (192.176.1.74) by\n AMS1EPF0000004A.mail.protection.outlook.com (10.167.16.134) with Microsoft\n SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id\n 15.20.6838.22 via Frontend Transport; Mon, 9 Oct 2023 18:22:32 +0000",
            "from SESAMB601.ericsson.se (100.87.178.23) by ESESSMB501.ericsson.se\n (153.88.183.162) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.32; Mon, 9\n Oct 2023 20:22:32 +0200",
            "from ESESBMB502.ericsson.se (153.88.183.34) by SESAMB601.ericsson.se\n (100.87.178.23) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.2.1118.26; Mon, 9 Oct\n 2023 20:22:31 +0200",
            "from seliicinfr00050.seli.gic.ericsson.se (153.88.183.153) by\n smtp.internal.ericsson.com (153.88.183.185) with Microsoft SMTP Server id\n 15.1.2507.32 via Frontend Transport; Mon, 9 Oct 2023 20:22:31 +0200",
            "from breslau.. (seliicwb00002.seli.gic.ericsson.se [10.156.25.100])\n by seliicinfr00050.seli.gic.ericsson.se (Postfix) with ESMTP id\n C76391C006A; Mon,  9 Oct 2023 20:22:31 +0200 (CEST)"
        ],
        "ARC-Seal": "i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;\n b=Bu2kAl5L15AjwwoDIzHWBxwta+RwPLOeNWradvGPW6WdQrUXjEWwq7ozCsfrIcQlc5sOrKfGGy2OdoFl4P9gtD09XH4XR9bQZ5RTg1Xzl/jp31JJrBwDvv6Wgo6SiBBQOKIsB9CrFBuM/4SdQGuYlovfKGB33eus/L0rPw0w0UlquHL7F0d/kSDlDSVWvgwzVdhdZBFEkEZf1HGCnXD+8S75kryvqFcse9D9dorymjygQs9i/YqsatuQ6Xpcgr7cuxEiXJVPxDcswIY2ysIa1R6Pc159C2wbgfVCIl1sDy12qZe/qMoLBJPXptFXjdWOeiALvZEOU/hfc2xOep5exg==",
        "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=KuMcmt16ZSpSUsB7YhaIJETm0+fP9sDjNs6YhNNyiAE=;\n b=gfDp9lX2d5qVHEOno4bk8lyU+iGNO/TcXG65YY8JezoZnjd0wrCQJjn+tkG2mfixYFjO9SikKUbeD2VlS2L9nFw3SnS+6YWe1jbhCWznlN1to+89ph/BBgrbMyVjAXQH098spanZZJGmlgkWqBdP+8jjKBLIa94xMzvJkx3Bvn+/01/Le7BSxT0H9XQ2U+1M1VdVyJ2aN+q0upS8L0sMQoMCnd60LR8xiI5c6ykJVsFLtgIRQbcWF4tO7hlBSe6M5atYMUR4xk+LECYkPQ/YyktvdV2nGRHhrO+0Nt7BSQ8VAUdjL2GTpCyB0e7XVskBHp4kiOZqE5Whcy1eGve31g==",
        "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=KuMcmt16ZSpSUsB7YhaIJETm0+fP9sDjNs6YhNNyiAE=;\n b=mra0GTJ6R0hTAmBVZgz2pQeB6oSxAnVWKwwajr/69QdlPp9SBkKTJ9tpSzFcVXXNIW966Qr8hAnCpmS6Sy1vbwk9NXpfa8pUAC98LKGr8/CxMZk4RfUBMk5BCKTFbUSVZyVnnLTthL31uiBYvxpr4CwD/+OpoXuJBBZyfKmNBVY=",
        "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>, <david.marchand@redhat.com>",
        "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 v6 1/3] lib: introduce dispatcher library",
        "Date": "Mon, 9 Oct 2023 20:17:09 +0200",
        "Message-ID": "<20231009181711.440865-2-mattias.ronnblom@ericsson.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20231009181711.440865-1-mattias.ronnblom@ericsson.com>",
        "References": "<20230928073056.359356-2-mattias.ronnblom@ericsson.com>\n <20231009181711.440865-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": "AMS1EPF0000004A:EE_|AS4PR07MB9631:EE_",
        "X-MS-Office365-Filtering-Correlation-Id": "55cc0eda-c54b-4186-9682-08dbc8f4b435",
        "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 ygEwKUlBHkNDJ91btohAx4xkIszqtkegQAshFT50fYsQ1smlPZXNoNABVW0fPDm202TP8Bv6gxOSV3FWex+VBrdcXWrK3nXu6jal7BLKQ7PaAX1e2NDMYygRysvNIHkcf2EsxO46Lp6u0Nj8ubrb+qFkT6Rw2kyE3NLvPg7wSIcGeuhPiwjODEgx1DTYuiRrYRJAstBVO+5MQk85KWDChjm2OI7/fNSP3jhH8titESRfvfd3gjBhl7XL6i0spZ50eh57StL+lWvrl19CQn2r+e+MdFof7EQne4VqnBeEIqyKbOyDy9K6VqnPj6AdodSkf2udMqMKZAxD/TgKAbTyX6x2RSmiKOK1ZGpPaeMRt4Vl5af3uuVVEAVmT7htB5K11gxegffwwqTAVCHvLsNeWP++SEnDvo2ovDPM6FmRkqpwLE3QyH+i2uU8Gu+zBUMCXe0CxcfzBokoit1GlKp+0HbZJZcy6Sz9QpqGth4TAw25s+00uiEThM7FMw3nI95M9DtrUErMY900y3v+Ym6sU/2hoeNgPZKK7qmI50JJIQDluAKhcW1Ra3CMgcgv8FVYr9dTHJNXtsWFVrxFpbSfRQ37Dz1tVYD9dKWj6fb8npvlNyZKONQ62q8tqng+lLmLgPkhn5bco1nSgTIpt7I/PCHKMsmYgJSz8q/ceNzQSoYVROPYuBw1ghdnDXumHgjGyVOor6uFzoINcdA5v+oSCHYL8fQYGaZJnIay8ROWs7XgrI81mcmS2pmLeQHB0opdM6HEhE+XqvO8zbsJb4PDbcA0ETqJHctP7tiIfp+F77CMJL//DHvwfEKov39aClvAIc/0jG5VkdtAAMz3vQWs8qgV4gVtMwQypcXS7dONdBc=",
        "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)(376002)(136003)(39860400002)(346002)(230922051799003)(82310400011)(451199024)(64100799003)(1800799009)(186009)(46966006)(40470700004)(36840700001)(40460700003)(6266002)(336012)(2616005)(26005)(1076003)(8936002)(36860700001)(8676002)(478600001)(6666004)(66574015)(47076005)(30864003)(7416002)(107886003)(83380400001)(4326008)(2906002)(54906003)(70586007)(70206006)(110136005)(41300700001)(316002)(5660300002)(82960400001)(7636003)(36756003)(82740400003)(40480700001)(86362001)(356005)(2101003);\n DIR:OUT; SFP:1101;",
        "X-OriginatorOrg": "ericsson.com",
        "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "09 Oct 2023 18:22:32.3418 (UTC)",
        "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n 55cc0eda-c54b-4186-9682-08dbc8f4b435",
        "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 AMS1EPF0000004A.eurprd04.prod.outlook.com",
        "X-MS-Exchange-CrossTenant-AuthAs": "Anonymous",
        "X-MS-Exchange-CrossTenant-FromEntityHeader": "HybridOnPrem",
        "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "AS4PR07MB9631",
        "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 v6:\n o Use single tab as indentation for continuation lines in multiple-line\n   function prototypes. (David Marchand)\n o Add dispatcher library release note. (David Marchand)\n o Various indentation and spelling improvements. (David Marchand)\n o Add direct <stdint.h>, <stdbool.h> and <rte_compat.h> includes,\n   instead of relying on <rte_eventdev.h>. (David Marchand)\n o Avoid Doxygen post annotations for struct fields. (David Marchand)\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                            |   4 +\n doc/api/doxy-api-index.md              |   1 +\n doc/api/doxy-api.conf.in               |   1 +\n doc/guides/rel_notes/release_23_11.rst |   5 +\n lib/dispatcher/meson.build             |  13 +\n lib/dispatcher/rte_dispatcher.c        | 691 +++++++++++++++++++++++++\n lib/dispatcher/rte_dispatcher.h        | 466 +++++++++++++++++\n lib/dispatcher/version.map             |  20 +\n lib/meson.build                        |   2 +\n 9 files changed, 1203 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 00f5a5f9e6..a4372701c4 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -1733,6 +1733,10 @@ 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 \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 df801d32f9..93709e1d2c 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/doc/guides/rel_notes/release_23_11.rst b/doc/guides/rel_notes/release_23_11.rst\nindex 9319c86cd8..b5c5073018 100644\n--- a/doc/guides/rel_notes/release_23_11.rst\n+++ b/doc/guides/rel_notes/release_23_11.rst\n@@ -80,6 +80,11 @@ New Features\n   device is different from the Tx Ethernet device with respective driver\n   callback functions in ``rte_eth_recycle_mbufs``.\n \n+* **Added dispatcher library.**\n+\n+  Added dispatcher library which purpose is to help decouple different\n+  parts (modules) of an eventdev-based application.\n+\n * **Updated Solarflare net driver.**\n \n   * Added support for transfer flow action ``INDIRECT`` with subtype ``VXLAN_ENCAP``.\ndiff --git a/lib/dispatcher/meson.build b/lib/dispatcher/meson.build\nnew file mode 100644\nindex 0000000000..ffaef26a6d\n--- /dev/null\n+++ b/lib/dispatcher/meson.build\n@@ -0,0 +1,13 @@\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('rte_dispatcher.c')\n+headers = files('rte_dispatcher.h')\n+\n+deps += ['eventdev']\ndiff --git a/lib/dispatcher/rte_dispatcher.c b/lib/dispatcher/rte_dispatcher.c\nnew file mode 100644\nindex 0000000000..83e80ede96\n--- /dev/null\n+++ b/lib/dispatcher/rte_dispatcher.c\n@@ -0,0 +1,691 @@\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+\tconst 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+\tint 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+\tlcore->handlers[handler_idx - 1] = lcore->handlers[handler_idx];\n+\tlcore->handlers[handler_idx] = tmp;\n+}\n+\n+static inline void\n+evd_consider_prioritize_handler(struct rte_dispatcher_lcore *lcore,\n+\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+\tstruct rte_dispatcher_lcore *lcore,\n+\tstruct rte_dispatcher_lcore_port *port,\n+\tstruct 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+\tstruct rte_dispatcher_lcore *lcore,\n+\tstruct 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+\tstruct 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+\tif (rc != 0)\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+\tif (rc != 0)\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+\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+\tif (rc != 0)\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+\tuint8_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+\tuint8_t event_port_id, uint16_t batch_size, uint64_t timeout,\n+\tunsigned 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+\tuint8_t event_port_id, 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, 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+\tconst 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+\tconst 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+\trte_dispatcher_match_t match_fun, void *match_data,\n+\trte_dispatcher_process_t process_fun, void *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+\tint 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, 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+\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+\treturn evd_uninstall_handler(dispatcher, handler_id);\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+\trte_dispatcher_finalize_t finalize_fun, 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+\tint 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+\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+\tconst 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+\tstruct 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..1f2faaaee8\n--- /dev/null\n+++ b/lib/dispatcher/rte_dispatcher.h\n@@ -0,0 +1,466 @@\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 <stdbool.h>\n+#include <stdint.h>\n+#include <rte_compat.h>\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 event 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+\tstruct rte_event *events, uint16_t num, 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+\tvoid *cb_data);\n+\n+/**\n+ * Dispatcher statistics\n+ */\n+struct rte_dispatcher_stats {\n+\t/** Number of event dequeue calls made toward the event device. */\n+\tuint64_t poll_count;\n+\t/** Number of non-empty event batches dequeued from event device.*/\n+\tuint64_t ev_batch_count;\n+\t/** Number of events dispatched to a handler.*/\n+\tuint64_t ev_dispatch_count;\n+\t/** Number of events dropped because no handler was found. */\n+\tuint64_t ev_drop_count;\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+\tuint8_t event_port_id, uint16_t batch_size, uint64_t timeout,\n+\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 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+\tuint8_t event_port_id, 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+\trte_dispatcher_match_t match_fun, void *match_cb_data,\n+\trte_dispatcher_process_t process_fun, 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 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+\trte_dispatcher_finalize_t finalize_fun, 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, 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+\tstruct 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..44585e4f15\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_bind_port_to_lcore;\n+\trte_dispatcher_create;\n+\trte_dispatcher_finalize_register;\n+\trte_dispatcher_finalize_unregister;\n+\trte_dispatcher_free;\n+\trte_dispatcher_register;\n+\trte_dispatcher_service_id_get;\n+\trte_dispatcher_start;\n+\trte_dispatcher_stats_get;\n+\trte_dispatcher_stats_reset;\n+\trte_dispatcher_stop;\n+\trte_dispatcher_unbind_port_from_lcore;\n+\trte_dispatcher_unregister;\n+\n+\tlocal: *;\n+};\ndiff --git a/lib/meson.build b/lib/meson.build\nindex cf4aa63630..59d381bf7a 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": [
        "v6",
        "1/3"
    ]
}