get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 131825,
    "url": "https://patches.dpdk.org/api/patches/131825/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/20230922081912.7090-5-bruce.richardson@intel.com/",
    "project": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20230922081912.7090-5-bruce.richardson@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230922081912.7090-5-bruce.richardson@intel.com",
    "date": "2023-09-22T08:19:11",
    "name": "[RFC,4/5] app: add IO proxy app using shared memory interfaces",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "68066e0b188f3d632239db782cb04355c7c8526f",
    "submitter": {
        "id": 20,
        "url": "https://patches.dpdk.org/api/people/20/?format=api",
        "name": "Bruce Richardson",
        "email": "bruce.richardson@intel.com"
    },
    "delegate": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/20230922081912.7090-5-bruce.richardson@intel.com/mbox/",
    "series": [
        {
            "id": 29602,
            "url": "https://patches.dpdk.org/api/series/29602/?format=api",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=29602",
            "date": "2023-09-22T08:19:07",
            "name": "Using shared mempools for zero-copy IO proxying",
            "version": 1,
            "mbox": "https://patches.dpdk.org/series/29602/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/131825/comments/",
    "check": "warning",
    "checks": "https://patches.dpdk.org/api/patches/131825/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 F15B842612;\n\tFri, 22 Sep 2023 10:20:09 +0200 (CEST)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 2A29840A6E;\n\tFri, 22 Sep 2023 10:19:48 +0200 (CEST)",
            "from mgamail.intel.com (mgamail.intel.com [192.55.52.93])\n by mails.dpdk.org (Postfix) with ESMTP id 86A97402EC\n for <dev@dpdk.org>; Fri, 22 Sep 2023 10:19:43 +0200 (CEST)",
            "from fmsmga005.fm.intel.com ([10.253.24.32])\n by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 22 Sep 2023 01:19:35 -0700",
            "from silpixa00401385.ir.intel.com ([10.237.214.14])\n by fmsmga005.fm.intel.com with ESMTP; 22 Sep 2023 01:19:34 -0700"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple;\n d=intel.com; i=@intel.com; q=dns/txt; s=Intel;\n t=1695370783; x=1726906783;\n h=from:to:cc:subject:date:message-id:in-reply-to:\n references:mime-version:content-transfer-encoding;\n bh=xF9yrPvNRndGFLbQAc2hNdUvCFdStS47rLDogqd0gCY=;\n b=nlJREX5cVD7Y52dF0JbdsjZ/0TOJBmPja/VSHnu+tk/+GY3hPqilbY8M\n HIiQm5x8hQQwbrD8fNF0HKjaMEKS/uabwqA3iOISd0UzV2sYCBo3F9ILp\n Txe/lfI99kPXww6cOVXAniPM02/IWpwqvWq6QzKew9a+qpjn3oZNC/lbx\n q+lAJjp1NHAZML3wOQyIPqKsDMKN8GBri+lJ+tYLi+5rPOTreyNFEF9Qk\n PQBwHsA2FDP8zYMZ1y3VqTeZ8sv6t0RgOaXp0tFFnKrLopXGpJGxX9ZCv\n Hihwf/KLPj/Sa9JkytMHLGvNC1qVZG/JCnzuRwQmR1vcxoRpvuq+Wpc4K w==;",
        "X-IronPort-AV": [
            "E=McAfee;i=\"6600,9927,10840\"; a=\"378063977\"",
            "E=Sophos;i=\"6.03,167,1694761200\"; d=\"scan'208\";a=\"378063977\"",
            "E=McAfee;i=\"6600,9927,10840\"; a=\"1078281148\"",
            "E=Sophos;i=\"6.03,167,1694761200\"; d=\"scan'208\";a=\"1078281148\""
        ],
        "X-ExtLoop1": "1",
        "From": "Bruce Richardson <bruce.richardson@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "Bruce Richardson <bruce.richardson@intel.com>",
        "Subject": "[RFC PATCH 4/5] app: add IO proxy app using shared memory interfaces",
        "Date": "Fri, 22 Sep 2023 09:19:11 +0100",
        "Message-Id": "<20230922081912.7090-5-bruce.richardson@intel.com>",
        "X-Mailer": "git-send-email 2.39.2",
        "In-Reply-To": "<20230922081912.7090-1-bruce.richardson@intel.com>",
        "References": "<20230922081912.7090-1-bruce.richardson@intel.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "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": "This app uses the shared memory poll, and shared ethdev infrastructure\nto act as a zero-copy IO proxy to other applications. It has been tested\nand verified to work successfully proxying data to testpmd instances on\nthe system, with those testpmd instances each being passed a unix socket\nto work with via the shared memory bus \"-a sock:/path/to/sock...\"\nparameter.\n\nSigned-off-by: Bruce Richardson <bruce.richardson@intel.com>\n---\n app/io-proxy/command_fns.c | 160 ++++++++++\n app/io-proxy/commands.list |   6 +\n app/io-proxy/datapath.c    | 595 +++++++++++++++++++++++++++++++++++++\n app/io-proxy/datapath.h    |  37 +++\n app/io-proxy/datapath_mp.c |  78 +++++\n app/io-proxy/main.c        |  71 +++++\n app/io-proxy/meson.build   |  12 +\n app/meson.build            |   1 +\n 8 files changed, 960 insertions(+)\n create mode 100644 app/io-proxy/command_fns.c\n create mode 100644 app/io-proxy/commands.list\n create mode 100644 app/io-proxy/datapath.c\n create mode 100644 app/io-proxy/datapath.h\n create mode 100644 app/io-proxy/datapath_mp.c\n create mode 100644 app/io-proxy/main.c\n create mode 100644 app/io-proxy/meson.build",
    "diff": "diff --git a/app/io-proxy/command_fns.c b/app/io-proxy/command_fns.c\nnew file mode 100644\nindex 0000000000..f48921e005\n--- /dev/null\n+++ b/app/io-proxy/command_fns.c\n@@ -0,0 +1,160 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Intel Corporation\n+ */\n+\n+#include <stdlib.h>\n+#include <unistd.h>\n+#include <stdbool.h>\n+\n+#include <rte_ethdev.h>\n+\n+#include \"datapath.h\"\n+#include \"commands.h\"\n+\n+extern volatile bool quit;\n+extern volatile bool running_startup_script;\n+\n+void\n+cmd_add_socket_parsed(void *parsed_result, struct cmdline *cl __rte_unused,\n+\t\tvoid *data __rte_unused)\n+{\n+\tstruct cmd_add_socket_result *res = parsed_result;\n+\tuint64_t maxmem = 0;\n+\tchar *endchar;\n+\n+\tmaxmem = strtoull(res->memsize, &endchar, 0);\n+\tswitch (*endchar) {\n+\tcase 'G': case 'g':\n+\t\tmaxmem *= 1024;\n+\t\t/* fall-through */\n+\tcase 'M': case 'm':\n+\t\tmaxmem *= 1024;\n+\t\t/* fall-through */\n+\tcase 'K': case 'k':\n+\t\tmaxmem *= 1024;\n+\t\tbreak;\n+\t}\n+\tif (res->port >= MAX_PORTS_SUPPORTED) {\n+\t\tfprintf(stderr, \"Port id out of range. Must be <%u\\n\", MAX_PORTS_SUPPORTED);\n+\t\tgoto err;\n+\t}\n+\tif (res->queue >= MAX_QUEUES_SUPPORTED) {\n+\t\tfprintf(stderr, \"Queue id out of range. Must be <%u\\n\", MAX_QUEUES_SUPPORTED);\n+\t\tgoto err;\n+\t}\n+\tif (listen_unix_socket(res->path, maxmem, res->port, res->queue) != 0) {\n+\t\tfprintf(stderr, \"error initializing socket: %s\\n\", res->path);\n+\t\tgoto err;\n+\t}\n+\n+\tprintf(\"Created socket = %s with memsize = %s using port = %u, queue = %u\\n\",\n+\t\t\tres->path, res->memsize, res->port, res->queue);\n+\treturn;\n+\n+err:\n+\tif (running_startup_script) {\n+\t\tquit = true;\n+\t\t/* wait for main thread to quit. Just spin here for condition which\n+\t\t * will never actually come true, as main thread should just exit\n+\t\t */\n+\t\twhile (quit)\n+\t\t\tusleep(100);\n+\t}\n+\t/* if running interactively, do nothing on error except report it above */\n+}\n+\n+void\n+cmd_list_sockets_parsed(__rte_unused void *parsed_result,\n+\t\t__rte_unused struct cmdline *cl,\n+\t\t__rte_unused void *data)\n+{\n+\tconst char *path;\n+\tint sock;\n+\tuint64_t maxmem;\n+\tuint16_t port, queue;\n+\tbool connected;\n+\n+\tfor (int i = get_next_socket(0, &path, &sock, &maxmem, &port, &queue, &connected);\n+\t\t\ti < MAX_SOCKETS;\n+\t\t\ti = get_next_socket(i + 1, &path, &sock, &maxmem, &port,\n+\t\t\t\t\t&queue, &connected)) {\n+\t\tchar memstr[32];\n+\t\tif (maxmem % (1UL << 30) == 0)\n+\t\t\tsnprintf(memstr, sizeof(memstr), \"%\" PRIu64 \"G\", maxmem >> 30);\n+\t\telse if (maxmem % (1UL << 20) == 0)\n+\t\t\tsnprintf(memstr, sizeof(memstr), \"%\" PRIu64 \"M\", maxmem >> 20);\n+\t\telse if (maxmem % (1UL << 10) == 0)\n+\t\t\tsnprintf(memstr, sizeof(memstr), \"%\" PRIu64 \"K\", maxmem >> 10);\n+\t\telse\n+\t\t\tsnprintf(memstr, sizeof(memstr), \"%\" PRIu64, maxmem);\n+\n+\t\tprintf(\"Socket %s [%s]: mem=%s, port=%u, queue=%u\\n\",\n+\t\t\t\tpath, connected ? \"connected\" : \"idle\", memstr, port, queue);\n+\t}\n+}\n+\n+void\n+cmd_list_ports_parsed(__rte_unused void *parsed_result,\n+\t\t__rte_unused struct cmdline *cl,\n+\t\t__rte_unused void *data)\n+{\n+\tfor (int i = 0; i < rte_eth_dev_count_avail(); i++) {\n+\t\tstruct rte_ether_addr addr;\n+\t\tint retval = rte_eth_macaddr_get(i, &addr);\n+\t\tif (retval != 0) {\n+\t\t\tprintf(\"Port %d - MAC UNKNOWN\\n\", i);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tprintf(\"Port %d - \"RTE_ETHER_ADDR_PRT_FMT\"\\n\", i, RTE_ETHER_ADDR_BYTES(&addr));\n+\t}\n+}\n+\n+void\n+cmd_show_port_stats_parsed(__rte_unused void *parsed_result,\n+\t\t__rte_unused struct cmdline *cl,\n+\t\t__rte_unused void *data)\n+{\n+\tfor (int i = 0; i < rte_eth_dev_count_avail(); i++) {\n+\t\tstruct rte_eth_stats stats = {0};\n+\t\tint retval = rte_eth_stats_get(i, &stats);\n+\t\tif (retval != 0) {\n+\t\t\tprintf(\"Port %d - Cannot get stats\\n\", i);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tprintf(\"Port %d - ipkts: %\"PRIu64\", imissed: %\"PRIu64\n+\t\t\t\t\", ierrors: %\"PRIu64\", opkts: %\"PRIu64\"\\n\",\n+\t\t\t\ti, stats.ipackets, stats.imissed, stats.ierrors, stats.opackets);\n+\t}\n+}\n+\n+void\n+cmd_show_socket_stats_parsed(__rte_unused void *parsed_result,\n+\t\t__rte_unused struct cmdline *cl,\n+\t\t__rte_unused void *data)\n+{\n+\tconst char *path;\n+\tint sock;\n+\tuint64_t maxmem;\n+\tuint16_t port, queue;\n+\tbool connected;\n+\n+\tfor (int i = get_next_socket(0, &path, &sock, &maxmem, &port, &queue, &connected);\n+\t\t\ti < MAX_SOCKETS;\n+\t\t\ti = get_next_socket(i + 1, &path, &sock, &maxmem, &port,\n+\t\t\t\t\t&queue, &connected)) {\n+\t\tif (connected || dp_stats[i].rx != 0 || dp_stats[i].deq != 0)\n+\t\t\tprintf(\"Socket %u [port %u, q %u]: RX %\" PRIu64 \", Enq_drops %\" PRIu64\n+\t\t\t\t\t\", Deq %\" PRIu64 \", TX_drops %\" PRIu64 \"\\n\",\n+\t\t\t\t\ti, i / MAX_QUEUES_SUPPORTED, i % MAX_QUEUES_SUPPORTED,\n+\t\t\t\t\tdp_stats[i].rx, dp_stats[i].enq_drop,\n+\t\t\t\t\tdp_stats[i].deq, dp_stats[i].tx_drop);\n+\n+\t}\n+}\n+\n+void\n+cmd_quit_parsed(__rte_unused void *parsed_result, struct cmdline *cl,\n+\t\t__rte_unused void *data)\n+{\n+\tcmdline_quit(cl);\n+}\ndiff --git a/app/io-proxy/commands.list b/app/io-proxy/commands.list\nnew file mode 100644\nindex 0000000000..9dab9bba28\n--- /dev/null\n+++ b/app/io-proxy/commands.list\n@@ -0,0 +1,6 @@\n+add socket <STRING>path <STRING>memsize <UINT16>port <UINT16>queue\n+list sockets\n+list ports\n+show port stats\n+show socket stats\n+quit\ndiff --git a/app/io-proxy/datapath.c b/app/io-proxy/datapath.c\nnew file mode 100644\nindex 0000000000..1f7162de18\n--- /dev/null\n+++ b/app/io-proxy/datapath.c\n@@ -0,0 +1,595 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Intel Corporation\n+ */\n+#include <errno.h>\n+#include <stdarg.h>\n+#include <unistd.h>\n+#include <stdlib.h>\n+#include <libgen.h>\n+#include <sys/un.h>\n+#include <sys/stat.h>\n+#include <sys/mman.h>\n+#include <sys/socket.h>\n+#include <linux/memfd.h>\n+\n+#include <rte_eal.h>\n+#include <rte_dev.h>\n+#include <rte_malloc.h>\n+#include <rte_ethdev.h>\n+#include <rte_common.h>\n+#include <rte_config.h>\n+#include <rte_mempool.h>\n+#include <shared_mem_bus.h>\n+\n+#include \"datapath.h\"\n+\n+static int mempool_ops_index = -1;\n+static struct rte_mempool *default_mempool;\n+static volatile unsigned long long port_poll_mask;\n+static volatile unsigned long long used_poll_mask;\n+\n+struct listen_socket_params {\n+\tconst char *path;\n+\tint sock;\n+\tuint16_t port_id;\n+\tuint16_t qid;\n+\tuint64_t maxmem;\n+};\n+\n+#define S_IDX(p, q) (((p) * MAX_QUEUES_SUPPORTED) + (q))\n+static struct rte_ring *rx_rings[MAX_SOCKETS];\n+static struct rte_ring *tx_rings[MAX_SOCKETS];\n+static uintptr_t base_addrs[MAX_SOCKETS];\n+static uint64_t lengths[MAX_SOCKETS];\n+static struct rte_mempool *mps[MAX_SOCKETS];\n+static struct listen_socket_params sock_params[MAX_SOCKETS];\n+struct rxtx_stats dp_stats[MAX_SOCKETS] = {0};\n+\n+int\n+get_next_socket(int start, const char **path, int *sock, uint64_t *maxmem,\n+\t\tuint16_t *port, uint16_t *queue, bool *connected)\n+{\n+\tint i;\n+\tfor (i = start; i < MAX_SOCKETS; i++) {\n+\t\tif (sock_params[i].sock > 0) {\n+\t\t\t*path = sock_params[i].path;\n+\t\t\t*sock = sock_params[i].sock;\n+\t\t\t*maxmem = sock_params[i].maxmem;\n+\t\t\t*port = sock_params[i].port_id;\n+\t\t\t*queue = sock_params[i].qid;\n+\t\t\t*connected = (port_poll_mask & (1U << i)) != 0;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\treturn i;\n+}\n+\n+static int\n+init_port(uint16_t port_id, struct rte_mempool *mbuf_pool)\n+{\n+\tstruct rte_eth_conf port_conf = {\n+\t\t.rxmode = { .mq_mode = RTE_ETH_MQ_RX_RSS, },\n+\t\t.rx_adv_conf = {\n+\t\t\t.rss_conf = { .rss_hf = RTE_ETH_RSS_IP | RTE_ETH_RSS_UDP, },\n+\t\t},\n+\t};\n+\tstruct rte_eth_dev_info dev_info;\n+\tint socket = rte_socket_id();\n+\n+\tint retval = rte_eth_dev_info_get(port_id, &dev_info);\n+\tif (retval != 0) {\n+\t\tprintf(\"Error during getting device (port %u) info: %s\\n\",\n+\t\t\t\tport_id, strerror(-retval));\n+\t\treturn retval;\n+\t}\n+\n+\tif (dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE)\n+\t\tport_conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE;\n+\n+\tport_conf.rx_adv_conf.rss_conf.rss_hf &= dev_info.flow_type_rss_offloads;\n+\n+\tif (rte_eth_dev_configure(port_id, MAX_QUEUES_SUPPORTED, MAX_QUEUES_SUPPORTED,\n+\t\t\t&port_conf) < 0) {\n+\t\tprintf(\"Error configuring port\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tfor (uint16_t q = 0; q < MAX_QUEUES_SUPPORTED; q++) {\n+\t\tretval = rte_eth_rx_queue_setup(port_id, q, 128, socket, NULL, mbuf_pool);\n+\t\tif (retval < 0) {\n+\t\t\tprintf(\"Error running rx_queue_setup\\n\");\n+\t\t\treturn retval;\n+\t\t}\n+\t\tretval = rte_eth_tx_queue_setup(port_id, q, 256, socket, NULL);\n+\t\tif (retval < 0) {\n+\t\t\tprintf(\"Error running tx_queue_setup\\n\");\n+\t\t\treturn retval;\n+\t\t}\n+\t}\n+\n+\tretval = rte_eth_dev_start(port_id);\n+\tif (retval < 0) {\n+\t\tprintf(\"Error running dev_start\\n\");\n+\t\treturn retval;\n+\t}\n+\tprintf(\"Port %u started ok\\n\", port_id);\n+\n+\tif (rte_eth_promiscuous_enable(port_id) < 0)\n+\t\tprintf(\"Warning: could not enable promisc mode on port %u\\n\", port_id);\n+\n+\treturn 0;\n+}\n+\n+int\n+datapath_init(const char *corelist)\n+{\n+\t/* eal init requires non-const parameters, so copy */\n+\tchar *cl = strdup(corelist); /* todo, free copy */\n+\tchar l_flag[] = \"-l\";\n+\tchar in_mem[] = \"--in-memory\";\n+\tchar use_avx512[] = \"--force-max-simd-bitwidth=512\";\n+\tchar *argv[] = {\n+\t\t\tprogram_invocation_short_name,\n+\t\t\tl_flag, cl,\n+\t\t\tin_mem,\n+\t\t\tuse_avx512,\n+\t\t\tNULL,\n+\t};\n+\n+\tRTE_BUILD_BUG_ON(sizeof(port_poll_mask) * CHAR_BIT < MAX_SOCKETS);\n+\n+\tint ret = rte_eal_init(RTE_DIM(argv) - 1, argv);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tmempool_ops_index = check_mempool_ops();\n+\tif (mempool_ops_index == -1)\n+\t\trte_panic(\"Cannot get mempool ops\");\n+\tprintf(\"Mempool ops index is %d\\n\", mempool_ops_index);\n+\n+\tdefault_mempool = rte_pktmbuf_pool_create(\"proxy_def\",\n+\t\t\tMAX_SOCKETS * 200, 32, 0,\n+\t\t\tRTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());\n+\tif (default_mempool == NULL)\n+\t\trte_panic(\"Cannot create default mempool\\n\");\n+\n+\tint nb_ethdevs = rte_eth_dev_count_avail();\n+\tif (nb_ethdevs > MAX_PORTS_SUPPORTED) {\n+\t\tfprintf(stderr, \"More ports available than supported, some will be unused\\n\");\n+\t\tnb_ethdevs = MAX_PORTS_SUPPORTED;\n+\t}\n+\tfor (int i = 0; i < nb_ethdevs; i++) {\n+\t\tif (init_port(i, default_mempool) != 0)\n+\t\t\trte_panic(\"Cannot init port %d\\n\", i);\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+send_fd(int to, int fd, uint64_t fd_size, rte_iova_t iova, uint64_t pg_size)\n+{\n+\tstruct iovec iov = {0};\n+\tstruct msghdr msg = {0};\n+\tsize_t cmsglen = CMSG_LEN(sizeof(fd));\n+\tstruct cmsghdr *cmhdr = malloc(cmsglen);\n+\tint ret = 0;\n+\n+\tstruct {\n+\t\tuint64_t fd_size;\n+\t\trte_iova_t iova;\n+\t\tuint64_t pg_size;\n+\t} data_message = {fd_size, iova, pg_size};\n+\n+\tif (cmhdr == NULL)\n+\t\treturn -1;\n+\tiov.iov_base = (void *)&data_message;\n+\tiov.iov_len = sizeof(data_message);\n+\tmsg.msg_iov = &iov;\n+\tmsg.msg_iovlen = 1;\n+\tcmhdr->cmsg_level = SOL_SOCKET;\n+\tcmhdr->cmsg_type = SCM_RIGHTS;\n+\tcmhdr->cmsg_len = cmsglen;\n+\tmsg.msg_control = cmhdr;\n+\tmsg.msg_controllen = cmsglen;\n+\t*(int *)CMSG_DATA(cmhdr) = fd;\n+\n+\tif (sendmsg(to, &msg, 0) != (int)iov.iov_len) {\n+\t\tprintf(\"Error sending message to client, %s\\n\", strerror(errno));\n+\t\tret = -1;\n+\t}\n+\tfree(cmhdr);\n+\treturn ret;\n+}\n+\n+static int\n+reconfigure_queue(uint16_t port_id, uint16_t qid, struct rte_mempool *p)\n+{\n+\tif (rte_eth_dev_rx_queue_stop(port_id, qid) != 0) {\n+\t\tprintf(\"Error with rx_queue_stop\\n\");\n+\t\treturn -1;\n+\t}\n+\tif (rte_eth_dev_tx_queue_stop(port_id, qid) != 0) {\n+\t\tprintf(\"Error with tx_queue_stop\\n\");\n+\t\treturn -1;\n+\t}\n+\tif (rte_eth_rx_queue_setup(port_id, qid, 1024,\n+\t\t\trte_socket_id(), NULL, p) != 0) {\n+\t\tprintf(\"Error with rx_queue_setup\\n\");\n+\t\treturn -1;\n+\t}\n+\tif (rte_eth_dev_tx_queue_start(port_id, qid) != 0) {\n+\t\tprintf(\"Error with tx_queue_start\\n\");\n+\t\treturn -1;\n+\t}\n+\tif (rte_eth_dev_rx_queue_start(port_id, qid) != 0) {\n+\t\tprintf(\"Error with rx_queue_start\\n\");\n+\t\treturn -1;\n+\t}\n+\treturn 0;\n+}\n+\n+static void\n+handle_connection(int client, void *const client_mem, uint64_t memsize,\n+\t\tuint16_t port_id, uint16_t qid)\n+{\n+\tuintptr_t client_mmap_addr = 0;\n+\tstruct rte_ring *rx_ring, *tx_ring;\n+\tstruct rte_mempool *local_mp;\n+\tsize_t mempool_memsize = sizeof(*local_mp)\n+\t\t\t\t\t+ sizeof(local_mp->local_cache[0]) * RTE_MAX_LCORE\n+\t\t\t\t\t+ sizeof(struct rte_pktmbuf_pool_private);\n+\tlocal_mp = rte_malloc(NULL, mempool_memsize, 0);\n+\tif (local_mp == NULL) {\n+\t\tprintf(\"Error allocating mempool struct\\n\");\n+\t\treturn;\n+\t}\n+\tmemset(local_mp, 0, mempool_memsize);\n+\t*local_mp = (struct rte_mempool){\n+\t\t.name = \"proxy_mp\",\n+\t\t.cache_size = 256,\n+\t\t.ops_index = mempool_ops_index,\n+\t\t.pool_config = client_mem,\n+\t\t.private_data_size = sizeof(struct rte_pktmbuf_pool_private),\n+\t\t.local_cache = RTE_PTR_ADD(local_mp, sizeof(*local_mp)),\n+\t};\n+\tfor (uint i = 0; i < RTE_MAX_LCORE; i++) {\n+\t\tlocal_mp->local_cache[i].size = 256;\n+\t\tlocal_mp->local_cache[i].flushthresh = 300;\n+\t}\n+\n+\tstruct eth_shared_mem_msg *msg = malloc(sizeof(*msg) + 1024);\n+\tif (msg == NULL) {\n+\t\tprintf(\"Error mallocing message buffer\\n\");\n+\t\tgoto out;\n+\t}\n+\tint bytes_read = read(client, msg, sizeof(msg) + 1024);\n+\twhile (bytes_read != 0) {\n+\t\tswitch (msg->type) {\n+\t\tcase MSG_TYPE_MMAP_BASE_ADDR:\n+\t\t\tclient_mmap_addr = msg->offset;\n+\t\t\tprintf(\"Got mmap base addr of %p\\n\", (void *)client_mmap_addr);\n+\t\t\tbreak;\n+\t\tcase MSG_TYPE_MEMPOOL_OFFSET: {\n+\t\t\tstruct rte_mempool *remote_pool;\n+\t\t\tuintptr_t remote_pd_offset;\n+\n+\t\t\tremote_pool = RTE_PTR_ADD(client_mem, msg->offset);\n+\t\t\tremote_pd_offset = (uintptr_t)remote_pool->pool_data - client_mmap_addr;\n+\t\t\tlocal_mp->pool_data = RTE_PTR_ADD(client_mem, remote_pd_offset);\n+\t\t\tmemcpy(rte_mempool_get_priv(local_mp), rte_mempool_get_priv(remote_pool),\n+\t\t\t\t\tsizeof(struct rte_pktmbuf_pool_private));\n+\n+\t\t\tprintf(\"Got mempool offset of %p, stack name is %s\\n\",\n+\t\t\t\t\t(void *)msg->offset, (char *)local_mp->pool_data);\n+\t\t\tstruct rte_mbuf *mb = rte_pktmbuf_alloc(local_mp);\n+\t\t\tif (mb == NULL) {\n+\t\t\t\tprintf(\"Error allocating buffer\\n\");\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t\tif ((uintptr_t)mb->buf_addr != (uintptr_t)mb + 128)\n+\t\t\t\trte_panic(\"Error, bad buffer\\n\");\n+\t\t\trte_pktmbuf_free(mb);\n+\t\t\tbreak;\n+\t\t}\n+\t\tcase MSG_TYPE_RX_RING_OFFSET:\n+\t\t\tprintf(\"Got Rx ring offset of %p\\n\", (void *)msg->offset);\n+\t\t\trx_ring = RTE_PTR_ADD(client_mem, msg->offset);\n+\t\t\trx_rings[S_IDX(port_id, qid)] = rx_ring;\n+\t\t\tbreak;\n+\t\tcase MSG_TYPE_TX_RING_OFFSET:\n+\t\t\tprintf(\"Got Tx ring offset of %p\\n\", (void *)msg->offset);\n+\t\t\ttx_ring = RTE_PTR_ADD(client_mem, msg->offset);\n+\t\t\ttx_rings[S_IDX(port_id, qid)] = tx_ring;\n+\t\t\tbreak;\n+\n+\t\tcase MSG_TYPE_START:\n+\t\t\tbase_addrs[S_IDX(port_id, qid)] = (uintptr_t)client_mem;\n+\t\t\tlengths[S_IDX(port_id, qid)] = memsize;\n+\t\t\tmps[S_IDX(port_id, qid)] = local_mp;\n+\t\t\tif (reconfigure_queue(port_id, qid, local_mp) < 0)\n+\t\t\t\tgoto out;\n+\n+\t\t\tport_poll_mask |= (1UL << S_IDX(port_id, qid));\n+\t\t\twhile (used_poll_mask != port_poll_mask)\n+\t\t\t\tusleep(10);\n+\n+\t\t\t*msg = (struct eth_shared_mem_msg){ .type = MSG_TYPE_ACK, };\n+\t\t\tif (write(client, msg, sizeof(*msg)) < (int)sizeof(*msg))\n+\t\t\t\tgoto out;\n+\n+\t\t\tdp_stats[S_IDX(port_id, qid)] = (struct rxtx_stats){0};\n+\t\t\tbreak;\n+\n+\t\tcase MSG_TYPE_GET_MAC:\n+\t\t\t*msg = (struct eth_shared_mem_msg){\n+\t\t\t\t.type = MSG_TYPE_REPORT_MAC,\n+\t\t\t};\n+\t\t\trte_eth_macaddr_get(port_id, &msg->ethaddr);\n+\t\t\tif (write(client, msg, sizeof(*msg)) < (int)sizeof(*msg))\n+\t\t\t\tgoto out;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tprintf(\"Unknown message\\n\");\n+\t\t}\n+\t\tbytes_read = read(client, msg, sizeof(msg) + 1024);\n+\t}\n+out:\n+\tport_poll_mask &= ~(1UL << S_IDX(port_id, qid));\n+\twhile (used_poll_mask != port_poll_mask)\n+\t\tusleep(10);\n+\n+\treconfigure_queue(port_id, qid, default_mempool);\n+\n+\tfree(msg);\n+\trte_free(local_mp);\n+\n+\tprintf(\"Client disconnect\\n\");\n+}\n+\n+static int\n+accept_client(const int sock, uint64_t maxmem, uint16_t port_id, uint16_t qid)\n+{\n+\tint ret = 0;\n+\trte_iova_t *iovas = NULL;\n+\tconst int client = accept(sock, NULL, NULL);\n+\tif (client < 0) {\n+\t\tprintf(\"Error with accept\\n\");\n+\t\treturn errno;\n+\t}\n+\tprintf(\"Client connected\\n\");\n+\n+\tchar filename[32];\n+\tint flags = MFD_HUGETLB;\n+\tuint32_t pgsize = (1 << 21);\n+\tif (maxmem % (1 << 30) == 0) {\n+\t\tflags |= MFD_HUGE_1GB;\n+\t\tpgsize = (1 << 30);\n+\t}\n+\tsnprintf(filename, sizeof(filename), \"client_memory_%d\", client);\n+\n+\tconst int memfd = memfd_create(filename, flags);\n+\tif (memfd < 0) {\n+\t\tprintf(\"Error with memfd_create\\n\");\n+\t\treturn errno;\n+\t}\n+\tif (ftruncate(memfd, maxmem) < 0) {\n+\t\tprintf(\"Error with ftruncate\\n\");\n+\t\tclose(memfd);\n+\t\treturn errno;\n+\t}\n+\tvoid * const client_mem = mmap(NULL, maxmem, PROT_READ | PROT_WRITE,\n+\t\t\tMAP_SHARED, memfd, 0);\n+\tif (client_mem == MAP_FAILED) {\n+\t\tprintf(\"Error with mmap\\n\");\n+\t\tret = errno;\n+\t\tgoto out;\n+\t}\n+\n+\tconst int nb_pages = maxmem / pgsize;\n+\tprintf(\"Registering %d pages of memory with DPDK\\n\", nb_pages);\n+\tiovas = malloc(sizeof(*iovas) * nb_pages);\n+\tif (iovas == NULL) {\n+\t\tprintf(\"Error with malloc for iovas\\n\");\n+\t\tret = ENOMEM;\n+\t\tgoto out;\n+\t}\n+\t/* assume vfio, VA = IOVA */\n+\tiovas[0] = (uintptr_t)client_mem;\n+\tfor (int i = 1; i < nb_pages; i++)\n+\t\tiovas[i] = iovas[i - 1] + pgsize;\n+\n+\n+\tif (rte_extmem_register(client_mem, maxmem, iovas, nb_pages, pgsize) < 0) {\n+\t\tprintf(\"Error registering memory with DPDK, %s\\n\", strerror(rte_errno));\n+\t\tret = rte_errno;\n+\t\tgoto out;\n+\t}\n+\tprintf(\"Registered memory: %\" PRIu64 \" @ %p in heap %s\\n\", maxmem, client_mem, filename);\n+\n+\tstruct rte_eth_dev_info info;\n+\tif (rte_eth_dev_info_get(port_id, &info) < 0) {\n+\t\tprintf(\"Error getting ethdev info\\n\");\n+\t\tret = -1;\n+\t\tgoto out;\n+\t}\n+\tif (rte_dev_dma_map(info.device, client_mem, iovas[0], maxmem) < 0) {\n+\t\tprintf(\"Error mapping dma for device, %s\\n\", strerror(rte_errno));\n+\t\tret = rte_errno;\n+\t\tgoto out;\n+\t}\n+\n+\tif (send_fd(client, memfd, maxmem, iovas[0], pgsize) != 0) {\n+\t\tprintf(\"Error sending fd to client\\n\");\n+\t\tret = errno;\n+\t\tgoto out;\n+\t}\n+\tprintf(\"Sent FD to client for mapping\\n\");\n+\n+\thandle_connection(client, client_mem, maxmem, port_id, qid);\n+out:\n+\tif (iovas != NULL)\n+\t\trte_dev_dma_unmap(info.device, client_mem, iovas[0], maxmem);\n+\tprintf(\"Unregistering memory: %\" PRIu64 \" @ %p in heap %s\\n\", maxmem, client_mem, filename);\n+\tif (rte_extmem_unregister(client_mem, maxmem) < 0)\n+\t\tprintf(\"Error unregistering memory, %s\\n\", strerror(rte_errno));\n+\tclose(memfd);\n+\tclose(client);\n+\tif (client_mem != NULL)\n+\t\tmunmap(client_mem, maxmem);\n+\treturn ret;\n+}\n+\n+static void *\n+listen_fn(void *param)\n+{\n+\tstruct listen_socket_params *p = param;\n+\tint ret = 0;\n+\n+\trte_thread_register();\n+\n+\twhile (1) {\n+\t\tconst int ret = accept_client(p->sock, p->maxmem, p->port_id, p->qid);\n+\t\tif (ret != 0)\n+\t\t\tgoto out;\n+\t}\n+out:\n+\tfree(p);\n+\treturn (void *)(uintptr_t)ret;\n+}\n+\n+int\n+listen_unix_socket(const char *path, const uint64_t maxmem, uint16_t port_id, uint16_t qid)\n+{\n+\tif (sock_params[S_IDX(port_id, qid)].sock != 0) {\n+\t\tprintf(\"Error, port already in use\\n\");\n+\t\treturn EEXIST;\n+\t}\n+\n+\tif (port_id >= rte_eth_dev_count_avail()) {\n+\t\tprintf(\"Error, port %u does not exist\\n\", port_id);\n+\t\treturn EINVAL;\n+\t}\n+\n+\tprintf(\"Opening and listening on socket: %s\\n\", path);\n+\tchar *pathcp = strdup(path);\n+\tif (pathcp == NULL) {\n+\t\tprintf(\"Error with strdup()\\n\");\n+\t\tfree(pathcp);\n+\t\treturn ENOMEM;\n+\t}\n+\tchar *dirpath = dirname(pathcp);\n+\tmkdir(dirpath, 0700);\n+\tfree(pathcp);\n+\n+\tint sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);\n+\tif (sock < 0) {\n+\t\tprintf(\"Error creating socket\\n\");\n+\t\treturn errno;\n+\t}\n+\n+\tstruct sockaddr_un sun = {.sun_family = AF_UNIX};\n+\tstrlcpy(sun.sun_path, path, sizeof(sun.sun_path));\n+\tprintf(\"Attempting socket bind to path '%s'\\n\", path);\n+\tprintf(\"Associated parameters are: maxmem = %\"PRIu64\", port = %u, qid = %u\\n\",\n+\t\t\tmaxmem, port_id, qid);\n+\n+\tif (bind(sock, (void *) &sun, sizeof(sun)) < 0) {\n+\t\tprintf(\"Initial bind to socket '%s' failed.\\n\", path);\n+\n+\t\t/* check if current socket is active */\n+\t\tif (connect(sock, (void *)&sun, sizeof(sun)) == 0) {\n+\t\t\tclose(sock);\n+\t\t\treturn EADDRINUSE;\n+\t\t}\n+\n+\t\t/* socket is not active, delete and attempt rebind */\n+\t\tprintf(\"Attempting unlink and retrying bind\\n\");\n+\t\tunlink(sun.sun_path);\n+\t\tif (bind(sock, (void *) &sun, sizeof(sun)) < 0) {\n+\t\t\tprintf(\"Error binding socket: %s\\n\", strerror(errno));\n+\t\t\tclose(sock);\n+\t\t\treturn errno; /* if unlink failed, this will be -EADDRINUSE as above */\n+\t\t}\n+\t}\n+\n+\tif (listen(sock, 1) < 0) {\n+\t\tprintf(\"Error calling listen for socket: %s\\n\", strerror(errno));\n+\t\tunlink(sun.sun_path);\n+\t\tclose(sock);\n+\t\treturn errno;\n+\t}\n+\tprintf(\"Socket %s listening ok\\n\", path);\n+\n+\tstruct listen_socket_params *p = &sock_params[S_IDX(port_id, qid)];\n+\tpthread_t listen_thread;\n+\t*p = (struct listen_socket_params){strdup(path), sock, port_id, qid, maxmem};\n+\tpthread_create(&listen_thread, NULL, listen_fn, p);\n+\tpthread_detach(listen_thread);\n+\treturn 0;\n+}\n+\n+void\n+handle_forwarding(void)\n+{\n+\tconst typeof(port_poll_mask) to_poll = port_poll_mask;\n+\tif (used_poll_mask != to_poll) {\n+\t\tprintf(\"Poll mask is now %#llx\\n\", to_poll);\n+\t\tused_poll_mask = to_poll;\n+\t}\n+\tif (to_poll == 0) {\n+\t\tusleep(100);\n+\t\treturn;\n+\t}\n+\tfor (uint16_t i = 0; i < sizeof(to_poll) * CHAR_BIT; i++) {\n+\t\tstruct rte_mbuf *mbs[32];\n+\t\tvoid *offsets[32];\n+\t\tif (((1UL << i) & to_poll) == 0)\n+\t\t\tcontinue;\n+\n+\t\tuint16_t port_id = i / MAX_QUEUES_SUPPORTED;\n+\t\tuint16_t qid = i % MAX_QUEUES_SUPPORTED;\n+\t\tuint16_t nb_rx = rte_eth_rx_burst(port_id, qid, mbs, RTE_DIM(mbs));\n+\t\tif (nb_rx != 0) {\n+\t\t\tdp_stats[i].rx += nb_rx;\n+\t\t\tfor (uint pkt = 0; pkt < nb_rx; pkt++) {\n+\t\t\t\tmbs[pkt]->buf_addr = RTE_PTR_SUB(mbs[pkt]->buf_addr, base_addrs[i]);\n+\t\t\t\toffsets[pkt] = RTE_PTR_SUB(mbs[pkt], base_addrs[i]);\n+\t\t\t}\n+\t\t\tuint16_t nb_enq = rte_ring_enqueue_burst(rx_rings[i], offsets, nb_rx, NULL);\n+\t\t\tif (nb_enq != nb_rx) {\n+\t\t\t\tdp_stats[i].enq_drop += nb_rx - nb_enq;\n+\t\t\t\tfor (uint pkt = nb_enq; pkt < nb_rx; pkt++) {\n+\t\t\t\t\tmbs[pkt]->buf_addr = RTE_PTR_ADD(mbs[pkt]->buf_addr,\n+\t\t\t\t\t\t\tbase_addrs[i]);\n+\t\t\t\t\tmbs[pkt]->pool = mps[i];\n+\t\t\t\t}\n+\t\t\t\trte_mempool_put_bulk(mps[i], (void *)&mbs[nb_enq], nb_rx - nb_enq);\n+\t\t\t}\n+\t\t}\n+\n+\t\tuint16_t nb_deq = rte_ring_dequeue_burst(tx_rings[i], offsets,\n+\t\t\t\tRTE_DIM(offsets), NULL);\n+\t\tif (nb_deq != 0) {\n+\t\t\tdp_stats[i].deq += nb_deq;\n+\t\t\tfor (uint pkt = 0; pkt < nb_deq; pkt++) {\n+\t\t\t\tmbs[pkt] = RTE_PTR_ADD(offsets[pkt], base_addrs[i]);\n+\t\t\t\trte_prefetch0_write(mbs[pkt]);\n+\t\t\t}\n+\t\t\tfor (uint pkt = 0; pkt < nb_deq; pkt++) {\n+\t\t\t\tmbs[pkt]->pool = mps[i];\n+\t\t\t\tmbs[pkt]->buf_addr = RTE_PTR_ADD(mbs[pkt]->buf_addr, base_addrs[i]);\n+\t\t\t}\n+\t\t\tuint16_t nb_tx = rte_eth_tx_burst(port_id, qid, mbs, nb_deq);\n+\t\t\tif (nb_tx != nb_deq) {\n+\t\t\t\tdp_stats[i].tx_drop += (nb_deq - nb_tx);\n+\t\t\t\trte_pktmbuf_free_bulk(&mbs[nb_tx], nb_deq - nb_tx);\n+\t\t\t}\n+\t\t}\n+\t}\n+}\n+\n+unsigned int\n+lcore_id(void)\n+{\n+\treturn rte_lcore_id();\n+}\ndiff --git a/app/io-proxy/datapath.h b/app/io-proxy/datapath.h\nnew file mode 100644\nindex 0000000000..ec5b395164\n--- /dev/null\n+++ b/app/io-proxy/datapath.h\n@@ -0,0 +1,37 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Intel Corporation\n+ */\n+#ifndef DATAPATH_H_INC\n+#define DATAPATH_H_INC\n+\n+#include <stdint.h>\n+\n+#define MEMPOOL_OPS_NAME \"proxy_mp\"\n+#define MAX_PORTS_SUPPORTED 8\n+#define MAX_QUEUES_SUPPORTED 2\n+#define MAX_SOCKETS (MAX_PORTS_SUPPORTED * MAX_QUEUES_SUPPORTED)\n+\n+struct rxtx_stats {\n+\tuint64_t rx;\n+\tuint64_t enq_drop;\n+\tuint64_t deq;\n+\tuint64_t tx_drop;\n+};\n+\n+extern struct rxtx_stats dp_stats[MAX_SOCKETS];\n+\n+int check_mempool_ops(void);\n+\n+int datapath_init(const char *corelist);\n+\n+int listen_unix_socket(const char *path, uint64_t maxmem, uint16_t port, uint16_t qid);\n+\n+void handle_forwarding(void);\n+\n+unsigned int lcore_id(void);\n+\n+int get_next_socket(int start, const char **path, int *sock, uint64_t *maxmem,\n+\t\tuint16_t *port, uint16_t *queue, bool *connected);\n+\n+\n+#endif\ndiff --git a/app/io-proxy/datapath_mp.c b/app/io-proxy/datapath_mp.c\nnew file mode 100644\nindex 0000000000..bba21a5b14\n--- /dev/null\n+++ b/app/io-proxy/datapath_mp.c\n@@ -0,0 +1,78 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Intel Corporation\n+ */\n+#include <sys/types.h>\n+#include <rte_stack.h>\n+#include <rte_mempool.h>\n+#include <rte_mbuf.h>\n+#include \"datapath.h\"\n+\n+/* Mempool value \"pool_config\" contains pointer to base address for this mapping */\n+/* no alloc/free etc. functions for this pool, as we never create/destroy it, only use\n+ * enqueue and dequeue from it.\n+ */\n+\n+static int\n+proxy_mp_enqueue(struct rte_mempool *mp, void * const *obj_table,\n+\t      unsigned int n)\n+{\n+\tstruct rte_stack *s = mp->pool_data;\n+\tvoid *offset_table[n];\n+\tuintptr_t mempool_base = (uintptr_t)mp->pool_config;\n+\n+\tfor (uint i = 0; i < n; i++)\n+\t\toffset_table[i] = RTE_PTR_SUB(obj_table[i], mempool_base);\n+\n+\treturn rte_stack_push(s, offset_table, n) == 0 ? -ENOBUFS : 0;\n+}\n+\n+static int\n+proxy_mp_dequeue(struct rte_mempool *mp, void **obj_table,\n+\t      unsigned int n)\n+{\n+\tstruct rte_stack *s = mp->pool_data;\n+\tuintptr_t mempool_base = (uintptr_t)mp->pool_config;\n+\n+\tif (rte_stack_pop(s, obj_table, n) == 0)\n+\t\treturn -ENOBUFS;\n+\tfor (uint i = 0; i < n; i++) {\n+\t\tobj_table[i] = RTE_PTR_ADD(obj_table[i], mempool_base);\n+\t\tstruct rte_mbuf *mb = obj_table[i];\n+\t\tmb->buf_addr = RTE_PTR_ADD(mb, sizeof(struct rte_mbuf) + rte_pktmbuf_priv_size(mp));\n+\t\tmb->pool = mp;\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+proxy_mp_alloc(struct rte_mempool *mp __rte_unused)\n+{\n+\trte_panic(\"Should not be called\\n\");\n+}\n+\n+static unsigned int\n+proxy_mp_get_count(const struct rte_mempool *mp __rte_unused)\n+{\n+\trte_panic(\"Should not be called\\n\");\n+}\n+\n+\n+static struct rte_mempool_ops ops_proxy_mp = {\n+\t.name = MEMPOOL_OPS_NAME,\n+\t.alloc = proxy_mp_alloc,\n+\t.enqueue = proxy_mp_enqueue,\n+\t.dequeue = proxy_mp_dequeue,\n+\t.get_count = proxy_mp_get_count,\n+};\n+\n+RTE_MEMPOOL_REGISTER_OPS(ops_proxy_mp);\n+\n+int\n+check_mempool_ops(void)\n+{\n+\tfor (uint i = 0; i < rte_mempool_ops_table.num_ops; i++) {\n+\t\tif (strcmp(rte_mempool_ops_table.ops[i].name, MEMPOOL_OPS_NAME) == 0)\n+\t\t\treturn i;\n+\t}\n+\treturn -1;\n+}\ndiff --git a/app/io-proxy/main.c b/app/io-proxy/main.c\nnew file mode 100644\nindex 0000000000..82eef81fb0\n--- /dev/null\n+++ b/app/io-proxy/main.c\n@@ -0,0 +1,71 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Intel Corporation\n+ */\n+#include <stdio.h>\n+#include <errno.h>\n+#include <fcntl.h>\n+#include <stdlib.h>\n+#include <unistd.h>\n+\n+#include <rte_eal.h>\n+#include <rte_common.h>\n+#include <cmdline.h>\n+#include <cmdline_socket.h>\n+\n+#include \"datapath.h\"\n+#include \"commands.h\"\n+\n+volatile bool quit;\n+volatile bool running_startup_script;\n+static const char *startup_file = \"dpdk-io-proxy.cmds\";\n+\n+static void *\n+run_cmdline(void *arg __rte_unused)\n+{\n+\tstruct cmdline *cl;\n+\tint fd = open(startup_file, O_RDONLY);\n+\n+\tif (fd >= 0) {\n+\t\trunning_startup_script = true;\n+\t\tcl = cmdline_new(ctx, \"\\n# \", fd, STDOUT_FILENO);\n+\t\tif (cl == NULL) {\n+\t\t\tfprintf(stderr, \"Error processing %s\\n\", startup_file);\n+\t\t\tgoto end_startup;\n+\t\t}\n+\t\tcmdline_interact(cl);\n+\t\tcmdline_quit(cl);\n+end_startup:\n+\t\trunning_startup_script = false;\n+\t\tclose(fd);\n+\t}\n+\n+\tcl = cmdline_stdin_new(ctx, \"\\nProxy>> \");\n+\tif (cl == NULL)\n+\t\tgoto out;\n+\n+\tcmdline_interact(cl);\n+\tcmdline_stdin_exit(cl);\n+\n+out:\n+\tquit = true;\n+\treturn NULL;\n+}\n+\n+int\n+main(int argc, char *argv[])\n+{\n+\tpthread_t cmdline_th;\n+\n+\tif (argc != 2 || datapath_init(argv[1]) < 0) {\n+\t\tfprintf(stderr, \"Usage %s <corelist>\\n\", program_invocation_short_name);\n+\t\trte_exit(EXIT_FAILURE, \"Cannot init\\n\");\n+\t}\n+\n+\tif (pthread_create(&cmdline_th, NULL, run_cmdline, NULL) < 0)\n+\t\trte_exit(EXIT_FAILURE, \"Cannot spawn cmdline thread\\n\");\n+\tpthread_detach(cmdline_th);\n+\n+\twhile (!quit)\n+\t\thandle_forwarding();\n+\treturn 0;\n+}\ndiff --git a/app/io-proxy/meson.build b/app/io-proxy/meson.build\nnew file mode 100644\nindex 0000000000..f03783b68f\n--- /dev/null\n+++ b/app/io-proxy/meson.build\n@@ -0,0 +1,12 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(c) 2023 Intel Corporation\n+\n+cmd_h = custom_target('commands_hdr',\n+        output: 'commands.h',\n+        input: files('commands.list'),\n+        capture: true,\n+        command: [cmdline_gen_cmd, '@INPUT@']\n+)\n+sources += files('datapath.c', 'datapath_mp.c', 'main.c', 'command_fns.c')\n+sources += cmd_h\n+deps += ['cmdline', 'ethdev', 'stack', 'bus_shared_mem']\ndiff --git a/app/meson.build b/app/meson.build\nindex e4bf5c531c..27f69d883e 100644\n--- a/app/meson.build\n+++ b/app/meson.build\n@@ -18,6 +18,7 @@ apps = [\n         'dumpcap',\n         'pdump',\n         'proc-info',\n+        'io-proxy',\n         'test-acl',\n         'test-bbdev',\n         'test-cmdline',\n",
    "prefixes": [
        "RFC",
        "4/5"
    ]
}