get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 110544,
    "url": "https://patches.dpdk.org/api/patches/110544/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/20220430134059.80255-1-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": "<20220430134059.80255-1-cristian.dumitrescu@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20220430134059.80255-1-cristian.dumitrescu@intel.com",
    "date": "2022-04-30T13:40:58",
    "name": "[V3,1/2] pipeline: support hash functions",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "348ddad427cb386b8f2ab31ddf97fd25ad8263d2",
    "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/20220430134059.80255-1-cristian.dumitrescu@intel.com/mbox/",
    "series": [
        {
            "id": 22748,
            "url": "https://patches.dpdk.org/api/series/22748/?format=api",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=22748",
            "date": "2022-04-30T13:40:58",
            "name": "[V3,1/2] pipeline: support hash functions",
            "version": 3,
            "mbox": "https://patches.dpdk.org/series/22748/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/110544/comments/",
    "check": "warning",
    "checks": "https://patches.dpdk.org/api/patches/110544/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 CA151A0507;\n\tSat, 30 Apr 2022 15:41:03 +0200 (CEST)",
            "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 6701A40DDB;\n\tSat, 30 Apr 2022 15:41:03 +0200 (CEST)",
            "from mga04.intel.com (mga04.intel.com [192.55.52.120])\n by mails.dpdk.org (Postfix) with ESMTP id C6FFF4069D\n for <dev@dpdk.org>; Sat, 30 Apr 2022 15:41:01 +0200 (CEST)",
            "from orsmga003.jf.intel.com ([10.7.209.27])\n by fmsmga104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 30 Apr 2022 06:41:00 -0700",
            "from silpixa00400573.ir.intel.com (HELO\n silpixa00400573.ger.corp.intel.com) ([10.237.223.107])\n by orsmga003.jf.intel.com with ESMTP; 30 Apr 2022 06:40:59 -0700"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple;\n d=intel.com; i=@intel.com; q=dns/txt; s=Intel;\n t=1651326062; x=1682862062;\n h=from:to:subject:date:message-id:in-reply-to:references;\n bh=qkGPDtR0sZ7A9NovZSIi1J0mN8HA++J0MnVCHqOXyr8=;\n b=c+zWVnkNZq/0EOOV2zIRUjKSskKlU+l9Cx4mVWrfIgPM9vcocYSjskaK\n o+NttxUYAAlLGayTINIKYIH7AL1+o/X20MAgCdclz3q7z6yqxjq7ruTFZ\n pJQW8frCZS3PC/Fb7VSjVQS1NOlL3EhfIeUuFiO2F8/O3Dqj6wWex2zvU\n nZTzen7q7RvYg4qFjARcv8TwJ2BLpwe7A/F2tDj8vjLspftRzr/vm1hMn\n ehjCpLQeiV7XjShZaGIGVwmLepEyfbfQb1ujQSZXqjxW1S5UhB01NHMp3\n PvJmcDrk/D9Qw7w9gkb1JR+dPK2sKLNpA62ZuUqh7KcJv/KDfCPfzAObd g==;",
        "X-IronPort-AV": [
            "E=McAfee;i=\"6400,9594,10332\"; a=\"265728715\"",
            "E=Sophos;i=\"5.91,188,1647327600\"; d=\"scan'208\";a=\"265728715\"",
            "E=Sophos;i=\"5.91,188,1647327600\"; d=\"scan'208\";a=\"515302448\""
        ],
        "X-ExtLoop1": "1",
        "From": "Cristian Dumitrescu <cristian.dumitrescu@intel.com>",
        "To": "dev@dpdk.org",
        "Subject": "[PATCH V3 1/2] pipeline: support hash functions",
        "Date": "Sat, 30 Apr 2022 14:40:58 +0100",
        "Message-Id": "<20220430134059.80255-1-cristian.dumitrescu@intel.com>",
        "X-Mailer": "git-send-email 2.17.1",
        "In-Reply-To": "<20220430133349.80152-1-cristian.dumitrescu@intel.com>",
        "References": "<20220430133349.80152-1-cristian.dumitrescu@intel.com>",
        "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"
    },
    "content": "Add support for hash functions that compute a signature for an array\nof bytes read from a packet header or meta-data. Useful for flow\naffinity-based load balancing.\n\nSigned-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>\n---\nDepends-on: series-22635 (\"[V2,1/3] table: improve learner table timers\")\n\n lib/pipeline/rte_swx_pipeline.c          | 212 +++++++++++++++++++++++\n lib/pipeline/rte_swx_pipeline.h          |  41 +++++\n lib/pipeline/rte_swx_pipeline_internal.h |  71 ++++++++\n lib/pipeline/version.map                 |   3 +\n 4 files changed, 327 insertions(+)",
    "diff": "diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c\nindex 84d2c24311..ea7df98ecb 100644\n--- a/lib/pipeline/rte_swx_pipeline.c\n+++ b/lib/pipeline/rte_swx_pipeline.c\n@@ -6,6 +6,9 @@\n #include <errno.h>\n #include <dlfcn.h>\n \n+#include <rte_jhash.h>\n+#include <rte_hash_crc.h>\n+\n #include <rte_swx_port_ethdev.h>\n #include <rte_swx_port_fd.h>\n #include <rte_swx_port_ring.h>\n@@ -1166,6 +1169,94 @@ extern_func_free(struct rte_swx_pipeline *p)\n \t}\n }\n \n+/*\n+ * Hash function.\n+ */\n+static struct hash_func *\n+hash_func_find(struct rte_swx_pipeline *p, const char *name)\n+{\n+\tstruct hash_func *elem;\n+\n+\tTAILQ_FOREACH(elem, &p->hash_funcs, node)\n+\t\tif (strcmp(elem->name, name) == 0)\n+\t\t\treturn elem;\n+\n+\treturn NULL;\n+}\n+\n+int\n+rte_swx_pipeline_hash_func_register(struct rte_swx_pipeline *p,\n+\t\t\t\t    const char *name,\n+\t\t\t\t    rte_swx_hash_func_t func)\n+{\n+\tstruct hash_func *f;\n+\n+\tCHECK(p, EINVAL);\n+\n+\tCHECK_NAME(name, EINVAL);\n+\tCHECK(!hash_func_find(p, name), EEXIST);\n+\n+\tCHECK(func, EINVAL);\n+\n+\t/* Node allocation. */\n+\tf = calloc(1, sizeof(struct hash_func));\n+\tCHECK(func, ENOMEM);\n+\n+\t/* Node initialization. */\n+\tstrcpy(f->name, name);\n+\tf->func = func;\n+\tf->id = p->n_hash_funcs;\n+\n+\t/* Node add to tailq. */\n+\tTAILQ_INSERT_TAIL(&p->hash_funcs, f, node);\n+\tp->n_hash_funcs++;\n+\n+\treturn 0;\n+}\n+\n+static int\n+hash_func_build(struct rte_swx_pipeline *p)\n+{\n+\tstruct hash_func *func;\n+\n+\t/* Memory allocation. */\n+\tp->hash_func_runtime = calloc(p->n_hash_funcs, sizeof(struct hash_func_runtime));\n+\tCHECK(p->hash_func_runtime, ENOMEM);\n+\n+\t/* Hash function. */\n+\tTAILQ_FOREACH(func, &p->hash_funcs, node) {\n+\t\tstruct hash_func_runtime *r = &p->hash_func_runtime[func->id];\n+\n+\t\tr->func = func->func;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void\n+hash_func_build_free(struct rte_swx_pipeline *p)\n+{\n+\tfree(p->hash_func_runtime);\n+\tp->hash_func_runtime = NULL;\n+}\n+\n+static void\n+hash_func_free(struct rte_swx_pipeline *p)\n+{\n+\thash_func_build_free(p);\n+\n+\tfor ( ; ; ) {\n+\t\tstruct hash_func *elem;\n+\n+\t\telem = TAILQ_FIRST(&p->hash_funcs);\n+\t\tif (!elem)\n+\t\t\tbreak;\n+\n+\t\tTAILQ_REMOVE(&p->hash_funcs, elem, node);\n+\t\tfree(elem);\n+\t}\n+}\n+\n /*\n  * Header.\n  */\n@@ -2796,6 +2887,60 @@ instr_extern_func_exec(struct rte_swx_pipeline *p)\n \tthread_yield_cond(p, done ^ 1);\n }\n \n+/*\n+ * hash.\n+ */\n+static int\n+instr_hash_translate(struct rte_swx_pipeline *p,\n+\t\t     struct action *action,\n+\t\t     char **tokens,\n+\t\t     int n_tokens,\n+\t\t     struct instruction *instr,\n+\t\t     struct instruction_data *data __rte_unused)\n+{\n+\tstruct hash_func *func;\n+\tstruct field *dst, *src_first, *src_last;\n+\tuint32_t src_struct_id_first = 0, src_struct_id_last = 0;\n+\n+\tCHECK(n_tokens == 5, EINVAL);\n+\n+\tfunc = hash_func_find(p, tokens[1]);\n+\tCHECK(func, EINVAL);\n+\n+\tdst = metadata_field_parse(p, tokens[2]);\n+\tCHECK(dst, EINVAL);\n+\n+\tsrc_first = struct_field_parse(p, action, tokens[3], &src_struct_id_first);\n+\tCHECK(src_first, EINVAL);\n+\n+\tsrc_last = struct_field_parse(p, action, tokens[4], &src_struct_id_last);\n+\tCHECK(src_last, EINVAL);\n+\tCHECK(src_struct_id_first == src_struct_id_last, EINVAL);\n+\n+\tinstr->type = INSTR_HASH_FUNC;\n+\tinstr->hash_func.hash_func_id = (uint8_t)func->id;\n+\tinstr->hash_func.dst.offset = (uint8_t)dst->offset / 8;\n+\tinstr->hash_func.dst.n_bits = (uint8_t)dst->n_bits;\n+\tinstr->hash_func.src.struct_id = (uint8_t)src_struct_id_first;\n+\tinstr->hash_func.src.offset = (uint16_t)src_first->offset / 8;\n+\tinstr->hash_func.src.n_bytes = (uint16_t)((src_last->offset - src_first->offset) / 8);\n+\n+\treturn 0;\n+}\n+\n+static inline void\n+instr_hash_func_exec(struct rte_swx_pipeline *p)\n+{\n+\tstruct thread *t = &p->threads[p->thread_id];\n+\tstruct instruction *ip = t->ip;\n+\n+\t/* Extern function execute. */\n+\t__instr_hash_func_exec(p, t, ip);\n+\n+\t/* Thread. */\n+\tthread_ip_inc(p);\n+}\n+\n /*\n  * mov.\n  */\n@@ -6142,6 +6287,14 @@ instr_translate(struct rte_swx_pipeline *p,\n \t\t\t\t\t      instr,\n \t\t\t\t\t      data);\n \n+\tif (!strcmp(tokens[tpos], \"hash\"))\n+\t\treturn instr_hash_translate(p,\n+\t\t\t\t\t    action,\n+\t\t\t\t\t    &tokens[tpos],\n+\t\t\t\t\t    n_tokens - tpos,\n+\t\t\t\t\t    instr,\n+\t\t\t\t\t    data);\n+\n \tif (!strcmp(tokens[tpos], \"jmp\"))\n \t\treturn instr_jmp_translate(p,\n \t\t\t\t\t   action,\n@@ -7119,6 +7272,7 @@ static instr_exec_t instruction_table[] = {\n \t[INSTR_LEARNER_FORGET] = instr_forget_exec,\n \t[INSTR_EXTERN_OBJ] = instr_extern_obj_exec,\n \t[INSTR_EXTERN_FUNC] = instr_extern_func_exec,\n+\t[INSTR_HASH_FUNC] = instr_hash_func_exec,\n \n \t[INSTR_JMP] = instr_jmp_exec,\n \t[INSTR_JMP_VALID] = instr_jmp_valid_exec,\n@@ -9462,6 +9616,7 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)\n \tinstruction_table_free(p);\n \tmetadata_free(p);\n \theader_free(p);\n+\thash_func_free(p);\n \textern_func_free(p);\n \textern_obj_free(p);\n \tmirroring_free(p);\n@@ -9563,6 +9718,22 @@ table_types_register(struct rte_swx_pipeline *p)\n \treturn 0;\n }\n \n+static int\n+hash_funcs_register(struct rte_swx_pipeline *p)\n+{\n+\tint status;\n+\n+\tstatus = rte_swx_pipeline_hash_func_register(p, \"jhash\", rte_jhash);\n+\tif (status)\n+\t\treturn status;\n+\n+\tstatus = rte_swx_pipeline_hash_func_register(p, \"crc32\", rte_hash_crc);\n+\tif (status)\n+\t\treturn status;\n+\n+\treturn 0;\n+}\n+\n int\n rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)\n {\n@@ -9588,6 +9759,7 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)\n \tTAILQ_INIT(&pipeline->extern_types);\n \tTAILQ_INIT(&pipeline->extern_objs);\n \tTAILQ_INIT(&pipeline->extern_funcs);\n+\tTAILQ_INIT(&pipeline->hash_funcs);\n \tTAILQ_INIT(&pipeline->headers);\n \tTAILQ_INIT(&pipeline->actions);\n \tTAILQ_INIT(&pipeline->table_types);\n@@ -9613,6 +9785,10 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)\n \tif (status)\n \t\tgoto error;\n \n+\tstatus = hash_funcs_register(pipeline);\n+\tif (status)\n+\t\tgoto error;\n+\n \t*p = pipeline;\n \treturn 0;\n \n@@ -9689,6 +9865,10 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)\n \tif (status)\n \t\tgoto error;\n \n+\tstatus = hash_func_build(p);\n+\tif (status)\n+\t\tgoto error;\n+\n \tstatus = header_build(p);\n \tif (status)\n \t\tgoto error;\n@@ -9746,6 +9926,7 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)\n \tinstruction_table_build_free(p);\n \tmetadata_build_free(p);\n \theader_build_free(p);\n+\thash_func_build_free(p);\n \textern_func_build_free(p);\n \textern_obj_build_free(p);\n \tmirroring_build_free(p);\n@@ -10680,6 +10861,7 @@ instr_type_to_name(struct instruction *instr)\n \n \tcase INSTR_EXTERN_OBJ: return \"INSTR_EXTERN_OBJ\";\n \tcase INSTR_EXTERN_FUNC: return \"INSTR_EXTERN_FUNC\";\n+\tcase INSTR_HASH_FUNC: return \"INSTR_HASH_FUNC\";\n \n \tcase INSTR_JMP: return \"INSTR_JMP\";\n \tcase INSTR_JMP_VALID: return \"INSTR_JMP_VALID\";\n@@ -11098,6 +11280,34 @@ instr_alu_export(struct instruction *instr, FILE *f)\n \t\t\tinstr->alu.src_val);\n }\n \n+static void\n+instr_hash_export(struct instruction *instr, FILE *f)\n+{\n+\tfprintf(f,\n+\t\t\"\\t{\\n\"\n+\t\t\"\\t\\t.type = %s,\\n\"\n+\t\t\"\\t\\t.hash_func = {\\n\"\n+\t\t\"\\t\\t\\t.hash_func_id = %u,\\n\"\n+\t\t\"\\t\\t\\t.dst = {\\n\"\n+\t\t\"\\t\\t\\t\\t.offset = %u,\\n\"\n+\t\t\"\\t\\t\\t\\t.n_bits = %u,\\n\"\n+\t\t\"\\t\\t\\t},\\n\"\n+\t\t\"\\t\\t\\t.src = {\\n\"\n+\t\t\"\\t\\t\\t\\t.struct_id = %u,\\n\"\n+\t\t\"\\t\\t\\t\\t.offset = %u,\\n\"\n+\t\t\"\\t\\t\\t\\t.n_bytes = %u,\\n\"\n+\t\t\"\\t\\t\\t},\\n\"\n+\t\t\"\\t\\t},\\n\"\n+\t\t\"\\t},\\n\",\n+\t\tinstr_type_to_name(instr),\n+\t\tinstr->hash_func.hash_func_id,\n+\t\tinstr->hash_func.dst.offset,\n+\t\tinstr->hash_func.dst.n_bits,\n+\t\tinstr->hash_func.src.struct_id,\n+\t\tinstr->hash_func.src.offset,\n+\t\tinstr->hash_func.src.n_bytes);\n+}\n+\n static void\n instr_reg_export(struct instruction *instr __rte_unused, FILE *f __rte_unused)\n {\n@@ -11637,6 +11847,7 @@ static instruction_export_t export_table[] = {\n \n \t[INSTR_EXTERN_OBJ] = instr_extern_export,\n \t[INSTR_EXTERN_FUNC] = instr_extern_export,\n+\t[INSTR_HASH_FUNC] = instr_hash_export,\n \n \t[INSTR_JMP] = instr_jmp_export,\n \t[INSTR_JMP_VALID] = instr_jmp_export,\n@@ -11860,6 +12071,7 @@ instr_type_to_func(struct instruction *instr)\n \n \tcase INSTR_EXTERN_OBJ: return NULL;\n \tcase INSTR_EXTERN_FUNC: return NULL;\n+\tcase INSTR_HASH_FUNC: return \"__instr_hash_func_exec\";\n \n \tcase INSTR_JMP: return NULL;\n \tcase INSTR_JMP_VALID: return NULL;\ndiff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h\nindex a5a0954915..adc7fa53b7 100644\n--- a/lib/pipeline/rte_swx_pipeline.h\n+++ b/lib/pipeline/rte_swx_pipeline.h\n@@ -301,6 +301,47 @@ rte_swx_pipeline_extern_func_register(struct rte_swx_pipeline *p,\n \t\t\t\t      const char *name,\n \t\t\t\t      const char *mailbox_struct_type_name,\n \t\t\t\t      rte_swx_extern_func_t func);\n+/*\n+ * Hash function.\n+ */\n+\n+/**\n+ * Hash function prototype\n+ *\n+ * @param[in] key\n+ *   Key to hash. Must be non-NULL.\n+ * @param[in] length\n+ *   Key length in bytes.\n+ * @param[in] seed\n+ *   Hash seed.\n+ * @return\n+ *   Hash value.\n+ */\n+typedef uint32_t\n+(*rte_swx_hash_func_t)(const void *key,\n+\t\t       uint32_t length,\n+\t\t       uint32_t seed);\n+\n+/**\n+ * Pipeline hash function register\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[in] name\n+ *   Hash function name.\n+ * @param[in] func\n+ *   Hash function.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument;\n+ *   -ENOMEM: Not enough space/cannot allocate memory;\n+ *   -EEXIST: Hash function with this name already exists.\n+ */\n+__rte_experimental\n+int\n+rte_swx_pipeline_hash_func_register(struct rte_swx_pipeline *p,\n+\t\t\t\t    const char *name,\n+\t\t\t\t    rte_swx_hash_func_t func);\n \n /*\n  * Packet headers and meta-data\ndiff --git a/lib/pipeline/rte_swx_pipeline_internal.h b/lib/pipeline/rte_swx_pipeline_internal.h\nindex dd1d499f57..5feee8eff6 100644\n--- a/lib/pipeline/rte_swx_pipeline_internal.h\n+++ b/lib/pipeline/rte_swx_pipeline_internal.h\n@@ -183,6 +183,22 @@ struct extern_func_runtime {\n \trte_swx_extern_func_t func;\n };\n \n+/*\n+ * Hash function.\n+ */\n+struct hash_func {\n+\tTAILQ_ENTRY(hash_func) node;\n+\tchar name[RTE_SWX_NAME_SIZE];\n+\trte_swx_hash_func_t func;\n+\tuint32_t id;\n+};\n+\n+TAILQ_HEAD(hash_func_tailq, hash_func);\n+\n+struct hash_func_runtime {\n+\trte_swx_hash_func_t func;\n+};\n+\n /*\n  * Header.\n  */\n@@ -492,6 +508,15 @@ enum instruction_type {\n \t/* extern f.func */\n \tINSTR_EXTERN_FUNC,\n \n+\t/* hash HASH_FUNC_NAME dst src_first src_last\n+\t * Compute hash value over range of struct fields.\n+\t * dst = M\n+\t * src_first = HMEFT\n+\t * src_last = HMEFT\n+\t * src_first and src_last must be fields within the same struct\n+\t */\n+\tINSTR_HASH_FUNC,\n+\n \t/* jmp LABEL\n \t * Unconditional jump\n \t */\n@@ -629,6 +654,21 @@ struct instr_extern_func {\n \tuint8_t ext_func_id;\n };\n \n+struct instr_hash_func {\n+\tuint8_t hash_func_id;\n+\n+\tstruct {\n+\t\tuint8_t offset;\n+\t\tuint8_t n_bits;\n+\t} dst;\n+\n+\tstruct {\n+\t\tuint8_t struct_id;\n+\t\tuint16_t offset;\n+\t\tuint16_t n_bytes;\n+\t} src;\n+};\n+\n struct instr_dst_src {\n \tstruct instr_operand dst;\n \tunion {\n@@ -714,6 +754,7 @@ struct instruction {\n \t\tstruct instr_learn learn;\n \t\tstruct instr_extern_obj ext_obj;\n \t\tstruct instr_extern_func ext_func;\n+\t\tstruct instr_hash_func hash_func;\n \t\tstruct instr_jmp jmp;\n \t};\n };\n@@ -1425,6 +1466,7 @@ struct rte_swx_pipeline {\n \tstruct extern_type_tailq extern_types;\n \tstruct extern_obj_tailq extern_objs;\n \tstruct extern_func_tailq extern_funcs;\n+\tstruct hash_func_tailq hash_funcs;\n \tstruct header_tailq headers;\n \tstruct struct_type *metadata_st;\n \tuint32_t metadata_struct_id;\n@@ -1446,6 +1488,7 @@ struct rte_swx_pipeline {\n \tstruct table_statistics *table_stats;\n \tstruct selector_statistics *selector_stats;\n \tstruct learner_statistics *learner_stats;\n+\tstruct hash_func_runtime *hash_func_runtime;\n \tstruct regarray_runtime *regarray_runtime;\n \tstruct metarray_runtime *metarray_runtime;\n \tstruct instruction *instructions;\n@@ -1461,6 +1504,7 @@ struct rte_swx_pipeline {\n \tuint32_t n_mirroring_sessions;\n \tuint32_t n_extern_objs;\n \tuint32_t n_extern_funcs;\n+\tuint32_t n_hash_funcs;\n \tuint32_t n_actions;\n \tuint32_t n_tables;\n \tuint32_t n_selectors;\n@@ -2357,6 +2401,33 @@ __instr_extern_func_exec(struct rte_swx_pipeline *p __rte_unused,\n \treturn done;\n }\n \n+/*\n+ * hash.\n+ */\n+static inline void\n+__instr_hash_func_exec(struct rte_swx_pipeline *p,\n+\t\t       struct thread *t,\n+\t\t       const struct instruction *ip)\n+{\n+\tuint32_t hash_func_id = ip->hash_func.hash_func_id;\n+\tuint32_t dst_offset = ip->hash_func.dst.offset;\n+\tuint32_t n_dst_bits = ip->hash_func.dst.n_bits;\n+\tuint32_t src_struct_id = ip->hash_func.src.struct_id;\n+\tuint32_t src_offset = ip->hash_func.src.offset;\n+\tuint32_t n_src_bytes = ip->hash_func.src.n_bytes;\n+\n+\tstruct hash_func_runtime *func = &p->hash_func_runtime[hash_func_id];\n+\tuint8_t *src_ptr = t->structs[src_struct_id];\n+\tuint32_t result;\n+\n+\tTRACE(\"[Thread %2u] hash %u\\n\",\n+\t      p->thread_id,\n+\t      hash_func_id);\n+\n+\tresult = func->func(&src_ptr[src_offset], n_src_bytes, 0);\n+\tMETADATA_WRITE(t, dst_offset, n_dst_bits, result);\n+}\n+\n /*\n  * mov.\n  */\ndiff --git a/lib/pipeline/version.map b/lib/pipeline/version.map\nindex 44332aad26..f2af0b5004 100644\n--- a/lib/pipeline/version.map\n+++ b/lib/pipeline/version.map\n@@ -140,4 +140,7 @@ EXPERIMENTAL {\n \trte_swx_ctl_learner_info_get;\n \trte_swx_ctl_learner_match_field_info_get;\n \trte_swx_pipeline_learner_config;\n+\n+\t#added in 22.07\n+\trte_swx_pipeline_hash_func_register;\n };\n",
    "prefixes": [
        "V3",
        "1/2"
    ]
}