From patchwork Sat Apr 30 13:40:58 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Dumitrescu X-Patchwork-Id: 110544 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id CA151A0507; Sat, 30 Apr 2022 15:41:03 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 6701A40DDB; Sat, 30 Apr 2022 15:41:03 +0200 (CEST) Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) by mails.dpdk.org (Postfix) with ESMTP id C6FFF4069D for ; Sat, 30 Apr 2022 15:41:01 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1651326062; x=1682862062; h=from:to:subject:date:message-id:in-reply-to:references; bh=qkGPDtR0sZ7A9NovZSIi1J0mN8HA++J0MnVCHqOXyr8=; b=c+zWVnkNZq/0EOOV2zIRUjKSskKlU+l9Cx4mVWrfIgPM9vcocYSjskaK o+NttxUYAAlLGayTINIKYIH7AL1+o/X20MAgCdclz3q7z6yqxjq7ruTFZ pJQW8frCZS3PC/Fb7VSjVQS1NOlL3EhfIeUuFiO2F8/O3Dqj6wWex2zvU nZTzen7q7RvYg4qFjARcv8TwJ2BLpwe7A/F2tDj8vjLspftRzr/vm1hMn ehjCpLQeiV7XjShZaGIGVwmLepEyfbfQb1ujQSZXqjxW1S5UhB01NHMp3 PvJmcDrk/D9Qw7w9gkb1JR+dPK2sKLNpA62ZuUqh7KcJv/KDfCPfzAObd g==; X-IronPort-AV: E=McAfee;i="6400,9594,10332"; a="265728715" X-IronPort-AV: E=Sophos;i="5.91,188,1647327600"; d="scan'208";a="265728715" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 30 Apr 2022 06:41:00 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.91,188,1647327600"; d="scan'208";a="515302448" Received: from silpixa00400573.ir.intel.com (HELO silpixa00400573.ger.corp.intel.com) ([10.237.223.107]) by orsmga003.jf.intel.com with ESMTP; 30 Apr 2022 06:40:59 -0700 From: Cristian Dumitrescu 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Add support for hash functions that compute a signature for an array of bytes read from a packet header or meta-data. Useful for flow affinity-based load balancing. Signed-off-by: Cristian Dumitrescu --- Depends-on: series-22635 ("[V2,1/3] table: improve learner table timers") lib/pipeline/rte_swx_pipeline.c | 212 +++++++++++++++++++++++ lib/pipeline/rte_swx_pipeline.h | 41 +++++ lib/pipeline/rte_swx_pipeline_internal.h | 71 ++++++++ lib/pipeline/version.map | 3 + 4 files changed, 327 insertions(+) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 84d2c24311..ea7df98ecb 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -6,6 +6,9 @@ #include #include +#include +#include + #include #include #include @@ -1166,6 +1169,94 @@ extern_func_free(struct rte_swx_pipeline *p) } } +/* + * Hash function. + */ +static struct hash_func * +hash_func_find(struct rte_swx_pipeline *p, const char *name) +{ + struct hash_func *elem; + + TAILQ_FOREACH(elem, &p->hash_funcs, node) + if (strcmp(elem->name, name) == 0) + return elem; + + return NULL; +} + +int +rte_swx_pipeline_hash_func_register(struct rte_swx_pipeline *p, + const char *name, + rte_swx_hash_func_t func) +{ + struct hash_func *f; + + CHECK(p, EINVAL); + + CHECK_NAME(name, EINVAL); + CHECK(!hash_func_find(p, name), EEXIST); + + CHECK(func, EINVAL); + + /* Node allocation. */ + f = calloc(1, sizeof(struct hash_func)); + CHECK(func, ENOMEM); + + /* Node initialization. */ + strcpy(f->name, name); + f->func = func; + f->id = p->n_hash_funcs; + + /* Node add to tailq. */ + TAILQ_INSERT_TAIL(&p->hash_funcs, f, node); + p->n_hash_funcs++; + + return 0; +} + +static int +hash_func_build(struct rte_swx_pipeline *p) +{ + struct hash_func *func; + + /* Memory allocation. */ + p->hash_func_runtime = calloc(p->n_hash_funcs, sizeof(struct hash_func_runtime)); + CHECK(p->hash_func_runtime, ENOMEM); + + /* Hash function. */ + TAILQ_FOREACH(func, &p->hash_funcs, node) { + struct hash_func_runtime *r = &p->hash_func_runtime[func->id]; + + r->func = func->func; + } + + return 0; +} + +static void +hash_func_build_free(struct rte_swx_pipeline *p) +{ + free(p->hash_func_runtime); + p->hash_func_runtime = NULL; +} + +static void +hash_func_free(struct rte_swx_pipeline *p) +{ + hash_func_build_free(p); + + for ( ; ; ) { + struct hash_func *elem; + + elem = TAILQ_FIRST(&p->hash_funcs); + if (!elem) + break; + + TAILQ_REMOVE(&p->hash_funcs, elem, node); + free(elem); + } +} + /* * Header. */ @@ -2796,6 +2887,60 @@ instr_extern_func_exec(struct rte_swx_pipeline *p) thread_yield_cond(p, done ^ 1); } +/* + * hash. + */ +static int +instr_hash_translate(struct rte_swx_pipeline *p, + struct action *action, + char **tokens, + int n_tokens, + struct instruction *instr, + struct instruction_data *data __rte_unused) +{ + struct hash_func *func; + struct field *dst, *src_first, *src_last; + uint32_t src_struct_id_first = 0, src_struct_id_last = 0; + + CHECK(n_tokens == 5, EINVAL); + + func = hash_func_find(p, tokens[1]); + CHECK(func, EINVAL); + + dst = metadata_field_parse(p, tokens[2]); + CHECK(dst, EINVAL); + + src_first = struct_field_parse(p, action, tokens[3], &src_struct_id_first); + CHECK(src_first, EINVAL); + + src_last = struct_field_parse(p, action, tokens[4], &src_struct_id_last); + CHECK(src_last, EINVAL); + CHECK(src_struct_id_first == src_struct_id_last, EINVAL); + + instr->type = INSTR_HASH_FUNC; + instr->hash_func.hash_func_id = (uint8_t)func->id; + instr->hash_func.dst.offset = (uint8_t)dst->offset / 8; + instr->hash_func.dst.n_bits = (uint8_t)dst->n_bits; + instr->hash_func.src.struct_id = (uint8_t)src_struct_id_first; + instr->hash_func.src.offset = (uint16_t)src_first->offset / 8; + instr->hash_func.src.n_bytes = (uint16_t)((src_last->offset - src_first->offset) / 8); + + return 0; +} + +static inline void +instr_hash_func_exec(struct rte_swx_pipeline *p) +{ + struct thread *t = &p->threads[p->thread_id]; + struct instruction *ip = t->ip; + + /* Extern function execute. */ + __instr_hash_func_exec(p, t, ip); + + /* Thread. */ + thread_ip_inc(p); +} + /* * mov. */ @@ -6142,6 +6287,14 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); + if (!strcmp(tokens[tpos], "hash")) + return instr_hash_translate(p, + action, + &tokens[tpos], + n_tokens - tpos, + instr, + data); + if (!strcmp(tokens[tpos], "jmp")) return instr_jmp_translate(p, action, @@ -7119,6 +7272,7 @@ static instr_exec_t instruction_table[] = { [INSTR_LEARNER_FORGET] = instr_forget_exec, [INSTR_EXTERN_OBJ] = instr_extern_obj_exec, [INSTR_EXTERN_FUNC] = instr_extern_func_exec, + [INSTR_HASH_FUNC] = instr_hash_func_exec, [INSTR_JMP] = instr_jmp_exec, [INSTR_JMP_VALID] = instr_jmp_valid_exec, @@ -9462,6 +9616,7 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p) instruction_table_free(p); metadata_free(p); header_free(p); + hash_func_free(p); extern_func_free(p); extern_obj_free(p); mirroring_free(p); @@ -9563,6 +9718,22 @@ table_types_register(struct rte_swx_pipeline *p) return 0; } +static int +hash_funcs_register(struct rte_swx_pipeline *p) +{ + int status; + + status = rte_swx_pipeline_hash_func_register(p, "jhash", rte_jhash); + if (status) + return status; + + status = rte_swx_pipeline_hash_func_register(p, "crc32", rte_hash_crc); + if (status) + return status; + + return 0; +} + int rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node) { @@ -9588,6 +9759,7 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node) TAILQ_INIT(&pipeline->extern_types); TAILQ_INIT(&pipeline->extern_objs); TAILQ_INIT(&pipeline->extern_funcs); + TAILQ_INIT(&pipeline->hash_funcs); TAILQ_INIT(&pipeline->headers); TAILQ_INIT(&pipeline->actions); TAILQ_INIT(&pipeline->table_types); @@ -9613,6 +9785,10 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node) if (status) goto error; + status = hash_funcs_register(pipeline); + if (status) + goto error; + *p = pipeline; return 0; @@ -9689,6 +9865,10 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p) if (status) goto error; + status = hash_func_build(p); + if (status) + goto error; + status = header_build(p); if (status) goto error; @@ -9746,6 +9926,7 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p) instruction_table_build_free(p); metadata_build_free(p); header_build_free(p); + hash_func_build_free(p); extern_func_build_free(p); extern_obj_build_free(p); mirroring_build_free(p); @@ -10680,6 +10861,7 @@ instr_type_to_name(struct instruction *instr) case INSTR_EXTERN_OBJ: return "INSTR_EXTERN_OBJ"; case INSTR_EXTERN_FUNC: return "INSTR_EXTERN_FUNC"; + case INSTR_HASH_FUNC: return "INSTR_HASH_FUNC"; case INSTR_JMP: return "INSTR_JMP"; case INSTR_JMP_VALID: return "INSTR_JMP_VALID"; @@ -11098,6 +11280,34 @@ instr_alu_export(struct instruction *instr, FILE *f) instr->alu.src_val); } +static void +instr_hash_export(struct instruction *instr, FILE *f) +{ + fprintf(f, + "\t{\n" + "\t\t.type = %s,\n" + "\t\t.hash_func = {\n" + "\t\t\t.hash_func_id = %u,\n" + "\t\t\t.dst = {\n" + "\t\t\t\t.offset = %u,\n" + "\t\t\t\t.n_bits = %u,\n" + "\t\t\t},\n" + "\t\t\t.src = {\n" + "\t\t\t\t.struct_id = %u,\n" + "\t\t\t\t.offset = %u,\n" + "\t\t\t\t.n_bytes = %u,\n" + "\t\t\t},\n" + "\t\t},\n" + "\t},\n", + instr_type_to_name(instr), + instr->hash_func.hash_func_id, + instr->hash_func.dst.offset, + instr->hash_func.dst.n_bits, + instr->hash_func.src.struct_id, + instr->hash_func.src.offset, + instr->hash_func.src.n_bytes); +} + static void instr_reg_export(struct instruction *instr __rte_unused, FILE *f __rte_unused) { @@ -11637,6 +11847,7 @@ static instruction_export_t export_table[] = { [INSTR_EXTERN_OBJ] = instr_extern_export, [INSTR_EXTERN_FUNC] = instr_extern_export, + [INSTR_HASH_FUNC] = instr_hash_export, [INSTR_JMP] = instr_jmp_export, [INSTR_JMP_VALID] = instr_jmp_export, @@ -11860,6 +12071,7 @@ instr_type_to_func(struct instruction *instr) case INSTR_EXTERN_OBJ: return NULL; case INSTR_EXTERN_FUNC: return NULL; + case INSTR_HASH_FUNC: return "__instr_hash_func_exec"; case INSTR_JMP: return NULL; case INSTR_JMP_VALID: return NULL; diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h index a5a0954915..adc7fa53b7 100644 --- a/lib/pipeline/rte_swx_pipeline.h +++ b/lib/pipeline/rte_swx_pipeline.h @@ -301,6 +301,47 @@ rte_swx_pipeline_extern_func_register(struct rte_swx_pipeline *p, const char *name, const char *mailbox_struct_type_name, rte_swx_extern_func_t func); +/* + * Hash function. + */ + +/** + * Hash function prototype + * + * @param[in] key + * Key to hash. Must be non-NULL. + * @param[in] length + * Key length in bytes. + * @param[in] seed + * Hash seed. + * @return + * Hash value. + */ +typedef uint32_t +(*rte_swx_hash_func_t)(const void *key, + uint32_t length, + uint32_t seed); + +/** + * Pipeline hash function register + * + * @param[in] p + * Pipeline handle. + * @param[in] name + * Hash function name. + * @param[in] func + * Hash function. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument; + * -ENOMEM: Not enough space/cannot allocate memory; + * -EEXIST: Hash function with this name already exists. + */ +__rte_experimental +int +rte_swx_pipeline_hash_func_register(struct rte_swx_pipeline *p, + const char *name, + rte_swx_hash_func_t func); /* * Packet headers and meta-data diff --git a/lib/pipeline/rte_swx_pipeline_internal.h b/lib/pipeline/rte_swx_pipeline_internal.h index dd1d499f57..5feee8eff6 100644 --- a/lib/pipeline/rte_swx_pipeline_internal.h +++ b/lib/pipeline/rte_swx_pipeline_internal.h @@ -183,6 +183,22 @@ struct extern_func_runtime { rte_swx_extern_func_t func; }; +/* + * Hash function. + */ +struct hash_func { + TAILQ_ENTRY(hash_func) node; + char name[RTE_SWX_NAME_SIZE]; + rte_swx_hash_func_t func; + uint32_t id; +}; + +TAILQ_HEAD(hash_func_tailq, hash_func); + +struct hash_func_runtime { + rte_swx_hash_func_t func; +}; + /* * Header. */ @@ -492,6 +508,15 @@ enum instruction_type { /* extern f.func */ INSTR_EXTERN_FUNC, + /* hash HASH_FUNC_NAME dst src_first src_last + * Compute hash value over range of struct fields. + * dst = M + * src_first = HMEFT + * src_last = HMEFT + * src_first and src_last must be fields within the same struct + */ + INSTR_HASH_FUNC, + /* jmp LABEL * Unconditional jump */ @@ -629,6 +654,21 @@ struct instr_extern_func { uint8_t ext_func_id; }; +struct instr_hash_func { + uint8_t hash_func_id; + + struct { + uint8_t offset; + uint8_t n_bits; + } dst; + + struct { + uint8_t struct_id; + uint16_t offset; + uint16_t n_bytes; + } src; +}; + struct instr_dst_src { struct instr_operand dst; union { @@ -714,6 +754,7 @@ struct instruction { struct instr_learn learn; struct instr_extern_obj ext_obj; struct instr_extern_func ext_func; + struct instr_hash_func hash_func; struct instr_jmp jmp; }; }; @@ -1425,6 +1466,7 @@ struct rte_swx_pipeline { struct extern_type_tailq extern_types; struct extern_obj_tailq extern_objs; struct extern_func_tailq extern_funcs; + struct hash_func_tailq hash_funcs; struct header_tailq headers; struct struct_type *metadata_st; uint32_t metadata_struct_id; @@ -1446,6 +1488,7 @@ struct rte_swx_pipeline { struct table_statistics *table_stats; struct selector_statistics *selector_stats; struct learner_statistics *learner_stats; + struct hash_func_runtime *hash_func_runtime; struct regarray_runtime *regarray_runtime; struct metarray_runtime *metarray_runtime; struct instruction *instructions; @@ -1461,6 +1504,7 @@ struct rte_swx_pipeline { uint32_t n_mirroring_sessions; uint32_t n_extern_objs; uint32_t n_extern_funcs; + uint32_t n_hash_funcs; uint32_t n_actions; uint32_t n_tables; uint32_t n_selectors; @@ -2357,6 +2401,33 @@ __instr_extern_func_exec(struct rte_swx_pipeline *p __rte_unused, return done; } +/* + * hash. + */ +static inline void +__instr_hash_func_exec(struct rte_swx_pipeline *p, + struct thread *t, + const struct instruction *ip) +{ + uint32_t hash_func_id = ip->hash_func.hash_func_id; + uint32_t dst_offset = ip->hash_func.dst.offset; + uint32_t n_dst_bits = ip->hash_func.dst.n_bits; + uint32_t src_struct_id = ip->hash_func.src.struct_id; + uint32_t src_offset = ip->hash_func.src.offset; + uint32_t n_src_bytes = ip->hash_func.src.n_bytes; + + struct hash_func_runtime *func = &p->hash_func_runtime[hash_func_id]; + uint8_t *src_ptr = t->structs[src_struct_id]; + uint32_t result; + + TRACE("[Thread %2u] hash %u\n", + p->thread_id, + hash_func_id); + + result = func->func(&src_ptr[src_offset], n_src_bytes, 0); + METADATA_WRITE(t, dst_offset, n_dst_bits, result); +} + /* * mov. */ diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map index 44332aad26..f2af0b5004 100644 --- a/lib/pipeline/version.map +++ b/lib/pipeline/version.map @@ -140,4 +140,7 @@ EXPERIMENTAL { rte_swx_ctl_learner_info_get; rte_swx_ctl_learner_match_field_info_get; rte_swx_pipeline_learner_config; + + #added in 22.07 + rte_swx_pipeline_hash_func_register; }; From patchwork Sat Apr 30 13:40:59 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Dumitrescu X-Patchwork-Id: 110545 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 4C4D6A0507; Sat, 30 Apr 2022 15:41:08 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 45C94410E6; Sat, 30 Apr 2022 15:41:04 +0200 (CEST) Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) by mails.dpdk.org (Postfix) with ESMTP id B170A4069D for ; Sat, 30 Apr 2022 15:41:02 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1651326062; x=1682862062; h=from:to:subject:date:message-id:in-reply-to:references; bh=LW2rvRnIbEG748B3X1gxMmKEh76nSL5fhqyN5vCwykM=; b=gEOqqAXJfbQ/ZY9YbOolRUK0P7gbBfedAwsY9XC0dCmoUN0uHiclW1+3 hwpEyTGVNhTFASxPH2XWUhrg4kM1UZi0cEaiLlcp+Mue0TvbIUFcN5++N nF+HuJZiYa0sj1w3iASPC8KFtv6JAFtppbZZMKTzeWKXCJkOJ1Li4tAas RaYfAb/6ZdfLqxvWV91S70F2a/1ROs5TsEFEIIQk9JSY4tXlzeQGxsa/a he/ifRMTciuIgD8ti4khuIbxTbN3u0jqiIcb/E2GdzQio7Q21i4fdhHuj BAsvXu5M3fChlNWrH2vPWIILjO1WbsDDJ1LezsN0u7tDTnlesf10BddYj Q==; X-IronPort-AV: E=McAfee;i="6400,9594,10332"; a="265728720" X-IronPort-AV: E=Sophos;i="5.91,188,1647327600"; d="scan'208";a="265728720" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 30 Apr 2022 06:41:01 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.91,188,1647327600"; d="scan'208";a="515302450" Received: from silpixa00400573.ir.intel.com (HELO silpixa00400573.ger.corp.intel.com) ([10.237.223.107]) by orsmga003.jf.intel.com with ESMTP; 30 Apr 2022 06:41:00 -0700 From: Cristian Dumitrescu To: dev@dpdk.org Subject: [PATCH V3 2/2] examples/pipeline: support hash functions Date: Sat, 30 Apr 2022 14:40:59 +0100 Message-Id: <20220430134059.80255-2-cristian.dumitrescu@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220430134059.80255-1-cristian.dumitrescu@intel.com> References: <20220430133349.80152-1-cristian.dumitrescu@intel.com> <20220430134059.80255-1-cristian.dumitrescu@intel.com> X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Add example for hash function operation. Signed-off-by: Cristian Dumitrescu --- examples/pipeline/examples/hash_func.cli | 35 +++++++ examples/pipeline/examples/hash_func.spec | 107 ++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 examples/pipeline/examples/hash_func.cli create mode 100644 examples/pipeline/examples/hash_func.spec diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli new file mode 100644 index 0000000000..df6e6e6205 --- /dev/null +++ b/examples/pipeline/examples/hash_func.cli @@ -0,0 +1,35 @@ +; SPDX-License-Identifier: BSD-3-Clause +; Copyright(c) 2022 Intel Corporation + +; +; Customize the LINK parameters to match your setup. +; +mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0 + +link LINK0 dev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on +link LINK1 dev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on +link LINK2 dev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on +link LINK3 dev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on + +; +; PIPELINE0 setup. +; +pipeline PIPELINE0 create 0 +pipeline PIPELINE0 mirror slots 4 sessions 16 + +pipeline PIPELINE0 port in 0 link LINK0 rxq 0 bsz 32 +pipeline PIPELINE0 port in 1 link LINK1 rxq 0 bsz 32 +pipeline PIPELINE0 port in 2 link LINK2 rxq 0 bsz 32 +pipeline PIPELINE0 port in 3 link LINK3 rxq 0 bsz 32 + +pipeline PIPELINE0 port out 0 link LINK0 txq 0 bsz 32 +pipeline PIPELINE0 port out 1 link LINK1 txq 0 bsz 32 +pipeline PIPELINE0 port out 2 link LINK2 txq 0 bsz 32 +pipeline PIPELINE0 port out 3 link LINK3 txq 0 bsz 32 + +pipeline PIPELINE0 build ./examples/pipeline/examples/hash_func.spec + +; +; Pipelines-to-threads mapping. +; +thread 1 pipeline PIPELINE0 enable diff --git a/examples/pipeline/examples/hash_func.spec b/examples/pipeline/examples/hash_func.spec new file mode 100644 index 0000000000..22c9e13411 --- /dev/null +++ b/examples/pipeline/examples/hash_func.spec @@ -0,0 +1,107 @@ +; SPDX-License-Identifier: BSD-3-Clause +; Copyright(c) 2022 Intel Corporation + +; This simple example illustrates how to compute a hash signature over an n-tuple set of fields read +; from the packet headers and/or the packet meta-data by using the "hash" instruction. In this +; specific example, the n-tuple is the classical DiffServ 5-tuple. + +// +// Headers +// +struct ethernet_h { + bit<48> dst_addr + bit<48> src_addr + bit<16> ethertype +} + +struct ipv4_h { + bit<8> ver_ihl + bit<8> diffserv + bit<16> total_len + bit<16> identification + bit<16> flags_offset + bit<8> ttl + bit<8> protocol + bit<16> hdr_checksum + bit<32> src_addr + bit<32> dst_addr +} + +struct udp_h { + bit<16> src_port + bit<16> dst_port + bit<16> length + bit<16> checksum +} + +header ethernet instanceof ethernet_h +header ipv4 instanceof ipv4_h +header udp instanceof udp_h + +// +// Meta-data. +// +struct metadata_t { + bit<32> port + bit<32> src_addr + bit<32> dst_addr + bit<8> protocol + bit<16> src_port + bit<16> dst_port + bit<32> hash +} + +metadata instanceof metadata_t + +// +// Pipeline. +// +apply { + // + // RX and parse. + // + rx m.port + extract h.ethernet + extract h.ipv4 + extract h.udp + + // + // Prepare the n-tuple to be hashed in meta-data. + // + // This is required when: + // a) The n-tuple fields are part of different headers; + // b) Some n-tuple fields come from headers and some from meta-data. + // + mov m.src_addr h.ipv4.src_addr + mov m.dst_addr h.ipv4.dst_addr + mov m.protocol h.ipv4.protocol + mov m.src_port h.udp.src_port + mov m.dst_port h.udp.dst_port + + // + // Compute the hash over the n-tuple. + // + // Details: + // a) Hash function: jhash; + // b) Destination (i.e. hash result): m.hash; + // c) Source (i.e. n-tuple to be hashed): The 5-tuple formed by the meta-data fields + // (m.src_addr, m.dst_addr, m.protocol, m.src_port, m.dst_port). Only the first and + // the last n-tuple fields are specified in the hash instruction, but all the fields + // in between are part of the n-tuple to be hashed. + // + hash jhash m.hash m.src_addr m.dst_port + + // + // Use the computed hash to create a uniform distribution of pkts across the 4 output ports. + // + and m.hash 3 + mov m.port m.hash + + // + // De-parse and TX. + // + emit h.ethernet + emit h.ipv4 + emit h.udp + tx m.port +}