get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 76941,
    "url": "http://patches.dpdk.org/api/patches/76941/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20200908201830.74206-8-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-8-cristian.dumitrescu@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20200908201830.74206-8-cristian.dumitrescu@intel.com",
    "date": "2020-09-08T20:17:56",
    "name": "[v3,07/41] pipeline: add SWX pipeline tables",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "14139cfda0f20024c9189290671a7b219844ded9",
    "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-8-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/76941/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/76941/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 277D6A04B1;\n\tTue,  8 Sep 2020 22:20:13 +0200 (CEST)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 6EB931C12F;\n\tTue,  8 Sep 2020 22:19:08 +0200 (CEST)",
            "from mga02.intel.com (mga02.intel.com [134.134.136.20])\n by dpdk.org (Postfix) with ESMTP id 803A41C0CA\n for <dev@dpdk.org>; Tue,  8 Sep 2020 22:18:55 +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:39 -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:38 -0700"
        ],
        "IronPort-SDR": [
            "\n 0+7Hi21gfDJoveyPJAqugA2Er+2ZLgPX5uS33xRfvZojuZq6s1VqEYd0qUCCiVMb8YnBbWsGzw\n yRt7dPsF0MnQ==",
            "\n ZQWk0A1VTkGOCDhkm21Z0dMTcmvmxN1vD3VcfdLy2TIT75WPUG5Z3FAGU4H+fLHTV91FK0hOAQ\n ODR2pYETWcmw=="
        ],
        "X-IronPort-AV": [
            "E=McAfee;i=\"6000,8403,9738\"; a=\"145939380\"",
            "E=Sophos;i=\"5.76,407,1592895600\"; d=\"scan'208\";a=\"145939380\"",
            "E=Sophos;i=\"5.76,406,1592895600\"; d=\"scan'208\";a=\"504493389\""
        ],
        "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:56 +0100",
        "Message-Id": "<20200908201830.74206-8-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 07/41] pipeline: add SWX pipeline tables",
        "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 tables to the SWX pipeline. The match fields are flexibly selected\nfrom the headers and meta-data. The set of table actions is flexibly\nselected for each table from the set of pipeline actions.\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_ctl.h            |  85 +++\n lib/librte_pipeline/rte_swx_pipeline.c       | 700 +++++++++++++++++++\n lib/librte_pipeline/rte_swx_pipeline.h       | 118 ++++\n lib/librte_table/meson.build                 |   3 +-\n lib/librte_table/rte_swx_table.h             | 295 ++++++++\n 7 files changed, 1206 insertions(+), 2 deletions(-)\n create mode 100644 lib/librte_pipeline/rte_swx_ctl.h\n create mode 100644 lib/librte_table/rte_swx_table.h",
    "diff": "diff --git a/lib/librte_pipeline/meson.build b/lib/librte_pipeline/meson.build\nindex bea406848..d5f4d16e5 100644\n--- a/lib/librte_pipeline/meson.build\n+++ b/lib/librte_pipeline/meson.build\n@@ -9,5 +9,6 @@ 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_extern.h',)\n+\t'rte_swx_extern.h',\n+\t'rte_swx_ctl.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 c701f158d..b9e59bce2 100644\n--- a/lib/librte_pipeline/rte_pipeline_version.map\n+++ b/lib/librte_pipeline/rte_pipeline_version.map\n@@ -68,6 +68,10 @@ EXPERIMENTAL {\n \trte_swx_pipeline_packet_header_register;\n \trte_swx_pipeline_packet_metadata_register;\n \trte_swx_pipeline_action_config;\n+\trte_swx_pipeline_table_type_register;\n+\trte_swx_pipeline_table_config;\n \trte_swx_pipeline_build;\n \trte_swx_pipeline_free;\n+\trte_swx_pipeline_table_state_get;\n+\trte_swx_pipeline_table_state_set;\n };\ndiff --git a/lib/librte_pipeline/rte_swx_ctl.h b/lib/librte_pipeline/rte_swx_ctl.h\nnew file mode 100644\nindex 000000000..c824ab56f\n--- /dev/null\n+++ b/lib/librte_pipeline/rte_swx_ctl.h\n@@ -0,0 +1,85 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2020 Intel Corporation\n+ */\n+#ifndef __INCLUDE_RTE_SWX_CTL_H__\n+#define __INCLUDE_RTE_SWX_CTL_H__\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+/**\n+ * @file\n+ * RTE SWX Pipeline Control\n+ */\n+\n+#include <stddef.h>\n+#include <stdint.h>\n+\n+#include <rte_compat.h>\n+\n+#include \"rte_swx_table.h\"\n+\n+/*\n+ * Table Update API.\n+ */\n+\n+/** Table state. */\n+struct rte_swx_table_state {\n+\t/** Table object. */\n+\tvoid *obj;\n+\n+\t/** Action ID of the table default action. */\n+\tuint64_t default_action_id;\n+\n+\t/** Action data of the table default action. Ignored when the action\n+\t * data size is zero; otherwise, action data size bytes are meaningful.\n+\t */\n+\tuint8_t *default_action_data;\n+};\n+\n+/**\n+ * Pipeline table state get\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[out] table_state\n+ *   After successful execution, the *table_state* contains the pointer to the\n+ *   current pipeline table state, which is an array of *n_tables* elements,\n+ *   with array element i containing the state of the i-th pipeline table. The\n+ *   pipeline continues to own all the data structures directly or indirectly\n+ *   referenced by the *table_state* until the subsequent successful invocation\n+ *   of function *rte_swx_pipeline_table_state_set*.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument.\n+ */\n+__rte_experimental\n+int\n+rte_swx_pipeline_table_state_get(struct rte_swx_pipeline *p,\n+\t\t\t\t struct rte_swx_table_state **table_state);\n+\n+/**\n+ * Pipeline table state set\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[out] table_state\n+ *   After successful execution, the pipeline table state is updated to this\n+ *   *table_state*. The ownership of all the data structures directly or\n+ *   indirectly referenced by this *table_state* is passed from the caller to\n+ *   the pipeline.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument.\n+ */\n+__rte_experimental\n+int\n+rte_swx_pipeline_table_state_set(struct rte_swx_pipeline *p,\n+\t\t\t\t struct rte_swx_table_state *table_state);\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 678700050..eb5b327e8 100644\n--- a/lib/librte_pipeline/rte_swx_pipeline.c\n+++ b/lib/librte_pipeline/rte_swx_pipeline.c\n@@ -10,6 +10,7 @@\n #include <rte_common.h>\n \n #include \"rte_swx_pipeline.h\"\n+#include \"rte_swx_ctl.h\"\n \n #define CHECK(condition, err_code)                                             \\\n do {                                                                           \\\n@@ -197,6 +198,55 @@ struct action {\n \n TAILQ_HEAD(action_tailq, action);\n \n+/*\n+ * Table.\n+ */\n+struct table_type {\n+\tTAILQ_ENTRY(table_type) node;\n+\tchar name[RTE_SWX_NAME_SIZE];\n+\tenum rte_swx_table_match_type match_type;\n+\tstruct rte_swx_table_ops ops;\n+};\n+\n+TAILQ_HEAD(table_type_tailq, table_type);\n+\n+struct match_field {\n+\tenum rte_swx_table_match_type match_type;\n+\tstruct field *field;\n+};\n+\n+struct table {\n+\tTAILQ_ENTRY(table) node;\n+\tchar name[RTE_SWX_NAME_SIZE];\n+\tchar args[RTE_SWX_NAME_SIZE];\n+\tstruct table_type *type; /* NULL when n_fields == 0. */\n+\n+\t/* Match. */\n+\tstruct match_field *fields;\n+\tuint32_t n_fields;\n+\tint is_header; /* Only valid when n_fields > 0. */\n+\tstruct header *header; /* Only valid when n_fields > 0. */\n+\n+\t/* Action. */\n+\tstruct action **actions;\n+\tstruct action *default_action;\n+\tuint8_t *default_action_data;\n+\tuint32_t n_actions;\n+\tint default_action_is_const;\n+\tuint32_t action_data_size_max;\n+\n+\tuint32_t size;\n+\tuint32_t id;\n+};\n+\n+TAILQ_HEAD(table_tailq, table);\n+\n+struct table_runtime {\n+\trte_swx_table_lookup_t func;\n+\tvoid *mailbox;\n+\tuint8_t **key;\n+};\n+\n /*\n  * Pipeline.\n  */\n@@ -215,6 +265,12 @@ struct thread {\n \t/* Packet meta-data. */\n \tuint8_t *metadata;\n \n+\t/* Tables. */\n+\tstruct table_runtime *tables;\n+\tstruct rte_swx_table_state *table_state;\n+\tuint64_t action_id;\n+\tint hit; /* 0 = Miss, 1 = Hit. */\n+\n \t/* Extern objects and functions. */\n \tstruct extern_obj_runtime *extern_objs;\n \tstruct extern_func_runtime *extern_funcs;\n@@ -237,10 +293,13 @@ struct rte_swx_pipeline {\n \tstruct struct_type *metadata_st;\n \tuint32_t metadata_struct_id;\n \tstruct action_tailq actions;\n+\tstruct table_type_tailq table_types;\n+\tstruct table_tailq tables;\n \n \tstruct port_in_runtime *in;\n \tstruct port_out_runtime *out;\n \tstruct instruction **action_instructions;\n+\tstruct rte_swx_table_state *table_state;\n \tstruct thread threads[RTE_SWX_PIPELINE_THREADS_MAX];\n \n \tuint32_t n_structs;\n@@ -249,6 +308,7 @@ struct rte_swx_pipeline {\n \tuint32_t n_extern_objs;\n \tuint32_t n_extern_funcs;\n \tuint32_t n_actions;\n+\tuint32_t n_tables;\n \tuint32_t n_headers;\n \tint build_done;\n \tint numa_node;\n@@ -269,6 +329,21 @@ struct_type_find(struct rte_swx_pipeline *p, const char *name)\n \treturn NULL;\n }\n \n+static struct field *\n+struct_type_field_find(struct struct_type *st, const char *name)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < st->n_fields; i++) {\n+\t\tstruct field *f = &st->fields[i];\n+\n+\t\tif (strcmp(f->name, name) == 0)\n+\t\t\treturn f;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n int\n rte_swx_pipeline_struct_type_register(struct rte_swx_pipeline *p,\n \t\t\t\t      const char *name,\n@@ -1106,6 +1181,50 @@ header_find(struct rte_swx_pipeline *p, const char *name)\n \treturn NULL;\n }\n \n+static struct field *\n+header_field_parse(struct rte_swx_pipeline *p,\n+\t\t   const char *name,\n+\t\t   struct header **header)\n+{\n+\tstruct header *h;\n+\tstruct field *f;\n+\tchar *header_name, *field_name;\n+\n+\tif ((name[0] != 'h') || (name[1] != '.'))\n+\t\treturn NULL;\n+\n+\theader_name = strdup(&name[2]);\n+\tif (!header_name)\n+\t\treturn NULL;\n+\n+\tfield_name = strchr(header_name, '.');\n+\tif (!field_name) {\n+\t\tfree(header_name);\n+\t\treturn NULL;\n+\t}\n+\n+\t*field_name = 0;\n+\tfield_name++;\n+\n+\th = header_find(p, header_name);\n+\tif (!h) {\n+\t\tfree(header_name);\n+\t\treturn NULL;\n+\t}\n+\n+\tf = struct_type_field_find(h->st, field_name);\n+\tif (!f) {\n+\t\tfree(header_name);\n+\t\treturn NULL;\n+\t}\n+\n+\tif (header)\n+\t\t*header = h;\n+\n+\tfree(header_name);\n+\treturn f;\n+}\n+\n int\n rte_swx_pipeline_packet_header_register(struct rte_swx_pipeline *p,\n \t\t\t\t\tconst char *name,\n@@ -1229,6 +1348,18 @@ header_free(struct rte_swx_pipeline *p)\n /*\n  * Meta-data.\n  */\n+static struct field *\n+metadata_field_parse(struct rte_swx_pipeline *p, const char *name)\n+{\n+\tif (!p->metadata_st)\n+\t\treturn NULL;\n+\n+\tif (name[0] != 'm' || name[1] != '.')\n+\t\treturn NULL;\n+\n+\treturn struct_type_field_find(p->metadata_st, &name[2]);\n+}\n+\n int\n rte_swx_pipeline_packet_metadata_register(struct rte_swx_pipeline *p,\n \t\t\t\t\t  const char *struct_type_name)\n@@ -1407,6 +1538,536 @@ action_free(struct rte_swx_pipeline *p)\n \t}\n }\n \n+/*\n+ * Table.\n+ */\n+static struct table_type *\n+table_type_find(struct rte_swx_pipeline *p, const char *name)\n+{\n+\tstruct table_type *elem;\n+\n+\tTAILQ_FOREACH(elem, &p->table_types, node)\n+\t\tif (strcmp(elem->name, name) == 0)\n+\t\t\treturn elem;\n+\n+\treturn NULL;\n+}\n+\n+static struct table_type *\n+table_type_resolve(struct rte_swx_pipeline *p,\n+\t\t   const char *recommended_type_name,\n+\t\t   enum rte_swx_table_match_type match_type)\n+{\n+\tstruct table_type *elem;\n+\n+\t/* Only consider the recommended type if the match type is correct. */\n+\tif (recommended_type_name)\n+\t\tTAILQ_FOREACH(elem, &p->table_types, node)\n+\t\t\tif (!strcmp(elem->name, recommended_type_name) &&\n+\t\t\t    (elem->match_type == match_type))\n+\t\t\t\treturn elem;\n+\n+\t/* Ignore the recommended type and get the first element with this match\n+\t * type.\n+\t */\n+\tTAILQ_FOREACH(elem, &p->table_types, node)\n+\t\tif (elem->match_type == match_type)\n+\t\t\treturn elem;\n+\n+\treturn NULL;\n+}\n+\n+static struct table *\n+table_find(struct rte_swx_pipeline *p, const char *name)\n+{\n+\tstruct table *elem;\n+\n+\tTAILQ_FOREACH(elem, &p->tables, node)\n+\t\tif (strcmp(elem->name, name) == 0)\n+\t\t\treturn elem;\n+\n+\treturn NULL;\n+}\n+\n+static struct table *\n+table_find_by_id(struct rte_swx_pipeline *p, uint32_t id)\n+{\n+\tstruct table *table = NULL;\n+\n+\tTAILQ_FOREACH(table, &p->tables, node)\n+\t\tif (table->id == id)\n+\t\t\treturn table;\n+\n+\treturn NULL;\n+}\n+\n+int\n+rte_swx_pipeline_table_type_register(struct rte_swx_pipeline *p,\n+\t\t\t\t     const char *name,\n+\t\t\t\t     enum rte_swx_table_match_type match_type,\n+\t\t\t\t     struct rte_swx_table_ops *ops)\n+{\n+\tstruct table_type *elem;\n+\n+\tCHECK(p, EINVAL);\n+\n+\tCHECK_NAME(name, EINVAL);\n+\tCHECK(!table_type_find(p, name), EEXIST);\n+\n+\tCHECK(ops, EINVAL);\n+\tCHECK(ops->create, EINVAL);\n+\tCHECK(ops->lkp, EINVAL);\n+\tCHECK(ops->free, EINVAL);\n+\n+\t/* Node allocation. */\n+\telem = calloc(1, sizeof(struct table_type));\n+\tCHECK(elem, ENOMEM);\n+\n+\t/* Node initialization. */\n+\tstrcpy(elem->name, name);\n+\telem->match_type = match_type;\n+\tmemcpy(&elem->ops, ops, sizeof(*ops));\n+\n+\t/* Node add to tailq. */\n+\tTAILQ_INSERT_TAIL(&p->table_types, elem, node);\n+\n+\treturn 0;\n+}\n+\n+static enum rte_swx_table_match_type\n+table_match_type_resolve(struct rte_swx_match_field_params *fields,\n+\t\t\t uint32_t n_fields)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < n_fields; i++)\n+\t\tif (fields[i].match_type != RTE_SWX_TABLE_MATCH_EXACT)\n+\t\t\tbreak;\n+\n+\tif (i == n_fields)\n+\t\treturn RTE_SWX_TABLE_MATCH_EXACT;\n+\n+\tif ((i == n_fields - 1) &&\n+\t    (fields[i].match_type == RTE_SWX_TABLE_MATCH_LPM))\n+\t\treturn RTE_SWX_TABLE_MATCH_LPM;\n+\n+\treturn RTE_SWX_TABLE_MATCH_WILDCARD;\n+}\n+\n+int\n+rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,\n+\t\t\t      const char *name,\n+\t\t\t      struct rte_swx_pipeline_table_params *params,\n+\t\t\t      const char *recommended_table_type_name,\n+\t\t\t      const char *args,\n+\t\t\t      uint32_t size)\n+{\n+\tstruct table_type *type;\n+\tstruct table *t;\n+\tstruct action *default_action;\n+\tstruct header *header = NULL;\n+\tint is_header = 0;\n+\tuint32_t offset_prev = 0, action_data_size_max = 0, i;\n+\n+\tCHECK(p, EINVAL);\n+\n+\tCHECK_NAME(name, EINVAL);\n+\tCHECK(!table_find(p, name), EEXIST);\n+\n+\tCHECK(params, EINVAL);\n+\n+\t/* Match checks. */\n+\tCHECK(!params->n_fields || params->fields, EINVAL);\n+\tfor (i = 0; i < params->n_fields; i++) {\n+\t\tstruct rte_swx_match_field_params *field = &params->fields[i];\n+\t\tstruct header *h;\n+\t\tstruct field *hf, *mf;\n+\t\tuint32_t offset;\n+\n+\t\tCHECK_NAME(field->name, EINVAL);\n+\n+\t\thf = header_field_parse(p, field->name, &h);\n+\t\tmf = metadata_field_parse(p, field->name);\n+\t\tCHECK(hf || mf, EINVAL);\n+\n+\t\toffset = hf ? hf->offset : mf->offset;\n+\n+\t\tif (i == 0) {\n+\t\t\tis_header = hf ? 1 : 0;\n+\t\t\theader = hf ? h : NULL;\n+\t\t\toffset_prev = offset;\n+\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tCHECK((is_header && hf && (h->id == header->id)) ||\n+\t\t      (!is_header && mf), EINVAL);\n+\n+\t\tCHECK(offset > offset_prev, EINVAL);\n+\t\toffset_prev = offset;\n+\t}\n+\n+\t/* Action checks. */\n+\tCHECK(params->n_actions, EINVAL);\n+\tCHECK(params->action_names, EINVAL);\n+\tfor (i = 0; i < params->n_actions; i++) {\n+\t\tconst char *action_name = params->action_names[i];\n+\t\tstruct action *a;\n+\t\tuint32_t action_data_size;\n+\n+\t\tCHECK(action_name, EINVAL);\n+\n+\t\ta = action_find(p, action_name);\n+\t\tCHECK(a, EINVAL);\n+\n+\t\taction_data_size = a->st ? a->st->n_bits / 8 : 0;\n+\t\tif (action_data_size > action_data_size_max)\n+\t\t\taction_data_size_max = action_data_size;\n+\t}\n+\n+\tCHECK(params->default_action_name, EINVAL);\n+\tfor (i = 0; i < p->n_actions; i++)\n+\t\tif (!strcmp(params->action_names[i],\n+\t\t\t    params->default_action_name))\n+\t\t\tbreak;\n+\tCHECK(i < params->n_actions, EINVAL);\n+\tdefault_action = action_find(p, params->default_action_name);\n+\tCHECK((default_action->st && params->default_action_data) ||\n+\t      !params->default_action_data, EINVAL);\n+\n+\t/* Table type checks. */\n+\tif (params->n_fields) {\n+\t\tenum rte_swx_table_match_type match_type;\n+\n+\t\tmatch_type = table_match_type_resolve(params->fields,\n+\t\t\t\t\t\t      params->n_fields);\n+\t\ttype = table_type_resolve(p,\n+\t\t\t\t\t  recommended_table_type_name,\n+\t\t\t\t\t  match_type);\n+\t\tCHECK(type, EINVAL);\n+\t} else {\n+\t\ttype = NULL;\n+\t}\n+\n+\t/* Memory allocation. */\n+\tt = calloc(1, sizeof(struct table));\n+\tCHECK(t, ENOMEM);\n+\n+\tt->fields = calloc(params->n_fields, sizeof(struct match_field));\n+\tif (!t->fields) {\n+\t\tfree(t);\n+\t\tCHECK(0, ENOMEM);\n+\t}\n+\n+\tt->actions = calloc(params->n_actions, sizeof(struct action *));\n+\tif (!t->actions) {\n+\t\tfree(t->fields);\n+\t\tfree(t);\n+\t\tCHECK(0, ENOMEM);\n+\t}\n+\n+\tif (action_data_size_max) {\n+\t\tt->default_action_data = calloc(1, action_data_size_max);\n+\t\tif (!t->default_action_data) {\n+\t\t\tfree(t->actions);\n+\t\t\tfree(t->fields);\n+\t\t\tfree(t);\n+\t\t\tCHECK(0, ENOMEM);\n+\t\t}\n+\t}\n+\n+\t/* Node initialization. */\n+\tstrcpy(t->name, name);\n+\tif (args && args[0])\n+\t\tstrcpy(t->args, args);\n+\tt->type = type;\n+\n+\tfor (i = 0; i < params->n_fields; i++) {\n+\t\tstruct rte_swx_match_field_params *field = &params->fields[i];\n+\t\tstruct match_field *f = &t->fields[i];\n+\n+\t\tf->match_type = field->match_type;\n+\t\tf->field = is_header ?\n+\t\t\theader_field_parse(p, field->name, NULL) :\n+\t\t\tmetadata_field_parse(p, field->name);\n+\t}\n+\tt->n_fields = params->n_fields;\n+\tt->is_header = is_header;\n+\tt->header = header;\n+\n+\tfor (i = 0; i < params->n_actions; i++)\n+\t\tt->actions[i] = action_find(p, params->action_names[i]);\n+\tt->default_action = default_action;\n+\tif (default_action->st)\n+\t\tmemcpy(t->default_action_data,\n+\t\t       params->default_action_data,\n+\t\t       default_action->st->n_bits / 8);\n+\tt->n_actions = params->n_actions;\n+\tt->default_action_is_const = params->default_action_is_const;\n+\tt->action_data_size_max = action_data_size_max;\n+\n+\tt->size = size;\n+\tt->id = p->n_tables;\n+\n+\t/* Node add to tailq. */\n+\tTAILQ_INSERT_TAIL(&p->tables, t, node);\n+\tp->n_tables++;\n+\n+\treturn 0;\n+}\n+\n+static struct rte_swx_table_params *\n+table_params_get(struct table *table)\n+{\n+\tstruct rte_swx_table_params *params;\n+\tstruct field *first, *last;\n+\tuint8_t *key_mask;\n+\tuint32_t key_size, key_offset, action_data_size, i;\n+\n+\t/* Memory allocation. */\n+\tparams = calloc(1, sizeof(struct rte_swx_table_params));\n+\tif (!params)\n+\t\treturn NULL;\n+\n+\t/* Key offset and size. */\n+\tfirst = table->fields[0].field;\n+\tlast = table->fields[table->n_fields - 1].field;\n+\tkey_offset = first->offset / 8;\n+\tkey_size = (last->offset + last->n_bits - first->offset) / 8;\n+\n+\t/* Memory allocation. */\n+\tkey_mask = calloc(1, key_size);\n+\tif (!key_mask) {\n+\t\tfree(params);\n+\t\treturn NULL;\n+\t}\n+\n+\t/* Key mask. */\n+\tfor (i = 0; i < table->n_fields; i++) {\n+\t\tstruct field *f = table->fields[i].field;\n+\t\tuint32_t start = (f->offset - first->offset) / 8;\n+\t\tsize_t size = f->n_bits / 8;\n+\n+\t\tmemset(&key_mask[start], 0xFF, size);\n+\t}\n+\n+\t/* Action data size. */\n+\taction_data_size = 0;\n+\tfor (i = 0; i < table->n_actions; i++) {\n+\t\tstruct action *action = table->actions[i];\n+\t\tuint32_t ads = action->st ? action->st->n_bits / 8 : 0;\n+\n+\t\tif (ads > action_data_size)\n+\t\t\taction_data_size = ads;\n+\t}\n+\n+\t/* Fill in. */\n+\tparams->match_type = table->type->match_type;\n+\tparams->key_size = key_size;\n+\tparams->key_offset = key_offset;\n+\tparams->key_mask0 = key_mask;\n+\tparams->action_data_size = action_data_size;\n+\tparams->n_keys_max = table->size;\n+\n+\treturn params;\n+}\n+\n+static void\n+table_params_free(struct rte_swx_table_params *params)\n+{\n+\tif (!params)\n+\t\treturn;\n+\n+\tfree(params->key_mask0);\n+\tfree(params);\n+}\n+\n+static int\n+table_state_build(struct rte_swx_pipeline *p)\n+{\n+\tstruct table *table;\n+\n+\tp->table_state = calloc(p->n_tables,\n+\t\t\t\tsizeof(struct rte_swx_table_state));\n+\tCHECK(p->table_state, ENOMEM);\n+\n+\tTAILQ_FOREACH(table, &p->tables, node) {\n+\t\tstruct rte_swx_table_state *ts = &p->table_state[table->id];\n+\n+\t\tif (table->type) {\n+\t\t\tstruct rte_swx_table_params *params;\n+\n+\t\t\t/* ts->obj. */\n+\t\t\tparams = table_params_get(table);\n+\t\t\tCHECK(params, ENOMEM);\n+\n+\t\t\tts->obj = table->type->ops.create(params,\n+\t\t\t\tNULL,\n+\t\t\t\ttable->args,\n+\t\t\t\tp->numa_node);\n+\n+\t\t\ttable_params_free(params);\n+\t\t\tCHECK(ts->obj, ENODEV);\n+\t\t}\n+\n+\t\t/* ts->default_action_data. */\n+\t\tif (table->action_data_size_max) {\n+\t\t\tts->default_action_data =\n+\t\t\t\tmalloc(table->action_data_size_max);\n+\t\t\tCHECK(ts->default_action_data, ENOMEM);\n+\n+\t\t\tmemcpy(ts->default_action_data,\n+\t\t\t       table->default_action_data,\n+\t\t\t       table->action_data_size_max);\n+\t\t}\n+\n+\t\t/* ts->default_action_id. */\n+\t\tts->default_action_id = table->default_action->id;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void\n+table_state_build_free(struct rte_swx_pipeline *p)\n+{\n+\tuint32_t i;\n+\n+\tif (!p->table_state)\n+\t\treturn;\n+\n+\tfor (i = 0; i < p->n_tables; i++) {\n+\t\tstruct rte_swx_table_state *ts = &p->table_state[i];\n+\t\tstruct table *table = table_find_by_id(p, i);\n+\n+\t\t/* ts->obj. */\n+\t\tif (table->type && ts->obj)\n+\t\t\ttable->type->ops.free(ts->obj);\n+\n+\t\t/* ts->default_action_data. */\n+\t\tfree(ts->default_action_data);\n+\t}\n+\n+\tfree(p->table_state);\n+\tp->table_state = NULL;\n+}\n+\n+static void\n+table_state_free(struct rte_swx_pipeline *p)\n+{\n+\ttable_state_build_free(p);\n+}\n+\n+static int\n+table_stub_lkp(void *table __rte_unused,\n+\t       void *mailbox __rte_unused,\n+\t       uint8_t **key __rte_unused,\n+\t       uint64_t *action_id __rte_unused,\n+\t       uint8_t **action_data __rte_unused,\n+\t       int *hit)\n+{\n+\t*hit = 0;\n+\treturn 1; /* DONE. */\n+}\n+\n+static int\n+table_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 table *table;\n+\n+\t\tt->tables = calloc(p->n_tables, sizeof(struct table_runtime));\n+\t\tCHECK(t->tables, ENOMEM);\n+\n+\t\tTAILQ_FOREACH(table, &p->tables, node) {\n+\t\t\tstruct table_runtime *r = &t->tables[table->id];\n+\n+\t\t\tif (table->type) {\n+\t\t\t\tuint64_t size;\n+\n+\t\t\t\tsize = table->type->ops.mailbox_size_get();\n+\n+\t\t\t\t/* r->func. */\n+\t\t\t\tr->func = table->type->ops.lkp;\n+\n+\t\t\t\t/* r->mailbox. */\n+\t\t\t\tif (size) {\n+\t\t\t\t\tr->mailbox = calloc(1, size);\n+\t\t\t\t\tCHECK(r->mailbox, ENOMEM);\n+\t\t\t\t}\n+\n+\t\t\t\t/* r->key. */\n+\t\t\t\tr->key = table->is_header ?\n+\t\t\t\t\t&t->structs[table->header->struct_id] :\n+\t\t\t\t\t&t->structs[p->metadata_struct_id];\n+\t\t\t} else {\n+\t\t\t\tr->func = table_stub_lkp;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void\n+table_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->tables)\n+\t\t\tcontinue;\n+\n+\t\tfor (j = 0; j < p->n_tables; j++) {\n+\t\t\tstruct table_runtime *r = &t->tables[j];\n+\n+\t\t\tfree(r->mailbox);\n+\t\t}\n+\n+\t\tfree(t->tables);\n+\t\tt->tables = NULL;\n+\t}\n+}\n+\n+static void\n+table_free(struct rte_swx_pipeline *p)\n+{\n+\ttable_build_free(p);\n+\n+\t/* Tables. */\n+\tfor ( ; ; ) {\n+\t\tstruct table *elem;\n+\n+\t\telem = TAILQ_FIRST(&p->tables);\n+\t\tif (!elem)\n+\t\t\tbreak;\n+\n+\t\tTAILQ_REMOVE(&p->tables, elem, node);\n+\t\tfree(elem->fields);\n+\t\tfree(elem->actions);\n+\t\tfree(elem->default_action_data);\n+\t\tfree(elem);\n+\t}\n+\n+\t/* Table types. */\n+\tfor ( ; ; ) {\n+\t\tstruct table_type *elem;\n+\n+\t\telem = TAILQ_FIRST(&p->table_types);\n+\t\tif (!elem)\n+\t\t\tbreak;\n+\n+\t\tTAILQ_REMOVE(&p->table_types, elem, node);\n+\t\tfree(elem);\n+\t}\n+}\n+\n /*\n  * Pipeline.\n  */\n@@ -1433,6 +2094,8 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)\n \tTAILQ_INIT(&pipeline->extern_funcs);\n \tTAILQ_INIT(&pipeline->headers);\n \tTAILQ_INIT(&pipeline->actions);\n+\tTAILQ_INIT(&pipeline->table_types);\n+\tTAILQ_INIT(&pipeline->tables);\n \n \tpipeline->n_structs = 1; /* Struct 0 is reserved for action_data. */\n \tpipeline->numa_node = numa_node;\n@@ -1447,6 +2110,8 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)\n \tif (!p)\n \t\treturn;\n \n+\ttable_state_free(p);\n+\ttable_free(p);\n \taction_free(p);\n \tmetadata_free(p);\n \theader_free(p);\n@@ -1499,10 +2164,20 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)\n \tif (status)\n \t\tgoto error;\n \n+\tstatus = table_build(p);\n+\tif (status)\n+\t\tgoto error;\n+\n+\tstatus = table_state_build(p);\n+\tif (status)\n+\t\tgoto error;\n+\n \tp->build_done = 1;\n \treturn 0;\n \n error:\n+\ttable_state_build_free(p);\n+\ttable_build_free(p);\n \taction_build_free(p);\n \tmetadata_build_free(p);\n \theader_build_free(p);\n@@ -1514,3 +2189,28 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)\n \n \treturn status;\n }\n+\n+/*\n+ * Control.\n+ */\n+int\n+rte_swx_pipeline_table_state_get(struct rte_swx_pipeline *p,\n+\t\t\t\t struct rte_swx_table_state **table_state)\n+{\n+\tif (!p || !table_state || !p->build_done)\n+\t\treturn -EINVAL;\n+\n+\t*table_state = p->table_state;\n+\treturn 0;\n+}\n+\n+int\n+rte_swx_pipeline_table_state_set(struct rte_swx_pipeline *p,\n+\t\t\t\t struct rte_swx_table_state *table_state)\n+{\n+\tif (!p || !table_state || !p->build_done)\n+\t\treturn -EINVAL;\n+\n+\tp->table_state = table_state;\n+\treturn 0;\n+}\ndiff --git a/lib/librte_pipeline/rte_swx_pipeline.h b/lib/librte_pipeline/rte_swx_pipeline.h\nindex 1b20293cb..d7e3ba1ec 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_table.h\"\n #include \"rte_swx_extern.h\"\n \n /** Name size. */\n@@ -377,6 +378,123 @@ rte_swx_pipeline_action_config(struct rte_swx_pipeline *p,\n \t\t\t       const char **instructions,\n \t\t\t       uint32_t n_instructions);\n \n+/*\n+ * Pipeline table\n+ */\n+\n+/**\n+ * Pipeline table type register\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[in] name\n+ *   Table type name.\n+ * @param[in] match type\n+ *   Match type implemented by the new table type.\n+ * @param[in] ops\n+ *   Table type operations.\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: Table type with this name already exists.\n+ */\n+__rte_experimental\n+int\n+rte_swx_pipeline_table_type_register(struct rte_swx_pipeline *p,\n+\t\t\t\t     const char *name,\n+\t\t\t\t     enum rte_swx_table_match_type match_type,\n+\t\t\t\t     struct rte_swx_table_ops *ops);\n+\n+/** Match field parameters. */\n+struct rte_swx_match_field_params {\n+\t/** Match field name. Must be either a field of one of the registered\n+\t * packet headers (\"h.header.field\") or a field of the registered\n+\t * meta-data (\"m.field\").\n+\t */\n+\tconst char *name;\n+\n+\t/** Match type of the field. */\n+\tenum rte_swx_table_match_type match_type;\n+};\n+\n+/** Pipeline table parameters. */\n+struct rte_swx_pipeline_table_params {\n+\t/** The set of match fields for the current table.\n+\t * Restriction: All the match fields of the current table need to be\n+\t * part of the same struct, i.e. either all the match fields are part of\n+\t * the same header or all the match fields are part of the meta-data.\n+\t */\n+\tstruct rte_swx_match_field_params *fields;\n+\n+\t/** The number of match fields for the current table. If set to zero, no\n+\t * \"regular\" entries (i.e. entries other than the default entry) can be\n+\t * added to the current table and the match process always results in\n+\t * lookup miss.\n+\t */\n+\tuint32_t n_fields;\n+\n+\t/** The set of actions for the current table. */\n+\tconst char **action_names;\n+\n+\t/** The number of actions for the current table. Must be at least one.\n+\t */\n+\tuint32_t n_actions;\n+\n+\t/** The default table action that gets executed on lookup miss. Must be\n+\t * one of the table actions included in the *action_names*.\n+\t */\n+\tconst char *default_action_name;\n+\n+\t/** Default action data. The size of this array is the action data size\n+\t * of the default action. Must be NULL if the default action data size\n+\t * is zero.\n+\t */\n+\tuint8_t *default_action_data;\n+\n+\t/** If non-zero (true), then the default action of the current table\n+\t * cannot be changed. If zero (false), then the default action can be\n+\t * changed in the future with another action from the *action_names*\n+\t * list.\n+\t */\n+\tint default_action_is_const;\n+};\n+\n+/**\n+ * Pipeline table configure\n+ *\n+ * @param[out] p\n+ *   Pipeline handle.\n+ * @param[in] name\n+ *   Table name.\n+ * @param[in] params\n+ *   Table parameters.\n+ * @param[in] recommended_table_type_name\n+ *   Recommended table type. Typically set to NULL. Useful as guidance when\n+ *   there are multiple table types registered for the match type of the table,\n+ *   as determined from the table match fields specification. Silently ignored\n+ *   if the recommended table type does not exist or it serves a different match\n+ *   type.\n+ * @param[in] args\n+ *   Table creation arguments.\n+ * @param[in] size\n+ *   Guideline on maximum number of table entries.\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: Table with this name already exists;\n+ *   -ENODEV: Table creation error.\n+ */\n+__rte_experimental\n+int\n+rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,\n+\t\t\t      const char *name,\n+\t\t\t      struct rte_swx_pipeline_table_params *params,\n+\t\t\t      const char *recommended_table_type_name,\n+\t\t\t      const char *args,\n+\t\t\t      uint32_t size);\n+\n /**\n  * Pipeline build\n  *\ndiff --git a/lib/librte_table/meson.build b/lib/librte_table/meson.build\nindex 71d134768..b9d4fe3dc 100644\n--- a/lib/librte_table/meson.build\n+++ b/lib/librte_table/meson.build\n@@ -22,7 +22,8 @@ headers = files('rte_table.h',\n \t\t'rte_table_hash_func_arm64.h',\n \t\t'rte_lru.h',\n \t\t'rte_table_array.h',\n-\t\t'rte_table_stub.h')\n+\t\t'rte_table_stub.h',\n+\t\t'rte_swx_table.h',)\n deps += ['mbuf', 'port', 'lpm', 'hash', 'acl']\n \n if arch_subdir == 'x86'\ndiff --git a/lib/librte_table/rte_swx_table.h b/lib/librte_table/rte_swx_table.h\nnew file mode 100644\nindex 000000000..c5c202723\n--- /dev/null\n+++ b/lib/librte_table/rte_swx_table.h\n@@ -0,0 +1,295 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2020 Intel Corporation\n+ */\n+#ifndef __INCLUDE_RTE_SWX_TABLE_H__\n+#define __INCLUDE_RTE_SWX_TABLE_H__\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+/**\n+ * @file\n+ * RTE SWX Table\n+ *\n+ * Table interface.\n+ */\n+\n+#include <stdint.h>\n+#include <sys/queue.h>\n+\n+/** Match type. */\n+enum rte_swx_table_match_type {\n+\t/** Wildcard Match (WM). */\n+\tRTE_SWX_TABLE_MATCH_WILDCARD,\n+\n+\t/** Longest Prefix Match (LPM). */\n+\tRTE_SWX_TABLE_MATCH_LPM,\n+\n+\t/** Exact Match (EM). */\n+\tRTE_SWX_TABLE_MATCH_EXACT,\n+};\n+\n+/** Table creation parameters. */\n+struct rte_swx_table_params {\n+\t/** Table match type. */\n+\tenum rte_swx_table_match_type match_type;\n+\n+\t/** Key size in bytes. */\n+\tuint32_t key_size;\n+\n+\t/** Offset of the first byte of the key within the key buffer. */\n+\tuint32_t key_offset;\n+\n+\t/** Mask of *key_size* bytes logically laid over the bytes at positions\n+\t * *key_offset* .. (*key_offset* + *key_size* - 1) of the key buffer in\n+\t * order to specify which bits from the key buffer are part of the key\n+\t * and which ones are not. A bit value of 1 in the *key_mask0* means the\n+\t * respective bit in the key buffer is part of the key, while a bit\n+\t * value of 0 means the opposite. A NULL value means that all the bits\n+\t * are part of the key, i.e. the *key_mask0* is an all-ones mask.\n+\t */\n+\tuint8_t *key_mask0;\n+\n+\t/** Maximum size (in bytes) of the action data. The data stored in the\n+\t * table for each entry is equal to *action_data_size* plus 8 bytes,\n+\t * which are used to store the action ID.\n+\t */\n+\tuint32_t action_data_size;\n+\n+\t/** Maximum number of keys to be stored in the table together with their\n+\t * associated data.\n+\t */\n+\tuint32_t n_keys_max;\n+};\n+\n+/** Table entry. */\n+struct rte_swx_table_entry {\n+\t/** Used to faciliate the addition of the current table entry to a\n+\t * linked list.\n+\t */\n+\tTAILQ_ENTRY(rte_swx_table_entry) node;\n+\n+\t/** Key value for the current entry. Array of *key_size* bytes or NULL\n+\t * if the *key_size* for the current table is 0.\n+\t */\n+\tuint8_t *key;\n+\n+\t/** Key mask for the current entry. Array of *key_size* bytes that is\n+\t * logically and'ed with *key_mask0* of the current table. A NULL value\n+\t * means that all the key bits already enabled by *key_mask0* are part\n+\t * of the key of the current entry.\n+\t */\n+\tuint8_t *key_mask;\n+\n+\t/** Placeholder for a possible compressed version of the *key* and\n+\t * *key_mask* of the current entry. Typically a hash signature, its main\n+\t * purpose is to the linked list search operation. Should be ignored by\n+\t * the API functions below.\n+\t */\n+\tuint64_t key_signature;\n+\n+\t/** Action ID for the current entry. */\n+\tuint64_t action_id;\n+\n+\t/** Action data for the current entry. Its size is defined by the action\n+\t * specified by the *action_id*. It must be NULL when the action data\n+\t * size of the *action_id* action is NULL. It must never exceed the\n+\t * *action_data_size* of the table.\n+\t */\n+\tuint8_t *action_data;\n+};\n+\n+/** List of table entries. */\n+TAILQ_HEAD(rte_swx_table_entry_list, rte_swx_table_entry);\n+\n+/**\n+ * Table memory footprint get\n+ *\n+ * @param[in] params\n+ *   Table create parameters.\n+ * @param[in] entries\n+ *   Table entries.\n+ * @param[in] args\n+ *   Any additional table create arguments. It may be NULL.\n+ * @return\n+ *   Table memory footprint in bytes, if successful, or zero, on error.\n+ */\n+typedef uint64_t\n+(*rte_swx_table_footprint_get_t)(struct rte_swx_table_params *params,\n+\t\t\t\t struct rte_swx_table_entry_list *entries,\n+\t\t\t\t const char *args);\n+\n+/**\n+ * Table mailbox size get\n+ *\n+ * The mailbox is used to store the context of a lookup operation that is in\n+ * progress and it is passed as a parameter to the lookup operation. This allows\n+ * for multiple concurrent lookup operations into the same table.\n+ *\n+ * @param[in] params\n+ *   Table creation parameters.\n+ * @param[in] entries\n+ *   Entries to be added to the table at creation time.\n+ * @param[in] args\n+ *   Any additional table create arguments. It may be NULL.\n+ * @return\n+ *   Table memory footprint in bytes, on success, or zero, on error.\n+ */\n+typedef uint64_t\n+(*rte_swx_table_mailbox_size_get_t)(void);\n+\n+/**\n+ * Table create\n+ *\n+ * @param[in] params\n+ *   Table creation parameters.\n+ * @param[in] entries\n+ *   Entries to be added to the table at creation time.\n+ * @param[in] args\n+ *   Any additional table create arguments. It may be NULL.\n+ * @param[in] numa_node\n+ *   Non-Uniform Memory Access (NUMA) node.\n+ * @return\n+ *   Table handle, on success, or NULL, on error.\n+ */\n+typedef void *\n+(*rte_swx_table_create_t)(struct rte_swx_table_params *params,\n+\t\t\t  struct rte_swx_table_entry_list *entries,\n+\t\t\t  const char *args,\n+\t\t\t  int numa_node);\n+\n+/**\n+ * Table entry add\n+ *\n+ * @param[in] table\n+ *   Table handle.\n+ * @param[in] entry\n+ *   Entry to be added to the table.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid table handle, entry or entry field;\n+ *   -ENOSPC: Table full.\n+ */\n+typedef int\n+(*rte_swx_table_add_t)(void *table,\n+\t\t       struct rte_swx_table_entry *entry);\n+\n+/**\n+ * Table entry delete\n+ *\n+ * @param[in] table\n+ *   Table handle.\n+ * @param[in] entry\n+ *   Entry to be deleted from the table. The entry *action_id* and *action_data*\n+ *   fields are ignored.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid table handle, entry or entry field;\n+ *   -ENOSPC: Table full.\n+ */\n+typedef int\n+(*rte_swx_table_delete_t)(void *table,\n+\t\t\t  struct rte_swx_table_entry *entry);\n+\n+/**\n+ * Table lookup\n+ *\n+ * The table lookup operation seaches a given key in the table and upon its\n+ * completion it returns an indication of whether the key is found in the table\n+ * (lookup hit) or not (lookup miss). In case of lookup hit, the action_id and\n+ * the action_data associated with the key are also returned.\n+ *\n+ * Multiple invocations of this function may be required in order to complete a\n+ * single table lookup operation for a given table and a given lookup key. The\n+ * completion of the table lookup operation is flagged by a return value of 1;\n+ * in case of a return value of 0, the function must be invoked again with\n+ * exactly the same arguments.\n+ *\n+ * The mailbox argument is used to store the context of an on-going table lookup\n+ * operation. The mailbox mechanism allows for multiple concurrent table lookup\n+ * operations into the same table.\n+ *\n+ * The typical reason an implementation may choose to split the table lookup\n+ * operation into multiple steps is to hide the latency of the inherrent memory\n+ * read operations: before a read operation with the source data likely not in\n+ * the CPU cache, the source data prefetch is issued and the table lookup\n+ * operation is postponed in favor of some other unrelated work, which the CPU\n+ * executes in parallel with the source data being fetched into the CPU cache;\n+ * later on, the table lookup operation is resumed, this time with the source\n+ * data likely to be read from the CPU cache with no CPU pipeline stall, which\n+ * significantly improves the table lookup performance.\n+ *\n+ * @param[in] table\n+ *   Table handle.\n+ * @param[in] mailbox\n+ *   Mailbox for the current table lookup operation.\n+ * @param[in] key\n+ *   Lookup key. Its size mult be equal to the table *key_size*. If the latter\n+ *   is zero, then the lookup key must be NULL.\n+ * @param[out] action_id\n+ *   ID of the action associated with the *key*. Must point to a valid 64-bit\n+ *   variable. Only valid when the function returns 1 and *hit* is set to true.\n+ * @param[out] action_data\n+ *   Action data for the *action_id* action. Must point to a valid array of\n+ *   table *action_data_size* bytes. Only valid when the function returns 1 and\n+ *   *hit* is set to true.\n+ * @param[out] hit\n+ *   Only valid when the function returns 1. Set to non-zero (true) on table\n+ *   lookup hit and to zero (false) on table lookup miss.\n+ * @return\n+ *   0 when the table lookup operation is not yet completed, and 1 when the\n+ *   table lookup operation is completed. No other return values are allowed.\n+ */\n+typedef int\n+(*rte_swx_table_lookup_t)(void *table,\n+\t\t\t  void *mailbox,\n+\t\t\t  uint8_t **key,\n+\t\t\t  uint64_t *action_id,\n+\t\t\t  uint8_t **action_data,\n+\t\t\t  int *hit);\n+\n+/**\n+ * Table free\n+ *\n+ * @param[in] table\n+ *   Table handle.\n+ */\n+typedef void\n+(*rte_swx_table_free_t)(void *table);\n+\n+/** Table operations.  */\n+struct rte_swx_table_ops {\n+\t/** Table memory footprint get. Set to NULL when not supported. */\n+\trte_swx_table_footprint_get_t footprint_get;\n+\n+\t/** Table mailbox size get. When NULL, the mailbox size is 0. */\n+\trte_swx_table_mailbox_size_get_t mailbox_size_get;\n+\n+\t/** Table create. Must be non-NULL. */\n+\trte_swx_table_create_t create;\n+\n+\t/** Incremental table entry add. Set to NULL when not supported, in\n+\t * which case the existing table has to be destroyed and a new table\n+\t * built from scratch with the new entry included.\n+\t */\n+\trte_swx_table_add_t add;\n+\n+\t/** Incremental table entry delete. Set to NULL when not supported, in\n+\t * which case the existing table has to be destroyed and a new table\n+\t * built from scratch with the entry excluded.\n+\t */\n+\trte_swx_table_delete_t del;\n+\n+\t/** Table lookup. Must be non-NULL. */\n+\trte_swx_table_lookup_t lkp;\n+\n+\t/** Table free. Must be non-NULL. */\n+\trte_swx_table_free_t free;\n+};\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif\n",
    "prefixes": [
        "v3",
        "07/41"
    ]
}