get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 76969,
    "url": "http://patches.dpdk.org/api/patches/76969/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20200908201830.74206-37-cristian.dumitrescu@intel.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20200908201830.74206-37-cristian.dumitrescu@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20200908201830.74206-37-cristian.dumitrescu@intel.com",
    "date": "2020-09-08T20:18:25",
    "name": "[v3,36/41] examples/pipeline: add new example application",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "1085eac37bb978084f0f0e30530054e4eb07beaa",
    "submitter": {
        "id": 19,
        "url": "http://patches.dpdk.org/api/people/19/?format=api",
        "name": "Cristian Dumitrescu",
        "email": "cristian.dumitrescu@intel.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20200908201830.74206-37-cristian.dumitrescu@intel.com/mbox/",
    "series": [
        {
            "id": 12034,
            "url": "http://patches.dpdk.org/api/series/12034/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=12034",
            "date": "2020-09-08T20:17:52",
            "name": "Pipeline alignment with the P4 language",
            "version": 3,
            "mbox": "http://patches.dpdk.org/series/12034/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/76969/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/76969/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 dpdk.org (dpdk.org [92.243.14.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 00B47A04B1;\n\tTue,  8 Sep 2020 22:24:50 +0200 (CEST)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id BED301C2A3;\n\tTue,  8 Sep 2020 22:19:46 +0200 (CEST)",
            "from mga02.intel.com (mga02.intel.com [134.134.136.20])\n by dpdk.org (Postfix) with ESMTP id 60BF71C132\n for <dev@dpdk.org>; Tue,  8 Sep 2020 22:19:09 +0200 (CEST)",
            "from fmsmga006.fm.intel.com ([10.253.24.20])\n by orsmga101.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 08 Sep 2020 13:19:08 -0700",
            "from silpixa00400573.ir.intel.com (HELO\n silpixa00400573.ger.corp.intel.com) ([10.237.223.107])\n by fmsmga006.fm.intel.com with ESMTP; 08 Sep 2020 13:19:07 -0700"
        ],
        "IronPort-SDR": [
            "\n qkWJ+veQzkRy4AJ8SMaJmPgx/rKLglCSwsD5wAHaP93mGmk4y5vioEgvfxaY3FSDlZChcOMr4j\n 37ZCC6CiAPGg==",
            "\n 97fCiZAcpeW/3wvvH6yG2aoQYNtcfWdn2DGrP8oG56auRNKd9d1p2/wx3H9XKUUi/RMyfNoKT2\n ipGw7HZiJomg=="
        ],
        "X-IronPort-AV": [
            "E=McAfee;i=\"6000,8403,9738\"; a=\"145939454\"",
            "E=Sophos;i=\"5.76,407,1592895600\"; d=\"scan'208\";a=\"145939454\"",
            "E=Sophos;i=\"5.76,406,1592895600\"; d=\"scan'208\";a=\"504493554\""
        ],
        "X-Amp-Result": "SKIPPED(no attachment in message)",
        "X-Amp-File-Uploaded": "False",
        "X-ExtLoop1": "1",
        "From": "Cristian Dumitrescu <cristian.dumitrescu@intel.com>",
        "To": "dev@dpdk.org",
        "Date": "Tue,  8 Sep 2020 21:18:25 +0100",
        "Message-Id": "<20200908201830.74206-37-cristian.dumitrescu@intel.com>",
        "X-Mailer": "git-send-email 2.17.1",
        "In-Reply-To": "<20200908201830.74206-1-cristian.dumitrescu@intel.com>",
        "References": "<20200907214032.95052-2-cristian.dumitrescu@intel.com>\n <20200908201830.74206-1-cristian.dumitrescu@intel.com>",
        "Subject": "[dpdk-dev] [PATCH v3 36/41] examples/pipeline: add new example\n\tapplication",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "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",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "Add new example application to showcase the API of the newly\nintroduced SWX pipeline type.\n\nSigned-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>\n---\n examples/meson.build          |   1 +\n examples/pipeline/Makefile    |  51 ++++\n examples/pipeline/main.c      |  52 ++++\n examples/pipeline/meson.build |  16 +\n examples/pipeline/obj.c       | 470 +++++++++++++++++++++++++++++\n examples/pipeline/obj.h       | 131 ++++++++\n examples/pipeline/thread.c    | 549 ++++++++++++++++++++++++++++++++++\n examples/pipeline/thread.h    |  28 ++\n 8 files changed, 1298 insertions(+)\n create mode 100644 examples/pipeline/Makefile\n create mode 100644 examples/pipeline/main.c\n create mode 100644 examples/pipeline/meson.build\n create mode 100644 examples/pipeline/obj.c\n create mode 100644 examples/pipeline/obj.h\n create mode 100644 examples/pipeline/thread.c\n create mode 100644 examples/pipeline/thread.h",
    "diff": "diff --git a/examples/meson.build b/examples/meson.build\nindex eb13e8210..245d98575 100644\n--- a/examples/meson.build\n+++ b/examples/meson.build\n@@ -33,6 +33,7 @@ all_examples = [\n \t'ntb', 'packet_ordering',\n \t'performance-thread/l3fwd-thread',\n \t'performance-thread/pthread_shim',\n+\t'pipeline',\n \t'ptpclient',\n \t'qos_meter', 'qos_sched',\n \t'rxtx_callbacks',\ndiff --git a/examples/pipeline/Makefile b/examples/pipeline/Makefile\nnew file mode 100644\nindex 000000000..8d01fbfed\n--- /dev/null\n+++ b/examples/pipeline/Makefile\n@@ -0,0 +1,51 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(c) 2010-2020 Intel Corporation\n+\n+# binary name\n+APP = pipeline\n+\n+# all source are stored in SRCS-y\n+SRCS-y += main.c\n+SRCS-y += obj.c\n+SRCS-y += thread.c\n+\n+# Build using pkg-config variables if possible\n+ifneq ($(shell pkg-config --exists libdpdk && echo 0),0)\n+$(error \"no installation of DPDK found\")\n+endif\n+\n+all: shared\n+.PHONY: shared static\n+shared: build/$(APP)-shared\n+\tln -sf $(APP)-shared build/$(APP)\n+static: build/$(APP)-static\n+\tln -sf $(APP)-static build/$(APP)\n+\n+PKGCONF ?= pkg-config\n+\n+PC_FILE := $(shell $(PKGCONF) --path libdpdk 2>/dev/null)\n+CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk)\n+LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk)\n+LDFLAGS_STATIC = -Wl,-Bstatic $(shell $(PKGCONF) --static --libs libdpdk)\n+\n+CFLAGS += -I.\n+\n+OBJS := $(patsubst %.c,build/%.o,$(SRCS-y))\n+\n+build/%.o: %.c Makefile $(PC_FILE) | build\n+\t$(CC) $(CFLAGS) -c $< -o $@\n+\n+build/$(APP)-shared: $(OBJS)\n+\t$(CC) $(OBJS) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)\n+\n+build/$(APP)-static: $(OBJS)\n+\t$(CC) $(OBJS) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC)\n+\n+build:\n+\t@mkdir -p $@\n+\n+.PHONY: clean\n+clean:\n+\trm -f build/$(APP)* build/*.o\n+\ttest -d build && rmdir -p build || true\n+\ndiff --git a/examples/pipeline/main.c b/examples/pipeline/main.c\nnew file mode 100644\nindex 000000000..353e62c10\n--- /dev/null\n+++ b/examples/pipeline/main.c\n@@ -0,0 +1,52 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2010-2020 Intel Corporation\n+ */\n+\n+#include <stdio.h>\n+#include <string.h>\n+#include <fcntl.h>\n+#include <unistd.h>\n+#include <getopt.h>\n+\n+#include <rte_launch.h>\n+#include <rte_eal.h>\n+\n+#include \"obj.h\"\n+#include \"thread.h\"\n+\n+int\n+main(int argc, char **argv)\n+{\n+\tstruct obj *obj;\n+\tint status;\n+\n+\t/* EAL */\n+\tstatus = rte_eal_init(argc, argv);\n+\tif (status < 0) {\n+\t\tprintf(\"Error: EAL initialization failed (%d)\\n\", status);\n+\t\treturn status;\n+\t};\n+\n+\t/* Obj */\n+\tobj = obj_init();\n+\tif (!obj) {\n+\t\tprintf(\"Error: Obj initialization failed (%d)\\n\", status);\n+\t\treturn status;\n+\t}\n+\n+\t/* Thread */\n+\tstatus = thread_init();\n+\tif (status) {\n+\t\tprintf(\"Error: Thread initialization failed (%d)\\n\", status);\n+\t\treturn status;\n+\t}\n+\n+\trte_eal_mp_remote_launch(\n+\t\tthread_main,\n+\t\tNULL,\n+\t\tSKIP_MASTER);\n+\n+\t/* Dispatch loop */\n+\tfor ( ; ; );\n+}\n+\ndiff --git a/examples/pipeline/meson.build b/examples/pipeline/meson.build\nnew file mode 100644\nindex 000000000..ade485f97\n--- /dev/null\n+++ b/examples/pipeline/meson.build\n@@ -0,0 +1,16 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(c) 2017-2020 Intel Corporation\n+\n+# meson file, for building this example as part of a main DPDK build.\n+#\n+# To build this example as a standalone application with an already-installed\n+# DPDK instance, use 'make'\n+\n+build = cc.has_header('sys/epoll.h')\n+deps += ['pipeline', 'bus_pci']\n+allow_experimental_apis = true\n+sources = files(\n+\t'main.c',\n+\t'obj.c',\n+\t'thread.c',\n+)\ndiff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c\nnew file mode 100644\nindex 000000000..688870f97\n--- /dev/null\n+++ b/examples/pipeline/obj.c\n@@ -0,0 +1,470 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2010-2020 Intel Corporation\n+ */\n+\n+#include <stdlib.h>\n+#include <string.h>\n+\n+#include <rte_mempool.h>\n+#include <rte_mbuf.h>\n+#include <rte_ethdev.h>\n+#include <rte_swx_port_ethdev.h>\n+#include <rte_swx_port_source_sink.h>\n+#include <rte_swx_table_em.h>\n+#include <rte_swx_pipeline.h>\n+#include <rte_swx_ctl.h>\n+\n+#include \"obj.h\"\n+\n+/*\n+ * mempool\n+ */\n+TAILQ_HEAD(mempool_list, mempool);\n+\n+/*\n+ * link\n+ */\n+TAILQ_HEAD(link_list, link);\n+\n+/*\n+ * pipeline\n+ */\n+TAILQ_HEAD(pipeline_list, pipeline);\n+\n+/*\n+ * obj\n+ */\n+struct obj {\n+\tstruct mempool_list mempool_list;\n+\tstruct link_list link_list;\n+\tstruct pipeline_list pipeline_list;\n+};\n+\n+/*\n+ * mempool\n+ */\n+#define BUFFER_SIZE_MIN (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)\n+\n+struct mempool *\n+mempool_create(struct obj *obj, const char *name, struct mempool_params *params)\n+{\n+\tstruct mempool *mempool;\n+\tstruct rte_mempool *m;\n+\n+\t/* Check input params */\n+\tif ((name == NULL) ||\n+\t\tmempool_find(obj, name) ||\n+\t\t(params == NULL) ||\n+\t\t(params->buffer_size < BUFFER_SIZE_MIN) ||\n+\t\t(params->pool_size == 0))\n+\t\treturn NULL;\n+\n+\t/* Resource create */\n+\tm = rte_pktmbuf_pool_create(\n+\t\tname,\n+\t\tparams->pool_size,\n+\t\tparams->cache_size,\n+\t\t0,\n+\t\tparams->buffer_size - sizeof(struct rte_mbuf),\n+\t\tparams->cpu_id);\n+\n+\tif (m == NULL)\n+\t\treturn NULL;\n+\n+\t/* Node allocation */\n+\tmempool = calloc(1, sizeof(struct mempool));\n+\tif (mempool == NULL) {\n+\t\trte_mempool_free(m);\n+\t\treturn NULL;\n+\t}\n+\n+\t/* Node fill in */\n+\tstrlcpy(mempool->name, name, sizeof(mempool->name));\n+\tmempool->m = m;\n+\tmempool->buffer_size = params->buffer_size;\n+\n+\t/* Node add to list */\n+\tTAILQ_INSERT_TAIL(&obj->mempool_list, mempool, node);\n+\n+\treturn mempool;\n+}\n+\n+struct mempool *\n+mempool_find(struct obj *obj, const char *name)\n+{\n+\tstruct mempool *mempool;\n+\n+\tif (!obj || !name)\n+\t\treturn NULL;\n+\n+\tTAILQ_FOREACH(mempool, &obj->mempool_list, node)\n+\t\tif (strcmp(mempool->name, name) == 0)\n+\t\t\treturn mempool;\n+\n+\treturn NULL;\n+}\n+\n+/*\n+ * link\n+ */\n+static struct rte_eth_conf port_conf_default = {\n+\t.link_speeds = 0,\n+\t.rxmode = {\n+\t\t.mq_mode = ETH_MQ_RX_NONE,\n+\t\t.max_rx_pkt_len = 9000, /* Jumbo frame max packet len */\n+\t\t.split_hdr_size = 0, /* Header split buffer size */\n+\t},\n+\t.rx_adv_conf = {\n+\t\t.rss_conf = {\n+\t\t\t.rss_key = NULL,\n+\t\t\t.rss_key_len = 40,\n+\t\t\t.rss_hf = 0,\n+\t\t},\n+\t},\n+\t.txmode = {\n+\t\t.mq_mode = ETH_MQ_TX_NONE,\n+\t},\n+\t.lpbk_mode = 0,\n+};\n+\n+#define RETA_CONF_SIZE     (ETH_RSS_RETA_SIZE_512 / RTE_RETA_GROUP_SIZE)\n+\n+static int\n+rss_setup(uint16_t port_id,\n+\tuint16_t reta_size,\n+\tstruct link_params_rss *rss)\n+{\n+\tstruct rte_eth_rss_reta_entry64 reta_conf[RETA_CONF_SIZE];\n+\tuint32_t i;\n+\tint status;\n+\n+\t/* RETA setting */\n+\tmemset(reta_conf, 0, sizeof(reta_conf));\n+\n+\tfor (i = 0; i < reta_size; i++)\n+\t\treta_conf[i / RTE_RETA_GROUP_SIZE].mask = UINT64_MAX;\n+\n+\tfor (i = 0; i < reta_size; i++) {\n+\t\tuint32_t reta_id = i / RTE_RETA_GROUP_SIZE;\n+\t\tuint32_t reta_pos = i % RTE_RETA_GROUP_SIZE;\n+\t\tuint32_t rss_qs_pos = i % rss->n_queues;\n+\n+\t\treta_conf[reta_id].reta[reta_pos] =\n+\t\t\t(uint16_t) rss->queue_id[rss_qs_pos];\n+\t}\n+\n+\t/* RETA update */\n+\tstatus = rte_eth_dev_rss_reta_update(port_id,\n+\t\treta_conf,\n+\t\treta_size);\n+\n+\treturn status;\n+}\n+\n+struct link *\n+link_create(struct obj *obj, const char *name, struct link_params *params)\n+{\n+\tstruct rte_eth_dev_info port_info;\n+\tstruct rte_eth_conf port_conf;\n+\tstruct link *link;\n+\tstruct link_params_rss *rss;\n+\tstruct mempool *mempool;\n+\tuint32_t cpu_id, i;\n+\tint status;\n+\tuint16_t port_id;\n+\n+\t/* Check input params */\n+\tif ((name == NULL) ||\n+\t\tlink_find(obj, name) ||\n+\t\t(params == NULL) ||\n+\t\t(params->rx.n_queues == 0) ||\n+\t\t(params->rx.queue_size == 0) ||\n+\t\t(params->tx.n_queues == 0) ||\n+\t\t(params->tx.queue_size == 0))\n+\t\treturn NULL;\n+\n+\tport_id = params->port_id;\n+\tif (params->dev_name) {\n+\t\tstatus = rte_eth_dev_get_port_by_name(params->dev_name,\n+\t\t\t&port_id);\n+\n+\t\tif (status)\n+\t\t\treturn NULL;\n+\t} else\n+\t\tif (!rte_eth_dev_is_valid_port(port_id))\n+\t\t\treturn NULL;\n+\n+\tif (rte_eth_dev_info_get(port_id, &port_info) != 0)\n+\t\treturn NULL;\n+\n+\tmempool = mempool_find(obj, params->rx.mempool_name);\n+\tif (mempool == NULL)\n+\t\treturn NULL;\n+\n+\trss = params->rx.rss;\n+\tif (rss) {\n+\t\tif ((port_info.reta_size == 0) ||\n+\t\t\t(port_info.reta_size > ETH_RSS_RETA_SIZE_512))\n+\t\t\treturn NULL;\n+\n+\t\tif ((rss->n_queues == 0) ||\n+\t\t\t(rss->n_queues >= LINK_RXQ_RSS_MAX))\n+\t\t\treturn NULL;\n+\n+\t\tfor (i = 0; i < rss->n_queues; i++)\n+\t\t\tif (rss->queue_id[i] >= port_info.max_rx_queues)\n+\t\t\t\treturn NULL;\n+\t}\n+\n+\t/**\n+\t * Resource create\n+\t */\n+\t/* Port */\n+\tmemcpy(&port_conf, &port_conf_default, sizeof(port_conf));\n+\tif (rss) {\n+\t\tport_conf.rxmode.mq_mode = ETH_MQ_RX_RSS;\n+\t\tport_conf.rx_adv_conf.rss_conf.rss_hf =\n+\t\t\t(ETH_RSS_IP | ETH_RSS_TCP | ETH_RSS_UDP) &\n+\t\t\tport_info.flow_type_rss_offloads;\n+\t}\n+\n+\tcpu_id = (uint32_t) rte_eth_dev_socket_id(port_id);\n+\tif (cpu_id == (uint32_t) SOCKET_ID_ANY)\n+\t\tcpu_id = 0;\n+\n+\tstatus = rte_eth_dev_configure(\n+\t\tport_id,\n+\t\tparams->rx.n_queues,\n+\t\tparams->tx.n_queues,\n+\t\t&port_conf);\n+\n+\tif (status < 0)\n+\t\treturn NULL;\n+\n+\tif (params->promiscuous) {\n+\t\tstatus = rte_eth_promiscuous_enable(port_id);\n+\t\tif (status != 0)\n+\t\t\treturn NULL;\n+\t}\n+\n+\t/* Port RX */\n+\tfor (i = 0; i < params->rx.n_queues; i++) {\n+\t\tstatus = rte_eth_rx_queue_setup(\n+\t\t\tport_id,\n+\t\t\ti,\n+\t\t\tparams->rx.queue_size,\n+\t\t\tcpu_id,\n+\t\t\tNULL,\n+\t\t\tmempool->m);\n+\n+\t\tif (status < 0)\n+\t\t\treturn NULL;\n+\t}\n+\n+\t/* Port TX */\n+\tfor (i = 0; i < params->tx.n_queues; i++) {\n+\t\tstatus = rte_eth_tx_queue_setup(\n+\t\t\tport_id,\n+\t\t\ti,\n+\t\t\tparams->tx.queue_size,\n+\t\t\tcpu_id,\n+\t\t\tNULL);\n+\n+\t\tif (status < 0)\n+\t\t\treturn NULL;\n+\t}\n+\n+\t/* Port start */\n+\tstatus = rte_eth_dev_start(port_id);\n+\tif (status < 0)\n+\t\treturn NULL;\n+\n+\tif (rss) {\n+\t\tstatus = rss_setup(port_id, port_info.reta_size, rss);\n+\n+\t\tif (status) {\n+\t\t\trte_eth_dev_stop(port_id);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\n+\t/* Port link up */\n+\tstatus = rte_eth_dev_set_link_up(port_id);\n+\tif ((status < 0) && (status != -ENOTSUP)) {\n+\t\trte_eth_dev_stop(port_id);\n+\t\treturn NULL;\n+\t}\n+\n+\t/* Node allocation */\n+\tlink = calloc(1, sizeof(struct link));\n+\tif (link == NULL) {\n+\t\trte_eth_dev_stop(port_id);\n+\t\treturn NULL;\n+\t}\n+\n+\t/* Node fill in */\n+\tstrlcpy(link->name, name, sizeof(link->name));\n+\tlink->port_id = port_id;\n+\trte_eth_dev_get_name_by_port(port_id, link->dev_name);\n+\tlink->n_rxq = params->rx.n_queues;\n+\tlink->n_txq = params->tx.n_queues;\n+\n+\t/* Node add to list */\n+\tTAILQ_INSERT_TAIL(&obj->link_list, link, node);\n+\n+\treturn link;\n+}\n+\n+int\n+link_is_up(struct obj *obj, const char *name)\n+{\n+\tstruct rte_eth_link link_params;\n+\tstruct link *link;\n+\n+\t/* Check input params */\n+\tif (!obj || !name)\n+\t\treturn 0;\n+\n+\tlink = link_find(obj, name);\n+\tif (link == NULL)\n+\t\treturn 0;\n+\n+\t/* Resource */\n+\tif (rte_eth_link_get(link->port_id, &link_params) < 0)\n+\t\treturn 0;\n+\n+\treturn (link_params.link_status == ETH_LINK_DOWN) ? 0 : 1;\n+}\n+\n+struct link *\n+link_find(struct obj *obj, const char *name)\n+{\n+\tstruct link *link;\n+\n+\tif (!obj || !name)\n+\t\treturn NULL;\n+\n+\tTAILQ_FOREACH(link, &obj->link_list, node)\n+\t\tif (strcmp(link->name, name) == 0)\n+\t\t\treturn link;\n+\n+\treturn NULL;\n+}\n+\n+struct link *\n+link_next(struct obj *obj, struct link *link)\n+{\n+\treturn (link == NULL) ?\n+\t\tTAILQ_FIRST(&obj->link_list) : TAILQ_NEXT(link, node);\n+}\n+\n+/*\n+ * pipeline\n+ */\n+#ifndef PIPELINE_MSGQ_SIZE\n+#define PIPELINE_MSGQ_SIZE                                 64\n+#endif\n+\n+struct pipeline *\n+pipeline_create(struct obj *obj, const char *name, int numa_node)\n+{\n+\tstruct pipeline *pipeline;\n+\tstruct rte_swx_pipeline *p = NULL;\n+\tint status;\n+\n+\t/* Check input params */\n+\tif ((name == NULL) ||\n+\t\tpipeline_find(obj, name))\n+\t\treturn NULL;\n+\n+\t/* Resource create */\n+\tstatus = rte_swx_pipeline_config(&p, numa_node);\n+\tif (status)\n+\t\tgoto error;\n+\n+\tstatus = rte_swx_pipeline_port_in_type_register(p,\n+\t\t\"ethdev\",\n+\t\t&rte_swx_port_ethdev_reader_ops);\n+\tif (status)\n+\t\tgoto error;\n+\n+\tstatus = rte_swx_pipeline_port_out_type_register(p,\n+\t\t\"ethdev\",\n+\t\t&rte_swx_port_ethdev_writer_ops);\n+\tif (status)\n+\t\tgoto error;\n+\n+#ifdef RTE_PORT_PCAP\n+\tstatus = rte_swx_pipeline_port_in_type_register(p,\n+\t\t\"source\",\n+\t\t&rte_swx_port_source_ops);\n+\tif (status)\n+\t\tgoto error;\n+#endif\n+\n+\tstatus = rte_swx_pipeline_port_out_type_register(p,\n+\t\t\"sink\",\n+\t\t&rte_swx_port_sink_ops);\n+\tif (status)\n+\t\tgoto error;\n+\n+\tstatus = rte_swx_pipeline_table_type_register(p,\n+\t\t\"exact\",\n+\t\tRTE_SWX_TABLE_MATCH_EXACT,\n+\t\t&rte_swx_table_exact_match_ops);\n+\tif (status)\n+\t\tgoto error;\n+\n+\t/* Node allocation */\n+\tpipeline = calloc(1, sizeof(struct pipeline));\n+\tif (pipeline == NULL)\n+\t\tgoto error;\n+\n+\t/* Node fill in */\n+\tstrlcpy(pipeline->name, name, sizeof(pipeline->name));\n+\tpipeline->p = p;\n+\tpipeline->timer_period_ms = 10;\n+\n+\t/* Node add to list */\n+\tTAILQ_INSERT_TAIL(&obj->pipeline_list, pipeline, node);\n+\n+\treturn pipeline;\n+\n+error:\n+\trte_swx_pipeline_free(p);\n+\treturn NULL;\n+}\n+\n+struct pipeline *\n+pipeline_find(struct obj *obj, const char *name)\n+{\n+\tstruct pipeline *pipeline;\n+\n+\tif (!obj || !name)\n+\t\treturn NULL;\n+\n+\tTAILQ_FOREACH(pipeline, &obj->pipeline_list, node)\n+\t\tif (strcmp(name, pipeline->name) == 0)\n+\t\t\treturn pipeline;\n+\n+\treturn NULL;\n+}\n+\n+/*\n+ * obj\n+ */\n+struct obj *\n+obj_init(void)\n+{\n+\tstruct obj *obj;\n+\n+\tobj = calloc(1, sizeof(struct obj));\n+\tif (!obj)\n+\t\treturn NULL;\n+\n+\tTAILQ_INIT(&obj->mempool_list);\n+\tTAILQ_INIT(&obj->link_list);\n+\tTAILQ_INIT(&obj->pipeline_list);\n+\n+\treturn obj;\n+}\ndiff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h\nnew file mode 100644\nindex 000000000..2f48b790f\n--- /dev/null\n+++ b/examples/pipeline/obj.h\n@@ -0,0 +1,131 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2010-2020 Intel Corporation\n+ */\n+\n+#ifndef _INCLUDE_OBJ_H_\n+#define _INCLUDE_OBJ_H_\n+\n+#include <stdint.h>\n+#include <sys/queue.h>\n+\n+#include <rte_mempool.h>\n+#include <rte_swx_pipeline.h>\n+#include <rte_swx_ctl.h>\n+\n+#ifndef NAME_SIZE\n+#define NAME_SIZE 64\n+#endif\n+\n+/*\n+ * obj\n+ */\n+struct obj;\n+\n+struct obj *\n+obj_init(void);\n+\n+/*\n+ * mempool\n+ */\n+struct mempool_params {\n+\tuint32_t buffer_size;\n+\tuint32_t pool_size;\n+\tuint32_t cache_size;\n+\tuint32_t cpu_id;\n+};\n+\n+struct mempool {\n+\tTAILQ_ENTRY(mempool) node;\n+\tchar name[NAME_SIZE];\n+\tstruct rte_mempool *m;\n+\tuint32_t buffer_size;\n+};\n+\n+struct mempool *\n+mempool_create(struct obj *obj,\n+\t       const char *name,\n+\t       struct mempool_params *params);\n+\n+struct mempool *\n+mempool_find(struct obj *obj,\n+\t     const char *name);\n+\n+/*\n+ * link\n+ */\n+#ifndef LINK_RXQ_RSS_MAX\n+#define LINK_RXQ_RSS_MAX                                   16\n+#endif\n+\n+struct link_params_rss {\n+\tuint32_t queue_id[LINK_RXQ_RSS_MAX];\n+\tuint32_t n_queues;\n+};\n+\n+struct link_params {\n+\tconst char *dev_name;\n+\tuint16_t port_id; /**< Valid only when *dev_name* is NULL. */\n+\n+\tstruct {\n+\t\tuint32_t n_queues;\n+\t\tuint32_t queue_size;\n+\t\tconst char *mempool_name;\n+\t\tstruct link_params_rss *rss;\n+\t} rx;\n+\n+\tstruct {\n+\t\tuint32_t n_queues;\n+\t\tuint32_t queue_size;\n+\t} tx;\n+\n+\tint promiscuous;\n+};\n+\n+struct link {\n+\tTAILQ_ENTRY(link) node;\n+\tchar name[NAME_SIZE];\n+\tchar dev_name[NAME_SIZE];\n+\tuint16_t port_id;\n+\tuint32_t n_rxq;\n+\tuint32_t n_txq;\n+};\n+\n+struct link *\n+link_create(struct obj *obj,\n+\t    const char *name,\n+\t    struct link_params *params);\n+\n+int\n+link_is_up(struct obj *obj, const char *name);\n+\n+struct link *\n+link_find(struct obj *obj, const char *name);\n+\n+struct link *\n+link_next(struct obj *obj, struct link *link);\n+\n+/*\n+ * pipeline\n+ */\n+struct pipeline {\n+\tTAILQ_ENTRY(pipeline) node;\n+\tchar name[NAME_SIZE];\n+\n+\tstruct rte_swx_pipeline *p;\n+\tstruct rte_swx_ctl_pipeline *ctl;\n+\n+\tuint32_t timer_period_ms;\n+\tint enabled;\n+\tuint32_t thread_id;\n+\tuint32_t cpu_id;\n+};\n+\n+struct pipeline *\n+pipeline_create(struct obj *obj,\n+\t\tconst char *name,\n+\t\tint numa_node);\n+\n+struct pipeline *\n+pipeline_find(struct obj *obj, const char *name);\n+\n+#endif /* _INCLUDE_OBJ_H_ */\ndiff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c\nnew file mode 100644\nindex 000000000..0d1474c55\n--- /dev/null\n+++ b/examples/pipeline/thread.c\n@@ -0,0 +1,549 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2010-2020 Intel Corporation\n+ */\n+\n+#include <stdlib.h>\n+\n+#include <rte_common.h>\n+#include <rte_cycles.h>\n+#include <rte_lcore.h>\n+#include <rte_ring.h>\n+\n+#include <rte_table_acl.h>\n+#include <rte_table_array.h>\n+#include <rte_table_hash.h>\n+#include <rte_table_lpm.h>\n+#include <rte_table_lpm_ipv6.h>\n+\n+#include \"obj.h\"\n+#include \"thread.h\"\n+\n+#ifndef THREAD_PIPELINES_MAX\n+#define THREAD_PIPELINES_MAX                               256\n+#endif\n+\n+#ifndef THREAD_MSGQ_SIZE\n+#define THREAD_MSGQ_SIZE                                   64\n+#endif\n+\n+#ifndef THREAD_TIMER_PERIOD_MS\n+#define THREAD_TIMER_PERIOD_MS                             100\n+#endif\n+\n+/**\n+ * Master thead: data plane thread context\n+ */\n+struct thread {\n+\tstruct rte_ring *msgq_req;\n+\tstruct rte_ring *msgq_rsp;\n+\n+\tuint32_t enabled;\n+};\n+\n+static struct thread thread[RTE_MAX_LCORE];\n+\n+/**\n+ * Data plane threads: context\n+ */\n+struct pipeline_data {\n+\tstruct rte_swx_pipeline *p;\n+\tuint64_t timer_period; /* Measured in CPU cycles. */\n+\tuint64_t time_next;\n+};\n+\n+struct thread_data {\n+\tstruct rte_swx_pipeline *p[THREAD_PIPELINES_MAX];\n+\tuint32_t n_pipelines;\n+\n+\tstruct pipeline_data pipeline_data[THREAD_PIPELINES_MAX];\n+\tstruct rte_ring *msgq_req;\n+\tstruct rte_ring *msgq_rsp;\n+\tuint64_t timer_period; /* Measured in CPU cycles. */\n+\tuint64_t time_next;\n+\tuint64_t time_next_min;\n+} __rte_cache_aligned;\n+\n+static struct thread_data thread_data[RTE_MAX_LCORE];\n+\n+/**\n+ * Master thread: data plane thread init\n+ */\n+static void\n+thread_free(void)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < RTE_MAX_LCORE; i++) {\n+\t\tstruct thread *t = &thread[i];\n+\n+\t\tif (!rte_lcore_is_enabled(i))\n+\t\t\tcontinue;\n+\n+\t\t/* MSGQs */\n+\t\tif (t->msgq_req)\n+\t\t\trte_ring_free(t->msgq_req);\n+\n+\t\tif (t->msgq_rsp)\n+\t\t\trte_ring_free(t->msgq_rsp);\n+\t}\n+}\n+\n+int\n+thread_init(void)\n+{\n+\tuint32_t i;\n+\n+\tRTE_LCORE_FOREACH_SLAVE(i) {\n+\t\tchar name[NAME_MAX];\n+\t\tstruct rte_ring *msgq_req, *msgq_rsp;\n+\t\tstruct thread *t = &thread[i];\n+\t\tstruct thread_data *t_data = &thread_data[i];\n+\t\tuint32_t cpu_id = rte_lcore_to_socket_id(i);\n+\n+\t\t/* MSGQs */\n+\t\tsnprintf(name, sizeof(name), \"THREAD-%04x-MSGQ-REQ\", i);\n+\n+\t\tmsgq_req = rte_ring_create(name,\n+\t\t\tTHREAD_MSGQ_SIZE,\n+\t\t\tcpu_id,\n+\t\t\tRING_F_SP_ENQ | RING_F_SC_DEQ);\n+\n+\t\tif (msgq_req == NULL) {\n+\t\t\tthread_free();\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tsnprintf(name, sizeof(name), \"THREAD-%04x-MSGQ-RSP\", i);\n+\n+\t\tmsgq_rsp = rte_ring_create(name,\n+\t\t\tTHREAD_MSGQ_SIZE,\n+\t\t\tcpu_id,\n+\t\t\tRING_F_SP_ENQ | RING_F_SC_DEQ);\n+\n+\t\tif (msgq_rsp == NULL) {\n+\t\t\tthread_free();\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\t/* Master thread records */\n+\t\tt->msgq_req = msgq_req;\n+\t\tt->msgq_rsp = msgq_rsp;\n+\t\tt->enabled = 1;\n+\n+\t\t/* Data plane thread records */\n+\t\tt_data->n_pipelines = 0;\n+\t\tt_data->msgq_req = msgq_req;\n+\t\tt_data->msgq_rsp = msgq_rsp;\n+\t\tt_data->timer_period =\n+\t\t\t(rte_get_tsc_hz() * THREAD_TIMER_PERIOD_MS) / 1000;\n+\t\tt_data->time_next = rte_get_tsc_cycles() + t_data->timer_period;\n+\t\tt_data->time_next_min = t_data->time_next;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static inline int\n+thread_is_running(uint32_t thread_id)\n+{\n+\tenum rte_lcore_state_t thread_state;\n+\n+\tthread_state = rte_eal_get_lcore_state(thread_id);\n+\treturn (thread_state == RUNNING) ? 1 : 0;\n+}\n+\n+/**\n+ * Master thread & data plane threads: message passing\n+ */\n+enum thread_req_type {\n+\tTHREAD_REQ_PIPELINE_ENABLE = 0,\n+\tTHREAD_REQ_PIPELINE_DISABLE,\n+\tTHREAD_REQ_MAX\n+};\n+\n+struct thread_msg_req {\n+\tenum thread_req_type type;\n+\n+\tunion {\n+\t\tstruct {\n+\t\t\tstruct rte_swx_pipeline *p;\n+\t\t\tuint32_t timer_period_ms;\n+\t\t} pipeline_enable;\n+\n+\t\tstruct {\n+\t\t\tstruct rte_swx_pipeline *p;\n+\t\t} pipeline_disable;\n+\t};\n+};\n+\n+struct thread_msg_rsp {\n+\tint status;\n+};\n+\n+/**\n+ * Master thread\n+ */\n+static struct thread_msg_req *\n+thread_msg_alloc(void)\n+{\n+\tsize_t size = RTE_MAX(sizeof(struct thread_msg_req),\n+\t\tsizeof(struct thread_msg_rsp));\n+\n+\treturn calloc(1, size);\n+}\n+\n+static void\n+thread_msg_free(struct thread_msg_rsp *rsp)\n+{\n+\tfree(rsp);\n+}\n+\n+static struct thread_msg_rsp *\n+thread_msg_send_recv(uint32_t thread_id,\n+\tstruct thread_msg_req *req)\n+{\n+\tstruct thread *t = &thread[thread_id];\n+\tstruct rte_ring *msgq_req = t->msgq_req;\n+\tstruct rte_ring *msgq_rsp = t->msgq_rsp;\n+\tstruct thread_msg_rsp *rsp;\n+\tint status;\n+\n+\t/* send */\n+\tdo {\n+\t\tstatus = rte_ring_sp_enqueue(msgq_req, req);\n+\t} while (status == -ENOBUFS);\n+\n+\t/* recv */\n+\tdo {\n+\t\tstatus = rte_ring_sc_dequeue(msgq_rsp, (void **) &rsp);\n+\t} while (status != 0);\n+\n+\treturn rsp;\n+}\n+\n+int\n+thread_pipeline_enable(uint32_t thread_id,\n+\tstruct obj *obj,\n+\tconst char *pipeline_name)\n+{\n+\tstruct pipeline *p = pipeline_find(obj, pipeline_name);\n+\tstruct thread *t;\n+\tstruct thread_msg_req *req;\n+\tstruct thread_msg_rsp *rsp;\n+\tint status;\n+\n+\t/* Check input params */\n+\tif ((thread_id >= RTE_MAX_LCORE) ||\n+\t\t(p == NULL))\n+\t\treturn -1;\n+\n+\tt = &thread[thread_id];\n+\tif (t->enabled == 0)\n+\t\treturn -1;\n+\n+\tif (!thread_is_running(thread_id)) {\n+\t\tstruct thread_data *td = &thread_data[thread_id];\n+\t\tstruct pipeline_data *tdp = &td->pipeline_data[td->n_pipelines];\n+\n+\t\tif (td->n_pipelines >= THREAD_PIPELINES_MAX)\n+\t\t\treturn -1;\n+\n+\t\t/* Data plane thread */\n+\t\ttd->p[td->n_pipelines] = p->p;\n+\n+\t\ttdp->p = p->p;\n+\t\ttdp->timer_period =\n+\t\t\t(rte_get_tsc_hz() * p->timer_period_ms) / 1000;\n+\t\ttdp->time_next = rte_get_tsc_cycles() + tdp->timer_period;\n+\n+\t\ttd->n_pipelines++;\n+\n+\t\t/* Pipeline */\n+\t\tp->thread_id = thread_id;\n+\t\tp->enabled = 1;\n+\n+\t\treturn 0;\n+\t}\n+\n+\t/* Allocate request */\n+\treq = thread_msg_alloc();\n+\tif (req == NULL)\n+\t\treturn -1;\n+\n+\t/* Write request */\n+\treq->type = THREAD_REQ_PIPELINE_ENABLE;\n+\treq->pipeline_enable.p = p->p;\n+\treq->pipeline_enable.timer_period_ms = p->timer_period_ms;\n+\n+\t/* Send request and wait for response */\n+\trsp = thread_msg_send_recv(thread_id, req);\n+\n+\t/* Read response */\n+\tstatus = rsp->status;\n+\n+\t/* Free response */\n+\tthread_msg_free(rsp);\n+\n+\t/* Request completion */\n+\tif (status)\n+\t\treturn status;\n+\n+\tp->thread_id = thread_id;\n+\tp->enabled = 1;\n+\n+\treturn 0;\n+}\n+\n+int\n+thread_pipeline_disable(uint32_t thread_id,\n+\tstruct obj *obj,\n+\tconst char *pipeline_name)\n+{\n+\tstruct pipeline *p = pipeline_find(obj, pipeline_name);\n+\tstruct thread *t;\n+\tstruct thread_msg_req *req;\n+\tstruct thread_msg_rsp *rsp;\n+\tint status;\n+\n+\t/* Check input params */\n+\tif ((thread_id >= RTE_MAX_LCORE) ||\n+\t\t(p == NULL))\n+\t\treturn -1;\n+\n+\tt = &thread[thread_id];\n+\tif (t->enabled == 0)\n+\t\treturn -1;\n+\n+\tif (p->enabled == 0)\n+\t\treturn 0;\n+\n+\tif (p->thread_id != thread_id)\n+\t\treturn -1;\n+\n+\tif (!thread_is_running(thread_id)) {\n+\t\tstruct thread_data *td = &thread_data[thread_id];\n+\t\tuint32_t i;\n+\n+\t\tfor (i = 0; i < td->n_pipelines; i++) {\n+\t\t\tstruct pipeline_data *tdp = &td->pipeline_data[i];\n+\n+\t\t\tif (tdp->p != p->p)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/* Data plane thread */\n+\t\t\tif (i < td->n_pipelines - 1) {\n+\t\t\t\tstruct rte_swx_pipeline *pipeline_last =\n+\t\t\t\t\ttd->p[td->n_pipelines - 1];\n+\t\t\t\tstruct pipeline_data *tdp_last =\n+\t\t\t\t\t&td->pipeline_data[td->n_pipelines - 1];\n+\n+\t\t\t\ttd->p[i] = pipeline_last;\n+\t\t\t\tmemcpy(tdp, tdp_last, sizeof(*tdp));\n+\t\t\t}\n+\n+\t\t\ttd->n_pipelines--;\n+\n+\t\t\t/* Pipeline */\n+\t\t\tp->enabled = 0;\n+\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\treturn 0;\n+\t}\n+\n+\t/* Allocate request */\n+\treq = thread_msg_alloc();\n+\tif (req == NULL)\n+\t\treturn -1;\n+\n+\t/* Write request */\n+\treq->type = THREAD_REQ_PIPELINE_DISABLE;\n+\treq->pipeline_disable.p = p->p;\n+\n+\t/* Send request and wait for response */\n+\trsp = thread_msg_send_recv(thread_id, req);\n+\n+\t/* Read response */\n+\tstatus = rsp->status;\n+\n+\t/* Free response */\n+\tthread_msg_free(rsp);\n+\n+\t/* Request completion */\n+\tif (status)\n+\t\treturn status;\n+\n+\tp->enabled = 0;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * Data plane threads: message handling\n+ */\n+static inline struct thread_msg_req *\n+thread_msg_recv(struct rte_ring *msgq_req)\n+{\n+\tstruct thread_msg_req *req;\n+\n+\tint status = rte_ring_sc_dequeue(msgq_req, (void **) &req);\n+\n+\tif (status != 0)\n+\t\treturn NULL;\n+\n+\treturn req;\n+}\n+\n+static inline void\n+thread_msg_send(struct rte_ring *msgq_rsp,\n+\tstruct thread_msg_rsp *rsp)\n+{\n+\tint status;\n+\n+\tdo {\n+\t\tstatus = rte_ring_sp_enqueue(msgq_rsp, rsp);\n+\t} while (status == -ENOBUFS);\n+}\n+\n+static struct thread_msg_rsp *\n+thread_msg_handle_pipeline_enable(struct thread_data *t,\n+\tstruct thread_msg_req *req)\n+{\n+\tstruct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;\n+\tstruct pipeline_data *p = &t->pipeline_data[t->n_pipelines];\n+\n+\t/* Request */\n+\tif (t->n_pipelines >= THREAD_PIPELINES_MAX) {\n+\t\trsp->status = -1;\n+\t\treturn rsp;\n+\t}\n+\n+\tt->p[t->n_pipelines] = req->pipeline_enable.p;\n+\n+\tp->p = req->pipeline_enable.p;\n+\tp->timer_period = (rte_get_tsc_hz() *\n+\t\treq->pipeline_enable.timer_period_ms) / 1000;\n+\tp->time_next = rte_get_tsc_cycles() + p->timer_period;\n+\n+\tt->n_pipelines++;\n+\n+\t/* Response */\n+\trsp->status = 0;\n+\treturn rsp;\n+}\n+\n+static struct thread_msg_rsp *\n+thread_msg_handle_pipeline_disable(struct thread_data *t,\n+\tstruct thread_msg_req *req)\n+{\n+\tstruct thread_msg_rsp *rsp = (struct thread_msg_rsp *) req;\n+\tuint32_t n_pipelines = t->n_pipelines;\n+\tstruct rte_swx_pipeline *pipeline = req->pipeline_disable.p;\n+\tuint32_t i;\n+\n+\t/* find pipeline */\n+\tfor (i = 0; i < n_pipelines; i++) {\n+\t\tstruct pipeline_data *p = &t->pipeline_data[i];\n+\n+\t\tif (p->p != pipeline)\n+\t\t\tcontinue;\n+\n+\t\tif (i < n_pipelines - 1) {\n+\t\t\tstruct rte_swx_pipeline *pipeline_last =\n+\t\t\t\tt->p[n_pipelines - 1];\n+\t\t\tstruct pipeline_data *p_last =\n+\t\t\t\t&t->pipeline_data[n_pipelines - 1];\n+\n+\t\t\tt->p[i] = pipeline_last;\n+\t\t\tmemcpy(p, p_last, sizeof(*p));\n+\t\t}\n+\n+\t\tt->n_pipelines--;\n+\n+\t\trsp->status = 0;\n+\t\treturn rsp;\n+\t}\n+\n+\t/* should not get here */\n+\trsp->status = 0;\n+\treturn rsp;\n+}\n+\n+static void\n+thread_msg_handle(struct thread_data *t)\n+{\n+\tfor ( ; ; ) {\n+\t\tstruct thread_msg_req *req;\n+\t\tstruct thread_msg_rsp *rsp;\n+\n+\t\treq = thread_msg_recv(t->msgq_req);\n+\t\tif (req == NULL)\n+\t\t\tbreak;\n+\n+\t\tswitch (req->type) {\n+\t\tcase THREAD_REQ_PIPELINE_ENABLE:\n+\t\t\trsp = thread_msg_handle_pipeline_enable(t, req);\n+\t\t\tbreak;\n+\n+\t\tcase THREAD_REQ_PIPELINE_DISABLE:\n+\t\t\trsp = thread_msg_handle_pipeline_disable(t, req);\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\trsp = (struct thread_msg_rsp *) req;\n+\t\t\trsp->status = -1;\n+\t\t}\n+\n+\t\tthread_msg_send(t->msgq_rsp, rsp);\n+\t}\n+}\n+\n+/**\n+ * Data plane threads: main\n+ */\n+int\n+thread_main(void *arg __rte_unused)\n+{\n+\tstruct thread_data *t;\n+\tuint32_t thread_id, i;\n+\n+\tthread_id = rte_lcore_id();\n+\tt = &thread_data[thread_id];\n+\n+\t/* Dispatch loop */\n+\tfor (i = 0; ; i++) {\n+\t\tuint32_t j;\n+\n+\t\t/* Data Plane */\n+\t\tfor (j = 0; j < t->n_pipelines; j++)\n+\t\t\trte_swx_pipeline_run(t->p[j], 1000000);\n+\n+\t\t/* Control Plane */\n+\t\tif ((i & 0xF) == 0) {\n+\t\t\tuint64_t time = rte_get_tsc_cycles();\n+\t\t\tuint64_t time_next_min = UINT64_MAX;\n+\n+\t\t\tif (time < t->time_next_min)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/* Thread message queues */\n+\t\t\t{\n+\t\t\t\tuint64_t time_next = t->time_next;\n+\n+\t\t\t\tif (time_next <= time) {\n+\t\t\t\t\tthread_msg_handle(t);\n+\t\t\t\t\ttime_next = time + t->timer_period;\n+\t\t\t\t\tt->time_next = time_next;\n+\t\t\t\t}\n+\n+\t\t\t\tif (time_next < time_next_min)\n+\t\t\t\t\ttime_next_min = time_next;\n+\t\t\t}\n+\n+\t\t\tt->time_next_min = time_next_min;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\ndiff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h\nnew file mode 100644\nindex 000000000..829d82cbd\n--- /dev/null\n+++ b/examples/pipeline/thread.h\n@@ -0,0 +1,28 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2010-2020 Intel Corporation\n+ */\n+\n+#ifndef _INCLUDE_THREAD_H_\n+#define _INCLUDE_THREAD_H_\n+\n+#include <stdint.h>\n+\n+#include \"obj.h\"\n+\n+int\n+thread_pipeline_enable(uint32_t thread_id,\n+\tstruct obj *obj,\n+\tconst char *pipeline_name);\n+\n+int\n+thread_pipeline_disable(uint32_t thread_id,\n+\tstruct obj *obj,\n+\tconst char *pipeline_name);\n+\n+int\n+thread_init(void);\n+\n+int\n+thread_main(void *arg);\n+\n+#endif /* _INCLUDE_THREAD_H_ */\n",
    "prefixes": [
        "v3",
        "36/41"
    ]
}