get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 76939,
    "url": "http://patches.dpdk.org/api/patches/76939/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20200908201830.74206-6-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-6-cristian.dumitrescu@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20200908201830.74206-6-cristian.dumitrescu@intel.com",
    "date": "2020-09-08T20:17:54",
    "name": "[v3,05/41] pipeline: add SWX extern objects and funcs",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "0c83225b07c1bd99afbacb11885bd987cfec463d",
    "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-6-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/76939/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/76939/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 44610A04B1;\n\tTue,  8 Sep 2020 22:19:53 +0200 (CEST)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 2B42C1C125;\n\tTue,  8 Sep 2020 22:19:05 +0200 (CEST)",
            "from mga02.intel.com (mga02.intel.com [134.134.136.20])\n by dpdk.org (Postfix) with ESMTP id 054291DB8\n for <dev@dpdk.org>; Tue,  8 Sep 2020 22:18:54 +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:18:37 -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:18:36 -0700"
        ],
        "IronPort-SDR": [
            "\n a3KMvGr41ZNNkGjC/DbgB1ap0IRSXD2Zh+evnUdYW5OVy5nKOc7NUz+NZ07EGGHymsOaa2ohXw\n ztgVF+WJMP3w==",
            "\n l4IXUyUdnhIE+HnoGEoUjDJLzC1wwdLS4q8sMLNYxrYFFHCZIojwcjEx0QF8CAbfKrSJZxUC1o\n Y9QgU7u6Cn4g=="
        ],
        "X-IronPort-AV": [
            "E=McAfee;i=\"6000,8403,9738\"; a=\"145939373\"",
            "E=Sophos;i=\"5.76,407,1592895600\"; d=\"scan'208\";a=\"145939373\"",
            "E=Sophos;i=\"5.76,406,1592895600\"; d=\"scan'208\";a=\"504493374\""
        ],
        "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:17:54 +0100",
        "Message-Id": "<20200908201830.74206-6-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 05/41] pipeline: add SWX extern objects and\n\tfuncs",
        "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 extern objects and functions to plug into the SWX pipeline any\nfunctionality that cannot be efficiently implemented with existing\ninstructions, e.g. special checksum/ECC, crypto, meters, stats arrays,\nheuristics, etc. In/out arguments are passed through mailbox with\nformat defined by struct.\n\nSigned-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>\n---\n lib/librte_pipeline/meson.build              |   3 +-\n lib/librte_pipeline/rte_pipeline_version.map |   4 +\n lib/librte_pipeline/rte_swx_extern.h         |  98 ++++\n lib/librte_pipeline/rte_swx_pipeline.c       | 477 +++++++++++++++++++\n lib/librte_pipeline/rte_swx_pipeline.h       | 113 +++++\n 5 files changed, 694 insertions(+), 1 deletion(-)\n create mode 100644 lib/librte_pipeline/rte_swx_extern.h",
    "diff": "diff --git a/lib/librte_pipeline/meson.build b/lib/librte_pipeline/meson.build\nindex 880c2b274..bea406848 100644\n--- a/lib/librte_pipeline/meson.build\n+++ b/lib/librte_pipeline/meson.build\n@@ -8,5 +8,6 @@ sources = files('rte_pipeline.c',\n headers = files('rte_pipeline.h',\n \t'rte_port_in_action.h',\n \t'rte_table_action.h',\n-\t'rte_swx_pipeline.h',)\n+\t'rte_swx_pipeline.h',\n+\t'rte_swx_extern.h',)\n deps += ['port', 'table', 'meter', 'sched', 'cryptodev']\ndiff --git a/lib/librte_pipeline/rte_pipeline_version.map b/lib/librte_pipeline/rte_pipeline_version.map\nindex 6a48c3666..4297e185d 100644\n--- a/lib/librte_pipeline/rte_pipeline_version.map\n+++ b/lib/librte_pipeline/rte_pipeline_version.map\n@@ -60,6 +60,10 @@ EXPERIMENTAL {\n \trte_swx_pipeline_port_in_config;\n \trte_swx_pipeline_port_out_type_register;\n \trte_swx_pipeline_port_out_config;\n+\trte_swx_pipeline_extern_type_register;\n+\trte_swx_pipeline_extern_type_member_func_register;\n+\trte_swx_pipeline_extern_object_config;\n+\trte_swx_pipeline_extern_func_register;\n \trte_swx_pipeline_struct_type_register;\n \trte_swx_pipeline_packet_header_register;\n \trte_swx_pipeline_packet_metadata_register;\ndiff --git a/lib/librte_pipeline/rte_swx_extern.h b/lib/librte_pipeline/rte_swx_extern.h\nnew file mode 100644\nindex 000000000..e10e963d6\n--- /dev/null\n+++ b/lib/librte_pipeline/rte_swx_extern.h\n@@ -0,0 +1,98 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2020 Intel Corporation\n+ */\n+#ifndef __INCLUDE_RTE_SWX_EXTERN_H__\n+#define __INCLUDE_RTE_SWX_EXTERN_H__\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+/**\n+ * @file\n+ * RTE SWX Extern objects and functions\n+ *\n+ * Extern object and extern function interfaces. The extern objects and extern\n+ * functions provide the mechanisms to hook external functionality into the\n+ * packet processing pipeline.\n+ */\n+\n+#include <stdint.h>\n+\n+/*\n+ * Extern type\n+ */\n+\n+/**\n+ * Extern object constructor\n+ *\n+ * @param[in] args\n+ *   Extern object constructor arguments. It may be NULL.\n+ * @return\n+ *   Extern object handle.\n+ */\n+typedef void *\n+(*rte_swx_extern_type_constructor_t)(const char *args);\n+\n+/**\n+ * Extern object destructor\n+ *\n+ * @param[in] object\n+ *   Extern object handle.\n+ */\n+typedef void\n+(*rte_swx_extern_type_destructor_t)(void *object);\n+\n+/**\n+ * Extern object member function\n+ *\n+ * The mailbox is used to pass input arguments to the member function and\n+ * retrieve the output results. The mailbox mechanism allows for multiple\n+ * concurrent executions of the same member function for the same extern object.\n+ *\n+ * Multiple invocations of the same member function may be required in order for\n+ * the associated operation to complete. The completion is flagged by a return\n+ * value of 1, in which case the results are available in the mailbox; in case\n+ * of a return value of 0, the operation is not yet completed, so the member\n+ * function must be invoked again with exactly the same object and mailbox\n+ * arguments.\n+ *\n+ * @param[in] object\n+ *   Extern object handle.\n+ * @param[in] mailbox\n+ *   Extern object mailbox.\n+ * @return\n+ *   0 when the operation is not yet completed, and 1 when the operation is\n+ *   completed. No other return values are allowed.\n+ */\n+typedef int\n+(*rte_swx_extern_type_member_func_t)(void *object, void *mailbox);\n+\n+/*\n+ * Extern function\n+ */\n+\n+/** The mailbox is used to pass input arguments to the extern function and\n+ * retrieve the output results. The mailbox mechanism allows for multiple\n+ * concurrent executions of the same extern function.\n+ *\n+ * Multiple invocations of the same extern function may be required in order for\n+ * the associated operation to complete. The completion is flagged by a return\n+ * value of 1, in which case the results are available in the mailbox; in case\n+ * of a return value of 0, the operation is not yet completed, so the extern\n+ * function must be invoked again with exactly the same mailbox argument.\n+ *\n+ * @param[in] mailbox\n+ *   Extern object mailbox.\n+ * @return\n+ *   0 when the operation is not yet completed, and 1 when the operation is\n+ *   completed. No other return values are allowed.\n+ */\n+typedef int\n+(*rte_swx_extern_func_t)(void *mailbox);\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif\ndiff --git a/lib/librte_pipeline/rte_swx_pipeline.c b/lib/librte_pipeline/rte_swx_pipeline.c\nindex cb2e32b83..2335831bf 100644\n--- a/lib/librte_pipeline/rte_swx_pipeline.c\n+++ b/lib/librte_pipeline/rte_swx_pipeline.c\n@@ -90,6 +90,70 @@ struct port_out_runtime {\n \tvoid *obj;\n };\n \n+/*\n+ * Extern object.\n+ */\n+struct extern_type_member_func {\n+\tTAILQ_ENTRY(extern_type_member_func) node;\n+\tchar name[RTE_SWX_NAME_SIZE];\n+\trte_swx_extern_type_member_func_t func;\n+\tuint32_t id;\n+};\n+\n+TAILQ_HEAD(extern_type_member_func_tailq, extern_type_member_func);\n+\n+struct extern_type {\n+\tTAILQ_ENTRY(extern_type) node;\n+\tchar name[RTE_SWX_NAME_SIZE];\n+\tstruct struct_type *mailbox_struct_type;\n+\trte_swx_extern_type_constructor_t constructor;\n+\trte_swx_extern_type_destructor_t destructor;\n+\tstruct extern_type_member_func_tailq funcs;\n+\tuint32_t n_funcs;\n+};\n+\n+TAILQ_HEAD(extern_type_tailq, extern_type);\n+\n+struct extern_obj {\n+\tTAILQ_ENTRY(extern_obj) node;\n+\tchar name[RTE_SWX_NAME_SIZE];\n+\tstruct extern_type *type;\n+\tvoid *obj;\n+\tuint32_t struct_id;\n+\tuint32_t id;\n+};\n+\n+TAILQ_HEAD(extern_obj_tailq, extern_obj);\n+\n+#ifndef RTE_SWX_EXTERN_TYPE_MEMBER_FUNCS_MAX\n+#define RTE_SWX_EXTERN_TYPE_MEMBER_FUNCS_MAX 8\n+#endif\n+\n+struct extern_obj_runtime {\n+\tvoid *obj;\n+\tuint8_t *mailbox;\n+\trte_swx_extern_type_member_func_t funcs[RTE_SWX_EXTERN_TYPE_MEMBER_FUNCS_MAX];\n+};\n+\n+/*\n+ * Extern function.\n+ */\n+struct extern_func {\n+\tTAILQ_ENTRY(extern_func) node;\n+\tchar name[RTE_SWX_NAME_SIZE];\n+\tstruct struct_type *mailbox_struct_type;\n+\trte_swx_extern_func_t func;\n+\tuint32_t struct_id;\n+\tuint32_t id;\n+};\n+\n+TAILQ_HEAD(extern_func_tailq, extern_func);\n+\n+struct extern_func_runtime {\n+\tuint8_t *mailbox;\n+\trte_swx_extern_func_t func;\n+};\n+\n /*\n  * Header.\n  */\n@@ -130,6 +194,10 @@ struct thread {\n \n \t/* Packet meta-data. */\n \tuint8_t *metadata;\n+\n+\t/* Extern objects and functions. */\n+\tstruct extern_obj_runtime *extern_objs;\n+\tstruct extern_func_runtime *extern_funcs;\n };\n \n #ifndef RTE_SWX_PIPELINE_THREADS_MAX\n@@ -142,6 +210,9 @@ struct rte_swx_pipeline {\n \tstruct port_in_tailq ports_in;\n \tstruct port_out_type_tailq port_out_types;\n \tstruct port_out_tailq ports_out;\n+\tstruct extern_type_tailq extern_types;\n+\tstruct extern_obj_tailq extern_objs;\n+\tstruct extern_func_tailq extern_funcs;\n \tstruct header_tailq headers;\n \tstruct struct_type *metadata_st;\n \tuint32_t metadata_struct_id;\n@@ -153,6 +224,8 @@ struct rte_swx_pipeline {\n \tuint32_t n_structs;\n \tuint32_t n_ports_in;\n \tuint32_t n_ports_out;\n+\tuint32_t n_extern_objs;\n+\tuint32_t n_extern_funcs;\n \tuint32_t n_headers;\n \tint build_done;\n \tint numa_node;\n@@ -606,6 +679,395 @@ port_out_free(struct rte_swx_pipeline *p)\n \t}\n }\n \n+/*\n+ * Extern object.\n+ */\n+static struct extern_type *\n+extern_type_find(struct rte_swx_pipeline *p, const char *name)\n+{\n+\tstruct extern_type *elem;\n+\n+\tTAILQ_FOREACH(elem, &p->extern_types, node)\n+\t\tif (strcmp(elem->name, name) == 0)\n+\t\t\treturn elem;\n+\n+\treturn NULL;\n+}\n+\n+static struct extern_type_member_func *\n+extern_type_member_func_find(struct extern_type *type, const char *name)\n+{\n+\tstruct extern_type_member_func *elem;\n+\n+\tTAILQ_FOREACH(elem, &type->funcs, node)\n+\t\tif (strcmp(elem->name, name) == 0)\n+\t\t\treturn elem;\n+\n+\treturn NULL;\n+}\n+\n+static struct extern_obj *\n+extern_obj_find(struct rte_swx_pipeline *p, const char *name)\n+{\n+\tstruct extern_obj *elem;\n+\n+\tTAILQ_FOREACH(elem, &p->extern_objs, node)\n+\t\tif (strcmp(elem->name, name) == 0)\n+\t\t\treturn elem;\n+\n+\treturn NULL;\n+}\n+\n+int\n+rte_swx_pipeline_extern_type_register(struct rte_swx_pipeline *p,\n+\tconst char *name,\n+\tconst char *mailbox_struct_type_name,\n+\trte_swx_extern_type_constructor_t constructor,\n+\trte_swx_extern_type_destructor_t destructor)\n+{\n+\tstruct extern_type *elem;\n+\tstruct struct_type *mailbox_struct_type;\n+\n+\tCHECK(p, EINVAL);\n+\n+\tCHECK_NAME(name, EINVAL);\n+\tCHECK(!extern_type_find(p, name), EEXIST);\n+\n+\tCHECK_NAME(mailbox_struct_type_name, EINVAL);\n+\tmailbox_struct_type = struct_type_find(p, mailbox_struct_type_name);\n+\tCHECK(mailbox_struct_type, EINVAL);\n+\n+\tCHECK(constructor, EINVAL);\n+\tCHECK(destructor, EINVAL);\n+\n+\t/* Node allocation. */\n+\telem = calloc(1, sizeof(struct extern_type));\n+\tCHECK(elem, ENOMEM);\n+\n+\t/* Node initialization. */\n+\tstrcpy(elem->name, name);\n+\telem->mailbox_struct_type = mailbox_struct_type;\n+\telem->constructor = constructor;\n+\telem->destructor = destructor;\n+\tTAILQ_INIT(&elem->funcs);\n+\n+\t/* Node add to tailq. */\n+\tTAILQ_INSERT_TAIL(&p->extern_types, elem, node);\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_swx_pipeline_extern_type_member_func_register(struct rte_swx_pipeline *p,\n+\tconst char *extern_type_name,\n+\tconst char *name,\n+\trte_swx_extern_type_member_func_t member_func)\n+{\n+\tstruct extern_type *type;\n+\tstruct extern_type_member_func *type_member;\n+\n+\tCHECK(p, EINVAL);\n+\n+\tCHECK(extern_type_name, EINVAL);\n+\ttype = extern_type_find(p, extern_type_name);\n+\tCHECK(type, EINVAL);\n+\tCHECK(type->n_funcs < RTE_SWX_EXTERN_TYPE_MEMBER_FUNCS_MAX, ENOSPC);\n+\n+\tCHECK(name, EINVAL);\n+\tCHECK(!extern_type_member_func_find(type, name), EEXIST);\n+\n+\tCHECK(member_func, EINVAL);\n+\n+\t/* Node allocation. */\n+\ttype_member = calloc(1, sizeof(struct extern_type_member_func));\n+\tCHECK(type_member, ENOMEM);\n+\n+\t/* Node initialization. */\n+\tstrcpy(type_member->name, name);\n+\ttype_member->func = member_func;\n+\ttype_member->id = type->n_funcs;\n+\n+\t/* Node add to tailq. */\n+\tTAILQ_INSERT_TAIL(&type->funcs, type_member, node);\n+\ttype->n_funcs++;\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_swx_pipeline_extern_object_config(struct rte_swx_pipeline *p,\n+\t\t\t\t      const char *extern_type_name,\n+\t\t\t\t      const char *name,\n+\t\t\t\t      const char *args)\n+{\n+\tstruct extern_type *type;\n+\tstruct extern_obj *obj;\n+\tvoid *obj_handle;\n+\n+\tCHECK(p, EINVAL);\n+\n+\tCHECK_NAME(extern_type_name, EINVAL);\n+\ttype = extern_type_find(p, extern_type_name);\n+\tCHECK(type, EINVAL);\n+\n+\tCHECK_NAME(name, EINVAL);\n+\tCHECK(!extern_obj_find(p, name), EEXIST);\n+\n+\t/* Node allocation. */\n+\tobj = calloc(1, sizeof(struct extern_obj));\n+\tCHECK(obj, ENOMEM);\n+\n+\t/* Object construction. */\n+\tobj_handle = type->constructor(args);\n+\tif (!obj_handle) {\n+\t\tfree(obj);\n+\t\tCHECK(0, ENODEV);\n+\t}\n+\n+\t/* Node initialization. */\n+\tstrcpy(obj->name, name);\n+\tobj->type = type;\n+\tobj->obj = obj_handle;\n+\tobj->struct_id = p->n_structs;\n+\tobj->id = p->n_extern_objs;\n+\n+\t/* Node add to tailq. */\n+\tTAILQ_INSERT_TAIL(&p->extern_objs, obj, node);\n+\tp->n_extern_objs++;\n+\tp->n_structs++;\n+\n+\treturn 0;\n+}\n+\n+static int\n+extern_obj_build(struct rte_swx_pipeline *p)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {\n+\t\tstruct thread *t = &p->threads[i];\n+\t\tstruct extern_obj *obj;\n+\n+\t\tt->extern_objs = calloc(p->n_extern_objs,\n+\t\t\t\t\tsizeof(struct extern_obj_runtime));\n+\t\tCHECK(t->extern_objs, ENOMEM);\n+\n+\t\tTAILQ_FOREACH(obj, &p->extern_objs, node) {\n+\t\t\tstruct extern_obj_runtime *r =\n+\t\t\t\t&t->extern_objs[obj->id];\n+\t\t\tstruct extern_type_member_func *func;\n+\t\t\tuint32_t mailbox_size =\n+\t\t\t\tobj->type->mailbox_struct_type->n_bits / 8;\n+\n+\t\t\tr->obj = obj->obj;\n+\n+\t\t\tr->mailbox = calloc(1, mailbox_size);\n+\t\t\tCHECK(r->mailbox, ENOMEM);\n+\n+\t\t\tTAILQ_FOREACH(func, &obj->type->funcs, node)\n+\t\t\t\tr->funcs[func->id] = func->func;\n+\n+\t\t\tt->structs[obj->struct_id] = r->mailbox;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void\n+extern_obj_build_free(struct rte_swx_pipeline *p)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {\n+\t\tstruct thread *t = &p->threads[i];\n+\t\tuint32_t j;\n+\n+\t\tif (!t->extern_objs)\n+\t\t\tcontinue;\n+\n+\t\tfor (j = 0; j < p->n_extern_objs; j++) {\n+\t\t\tstruct extern_obj_runtime *r = &t->extern_objs[j];\n+\n+\t\t\tfree(r->mailbox);\n+\t\t}\n+\n+\t\tfree(t->extern_objs);\n+\t\tt->extern_objs = NULL;\n+\t}\n+}\n+\n+static void\n+extern_obj_free(struct rte_swx_pipeline *p)\n+{\n+\textern_obj_build_free(p);\n+\n+\t/* Extern objects. */\n+\tfor ( ; ; ) {\n+\t\tstruct extern_obj *elem;\n+\n+\t\telem = TAILQ_FIRST(&p->extern_objs);\n+\t\tif (!elem)\n+\t\t\tbreak;\n+\n+\t\tTAILQ_REMOVE(&p->extern_objs, elem, node);\n+\t\tif (elem->obj)\n+\t\t\telem->type->destructor(elem->obj);\n+\t\tfree(elem);\n+\t}\n+\n+\t/* Extern types. */\n+\tfor ( ; ; ) {\n+\t\tstruct extern_type *elem;\n+\n+\t\telem = TAILQ_FIRST(&p->extern_types);\n+\t\tif (!elem)\n+\t\t\tbreak;\n+\n+\t\tTAILQ_REMOVE(&p->extern_types, elem, node);\n+\n+\t\tfor ( ; ; ) {\n+\t\t\tstruct extern_type_member_func *func;\n+\n+\t\t\tfunc = TAILQ_FIRST(&elem->funcs);\n+\t\t\tif (!func)\n+\t\t\t\tbreak;\n+\n+\t\t\tTAILQ_REMOVE(&elem->funcs, func, node);\n+\t\t\tfree(func);\n+\t\t}\n+\n+\t\tfree(elem);\n+\t}\n+}\n+\n+/*\n+ * Extern function.\n+ */\n+static struct extern_func *\n+extern_func_find(struct rte_swx_pipeline *p, const char *name)\n+{\n+\tstruct extern_func *elem;\n+\n+\tTAILQ_FOREACH(elem, &p->extern_funcs, node)\n+\t\tif (strcmp(elem->name, name) == 0)\n+\t\t\treturn elem;\n+\n+\treturn NULL;\n+}\n+\n+int\n+rte_swx_pipeline_extern_func_register(struct rte_swx_pipeline *p,\n+\t\t\t\t      const char *name,\n+\t\t\t\t      const char *mailbox_struct_type_name,\n+\t\t\t\t      rte_swx_extern_func_t func)\n+{\n+\tstruct extern_func *f;\n+\tstruct struct_type *mailbox_struct_type;\n+\n+\tCHECK(p, EINVAL);\n+\n+\tCHECK_NAME(name, EINVAL);\n+\tCHECK(!extern_func_find(p, name), EEXIST);\n+\n+\tCHECK_NAME(mailbox_struct_type_name, EINVAL);\n+\tmailbox_struct_type = struct_type_find(p, mailbox_struct_type_name);\n+\tCHECK(mailbox_struct_type, EINVAL);\n+\n+\tCHECK(func, EINVAL);\n+\n+\t/* Node allocation. */\n+\tf = calloc(1, sizeof(struct extern_func));\n+\tCHECK(func, ENOMEM);\n+\n+\t/* Node initialization. */\n+\tstrcpy(f->name, name);\n+\tf->mailbox_struct_type = mailbox_struct_type;\n+\tf->func = func;\n+\tf->struct_id = p->n_structs;\n+\tf->id = p->n_extern_funcs;\n+\n+\t/* Node add to tailq. */\n+\tTAILQ_INSERT_TAIL(&p->extern_funcs, f, node);\n+\tp->n_extern_funcs++;\n+\tp->n_structs++;\n+\n+\treturn 0;\n+}\n+\n+static int\n+extern_func_build(struct rte_swx_pipeline *p)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {\n+\t\tstruct thread *t = &p->threads[i];\n+\t\tstruct extern_func *func;\n+\n+\t\t/* Memory allocation. */\n+\t\tt->extern_funcs = calloc(p->n_extern_funcs,\n+\t\t\t\t\t sizeof(struct extern_func_runtime));\n+\t\tCHECK(t->extern_funcs, ENOMEM);\n+\n+\t\t/* Extern function. */\n+\t\tTAILQ_FOREACH(func, &p->extern_funcs, node) {\n+\t\t\tstruct extern_func_runtime *r =\n+\t\t\t\t&t->extern_funcs[func->id];\n+\t\t\tuint32_t mailbox_size =\n+\t\t\t\tfunc->mailbox_struct_type->n_bits / 8;\n+\n+\t\t\tr->func = func->func;\n+\n+\t\t\tr->mailbox = calloc(1, mailbox_size);\n+\t\t\tCHECK(r->mailbox, ENOMEM);\n+\n+\t\t\tt->structs[func->struct_id] = r->mailbox;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void\n+extern_func_build_free(struct rte_swx_pipeline *p)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {\n+\t\tstruct thread *t = &p->threads[i];\n+\t\tuint32_t j;\n+\n+\t\tif (!t->extern_funcs)\n+\t\t\tcontinue;\n+\n+\t\tfor (j = 0; j < p->n_extern_funcs; j++) {\n+\t\t\tstruct extern_func_runtime *r = &t->extern_funcs[j];\n+\n+\t\t\tfree(r->mailbox);\n+\t\t}\n+\n+\t\tfree(t->extern_funcs);\n+\t\tt->extern_funcs = NULL;\n+\t}\n+}\n+\n+static void\n+extern_func_free(struct rte_swx_pipeline *p)\n+{\n+\textern_func_build_free(p);\n+\n+\tfor ( ; ; ) {\n+\t\tstruct extern_func *elem;\n+\n+\t\telem = TAILQ_FIRST(&p->extern_funcs);\n+\t\tif (!elem)\n+\t\t\tbreak;\n+\n+\t\tTAILQ_REMOVE(&p->extern_funcs, elem, node);\n+\t\tfree(elem);\n+\t}\n+}\n+\n /*\n  * Header.\n  */\n@@ -826,6 +1288,9 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)\n \tTAILQ_INIT(&pipeline->ports_in);\n \tTAILQ_INIT(&pipeline->port_out_types);\n \tTAILQ_INIT(&pipeline->ports_out);\n+\tTAILQ_INIT(&pipeline->extern_types);\n+\tTAILQ_INIT(&pipeline->extern_objs);\n+\tTAILQ_INIT(&pipeline->extern_funcs);\n \tTAILQ_INIT(&pipeline->headers);\n \n \tpipeline->n_structs = 1; /* Struct 0 is reserved for action_data. */\n@@ -843,6 +1308,8 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)\n \n \tmetadata_free(p);\n \theader_free(p);\n+\textern_func_free(p);\n+\textern_obj_free(p);\n \tport_out_free(p);\n \tport_in_free(p);\n \tstruct_free(p);\n@@ -870,6 +1337,14 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)\n \tif (status)\n \t\tgoto error;\n \n+\tstatus = extern_obj_build(p);\n+\tif (status)\n+\t\tgoto error;\n+\n+\tstatus = extern_func_build(p);\n+\tif (status)\n+\t\tgoto error;\n+\n \tstatus = header_build(p);\n \tif (status)\n \t\tgoto error;\n@@ -884,6 +1359,8 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)\n error:\n \tmetadata_build_free(p);\n \theader_build_free(p);\n+\textern_func_build_free(p);\n+\textern_obj_build_free(p);\n \tport_out_build_free(p);\n \tport_in_build_free(p);\n \tstruct_build_free(p);\ndiff --git a/lib/librte_pipeline/rte_swx_pipeline.h b/lib/librte_pipeline/rte_swx_pipeline.h\nindex 4a7b679a4..2e8a6cdf8 100644\n--- a/lib/librte_pipeline/rte_swx_pipeline.h\n+++ b/lib/librte_pipeline/rte_swx_pipeline.h\n@@ -19,6 +19,7 @@ extern \"C\" {\n #include <rte_compat.h>\n \n #include \"rte_swx_port.h\"\n+#include \"rte_swx_extern.h\"\n \n /** Name size. */\n #ifndef RTE_SWX_NAME_SIZE\n@@ -147,6 +148,118 @@ rte_swx_pipeline_port_out_config(struct rte_swx_pipeline *p,\n \t\t\t\t const char *port_type_name,\n \t\t\t\t void *args);\n \n+/*\n+ * Extern objects and functions\n+ */\n+\n+/**\n+ * Pipeline extern type register\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[in] name\n+ *   Extern type name.\n+ * @param[in] mailbox_struct_type_name\n+ *   Name of existing struct type used to define the mailbox size and layout for\n+ *   the extern objects that are instances of this type. Each extern object gets\n+ *   its own mailbox, which is used to pass the input arguments to the member\n+ *   functions and retrieve the output results.\n+ * @param[in] constructor\n+ *   Function used to create the extern objects that are instances of this type.\n+ * @param[in] destructor\n+ *   Function used to free the extern objects that are instances of  this type.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument;\n+ *   -ENOMEM: Not enough space/cannot allocate memory;\n+ *   -EEXIST: Extern type with this name already exists.\n+ */\n+__rte_experimental\n+int\n+rte_swx_pipeline_extern_type_register(struct rte_swx_pipeline *p,\n+\tconst char *name,\n+\tconst char *mailbox_struct_type_name,\n+\trte_swx_extern_type_constructor_t constructor,\n+\trte_swx_extern_type_destructor_t destructor);\n+\n+/**\n+ * Pipeline extern type member function register\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[in] extern_type_name\n+ *   Existing extern type name.\n+ * @param[in] name\n+ *   Name for the new member function to be added to the extern type.\n+ * @param[in] member_func\n+ *   The new member function.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument;\n+ *   -ENOMEM: Not enough space/cannot allocate memory;\n+ *   -EEXIST: Member function with this name already exists for this type;\n+ *   -ENOSPC: Maximum number of member functions reached for this type.\n+ */\n+__rte_experimental\n+int\n+rte_swx_pipeline_extern_type_member_func_register(struct rte_swx_pipeline *p,\n+\tconst char *extern_type_name,\n+\tconst char *name,\n+\trte_swx_extern_type_member_func_t member_func);\n+\n+/**\n+ * Pipeline extern object configure\n+ *\n+ * Instantiate a given extern type to create new extern object.\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[in] extern_type_name\n+ *   Existing extern type name.\n+ * @param[in] name\n+ *   Name for the new object instantiating the extern type.\n+ * @param[in] args\n+ *   Extern object constructor arguments.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument;\n+ *   -ENOMEM: Not enough space/cannot allocate memory;\n+ *   -EEXIST: Extern object with this name already exists;\n+ *   -ENODEV: Extern object constructor error.\n+ */\n+__rte_experimental\n+int\n+rte_swx_pipeline_extern_object_config(struct rte_swx_pipeline *p,\n+\t\t\t\t      const char *extern_type_name,\n+\t\t\t\t      const char *name,\n+\t\t\t\t      const char *args);\n+\n+/**\n+ * Pipeline extern function register\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[in] name\n+ *   Extern function name.\n+ * @param[in] mailbox_struct_type_name\n+ *   Name of existing struct type used to define the mailbox size and layout for\n+ *   this extern function. The mailbox is used to pass the input arguments to\n+ *   the extern function and retrieve the output results.\n+ * @param[in] func\n+ *   The extern function.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument;\n+ *   -ENOMEM: Not enough space/cannot allocate memory;\n+ *   -EEXIST: Extern function with this name already exists.\n+ */\n+__rte_experimental\n+int\n+rte_swx_pipeline_extern_func_register(struct rte_swx_pipeline *p,\n+\t\t\t\t      const char *name,\n+\t\t\t\t      const char *mailbox_struct_type_name,\n+\t\t\t\t      rte_swx_extern_func_t func);\n+\n /*\n  * Packet headers and meta-data\n  */\n",
    "prefixes": [
        "v3",
        "05/41"
    ]
}