[V5,1/2] pipeline: support hash functions

Message ID 20220520223125.2036-1-cristian.dumitrescu@intel.com (mailing list archive)
State Accepted, archived
Delegated to: Thomas Monjalon
Headers
Series [V5,1/2] pipeline: support hash functions |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/iol-testing warning apply patch failure

Commit Message

Cristian Dumitrescu May 20, 2022, 10:31 p.m. UTC
  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(+)
  

Comments

Thomas Monjalon June 1, 2022, 2:24 p.m. UTC | #1
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.
  

Patch

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;
 };