get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 76964,
    "url": "http://patches.dpdk.org/api/patches/76964/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20200908201830.74206-32-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-32-cristian.dumitrescu@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20200908201830.74206-32-cristian.dumitrescu@intel.com",
    "date": "2020-09-08T20:18:20",
    "name": "[v3,31/41] pipeline: add SWX table update high level API",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "9372d919ad80ea55a131fa482fd79c6da32c1a2a",
    "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-32-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/76964/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/76964/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 65539A04B1;\n\tTue,  8 Sep 2020 22:24:01 +0200 (CEST)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 05EBC1C23E;\n\tTue,  8 Sep 2020 22:19:41 +0200 (CEST)",
            "from mga02.intel.com (mga02.intel.com [134.134.136.20])\n by dpdk.org (Postfix) with ESMTP id DE4031C119\n for <dev@dpdk.org>; Tue,  8 Sep 2020 22:19:04 +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:02 -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:01 -0700"
        ],
        "IronPort-SDR": [
            "\n LI9Cx+KnPL/RwA6IUzEkSvJuOoovySo1uoI5L5+iehknbe8sF4uN/ld1gER2I3a2A+PvgOPzat\n tzwHWqGarOtg==",
            "\n +yiXPaOnLURyg2530Z6H9y3lAl5NhE1CiPffiPUK9GQWIvricc1P+25CcR6yKtkP+n0z+dOzXG\n f3clvKTY7NFw=="
        ],
        "X-IronPort-AV": [
            "E=McAfee;i=\"6000,8403,9738\"; a=\"145939444\"",
            "E=Sophos;i=\"5.76,407,1592895600\"; d=\"scan'208\";a=\"145939444\"",
            "E=Sophos;i=\"5.76,406,1592895600\"; d=\"scan'208\";a=\"504493516\""
        ],
        "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:20 +0100",
        "Message-Id": "<20200908201830.74206-32-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 31/41] pipeline: add SWX table update high\n\tlevel API",
        "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": "High-level transaction-oriented API for SWX pipeline table updates. It\nsupports multi-table atomic updates, i.e. multiple tables can be\nupdated in a single step with only the before and after table set\nvisible to the packets. Uses the lower-level table update mechanisms.\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 |   15 +-\n lib/librte_pipeline/rte_swx_ctl.c            | 1552 ++++++++++++++++++\n lib/librte_pipeline/rte_swx_ctl.h            |  170 ++\n 4 files changed, 1736 insertions(+), 4 deletions(-)\n create mode 100644 lib/librte_pipeline/rte_swx_ctl.c",
    "diff": "diff --git a/lib/librte_pipeline/meson.build b/lib/librte_pipeline/meson.build\nindex d5f4d16e5..be1d9c3a4 100644\n--- a/lib/librte_pipeline/meson.build\n+++ b/lib/librte_pipeline/meson.build\n@@ -4,7 +4,8 @@\n sources = files('rte_pipeline.c',\n \t'rte_port_in_action.c',\n \t'rte_table_action.c',\n-\t'rte_swx_pipeline.c',)\n+\t'rte_swx_pipeline.c',\n+\t'rte_swx_ctl.c',)\n headers = files('rte_pipeline.h',\n \t'rte_port_in_action.h',\n \t'rte_table_action.h',\ndiff --git a/lib/librte_pipeline/rte_pipeline_version.map b/lib/librte_pipeline/rte_pipeline_version.map\nindex 730e11a0c..ec38f0eef 100644\n--- a/lib/librte_pipeline/rte_pipeline_version.map\n+++ b/lib/librte_pipeline/rte_pipeline_version.map\n@@ -1,4 +1,4 @@\n-DPDK_21 {\n+DPDK_20.0 {\n \tglobal:\n \n \trte_pipeline_ah_packet_drop;\n@@ -75,8 +75,6 @@ EXPERIMENTAL {\n \trte_swx_pipeline_free;\n \trte_swx_pipeline_run;\n \trte_swx_pipeline_flush;\n-\trte_swx_pipeline_table_state_get;\n-\trte_swx_pipeline_table_state_set;\n \trte_swx_ctl_pipeline_info_get;\n \trte_swx_ctl_pipeline_numa_node_get;\n \trte_swx_ctl_pipeline_port_in_stats_read;\n@@ -87,4 +85,15 @@ EXPERIMENTAL {\n \trte_swx_ctl_table_match_field_info_get;\n \trte_swx_ctl_table_action_info_get;\n \trte_swx_ctl_table_ops_get;\n+\trte_swx_pipeline_table_state_get;\n+\trte_swx_pipeline_table_state_set;\n+\trte_swx_ctl_pipeline_create;\n+\trte_swx_ctl_pipeline_free;\n+\trte_swx_ctl_pipeline_table_entry_add;\n+\trte_swx_ctl_pipeline_table_default_entry_add;\n+\trte_swx_ctl_pipeline_table_entry_delete;\n+\trte_swx_ctl_pipeline_commit;\n+\trte_swx_ctl_pipeline_abort;\n+\trte_swx_ctl_pipeline_table_entry_read;\n+\trte_swx_ctl_pipeline_table_fprintf;\n };\ndiff --git a/lib/librte_pipeline/rte_swx_ctl.c b/lib/librte_pipeline/rte_swx_ctl.c\nnew file mode 100644\nindex 000000000..44da678c1\n--- /dev/null\n+++ b/lib/librte_pipeline/rte_swx_ctl.c\n@@ -0,0 +1,1552 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2020 Intel Corporation\n+ */\n+#include <stdlib.h>\n+#include <string.h>\n+#include <stdio.h>\n+#include <sys/queue.h>\n+#include <unistd.h>\n+\n+#include <rte_common.h>\n+#include <rte_byteorder.h>\n+\n+#include \"rte_swx_ctl.h\"\n+\n+#define CHECK(condition, err_code)                                             \\\n+do {                                                                           \\\n+\tif (!(condition))                                                      \\\n+\t\treturn -(err_code);                                            \\\n+} while (0)\n+\n+#define ntoh64(x) rte_be_to_cpu_64(x)\n+#define hton64(x) rte_cpu_to_be_64(x)\n+\n+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN\n+#define field_ntoh(val, n_bits) (ntoh64((val) << (64 - n_bits)))\n+#define field_hton(val, n_bits) (hton64((val) << (64 - n_bits)))\n+#else\n+#define field_ntoh(val, n_bits) (val)\n+#define field_hton(val, n_bits) (val)\n+#endif\n+\n+struct action {\n+\tstruct rte_swx_ctl_action_info info;\n+\tstruct rte_swx_ctl_action_arg_info *args;\n+\tuint32_t data_size;\n+};\n+\n+struct table {\n+\tstruct rte_swx_ctl_table_info info;\n+\tstruct rte_swx_ctl_table_match_field_info *mf;\n+\tstruct rte_swx_ctl_table_action_info *actions;\n+\tstruct rte_swx_table_ops ops;\n+\tstruct rte_swx_table_params params;\n+\n+\tstruct rte_swx_table_entry_list entries;\n+\tstruct rte_swx_table_entry_list pending_add;\n+\tstruct rte_swx_table_entry_list pending_modify0;\n+\tstruct rte_swx_table_entry_list pending_modify1;\n+\tstruct rte_swx_table_entry_list pending_delete;\n+\tstruct rte_swx_table_entry *pending_default;\n+\n+\tint is_stub;\n+\tuint32_t n_add;\n+\tuint32_t n_modify;\n+\tuint32_t n_delete;\n+};\n+\n+struct rte_swx_ctl_pipeline {\n+\tstruct rte_swx_ctl_pipeline_info info;\n+\tstruct rte_swx_pipeline *p;\n+\tstruct action *actions;\n+\tstruct table *tables;\n+\tstruct rte_swx_table_state *ts;\n+\tstruct rte_swx_table_state *ts_next;\n+\tint numa_node;\n+};\n+\n+static struct action *\n+action_find(struct rte_swx_ctl_pipeline *ctl, const char *action_name)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < ctl->info.n_actions; i++) {\n+\t\tstruct action *a = &ctl->actions[i];\n+\n+\t\tif (!strcmp(action_name, a->info.name))\n+\t\t\treturn a;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static void\n+action_free(struct rte_swx_ctl_pipeline *ctl)\n+{\n+\tuint32_t i;\n+\n+\tif (!ctl->actions)\n+\t\treturn;\n+\n+\tfor (i = 0; i < ctl->info.n_actions; i++) {\n+\t\tstruct action *action = &ctl->actions[i];\n+\n+\t\tfree(action->args);\n+\t}\n+\n+\tfree(ctl->actions);\n+\tctl->actions = NULL;\n+}\n+\n+static struct table *\n+table_find(struct rte_swx_ctl_pipeline *ctl, const char *table_name)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < ctl->info.n_tables; i++) {\n+\t\tstruct table *table = &ctl->tables[i];\n+\n+\t\tif (!strcmp(table_name, table->info.name))\n+\t\t\treturn table;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static int\n+table_params_get(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)\n+{\n+\tstruct table *table = &ctl->tables[table_id];\n+\tuint8_t *key_mask = NULL;\n+\tenum rte_swx_table_match_type match_type = RTE_SWX_TABLE_MATCH_WILDCARD;\n+\tuint32_t key_size = 0, key_offset = 0, action_data_size = 0, i;\n+\n+\tif (table->info.n_match_fields) {\n+\t\tstruct rte_swx_ctl_table_match_field_info *first, *last;\n+\t\tuint32_t i;\n+\n+\t\tfirst = &table->mf[0];\n+\t\tlast = &table->mf[table->info.n_match_fields - 1];\n+\n+\t\t/* match_type. */\n+\t\tfor (i = 0; i < table->info.n_match_fields; i++) {\n+\t\t\tstruct rte_swx_ctl_table_match_field_info *f;\n+\n+\t\t\tf = &table->mf[i];\n+\t\t\tif (f->match_type != RTE_SWX_TABLE_MATCH_EXACT)\n+\t\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (i == table->info.n_match_fields)\n+\t\t\tmatch_type = RTE_SWX_TABLE_MATCH_EXACT;\n+\t\telse if ((i == table->info.n_match_fields - 1) &&\n+\t\t\t (last->match_type == RTE_SWX_TABLE_MATCH_LPM))\n+\t\t\tmatch_type = RTE_SWX_TABLE_MATCH_LPM;\n+\n+\t\t/* key_offset. */\n+\t\tkey_offset = first->offset / 8;\n+\n+\t\t/* key_size. */\n+\t\tkey_size = (last->offset + last->n_bits - first->offset) / 8;\n+\n+\t\t/* key_mask. */\n+\t\tkey_mask = calloc(1, key_size);\n+\t\tCHECK(key_mask, ENOMEM);\n+\n+\t\tfor (i = 0; i < table->info.n_match_fields; i++) {\n+\t\t\tstruct rte_swx_ctl_table_match_field_info *f;\n+\t\t\tuint32_t start;\n+\t\t\tsize_t size;\n+\n+\t\t\tf = &table->mf[i];\n+\t\t\tstart = (f->offset - first->offset) / 8;\n+\t\t\tsize = f->n_bits / 8;\n+\n+\t\t\tmemset(&key_mask[start], 0xFF, size);\n+\t\t}\n+\t}\n+\n+\t/* action_data_size. */\n+\tfor (i = 0; i < table->info.n_actions; i++) {\n+\t\tuint32_t action_id = table->actions[i].action_id;\n+\t\tstruct action *a = &ctl->actions[action_id];\n+\n+\t\tif (a->data_size > action_data_size)\n+\t\t\taction_data_size = a->data_size;\n+\t}\n+\n+\t/* Fill in. */\n+\ttable->params.match_type = match_type;\n+\ttable->params.key_size = key_size;\n+\ttable->params.key_offset = key_offset;\n+\ttable->params.key_mask0 = key_mask;\n+\ttable->params.action_data_size = action_data_size;\n+\ttable->params.n_keys_max = table->info.size;\n+\n+\treturn 0;\n+}\n+\n+static void\n+table_entry_free(struct rte_swx_table_entry *entry)\n+{\n+\tif (!entry)\n+\t\treturn;\n+\n+\tfree(entry->key);\n+\tfree(entry->key_mask);\n+\tfree(entry->action_data);\n+\tfree(entry);\n+}\n+\n+static struct rte_swx_table_entry *\n+table_entry_alloc(struct table *table)\n+{\n+\tstruct rte_swx_table_entry *entry;\n+\n+\tentry = calloc(1, sizeof(struct rte_swx_table_entry));\n+\tif (!entry)\n+\t\tgoto error;\n+\n+\t/* key, key_mask. */\n+\tif (!table->is_stub) {\n+\t\tentry->key = calloc(1, table->params.key_size);\n+\t\tif (!entry->key)\n+\t\t\tgoto error;\n+\n+\t\tif (table->params.match_type != RTE_SWX_TABLE_MATCH_EXACT) {\n+\t\t\tentry->key_mask = calloc(1, table->params.key_size);\n+\t\t\tif (!entry->key_mask)\n+\t\t\t\tgoto error;\n+\t\t}\n+\t}\n+\n+\t/* action_data. */\n+\tif (table->params.action_data_size) {\n+\t\tentry->action_data = calloc(1, table->params.action_data_size);\n+\t\tif (!entry->action_data)\n+\t\t\tgoto error;\n+\t}\n+\n+\treturn entry;\n+\n+error:\n+\ttable_entry_free(entry);\n+\treturn NULL;\n+}\n+\n+static int\n+table_entry_check(struct rte_swx_ctl_pipeline *ctl,\n+\t\t  uint32_t table_id,\n+\t\t  struct rte_swx_table_entry *entry,\n+\t\t  int key_check,\n+\t\t  int data_check)\n+{\n+\tstruct table *table = &ctl->tables[table_id];\n+\n+\tCHECK(entry, EINVAL);\n+\n+\tif (key_check) {\n+\t\tif (table->is_stub) {\n+\t\t\t/* key. */\n+\t\t\tCHECK(!entry->key, EINVAL);\n+\n+\t\t\t/* key_mask. */\n+\t\t\tCHECK(!entry->key_mask, EINVAL);\n+\t\t} else {\n+\t\t\t/* key. */\n+\t\t\tCHECK(entry->key, EINVAL);\n+\n+\t\t\t/* key_mask. */\n+\t\t\tswitch (table->params.match_type) {\n+\t\t\tcase RTE_SWX_TABLE_MATCH_WILDCARD:\n+\t\t\t\tbreak;\n+\n+\t\t\tcase RTE_SWX_TABLE_MATCH_LPM:\n+\t\t\t\t/* TBD Check that key mask is prefix. */\n+\t\t\t\tbreak;\n+\n+\t\t\tcase RTE_SWX_TABLE_MATCH_EXACT:\n+\t\t\t\tCHECK(!entry->key_mask, EINVAL);\n+\t\t\t\tbreak;\n+\n+\t\t\tdefault:\n+\t\t\t\tCHECK(0, EINVAL);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tif (data_check) {\n+\t\tstruct action *a;\n+\t\tuint32_t i;\n+\n+\t\t/* action_id. */\n+\t\tfor (i = 0; i < table->info.n_actions; i++)\n+\t\t\tif (entry->action_id == table->actions[i].action_id)\n+\t\t\t\tbreak;\n+\n+\t\tCHECK(i < table->info.n_actions, EINVAL);\n+\n+\t\t/* action_data. */\n+\t\ta = &ctl->actions[entry->action_id];\n+\t\tCHECK((a->data_size && entry->action_data) ||\n+\t\t      (!a->data_size && !entry->action_data), EINVAL);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static struct rte_swx_table_entry *\n+table_entry_duplicate(struct rte_swx_ctl_pipeline *ctl,\n+\t\t      uint32_t table_id,\n+\t\t      struct rte_swx_table_entry *entry,\n+\t\t      int key_duplicate,\n+\t\t      int data_duplicate)\n+{\n+\tstruct table *table = &ctl->tables[table_id];\n+\tstruct rte_swx_table_entry *new_entry = NULL;\n+\n+\tif (!entry)\n+\t\tgoto error;\n+\n+\tnew_entry = calloc(1, sizeof(struct rte_swx_table_entry));\n+\tif (!new_entry)\n+\t\tgoto error;\n+\n+\tif (key_duplicate && !table->is_stub) {\n+\t\t/* key. */\n+\t\tif (!entry->key)\n+\t\t\tgoto error;\n+\n+\t\tnew_entry->key = malloc(table->params.key_size);\n+\t\tif (!new_entry->key)\n+\t\t\tgoto error;\n+\n+\t\tmemcpy(new_entry->key, entry->key, table->params.key_size);\n+\n+\t\t/* key_signature. */\n+\t\tnew_entry->key_signature = entry->key_signature;\n+\n+\t\t/* key_mask. */\n+\t\tif (table->params.match_type != RTE_SWX_TABLE_MATCH_EXACT) {\n+\t\t\tif (!entry->key_mask)\n+\t\t\t\tgoto error;\n+\n+\t\t\tnew_entry->key_mask = malloc(table->params.key_size);\n+\t\t\tif (!new_entry->key_mask)\n+\t\t\t\tgoto error;\n+\n+\t\t\tmemcpy(new_entry->key_mask,\n+\t\t\t       entry->key_mask,\n+\t\t\t       table->params.key_size);\n+\t\t}\n+\t}\n+\n+\tif (data_duplicate) {\n+\t\tstruct action *a;\n+\t\tuint32_t i;\n+\n+\t\t/* action_id. */\n+\t\tfor (i = 0; i < table->info.n_actions; i++)\n+\t\t\tif (entry->action_id == table->actions[i].action_id)\n+\t\t\t\tbreak;\n+\n+\t\tif (i >= table->info.n_actions)\n+\t\t\tgoto error;\n+\n+\t\tnew_entry->action_id = entry->action_id;\n+\n+\t\t/* action_data. */\n+\t\ta = &ctl->actions[entry->action_id];\n+\t\tif (a->data_size) {\n+\t\t\tif (!entry->action_data)\n+\t\t\t\tgoto error;\n+\n+\t\t\tnew_entry->action_data = malloc(a->data_size);\n+\t\t\tif (!new_entry->action_data)\n+\t\t\t\tgoto error;\n+\n+\t\t\tmemcpy(new_entry->action_data,\n+\t\t\t       entry->action_data,\n+\t\t\t       a->data_size);\n+\t\t}\n+\t}\n+\n+\treturn entry;\n+\n+error:\n+\ttable_entry_free(new_entry);\n+\treturn NULL;\n+}\n+\n+static int\n+entry_keycmp_em(struct rte_swx_table_entry *e0,\n+\t\tstruct rte_swx_table_entry *e1,\n+\t\tuint32_t key_size)\n+{\n+\tif (e0->key_signature != e1->key_signature)\n+\t\treturn 1; /* Not equal. */\n+\n+\tif (memcmp(e0->key, e1->key, key_size))\n+\t\treturn 1; /* Not equal. */\n+\n+\treturn 0; /* Equal */\n+}\n+\n+static int\n+entry_keycmp_wm(struct rte_swx_table_entry *e0 __rte_unused,\n+\t\tstruct rte_swx_table_entry *e1 __rte_unused,\n+\t\tuint32_t key_size __rte_unused)\n+{\n+\t/* TBD */\n+\n+\treturn 1; /* Not equal */\n+}\n+\n+static int\n+entry_keycmp_lpm(struct rte_swx_table_entry *e0 __rte_unused,\n+\t\t struct rte_swx_table_entry *e1 __rte_unused,\n+\t\t uint32_t key_size __rte_unused)\n+{\n+\t/* TBD */\n+\n+\treturn 1; /* Not equal */\n+}\n+\n+static int\n+table_entry_keycmp(struct table *table,\n+\t\t   struct rte_swx_table_entry *e0,\n+\t\t   struct rte_swx_table_entry *e1)\n+{\n+\tswitch (table->params.match_type) {\n+\tcase RTE_SWX_TABLE_MATCH_EXACT:\n+\t\treturn entry_keycmp_em(e0, e1, table->params.key_size);\n+\n+\tcase RTE_SWX_TABLE_MATCH_WILDCARD:\n+\t\treturn entry_keycmp_wm(e0, e1, table->params.key_size);\n+\n+\tcase RTE_SWX_TABLE_MATCH_LPM:\n+\t\treturn entry_keycmp_lpm(e0, e1, table->params.key_size);\n+\n+\tdefault:\n+\t\treturn 1; /* Not equal. */\n+\t}\n+}\n+\n+static struct rte_swx_table_entry *\n+table_entries_find(struct table *table, struct rte_swx_table_entry *entry)\n+{\n+\tstruct rte_swx_table_entry *e;\n+\n+\tTAILQ_FOREACH(e, &table->entries, node)\n+\t\tif (!table_entry_keycmp(table, entry, e))\n+\t\t\treturn e; /* Found. */\n+\n+\treturn NULL; /* Not found. */\n+}\n+\n+static void\n+table_entries_free(struct table *table)\n+{\n+\tfor ( ; ; ) {\n+\t\tstruct rte_swx_table_entry *entry;\n+\n+\t\tentry = TAILQ_FIRST(&table->entries);\n+\t\tif (!entry)\n+\t\t\tbreak;\n+\n+\t\tTAILQ_REMOVE(&table->entries, entry, node);\n+\t\ttable_entry_free(entry);\n+\t}\n+}\n+\n+static struct rte_swx_table_entry *\n+table_pending_add_find(struct table *table, struct rte_swx_table_entry *entry)\n+{\n+\tstruct rte_swx_table_entry *e;\n+\n+\tTAILQ_FOREACH(e, &table->pending_add, node)\n+\t\tif (!table_entry_keycmp(table, entry, e))\n+\t\t\treturn e; /* Found. */\n+\n+\treturn NULL; /* Not found. */\n+}\n+\n+static void\n+table_pending_add_admit(struct table *table)\n+{\n+\tTAILQ_CONCAT(&table->entries, &table->pending_add, node);\n+}\n+\n+static void\n+table_pending_add_free(struct table *table)\n+{\n+\tfor ( ; ; ) {\n+\t\tstruct rte_swx_table_entry *entry;\n+\n+\t\tentry = TAILQ_FIRST(&table->pending_add);\n+\t\tif (!entry)\n+\t\t\tbreak;\n+\n+\t\tTAILQ_REMOVE(&table->pending_add, entry, node);\n+\t\ttable_entry_free(entry);\n+\t}\n+}\n+\n+static struct rte_swx_table_entry *\n+table_pending_modify0_find(struct table *table,\n+\t\t\t   struct rte_swx_table_entry *entry)\n+{\n+\tstruct rte_swx_table_entry *e;\n+\n+\tTAILQ_FOREACH(e, &table->pending_modify0, node)\n+\t\tif (!table_entry_keycmp(table, entry, e))\n+\t\t\treturn e; /* Found. */\n+\n+\treturn NULL; /* Not found. */\n+}\n+\n+static void\n+table_pending_modify0_admit(struct table *table)\n+{\n+\tTAILQ_CONCAT(&table->entries, &table->pending_modify0, node);\n+}\n+\n+static void\n+table_pending_modify0_free(struct table *table)\n+{\n+\tfor ( ; ; ) {\n+\t\tstruct rte_swx_table_entry *entry;\n+\n+\t\tentry = TAILQ_FIRST(&table->pending_modify0);\n+\t\tif (!entry)\n+\t\t\tbreak;\n+\n+\t\tTAILQ_REMOVE(&table->pending_modify0, entry, node);\n+\t\ttable_entry_free(entry);\n+\t}\n+}\n+\n+static struct rte_swx_table_entry *\n+table_pending_modify1_find(struct table *table,\n+\t\t\t   struct rte_swx_table_entry *entry)\n+{\n+\tstruct rte_swx_table_entry *e;\n+\n+\tTAILQ_FOREACH(e, &table->pending_modify1, node)\n+\t\tif (!table_entry_keycmp(table, entry, e))\n+\t\t\treturn e; /* Found. */\n+\n+\treturn NULL; /* Not found. */\n+}\n+\n+static void\n+table_pending_modify1_admit(struct table *table)\n+{\n+\tTAILQ_CONCAT(&table->entries, &table->pending_modify1, node);\n+}\n+\n+static void\n+table_pending_modify1_free(struct table *table)\n+{\n+\tfor ( ; ; ) {\n+\t\tstruct rte_swx_table_entry *entry;\n+\n+\t\tentry = TAILQ_FIRST(&table->pending_modify1);\n+\t\tif (!entry)\n+\t\t\tbreak;\n+\n+\t\tTAILQ_REMOVE(&table->pending_modify1, entry, node);\n+\t\ttable_entry_free(entry);\n+\t}\n+}\n+\n+static struct rte_swx_table_entry *\n+table_pending_delete_find(struct table *table,\n+\t\t\t  struct rte_swx_table_entry *entry)\n+{\n+\tstruct rte_swx_table_entry *e;\n+\n+\tTAILQ_FOREACH(e, &table->pending_delete, node)\n+\t\tif (!table_entry_keycmp(table, entry, e))\n+\t\t\treturn e; /* Found. */\n+\n+\treturn NULL; /* Not found. */\n+}\n+\n+static void\n+table_pending_delete_admit(struct table *table)\n+{\n+\tTAILQ_CONCAT(&table->entries, &table->pending_delete, node);\n+}\n+\n+static void\n+table_pending_delete_free(struct table *table)\n+{\n+\tfor ( ; ; ) {\n+\t\tstruct rte_swx_table_entry *entry;\n+\n+\t\tentry = TAILQ_FIRST(&table->pending_delete);\n+\t\tif (!entry)\n+\t\t\tbreak;\n+\n+\t\tTAILQ_REMOVE(&table->pending_delete, entry, node);\n+\t\ttable_entry_free(entry);\n+\t}\n+}\n+\n+static void\n+table_pending_default_free(struct table *table)\n+{\n+\tif (!table->pending_default)\n+\t\treturn;\n+\n+\tfree(table->pending_default->action_data);\n+\tfree(table->pending_default);\n+\ttable->pending_default = NULL;\n+}\n+\n+static void\n+table_free(struct rte_swx_ctl_pipeline *ctl)\n+{\n+\tuint32_t i;\n+\n+\tif (!ctl->tables)\n+\t\treturn;\n+\n+\tfor (i = 0; i < ctl->info.n_tables; i++) {\n+\t\tstruct table *table = &ctl->tables[i];\n+\n+\t\tfree(table->mf);\n+\t\tfree(table->actions);\n+\t\tfree(table->params.key_mask0);\n+\n+\t\ttable_entries_free(table);\n+\t\ttable_pending_add_free(table);\n+\t\ttable_pending_modify0_free(table);\n+\t\ttable_pending_modify1_free(table);\n+\t\ttable_pending_delete_free(table);\n+\t\ttable_pending_default_free(table);\n+\t}\n+\n+\tfree(ctl->tables);\n+\tctl->tables = NULL;\n+}\n+\n+static void\n+table_state_free(struct rte_swx_ctl_pipeline *ctl)\n+{\n+\tuint32_t i;\n+\n+\tif (!ctl->ts_next)\n+\t\treturn;\n+\n+\t/* For each table, free its table state. */\n+\tfor (i = 0; i < ctl->info.n_tables; i++) {\n+\t\tstruct table *table = &ctl->tables[i];\n+\t\tstruct rte_swx_table_state *ts = &ctl->ts_next[i];\n+\n+\t\t/* Default action data. */\n+\t\tfree(ts->default_action_data);\n+\n+\t\t/* Table object. */\n+\t\tif (!table->is_stub && table->ops.free && ts->obj)\n+\t\t\ttable->ops.free(ts->obj);\n+\t}\n+\n+\tfree(ctl->ts_next);\n+\tctl->ts_next = NULL;\n+}\n+\n+static int\n+table_state_create(struct rte_swx_ctl_pipeline *ctl)\n+{\n+\tint status = 0;\n+\tuint32_t i;\n+\n+\tctl->ts_next = calloc(ctl->info.n_tables,\n+\t\t\t      sizeof(struct rte_swx_table_state));\n+\tif (!ctl->ts_next) {\n+\t\tstatus = -ENOMEM;\n+\t\tgoto error;\n+\t}\n+\n+\tfor (i = 0; i < ctl->info.n_tables; i++) {\n+\t\tstruct table *table = &ctl->tables[i];\n+\t\tstruct rte_swx_table_state *ts = &ctl->ts[i];\n+\t\tstruct rte_swx_table_state *ts_next = &ctl->ts_next[i];\n+\n+\t\t/* Table object. */\n+\t\tif (!table->is_stub) {\n+\t\t\tts_next->obj = table->ops.create(&table->params,\n+\t\t\t\t\t\t\t &table->entries,\n+\t\t\t\t\t\t\t table->info.args,\n+\t\t\t\t\t\t\t ctl->numa_node);\n+\t\t\tif (!ts_next->obj) {\n+\t\t\t\tstatus = -ENODEV;\n+\t\t\t\tgoto error;\n+\t\t\t}\n+\t\t}\n+\n+\t\t/* Default action data: duplicate from current table state. */\n+\t\tts_next->default_action_data =\n+\t\t\tmalloc(table->params.action_data_size);\n+\t\tif (!ts_next->default_action_data) {\n+\t\t\tstatus = -ENOMEM;\n+\t\t\tgoto error;\n+\t\t}\n+\n+\t\tmemcpy(ts_next->default_action_data,\n+\t\t       ts->default_action_data,\n+\t\t       table->params.action_data_size);\n+\n+\t\tts_next->default_action_id = ts->default_action_id;\n+\t}\n+\n+\treturn 0;\n+\n+error:\n+\ttable_state_free(ctl);\n+\treturn status;\n+}\n+\n+void\n+rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline *ctl)\n+{\n+\tif (!ctl)\n+\t\treturn;\n+\n+\taction_free(ctl);\n+\n+\ttable_state_free(ctl);\n+\n+\ttable_free(ctl);\n+\n+\tfree(ctl);\n+}\n+\n+struct rte_swx_ctl_pipeline *\n+rte_swx_ctl_pipeline_create(struct rte_swx_pipeline *p)\n+{\n+\tstruct rte_swx_ctl_pipeline *ctl = NULL;\n+\tuint32_t i;\n+\tint status;\n+\n+\tif (!p)\n+\t\tgoto error;\n+\n+\tctl = calloc(1, sizeof(struct rte_swx_ctl_pipeline));\n+\tif (!ctl)\n+\t\tgoto error;\n+\n+\t/* info. */\n+\tstatus = rte_swx_ctl_pipeline_info_get(p, &ctl->info);\n+\tif (status)\n+\t\tgoto error;\n+\n+\t/* numa_node. */\n+\tstatus = rte_swx_ctl_pipeline_numa_node_get(p, &ctl->numa_node);\n+\tif (status)\n+\t\tgoto error;\n+\n+\t/* p. */\n+\tctl->p = p;\n+\n+\t/* actions. */\n+\tctl->actions = calloc(ctl->info.n_actions, sizeof(struct action));\n+\tif (!ctl->actions)\n+\t\tgoto error;\n+\n+\tfor (i = 0; i < ctl->info.n_actions; i++) {\n+\t\tstruct action *a = &ctl->actions[i];\n+\t\tuint32_t j;\n+\n+\t\t/* info. */\n+\t\tstatus = rte_swx_ctl_action_info_get(p, i, &a->info);\n+\t\tif (status)\n+\t\t\tgoto error;\n+\n+\t\t/* args. */\n+\t\ta->args = calloc(a->info.n_args,\n+\t\t\t\t sizeof(struct rte_swx_ctl_action_arg_info));\n+\t\tif (!a->args)\n+\t\t\tgoto error;\n+\n+\t\tfor (j = 0; j < a->info.n_args; j++) {\n+\t\t\tstatus = rte_swx_ctl_action_arg_info_get(p,\n+\t\t\t\t\t\t\t\t i,\n+\t\t\t\t\t\t\t\t j,\n+\t\t\t\t\t\t\t\t &a->args[j]);\n+\t\t\tif (status)\n+\t\t\t\tgoto error;\n+\t\t}\n+\n+\t\t/* data_size. */\n+\t\tfor (j = 0; j < a->info.n_args; j++) {\n+\t\t\tstruct rte_swx_ctl_action_arg_info *info = &a->args[j];\n+\n+\t\t\ta->data_size += info->n_bits;\n+\t\t}\n+\n+\t\ta->data_size = (a->data_size + 7) / 8;\n+\t}\n+\n+\t/* tables. */\n+\tctl->tables = calloc(ctl->info.n_tables, sizeof(struct table));\n+\tif (!ctl->tables)\n+\t\tgoto error;\n+\n+\tfor (i = 0; i < ctl->info.n_tables; i++) {\n+\t\tstruct table *t = &ctl->tables[i];\n+\n+\t\tTAILQ_INIT(&t->entries);\n+\t\tTAILQ_INIT(&t->pending_add);\n+\t\tTAILQ_INIT(&t->pending_modify0);\n+\t\tTAILQ_INIT(&t->pending_modify1);\n+\t\tTAILQ_INIT(&t->pending_delete);\n+\t}\n+\n+\tfor (i = 0; i < ctl->info.n_tables; i++) {\n+\t\tstruct table *t = &ctl->tables[i];\n+\t\tuint32_t j;\n+\n+\t\t/* info. */\n+\t\tstatus = rte_swx_ctl_table_info_get(p, i, &t->info);\n+\t\tif (status)\n+\t\t\tgoto error;\n+\n+\t\t/* mf. */\n+\t\tt->mf = calloc(t->info.n_match_fields,\n+\t\t\tsizeof(struct rte_swx_ctl_table_match_field_info));\n+\t\tif (!t->mf)\n+\t\t\tgoto error;\n+\n+\t\tfor (j = 0; j < t->info.n_match_fields; j++) {\n+\t\t\tstatus = rte_swx_ctl_table_match_field_info_get(p,\n+\t\t\t\ti,\n+\t\t\t\tj,\n+\t\t\t\t&t->mf[j]);\n+\t\t\tif (status)\n+\t\t\t\tgoto error;\n+\t\t}\n+\n+\t\t/* actions. */\n+\t\tt->actions = calloc(t->info.n_actions,\n+\t\t\tsizeof(struct rte_swx_ctl_table_action_info));\n+\t\tif (!t->actions)\n+\t\t\tgoto error;\n+\n+\t\tfor (j = 0; j < t->info.n_actions; j++) {\n+\t\t\tstatus = rte_swx_ctl_table_action_info_get(p,\n+\t\t\t\ti,\n+\t\t\t\tj,\n+\t\t\t\t&t->actions[j]);\n+\t\t\tif (status ||\n+\t\t\t    t->actions[j].action_id >= ctl->info.n_actions)\n+\t\t\t\tgoto error;\n+\t\t}\n+\n+\t\t/* ops, is_stub. */\n+\t\tstatus = rte_swx_ctl_table_ops_get(p, i, &t->ops, &t->is_stub);\n+\t\tif (status)\n+\t\t\tgoto error;\n+\n+\t\tif ((t->is_stub && t->info.n_match_fields) ||\n+\t\t    (!t->is_stub && !t->info.n_match_fields))\n+\t\t\tgoto error;\n+\n+\t\t/* params. */\n+\t\tstatus = table_params_get(ctl, i);\n+\t\tif (status)\n+\t\t\tgoto error;\n+\t}\n+\n+\t/* ts. */\n+\tstatus = rte_swx_pipeline_table_state_get(p, &ctl->ts);\n+\tif (status)\n+\t\tgoto error;\n+\n+\t/* ts_next. */\n+\tstatus = table_state_create(ctl);\n+\tif (status)\n+\t\tgoto error;\n+\n+\treturn ctl;\n+\n+error:\n+\trte_swx_ctl_pipeline_free(ctl);\n+\treturn NULL;\n+}\n+\n+int\n+rte_swx_ctl_pipeline_table_entry_add(struct rte_swx_ctl_pipeline *ctl,\n+\t\t\t\t     const char *table_name,\n+\t\t\t\t     struct rte_swx_table_entry *entry)\n+{\n+\tstruct table *table;\n+\tstruct rte_swx_table_entry *new_entry, *existing_entry;\n+\tuint32_t table_id;\n+\n+\tCHECK(ctl, EINVAL);\n+\tCHECK(table_name && table_name[0], EINVAL);\n+\n+\ttable = table_find(ctl, table_name);\n+\tCHECK(table, EINVAL);\n+\ttable_id = table - ctl->tables;\n+\n+\tnew_entry = table_entry_duplicate(ctl, table_id, entry, 1, 1);\n+\tCHECK(new_entry, ENOMEM);\n+\n+\t/* The new entry is found in the table->entries list:\n+\t * - Add the new entry to the table->pending_modify1 list;\n+\t * - Move the existing entry from the table->entries list to the\n+\t *   table->pending_modify0 list.\n+\t */\n+\texisting_entry = table_entries_find(table, entry);\n+\tif (existing_entry) {\n+\t\tTAILQ_INSERT_TAIL(&table->pending_modify1,\n+\t\t\t\t  new_entry,\n+\t\t\t\t  node);\n+\n+\t\tTAILQ_REMOVE(&table->entries,\n+\t\t\t     existing_entry,\n+\t\t\t     node);\n+\n+\t\tTAILQ_INSERT_TAIL(&table->pending_modify0,\n+\t\t\t\t  existing_entry,\n+\t\t\t\t  node);\n+\n+\t\treturn 0;\n+\t}\n+\n+\t/* The new entry is found in the table->pending_add list:\n+\t * - Replace the entry in the table->pending_add list with the new entry\n+\t *   (and free the replaced entry).\n+\t */\n+\texisting_entry = table_pending_add_find(table, entry);\n+\tif (existing_entry) {\n+\t\tTAILQ_INSERT_AFTER(&table->pending_add,\n+\t\t\t\t   existing_entry,\n+\t\t\t\t   new_entry,\n+\t\t\t\t   node);\n+\n+\t\tTAILQ_REMOVE(&table->pending_add,\n+\t\t\t     existing_entry,\n+\t\t\t     node);\n+\n+\t\ttable_entry_free(existing_entry);\n+\n+\t\treturn 0;\n+\t}\n+\n+\t/* The new entry is found in the table->pending_modify1 list:\n+\t * - Replace the entry in the table->pending_modify1 list with the new\n+\t *   entry (and free the replaced entry).\n+\t */\n+\texisting_entry = table_pending_modify1_find(table, entry);\n+\tif (existing_entry) {\n+\t\tTAILQ_INSERT_AFTER(&table->pending_modify1,\n+\t\t\t\t   existing_entry,\n+\t\t\t\t   new_entry,\n+\t\t\t\t   node);\n+\n+\t\tTAILQ_REMOVE(&table->pending_modify1,\n+\t\t\t     existing_entry,\n+\t\t\t     node);\n+\n+\t\ttable_entry_free(existing_entry);\n+\n+\t\treturn 0;\n+\t}\n+\n+\t/* The new entry is found in the table->pending_delete list:\n+\t * - Add the new entry to the table->pending_modify1 list;\n+\t * - Move the existing entry from the table->pending_delete list to the\n+\t *   table->pending_modify0 list.\n+\t */\n+\texisting_entry = table_pending_delete_find(table, entry);\n+\tif (existing_entry) {\n+\t\tTAILQ_INSERT_TAIL(&table->pending_modify1,\n+\t\t\t\t  new_entry,\n+\t\t\t\t  node);\n+\n+\t\tTAILQ_REMOVE(&table->pending_delete,\n+\t\t\t     existing_entry,\n+\t\t\t     node);\n+\n+\t\tTAILQ_INSERT_TAIL(&table->pending_modify0,\n+\t\t\t\t  existing_entry,\n+\t\t\t\t  node);\n+\n+\t\treturn 0;\n+\t}\n+\n+\t/* The new entry is not found in any of the above lists:\n+\t * - Add the new entry to the table->pending_add list.\n+\t */\n+\tTAILQ_INSERT_TAIL(&table->pending_add, new_entry, node);\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_swx_ctl_pipeline_table_entry_delete(struct rte_swx_ctl_pipeline *ctl,\n+\t\t\t\t\tconst char *table_name,\n+\t\t\t\t\tstruct rte_swx_table_entry *entry)\n+{\n+\tstruct table *table;\n+\tstruct rte_swx_table_entry *existing_entry;\n+\tuint32_t table_id;\n+\n+\tCHECK(ctl, EINVAL);\n+\n+\tCHECK(table_name && table_name[0], EINVAL);\n+\ttable = table_find(ctl, table_name);\n+\tCHECK(table, EINVAL);\n+\ttable_id = table - ctl->tables;\n+\n+\tCHECK(entry, EINVAL);\n+\tCHECK(!table_entry_check(ctl, table_id, entry, 1, 0), EINVAL);\n+\n+\t/* The entry is found in the table->entries list:\n+\t * - Move the existing entry from the table->entries list to to the\n+\t *   table->pending_delete list.\n+\t */\n+\texisting_entry = table_entries_find(table, entry);\n+\tif (existing_entry) {\n+\t\tTAILQ_REMOVE(&table->entries,\n+\t\t\t     existing_entry,\n+\t\t\t     node);\n+\n+\t\tTAILQ_INSERT_TAIL(&table->pending_delete,\n+\t\t\t\t  existing_entry,\n+\t\t\t\t  node);\n+\n+\t\treturn 0;\n+\t}\n+\n+\t/* The entry is found in the table->pending_add list:\n+\t * - Remove the entry from the table->pending_add list and free it.\n+\t */\n+\texisting_entry = table_pending_add_find(table, entry);\n+\tif (existing_entry) {\n+\t\tTAILQ_REMOVE(&table->pending_add,\n+\t\t\t     existing_entry,\n+\t\t\t     node);\n+\n+\t\ttable_entry_free(existing_entry);\n+\t}\n+\n+\t/* The entry is found in the table->pending_modify1 list:\n+\t * - Free the entry in the table->pending_modify1 list;\n+\t * - Move the existing entry from the table->pending_modify0 list to the\n+\t *   table->pending_delete list.\n+\t */\n+\texisting_entry = table_pending_modify1_find(table, entry);\n+\tif (existing_entry) {\n+\t\tstruct rte_swx_table_entry *real_existing_entry;\n+\n+\t\tTAILQ_REMOVE(&table->pending_modify1,\n+\t\t\t     existing_entry,\n+\t\t\t     node);\n+\n+\t\ttable_entry_free(existing_entry);\n+\n+\t\treal_existing_entry = table_pending_modify0_find(table, entry);\n+\t\tCHECK(real_existing_entry, EINVAL); /* Coverity. */\n+\n+\t\tTAILQ_REMOVE(&table->pending_modify0,\n+\t\t\t     real_existing_entry,\n+\t\t\t     node);\n+\n+\t\tTAILQ_INSERT_TAIL(&table->pending_delete,\n+\t\t\t\t  real_existing_entry,\n+\t\t\t\t  node);\n+\n+\t\treturn 0;\n+\t}\n+\n+\t/* The entry is found in the table->pending_delete list:\n+\t * - Do nothing: the existing entry is already in the\n+\t *   table->pending_delete list, i.e. already marked for delete, so\n+\t *   simply keep it there as it is.\n+\t */\n+\n+\t/* The entry is not found in any of the above lists:\n+\t * - Do nothing: no existing entry to delete.\n+\t */\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_swx_ctl_pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *ctl,\n+\t\t\t\t\t     const char *table_name,\n+\t\t\t\t\t     struct rte_swx_table_entry *entry)\n+{\n+\tstruct table *table;\n+\tstruct rte_swx_table_entry *new_entry;\n+\tuint32_t table_id;\n+\n+\tCHECK(ctl, EINVAL);\n+\n+\tCHECK(table_name && table_name[0], EINVAL);\n+\ttable = table_find(ctl, table_name);\n+\tCHECK(table, EINVAL);\n+\ttable_id = table - ctl->tables;\n+\tCHECK(!table->info.default_action_is_const, EINVAL);\n+\n+\tnew_entry = table_entry_duplicate(ctl, table_id, entry, 0, 1);\n+\tCHECK(new_entry, ENOMEM);\n+\n+\ttable_pending_default_free(table);\n+\n+\ttable->pending_default = new_entry;\n+\treturn 0;\n+}\n+\n+static int\n+table_rollfwd0(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)\n+{\n+\tstruct table *table = &ctl->tables[table_id];\n+\tstruct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];\n+\tstruct rte_swx_table_entry *entry;\n+\n+\t/* Reset counters. */\n+\ttable->n_add = 0;\n+\ttable->n_modify = 0;\n+\ttable->n_delete = 0;\n+\n+\t/* Add pending rules. */\n+\tTAILQ_FOREACH(entry, &table->pending_add, node) {\n+\t\tint status;\n+\n+\t\tstatus = table->ops.add(ts_next->obj, entry);\n+\t\tif (status)\n+\t\t\treturn status;\n+\n+\t\ttable->n_add++;\n+\t}\n+\n+\t/* Modify pending rules. */\n+\tTAILQ_FOREACH(entry, &table->pending_modify1, node) {\n+\t\tint status;\n+\n+\t\tstatus = table->ops.add(ts_next->obj, entry);\n+\t\tif (status)\n+\t\t\treturn status;\n+\n+\t\ttable->n_modify++;\n+\t}\n+\n+\t/* Delete pending rules. */\n+\tTAILQ_FOREACH(entry, &table->pending_delete, node) {\n+\t\tint status;\n+\n+\t\tstatus = table->ops.del(ts_next->obj, entry);\n+\t\tif (status)\n+\t\t\treturn status;\n+\n+\t\ttable->n_delete++;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void\n+table_rollfwd1(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)\n+{\n+\tstruct table *table = &ctl->tables[table_id];\n+\tstruct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];\n+\tstruct action *a;\n+\tuint8_t *action_data;\n+\tuint64_t action_id;\n+\n+\t/* Copy the pending default entry. */\n+\tif (!table->pending_default)\n+\t\treturn;\n+\n+\taction_id = table->pending_default->action_id;\n+\taction_data = table->pending_default->action_data;\n+\ta = &ctl->actions[action_id];\n+\n+\tmemcpy(ts_next->default_action_data,\n+\t       action_data,\n+\t       a->data_size);\n+\n+\tts_next->default_action_id = action_id;\n+}\n+\n+static void\n+table_rollfwd2(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)\n+{\n+\tstruct table *table = &ctl->tables[table_id];\n+\n+\t/* Move all the pending add entries to the table, as they are now part\n+\t * of the table.\n+\t */\n+\ttable_pending_add_admit(table);\n+\n+\t/* Move all the pending modify1 entries to table, are they are now part\n+\t * of the table. Free up all the pending modify0 entries, as they are no\n+\t * longer part of the table.\n+\t */\n+\ttable_pending_modify1_admit(table);\n+\ttable_pending_modify0_free(table);\n+\n+\t/* Free up all the pending delete entries, as they are no longer part of\n+\t * the table.\n+\t */\n+\ttable_pending_delete_free(table);\n+\n+\t/* Free up the pending default entry, as it is now part of the table. */\n+\ttable_pending_default_free(table);\n+}\n+\n+static void\n+table_rollback(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)\n+{\n+\tstruct table *table = &ctl->tables[table_id];\n+\tstruct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];\n+\tstruct rte_swx_table_entry *entry;\n+\n+\t/* Add back all the entries that were just deleted. */\n+\tTAILQ_FOREACH(entry, &table->pending_delete, node) {\n+\t\tif (!table->n_delete)\n+\t\t\tbreak;\n+\n+\t\ttable->ops.add(ts_next->obj, entry);\n+\t\ttable->n_delete--;\n+\t}\n+\n+\t/* Add back the old copy for all the entries that were just\n+\t * modified.\n+\t */\n+\tTAILQ_FOREACH(entry, &table->pending_modify0, node) {\n+\t\tif (!table->n_modify)\n+\t\t\tbreak;\n+\n+\t\ttable->ops.add(ts_next->obj, entry);\n+\t\ttable->n_modify--;\n+\t}\n+\n+\t/* Delete all the entries that were just added. */\n+\tTAILQ_FOREACH(entry, &table->pending_add, node) {\n+\t\tif (!table->n_add)\n+\t\t\tbreak;\n+\n+\t\ttable->ops.del(ts_next->obj, entry);\n+\t\ttable->n_add--;\n+\t}\n+}\n+\n+static void\n+table_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)\n+{\n+\tstruct table *table = &ctl->tables[table_id];\n+\n+\t/* Free up all the pending add entries, as none of them is part of the\n+\t * table.\n+\t */\n+\ttable_pending_add_free(table);\n+\n+\t/* Free up all the pending modify1 entries, as none of them made it to\n+\t * the table. Add back all the pending modify0 entries, as none of them\n+\t * was deleted from the table.\n+\t */\n+\ttable_pending_modify1_free(table);\n+\ttable_pending_modify0_admit(table);\n+\n+\t/* Add back all the pending delete entries, as none of them was deleted\n+\t * from the table.\n+\t */\n+\ttable_pending_delete_admit(table);\n+\n+\t/* Free up the pending default entry, as it is no longer going to be\n+\t * added to the table.\n+\t */\n+\ttable_pending_default_free(table);\n+}\n+\n+int\n+rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail)\n+{\n+\tstruct rte_swx_table_state *ts;\n+\tint status = 0;\n+\tuint32_t i;\n+\n+\tCHECK(ctl, EINVAL);\n+\n+\t/* Operate the changes on the current ts_next before it becomes the new\n+\t * ts.\n+\t */\n+\tfor (i = 0; i < ctl->info.n_tables; i++) {\n+\t\tstatus = table_rollfwd0(ctl, i);\n+\t\tif (status)\n+\t\t\tgoto rollback;\n+\t}\n+\n+\tfor (i = 0; i < ctl->info.n_tables; i++)\n+\t\ttable_rollfwd1(ctl, i);\n+\n+\t/* Swap the table state for the data plane. The current ts and ts_next\n+\t * become the new ts_next and ts, respectively.\n+\t */\n+\trte_swx_pipeline_table_state_set(ctl->p, ctl->ts_next);\n+\tusleep(100);\n+\tts = ctl->ts;\n+\tctl->ts = ctl->ts_next;\n+\tctl->ts_next = ts;\n+\n+\t/* Operate the changes on the current ts_next, which is the previous ts.\n+\t */\n+\tfor (i = 0; i < ctl->info.n_tables; i++) {\n+\t\ttable_rollfwd0(ctl, i);\n+\t\ttable_rollfwd1(ctl, i);\n+\t\ttable_rollfwd2(ctl, i);\n+\t}\n+\n+\treturn 0;\n+\n+rollback:\n+\tfor (i = 0; i < ctl->info.n_tables; i++) {\n+\t\ttable_rollback(ctl, i);\n+\t\tif (abort_on_fail)\n+\t\t\ttable_abort(ctl, i);\n+\t}\n+\n+\treturn status;\n+}\n+\n+void\n+rte_swx_ctl_pipeline_abort(struct rte_swx_ctl_pipeline *ctl)\n+{\n+\tuint32_t i;\n+\n+\tif (!ctl)\n+\t\treturn;\n+\n+\tfor (i = 0; i < ctl->info.n_tables; i++)\n+\t\ttable_abort(ctl, i);\n+}\n+\n+#define RTE_SWX_CTL_ENTRY_TOKENS_MAX 256\n+\n+struct rte_swx_table_entry *\n+rte_swx_ctl_pipeline_table_entry_read(struct rte_swx_ctl_pipeline *ctl,\n+\t\t\t\t      const char *table_name,\n+\t\t\t\t      const char *string)\n+{\n+\tchar *tokens[RTE_SWX_CTL_ENTRY_TOKENS_MAX];\n+\tstruct table *table;\n+\tstruct action *action;\n+\tstruct rte_swx_table_entry *entry = NULL;\n+\tchar *s0 = NULL, *s;\n+\tuint32_t n_tokens = 0, arg_offset = 0, i;\n+\n+\t/* Check input arguments. */\n+\tif (!ctl)\n+\t\tgoto error;\n+\n+\tif (!table_name || !table_name[0])\n+\t\tgoto error;\n+\n+\ttable = table_find(ctl, table_name);\n+\tif (!table)\n+\t\tgoto error;\n+\n+\tif (!string || !string[0])\n+\t\tgoto error;\n+\n+\t/* Memory allocation. */\n+\ts0 = strdup(string);\n+\tif (!s0)\n+\t\tgoto error;\n+\n+\tentry = table_entry_alloc(table);\n+\tif (!entry)\n+\t\tgoto error;\n+\n+\t/* Parse the string into tokens. */\n+\tfor (s = s0; ; ) {\n+\t\tchar *token;\n+\n+\t\ttoken = strtok_r(s, \" \\f\\n\\r\\t\\v\", &s);\n+\t\tif (!token)\n+\t\t\tbreak;\n+\n+\t\tif (n_tokens >= RTE_SWX_CTL_ENTRY_TOKENS_MAX)\n+\t\t\tgoto error;\n+\n+\t\ttokens[n_tokens] = token;\n+\t\tn_tokens++;\n+\t}\n+\n+\tif ((n_tokens < 3 + table->info.n_match_fields) ||\n+\t    strcmp(tokens[0], \"match\") ||\n+\t    strcmp(tokens[1 + table->info.n_match_fields], \"action\"))\n+\t\tgoto error;\n+\n+\taction = action_find(ctl, tokens[2 + table->info.n_match_fields]);\n+\tif (!action)\n+\t\tgoto error;\n+\n+\tif (n_tokens != 3 + table->info.n_match_fields +\n+\t    action->info.n_args * 2)\n+\t\tgoto error;\n+\n+\t/*\n+\t * Match.\n+\t */\n+\tfor (i = 0; i < table->info.n_match_fields; i++) {\n+\t\tstruct rte_swx_ctl_table_match_field_info *mf = &table->mf[i];\n+\t\tchar *mf_val = tokens[1 + i];\n+\t\tuint64_t val;\n+\n+\t\tval = strtoull(mf_val, &mf_val, 0);\n+\t\tif (mf_val[0])\n+\t\t\tgoto error;\n+\n+\t\t/* Endianness conversion. */\n+\t\tif (mf->is_header)\n+\t\t\tval = field_hton(val, mf->n_bits);\n+\n+\t\t/* Copy key and key_mask to entry. */\n+\t\tmemcpy(&entry->key[(mf->offset - table->mf[0].offset) / 8],\n+\t\t       (uint8_t *)&val,\n+\t\t       mf->n_bits / 8);\n+\n+\t\t/* TBD Set entry->key_mask for wilcard and LPM match tables. */\n+\t}\n+\n+\t/*\n+\t * Action.\n+\t */\n+\t/* action_id. */\n+\tentry->action_id = action - ctl->actions;\n+\n+\t/* action_data. */\n+\tfor (i = 0; i < action->info.n_args; i++) {\n+\t\tstruct rte_swx_ctl_action_arg_info *arg = &action->args[i];\n+\t\tchar *arg_name, *arg_val;\n+\t\tuint64_t val;\n+\t\tint is_nbo = 0;\n+\n+\t\targ_name = tokens[3 + table->info.n_match_fields + i * 2];\n+\t\targ_val = tokens[3 + table->info.n_match_fields + i * 2 + 1];\n+\n+\t\tif (strcmp(arg_name, arg->name) ||\n+\t\t    (strlen(arg_val) < 4) ||\n+\t\t    ((arg_val[0] != 'H') && (arg_val[0] != 'N')) ||\n+\t\t    (arg_val[1] != '(') ||\n+\t\t    (arg_val[strlen(arg_val) - 1] != ')'))\n+\t\t\tgoto error;\n+\n+\t\tif (arg_val[0] == 'N')\n+\t\t\tis_nbo = 1;\n+\n+\t\targ_val[strlen(arg_val) - 1] = 0; /* Remove the ')'. */\n+\t\targ_val += 2; /* Remove the \"H(\" or \"N(\". */\n+\n+\t\tval = strtoull(arg_val, &arg_val, 0);\n+\t\tif (arg_val[0])\n+\t\t\tgoto error;\n+\n+\t\t/* Endianness conversion. */\n+\t\tif (is_nbo)\n+\t\t\tval = field_hton(val, arg->n_bits);\n+\n+\t\t/* Copy to entry. */\n+\t\tmemcpy(&entry->action_data[arg_offset],\n+\t\t       (uint8_t *)&val,\n+\t\t       arg->n_bits / 8);\n+\n+\t\targ_offset += arg->n_bits / 8;\n+\t}\n+\n+\treturn entry;\n+\n+error:\n+\ttable_entry_free(entry);\n+\tfree(s0);\n+\treturn NULL;\n+}\n+\n+int\n+rte_swx_ctl_pipeline_table_fprintf(FILE *f,\n+\t\t\t\t   struct rte_swx_ctl_pipeline *ctl,\n+\t\t\t\t   const char *table_name)\n+{\n+\tstruct table *table;\n+\tstruct rte_swx_table_entry *entry;\n+\tuint32_t n_entries = 0, i;\n+\n+\tif (!f || !ctl || !table_name || !table_name[0])\n+\t\treturn -EINVAL;\n+\n+\ttable = table_find(ctl, table_name);\n+\tif (!table)\n+\t\treturn -EINVAL;\n+\n+\t/* Table. */\n+\tfprintf(f, \"# Table %s: key size %u bytes, key offset %u, key mask [\",\n+\t\ttable->info.name,\n+\t\ttable->params.key_size,\n+\t\ttable->params.key_offset);\n+\n+\tfor (i = 0; i < table->params.key_size; i++)\n+\t\tfprintf(f, \"%02x\", table->params.key_mask0[i]);\n+\n+\tfprintf(f, \"], action data size %u bytes\\n\",\n+\t\ttable->params.action_data_size);\n+\n+\t/* Table entries. */\n+\tTAILQ_FOREACH(entry, &table->entries, node) {\n+\t\tstruct action *action = &ctl->actions[entry->action_id];\n+\n+\t\tfprintf(f, \"match \");\n+\t\tfor (i = 0; i < table->params.key_size; i++)\n+\t\t\tfprintf(f, \"%02x\", entry->key[i]);\n+\n+\t\tfprintf(f, \" action %s \", action->info.name);\n+\t\tfor (i = 0; i < action->data_size; i++)\n+\t\t\tfprintf(f, \"%02x\", entry->action_data[i]);\n+\n+\t\tfprintf(f, \"\\n\");\n+\t\tn_entries++;\n+\t}\n+\n+\tTAILQ_FOREACH(entry, &table->pending_modify0, node) {\n+\t\tstruct action *action = &ctl->actions[entry->action_id];\n+\n+\t\tfprintf(f, \"match \");\n+\t\tfor (i = 0; i < table->params.key_size; i++)\n+\t\t\tfprintf(f, \"%02x\", entry->key[i]);\n+\n+\t\tfprintf(f, \" action %s \", action->info.name);\n+\t\tfor (i = 0; i < action->data_size; i++)\n+\t\t\tfprintf(f, \"%02x\", entry->action_data[i]);\n+\n+\t\tfprintf(f, \"\\n\");\n+\t\tn_entries++;\n+\t}\n+\n+\tTAILQ_FOREACH(entry, &table->pending_delete, node) {\n+\t\tstruct action *action = &ctl->actions[entry->action_id];\n+\n+\t\tfprintf(f, \"match \");\n+\t\tfor (i = 0; i < table->params.key_size; i++)\n+\t\t\tfprintf(f, \"%02x\", entry->key[i]);\n+\n+\t\tfprintf(f, \" action %s \", action->info.name);\n+\t\tfor (i = 0; i < action->data_size; i++)\n+\t\t\tfprintf(f, \"%02x\", entry->action_data[i]);\n+\n+\t\tfprintf(f, \"\\n\");\n+\t\tn_entries++;\n+\t}\n+\n+\tfprintf(f, \"# Table %s currently has %u entries.\\n\",\n+\t\ttable_name,\n+\t\tn_entries);\n+\treturn 0;\n+}\ndiff --git a/lib/librte_pipeline/rte_swx_ctl.h b/lib/librte_pipeline/rte_swx_ctl.h\nindex bdcc24cee..786adedb2 100644\n--- a/lib/librte_pipeline/rte_swx_ctl.h\n+++ b/lib/librte_pipeline/rte_swx_ctl.h\n@@ -391,6 +391,176 @@ 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+/*\n+ * High Level Reference Table Update API.\n+ */\n+\n+/** Pipeline control opaque data structure. */\n+struct rte_swx_ctl_pipeline;\n+\n+/**\n+ * Pipeline control create\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @return\n+ *   Pipeline control handle, on success, or NULL, on error.\n+ */\n+__rte_experimental\n+struct rte_swx_ctl_pipeline *\n+rte_swx_ctl_pipeline_create(struct rte_swx_pipeline *p);\n+\n+/**\n+ * Pipeline table entry add\n+ *\n+ * Schedule entry for addition to table or update as part of the next commit\n+ * operation.\n+ *\n+ * @param[in] ctl\n+ *   Pipeline control handle.\n+ * @param[in] table_name\n+ *   Table name.\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 argument.\n+ */\n+__rte_experimental\n+int\n+rte_swx_ctl_pipeline_table_entry_add(struct rte_swx_ctl_pipeline *ctl,\n+\t\t\t\t     const char *table_name,\n+\t\t\t\t     struct rte_swx_table_entry *entry);\n+\n+/**\n+ * Pipeline table default entry add\n+ *\n+ * Schedule table default entry update as part of the next commit operation.\n+ *\n+ * @param[in] ctl\n+ *   Pipeline control handle.\n+ * @param[in] table_name\n+ *   Table name.\n+ * @param[in] entry\n+ *   The new table default entry. The *key* and *key_mask* entry fields are\n+ *   ignored.\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_ctl_pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *ctl,\n+\t\t\t\t\t     const char *table_name,\n+\t\t\t\t\t     struct rte_swx_table_entry *entry);\n+\n+/**\n+ * Pipeline table entry delete\n+ *\n+ * Schedule entry for deletion from table as part of the next commit operation.\n+ * Request is silently discarded if no such entry exists.\n+ *\n+ * @param[in] ctl\n+ *   Pipeline control handle.\n+ * @param[in] table_name\n+ *   Table name.\n+ * @param[in] entry\n+ *   Entry to be deleted from the table. The *action_id* and *action_data* entry\n+ *   fields are ignored.\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_ctl_pipeline_table_entry_delete(struct rte_swx_ctl_pipeline *ctl,\n+\t\t\t\t\tconst char *table_name,\n+\t\t\t\t\tstruct rte_swx_table_entry *entry);\n+\n+/**\n+ * Pipeline commit\n+ *\n+ * Perform all the scheduled table work.\n+ *\n+ * @param[in] ctl\n+ *   Pipeline control handle.\n+ * @param[in] abort_on_fail\n+ *   When non-zero (false), all the scheduled work is discarded after a failed\n+ *   commit. Otherwise, the scheduled work is still kept pending for the next\n+ *   commit.\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_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl,\n+\t\t\t    int abort_on_fail);\n+\n+/**\n+ * Pipeline abort\n+ *\n+ * Discard all the scheduled table work.\n+ *\n+ * @param[in] ctl\n+ *   Pipeline control handle.\n+ */\n+__rte_experimental\n+void\n+rte_swx_ctl_pipeline_abort(struct rte_swx_ctl_pipeline *ctl);\n+\n+/**\n+ * Pipeline table entry read\n+ *\n+ * Read table entry from string.\n+ *\n+ * @param[in] ctl\n+ *   Pipeline control handle.\n+ * @param[in] table_name\n+ *   Table name.\n+ * @param[in] string\n+ *   String containing the table entry.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument.\n+ */\n+__rte_experimental\n+struct rte_swx_table_entry *\n+rte_swx_ctl_pipeline_table_entry_read(struct rte_swx_ctl_pipeline *ctl,\n+\t\t\t\t      const char *table_name,\n+\t\t\t\t      const char *string);\n+\n+/**\n+ * Pipeline table print to file\n+ *\n+ * Print all the table entries to file.\n+ *\n+ * @param[in] f\n+ *   Output file.\n+ * @param[in] ctl\n+ *   Pipeline control handle.\n+ * @param[in] table_name\n+ *   Table name.\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_ctl_pipeline_table_fprintf(FILE *f,\n+\t\t\t\t   struct rte_swx_ctl_pipeline *ctl,\n+\t\t\t\t   const char *table_name);\n+\n+/**\n+ * Pipeline control free\n+ *\n+ * @param[in] ctl\n+ *   Pipeline control handle.\n+ */\n+__rte_experimental\n+void\n+rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline *ctl);\n+\n #ifdef __cplusplus\n }\n #endif\n",
    "prefixes": [
        "v3",
        "31/41"
    ]
}