get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 95239,
    "url": "https://patches.dpdk.org/api/patches/95239/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/20210702224608.66233-2-cristian.dumitrescu@intel.com/",
    "project": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20210702224608.66233-2-cristian.dumitrescu@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20210702224608.66233-2-cristian.dumitrescu@intel.com",
    "date": "2021-07-02T22:46:05",
    "name": "[V3,2/5] table: add support for selector tables",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": true,
    "hash": "7ad06ae3a687decc56a05cfdf486c6a316944369",
    "submitter": {
        "id": 19,
        "url": "https://patches.dpdk.org/api/people/19/?format=api",
        "name": "Cristian Dumitrescu",
        "email": "cristian.dumitrescu@intel.com"
    },
    "delegate": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/20210702224608.66233-2-cristian.dumitrescu@intel.com/mbox/",
    "series": [
        {
            "id": 17609,
            "url": "https://patches.dpdk.org/api/series/17609/?format=api",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=17609",
            "date": "2021-07-02T22:46:04",
            "name": "[V3,1/5] examples/pipeline: improve table update CLI commands",
            "version": 3,
            "mbox": "https://patches.dpdk.org/series/17609/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/95239/comments/",
    "check": "success",
    "checks": "https://patches.dpdk.org/api/patches/95239/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 7EC2EA0C3F;\n\tSat,  3 Jul 2021 00:46:21 +0200 (CEST)",
            "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 5341841431;\n\tSat,  3 Jul 2021 00:46:16 +0200 (CEST)",
            "from mga09.intel.com (mga09.intel.com [134.134.136.24])\n by mails.dpdk.org (Postfix) with ESMTP id E65E54014F\n for <dev@dpdk.org>; Sat,  3 Jul 2021 00:46:12 +0200 (CEST)",
            "from orsmga008.jf.intel.com ([10.7.209.65])\n by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 02 Jul 2021 15:46:12 -0700",
            "from silpixa00400573.ir.intel.com (HELO\n silpixa00400573.ger.corp.intel.com) ([10.237.223.107])\n by orsmga008.jf.intel.com with ESMTP; 02 Jul 2021 15:46:11 -0700"
        ],
        "X-IronPort-AV": [
            "E=McAfee;i=\"6200,9189,10033\"; a=\"208742589\"",
            "E=Sophos;i=\"5.83,320,1616482800\"; d=\"scan'208\";a=\"208742589\"",
            "E=Sophos;i=\"5.83,320,1616482800\"; d=\"scan'208\";a=\"456058938\""
        ],
        "X-ExtLoop1": "1",
        "From": "Cristian Dumitrescu <cristian.dumitrescu@intel.com>",
        "To": "dev@dpdk.org",
        "Date": "Fri,  2 Jul 2021 23:46:05 +0100",
        "Message-Id": "<20210702224608.66233-2-cristian.dumitrescu@intel.com>",
        "X-Mailer": "git-send-email 2.17.1",
        "In-Reply-To": "<20210702224608.66233-1-cristian.dumitrescu@intel.com>",
        "References": "<20210702223949.65944-1-cristian.dumitrescu@intel.com>\n <20210702224608.66233-1-cristian.dumitrescu@intel.com>",
        "Subject": "[dpdk-dev] [PATCH V3 2/5] table: add support for selector tables",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "A selector table is made up of groups of weighted members, with a\ngiven member potentially part of several groups. The select operation\nreturns a member ID by first selecting a group based on an input group\nID and then selecting a member within that group based on hashing one\nor several input header/meta-data fields. It is very useful for\nimplementing an ECMP/WCMP-enabled FIB or a load balancer. It is part\nof the action selector described by the P4 Portable Switch\nArchitecture (PSA) specification.\n\nSigned-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>\n---\n lib/table/meson.build              |   2 +\n lib/table/rte_swx_table_selector.c | 581 +++++++++++++++++++++++++++++\n lib/table/rte_swx_table_selector.h | 203 ++++++++++\n lib/table/version.map              |   8 +\n 4 files changed, 794 insertions(+)\n create mode 100644 lib/table/rte_swx_table_selector.c\n create mode 100644 lib/table/rte_swx_table_selector.h",
    "diff": "diff --git a/lib/table/meson.build b/lib/table/meson.build\nindex b7b70b805..16e55f086 100644\n--- a/lib/table/meson.build\n+++ b/lib/table/meson.build\n@@ -3,6 +3,7 @@\n \n sources = files(\n         'rte_swx_table_em.c',\n+\t'rte_swx_table_selector.c',\n         'rte_swx_table_wm.c',\n         'rte_table_acl.c',\n         'rte_table_array.c',\n@@ -20,6 +21,7 @@ headers = files(\n         'rte_lru.h',\n         'rte_swx_table.h',\n         'rte_swx_table_em.h',\n+\t'rte_swx_table_selector.h',\n         'rte_swx_table_wm.h',\n         'rte_table.h',\n         'rte_table_acl.h',\ndiff --git a/lib/table/rte_swx_table_selector.c b/lib/table/rte_swx_table_selector.c\nnew file mode 100644\nindex 000000000..541ebc221\n--- /dev/null\n+++ b/lib/table/rte_swx_table_selector.c\n@@ -0,0 +1,581 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2021 Intel Corporation\n+ */\n+#include <stdlib.h>\n+#include <string.h>\n+#include <stdio.h>\n+#include <errno.h>\n+\n+#include <rte_common.h>\n+#include <rte_prefetch.h>\n+\n+#include \"rte_swx_table_selector.h\"\n+\n+#ifndef RTE_SWX_TABLE_SELECTOR_HUGE_PAGES_DISABLE\n+\n+#include <rte_malloc.h>\n+\n+static void *\n+env_calloc(size_t size, size_t alignment, int numa_node)\n+{\n+\treturn rte_zmalloc_socket(NULL, size, alignment, numa_node);\n+}\n+\n+static void\n+env_free(void *start, size_t size __rte_unused)\n+{\n+\trte_free(start);\n+}\n+\n+#else\n+\n+#include <numa.h>\n+\n+static void *\n+env_calloc(size_t size, size_t alignment __rte_unused, int numa_node)\n+{\n+\tvoid *start;\n+\n+\tif (numa_available() == -1)\n+\t\treturn NULL;\n+\n+\tstart = numa_alloc_onnode(size, numa_node);\n+\tif (!start)\n+\t\treturn NULL;\n+\n+\tmemset(start, 0, size);\n+\treturn start;\n+}\n+\n+static void\n+env_free(void *start, size_t size)\n+{\n+\tif ((numa_available() == -1) || !start)\n+\t\treturn;\n+\n+\tnuma_free(start, size);\n+}\n+\n+#endif\n+\n+#if defined(RTE_ARCH_X86_64)\n+\n+#include <x86intrin.h>\n+\n+#define crc32_u64(crc, v) _mm_crc32_u64(crc, v)\n+\n+#else\n+\n+static inline uint64_t\n+crc32_u64_generic(uint64_t crc, uint64_t value)\n+{\n+\tint i;\n+\n+\tcrc = (crc & 0xFFFFFFFFLLU) ^ value;\n+\tfor (i = 63; i >= 0; i--) {\n+\t\tuint64_t mask;\n+\n+\t\tmask = -(crc & 1LLU);\n+\t\tcrc = (crc >> 1LLU) ^ (0x82F63B78LLU & mask);\n+\t}\n+\n+\treturn crc;\n+}\n+\n+#define crc32_u64(crc, v) crc32_u64_generic(crc, v)\n+\n+#endif\n+\n+/* Key size needs to be one of: 8, 16, 32 or 64. */\n+static inline uint32_t\n+hash(void *key, void *key_mask, uint32_t key_size, uint32_t seed)\n+{\n+\tuint64_t *k = key;\n+\tuint64_t *m = key_mask;\n+\tuint64_t k0, k2, k5, crc0, crc1, crc2, crc3, crc4, crc5;\n+\n+\tswitch (key_size) {\n+\tcase 8:\n+\t\tcrc0 = crc32_u64(seed, k[0] & m[0]);\n+\t\treturn crc0;\n+\n+\tcase 16:\n+\t\tk0 = k[0] & m[0];\n+\n+\t\tcrc0 = crc32_u64(k0, seed);\n+\t\tcrc1 = crc32_u64(k0 >> 32, k[1] & m[1]);\n+\n+\t\tcrc0 ^= crc1;\n+\n+\t\treturn crc0;\n+\n+\tcase 32:\n+\t\tk0 = k[0] & m[0];\n+\t\tk2 = k[2] & m[2];\n+\n+\t\tcrc0 = crc32_u64(k0, seed);\n+\t\tcrc1 = crc32_u64(k0 >> 32, k[1] & m[1]);\n+\n+\t\tcrc2 = crc32_u64(k2, k[3] & m[3]);\n+\t\tcrc3 = k2 >> 32;\n+\n+\t\tcrc0 = crc32_u64(crc0, crc1);\n+\t\tcrc1 = crc32_u64(crc2, crc3);\n+\n+\t\tcrc0 ^= crc1;\n+\n+\t\treturn crc0;\n+\n+\tcase 64:\n+\t\tk0 = k[0] & m[0];\n+\t\tk2 = k[2] & m[2];\n+\t\tk5 = k[5] & m[5];\n+\n+\t\tcrc0 = crc32_u64(k0, seed);\n+\t\tcrc1 = crc32_u64(k0 >> 32, k[1] & m[1]);\n+\n+\t\tcrc2 = crc32_u64(k2, k[3] & m[3]);\n+\t\tcrc3 = crc32_u64(k2 >> 32, k[4] & m[4]);\n+\n+\t\tcrc4 = crc32_u64(k5, k[6] & m[6]);\n+\t\tcrc5 = crc32_u64(k5 >> 32, k[7] & m[7]);\n+\n+\t\tcrc0 = crc32_u64(crc0, (crc1 << 32) ^ crc2);\n+\t\tcrc1 = crc32_u64(crc3, (crc4 << 32) ^ crc5);\n+\n+\t\tcrc0 ^= crc1;\n+\n+\t\treturn crc0;\n+\n+\tdefault:\n+\t\tcrc0 = 0;\n+\t\treturn crc0;\n+\t}\n+}\n+\n+struct group_member_info {\n+\tuint32_t member_id;\n+\tuint32_t member_weight;\n+\tuint32_t member_weight_normalized;\n+\tuint32_t count;\n+};\n+\n+struct table {\n+\t/* Input parameters */\n+\tstruct rte_swx_table_selector_params params;\n+\n+\t/* Internal. */\n+\tuint32_t *group_table;\n+\tuint64_t group_table_size;\n+\tstruct group_member_info *members;\n+\tuint32_t n_members_per_group_max_log2;\n+};\n+\n+uint64_t\n+rte_swx_table_selector_footprint_get(uint32_t n_groups_max, uint32_t n_members_per_group_max)\n+{\n+\tuint64_t group_table_size, members_size;\n+\n+\tgroup_table_size = n_groups_max * n_members_per_group_max * sizeof(uint32_t);\n+\n+\tmembers_size = n_members_per_group_max * sizeof(struct group_member_info);\n+\n+\treturn sizeof(struct table) + group_table_size + members_size;\n+}\n+\n+void\n+rte_swx_table_selector_free(void *table)\n+{\n+\tstruct table *t = table;\n+\n+\tif (!t)\n+\t\treturn;\n+\n+\tfree(t->members);\n+\n+\tenv_free(t->group_table, t->group_table_size);\n+\n+\tfree(t->params.selector_mask);\n+\n+\tfree(t);\n+}\n+\n+static int\n+table_create_check(struct rte_swx_table_selector_params *params)\n+{\n+\tif (!params)\n+\t\treturn -1;\n+\n+\tif (!params->selector_size ||\n+\t    (params->selector_size > 64) ||\n+\t    !params->n_groups_max ||\n+\t    (params->n_groups_max > 1U << 31) ||\n+\t    !params->n_members_per_group_max ||\n+\t    (params->n_members_per_group_max > 1U << 31))\n+\t\treturn -EINVAL;\n+\n+\treturn 0;\n+}\n+\n+static int\n+table_params_copy(struct table *t, struct rte_swx_table_selector_params *params)\n+{\n+\tuint32_t selector_size, i;\n+\n+\tselector_size = rte_align32pow2(params->selector_size);\n+\tif (selector_size < 8)\n+\t\tselector_size = 8;\n+\n+\tmemcpy(&t->params, params, sizeof(struct rte_swx_table_selector_params));\n+\tt->params.selector_size = selector_size;\n+\tt->params.selector_mask = NULL;\n+\tt->params.n_groups_max = rte_align32pow2(params->n_groups_max);\n+\tt->params.n_members_per_group_max = rte_align32pow2(params->n_members_per_group_max);\n+\n+\tfor (i = 0; i < 32; i++)\n+\t\tif (params->n_members_per_group_max == 1U << i)\n+\t\t\tt->n_members_per_group_max_log2 = i;\n+\n+\t/* t->params.selector_mask */\n+\tt->params.selector_mask = calloc(selector_size, sizeof(uint8_t));\n+\tif (!t->params.selector_mask)\n+\t\tgoto error;\n+\n+\tif (params->selector_mask)\n+\t\tmemcpy(t->params.selector_mask, params->selector_mask, params->selector_size);\n+\telse\n+\t\tmemset(t->params.selector_mask, 0xFF, params->selector_size);\n+\n+\treturn 0;\n+\n+error:\n+\tfree(t->params.selector_mask);\n+\tt->params.selector_mask = NULL;\n+\n+\treturn -ENOMEM;\n+}\n+\n+static int\n+group_set(struct table *t,\n+\t  uint32_t group_id,\n+\t  struct rte_swx_table_selector_group *group);\n+\n+void *\n+rte_swx_table_selector_create(struct rte_swx_table_selector_params *params,\n+\t\t\t      struct rte_swx_table_selector_group **groups,\n+\t\t\t      int numa_node)\n+{\n+\tstruct table *t = NULL;\n+\tuint32_t group_size, i;\n+\tint status;\n+\n+\t/* Check input arguments. */\n+\tstatus = table_create_check(params);\n+\tif (status)\n+\t\tgoto error;\n+\n+\t/* Table object. */\n+\tt = calloc(1, sizeof(struct table));\n+\tif (!t)\n+\t\tgoto error;\n+\n+\t/* Parameter copy. */\n+\tstatus = table_params_copy(t, params);\n+\tif (status)\n+\t\tgoto error;\n+\n+\t/* Group. */\n+\tgroup_size = params->n_members_per_group_max * sizeof(uint32_t);\n+\tt->group_table_size = params->n_groups_max * group_size;\n+\n+\tt->group_table = env_calloc(t->group_table_size, RTE_CACHE_LINE_SIZE, numa_node);\n+\tif (!t->group_table)\n+\t\tgoto error;\n+\n+\tt->members = calloc(params->n_members_per_group_max, sizeof(struct group_member_info));\n+\tif (!t->members)\n+\t\tgoto error;\n+\n+\tif (groups)\n+\t\tfor (i = 0; i < params->n_groups_max; i++)\n+\t\t\tif (groups[i]) {\n+\t\t\t\tstatus = group_set(t, i, groups[i]);\n+\t\t\t\tif (status)\n+\t\t\t\t\tgoto error;\n+\t\t\t}\n+\n+\treturn t;\n+\n+error:\n+\trte_swx_table_selector_free(t);\n+\treturn NULL;\n+}\n+\n+\n+static int\n+group_check(struct table *t, struct rte_swx_table_selector_group *group)\n+{\n+\tstruct rte_swx_table_selector_member *elem;\n+\tuint32_t n_members = 0;\n+\n+\tif (!group)\n+\t\treturn 0;\n+\n+\tTAILQ_FOREACH(elem, &group->members, node) {\n+\t\tstruct rte_swx_table_selector_member *e;\n+\t\tuint32_t n = 0;\n+\n+\t\t/* Check group size. */\n+\t\tif (n_members >= t->params.n_members_per_group_max)\n+\t\t\treturn -ENOSPC;\n+\n+\t\t/* Check attributes of the current group member. */\n+\t\tif (elem->member_id >= t->params.n_members_per_group_max ||\n+\t\t    !elem->member_weight)\n+\t\t\treturn -ENOSPC;\n+\n+\t\t/* Check against duplicate member IDs. */\n+\t\tTAILQ_FOREACH(e, &group->members, node)\n+\t\t\tif (e->member_id == elem->member_id)\n+\t\t\t\tn++;\n+\n+\t\tif (n != 1)\n+\t\t\treturn -EINVAL;\n+\n+\t\t/* Update group size. */\n+\t\tn_members++;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static uint32_t\n+members_read(struct group_member_info *members,\n+\t     struct rte_swx_table_selector_group *group)\n+{\n+\tstruct rte_swx_table_selector_member *elem;\n+\tuint32_t n_members = 0;\n+\n+\tif (!group)\n+\t\treturn 0;\n+\n+\tTAILQ_FOREACH(elem, &group->members, node) {\n+\t\tstruct group_member_info *m = &members[n_members];\n+\n+\t\tmemset(m, 0, sizeof(struct group_member_info));\n+\n+\t\tm->member_id = elem->member_id;\n+\t\tm->member_weight = elem->member_weight;\n+\t\tm->member_weight_normalized = elem->member_weight;\n+\n+\t\tn_members++;\n+\t}\n+\n+\treturn n_members;\n+}\n+\n+static uint32_t\n+members_min_weight_find(struct group_member_info *members, uint32_t n_members)\n+{\n+\tuint32_t min = UINT32_MAX, i;\n+\n+\tfor (i = 0; i < n_members; i++) {\n+\t\tstruct group_member_info *m = &members[i];\n+\n+\t\tif (m->member_weight < min)\n+\t\t\tmin = m->member_weight;\n+\t}\n+\n+\treturn min;\n+}\n+\n+static uint32_t\n+members_weight_divisor_check(struct group_member_info *members,\n+\t\t\t     uint32_t n_members,\n+\t\t\t     uint32_t divisor)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < n_members; i++) {\n+\t\tstruct group_member_info *m = &members[i];\n+\n+\t\tif (m->member_weight_normalized % divisor)\n+\t\t\treturn 0; /* FALSE. */\n+\t}\n+\n+\treturn 1; /* TRUE. */\n+}\n+\n+static void\n+members_weight_divisor_apply(struct group_member_info *members,\n+\t\t\t     uint32_t n_members,\n+\t\t\t     uint32_t divisor)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < n_members; i++) {\n+\t\tstruct group_member_info *m = &members[i];\n+\n+\t\tm->member_weight_normalized /= divisor;\n+\t}\n+}\n+\n+static uint32_t\n+members_weight_sum(struct group_member_info *members, uint32_t n_members)\n+{\n+\tuint32_t result = 0, i;\n+\n+\tfor (i = 0; i < n_members; i++) {\n+\t\tstruct group_member_info *m = &members[i];\n+\n+\t\tresult += m->member_weight_normalized;\n+\t}\n+\n+\treturn result;\n+}\n+\n+static void\n+members_weight_scale(struct group_member_info *members,\n+\t\t     uint32_t n_members,\n+\t\t     uint32_t n_members_per_group_max,\n+\t\t     uint32_t weight_sum)\n+{\n+\tuint32_t multiplier, remainder, i;\n+\n+\tmultiplier = n_members_per_group_max / weight_sum;\n+\tremainder = n_members_per_group_max % weight_sum;\n+\n+\tfor (i = 0; i < n_members; i++) {\n+\t\tstruct group_member_info *m = &members[i];\n+\n+\t\tm->count = m->member_weight_normalized * multiplier;\n+\t}\n+\n+\tfor (i = 0; i < n_members; i++) {\n+\t\tstruct group_member_info *m = &members[i];\n+\t\tuint32_t min;\n+\n+\t\tmin = m->member_weight_normalized;\n+\t\tif (remainder < m->member_weight_normalized)\n+\t\t\tmin = remainder;\n+\n+\t\tm->count += min;\n+\t\tremainder -= min;\n+\t\tif (!remainder)\n+\t\t\tbreak;\n+\t}\n+}\n+\n+static void\n+members_write(struct group_member_info *members,\n+\t      uint32_t n_members,\n+\t      uint32_t *group_table)\n+{\n+\tuint32_t pos = 0, i;\n+\n+\tfor (i = 0; i < n_members; i++) {\n+\t\tstruct group_member_info *m = &members[i];\n+\t\tuint32_t j;\n+\n+\t\tfor (j = 0; j < m->count; j++)\n+\t\t\tgroup_table[pos++] = m->member_id;\n+\t}\n+}\n+\n+static int\n+group_set(struct table *t,\n+\t  uint32_t group_id,\n+\t  struct rte_swx_table_selector_group *group)\n+{\n+\tuint32_t *gt = &t->group_table[group_id * t->params.n_members_per_group_max];\n+\tstruct group_member_info *members = t->members;\n+\tuint32_t n_members, weight_min, weight_sum, divisor;\n+\tint status = 0;\n+\n+\t/* Check input arguments. */\n+\tif (group_id >= t->params.n_groups_max)\n+\t\treturn -EINVAL;\n+\n+\tstatus = group_check(t, group);\n+\tif (status)\n+\t\treturn status;\n+\n+\t/* Read group members. */\n+\tn_members = members_read(members, group);\n+\n+\tif (!n_members) {\n+\t\tmemset(gt, 0, t->params.n_members_per_group_max * sizeof(uint32_t));\n+\n+\t\treturn 0;\n+\t}\n+\n+\t/* Normalize weights. */\n+\tweight_min = members_min_weight_find(members, n_members);\n+\n+\tfor (divisor = 2; divisor <= weight_min; divisor++)\n+\t\tif (members_weight_divisor_check(members, n_members, divisor))\n+\t\t\tmembers_weight_divisor_apply(members, n_members, divisor);\n+\n+\t/* Scale weights. */\n+\tweight_sum = members_weight_sum(members, n_members);\n+\tif (weight_sum > t->params.n_members_per_group_max)\n+\t\treturn -ENOSPC;\n+\n+\tmembers_weight_scale(members, n_members, t->params.n_members_per_group_max, weight_sum);\n+\n+\t/* Write group members to the group table. */\n+\tmembers_write(members, n_members, gt);\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_swx_table_selector_group_set(void *table,\n+\t\t\t\t uint32_t group_id,\n+\t\t\t\t struct rte_swx_table_selector_group *group)\n+{\n+\tstruct table *t = table;\n+\n+\treturn group_set(t, group_id, group);\n+}\n+\n+struct mailbox {\n+\n+};\n+\n+uint64_t\n+rte_swx_table_selector_mailbox_size_get(void)\n+{\n+\treturn sizeof(struct mailbox);\n+}\n+\n+int\n+rte_swx_table_selector_select(void *table,\n+\t\t\t      void *mailbox __rte_unused,\n+\t\t\t      uint8_t **group_id_buffer,\n+\t\t\t      uint8_t **selector_buffer,\n+\t\t\t      uint8_t **member_id_buffer)\n+{\n+\tstruct table *t = table;\n+\tuint32_t *group_id_ptr, *member_id_ptr, group_id, member_id, selector, group_member_index;\n+\n+\tgroup_id_ptr = (uint32_t *)&(*group_id_buffer)[t->params.group_id_offset];\n+\n+\tmember_id_ptr = (uint32_t *)&(*member_id_buffer)[t->params.member_id_offset];\n+\n+\tgroup_id = *group_id_ptr & (t->params.n_groups_max - 1);\n+\n+\tselector = hash(&(*selector_buffer)[t->params.selector_offset],\n+\t\t\tt->params.selector_mask,\n+\t\t\tt->params.selector_size,\n+\t\t\t0);\n+\n+\tgroup_member_index = selector & (t->params.n_members_per_group_max - 1);\n+\n+\tmember_id = t->group_table[(group_id << t->n_members_per_group_max_log2) +\n+\t\t\t\t   group_member_index];\n+\n+\t*member_id_ptr = member_id;\n+\n+\treturn 1;\n+}\ndiff --git a/lib/table/rte_swx_table_selector.h b/lib/table/rte_swx_table_selector.h\nnew file mode 100644\nindex 000000000..71b6a7481\n--- /dev/null\n+++ b/lib/table/rte_swx_table_selector.h\n@@ -0,0 +1,203 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2021 Intel Corporation\n+ */\n+#ifndef __INCLUDE_RTE_SWX_TABLE_SELECTOR_H__\n+#define __INCLUDE_RTE_SWX_TABLE_SELECTOR_H__\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+/**\n+ * @file\n+ * RTE SWX Selector Table\n+ *\n+ * Selector table interface.\n+ */\n+\n+#include <stdint.h>\n+#include <sys/queue.h>\n+\n+#include <rte_compat.h>\n+\n+#include \"rte_swx_table.h\"\n+\n+/** Selector table creation parameters. */\n+struct rte_swx_table_selector_params {\n+\t/** Group ID offset. */\n+\tuint32_t group_id_offset;\n+\n+\t/** Selector size in bytes. Must be non-zero. */\n+\tuint32_t selector_size;\n+\n+\t/** Offset of the first byte of the selector within the selector buffer. */\n+\tuint32_t selector_offset;\n+\n+\t/** Mask of *selector_size* bytes logically laid over the bytes at positions\n+\t * selector_offset* .. (*selector_offset* + *selector_size* - 1) of the selector buffer in\n+\t * order to specify which bits from the selector buffer are part of the selector and which\n+\t * ones are not. A bit value of 1 in the *selector_mask* means the respective bit in the\n+\t * selector buffer is part of the selector, while a bit value of 0 means the opposite. A\n+\t * NULL value means that all the bits are part of the selector, i.e. the *selector_mask*\n+\t * is an all-ones mask.\n+\t */\n+\tuint8_t *selector_mask;\n+\n+\t/** Member ID offset. */\n+\tuint32_t member_id_offset;\n+\n+\t/** Maximum number of groups. Must be non-zero. */\n+\tuint32_t n_groups_max;\n+\n+\t/** Maximum number of members per group. Must be non-zero. */\n+\tuint32_t n_members_per_group_max;\n+};\n+\n+/** Group member parameters. */\n+struct rte_swx_table_selector_member {\n+\t/** Linked list connectivity. */\n+\tTAILQ_ENTRY(rte_swx_table_selector_member) node;\n+\n+\t/** Member ID. */\n+\tuint32_t member_id;\n+\n+\t/** Member weight. */\n+\tuint32_t member_weight;\n+};\n+\n+/** List of group members. */\n+TAILQ_HEAD(rte_swx_table_selector_member_list, rte_swx_table_selector_member);\n+\n+/** Group parameters. */\n+struct rte_swx_table_selector_group {\n+\t/** List of group members. */\n+\tstruct rte_swx_table_selector_member_list members;\n+};\n+\n+/**\n+ * Selector table memory footprint get\n+ *\n+ * @param[in] n_groups_max\n+ *   Maximum number of groups. Must be non-zero.\n+ * @param[in] n_members_per_group_max\n+ *   Maximum number of members per group. Must be non-zero.\n+ * @return\n+ *   Selector table memory footprint in bytes.\n+ */\n+__rte_experimental\n+uint64_t\n+rte_swx_table_selector_footprint_get(uint32_t n_groups_max, uint32_t n_members_per_group_max);\n+\n+/**\n+ * Selector table mailbox size get\n+ *\n+ * The mailbox is used to store the context of a select operation that is in\n+ * progress and it is passed as a parameter to the select operation. This allows\n+ * for multiple concurrent select operations into the same table.\n+ *\n+ * @return\n+ *   Selector table mailbox footprint in bytes.\n+ */\n+__rte_experimental\n+uint64_t\n+rte_swx_table_selector_mailbox_size_get(void);\n+\n+/**\n+ * Selector table create\n+ *\n+ * @param[in] params\n+ *   Selector table creation parameters.\n+ * @param[in] groups\n+ *   Groups to be added to the table at creation time. When NULL, it signifies that all groups are\n+ *   invalid, otherwise it points to a pre-allocated array of size *n_groups_max*, where a NULL\n+ *   element indicates that the associated group is invalid.\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+__rte_experimental\n+void *\n+rte_swx_table_selector_create(struct rte_swx_table_selector_params *params,\n+\t\t\t      struct rte_swx_table_selector_group **groups,\n+\t\t\t      int numa_node);\n+\n+/**\n+ * Group set\n+ *\n+ * @param[in] table\n+ *   Selector table handle.\n+ * @param[in] group_id\n+ *   Group ID.\n+ * @param[in] group\n+ *   Group parameters.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument(s);\n+ *   -ENOSPC: Too many group members.\n+ */\n+__rte_experimental\n+int\n+rte_swx_table_selector_group_set(void *table,\n+\t\t\t\t uint32_t group_id,\n+\t\t\t\t struct rte_swx_table_selector_group *group);\n+\n+/**\n+ * Selector table select\n+ *\n+ * This operation selects a member from the given group based on a hasing scheme.\n+ *\n+ * Multiple invocations of this function may be required in order to complete a single select\n+ * operation for a given table and a given group ID. The completion of the operation is flagged by\n+ * a return value of 1; 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 each on-going  operation. The mailbox\n+ * mechanism allows for multiple concurrent select operations into the same table.\n+ *\n+ * The typical reason an implementation may choose to split the operation into multiple steps is to\n+ * hide the latency of the inherrent memory read operations: before a read operation with the\n+ * source data likely not in the CPU cache, the source data prefetch is issued and the operation is\n+ * postponed in favor of some other unrelated work, which the CPU executes in parallel with the\n+ * source data being fetched into the CPU cache; later on, the operation is resumed, this time with\n+ * the source data likely to be read from the CPU cache with no CPU pipeline stall, which\n+ * significantly improves the operation performance.\n+ *\n+ * @param[in] table\n+ *   Selector table handle.\n+ * @param[in] mailbox\n+ *   Mailbox for the current operation.\n+ * @param[in] group_id_buffer\n+ *   Buffer where the input group ID is located at offset *group_id_offset*.\n+ * @param[in] selector_buffer\n+ *   Buffer where the key to select a member within the identified group is located starting from\n+ *   offset *selector_offset*. Its size must be equal to the table *selector_size*.\n+ * @param[in] member_id_buffer\n+ *   Buffer where the output member ID is to be placed at offset *member_id_offset*.\n+ * @return\n+ *   0 when the operation is not yet completed, and 1 when the operation is complete. No other\n+ *   return values are allowed.\n+ */\n+__rte_experimental\n+int\n+rte_swx_table_selector_select(void *table,\n+\t\t\t      void *mailbox,\n+\t\t\t      uint8_t **group_id_buffer,\n+\t\t\t      uint8_t **selector_buffer,\n+\t\t\t      uint8_t **member_id_buffer);\n+\n+/**\n+ * Selector table free\n+ *\n+ * @param[in] table\n+ *   Selector table handle.\n+ */\n+__rte_experimental\n+void\n+rte_swx_table_selector_free(void *table);\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif\ndiff --git a/lib/table/version.map b/lib/table/version.map\nindex eb0291ac4..29301480c 100644\n--- a/lib/table/version.map\n+++ b/lib/table/version.map\n@@ -28,4 +28,12 @@ EXPERIMENTAL {\n \n \t# added in 21.05\n \trte_swx_table_wildcard_match_ops;\n+\n+\t# added in 21.08\n+\trte_swx_table_selector_create;\n+\trte_swx_table_selector_footprint_get;\n+\trte_swx_table_selector_free;\n+\trte_swx_table_selector_group_set;\n+\trte_swx_table_selector_mailbox_size_get;\n+\trte_swx_table_selector_select;\n };\n",
    "prefixes": [
        "V3",
        "2/5"
    ]
}