* [PATCH 1/2] pipeline: support hash functions
@ 2022-04-30 13:18 Cristian Dumitrescu
2022-04-30 13:18 ` [PATCH 2/2] examples/pipeline: " Cristian Dumitrescu
2022-04-30 13:33 ` [PATCH V2 1/2] pipeline: " Cristian Dumitrescu
0 siblings, 2 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2022-04-30 13:18 UTC (permalink / raw)
To: dev
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 <cristian.dumitrescu@intel.com>
---
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 <errno.h>
#include <dlfcn.h>
+#include <rte_jhash.h>
+#include <rte_hash_crc.h>
+
#include <rte_swx_port_ethdev.h>
#include <rte_swx_port_fd.h>
#include <rte_swx_port_ring.h>
@@ -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..cb6842d116 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;
};
--
2.17.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/2] examples/pipeline: support hash functions
2022-04-30 13:18 [PATCH 1/2] pipeline: support hash functions Cristian Dumitrescu
@ 2022-04-30 13:18 ` Cristian Dumitrescu
2022-04-30 13:33 ` [PATCH V2 1/2] pipeline: " Cristian Dumitrescu
1 sibling, 0 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2022-04-30 13:18 UTC (permalink / raw)
To: dev
Add example for hash function operation.
Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
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
+}
--
2.17.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH V2 1/2] pipeline: support hash functions
2022-04-30 13:18 [PATCH 1/2] pipeline: support hash functions Cristian Dumitrescu
2022-04-30 13:18 ` [PATCH 2/2] examples/pipeline: " Cristian Dumitrescu
@ 2022-04-30 13:33 ` Cristian Dumitrescu
2022-04-30 13:33 ` [PATCH V2 2/2] examples/pipeline: " Cristian Dumitrescu
2022-04-30 13:40 ` [PATCH V3 1/2] pipeline: " Cristian Dumitrescu
1 sibling, 2 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2022-04-30 13:33 UTC (permalink / raw)
To: dev
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 <cristian.dumitrescu@intel.com>
---
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 <errno.h>
#include <dlfcn.h>
+#include <rte_jhash.h>
+#include <rte_hash_crc.h>
+
#include <rte_swx_port_ethdev.h>
#include <rte_swx_port_fd.h>
#include <rte_swx_port_ring.h>
@@ -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;
};
--
2.17.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH V2 2/2] examples/pipeline: support hash functions
2022-04-30 13:33 ` [PATCH V2 1/2] pipeline: " Cristian Dumitrescu
@ 2022-04-30 13:33 ` Cristian Dumitrescu
2022-04-30 13:40 ` [PATCH V3 1/2] pipeline: " Cristian Dumitrescu
1 sibling, 0 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2022-04-30 13:33 UTC (permalink / raw)
To: dev
Add example for hash function operation.
Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
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
+}
--
2.17.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH V3 1/2] pipeline: support hash functions
2022-04-30 13:33 ` [PATCH V2 1/2] pipeline: " Cristian Dumitrescu
2022-04-30 13:33 ` [PATCH V2 2/2] examples/pipeline: " Cristian Dumitrescu
@ 2022-04-30 13:40 ` Cristian Dumitrescu
2022-04-30 13:40 ` [PATCH V3 2/2] examples/pipeline: " Cristian Dumitrescu
2022-05-04 11:23 ` [PATCH V4 1/2] pipeline: " Cristian Dumitrescu
1 sibling, 2 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2022-04-30 13:40 UTC (permalink / raw)
To: dev
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 <cristian.dumitrescu@intel.com>
---
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 <errno.h>
#include <dlfcn.h>
+#include <rte_jhash.h>
+#include <rte_hash_crc.h>
+
#include <rte_swx_port_ethdev.h>
#include <rte_swx_port_fd.h>
#include <rte_swx_port_ring.h>
@@ -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;
};
--
2.17.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH V3 2/2] examples/pipeline: support hash functions
2022-04-30 13:40 ` [PATCH V3 1/2] pipeline: " Cristian Dumitrescu
@ 2022-04-30 13:40 ` Cristian Dumitrescu
2022-05-04 11:23 ` [PATCH V4 1/2] pipeline: " Cristian Dumitrescu
1 sibling, 0 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2022-04-30 13:40 UTC (permalink / raw)
To: dev
Add example for hash function operation.
Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
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
+}
--
2.17.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH V4 1/2] pipeline: support hash functions
2022-04-30 13:40 ` [PATCH V3 1/2] pipeline: " Cristian Dumitrescu
2022-04-30 13:40 ` [PATCH V3 2/2] examples/pipeline: " Cristian Dumitrescu
@ 2022-05-04 11:23 ` Cristian Dumitrescu
2022-05-04 11:23 ` [PATCH V4 2/2] examples/pipeline: " Cristian Dumitrescu
2022-05-20 22:31 ` [PATCH V5 1/2] pipeline: " Cristian Dumitrescu
1 sibling, 2 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2022-05-04 11:23 UTC (permalink / raw)
To: dev
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 <cristian.dumitrescu@intel.com>
---
Depends-on: series-22635 ("[V2,1/3] table: improve learner table timers")
Change log:
V4:
-Fixed the key length for the hash to pick up the last field correctly.
V3:
-Added "Depends-on" statement for fixing the patch apply issues.
V2:
-Minor style issue fixed.
lib/pipeline/rte_swx_pipeline.c | 213 +++++++++++++++++++++++
lib/pipeline/rte_swx_pipeline.h | 41 +++++
lib/pipeline/rte_swx_pipeline_internal.h | 71 ++++++++
lib/pipeline/version.map | 3 +
4 files changed, 328 insertions(+)
diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 84d2c24311..ac80cbef38 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -6,6 +6,9 @@
#include <errno.h>
#include <dlfcn.h>
+#include <rte_jhash.h>
+#include <rte_hash_crc.h>
+
#include <rte_swx_port_ethdev.h>
#include <rte_swx_port_fd.h>
#include <rte_swx_port_ring.h>
@@ -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,61 @@ 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_last->n_bits -
+ 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 +6288,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 +7273,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 +9617,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 +9719,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 +9760,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 +9786,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 +9866,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 +9927,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 +10862,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 +11281,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 +11848,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 +12072,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;
};
--
2.17.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH V4 2/2] examples/pipeline: support hash functions
2022-05-04 11:23 ` [PATCH V4 1/2] pipeline: " Cristian Dumitrescu
@ 2022-05-04 11:23 ` Cristian Dumitrescu
2022-05-20 22:31 ` [PATCH V5 1/2] pipeline: " Cristian Dumitrescu
1 sibling, 0 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2022-05-04 11:23 UTC (permalink / raw)
To: dev
Add example for hash function operation.
Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
Change log:
V4:
-Removed redundant line in the CLI file.
-Added comment on the crc32 hash function in the .spec file.
examples/pipeline/examples/hash_func.cli | 34 +++++++
examples/pipeline/examples/hash_func.spec | 107 ++++++++++++++++++++++
2 files changed, 141 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..d65cd62d17
--- /dev/null
+++ b/examples/pipeline/examples/hash_func.cli
@@ -0,0 +1,34 @@
+; 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 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..a3275a17da
--- /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. Another available option is crc32.
+ // 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
+}
--
2.17.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH V5 1/2] pipeline: support hash functions
2022-05-04 11:23 ` [PATCH V4 1/2] pipeline: " Cristian Dumitrescu
2022-05-04 11:23 ` [PATCH V4 2/2] examples/pipeline: " Cristian Dumitrescu
@ 2022-05-20 22:31 ` Cristian Dumitrescu
2022-05-20 22:31 ` [PATCH V5 2/2] examples/pipeline: " Cristian Dumitrescu
2022-06-01 14:24 ` [PATCH V5 1/2] pipeline: " Thomas Monjalon
1 sibling, 2 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2022-05-20 22:31 UTC (permalink / raw)
To: dev
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 <cristian.dumitrescu@intel.com>
---
Depends-on: series-23072 ("[V3,1/3] table: improve learner table timers")
Change log:
V5:
-Updated the version.map file due to change in the dependency patch set.
V4:
-Fixed the key length for the hash to pick up the last field correctly.
V3:
-Added "Depends-on" statement for fixing the patch apply issues.
V2:
-Minor style issue fixed.
lib/pipeline/rte_swx_pipeline.c | 213 +++++++++++++++++++++++
lib/pipeline/rte_swx_pipeline.h | 41 +++++
lib/pipeline/rte_swx_pipeline_internal.h | 71 ++++++++
lib/pipeline/version.map | 1 +
4 files changed, 326 insertions(+)
diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 0d8c184ae7..b4b44ad897 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -6,6 +6,9 @@
#include <errno.h>
#include <dlfcn.h>
+#include <rte_jhash.h>
+#include <rte_hash_crc.h>
+
#include <rte_swx_port_ethdev.h>
#include <rte_swx_port_fd.h>
#include <rte_swx_port_ring.h>
@@ -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,61 @@ 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_last->n_bits -
+ 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 +6288,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 +7273,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 +9617,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 +9719,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 +9760,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 +9786,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 +9866,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 +9927,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);
@@ -10731,6 +10913,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";
@@ -11149,6 +11332,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)
{
@@ -11688,6 +11899,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,
@@ -11911,6 +12123,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 130278ff0d..8312307a7a 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -144,4 +144,5 @@ EXPERIMENTAL {
#added in 22.07
rte_swx_ctl_pipeline_learner_timeout_get;
rte_swx_ctl_pipeline_learner_timeout_set;
+ rte_swx_pipeline_hash_func_register;
};
--
2.17.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH V5 2/2] examples/pipeline: support hash functions
2022-05-20 22:31 ` [PATCH V5 1/2] pipeline: " Cristian Dumitrescu
@ 2022-05-20 22:31 ` Cristian Dumitrescu
2022-06-01 14:24 ` [PATCH V5 1/2] pipeline: " Thomas Monjalon
1 sibling, 0 replies; 11+ messages in thread
From: Cristian Dumitrescu @ 2022-05-20 22:31 UTC (permalink / raw)
To: dev
Add example for hash function operation.
Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
Change log:
V4:
-Removed redundant line in the CLI file.
-Added comment on the crc32 hash function in the .spec file.
examples/pipeline/examples/hash_func.cli | 34 +++++++
examples/pipeline/examples/hash_func.spec | 107 ++++++++++++++++++++++
2 files changed, 141 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..d65cd62d17
--- /dev/null
+++ b/examples/pipeline/examples/hash_func.cli
@@ -0,0 +1,34 @@
+; 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 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..a3275a17da
--- /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. Another available option is crc32.
+ // 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
+}
--
2.17.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH V5 1/2] pipeline: support hash functions
2022-05-20 22:31 ` [PATCH V5 1/2] pipeline: " Cristian Dumitrescu
2022-05-20 22:31 ` [PATCH V5 2/2] examples/pipeline: " Cristian Dumitrescu
@ 2022-06-01 14:24 ` Thomas Monjalon
1 sibling, 0 replies; 11+ messages in thread
From: Thomas Monjalon @ 2022-06-01 14:24 UTC (permalink / raw)
To: Cristian Dumitrescu; +Cc: dev
21/05/2022 00:31, Cristian Dumitrescu:
> 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 <cristian.dumitrescu@intel.com>
Series applied, thanks.
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2022-06-01 14:24 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-30 13:18 [PATCH 1/2] pipeline: support hash functions Cristian Dumitrescu
2022-04-30 13:18 ` [PATCH 2/2] examples/pipeline: " Cristian Dumitrescu
2022-04-30 13:33 ` [PATCH V2 1/2] pipeline: " Cristian Dumitrescu
2022-04-30 13:33 ` [PATCH V2 2/2] examples/pipeline: " Cristian Dumitrescu
2022-04-30 13:40 ` [PATCH V3 1/2] pipeline: " Cristian Dumitrescu
2022-04-30 13:40 ` [PATCH V3 2/2] examples/pipeline: " Cristian Dumitrescu
2022-05-04 11:23 ` [PATCH V4 1/2] pipeline: " Cristian Dumitrescu
2022-05-04 11:23 ` [PATCH V4 2/2] examples/pipeline: " Cristian Dumitrescu
2022-05-20 22:31 ` [PATCH V5 1/2] pipeline: " Cristian Dumitrescu
2022-05-20 22:31 ` [PATCH V5 2/2] examples/pipeline: " Cristian Dumitrescu
2022-06-01 14:24 ` [PATCH V5 1/2] pipeline: " Thomas Monjalon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).