DPDK patches and discussions
 help / color / mirror / Atom feed
* [PATCH 1/9] pipeline: move specification data structures to internal header
@ 2022-07-18 13:07 Cristian Dumitrescu
  2022-07-18 13:07 ` [PATCH 2/9] pipeline: add pipeline specification data structure Cristian Dumitrescu
                   ` (9 more replies)
  0 siblings, 10 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:07 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Move all the pipeline object specification data structures to an
internal header file.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 126 +------------------
 lib/pipeline/rte_swx_pipeline_spec.h | 176 +++++++++++++++++++++++++++
 2 files changed, 177 insertions(+), 125 deletions(-)
 create mode 100644 lib/pipeline/rte_swx_pipeline_spec.h

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 904b9eb471..5e07b4f794 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -9,7 +9,7 @@
 
 #include <rte_common.h>
 
-#include "rte_swx_pipeline.h"
+#include "rte_swx_pipeline_spec.h"
 
 #ifndef MAX_LINE_LENGTH
 #define MAX_LINE_LENGTH 2048
@@ -34,15 +34,7 @@
 
 /*
  * extobj.
- *
- * extobj OBJ_NAME instanceof OBJ_TYPE [ pragma OBJ_CREATE_ARGS ]
  */
-struct extobj_spec {
-	char *name;
-	char *extern_type_name;
-	char *pragma;
-};
-
 static void
 extobj_spec_free(struct extobj_spec *s)
 {
@@ -104,18 +96,7 @@ extobj_statement_parse(struct extobj_spec *s,
 /*
  * struct.
  *
- * struct STRUCT_TYPE_NAME {
- *	bit<SIZE> | varbit<SIZE> FIELD_NAME
- *	...
- * }
  */
-struct struct_spec {
-	char *name;
-	struct rte_swx_field_params *fields;
-	uint32_t n_fields;
-	int varbit;
-};
-
 static void
 struct_spec_free(struct struct_spec *s)
 {
@@ -293,13 +274,7 @@ struct_block_parse(struct struct_spec *s,
 /*
  * header.
  *
- * header HEADER_NAME instanceof STRUCT_TYPE_NAME
  */
-struct header_spec {
-	char *name;
-	char *struct_type_name;
-};
-
 static void
 header_spec_free(struct header_spec *s)
 {
@@ -351,12 +326,7 @@ header_statement_parse(struct header_spec *s,
 /*
  * metadata.
  *
- * metadata instanceof STRUCT_TYPE_NAME
  */
-struct metadata_spec {
-	char *struct_type_name;
-};
-
 static void
 metadata_spec_free(struct metadata_spec *s)
 {
@@ -400,18 +370,7 @@ metadata_statement_parse(struct metadata_spec *s,
 /*
  * action.
  *
- * action ACTION_NAME args none | instanceof STRUCT_TYPE_NAME {
- *	INSTRUCTION
- *	...
- * }
  */
-struct action_spec {
-	char *name;
-	char *args_struct_type_name;
-	const char **instructions;
-	uint32_t n_instructions;
-};
-
 static void
 action_spec_free(struct action_spec *s)
 {
@@ -540,29 +499,7 @@ action_block_parse(struct action_spec *s,
 /*
  * table.
  *
- * table TABLE_NAME {
- *	key {
- *		MATCH_FIELD_NAME exact | wildcard | lpm
- *		...
- *	}
- *	actions {
- *		ACTION_NAME [ @tableonly | @defaultonly ]
- *		...
- *	}
- *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
- *	instanceof TABLE_TYPE_NAME
- *	pragma ARGS
- *	size SIZE
- * }
  */
-struct table_spec {
-	char *name;
-	struct rte_swx_pipeline_table_params params;
-	char *recommended_table_type_name;
-	char *args;
-	uint32_t size;
-};
-
 static void
 table_spec_free(struct table_spec *s)
 {
@@ -1084,22 +1021,7 @@ table_block_parse(struct table_spec *s,
 /*
  * selector.
  *
- * selector SELECTOR_NAME {
- *	group_id FIELD_NAME
- *	selector {
- *		FIELD_NAME
- *		...
- *	}
- *	member_id FIELD_NAME
- *	n_groups N_GROUPS
- *	n_members_per_group N_MEMBERS_PER_GROUP
- * }
  */
-struct selector_spec {
-	char *name;
-	struct rte_swx_pipeline_selector_params params;
-};
-
 static void
 selector_spec_free(struct selector_spec *s)
 {
@@ -1385,31 +1307,7 @@ selector_block_parse(struct selector_spec *s,
 /*
  * learner.
  *
- * learner LEARNER_NAME {
- *	key {
- *		MATCH_FIELD_NAME
- *		...
- *	}
- *	actions {
- *		ACTION_NAME [ @tableonly | @defaultonly]
- *		...
- *	}
- *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
- *	size SIZE
- *	timeout {
- *		TIMEOUT_IN_SECONDS
- *		...
- *	}
- * }
  */
-struct learner_spec {
-	char *name;
-	struct rte_swx_pipeline_learner_params params;
-	uint32_t size;
-	uint32_t *timeout;
-	uint32_t n_timeouts;
-};
-
 static void
 learner_spec_free(struct learner_spec *s)
 {
@@ -1958,14 +1856,7 @@ learner_block_parse(struct learner_spec *s,
 /*
  * regarray.
  *
- * regarray NAME size SIZE initval INITVAL
  */
-struct regarray_spec {
-	char *name;
-	uint64_t init_val;
-	uint32_t size;
-};
-
 static void
 regarray_spec_free(struct regarray_spec *s)
 {
@@ -2033,13 +1924,7 @@ regarray_statement_parse(struct regarray_spec *s,
 /*
  * metarray.
  *
- * metarray NAME size SIZE
  */
-struct metarray_spec {
-	char *name;
-	uint32_t size;
-};
-
 static void
 metarray_spec_free(struct metarray_spec *s)
 {
@@ -2095,16 +1980,7 @@ metarray_statement_parse(struct metarray_spec *s,
 /*
  * apply.
  *
- * apply {
- *	INSTRUCTION
- *	...
- * }
  */
-struct apply_spec {
-	const char **instructions;
-	uint32_t n_instructions;
-};
-
 static void
 apply_spec_free(struct apply_spec *s)
 {
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
new file mode 100644
index 0000000000..8458de878a
--- /dev/null
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#include <stdint.h>
+#include <stdio.h>
+
+#include <rte_common.h>
+
+#include <rte_swx_pipeline.h>
+
+/*
+ * extobj.
+ *
+ * extobj OBJ_NAME instanceof OBJ_TYPE [ pragma OBJ_CREATE_ARGS ]
+ */
+struct extobj_spec {
+	char *name;
+	char *extern_type_name;
+	char *pragma;
+};
+
+/*
+ * struct.
+ *
+ * struct STRUCT_TYPE_NAME {
+ *	bit<SIZE> | varbit<SIZE> FIELD_NAME
+ *	...
+ * }
+ */
+struct struct_spec {
+	char *name;
+	struct rte_swx_field_params *fields;
+	uint32_t n_fields;
+	int varbit;
+};
+
+/*
+ * header.
+ *
+ * header HEADER_NAME instanceof STRUCT_TYPE_NAME
+ */
+struct header_spec {
+	char *name;
+	char *struct_type_name;
+};
+
+/*
+ * metadata.
+ *
+ * metadata instanceof STRUCT_TYPE_NAME
+ */
+struct metadata_spec {
+	char *struct_type_name;
+};
+
+/*
+ * action.
+ *
+ * action ACTION_NAME args none | instanceof STRUCT_TYPE_NAME {
+ *	INSTRUCTION
+ *	...
+ * }
+ */
+struct action_spec {
+	char *name;
+	char *args_struct_type_name;
+	const char **instructions;
+	uint32_t n_instructions;
+};
+
+/*
+ * table.
+ *
+ * table TABLE_NAME {
+ *	key {
+ *		MATCH_FIELD_NAME exact | wildcard | lpm
+ *		...
+ *	}
+ *	actions {
+ *		ACTION_NAME [ @tableonly | @defaultonly ]
+ *		...
+ *	}
+ *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
+ *	instanceof TABLE_TYPE_NAME
+ *	pragma ARGS
+ *	size SIZE
+ * }
+ */
+struct table_spec {
+	char *name;
+	struct rte_swx_pipeline_table_params params;
+	char *recommended_table_type_name;
+	char *args;
+	uint32_t size;
+};
+
+/*
+ * selector.
+ *
+ * selector SELECTOR_NAME {
+ *	group_id FIELD_NAME
+ *	selector {
+ *		FIELD_NAME
+ *		...
+ *	}
+ *	member_id FIELD_NAME
+ *	n_groups N_GROUPS
+ *	n_members_per_group N_MEMBERS_PER_GROUP
+ * }
+ */
+struct selector_spec {
+	char *name;
+	struct rte_swx_pipeline_selector_params params;
+};
+
+/*
+ * learner.
+ *
+ * learner LEARNER_NAME {
+ *	key {
+ *		MATCH_FIELD_NAME
+ *		...
+ *	}
+ *	actions {
+ *		ACTION_NAME [ @tableonly | @defaultonly]
+ *		...
+ *	}
+ *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
+ *	size SIZE
+ *	timeout {
+ *		TIMEOUT_IN_SECONDS
+ *		...
+ *	}
+ * }
+ */
+struct learner_spec {
+	char *name;
+	struct rte_swx_pipeline_learner_params params;
+	uint32_t size;
+	uint32_t *timeout;
+	uint32_t n_timeouts;
+};
+
+/*
+ * regarray.
+ *
+ * regarray NAME size SIZE initval INITVAL
+ */
+struct regarray_spec {
+	char *name;
+	uint64_t init_val;
+	uint32_t size;
+};
+
+/*
+ * metarray.
+ *
+ * metarray NAME size SIZE
+ */
+struct metarray_spec {
+	char *name;
+	uint32_t size;
+};
+
+/*
+ * apply.
+ *
+ * apply {
+ *	INSTRUCTION
+ *	...
+ * }
+ */
+struct apply_spec {
+	const char **instructions;
+	uint32_t n_instructions;
+};
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH 2/9] pipeline: add pipeline specification data structure
  2022-07-18 13:07 [PATCH 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
@ 2022-07-18 13:07 ` Cristian Dumitrescu
  2022-07-18 13:07 ` [PATCH 3/9] pipeline: rework the specification file-based pipeline build Cristian Dumitrescu
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:07 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add specification data structure for the entire pipeline.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 21 ++++++++++++++++++
 lib/pipeline/rte_swx_pipeline_spec.h | 32 ++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 5e07b4f794..642091b678 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2082,6 +2082,27 @@ apply_block_parse(struct apply_spec *s,
 /*
  * Pipeline.
  */
+void
+pipeline_spec_free(struct pipeline_spec *s)
+{
+	if (!s)
+		return;
+
+	free(s->extobjs);
+	free(s->structs);
+	free(s->headers);
+	free(s->metadata);
+	free(s->actions);
+	free(s->tables);
+	free(s->selectors);
+	free(s->learners);
+	free(s->regarrays);
+	free(s->metarrays);
+	free(s->apply);
+
+	memset(s, 0, sizeof(struct pipeline_spec));
+}
+
 int
 rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				 FILE *spec,
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 8458de878a..e1170a33b1 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -174,3 +174,35 @@ struct apply_spec {
 	const char **instructions;
 	uint32_t n_instructions;
 };
+
+/*
+ * Pipeline.
+ */
+struct pipeline_spec {
+	struct extobj_spec *extobjs;
+	struct struct_spec *structs;
+	struct header_spec *headers;
+	struct metadata_spec *metadata;
+	struct action_spec *actions;
+	struct table_spec *tables;
+	struct selector_spec *selectors;
+	struct learner_spec *learners;
+	struct regarray_spec *regarrays;
+	struct metarray_spec *metarrays;
+	struct apply_spec *apply;
+
+	uint32_t n_extobjs;
+	uint32_t n_structs;
+	uint32_t n_headers;
+	uint32_t n_metadata;
+	uint32_t n_actions;
+	uint32_t n_tables;
+	uint32_t n_selectors;
+	uint32_t n_learners;
+	uint32_t n_regarrays;
+	uint32_t n_metarrays;
+	uint32_t n_apply;
+};
+
+void
+pipeline_spec_free(struct pipeline_spec *s);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH 3/9] pipeline: rework the specification file-based pipeline build
  2022-07-18 13:07 [PATCH 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
  2022-07-18 13:07 ` [PATCH 2/9] pipeline: add pipeline specification data structure Cristian Dumitrescu
@ 2022-07-18 13:07 ` Cristian Dumitrescu
  2022-07-18 13:07 ` [PATCH 4/9] pipeline: generate the code for pipeline specification structure Cristian Dumitrescu
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:07 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Rework the specification file-based pipeline build operation to first
parse the specification file into the previously introduced pipeline
specification data structure, then use this structure to configure
and build the pipeline.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 478 +++++++++++++++++++++------
 lib/pipeline/rte_swx_pipeline_spec.h |   9 +
 2 files changed, 385 insertions(+), 102 deletions(-)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 642091b678..cbbcef852b 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2103,11 +2103,10 @@ pipeline_spec_free(struct pipeline_spec *s)
 	memset(s, 0, sizeof(struct pipeline_spec));
 }
 
-int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec,
-				 uint32_t *err_line,
-				 const char **err_msg)
+struct pipeline_spec *
+pipeline_spec_parse(FILE *spec,
+		    uint32_t *err_line,
+		    const char **err_msg)
 {
 	struct extobj_spec extobj_spec = {0};
 	struct struct_spec struct_spec = {0};
@@ -2120,26 +2119,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 	struct regarray_spec regarray_spec = {0};
 	struct metarray_spec metarray_spec = {0};
 	struct apply_spec apply_spec = {0};
-	uint32_t n_lines;
+	struct pipeline_spec *s = NULL;
+	uint32_t n_lines = 0;
 	uint32_t block_mask = 0;
-	int status;
+	int status = 0;
 
 	/* Check the input arguments. */
-	if (!p) {
+	if (!spec) {
 		if (err_line)
-			*err_line = 0;
+			*err_line = n_lines;
 		if (err_msg)
-			*err_msg = "Null pipeline argument.";
+			*err_msg = "Invalid input argument.";
 		status = -EINVAL;
 		goto error;
 	}
 
-	if (!spec) {
+	/* Memory allocation. */
+	s = calloc(sizeof(struct pipeline_spec), 1);
+	if (!s) {
 		if (err_line)
-			*err_line = 0;
+			*err_line = n_lines;
 		if (err_msg)
-			*err_msg = "Null specification file argument.";
-		status = -EINVAL;
+			*err_msg = "Memory allocation failed.";
+		status = -ENOMEM;
 		goto error;
 	}
 
@@ -2200,6 +2202,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* struct block. */
 		if (block_mask & (1 << STRUCT_BLOCK)) {
+			struct struct_spec *new_structs;
+
 			status = struct_block_parse(&struct_spec,
 						    &block_mask,
 						    tokens,
@@ -2214,26 +2218,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_struct_type_register(p,
-				struct_spec.name,
-				struct_spec.fields,
-				struct_spec.n_fields,
-				struct_spec.varbit);
-			if (status) {
+			new_structs = realloc(s->structs,
+					      (s->n_structs + 1) * sizeof(struct struct_spec));
+			if (!new_structs) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Struct registration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			struct_spec_free(&struct_spec);
+			s->structs = new_structs;
+			memcpy(&s->structs[s->n_structs], &struct_spec, sizeof(struct struct_spec));
+			s->n_structs++;
+			memset(&struct_spec, 0, sizeof(struct struct_spec));
 
 			continue;
 		}
 
 		/* action block. */
 		if (block_mask & (1 << ACTION_BLOCK)) {
+			struct action_spec *new_actions;
+
 			status = action_block_parse(&action_spec,
 						    &block_mask,
 						    tokens,
@@ -2248,26 +2255,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_action_config(p,
-				action_spec.name,
-				action_spec.args_struct_type_name,
-				action_spec.instructions,
-				action_spec.n_instructions);
-			if (status) {
+			new_actions = realloc(s->actions,
+					      (s->n_actions + 1) * sizeof(struct action_spec));
+			if (!new_actions) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Action config error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			action_spec_free(&action_spec);
+			s->actions = new_actions;
+			memcpy(&s->actions[s->n_actions], &action_spec, sizeof(struct action_spec));
+			s->n_actions++;
+			memset(&action_spec, 0, sizeof(struct action_spec));
 
 			continue;
 		}
 
 		/* table block. */
 		if (block_mask & (1 << TABLE_BLOCK)) {
+			struct table_spec *new_tables;
+
 			status = table_block_parse(&table_spec,
 						   &block_mask,
 						   tokens,
@@ -2282,27 +2292,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_table_config(p,
-				table_spec.name,
-				&table_spec.params,
-				table_spec.recommended_table_type_name,
-				table_spec.args,
-				table_spec.size);
-			if (status) {
+			new_tables = realloc(s->tables,
+					     (s->n_tables + 1) * sizeof(struct table_spec));
+			if (!new_tables) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Table configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			table_spec_free(&table_spec);
+			s->tables = new_tables;
+			memcpy(&s->tables[s->n_tables], &table_spec, sizeof(struct table_spec));
+			s->n_tables++;
+			memset(&table_spec, 0, sizeof(struct table_spec));
 
 			continue;
 		}
 
 		/* selector block. */
 		if (block_mask & (1 << SELECTOR_BLOCK)) {
+			struct selector_spec *new_selectors;
+
 			status = selector_block_parse(&selector_spec,
 						      &block_mask,
 						      tokens,
@@ -2317,24 +2329,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_selector_config(p,
-				selector_spec.name,
-				&selector_spec.params);
-			if (status) {
+			new_selectors = realloc(s->selectors,
+						(s->n_selectors + 1) * sizeof(struct selector_spec));
+			if (!new_selectors) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Selector configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			selector_spec_free(&selector_spec);
+			s->selectors = new_selectors;
+			memcpy(&s->selectors[s->n_selectors],
+			       &selector_spec,
+			       sizeof(struct selector_spec));
+			s->n_selectors++;
+			memset(&selector_spec, 0, sizeof(struct selector_spec));
 
 			continue;
 		}
 
 		/* learner block. */
 		if (block_mask & (1 << LEARNER_BLOCK)) {
+			struct learner_spec *new_learners;
+
 			status = learner_block_parse(&learner_spec,
 						     &block_mask,
 						     tokens,
@@ -2349,27 +2368,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_learner_config(p,
-				learner_spec.name,
-				&learner_spec.params,
-				learner_spec.size,
-				learner_spec.timeout,
-				learner_spec.n_timeouts);
-			if (status) {
+			new_learners = realloc(s->learners,
+					       (s->n_learners + 1) * sizeof(struct learner_spec));
+			if (!new_learners) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Learner table configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			learner_spec_free(&learner_spec);
+			s->learners = new_learners;
+			memcpy(&s->learners[s->n_learners],
+			       &learner_spec,
+			       sizeof(struct learner_spec));
+			s->n_learners++;
+			memset(&learner_spec, 0, sizeof(struct learner_spec));
 
 			continue;
 		}
 
 		/* apply block. */
 		if (block_mask & (1 << APPLY_BLOCK)) {
+			struct apply_spec *new_apply;
+
 			status = apply_block_parse(&apply_spec,
 						   &block_mask,
 						   tokens,
@@ -2384,24 +2407,28 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_instructions_config(p,
-				apply_spec.instructions,
-				apply_spec.n_instructions);
-			if (status) {
+			new_apply = realloc(s->apply, (s->n_apply + 1) * sizeof(struct apply_spec));
+			if (!new_apply) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Pipeline instructions err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			apply_spec_free(&apply_spec);
+			s->apply = new_apply;
+			memcpy(&s->apply[s->n_apply], &apply_spec, sizeof(struct apply_spec));
+			s->n_apply++;
+			memset(&apply_spec, 0, sizeof(struct apply_spec));
 
 			continue;
 		}
 
 		/* extobj. */
 		if (!strcmp(tokens[0], "extobj")) {
+			struct extobj_spec *new_extobjs;
+
 			status = extobj_statement_parse(&extobj_spec,
 							tokens,
 							n_tokens,
@@ -2411,19 +2438,21 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_extern_object_config(p,
-				extobj_spec.name,
-				extobj_spec.extern_type_name,
-				extobj_spec.pragma);
-			if (status) {
+			new_extobjs = realloc(s->extobjs,
+					      (s->n_extobjs + 1) * sizeof(struct extobj_spec));
+			if (!new_extobjs) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Extern object config err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			extobj_spec_free(&extobj_spec);
+			s->extobjs = new_extobjs;
+			memcpy(&s->extobjs[s->n_extobjs], &extobj_spec, sizeof(struct extobj_spec));
+			s->n_extobjs++;
+			memset(&extobj_spec, 0, sizeof(struct extobj_spec));
 
 			continue;
 		}
@@ -2445,6 +2474,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* header. */
 		if (!strcmp(tokens[0], "header")) {
+			struct header_spec *new_headers;
+
 			status = header_statement_parse(&header_spec,
 							tokens,
 							n_tokens,
@@ -2454,24 +2485,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_packet_header_register(p,
-				header_spec.name,
-				header_spec.struct_type_name);
-			if (status) {
+			new_headers = realloc(s->headers,
+					      (s->n_headers + 1) * sizeof(struct header_spec));
+			if (!new_headers) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Header registration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			header_spec_free(&header_spec);
+			s->headers = new_headers;
+			memcpy(&s->headers[s->n_headers], &header_spec, sizeof(struct header_spec));
+			s->n_headers++;
+			memset(&header_spec, 0, sizeof(struct header_spec));
 
 			continue;
 		}
 
 		/* metadata. */
 		if (!strcmp(tokens[0], "metadata")) {
+			struct metadata_spec *new_metadata;
+
 			status = metadata_statement_parse(&metadata_spec,
 							  tokens,
 							  n_tokens,
@@ -2481,17 +2517,23 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_packet_metadata_register(p,
-				metadata_spec.struct_type_name);
-			if (status) {
+			new_metadata = realloc(s->metadata,
+					       (s->n_metadata + 1) * sizeof(struct metadata_spec));
+			if (!new_metadata) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Meta-data reg err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			metadata_spec_free(&metadata_spec);
+			s->metadata = new_metadata;
+			memcpy(&s->metadata[s->n_metadata],
+			       &metadata_spec,
+			       sizeof(struct metadata_spec));
+			s->n_metadata++;
+			memset(&metadata_spec, 0, sizeof(struct metadata_spec));
 
 			continue;
 		}
@@ -2558,6 +2600,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* regarray. */
 		if (!strcmp(tokens[0], "regarray")) {
+			struct regarray_spec *new_regarrays;
+
 			status = regarray_statement_parse(&regarray_spec,
 							  tokens,
 							  n_tokens,
@@ -2567,25 +2611,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_regarray_config(p,
-				regarray_spec.name,
-				regarray_spec.size,
-				regarray_spec.init_val);
-			if (status) {
+			new_regarrays = realloc(s->regarrays,
+				(s->n_regarrays + 1) * sizeof(struct regarray_spec));
+			if (!new_regarrays) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Register array configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			regarray_spec_free(&regarray_spec);
+			s->regarrays = new_regarrays;
+			memcpy(&s->regarrays[s->n_regarrays],
+			       &regarray_spec,
+			       sizeof(struct regarray_spec));
+			s->n_regarrays++;
+			memset(&regarray_spec, 0, sizeof(struct regarray_spec));
 
 			continue;
 		}
 
 		/* metarray. */
 		if (!strcmp(tokens[0], "metarray")) {
+			struct metarray_spec *new_metarrays;
+
 			status = metarray_statement_parse(&metarray_spec,
 							  tokens,
 							  n_tokens,
@@ -2595,18 +2645,23 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_metarray_config(p,
-				metarray_spec.name,
-				metarray_spec.size);
-			if (status) {
+			new_metarrays = realloc(s->metarrays,
+				(s->n_metarrays + 1) * sizeof(struct metarray_spec));
+			if (!new_metarrays) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Meter array configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			metarray_spec_free(&metarray_spec);
+			s->metarrays = new_metarrays;
+			memcpy(&s->metarrays[s->n_metarrays],
+			       &metarray_spec,
+			       sizeof(struct metarray_spec));
+			s->n_metarrays++;
+			memset(&metarray_spec, 0, sizeof(struct metarray_spec));
 
 			continue;
 		}
@@ -2644,17 +2699,7 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 		goto error;
 	}
 
-	/* Pipeline build. */
-	status = rte_swx_pipeline_build(p);
-	if (status) {
-		if (err_line)
-			*err_line = n_lines;
-		if (err_msg)
-			*err_msg = "Pipeline build error.";
-		goto error;
-	}
-
-	return 0;
+	return s;
 
 error:
 	extobj_spec_free(&extobj_spec);
@@ -2668,5 +2713,234 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 	regarray_spec_free(&regarray_spec);
 	metarray_spec_free(&metarray_spec);
 	apply_spec_free(&apply_spec);
+	pipeline_spec_free(s);
+
+	return NULL;
+}
+
+int
+pipeline_spec_configure(struct rte_swx_pipeline *p,
+			struct pipeline_spec *s,
+			const char **err_msg)
+{
+	uint32_t i;
+	int status = 0;
+
+	/* extobj. */
+	for (i = 0; i < s->n_extobjs; i++) {
+		struct extobj_spec *extobj_spec = &s->extobjs[i];
+
+		status = rte_swx_pipeline_extern_object_config(p,
+			extobj_spec->name,
+			extobj_spec->extern_type_name,
+			extobj_spec->pragma);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Extern object configuration error.";
+			return status;
+		}
+	}
+
+	/* regarray. */
+	for (i = 0; i < s->n_regarrays; i++) {
+		struct regarray_spec *regarray_spec = &s->regarrays[i];
+
+		status = rte_swx_pipeline_regarray_config(p,
+			regarray_spec->name,
+			regarray_spec->size,
+			regarray_spec->init_val);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Register array configuration error.";
+			return status;
+		}
+	}
+
+	/* metarray. */
+	for (i = 0; i < s->n_metarrays; i++) {
+		struct metarray_spec *metarray_spec = &s->metarrays[i];
+
+		status = rte_swx_pipeline_metarray_config(p,
+			metarray_spec->name,
+			metarray_spec->size);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Meter array configuration error.";
+			return status;
+		}
+	}
+
+	/* struct. */
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+
+		status = rte_swx_pipeline_struct_type_register(p,
+			struct_spec->name,
+			struct_spec->fields,
+			struct_spec->n_fields,
+			struct_spec->varbit);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Struct type registration error.";
+			return status;
+		}
+	}
+
+	/* header. */
+	for (i = 0; i < s->n_headers; i++) {
+		struct header_spec *header_spec = &s->headers[i];
+
+		status = rte_swx_pipeline_packet_header_register(p,
+			header_spec->name,
+			header_spec->struct_type_name);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Header configuration error.";
+			return status;
+		}
+	}
+
+	/* metadata. */
+	for (i = 0; i < s->n_metadata; i++) {
+		struct metadata_spec *metadata_spec = &s->metadata[i];
+
+		status = rte_swx_pipeline_packet_metadata_register(p,
+			metadata_spec->struct_type_name);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Meta-data registration error.";
+			return status;
+		}
+	}
+
+	/* action. */
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+
+		status = rte_swx_pipeline_action_config(p,
+			action_spec->name,
+			action_spec->args_struct_type_name,
+			action_spec->instructions,
+			action_spec->n_instructions);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Action configuration error.";
+			return status;
+		}
+	}
+
+	/* table. */
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+
+		status = rte_swx_pipeline_table_config(p,
+			table_spec->name,
+			&table_spec->params,
+			table_spec->recommended_table_type_name,
+			table_spec->args,
+			table_spec->size);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Table configuration error.";
+			return status;
+		}
+	}
+
+	/* selector. */
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+
+		status = rte_swx_pipeline_selector_config(p,
+			selector_spec->name,
+			&selector_spec->params);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Selector table configuration error.";
+			return status;
+		}
+	}
+
+	/* learner. */
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+
+		status = rte_swx_pipeline_learner_config(p,
+			learner_spec->name,
+			&learner_spec->params,
+			learner_spec->size,
+			learner_spec->timeout,
+			learner_spec->n_timeouts);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Learner table configuration error.";
+			return status;
+		}
+	}
+
+	/* apply. */
+	for (i = 0; i < s->n_apply; i++) {
+		struct apply_spec *apply_spec = &s->apply[i];
+
+		status = rte_swx_pipeline_instructions_config(p,
+			apply_spec->instructions,
+			apply_spec->n_instructions);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Pipeline instructions configuration error.";
+			return status;
+		}
+	}
+
+	return 0;
+}
+
+int
+rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
+				 FILE *spec_file,
+				 uint32_t *err_line,
+				 const char **err_msg)
+{
+	struct pipeline_spec *s = NULL;
+	int status = 0;
+
+	/* Check the input arguments. */
+	if (!p || !spec_file) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Invalid input argument.";
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Spec file parse. */
+	s = pipeline_spec_parse(spec_file, err_line, err_msg);
+	if (!s) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Pipeline configure. */
+	status = pipeline_spec_configure(p, s, err_msg);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		goto error;
+	}
+
+	/* Pipeline build. */
+	status = rte_swx_pipeline_build(p);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Pipeline build error.";
+		goto error;
+	}
+
+	return 0;
+
+error:
+	pipeline_spec_free(s);
 	return status;
 }
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index e1170a33b1..4f3a0b5958 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -206,3 +206,12 @@ struct pipeline_spec {
 
 void
 pipeline_spec_free(struct pipeline_spec *s);
+struct pipeline_spec *
+pipeline_spec_parse(FILE *spec,
+		    uint32_t *err_line,
+		    const char **err_msg);
+
+int
+pipeline_spec_configure(struct rte_swx_pipeline *p,
+			struct pipeline_spec *s,
+			const char **err_msg);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH 4/9] pipeline: generate the code for pipeline specification structure
  2022-07-18 13:07 [PATCH 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
  2022-07-18 13:07 ` [PATCH 2/9] pipeline: add pipeline specification data structure Cristian Dumitrescu
  2022-07-18 13:07 ` [PATCH 3/9] pipeline: rework the specification file-based pipeline build Cristian Dumitrescu
@ 2022-07-18 13:07 ` Cristian Dumitrescu
  2022-07-18 13:07 ` [PATCH 5/9] pipeline: add API for pipeline code generation Cristian Dumitrescu
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:07 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add support to export the pipeline specification data structure to a C
source code file.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 621 +++++++++++++++++++++++++++
 lib/pipeline/rte_swx_pipeline_spec.h |   5 +
 2 files changed, 626 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index cbbcef852b..05cf952816 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2020 Intel Corporation
  */
 #include <stdint.h>
+#include <inttypes.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -2103,6 +2104,626 @@ pipeline_spec_free(struct pipeline_spec *s)
 	memset(s, 0, sizeof(struct pipeline_spec));
 }
 
+static const char *
+match_type_string_get(enum rte_swx_table_match_type match_type)
+{
+	switch (match_type) {
+		case RTE_SWX_TABLE_MATCH_WILDCARD: return "RTE_SWX_TABLE_MATCH_WILDCARD";
+		case RTE_SWX_TABLE_MATCH_LPM: return "RTE_SWX_TABLE_MATCH_LPM";
+		case RTE_SWX_TABLE_MATCH_EXACT: return "RTE_SWX_TABLE_MATCH_EXACT";
+		default: return "RTE_SWX_TABLE_MATCH_UNKNOWN";
+	}
+}
+
+void
+pipeline_spec_codegen(FILE *f,
+		      struct pipeline_spec *s)
+{
+	uint32_t i;
+
+	/* Check the input arguments. */
+	if (!f || !s)
+		return;
+
+	/* extobj. */
+	fprintf(f, "static struct extobj_spec extobjs[] = {\n");
+
+	for (i = 0; i < s->n_extobjs; i++) {
+		struct extobj_spec *extobj_spec = &s->extobjs[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", extobj_spec->name);
+		fprintf(f, "\t\t.extern_type_name = \"%s\",\n", extobj_spec->extern_type_name);
+		if (extobj_spec->pragma)
+			fprintf(f, "\t\t.pragma = \"%s\",\n", extobj_spec->pragma);
+		else
+			fprintf(f, "\t\t.pragma = NULL,\n");
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* regarray. */
+	fprintf(f, "static struct regarray_spec regarrays[] = {\n");
+
+	for (i = 0; i < s->n_regarrays; i++) {
+		struct regarray_spec *regarray_spec = &s->regarrays[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", regarray_spec->name);
+		fprintf(f, "\t\t.init_val = %" PRIu64 ",\n", regarray_spec->init_val);
+		fprintf(f, "\t\t.size = %u,\n", regarray_spec->size);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* metarray. */
+	fprintf(f, "static struct metarray_spec metarrays[] = {\n");
+
+	for (i = 0; i < s->n_metarrays; i++) {
+		struct metarray_spec *metarray_spec = &s->metarrays[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", metarray_spec->name);
+		fprintf(f, "\t\t.size = %u,\n", metarray_spec->size);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* struct. */
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+		uint32_t j;
+
+		fprintf(f, "static struct rte_swx_field_params struct_%s_fields[] = {\n",
+			struct_spec->name);
+
+		for (j = 0; j < struct_spec->n_fields; j++) {
+			struct rte_swx_field_params *field = &struct_spec->fields[j];
+
+			fprintf(f, "\t[%d] = {\n", j);
+			fprintf(f, "\t\t.name = \"%s\",\n", field->name);
+			fprintf(f, "\t\t.n_bits = %u,\n", field->n_bits);
+			fprintf(f, "\t},\n");
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct struct_spec structs[] = {\n");
+
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", struct_spec->name);
+		fprintf(f, "\t\t.fields = struct_%s_fields,\n", struct_spec->name);
+		fprintf(f, "\t\t.n_fields = "
+			"sizeof(struct_%s_fields) / sizeof(struct_%s_fields[0]),\n",
+			struct_spec->name,
+			struct_spec->name);
+		fprintf(f, "\t\t.varbit = %d,\n", struct_spec->varbit);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* header. */
+	fprintf(f, "static struct header_spec headers[] = {\n");
+
+	for (i = 0; i < s->n_headers; i++) {
+		struct header_spec *header_spec = &s->headers[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", header_spec->name);
+		fprintf(f, "\t\t.struct_type_name = \"%s\",\n", header_spec->struct_type_name);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* metadata. */
+	fprintf(f, "static struct metadata_spec metadata[] = {\n");
+
+	for (i = 0; i < s->n_metadata; i++) {
+		struct metadata_spec *metadata_spec = &s->metadata[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.struct_type_name = \"%s\",\n", metadata_spec->struct_type_name);
+		fprintf(f, "\t},\n");
+
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* action. */
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+		uint32_t j;
+
+		fprintf(f, "static const char *action_%s_initial_instructions[] = {\n",
+			action_spec->name);
+
+		for (j = 0; j < action_spec->n_instructions; j++) {
+			const char *instr = action_spec->instructions[j];
+
+			fprintf(f, "\t[%d] = \"%s\",\n", j, instr);
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct action_spec actions[] = {\n");
+
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", action_spec->name);
+
+		if (action_spec->args_struct_type_name)
+			fprintf(f, "\t\t.args_struct_type_name = \"%s\",\n",
+				action_spec->args_struct_type_name);
+		else
+			fprintf(f, "\t\t.args_struct_type_name = NULL,\n");
+
+		fprintf(f, "\t\t.instructions = action_%s_initial_instructions,\n",
+			action_spec->name);
+		fprintf(f, "\t\t.n_instructions = "
+			"sizeof(action_%s_initial_instructions) / "
+			"sizeof(action_%s_initial_instructions[0]),\n",
+			action_spec->name,
+			action_spec->name);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* table. */
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+		uint32_t j;
+
+		/* fields. */
+		if (table_spec->params.fields && table_spec->params.n_fields) {
+			fprintf(f, "static struct rte_swx_match_field_params "
+				"table_%s_fields[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_fields; j++) {
+				struct rte_swx_match_field_params *field =
+					&table_spec->params.fields[j];
+
+				fprintf(f, "\t[%d] = {\n", j);
+				fprintf(f, "\t\t.name = \"%s\",\n", field->name);
+				fprintf(f, "\t\t.match_type = %s,\n",
+					match_type_string_get(field->match_type));
+				fprintf(f, "\t},\n");
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_names. */
+		if (table_spec->params.action_names && table_spec->params.n_actions) {
+			fprintf(f, "static const char *table_%s_action_names[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				const char *action_name = table_spec->params.action_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, action_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_table_entries. */
+		if (table_spec->params.action_is_for_table_entries &&
+		    table_spec->params.n_actions) {
+			fprintf(f, "static int table_%s_action_is_for_table_entries[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				int value = table_spec->params.action_is_for_table_entries[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_default_entry. */
+		if (table_spec->params.action_is_for_default_entry &&
+		    table_spec->params.n_actions) {
+			fprintf(f, "static int table_%s_action_is_for_default_entry[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				int value = table_spec->params.action_is_for_default_entry[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct table_spec tables[] = {\n");
+
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", table_spec->name);
+
+		fprintf(f, "\t\t.params = {\n");
+
+		if (table_spec->params.fields && table_spec->params.n_fields) {
+			fprintf(f, "\t\t\t.fields = table_%s_fields,\n", table_spec->name);
+			fprintf(f, "\t\t\t.n_fields = "
+				"sizeof(table_%s_fields) / sizeof(table_%s_fields[0]),\n",
+				table_spec->name,
+				table_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.fields = NULL,\n");
+			fprintf(f, "\t\t\t.n_fields = 0,\n");
+		}
+
+		if (table_spec->params.action_names && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_names = table_%s_action_names,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_names = NULL,\n");
+
+		if (table_spec->params.action_is_for_table_entries && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_table_entries = "
+				"table_%s_action_is_for_table_entries,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_table_entries = NULL,\n");
+
+		if (table_spec->params.action_is_for_default_entry && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_default_entry = "
+				"table_%s_action_is_for_default_entry,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_default_entry = NULL,\n");
+
+		if (table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.n_actions = sizeof(table_%s_action_names) / "
+				"sizeof(table_%s_action_names[0]),\n",
+				table_spec->name,
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.n_actions = 0,\n");
+
+		if (table_spec->params.default_action_name)
+			fprintf(f, "\t\t\t.default_action_name = \"%s\",\n",
+				table_spec->params.default_action_name);
+		else
+			fprintf(f, "\t\t\t.default_action_name = NULL,\n");
+
+		if (table_spec->params.default_action_args)
+			fprintf(f, "\t\t\t.default_action_args = \"%s\",\n",
+				table_spec->params.default_action_args);
+		else
+			fprintf(f, "\t\t\t.default_action_args = NULL,\n");
+
+		fprintf(f, "\t\t\t.default_action_is_const = %d,\n",
+			table_spec->params.default_action_is_const);
+		fprintf(f, "\t\t},\n");
+
+		if (table_spec->recommended_table_type_name)
+			fprintf(f, "\t\t.recommended_table_type_name = \"%s\",\n",
+				table_spec->recommended_table_type_name);
+		else
+			fprintf(f, "\t\t.recommended_table_type_name = NULL,\n");
+
+		if (table_spec->args)
+			fprintf(f, "\t\t.args = \"%s\",\n", table_spec->args);
+		else
+			fprintf(f, "\t\t.args = NULL,\n");
+
+		fprintf(f, "\t\t.size = %u,\n", table_spec->size);
+
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* selector. */
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+		uint32_t j;
+
+		if (selector_spec->params.selector_field_names &&
+		    selector_spec->params.n_selector_fields) {
+			fprintf(f, "static const char *selector_%s_field_names[] = {\n",
+				selector_spec->name);
+
+			for (j = 0; j < selector_spec->params.n_selector_fields; j++) {
+				const char *field_name =
+					selector_spec->params.selector_field_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, field_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct selector_spec selectors[] = {\n");
+
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+
+		fprintf(f, "\t\t.name = \"%s\",\n", selector_spec->name);
+		fprintf(f, "\t\t.params = {\n");
+
+		if (selector_spec->params.group_id_field_name)
+			fprintf(f, "\t\t\t.group_id_field_name = \"%s\",\n",
+				selector_spec->params.group_id_field_name);
+		else
+			fprintf(f, "\t\t\t.group_id_field_name = NULL,\n");
+
+		if (selector_spec->params.selector_field_names &&
+		    selector_spec->params.n_selector_fields) {
+			fprintf(f, "\t\t\t.selector_field_names = selector_%s_field_names,\n",
+				selector_spec->name);
+			fprintf(f, "\t\t\t.n_selector_fields = "
+				"sizeof(selector_%s_field_names) / sizeof(selector_%s_field_names[0]),\n",
+				selector_spec->name,
+				selector_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.selector_field_names = NULL,\n");
+			fprintf(f, "\t\t\t.n_selector_fields = 0,\n");
+		}
+
+		if (selector_spec->params.member_id_field_name)
+			fprintf(f, "\t\t\t.member_id_field_name = \"%s\",\n",
+				selector_spec->params.member_id_field_name);
+		else
+			fprintf(f, "\t\t\t.member_id_field_name = NULL,\n");
+
+		fprintf(f, "\t\t\t.n_groups_max = %u,\n", selector_spec->params.n_groups_max);
+
+		fprintf(f, "\t\t\t.n_members_per_group_max = %u,\n",
+			selector_spec->params.n_members_per_group_max);
+
+		fprintf(f, "\t\t},\n");
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* learner. */
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+		uint32_t j;
+
+		/* field_names. */
+		if (learner_spec->params.field_names && learner_spec->params.n_fields) {
+			fprintf(f, "static const char *learner_%s_field_names[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_fields; j++) {
+				const char *field_name = learner_spec->params.field_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, field_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_names. */
+		if (learner_spec->params.action_names && learner_spec->params.n_actions) {
+			fprintf(f, "static const char *learner_%s_action_names[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				const char *action_name = learner_spec->params.action_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, action_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_table_entries. */
+		if (learner_spec->params.action_is_for_table_entries &&
+		    learner_spec->params.n_actions) {
+			fprintf(f, "static int learner_%s_action_is_for_table_entries[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				int value = learner_spec->params.action_is_for_table_entries[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_default_entry. */
+		if (learner_spec->params.action_is_for_default_entry &&
+		    learner_spec->params.n_actions) {
+			fprintf(f, "static int learner_%s_action_is_for_default_entry[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				int value = learner_spec->params.action_is_for_default_entry[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* timeout. */
+		if (learner_spec->timeout && learner_spec->n_timeouts) {
+			fprintf(f, "static uint32_t learner_%s_timeout[] = {\n", learner_spec->name);
+
+			for (j = 0; j < learner_spec->n_timeouts; j++) {
+				uint32_t value = learner_spec->timeout[j];
+
+				fprintf(f, "\t[%d] = %u,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct learner_spec learners[] = {\n");
+
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", learner_spec->name);
+
+		fprintf(f, "\t\t.params = {\n");
+
+		if (learner_spec->params.field_names && learner_spec->params.n_fields) {
+			fprintf(f, "\t\t\t.field_names = learner_%s_field_names,\n",
+				learner_spec->name);
+			fprintf(f, "\t\t\t.n_fields = "
+				"sizeof(learner_%s_field_names) / "
+				"sizeof(learner_%s_field_names[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.field_names = NULL,\n");
+			fprintf(f, "\t\t\t.n_fields = 0,\n");
+		}
+
+		if (learner_spec->params.action_names && learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_names = learner_%s_action_names,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_names = NULL,\n");
+
+		if (learner_spec->params.action_is_for_table_entries &&
+		    learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_table_entries = "
+				"learner_%s_action_is_for_table_entries,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_table_entries = NULL,\n");
+
+		if (learner_spec->params.action_is_for_default_entry &&
+		    learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_default_entry = "
+				"learner_%s_action_is_for_default_entry,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_default_entry = NULL,\n");
+
+		if (learner_spec->params.action_names && learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.n_actions = "
+				"sizeof(learner_%s_action_names) / sizeof(learner_%s_action_names[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.n_actions = NULL,\n");
+
+		if (learner_spec->params.default_action_name)
+			fprintf(f, "\t\t\t.default_action_name = \"%s\",\n",
+				learner_spec->params.default_action_name);
+		else
+			fprintf(f, "\t\t\t.default_action_name = NULL,\n");
+
+		if (learner_spec->params.default_action_args)
+			fprintf(f, "\t\t\t.default_action_args = \"%s\",\n",
+				learner_spec->params.default_action_args);
+		else
+			fprintf(f, "\t\t\t.default_action_args = NULL,\n");
+
+		fprintf(f, "\t\t\t.default_action_is_const = %d,\n",
+			learner_spec->params.default_action_is_const);
+
+		fprintf(f, "\t\t},\n");
+
+		fprintf(f, "\t\t.size = %u,\n", learner_spec->size);
+
+		if (learner_spec->timeout && learner_spec->n_timeouts) {
+			fprintf(f, "\t\t.timeout = learner_%s_timeout,\n", learner_spec->name);
+			fprintf(f, "\t\t\t.n_timeouts = "
+				"sizeof(learner_%s_timeout) / sizeof(learner_%s_timeout[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		} else {
+			fprintf(f, "\t\t.timeout = NULL,\n");
+			fprintf(f, "\t\t\t.n_timeouts = 0,\n");
+		}
+
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* apply. */
+	for (i = 0; i < s->n_apply; i++) {
+		struct apply_spec *apply_spec = &s->apply[i];
+		uint32_t j;
+
+		fprintf(f, "static const char *apply%u_initial_instructions[] = {\n", i);
+
+		for (j = 0; j < apply_spec->n_instructions; j++) {
+			const char *instr = apply_spec->instructions[j];
+
+			fprintf(f, "\t[%d] = \"%s\",\n", j, instr);
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct apply_spec apply[] = {\n");
+
+	for (i = 0; i < s->n_apply; i++) {
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t.instructions = apply%u_initial_instructions,\n", i);
+		fprintf(f, "\t.n_instructions = "
+			"sizeof(apply%u_initial_instructions) / "
+			"sizeof(apply%u_initial_instructions[0]),\n",
+			i,
+			i);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* pipeline. */
+	fprintf(f, "struct pipeline_spec pipeline_spec = {\n");
+	fprintf(f, "\t.extobjs = extobjs,\n");
+	fprintf(f, "\t.structs = structs,\n");
+	fprintf(f, "\t.headers = headers,\n");
+	fprintf(f, "\t.metadata = metadata,\n");
+	fprintf(f, "\t.actions = actions,\n");
+	fprintf(f, "\t.tables = tables,\n");
+	fprintf(f, "\t.selectors = selectors,\n");
+	fprintf(f, "\t.learners = learners,\n");
+	fprintf(f, "\t.regarrays = regarrays,\n");
+	fprintf(f, "\t.metarrays = metarrays,\n");
+	fprintf(f, "\t.apply = apply,\n");
+	fprintf(f, "\t.n_extobjs = sizeof(extobjs) / sizeof(extobjs[0]),\n");
+	fprintf(f, "\t.n_structs = sizeof(structs) / sizeof(structs[0]),\n");
+	fprintf(f, "\t.n_headers = sizeof(headers) / sizeof(headers[0]),\n");
+	fprintf(f, "\t.n_metadata = sizeof(metadata) / sizeof(metadata[0]),\n");
+	fprintf(f, "\t.n_actions = sizeof(actions) / sizeof(actions[0]),\n");
+	fprintf(f, "\t.n_tables = sizeof(tables) / sizeof(tables[0]),\n");
+	fprintf(f, "\t.n_selectors = sizeof(selectors) / sizeof(selectors[0]),\n");
+	fprintf(f, "\t.n_learners = sizeof(learners) / sizeof(learners[0]),\n");
+	fprintf(f, "\t.n_regarrays = sizeof(regarrays) / sizeof(regarrays[0]),\n");
+	fprintf(f, "\t.n_metarrays = sizeof(metarrays) / sizeof(metarrays[0]),\n");
+	fprintf(f, "\t.n_apply = sizeof(apply) / sizeof(apply[0]),\n");
+	fprintf(f, "};\n");
+}
+
 struct pipeline_spec *
 pipeline_spec_parse(FILE *spec,
 		    uint32_t *err_line,
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 4f3a0b5958..707b99ba09 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -206,6 +206,11 @@ struct pipeline_spec {
 
 void
 pipeline_spec_free(struct pipeline_spec *s);
+
+void
+pipeline_spec_codegen(FILE *f,
+		      struct pipeline_spec *s);
+
 struct pipeline_spec *
 pipeline_spec_parse(FILE *spec,
 		    uint32_t *err_line,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH 5/9] pipeline: add API for pipeline code generation
  2022-07-18 13:07 [PATCH 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
                   ` (2 preceding siblings ...)
  2022-07-18 13:07 ` [PATCH 4/9] pipeline: generate the code for pipeline specification structure Cristian Dumitrescu
@ 2022-07-18 13:07 ` Cristian Dumitrescu
  2022-07-18 13:07 ` [PATCH 6/9] pipeline: add API for shared library-based pipeline build Cristian Dumitrescu
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:07 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Previously, the C code generation for the pipeline was hidden under
the hood; now, we make this an explicit API operation. Besides the
functions for the pipeline actions and the pipeline instructions,
the generated C source code now includes the pipeline specification
structure required for the pipeline configuration operations.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline.c | 94 +++++++++++++++++++++++++++++++++
 lib/pipeline/rte_swx_pipeline.h | 25 +++++++++
 lib/pipeline/version.map        |  3 ++
 3 files changed, 122 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 3e1c6e9edb..52760111fd 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -18,6 +18,7 @@
 #include <rte_swx_table_wm.h>
 
 #include "rte_swx_pipeline_internal.h"
+#include "rte_swx_pipeline_spec.h"
 
 #define CHECK(condition, err_code)                                             \
 do {                                                                           \
@@ -13476,3 +13477,96 @@ pipeline_compile(struct rte_swx_pipeline *p)
 
 	return status;
 }
+
+int
+rte_swx_pipeline_codegen(FILE *spec_file,
+			 FILE *code_file,
+			 uint32_t *err_line,
+			 const char **err_msg)
+
+{
+	struct rte_swx_pipeline *p = NULL;
+	struct pipeline_spec *s = NULL;
+	struct instruction_group_list *igl = NULL;
+	struct action *a;
+	int status = 0;
+
+	/* Check input arguments. */
+	if (!spec_file || !code_file) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Invalid input argument.";
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Pipeline configuration. */
+	s = pipeline_spec_parse(spec_file, err_line, err_msg);
+	if (!s) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	status = rte_swx_pipeline_config(&p, 0);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Pipeline configuration error.";
+		goto free;
+	}
+
+	status = pipeline_spec_configure(p, s, err_msg);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		goto free;
+	}
+
+	/*
+	 * Pipeline code generation.
+	 */
+
+	/* Instruction Group List (IGL) computation: the pipeline configuration must be done first,
+	 * but there is no need for the pipeline build to be done as well.
+	 */
+	igl = instruction_group_list_create(p);
+	if (!igl) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		status = -ENOMEM;
+		goto free;
+	}
+
+	/* Header file inclusion. */
+	fprintf(code_file, "#include \"rte_swx_pipeline_internal.h\"\n");
+	fprintf(code_file, "#include \"rte_swx_pipeline_spec.h\"\n\n");
+
+	/* Code generation for the pipeline specification. */
+	pipeline_spec_codegen(code_file, s);
+	fprintf(code_file, "\n");
+
+	/* Code generation for the action instructions. */
+	TAILQ_FOREACH(a, &p->actions, node) {
+		fprintf(code_file, "/**\n * Action %s\n */\n\n", a->name);
+
+		action_data_codegen(a, code_file);
+		fprintf(code_file, "\n");
+
+		action_instr_codegen(a, code_file);
+		fprintf(code_file, "\n");
+	}
+
+	/* Code generation for the pipeline instructions. */
+	instruction_group_list_codegen(igl, p, code_file);
+
+free:
+	instruction_group_list_free(igl);
+	rte_swx_pipeline_free(p);
+	pipeline_spec_free(s);
+
+	return status;
+}
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index c41ca5cb15..2bd019b05f 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -941,6 +941,31 @@ __rte_experimental
 int
 rte_swx_pipeline_build(struct rte_swx_pipeline *p);
 
+/**
+ * Pipeline C code generate based on input specification file
+ *
+ * @param[in] spec_file
+ *   Pipeline specification file (.spec) provided as input.
+ * @param[in] code_file
+ *   Pipeline C language file (.c) to be generated.
+ * @param[out] err_line
+ *   In case of error and non-NULL, the line number within the *spec* file where
+ *   the error occurred. The first line number in the file is 1.
+ * @param[out] err_msg
+ *   In case of error and non-NULL, the error message.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Resource with the same name already exists.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_codegen(FILE *spec_file,
+			 FILE *code_file,
+			 uint32_t *err_line,
+			 const char **err_msg);
+
 /**
  * Pipeline build from specification file
  *
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 8312307a7a..51165d48cf 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -145,4 +145,7 @@ EXPERIMENTAL {
 	rte_swx_ctl_pipeline_learner_timeout_get;
 	rte_swx_ctl_pipeline_learner_timeout_set;
 	rte_swx_pipeline_hash_func_register;
+
+	#added in 22.11
+	rte_swx_pipeline_codegen;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH 6/9] pipeline: add API for shared library-based pipeline build
  2022-07-18 13:07 [PATCH 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
                   ` (3 preceding siblings ...)
  2022-07-18 13:07 ` [PATCH 5/9] pipeline: add API for pipeline code generation Cristian Dumitrescu
@ 2022-07-18 13:07 ` Cristian Dumitrescu
  2022-07-18 13:07 ` [PATCH 7/9] examples/pipeline: add CLI command for pipeline code generation Cristian Dumitrescu
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:07 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Previously, the pipeline build operation was done based on the
specification file (typically produced by the P4 compiler), then the C
code with optimized functions for the pipeline actions and
instructions was generated, built into a shared object library, loaded
and installed into the pipeline in a completely hardcoded and
non-customizable way.

Now, this process is split into three explicit stages:
i) code generation (specification file -> C file);
ii) code build (C file -> shared object library);
iii) code installation (library load into the pipeline).

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c              |  20 +-
 lib/pipeline/rte_swx_pipeline.c      | 289 +++++++++------------------
 lib/pipeline/rte_swx_pipeline.h      |  22 +-
 lib/pipeline/rte_swx_pipeline_spec.c |  51 -----
 lib/pipeline/version.map             |   2 +-
 5 files changed, 108 insertions(+), 276 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index ad553f19ab..1f75b5dc9d 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -984,7 +984,7 @@ cmd_pipeline_port_out(char **tokens,
 }
 
 static const char cmd_pipeline_build_help[] =
-"pipeline <pipeline_name> build <spec_file>\n";
+"pipeline <pipeline_name> build <lib_file>\n";
 
 static void
 cmd_pipeline_build(char **tokens,
@@ -994,9 +994,6 @@ cmd_pipeline_build(char **tokens,
 	void *obj)
 {
 	struct pipeline *p = NULL;
-	FILE *spec = NULL;
-	uint32_t err_line;
-	const char *err_msg;
 	int status;
 
 	if (n_tokens != 4) {
@@ -1010,20 +1007,9 @@ cmd_pipeline_build(char **tokens,
 		return;
 	}
 
-	spec = fopen(tokens[3], "r");
-	if (!spec) {
-		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
-		return;
-	}
-
-	status = rte_swx_pipeline_build_from_spec(p->p,
-		spec,
-		&err_line,
-		&err_msg);
-	fclose(spec);
+	status = rte_swx_pipeline_build_from_lib(p->p, tokens[3]);
 	if (status) {
-		snprintf(out, out_size, "Error %d at line %u: %s\n.",
-			status, err_line, err_msg);
+		snprintf(out, out_size, "Pipeline build error (%d).", status);
 		return;
 	}
 
diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 52760111fd..03414bfd1f 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -9807,9 +9807,6 @@ rte_swx_pipeline_instructions_config(struct rte_swx_pipeline *p,
 	return 0;
 }
 
-static int
-pipeline_compile(struct rte_swx_pipeline *p);
-
 int
 rte_swx_pipeline_build(struct rte_swx_pipeline *p)
 {
@@ -9899,8 +9896,6 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
 
 	p->build_done = 1;
 
-	pipeline_compile(p);
-
 	return 0;
 
 error:
@@ -13222,160 +13217,6 @@ instruction_group_list_custom_instructions_count(struct instruction_group_list *
 	return n_custom_instr;
 }
 
-static int
-pipeline_codegen(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
-{
-	struct action *a;
-	FILE *f = NULL;
-
-	/* Create the .c file. */
-	f = fopen("/tmp/pipeline.c", "w");
-	if (!f)
-		return -EIO;
-
-	/* Include the .h file. */
-	fprintf(f, "#include \"rte_swx_pipeline_internal.h\"\n");
-
-	/* Add the code for each action. */
-	TAILQ_FOREACH(a, &p->actions, node) {
-		fprintf(f, "/**\n * Action %s\n */\n\n", a->name);
-
-		action_data_codegen(a, f);
-
-		fprintf(f, "\n");
-
-		action_instr_codegen(a, f);
-
-		fprintf(f, "\n");
-	}
-
-	/* Add the pipeline code. */
-	instruction_group_list_codegen(igl, p, f);
-
-	/* Close the .c file. */
-	fclose(f);
-
-	return 0;
-}
-
-#ifndef RTE_SWX_PIPELINE_CMD_MAX_SIZE
-#define RTE_SWX_PIPELINE_CMD_MAX_SIZE 4096
-#endif
-
-static int
-pipeline_libload(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
-{
-	struct action *a;
-	struct instruction_group *g;
-	char *dir_in, *buffer = NULL;
-	const char *dir_out;
-	int status = 0;
-
-	/* Get the environment variables. */
-	dir_in = getenv("RTE_INSTALL_DIR");
-	if (!dir_in) {
-		status = -EINVAL;
-		goto free;
-	}
-
-	dir_out = "/tmp";
-
-	/* Memory allocation for the command buffer. */
-	buffer = malloc(RTE_SWX_PIPELINE_CMD_MAX_SIZE);
-	if (!buffer) {
-		status = -ENOMEM;
-		goto free;
-	}
-
-	snprintf(buffer,
-		 RTE_SWX_PIPELINE_CMD_MAX_SIZE,
-		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s/pipeline.o %s/pipeline.c "
-		 "-I %s/lib/pipeline "
-		 "-I %s/lib/eal/include "
-		 "-I %s/lib/eal/x86/include "
-		 "-I %s/lib/eal/include/generic "
-		 "-I %s/lib/meter "
-		 "-I %s/lib/port "
-		 "-I %s/lib/table "
-		 "-I %s/lib/pipeline "
-		 "-I %s/config "
-		 "-I %s/build "
-		 "-I %s/lib/eal/linux/include "
-		 ">%s/pipeline.log 2>&1 "
-		 "&& "
-		 "gcc -shared %s/pipeline.o -o %s/libpipeline.so "
-		 ">>%s/pipeline.log 2>&1",
-		 dir_out,
-		 dir_out,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_out,
-		 dir_out,
-		 dir_out,
-		 dir_out);
-
-	/* Build the shared object library. */
-	status = system(buffer);
-	if (status)
-		goto free;
-
-	/* Open library. */
-	snprintf(buffer,
-		 RTE_SWX_PIPELINE_CMD_MAX_SIZE,
-		 "%s/libpipeline.so",
-		 dir_out);
-
-	p->lib = dlopen(buffer, RTLD_LAZY);
-	if (!p->lib) {
-		status = -EIO;
-		goto free;
-	}
-
-	/* Get the action function symbols. */
-	TAILQ_FOREACH(a, &p->actions, node) {
-		snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "action_%s_run", a->name);
-
-		p->action_funcs[a->id] = dlsym(p->lib, buffer);
-		if (!p->action_funcs[a->id]) {
-			status = -EINVAL;
-			goto free;
-		}
-	}
-
-	/* Get the pipeline function symbols. */
-	TAILQ_FOREACH(g, igl, node) {
-		if (g->first_instr_id == g->last_instr_id)
-			continue;
-
-		snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "pipeline_func_%u", g->group_id);
-
-		g->func = dlsym(p->lib, buffer);
-		if (!g->func) {
-			status = -EINVAL;
-			goto free;
-		}
-	}
-
-free:
-	if (status && p->lib) {
-		dlclose(p->lib);
-		p->lib = NULL;
-	}
-
-	free(buffer);
-
-	return status;
-}
-
 static int
 pipeline_adjust_check(struct rte_swx_pipeline *p __rte_unused,
 		      struct instruction_group_list *igl)
@@ -13443,41 +13284,6 @@ pipeline_adjust(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
 	instr_jmp_resolve(p->instructions, p->instruction_data, p->n_instructions);
 }
 
-static int
-pipeline_compile(struct rte_swx_pipeline *p)
-{
-	struct instruction_group_list *igl = NULL;
-	int status = 0;
-
-	igl = instruction_group_list_create(p);
-	if (!igl) {
-		status = -ENOMEM;
-		goto free;
-	}
-
-	/* Code generation. */
-	status = pipeline_codegen(p, igl);
-	if (status)
-		goto free;
-
-	/* Build and load the shared object library. */
-	status = pipeline_libload(p, igl);
-	if (status)
-		goto free;
-
-	/* Adjust instructions. */
-	status = pipeline_adjust_check(p, igl);
-	if (status)
-		goto free;
-
-	pipeline_adjust(p, igl);
-
-free:
-	instruction_group_list_free(igl);
-
-	return status;
-}
-
 int
 rte_swx_pipeline_codegen(FILE *spec_file,
 			 FILE *code_file,
@@ -13570,3 +13376,98 @@ rte_swx_pipeline_codegen(FILE *spec_file,
 
 	return status;
 }
+
+int
+rte_swx_pipeline_build_from_lib(struct rte_swx_pipeline *p,
+				const char *lib_file_name)
+{
+	void *lib = NULL;
+	struct pipeline_spec *s = NULL;
+	struct instruction_group_list *igl = NULL;
+	struct action *a;
+	struct instruction_group *g;
+	int status = 0;
+
+	/* Check input arguments. */
+	if (!p || p->build_done || !lib_file_name || !lib_file_name[0]) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Open the library. */
+	lib = dlopen(lib_file_name, RTLD_LAZY);
+	if (!lib) {
+		status = -EIO;
+		goto free;
+	}
+
+	/* Get the pipeline specification structure from the library. */
+	s = dlsym(lib, "pipeline_spec");
+	if (!s) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Pipeline configuration based on the specification structure. */
+	status = pipeline_spec_configure(p, s, NULL);
+	if (status)
+		goto free;
+
+	/* Pipeline build. */
+	status = rte_swx_pipeline_build(p);
+	if (status)
+		goto free;
+
+	/* Action instructions. */
+	TAILQ_FOREACH(a, &p->actions, node) {
+		char name[RTE_SWX_NAME_SIZE * 2];
+
+		snprintf(name, sizeof(name), "action_%s_run", a->name);
+
+		p->action_funcs[a->id] = dlsym(lib, name);
+		if (!p->action_funcs[a->id]) {
+			status = -EINVAL;
+			goto free;
+		}
+	}
+
+	/* Pipeline instructions. */
+	igl = instruction_group_list_create(p);
+	if (!igl) {
+		status = -ENOMEM;
+		goto free;
+	}
+
+	TAILQ_FOREACH(g, igl, node) {
+		char name[RTE_SWX_NAME_SIZE * 2];
+
+		if (g->first_instr_id == g->last_instr_id)
+			continue;
+
+		snprintf(name, sizeof(name), "pipeline_func_%u", g->group_id);
+
+		g->func = dlsym(lib, name);
+		if (!g->func) {
+			status = -EINVAL;
+			goto free;
+		}
+	}
+
+	status = pipeline_adjust_check(p, igl);
+	if (status)
+		goto free;
+
+	pipeline_adjust(p, igl);
+
+	p->lib = lib;
+
+free:
+	if (status && lib) {
+		dlclose(lib);
+		p->lib = NULL;
+	}
+
+	instruction_group_list_free(igl);
+
+	return status;
+}
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index 2bd019b05f..cb834cd64d 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -967,30 +967,26 @@ rte_swx_pipeline_codegen(FILE *spec_file,
 			 const char **err_msg);
 
 /**
- * Pipeline build from specification file
+ * Pipeline build from shared object library
+ *
+ * The shared object library must be built from the C language source code file
+ * previously generated by the rte_swx_pipeline_codegen() API function.
  *
  * @param[in] p
  *   Pipeline handle.
- * @param[in] spec
- *   Pipeline specification file.
- * @param[out] err_line
- *   In case of error and non-NULL, the line number within the *spec* file where
- *   the error occurred. The first line number in the file is 1.
- * @param[out] err_msg
- *   In case of error and non-NULL, the error message.
+ * @param[in] lib_file_name
+ *   Shared object library file name.
  * @return
  *   0 on success or the following error codes otherwise:
  *   -EINVAL: Invalid argument;
  *   -ENOMEM: Not enough space/cannot allocate memory;
- *   -EEXIST: Resource with the same name already exists;
+ *   -EEXIST: Pipeline was already built successfully;
  *   -ENODEV: Extern object or table creation error.
  */
 __rte_experimental
 int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec,
-				 uint32_t *err_line,
-				 const char **err_msg);
+rte_swx_pipeline_build_from_lib(struct rte_swx_pipeline *p,
+				const char *lib_file_name);
 
 /**
  * Pipeline run
diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 05cf952816..206b514856 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -3514,54 +3514,3 @@ pipeline_spec_configure(struct rte_swx_pipeline *p,
 
 	return 0;
 }
-
-int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec_file,
-				 uint32_t *err_line,
-				 const char **err_msg)
-{
-	struct pipeline_spec *s = NULL;
-	int status = 0;
-
-	/* Check the input arguments. */
-	if (!p || !spec_file) {
-		if (err_line)
-			*err_line = 0;
-		if (err_msg)
-			*err_msg = "Invalid input argument.";
-		status = -EINVAL;
-		goto error;
-	}
-
-	/* Spec file parse. */
-	s = pipeline_spec_parse(spec_file, err_line, err_msg);
-	if (!s) {
-		status = -EINVAL;
-		goto error;
-	}
-
-	/* Pipeline configure. */
-	status = pipeline_spec_configure(p, s, err_msg);
-	if (status) {
-		if (err_line)
-			*err_line = 0;
-		goto error;
-	}
-
-	/* Pipeline build. */
-	status = rte_swx_pipeline_build(p);
-	if (status) {
-		if (err_line)
-			*err_line = 0;
-		if (err_msg)
-			*err_msg = "Pipeline build error.";
-		goto error;
-	}
-
-	return 0;
-
-error:
-	pipeline_spec_free(s);
-	return status;
-}
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 51165d48cf..810cc56467 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -82,7 +82,6 @@ EXPERIMENTAL {
 	rte_swx_ctl_table_ops_get;
 	rte_swx_pipeline_action_config;
 	rte_swx_pipeline_build;
-	rte_swx_pipeline_build_from_spec;
 	rte_swx_pipeline_config;
 	rte_swx_pipeline_extern_func_register;
 	rte_swx_pipeline_extern_object_config;
@@ -148,4 +147,5 @@ EXPERIMENTAL {
 
 	#added in 22.11
 	rte_swx_pipeline_codegen;
+	rte_swx_pipeline_build_from_lib;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH 7/9] examples/pipeline: add CLI command for pipeline code generation
  2022-07-18 13:07 [PATCH 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
                   ` (4 preceding siblings ...)
  2022-07-18 13:07 ` [PATCH 6/9] pipeline: add API for shared library-based pipeline build Cristian Dumitrescu
@ 2022-07-18 13:07 ` Cristian Dumitrescu
  2022-07-18 13:07 ` [PATCH 8/9] examples/pipeline: add CLI command for shared library build Cristian Dumitrescu
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:07 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add CLI command for the pipeline code generation operation.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 61 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 1f75b5dc9d..fdaf5dd16b 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -983,6 +983,53 @@ cmd_pipeline_port_out(char **tokens,
 	}
 }
 
+static const char cmd_pipeline_codegen_help[] =
+"pipeline codegen <spec_file> <code_file>\n";
+
+static void
+cmd_pipeline_codegen(char **tokens,
+	uint32_t n_tokens,
+	char *out,
+	size_t out_size,
+	void *obj __rte_unused)
+{
+	FILE *spec_file = NULL;
+	FILE *code_file = NULL;
+	uint32_t err_line;
+	const char *err_msg;
+	int status;
+
+	if (n_tokens != 4) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	spec_file = fopen(tokens[2], "r");
+	if (!spec_file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", tokens[2]);
+		return;
+	}
+
+	code_file = fopen(tokens[3], "w");
+	if (!code_file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
+		return;
+	}
+
+	status = rte_swx_pipeline_codegen(spec_file,
+					  code_file,
+					  &err_line,
+					  &err_msg);
+
+	fclose(spec_file);
+	fclose(code_file);
+
+	if (status) {
+		snprintf(out, out_size, "Error %d at line %u: %s\n.",
+			status, err_line, err_msg);
+		return;
+	}
+}
 static const char cmd_pipeline_build_help[] =
 "pipeline <pipeline_name> build <lib_file>\n";
 
@@ -2962,6 +3009,7 @@ cmd_help(char **tokens,
 			"\tpipeline create\n"
 			"\tpipeline port in\n"
 			"\tpipeline port out\n"
+			"\tpipeline codegen\n"
 			"\tpipeline build\n"
 			"\tpipeline table add\n"
 			"\tpipeline table delete\n"
@@ -3031,6 +3079,12 @@ cmd_help(char **tokens,
 		}
 	}
 
+	if ((strcmp(tokens[0], "pipeline") == 0) &&
+		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
@@ -3309,6 +3363,13 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			return;
 		}
 
+		if ((n_tokens >= 3) &&
+			(strcmp(tokens[1], "codegen") == 0)) {
+			cmd_pipeline_codegen(tokens, n_tokens, out, out_size,
+				obj);
+			return;
+		}
+
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[2], "build") == 0)) {
 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH 8/9] examples/pipeline: add CLI command for shared library build
  2022-07-18 13:07 [PATCH 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
                   ` (5 preceding siblings ...)
  2022-07-18 13:07 ` [PATCH 7/9] examples/pipeline: add CLI command for pipeline code generation Cristian Dumitrescu
@ 2022-07-18 13:07 ` Cristian Dumitrescu
  2022-07-18 13:07 ` [PATCH 9/9] examples/pipeline: call CLI commands for code generation and build Cristian Dumitrescu
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:07 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add CLI command for the shared object library build operation.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 147 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 143 insertions(+), 4 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index fdaf5dd16b..f1d2fbf52d 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -6,6 +6,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
@@ -25,6 +26,10 @@
 #define CMD_MAX_TOKENS     256
 #endif
 
+#ifndef MAX_LINE_SIZE
+#define MAX_LINE_SIZE 2048
+#endif
+
 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
@@ -1030,6 +1035,130 @@ cmd_pipeline_codegen(char **tokens,
 		return;
 	}
 }
+
+static const char cmd_pipeline_libbuild_help[] =
+"pipeline libbuild <code_file> <lib_file>\n";
+
+static void
+cmd_pipeline_libbuild(char **tokens,
+	uint32_t n_tokens,
+	char *out,
+	size_t out_size,
+	void *obj __rte_unused)
+{
+	char *code_file, *lib_file, *obj_file = NULL, *log_file = NULL;
+	char *install_dir, *buffer = NULL;
+	size_t length;
+	int status = 0;
+
+	if (n_tokens != 4) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		goto free;
+	}
+
+	install_dir = getenv("RTE_INSTALL_DIR");
+	if (!install_dir) {
+		snprintf(out, out_size, "Error: Environment variable RTE_INSTALL_DIR is not set.");
+		return;
+	}
+
+	snprintf(out, out_size, "Using DPDK source code from \"%s\".\n", install_dir);
+	out_size -= strlen(out);
+	out += strlen(out);
+
+	code_file = tokens[2];
+	length = strnlen(code_file, MAX_LINE_SIZE);
+	if ((length < 3) ||
+	    (code_file[length - 2] != '.') ||
+	    (code_file[length - 1] != 'c')) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "code_file");
+		goto free;
+	}
+
+	lib_file = tokens[3];
+	length = strnlen(lib_file, MAX_LINE_SIZE);
+	if ((length < 4) ||
+	    (lib_file[length - 3] != '.') ||
+	    (lib_file[length - 2] != 's') ||
+	    (lib_file[length - 1] != 'o')) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "lib_file");
+		goto free;
+	}
+
+	obj_file = malloc(length);
+	log_file = malloc(length + 2);
+	if (!obj_file || !log_file) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		goto free;
+	}
+
+	memcpy(obj_file, lib_file, length - 2);
+	obj_file[length - 2] = 'o';
+	obj_file[length - 1] = 0;
+
+	memcpy(log_file, lib_file, length - 2);
+	log_file[length - 2] = 'l';
+	log_file[length - 1] = 'o';
+	log_file[length] = 'g';
+	log_file[length + 1] = 0;
+
+	buffer = malloc(MAX_LINE_SIZE);
+	if (!buffer) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		return;
+	}
+
+	snprintf(buffer,
+		 MAX_LINE_SIZE,
+		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s %s "
+		 "-I %s/lib/pipeline "
+		 "-I %s/lib/eal/include "
+		 "-I %s/lib/eal/x86/include "
+		 "-I %s/lib/eal/include/generic "
+		 "-I %s/lib/meter "
+		 "-I %s/lib/port "
+		 "-I %s/lib/table "
+		 "-I %s/lib/pipeline "
+		 "-I %s/config "
+		 "-I %s/build "
+		 "-I %s/lib/eal/linux/include "
+		 ">%s 2>&1 "
+		 "&& "
+		 "gcc -shared %s -o %s "
+		 ">>%s 2>&1",
+		 obj_file,
+		 code_file,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 log_file,
+		 obj_file,
+		 lib_file,
+		 log_file);
+
+	status = system(buffer);
+	if (status) {
+		snprintf(out,
+			 out_size,
+			 "Library build failed, see file \"%s\" for details.\n",
+			 log_file);
+		goto free;
+	}
+
+free:
+	free(obj_file);
+	free(log_file);
+	free(buffer);
+}
+
 static const char cmd_pipeline_build_help[] =
 "pipeline <pipeline_name> build <lib_file>\n";
 
@@ -1080,10 +1209,6 @@ table_entry_free(struct rte_swx_table_entry *entry)
 	free(entry);
 }
 
-#ifndef MAX_LINE_SIZE
-#define MAX_LINE_SIZE 2048
-#endif
-
 static int
 pipeline_table_entries_add(struct rte_swx_ctl_pipeline *p,
 			   const char *table_name,
@@ -3010,6 +3135,7 @@ cmd_help(char **tokens,
 			"\tpipeline port in\n"
 			"\tpipeline port out\n"
 			"\tpipeline codegen\n"
+			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
 			"\tpipeline table add\n"
 			"\tpipeline table delete\n"
@@ -3085,6 +3211,12 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if ((strcmp(tokens[0], "pipeline") == 0) &&
+		(n_tokens == 2) && (strcmp(tokens[1], "libbuild") == 0)) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_libbuild_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
@@ -3370,6 +3502,13 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			return;
 		}
 
+		if ((n_tokens >= 3) &&
+			(strcmp(tokens[1], "libbuild") == 0)) {
+			cmd_pipeline_libbuild(tokens, n_tokens, out, out_size,
+				obj);
+			return;
+		}
+
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[2], "build") == 0)) {
 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH 9/9] examples/pipeline: call CLI commands for code generation and build
  2022-07-18 13:07 [PATCH 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
                   ` (6 preceding siblings ...)
  2022-07-18 13:07 ` [PATCH 8/9] examples/pipeline: add CLI command for shared library build Cristian Dumitrescu
@ 2022-07-18 13:07 ` Cristian Dumitrescu
  2022-07-18 13:25 ` [PATCH V2 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
  2022-07-28 15:11 ` [PATCH V6 00/17] pipeline: pipeline configuration and build improvements Cristian Dumitrescu
  9 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:07 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Update the example CLI scripts with the commands for code generation
and shared object library build.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/examples/fib.cli               | 8 +++++++-
 examples/pipeline/examples/hash_func.cli         | 8 +++++++-
 examples/pipeline/examples/l2fwd.cli             | 8 +++++++-
 examples/pipeline/examples/l2fwd_macswp.cli      | 8 +++++++-
 examples/pipeline/examples/l2fwd_macswp_pcap.cli | 8 +++++++-
 examples/pipeline/examples/l2fwd_pcap.cli        | 8 +++++++-
 examples/pipeline/examples/learner.cli           | 8 +++++++-
 examples/pipeline/examples/meter.cli             | 8 +++++++-
 examples/pipeline/examples/mirroring.cli         | 8 +++++++-
 examples/pipeline/examples/recirculation.cli     | 8 +++++++-
 examples/pipeline/examples/registers.cli         | 8 +++++++-
 examples/pipeline/examples/selector.cli          | 8 +++++++-
 examples/pipeline/examples/varbit.cli            | 8 +++++++-
 examples/pipeline/examples/vxlan.cli             | 8 +++++++-
 examples/pipeline/examples/vxlan_pcap.cli        | 8 +++++++-
 15 files changed, 105 insertions(+), 15 deletions(-)

diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 93ab2b08f8..8b55175bf3 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/fib.spec /tmp/fib.c
+pipeline libbuild /tmp/fib.c /tmp/fib.so
+
 ;
 ; Customize the LINK parameters to match your setup.
 ;
@@ -26,7 +32,7 @@ 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/fib.spec
+pipeline PIPELINE0 build /tmp/fib.so
 
 ;
 ; Initial set of table entries.
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index d65cd62d17..f7bb28b28b 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2022 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/hash_func.spec /tmp/hash_func.c
+pipeline libbuild /tmp/hash_func.c /tmp/hash_func.so
+
 ;
 ; Customize the LINK parameters to match your setup.
 ;
@@ -26,7 +32,7 @@ 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
+pipeline PIPELINE0 build /tmp/hash_func.so
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index d89caf2d0a..a71727309b 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd.spec /tmp/l2fwd.c
+pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
+
 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
@@ -20,6 +26,6 @@ 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/l2fwd.spec
+pipeline PIPELINE0 build /tmp/l2fwd.so
 
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index 0f2a89ac5b..d8f5f9f735 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd_macswp.spec /tmp/l2fwd_macswp.c
+pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
+
 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
@@ -20,6 +26,6 @@ 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/l2fwd_macswp.spec
+pipeline PIPELINE0 build /tmp/l2fwd_macswp.so
 
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index e9656fe3c2..bd077876ff 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd_macswp.spec /tmp/l2fwd_macswp.c
+pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
+
 mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
 
 pipeline PIPELINE0 create 0
@@ -15,6 +21,6 @@ pipeline PIPELINE0 port out 1 sink none
 pipeline PIPELINE0 port out 2 sink none
 pipeline PIPELINE0 port out 3 sink none
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/l2fwd_macswp.spec
+pipeline PIPELINE0 build /tmp/l2fwd_macswp.so
 
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 23fcb199f1..2e56a116af 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd.spec /tmp/l2fwd.c
+pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
+
 mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
 
 pipeline PIPELINE0 create 0
@@ -15,6 +21,6 @@ pipeline PIPELINE0 port out 1 sink none
 pipeline PIPELINE0 port out 2 sink none
 pipeline PIPELINE0 port out 3 sink none
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/l2fwd.spec
+pipeline PIPELINE0 build /tmp/l2fwd.so
 
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 688ce34f34..10eb2af417 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/learner.spec /tmp/learner.c
+pipeline libbuild /tmp/learner.c /tmp/learner.so
+
 ;
 ; Customize the LINK parameters to match your setup.
 ;
@@ -26,7 +32,7 @@ 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/learner.spec
+pipeline PIPELINE0 build /tmp/learner.so
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index b29ed24022..9c22014f46 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -4,6 +4,12 @@
 ; Example command line:
 ;	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/meter.cli
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/meter.spec /tmp/meter.c
+pipeline libbuild /tmp/meter.c /tmp/meter.so
+
 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
@@ -23,7 +29,7 @@ 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/meter.spec
+pipeline PIPELINE0 build /tmp/meter.so
 
 pipeline PIPELINE0 meter profile platinum add cir 46000000 pir 138000000 cbs 1000000 pbs 1000000
 pipeline PIPELINE0 meter meters from 0 to 15 set profile platinum
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index 46d57db4ec..9614f64d38 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2022 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/mirroring.spec /tmp/mirroring.c
+pipeline libbuild /tmp/mirroring.c /tmp/mirroring.so
+
 ;
 ; Customize the LINK parameters to match your setup.
 ;
@@ -27,7 +33,7 @@ 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/mirroring.spec
+pipeline PIPELINE0 build /tmp/mirroring.so
 
 ;
 ; Packet mirroring sessions.
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index f855c5c327..bd114e91cd 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2022 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/recirculation.spec /tmp/recirculation.c
+pipeline libbuild /tmp/recirculation.c /tmp/recirculation.so
+
 ;
 ; Customize the LINK parameters to match your setup.
 ;
@@ -26,7 +32,7 @@ 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/recirculation.spec
+pipeline PIPELINE0 build /tmp/recirculation.so
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index 8d026294cb..3d9eeb0d5c 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -4,6 +4,12 @@
 ; Example command line:
 ;	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/registers.cli
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/registers.spec /tmp/registers.c
+pipeline libbuild /tmp/registers.c /tmp/registers.so
+
 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
@@ -23,6 +29,6 @@ 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/registers.spec
+pipeline PIPELINE0 build /tmp/registers.so
 
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index 123782c57b..6c7d032b10 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/selector.spec /tmp/selector.c
+pipeline libbuild /tmp/selector.c /tmp/selector.so
+
 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
@@ -20,7 +26,7 @@ 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/selector.spec
+pipeline PIPELINE0 build /tmp/selector.so
 
 pipeline PIPELINE0 selector s group add
 pipeline PIPELINE0 selector s group member add ./examples/pipeline/examples/selector.txt
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 9caeb9ca26..545cde262e 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/varbit.spec /tmp/varbit.c
+pipeline libbuild /tmp/varbit.c /tmp/varbit.so
+
 ;
 ; Customize the LINK parameters to match your setup.
 ;
@@ -26,7 +32,7 @@ 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/varbit.spec
+pipeline PIPELINE0 build /tmp/varbit.so
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 444f3f7bd8..321a28ba44 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/vxlan.spec /tmp/vxlan.c
+pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
+
 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
@@ -20,7 +26,7 @@ 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/vxlan.spec
+pipeline PIPELINE0 build /tmp/vxlan.so
 pipeline PIPELINE0 table vxlan_table add ./examples/pipeline/examples/vxlan_table.txt
 pipeline PIPELINE0 commit
 
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index 83fca8d0d9..596169f933 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/vxlan.spec /tmp/vxlan.c
+pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
+
 mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
 
 pipeline PIPELINE0 create 0
@@ -15,7 +21,7 @@ pipeline PIPELINE0 port out 1 sink none
 pipeline PIPELINE0 port out 2 sink none
 pipeline PIPELINE0 port out 3 sink none
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/vxlan.spec
+pipeline PIPELINE0 build /tmp/vxlan.so
 pipeline PIPELINE0 table vxlan_table add ./examples/pipeline/examples/vxlan_table.txt
 pipeline PIPELINE0 commit
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V2 1/9] pipeline: move specification data structures to internal header
  2022-07-18 13:07 [PATCH 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
                   ` (7 preceding siblings ...)
  2022-07-18 13:07 ` [PATCH 9/9] examples/pipeline: call CLI commands for code generation and build Cristian Dumitrescu
@ 2022-07-18 13:25 ` Cristian Dumitrescu
  2022-07-18 13:25   ` [PATCH V2 2/9] pipeline: add pipeline specification data structure Cristian Dumitrescu
                     ` (8 more replies)
  2022-07-28 15:11 ` [PATCH V6 00/17] pipeline: pipeline configuration and build improvements Cristian Dumitrescu
  9 siblings, 9 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:25 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Move all the pipeline object specification data structures to an
internal header file.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 126 +------------------
 lib/pipeline/rte_swx_pipeline_spec.h | 176 +++++++++++++++++++++++++++
 2 files changed, 177 insertions(+), 125 deletions(-)
 create mode 100644 lib/pipeline/rte_swx_pipeline_spec.h

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 904b9eb471..5e07b4f794 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -9,7 +9,7 @@
 
 #include <rte_common.h>
 
-#include "rte_swx_pipeline.h"
+#include "rte_swx_pipeline_spec.h"
 
 #ifndef MAX_LINE_LENGTH
 #define MAX_LINE_LENGTH 2048
@@ -34,15 +34,7 @@
 
 /*
  * extobj.
- *
- * extobj OBJ_NAME instanceof OBJ_TYPE [ pragma OBJ_CREATE_ARGS ]
  */
-struct extobj_spec {
-	char *name;
-	char *extern_type_name;
-	char *pragma;
-};
-
 static void
 extobj_spec_free(struct extobj_spec *s)
 {
@@ -104,18 +96,7 @@ extobj_statement_parse(struct extobj_spec *s,
 /*
  * struct.
  *
- * struct STRUCT_TYPE_NAME {
- *	bit<SIZE> | varbit<SIZE> FIELD_NAME
- *	...
- * }
  */
-struct struct_spec {
-	char *name;
-	struct rte_swx_field_params *fields;
-	uint32_t n_fields;
-	int varbit;
-};
-
 static void
 struct_spec_free(struct struct_spec *s)
 {
@@ -293,13 +274,7 @@ struct_block_parse(struct struct_spec *s,
 /*
  * header.
  *
- * header HEADER_NAME instanceof STRUCT_TYPE_NAME
  */
-struct header_spec {
-	char *name;
-	char *struct_type_name;
-};
-
 static void
 header_spec_free(struct header_spec *s)
 {
@@ -351,12 +326,7 @@ header_statement_parse(struct header_spec *s,
 /*
  * metadata.
  *
- * metadata instanceof STRUCT_TYPE_NAME
  */
-struct metadata_spec {
-	char *struct_type_name;
-};
-
 static void
 metadata_spec_free(struct metadata_spec *s)
 {
@@ -400,18 +370,7 @@ metadata_statement_parse(struct metadata_spec *s,
 /*
  * action.
  *
- * action ACTION_NAME args none | instanceof STRUCT_TYPE_NAME {
- *	INSTRUCTION
- *	...
- * }
  */
-struct action_spec {
-	char *name;
-	char *args_struct_type_name;
-	const char **instructions;
-	uint32_t n_instructions;
-};
-
 static void
 action_spec_free(struct action_spec *s)
 {
@@ -540,29 +499,7 @@ action_block_parse(struct action_spec *s,
 /*
  * table.
  *
- * table TABLE_NAME {
- *	key {
- *		MATCH_FIELD_NAME exact | wildcard | lpm
- *		...
- *	}
- *	actions {
- *		ACTION_NAME [ @tableonly | @defaultonly ]
- *		...
- *	}
- *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
- *	instanceof TABLE_TYPE_NAME
- *	pragma ARGS
- *	size SIZE
- * }
  */
-struct table_spec {
-	char *name;
-	struct rte_swx_pipeline_table_params params;
-	char *recommended_table_type_name;
-	char *args;
-	uint32_t size;
-};
-
 static void
 table_spec_free(struct table_spec *s)
 {
@@ -1084,22 +1021,7 @@ table_block_parse(struct table_spec *s,
 /*
  * selector.
  *
- * selector SELECTOR_NAME {
- *	group_id FIELD_NAME
- *	selector {
- *		FIELD_NAME
- *		...
- *	}
- *	member_id FIELD_NAME
- *	n_groups N_GROUPS
- *	n_members_per_group N_MEMBERS_PER_GROUP
- * }
  */
-struct selector_spec {
-	char *name;
-	struct rte_swx_pipeline_selector_params params;
-};
-
 static void
 selector_spec_free(struct selector_spec *s)
 {
@@ -1385,31 +1307,7 @@ selector_block_parse(struct selector_spec *s,
 /*
  * learner.
  *
- * learner LEARNER_NAME {
- *	key {
- *		MATCH_FIELD_NAME
- *		...
- *	}
- *	actions {
- *		ACTION_NAME [ @tableonly | @defaultonly]
- *		...
- *	}
- *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
- *	size SIZE
- *	timeout {
- *		TIMEOUT_IN_SECONDS
- *		...
- *	}
- * }
  */
-struct learner_spec {
-	char *name;
-	struct rte_swx_pipeline_learner_params params;
-	uint32_t size;
-	uint32_t *timeout;
-	uint32_t n_timeouts;
-};
-
 static void
 learner_spec_free(struct learner_spec *s)
 {
@@ -1958,14 +1856,7 @@ learner_block_parse(struct learner_spec *s,
 /*
  * regarray.
  *
- * regarray NAME size SIZE initval INITVAL
  */
-struct regarray_spec {
-	char *name;
-	uint64_t init_val;
-	uint32_t size;
-};
-
 static void
 regarray_spec_free(struct regarray_spec *s)
 {
@@ -2033,13 +1924,7 @@ regarray_statement_parse(struct regarray_spec *s,
 /*
  * metarray.
  *
- * metarray NAME size SIZE
  */
-struct metarray_spec {
-	char *name;
-	uint32_t size;
-};
-
 static void
 metarray_spec_free(struct metarray_spec *s)
 {
@@ -2095,16 +1980,7 @@ metarray_statement_parse(struct metarray_spec *s,
 /*
  * apply.
  *
- * apply {
- *	INSTRUCTION
- *	...
- * }
  */
-struct apply_spec {
-	const char **instructions;
-	uint32_t n_instructions;
-};
-
 static void
 apply_spec_free(struct apply_spec *s)
 {
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
new file mode 100644
index 0000000000..8458de878a
--- /dev/null
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#include <stdint.h>
+#include <stdio.h>
+
+#include <rte_common.h>
+
+#include <rte_swx_pipeline.h>
+
+/*
+ * extobj.
+ *
+ * extobj OBJ_NAME instanceof OBJ_TYPE [ pragma OBJ_CREATE_ARGS ]
+ */
+struct extobj_spec {
+	char *name;
+	char *extern_type_name;
+	char *pragma;
+};
+
+/*
+ * struct.
+ *
+ * struct STRUCT_TYPE_NAME {
+ *	bit<SIZE> | varbit<SIZE> FIELD_NAME
+ *	...
+ * }
+ */
+struct struct_spec {
+	char *name;
+	struct rte_swx_field_params *fields;
+	uint32_t n_fields;
+	int varbit;
+};
+
+/*
+ * header.
+ *
+ * header HEADER_NAME instanceof STRUCT_TYPE_NAME
+ */
+struct header_spec {
+	char *name;
+	char *struct_type_name;
+};
+
+/*
+ * metadata.
+ *
+ * metadata instanceof STRUCT_TYPE_NAME
+ */
+struct metadata_spec {
+	char *struct_type_name;
+};
+
+/*
+ * action.
+ *
+ * action ACTION_NAME args none | instanceof STRUCT_TYPE_NAME {
+ *	INSTRUCTION
+ *	...
+ * }
+ */
+struct action_spec {
+	char *name;
+	char *args_struct_type_name;
+	const char **instructions;
+	uint32_t n_instructions;
+};
+
+/*
+ * table.
+ *
+ * table TABLE_NAME {
+ *	key {
+ *		MATCH_FIELD_NAME exact | wildcard | lpm
+ *		...
+ *	}
+ *	actions {
+ *		ACTION_NAME [ @tableonly | @defaultonly ]
+ *		...
+ *	}
+ *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
+ *	instanceof TABLE_TYPE_NAME
+ *	pragma ARGS
+ *	size SIZE
+ * }
+ */
+struct table_spec {
+	char *name;
+	struct rte_swx_pipeline_table_params params;
+	char *recommended_table_type_name;
+	char *args;
+	uint32_t size;
+};
+
+/*
+ * selector.
+ *
+ * selector SELECTOR_NAME {
+ *	group_id FIELD_NAME
+ *	selector {
+ *		FIELD_NAME
+ *		...
+ *	}
+ *	member_id FIELD_NAME
+ *	n_groups N_GROUPS
+ *	n_members_per_group N_MEMBERS_PER_GROUP
+ * }
+ */
+struct selector_spec {
+	char *name;
+	struct rte_swx_pipeline_selector_params params;
+};
+
+/*
+ * learner.
+ *
+ * learner LEARNER_NAME {
+ *	key {
+ *		MATCH_FIELD_NAME
+ *		...
+ *	}
+ *	actions {
+ *		ACTION_NAME [ @tableonly | @defaultonly]
+ *		...
+ *	}
+ *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
+ *	size SIZE
+ *	timeout {
+ *		TIMEOUT_IN_SECONDS
+ *		...
+ *	}
+ * }
+ */
+struct learner_spec {
+	char *name;
+	struct rte_swx_pipeline_learner_params params;
+	uint32_t size;
+	uint32_t *timeout;
+	uint32_t n_timeouts;
+};
+
+/*
+ * regarray.
+ *
+ * regarray NAME size SIZE initval INITVAL
+ */
+struct regarray_spec {
+	char *name;
+	uint64_t init_val;
+	uint32_t size;
+};
+
+/*
+ * metarray.
+ *
+ * metarray NAME size SIZE
+ */
+struct metarray_spec {
+	char *name;
+	uint32_t size;
+};
+
+/*
+ * apply.
+ *
+ * apply {
+ *	INSTRUCTION
+ *	...
+ * }
+ */
+struct apply_spec {
+	const char **instructions;
+	uint32_t n_instructions;
+};
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V2 2/9] pipeline: add pipeline specification data structure
  2022-07-18 13:25 ` [PATCH V2 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
@ 2022-07-18 13:25   ` Cristian Dumitrescu
  2022-07-18 13:25   ` [PATCH V2 3/9] pipeline: rework the specification file-based pipeline build Cristian Dumitrescu
                     ` (7 subsequent siblings)
  8 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:25 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add specification data structure for the entire pipeline.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 21 ++++++++++++++++++
 lib/pipeline/rte_swx_pipeline_spec.h | 32 ++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 5e07b4f794..642091b678 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2082,6 +2082,27 @@ apply_block_parse(struct apply_spec *s,
 /*
  * Pipeline.
  */
+void
+pipeline_spec_free(struct pipeline_spec *s)
+{
+	if (!s)
+		return;
+
+	free(s->extobjs);
+	free(s->structs);
+	free(s->headers);
+	free(s->metadata);
+	free(s->actions);
+	free(s->tables);
+	free(s->selectors);
+	free(s->learners);
+	free(s->regarrays);
+	free(s->metarrays);
+	free(s->apply);
+
+	memset(s, 0, sizeof(struct pipeline_spec));
+}
+
 int
 rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				 FILE *spec,
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 8458de878a..e1170a33b1 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -174,3 +174,35 @@ struct apply_spec {
 	const char **instructions;
 	uint32_t n_instructions;
 };
+
+/*
+ * Pipeline.
+ */
+struct pipeline_spec {
+	struct extobj_spec *extobjs;
+	struct struct_spec *structs;
+	struct header_spec *headers;
+	struct metadata_spec *metadata;
+	struct action_spec *actions;
+	struct table_spec *tables;
+	struct selector_spec *selectors;
+	struct learner_spec *learners;
+	struct regarray_spec *regarrays;
+	struct metarray_spec *metarrays;
+	struct apply_spec *apply;
+
+	uint32_t n_extobjs;
+	uint32_t n_structs;
+	uint32_t n_headers;
+	uint32_t n_metadata;
+	uint32_t n_actions;
+	uint32_t n_tables;
+	uint32_t n_selectors;
+	uint32_t n_learners;
+	uint32_t n_regarrays;
+	uint32_t n_metarrays;
+	uint32_t n_apply;
+};
+
+void
+pipeline_spec_free(struct pipeline_spec *s);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V2 3/9] pipeline: rework the specification file-based pipeline build
  2022-07-18 13:25 ` [PATCH V2 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
  2022-07-18 13:25   ` [PATCH V2 2/9] pipeline: add pipeline specification data structure Cristian Dumitrescu
@ 2022-07-18 13:25   ` Cristian Dumitrescu
  2022-07-18 13:25   ` [PATCH V2 4/9] pipeline: generate the code for pipeline specification structure Cristian Dumitrescu
                     ` (6 subsequent siblings)
  8 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:25 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Rework the specification file-based pipeline build operation to first
parse the specification file into the previously introduced pipeline
specification data structure, then use this structure to configure
and build the pipeline.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 478 +++++++++++++++++++++------
 lib/pipeline/rte_swx_pipeline_spec.h |   9 +
 2 files changed, 385 insertions(+), 102 deletions(-)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 642091b678..62929a9da6 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2103,11 +2103,10 @@ pipeline_spec_free(struct pipeline_spec *s)
 	memset(s, 0, sizeof(struct pipeline_spec));
 }
 
-int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec,
-				 uint32_t *err_line,
-				 const char **err_msg)
+struct pipeline_spec *
+pipeline_spec_parse(FILE *spec,
+		    uint32_t *err_line,
+		    const char **err_msg)
 {
 	struct extobj_spec extobj_spec = {0};
 	struct struct_spec struct_spec = {0};
@@ -2120,26 +2119,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 	struct regarray_spec regarray_spec = {0};
 	struct metarray_spec metarray_spec = {0};
 	struct apply_spec apply_spec = {0};
-	uint32_t n_lines;
+	struct pipeline_spec *s = NULL;
+	uint32_t n_lines = 0;
 	uint32_t block_mask = 0;
-	int status;
+	int status = 0;
 
 	/* Check the input arguments. */
-	if (!p) {
+	if (!spec) {
 		if (err_line)
-			*err_line = 0;
+			*err_line = n_lines;
 		if (err_msg)
-			*err_msg = "Null pipeline argument.";
+			*err_msg = "Invalid input argument.";
 		status = -EINVAL;
 		goto error;
 	}
 
-	if (!spec) {
+	/* Memory allocation. */
+	s = calloc(sizeof(struct pipeline_spec), 1);
+	if (!s) {
 		if (err_line)
-			*err_line = 0;
+			*err_line = n_lines;
 		if (err_msg)
-			*err_msg = "Null specification file argument.";
-		status = -EINVAL;
+			*err_msg = "Memory allocation failed.";
+		status = -ENOMEM;
 		goto error;
 	}
 
@@ -2200,6 +2202,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* struct block. */
 		if (block_mask & (1 << STRUCT_BLOCK)) {
+			struct struct_spec *new_structs;
+
 			status = struct_block_parse(&struct_spec,
 						    &block_mask,
 						    tokens,
@@ -2214,26 +2218,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_struct_type_register(p,
-				struct_spec.name,
-				struct_spec.fields,
-				struct_spec.n_fields,
-				struct_spec.varbit);
-			if (status) {
+			new_structs = realloc(s->structs,
+					      (s->n_structs + 1) * sizeof(struct struct_spec));
+			if (!new_structs) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Struct registration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			struct_spec_free(&struct_spec);
+			s->structs = new_structs;
+			memcpy(&s->structs[s->n_structs], &struct_spec, sizeof(struct struct_spec));
+			s->n_structs++;
+			memset(&struct_spec, 0, sizeof(struct struct_spec));
 
 			continue;
 		}
 
 		/* action block. */
 		if (block_mask & (1 << ACTION_BLOCK)) {
+			struct action_spec *new_actions;
+
 			status = action_block_parse(&action_spec,
 						    &block_mask,
 						    tokens,
@@ -2248,26 +2255,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_action_config(p,
-				action_spec.name,
-				action_spec.args_struct_type_name,
-				action_spec.instructions,
-				action_spec.n_instructions);
-			if (status) {
+			new_actions = realloc(s->actions,
+					      (s->n_actions + 1) * sizeof(struct action_spec));
+			if (!new_actions) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Action config error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			action_spec_free(&action_spec);
+			s->actions = new_actions;
+			memcpy(&s->actions[s->n_actions], &action_spec, sizeof(struct action_spec));
+			s->n_actions++;
+			memset(&action_spec, 0, sizeof(struct action_spec));
 
 			continue;
 		}
 
 		/* table block. */
 		if (block_mask & (1 << TABLE_BLOCK)) {
+			struct table_spec *new_tables;
+
 			status = table_block_parse(&table_spec,
 						   &block_mask,
 						   tokens,
@@ -2282,27 +2292,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_table_config(p,
-				table_spec.name,
-				&table_spec.params,
-				table_spec.recommended_table_type_name,
-				table_spec.args,
-				table_spec.size);
-			if (status) {
+			new_tables = realloc(s->tables,
+					     (s->n_tables + 1) * sizeof(struct table_spec));
+			if (!new_tables) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Table configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			table_spec_free(&table_spec);
+			s->tables = new_tables;
+			memcpy(&s->tables[s->n_tables], &table_spec, sizeof(struct table_spec));
+			s->n_tables++;
+			memset(&table_spec, 0, sizeof(struct table_spec));
 
 			continue;
 		}
 
 		/* selector block. */
 		if (block_mask & (1 << SELECTOR_BLOCK)) {
+			struct selector_spec *new_selectors;
+
 			status = selector_block_parse(&selector_spec,
 						      &block_mask,
 						      tokens,
@@ -2317,24 +2329,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_selector_config(p,
-				selector_spec.name,
-				&selector_spec.params);
-			if (status) {
+			new_selectors = realloc(s->selectors,
+				(s->n_selectors + 1) * sizeof(struct selector_spec));
+			if (!new_selectors) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Selector configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			selector_spec_free(&selector_spec);
+			s->selectors = new_selectors;
+			memcpy(&s->selectors[s->n_selectors],
+			       &selector_spec,
+			       sizeof(struct selector_spec));
+			s->n_selectors++;
+			memset(&selector_spec, 0, sizeof(struct selector_spec));
 
 			continue;
 		}
 
 		/* learner block. */
 		if (block_mask & (1 << LEARNER_BLOCK)) {
+			struct learner_spec *new_learners;
+
 			status = learner_block_parse(&learner_spec,
 						     &block_mask,
 						     tokens,
@@ -2349,27 +2368,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_learner_config(p,
-				learner_spec.name,
-				&learner_spec.params,
-				learner_spec.size,
-				learner_spec.timeout,
-				learner_spec.n_timeouts);
-			if (status) {
+			new_learners = realloc(s->learners,
+					       (s->n_learners + 1) * sizeof(struct learner_spec));
+			if (!new_learners) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Learner table configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			learner_spec_free(&learner_spec);
+			s->learners = new_learners;
+			memcpy(&s->learners[s->n_learners],
+			       &learner_spec,
+			       sizeof(struct learner_spec));
+			s->n_learners++;
+			memset(&learner_spec, 0, sizeof(struct learner_spec));
 
 			continue;
 		}
 
 		/* apply block. */
 		if (block_mask & (1 << APPLY_BLOCK)) {
+			struct apply_spec *new_apply;
+
 			status = apply_block_parse(&apply_spec,
 						   &block_mask,
 						   tokens,
@@ -2384,24 +2407,28 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_instructions_config(p,
-				apply_spec.instructions,
-				apply_spec.n_instructions);
-			if (status) {
+			new_apply = realloc(s->apply, (s->n_apply + 1) * sizeof(struct apply_spec));
+			if (!new_apply) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Pipeline instructions err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			apply_spec_free(&apply_spec);
+			s->apply = new_apply;
+			memcpy(&s->apply[s->n_apply], &apply_spec, sizeof(struct apply_spec));
+			s->n_apply++;
+			memset(&apply_spec, 0, sizeof(struct apply_spec));
 
 			continue;
 		}
 
 		/* extobj. */
 		if (!strcmp(tokens[0], "extobj")) {
+			struct extobj_spec *new_extobjs;
+
 			status = extobj_statement_parse(&extobj_spec,
 							tokens,
 							n_tokens,
@@ -2411,19 +2438,21 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_extern_object_config(p,
-				extobj_spec.name,
-				extobj_spec.extern_type_name,
-				extobj_spec.pragma);
-			if (status) {
+			new_extobjs = realloc(s->extobjs,
+					      (s->n_extobjs + 1) * sizeof(struct extobj_spec));
+			if (!new_extobjs) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Extern object config err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			extobj_spec_free(&extobj_spec);
+			s->extobjs = new_extobjs;
+			memcpy(&s->extobjs[s->n_extobjs], &extobj_spec, sizeof(struct extobj_spec));
+			s->n_extobjs++;
+			memset(&extobj_spec, 0, sizeof(struct extobj_spec));
 
 			continue;
 		}
@@ -2445,6 +2474,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* header. */
 		if (!strcmp(tokens[0], "header")) {
+			struct header_spec *new_headers;
+
 			status = header_statement_parse(&header_spec,
 							tokens,
 							n_tokens,
@@ -2454,24 +2485,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_packet_header_register(p,
-				header_spec.name,
-				header_spec.struct_type_name);
-			if (status) {
+			new_headers = realloc(s->headers,
+					      (s->n_headers + 1) * sizeof(struct header_spec));
+			if (!new_headers) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Header registration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			header_spec_free(&header_spec);
+			s->headers = new_headers;
+			memcpy(&s->headers[s->n_headers], &header_spec, sizeof(struct header_spec));
+			s->n_headers++;
+			memset(&header_spec, 0, sizeof(struct header_spec));
 
 			continue;
 		}
 
 		/* metadata. */
 		if (!strcmp(tokens[0], "metadata")) {
+			struct metadata_spec *new_metadata;
+
 			status = metadata_statement_parse(&metadata_spec,
 							  tokens,
 							  n_tokens,
@@ -2481,17 +2517,23 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_packet_metadata_register(p,
-				metadata_spec.struct_type_name);
-			if (status) {
+			new_metadata = realloc(s->metadata,
+					       (s->n_metadata + 1) * sizeof(struct metadata_spec));
+			if (!new_metadata) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Meta-data reg err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			metadata_spec_free(&metadata_spec);
+			s->metadata = new_metadata;
+			memcpy(&s->metadata[s->n_metadata],
+			       &metadata_spec,
+			       sizeof(struct metadata_spec));
+			s->n_metadata++;
+			memset(&metadata_spec, 0, sizeof(struct metadata_spec));
 
 			continue;
 		}
@@ -2558,6 +2600,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* regarray. */
 		if (!strcmp(tokens[0], "regarray")) {
+			struct regarray_spec *new_regarrays;
+
 			status = regarray_statement_parse(&regarray_spec,
 							  tokens,
 							  n_tokens,
@@ -2567,25 +2611,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_regarray_config(p,
-				regarray_spec.name,
-				regarray_spec.size,
-				regarray_spec.init_val);
-			if (status) {
+			new_regarrays = realloc(s->regarrays,
+				(s->n_regarrays + 1) * sizeof(struct regarray_spec));
+			if (!new_regarrays) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Register array configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			regarray_spec_free(&regarray_spec);
+			s->regarrays = new_regarrays;
+			memcpy(&s->regarrays[s->n_regarrays],
+			       &regarray_spec,
+			       sizeof(struct regarray_spec));
+			s->n_regarrays++;
+			memset(&regarray_spec, 0, sizeof(struct regarray_spec));
 
 			continue;
 		}
 
 		/* metarray. */
 		if (!strcmp(tokens[0], "metarray")) {
+			struct metarray_spec *new_metarrays;
+
 			status = metarray_statement_parse(&metarray_spec,
 							  tokens,
 							  n_tokens,
@@ -2595,18 +2645,23 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_metarray_config(p,
-				metarray_spec.name,
-				metarray_spec.size);
-			if (status) {
+			new_metarrays = realloc(s->metarrays,
+				(s->n_metarrays + 1) * sizeof(struct metarray_spec));
+			if (!new_metarrays) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Meter array configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			metarray_spec_free(&metarray_spec);
+			s->metarrays = new_metarrays;
+			memcpy(&s->metarrays[s->n_metarrays],
+			       &metarray_spec,
+			       sizeof(struct metarray_spec));
+			s->n_metarrays++;
+			memset(&metarray_spec, 0, sizeof(struct metarray_spec));
 
 			continue;
 		}
@@ -2644,17 +2699,7 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 		goto error;
 	}
 
-	/* Pipeline build. */
-	status = rte_swx_pipeline_build(p);
-	if (status) {
-		if (err_line)
-			*err_line = n_lines;
-		if (err_msg)
-			*err_msg = "Pipeline build error.";
-		goto error;
-	}
-
-	return 0;
+	return s;
 
 error:
 	extobj_spec_free(&extobj_spec);
@@ -2668,5 +2713,234 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 	regarray_spec_free(&regarray_spec);
 	metarray_spec_free(&metarray_spec);
 	apply_spec_free(&apply_spec);
+	pipeline_spec_free(s);
+
+	return NULL;
+}
+
+int
+pipeline_spec_configure(struct rte_swx_pipeline *p,
+			struct pipeline_spec *s,
+			const char **err_msg)
+{
+	uint32_t i;
+	int status = 0;
+
+	/* extobj. */
+	for (i = 0; i < s->n_extobjs; i++) {
+		struct extobj_spec *extobj_spec = &s->extobjs[i];
+
+		status = rte_swx_pipeline_extern_object_config(p,
+			extobj_spec->name,
+			extobj_spec->extern_type_name,
+			extobj_spec->pragma);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Extern object configuration error.";
+			return status;
+		}
+	}
+
+	/* regarray. */
+	for (i = 0; i < s->n_regarrays; i++) {
+		struct regarray_spec *regarray_spec = &s->regarrays[i];
+
+		status = rte_swx_pipeline_regarray_config(p,
+			regarray_spec->name,
+			regarray_spec->size,
+			regarray_spec->init_val);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Register array configuration error.";
+			return status;
+		}
+	}
+
+	/* metarray. */
+	for (i = 0; i < s->n_metarrays; i++) {
+		struct metarray_spec *metarray_spec = &s->metarrays[i];
+
+		status = rte_swx_pipeline_metarray_config(p,
+			metarray_spec->name,
+			metarray_spec->size);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Meter array configuration error.";
+			return status;
+		}
+	}
+
+	/* struct. */
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+
+		status = rte_swx_pipeline_struct_type_register(p,
+			struct_spec->name,
+			struct_spec->fields,
+			struct_spec->n_fields,
+			struct_spec->varbit);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Struct type registration error.";
+			return status;
+		}
+	}
+
+	/* header. */
+	for (i = 0; i < s->n_headers; i++) {
+		struct header_spec *header_spec = &s->headers[i];
+
+		status = rte_swx_pipeline_packet_header_register(p,
+			header_spec->name,
+			header_spec->struct_type_name);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Header configuration error.";
+			return status;
+		}
+	}
+
+	/* metadata. */
+	for (i = 0; i < s->n_metadata; i++) {
+		struct metadata_spec *metadata_spec = &s->metadata[i];
+
+		status = rte_swx_pipeline_packet_metadata_register(p,
+			metadata_spec->struct_type_name);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Meta-data registration error.";
+			return status;
+		}
+	}
+
+	/* action. */
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+
+		status = rte_swx_pipeline_action_config(p,
+			action_spec->name,
+			action_spec->args_struct_type_name,
+			action_spec->instructions,
+			action_spec->n_instructions);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Action configuration error.";
+			return status;
+		}
+	}
+
+	/* table. */
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+
+		status = rte_swx_pipeline_table_config(p,
+			table_spec->name,
+			&table_spec->params,
+			table_spec->recommended_table_type_name,
+			table_spec->args,
+			table_spec->size);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Table configuration error.";
+			return status;
+		}
+	}
+
+	/* selector. */
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+
+		status = rte_swx_pipeline_selector_config(p,
+			selector_spec->name,
+			&selector_spec->params);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Selector table configuration error.";
+			return status;
+		}
+	}
+
+	/* learner. */
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+
+		status = rte_swx_pipeline_learner_config(p,
+			learner_spec->name,
+			&learner_spec->params,
+			learner_spec->size,
+			learner_spec->timeout,
+			learner_spec->n_timeouts);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Learner table configuration error.";
+			return status;
+		}
+	}
+
+	/* apply. */
+	for (i = 0; i < s->n_apply; i++) {
+		struct apply_spec *apply_spec = &s->apply[i];
+
+		status = rte_swx_pipeline_instructions_config(p,
+			apply_spec->instructions,
+			apply_spec->n_instructions);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Pipeline instructions configuration error.";
+			return status;
+		}
+	}
+
+	return 0;
+}
+
+int
+rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
+				 FILE *spec_file,
+				 uint32_t *err_line,
+				 const char **err_msg)
+{
+	struct pipeline_spec *s = NULL;
+	int status = 0;
+
+	/* Check the input arguments. */
+	if (!p || !spec_file) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Invalid input argument.";
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Spec file parse. */
+	s = pipeline_spec_parse(spec_file, err_line, err_msg);
+	if (!s) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Pipeline configure. */
+	status = pipeline_spec_configure(p, s, err_msg);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		goto error;
+	}
+
+	/* Pipeline build. */
+	status = rte_swx_pipeline_build(p);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Pipeline build error.";
+		goto error;
+	}
+
+	return 0;
+
+error:
+	pipeline_spec_free(s);
 	return status;
 }
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index e1170a33b1..4f3a0b5958 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -206,3 +206,12 @@ struct pipeline_spec {
 
 void
 pipeline_spec_free(struct pipeline_spec *s);
+struct pipeline_spec *
+pipeline_spec_parse(FILE *spec,
+		    uint32_t *err_line,
+		    const char **err_msg);
+
+int
+pipeline_spec_configure(struct rte_swx_pipeline *p,
+			struct pipeline_spec *s,
+			const char **err_msg);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V2 4/9] pipeline: generate the code for pipeline specification structure
  2022-07-18 13:25 ` [PATCH V2 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
  2022-07-18 13:25   ` [PATCH V2 2/9] pipeline: add pipeline specification data structure Cristian Dumitrescu
  2022-07-18 13:25   ` [PATCH V2 3/9] pipeline: rework the specification file-based pipeline build Cristian Dumitrescu
@ 2022-07-18 13:25   ` Cristian Dumitrescu
  2022-07-18 13:25   ` [PATCH V2 5/9] pipeline: add API for pipeline code generation Cristian Dumitrescu
                     ` (5 subsequent siblings)
  8 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:25 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add support to export the pipeline specification data structure to a C
source code file.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 622 +++++++++++++++++++++++++++
 lib/pipeline/rte_swx_pipeline_spec.h |   5 +
 2 files changed, 627 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 62929a9da6..bf21fe17ba 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2020 Intel Corporation
  */
 #include <stdint.h>
+#include <inttypes.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -2103,6 +2104,627 @@ pipeline_spec_free(struct pipeline_spec *s)
 	memset(s, 0, sizeof(struct pipeline_spec));
 }
 
+static const char *
+match_type_string_get(enum rte_swx_table_match_type match_type)
+{
+	switch (match_type) {
+	case RTE_SWX_TABLE_MATCH_WILDCARD: return "RTE_SWX_TABLE_MATCH_WILDCARD";
+	case RTE_SWX_TABLE_MATCH_LPM: return "RTE_SWX_TABLE_MATCH_LPM";
+	case RTE_SWX_TABLE_MATCH_EXACT: return "RTE_SWX_TABLE_MATCH_EXACT";
+	default: return "RTE_SWX_TABLE_MATCH_UNKNOWN";
+	}
+}
+
+void
+pipeline_spec_codegen(FILE *f,
+		      struct pipeline_spec *s)
+{
+	uint32_t i;
+
+	/* Check the input arguments. */
+	if (!f || !s)
+		return;
+
+	/* extobj. */
+	fprintf(f, "static struct extobj_spec extobjs[] = {\n");
+
+	for (i = 0; i < s->n_extobjs; i++) {
+		struct extobj_spec *extobj_spec = &s->extobjs[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", extobj_spec->name);
+		fprintf(f, "\t\t.extern_type_name = \"%s\",\n", extobj_spec->extern_type_name);
+		if (extobj_spec->pragma)
+			fprintf(f, "\t\t.pragma = \"%s\",\n", extobj_spec->pragma);
+		else
+			fprintf(f, "\t\t.pragma = NULL,\n");
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* regarray. */
+	fprintf(f, "static struct regarray_spec regarrays[] = {\n");
+
+	for (i = 0; i < s->n_regarrays; i++) {
+		struct regarray_spec *regarray_spec = &s->regarrays[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", regarray_spec->name);
+		fprintf(f, "\t\t.init_val = %" PRIu64 ",\n", regarray_spec->init_val);
+		fprintf(f, "\t\t.size = %u,\n", regarray_spec->size);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* metarray. */
+	fprintf(f, "static struct metarray_spec metarrays[] = {\n");
+
+	for (i = 0; i < s->n_metarrays; i++) {
+		struct metarray_spec *metarray_spec = &s->metarrays[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", metarray_spec->name);
+		fprintf(f, "\t\t.size = %u,\n", metarray_spec->size);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* struct. */
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+		uint32_t j;
+
+		fprintf(f, "static struct rte_swx_field_params struct_%s_fields[] = {\n",
+			struct_spec->name);
+
+		for (j = 0; j < struct_spec->n_fields; j++) {
+			struct rte_swx_field_params *field = &struct_spec->fields[j];
+
+			fprintf(f, "\t[%d] = {\n", j);
+			fprintf(f, "\t\t.name = \"%s\",\n", field->name);
+			fprintf(f, "\t\t.n_bits = %u,\n", field->n_bits);
+			fprintf(f, "\t},\n");
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct struct_spec structs[] = {\n");
+
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", struct_spec->name);
+		fprintf(f, "\t\t.fields = struct_%s_fields,\n", struct_spec->name);
+		fprintf(f, "\t\t.n_fields = "
+			"sizeof(struct_%s_fields) / sizeof(struct_%s_fields[0]),\n",
+			struct_spec->name,
+			struct_spec->name);
+		fprintf(f, "\t\t.varbit = %d,\n", struct_spec->varbit);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* header. */
+	fprintf(f, "static struct header_spec headers[] = {\n");
+
+	for (i = 0; i < s->n_headers; i++) {
+		struct header_spec *header_spec = &s->headers[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", header_spec->name);
+		fprintf(f, "\t\t.struct_type_name = \"%s\",\n", header_spec->struct_type_name);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* metadata. */
+	fprintf(f, "static struct metadata_spec metadata[] = {\n");
+
+	for (i = 0; i < s->n_metadata; i++) {
+		struct metadata_spec *metadata_spec = &s->metadata[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.struct_type_name = \"%s\",\n", metadata_spec->struct_type_name);
+		fprintf(f, "\t},\n");
+
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* action. */
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+		uint32_t j;
+
+		fprintf(f, "static const char *action_%s_initial_instructions[] = {\n",
+			action_spec->name);
+
+		for (j = 0; j < action_spec->n_instructions; j++) {
+			const char *instr = action_spec->instructions[j];
+
+			fprintf(f, "\t[%d] = \"%s\",\n", j, instr);
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct action_spec actions[] = {\n");
+
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", action_spec->name);
+
+		if (action_spec->args_struct_type_name)
+			fprintf(f, "\t\t.args_struct_type_name = \"%s\",\n",
+				action_spec->args_struct_type_name);
+		else
+			fprintf(f, "\t\t.args_struct_type_name = NULL,\n");
+
+		fprintf(f, "\t\t.instructions = action_%s_initial_instructions,\n",
+			action_spec->name);
+		fprintf(f, "\t\t.n_instructions = "
+			"sizeof(action_%s_initial_instructions) / "
+			"sizeof(action_%s_initial_instructions[0]),\n",
+			action_spec->name,
+			action_spec->name);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* table. */
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+		uint32_t j;
+
+		/* fields. */
+		if (table_spec->params.fields && table_spec->params.n_fields) {
+			fprintf(f, "static struct rte_swx_match_field_params "
+				"table_%s_fields[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_fields; j++) {
+				struct rte_swx_match_field_params *field =
+					&table_spec->params.fields[j];
+
+				fprintf(f, "\t[%d] = {\n", j);
+				fprintf(f, "\t\t.name = \"%s\",\n", field->name);
+				fprintf(f, "\t\t.match_type = %s,\n",
+					match_type_string_get(field->match_type));
+				fprintf(f, "\t},\n");
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_names. */
+		if (table_spec->params.action_names && table_spec->params.n_actions) {
+			fprintf(f, "static const char *table_%s_action_names[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				const char *action_name = table_spec->params.action_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, action_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_table_entries. */
+		if (table_spec->params.action_is_for_table_entries &&
+		    table_spec->params.n_actions) {
+			fprintf(f, "static int table_%s_action_is_for_table_entries[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				int value = table_spec->params.action_is_for_table_entries[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_default_entry. */
+		if (table_spec->params.action_is_for_default_entry &&
+		    table_spec->params.n_actions) {
+			fprintf(f, "static int table_%s_action_is_for_default_entry[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				int value = table_spec->params.action_is_for_default_entry[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct table_spec tables[] = {\n");
+
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", table_spec->name);
+
+		fprintf(f, "\t\t.params = {\n");
+
+		if (table_spec->params.fields && table_spec->params.n_fields) {
+			fprintf(f, "\t\t\t.fields = table_%s_fields,\n", table_spec->name);
+			fprintf(f, "\t\t\t.n_fields = "
+				"sizeof(table_%s_fields) / sizeof(table_%s_fields[0]),\n",
+				table_spec->name,
+				table_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.fields = NULL,\n");
+			fprintf(f, "\t\t\t.n_fields = 0,\n");
+		}
+
+		if (table_spec->params.action_names && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_names = table_%s_action_names,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_names = NULL,\n");
+
+		if (table_spec->params.action_is_for_table_entries && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_table_entries = "
+				"table_%s_action_is_for_table_entries,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_table_entries = NULL,\n");
+
+		if (table_spec->params.action_is_for_default_entry && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_default_entry = "
+				"table_%s_action_is_for_default_entry,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_default_entry = NULL,\n");
+
+		if (table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.n_actions = sizeof(table_%s_action_names) / "
+				"sizeof(table_%s_action_names[0]),\n",
+				table_spec->name,
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.n_actions = 0,\n");
+
+		if (table_spec->params.default_action_name)
+			fprintf(f, "\t\t\t.default_action_name = \"%s\",\n",
+				table_spec->params.default_action_name);
+		else
+			fprintf(f, "\t\t\t.default_action_name = NULL,\n");
+
+		if (table_spec->params.default_action_args)
+			fprintf(f, "\t\t\t.default_action_args = \"%s\",\n",
+				table_spec->params.default_action_args);
+		else
+			fprintf(f, "\t\t\t.default_action_args = NULL,\n");
+
+		fprintf(f, "\t\t\t.default_action_is_const = %d,\n",
+			table_spec->params.default_action_is_const);
+		fprintf(f, "\t\t},\n");
+
+		if (table_spec->recommended_table_type_name)
+			fprintf(f, "\t\t.recommended_table_type_name = \"%s\",\n",
+				table_spec->recommended_table_type_name);
+		else
+			fprintf(f, "\t\t.recommended_table_type_name = NULL,\n");
+
+		if (table_spec->args)
+			fprintf(f, "\t\t.args = \"%s\",\n", table_spec->args);
+		else
+			fprintf(f, "\t\t.args = NULL,\n");
+
+		fprintf(f, "\t\t.size = %u,\n", table_spec->size);
+
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* selector. */
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+		uint32_t j;
+
+		if (selector_spec->params.selector_field_names &&
+		    selector_spec->params.n_selector_fields) {
+			fprintf(f, "static const char *selector_%s_field_names[] = {\n",
+				selector_spec->name);
+
+			for (j = 0; j < selector_spec->params.n_selector_fields; j++) {
+				const char *field_name =
+					selector_spec->params.selector_field_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, field_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct selector_spec selectors[] = {\n");
+
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+
+		fprintf(f, "\t\t.name = \"%s\",\n", selector_spec->name);
+		fprintf(f, "\t\t.params = {\n");
+
+		if (selector_spec->params.group_id_field_name)
+			fprintf(f, "\t\t\t.group_id_field_name = \"%s\",\n",
+				selector_spec->params.group_id_field_name);
+		else
+			fprintf(f, "\t\t\t.group_id_field_name = NULL,\n");
+
+		if (selector_spec->params.selector_field_names &&
+		    selector_spec->params.n_selector_fields) {
+			fprintf(f, "\t\t\t.selector_field_names = selector_%s_field_names,\n",
+				selector_spec->name);
+			fprintf(f, "\t\t\t.n_selector_fields = "
+				"sizeof(selector_%s_field_names) / sizeof(selector_%s_field_names[0]),\n",
+				selector_spec->name,
+				selector_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.selector_field_names = NULL,\n");
+			fprintf(f, "\t\t\t.n_selector_fields = 0,\n");
+		}
+
+		if (selector_spec->params.member_id_field_name)
+			fprintf(f, "\t\t\t.member_id_field_name = \"%s\",\n",
+				selector_spec->params.member_id_field_name);
+		else
+			fprintf(f, "\t\t\t.member_id_field_name = NULL,\n");
+
+		fprintf(f, "\t\t\t.n_groups_max = %u,\n", selector_spec->params.n_groups_max);
+
+		fprintf(f, "\t\t\t.n_members_per_group_max = %u,\n",
+			selector_spec->params.n_members_per_group_max);
+
+		fprintf(f, "\t\t},\n");
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* learner. */
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+		uint32_t j;
+
+		/* field_names. */
+		if (learner_spec->params.field_names && learner_spec->params.n_fields) {
+			fprintf(f, "static const char *learner_%s_field_names[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_fields; j++) {
+				const char *field_name = learner_spec->params.field_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, field_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_names. */
+		if (learner_spec->params.action_names && learner_spec->params.n_actions) {
+			fprintf(f, "static const char *learner_%s_action_names[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				const char *action_name = learner_spec->params.action_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, action_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_table_entries. */
+		if (learner_spec->params.action_is_for_table_entries &&
+		    learner_spec->params.n_actions) {
+			fprintf(f, "static int learner_%s_action_is_for_table_entries[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				int value = learner_spec->params.action_is_for_table_entries[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_default_entry. */
+		if (learner_spec->params.action_is_for_default_entry &&
+		    learner_spec->params.n_actions) {
+			fprintf(f, "static int learner_%s_action_is_for_default_entry[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				int value = learner_spec->params.action_is_for_default_entry[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* timeout. */
+		if (learner_spec->timeout && learner_spec->n_timeouts) {
+			fprintf(f, "static uint32_t learner_%s_timeout[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->n_timeouts; j++) {
+				uint32_t value = learner_spec->timeout[j];
+
+				fprintf(f, "\t[%d] = %u,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct learner_spec learners[] = {\n");
+
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", learner_spec->name);
+
+		fprintf(f, "\t\t.params = {\n");
+
+		if (learner_spec->params.field_names && learner_spec->params.n_fields) {
+			fprintf(f, "\t\t\t.field_names = learner_%s_field_names,\n",
+				learner_spec->name);
+			fprintf(f, "\t\t\t.n_fields = "
+				"sizeof(learner_%s_field_names) / "
+				"sizeof(learner_%s_field_names[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.field_names = NULL,\n");
+			fprintf(f, "\t\t\t.n_fields = 0,\n");
+		}
+
+		if (learner_spec->params.action_names && learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_names = learner_%s_action_names,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_names = NULL,\n");
+
+		if (learner_spec->params.action_is_for_table_entries &&
+		    learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_table_entries = "
+				"learner_%s_action_is_for_table_entries,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_table_entries = NULL,\n");
+
+		if (learner_spec->params.action_is_for_default_entry &&
+		    learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_default_entry = "
+				"learner_%s_action_is_for_default_entry,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_default_entry = NULL,\n");
+
+		if (learner_spec->params.action_names && learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.n_actions = "
+				"sizeof(learner_%s_action_names) / sizeof(learner_%s_action_names[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.n_actions = NULL,\n");
+
+		if (learner_spec->params.default_action_name)
+			fprintf(f, "\t\t\t.default_action_name = \"%s\",\n",
+				learner_spec->params.default_action_name);
+		else
+			fprintf(f, "\t\t\t.default_action_name = NULL,\n");
+
+		if (learner_spec->params.default_action_args)
+			fprintf(f, "\t\t\t.default_action_args = \"%s\",\n",
+				learner_spec->params.default_action_args);
+		else
+			fprintf(f, "\t\t\t.default_action_args = NULL,\n");
+
+		fprintf(f, "\t\t\t.default_action_is_const = %d,\n",
+			learner_spec->params.default_action_is_const);
+
+		fprintf(f, "\t\t},\n");
+
+		fprintf(f, "\t\t.size = %u,\n", learner_spec->size);
+
+		if (learner_spec->timeout && learner_spec->n_timeouts) {
+			fprintf(f, "\t\t.timeout = learner_%s_timeout,\n", learner_spec->name);
+			fprintf(f, "\t\t\t.n_timeouts = "
+				"sizeof(learner_%s_timeout) / sizeof(learner_%s_timeout[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		} else {
+			fprintf(f, "\t\t.timeout = NULL,\n");
+			fprintf(f, "\t\t\t.n_timeouts = 0,\n");
+		}
+
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* apply. */
+	for (i = 0; i < s->n_apply; i++) {
+		struct apply_spec *apply_spec = &s->apply[i];
+		uint32_t j;
+
+		fprintf(f, "static const char *apply%u_initial_instructions[] = {\n", i);
+
+		for (j = 0; j < apply_spec->n_instructions; j++) {
+			const char *instr = apply_spec->instructions[j];
+
+			fprintf(f, "\t[%d] = \"%s\",\n", j, instr);
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct apply_spec apply[] = {\n");
+
+	for (i = 0; i < s->n_apply; i++) {
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t.instructions = apply%u_initial_instructions,\n", i);
+		fprintf(f, "\t.n_instructions = "
+			"sizeof(apply%u_initial_instructions) / "
+			"sizeof(apply%u_initial_instructions[0]),\n",
+			i,
+			i);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* pipeline. */
+	fprintf(f, "struct pipeline_spec pipeline_spec = {\n");
+	fprintf(f, "\t.extobjs = extobjs,\n");
+	fprintf(f, "\t.structs = structs,\n");
+	fprintf(f, "\t.headers = headers,\n");
+	fprintf(f, "\t.metadata = metadata,\n");
+	fprintf(f, "\t.actions = actions,\n");
+	fprintf(f, "\t.tables = tables,\n");
+	fprintf(f, "\t.selectors = selectors,\n");
+	fprintf(f, "\t.learners = learners,\n");
+	fprintf(f, "\t.regarrays = regarrays,\n");
+	fprintf(f, "\t.metarrays = metarrays,\n");
+	fprintf(f, "\t.apply = apply,\n");
+	fprintf(f, "\t.n_extobjs = sizeof(extobjs) / sizeof(extobjs[0]),\n");
+	fprintf(f, "\t.n_structs = sizeof(structs) / sizeof(structs[0]),\n");
+	fprintf(f, "\t.n_headers = sizeof(headers) / sizeof(headers[0]),\n");
+	fprintf(f, "\t.n_metadata = sizeof(metadata) / sizeof(metadata[0]),\n");
+	fprintf(f, "\t.n_actions = sizeof(actions) / sizeof(actions[0]),\n");
+	fprintf(f, "\t.n_tables = sizeof(tables) / sizeof(tables[0]),\n");
+	fprintf(f, "\t.n_selectors = sizeof(selectors) / sizeof(selectors[0]),\n");
+	fprintf(f, "\t.n_learners = sizeof(learners) / sizeof(learners[0]),\n");
+	fprintf(f, "\t.n_regarrays = sizeof(regarrays) / sizeof(regarrays[0]),\n");
+	fprintf(f, "\t.n_metarrays = sizeof(metarrays) / sizeof(metarrays[0]),\n");
+	fprintf(f, "\t.n_apply = sizeof(apply) / sizeof(apply[0]),\n");
+	fprintf(f, "};\n");
+}
+
 struct pipeline_spec *
 pipeline_spec_parse(FILE *spec,
 		    uint32_t *err_line,
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 4f3a0b5958..707b99ba09 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -206,6 +206,11 @@ struct pipeline_spec {
 
 void
 pipeline_spec_free(struct pipeline_spec *s);
+
+void
+pipeline_spec_codegen(FILE *f,
+		      struct pipeline_spec *s);
+
 struct pipeline_spec *
 pipeline_spec_parse(FILE *spec,
 		    uint32_t *err_line,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V2 5/9] pipeline: add API for pipeline code generation
  2022-07-18 13:25 ` [PATCH V2 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
                     ` (2 preceding siblings ...)
  2022-07-18 13:25   ` [PATCH V2 4/9] pipeline: generate the code for pipeline specification structure Cristian Dumitrescu
@ 2022-07-18 13:25   ` Cristian Dumitrescu
  2022-07-18 13:26   ` [PATCH V2 6/9] pipeline: add API for shared library-based pipeline build Cristian Dumitrescu
                     ` (4 subsequent siblings)
  8 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:25 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Previously, the C code generation for the pipeline was hidden under
the hood; now, we make this an explicit API operation. Besides the
functions for the pipeline actions and the pipeline instructions,
the generated C source code now includes the pipeline specification
structure required for the pipeline configuration operations.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline.c | 94 +++++++++++++++++++++++++++++++++
 lib/pipeline/rte_swx_pipeline.h | 25 +++++++++
 lib/pipeline/version.map        |  3 ++
 3 files changed, 122 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 3e1c6e9edb..52760111fd 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -18,6 +18,7 @@
 #include <rte_swx_table_wm.h>
 
 #include "rte_swx_pipeline_internal.h"
+#include "rte_swx_pipeline_spec.h"
 
 #define CHECK(condition, err_code)                                             \
 do {                                                                           \
@@ -13476,3 +13477,96 @@ pipeline_compile(struct rte_swx_pipeline *p)
 
 	return status;
 }
+
+int
+rte_swx_pipeline_codegen(FILE *spec_file,
+			 FILE *code_file,
+			 uint32_t *err_line,
+			 const char **err_msg)
+
+{
+	struct rte_swx_pipeline *p = NULL;
+	struct pipeline_spec *s = NULL;
+	struct instruction_group_list *igl = NULL;
+	struct action *a;
+	int status = 0;
+
+	/* Check input arguments. */
+	if (!spec_file || !code_file) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Invalid input argument.";
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Pipeline configuration. */
+	s = pipeline_spec_parse(spec_file, err_line, err_msg);
+	if (!s) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	status = rte_swx_pipeline_config(&p, 0);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Pipeline configuration error.";
+		goto free;
+	}
+
+	status = pipeline_spec_configure(p, s, err_msg);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		goto free;
+	}
+
+	/*
+	 * Pipeline code generation.
+	 */
+
+	/* Instruction Group List (IGL) computation: the pipeline configuration must be done first,
+	 * but there is no need for the pipeline build to be done as well.
+	 */
+	igl = instruction_group_list_create(p);
+	if (!igl) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		status = -ENOMEM;
+		goto free;
+	}
+
+	/* Header file inclusion. */
+	fprintf(code_file, "#include \"rte_swx_pipeline_internal.h\"\n");
+	fprintf(code_file, "#include \"rte_swx_pipeline_spec.h\"\n\n");
+
+	/* Code generation for the pipeline specification. */
+	pipeline_spec_codegen(code_file, s);
+	fprintf(code_file, "\n");
+
+	/* Code generation for the action instructions. */
+	TAILQ_FOREACH(a, &p->actions, node) {
+		fprintf(code_file, "/**\n * Action %s\n */\n\n", a->name);
+
+		action_data_codegen(a, code_file);
+		fprintf(code_file, "\n");
+
+		action_instr_codegen(a, code_file);
+		fprintf(code_file, "\n");
+	}
+
+	/* Code generation for the pipeline instructions. */
+	instruction_group_list_codegen(igl, p, code_file);
+
+free:
+	instruction_group_list_free(igl);
+	rte_swx_pipeline_free(p);
+	pipeline_spec_free(s);
+
+	return status;
+}
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index c41ca5cb15..2bd019b05f 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -941,6 +941,31 @@ __rte_experimental
 int
 rte_swx_pipeline_build(struct rte_swx_pipeline *p);
 
+/**
+ * Pipeline C code generate based on input specification file
+ *
+ * @param[in] spec_file
+ *   Pipeline specification file (.spec) provided as input.
+ * @param[in] code_file
+ *   Pipeline C language file (.c) to be generated.
+ * @param[out] err_line
+ *   In case of error and non-NULL, the line number within the *spec* file where
+ *   the error occurred. The first line number in the file is 1.
+ * @param[out] err_msg
+ *   In case of error and non-NULL, the error message.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Resource with the same name already exists.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_codegen(FILE *spec_file,
+			 FILE *code_file,
+			 uint32_t *err_line,
+			 const char **err_msg);
+
 /**
  * Pipeline build from specification file
  *
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 8312307a7a..51165d48cf 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -145,4 +145,7 @@ EXPERIMENTAL {
 	rte_swx_ctl_pipeline_learner_timeout_get;
 	rte_swx_ctl_pipeline_learner_timeout_set;
 	rte_swx_pipeline_hash_func_register;
+
+	#added in 22.11
+	rte_swx_pipeline_codegen;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V2 6/9] pipeline: add API for shared library-based pipeline build
  2022-07-18 13:25 ` [PATCH V2 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
                     ` (3 preceding siblings ...)
  2022-07-18 13:25   ` [PATCH V2 5/9] pipeline: add API for pipeline code generation Cristian Dumitrescu
@ 2022-07-18 13:26   ` Cristian Dumitrescu
  2022-07-18 13:26   ` [PATCH V2 7/9] examples/pipeline: add CLI command for pipeline code generation Cristian Dumitrescu
                     ` (3 subsequent siblings)
  8 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:26 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Previously, the pipeline build operation was done based on the
specification file (typically produced by the P4 compiler), then the C
code with optimized functions for the pipeline actions and
instructions was generated, built into a shared object library, loaded
and installed into the pipeline in a completely hardcoded and
non-customizable way.

Now, this process is split into three explicit stages:
i) code generation (specification file -> C file);
ii) code build (C file -> shared object library);
iii) code installation (library load into the pipeline).

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c              |  20 +-
 lib/pipeline/rte_swx_pipeline.c      | 289 +++++++++------------------
 lib/pipeline/rte_swx_pipeline.h      |  22 +-
 lib/pipeline/rte_swx_pipeline_spec.c |  51 -----
 lib/pipeline/version.map             |   2 +-
 5 files changed, 108 insertions(+), 276 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index ad553f19ab..1f75b5dc9d 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -984,7 +984,7 @@ cmd_pipeline_port_out(char **tokens,
 }
 
 static const char cmd_pipeline_build_help[] =
-"pipeline <pipeline_name> build <spec_file>\n";
+"pipeline <pipeline_name> build <lib_file>\n";
 
 static void
 cmd_pipeline_build(char **tokens,
@@ -994,9 +994,6 @@ cmd_pipeline_build(char **tokens,
 	void *obj)
 {
 	struct pipeline *p = NULL;
-	FILE *spec = NULL;
-	uint32_t err_line;
-	const char *err_msg;
 	int status;
 
 	if (n_tokens != 4) {
@@ -1010,20 +1007,9 @@ cmd_pipeline_build(char **tokens,
 		return;
 	}
 
-	spec = fopen(tokens[3], "r");
-	if (!spec) {
-		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
-		return;
-	}
-
-	status = rte_swx_pipeline_build_from_spec(p->p,
-		spec,
-		&err_line,
-		&err_msg);
-	fclose(spec);
+	status = rte_swx_pipeline_build_from_lib(p->p, tokens[3]);
 	if (status) {
-		snprintf(out, out_size, "Error %d at line %u: %s\n.",
-			status, err_line, err_msg);
+		snprintf(out, out_size, "Pipeline build error (%d).", status);
 		return;
 	}
 
diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 52760111fd..03414bfd1f 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -9807,9 +9807,6 @@ rte_swx_pipeline_instructions_config(struct rte_swx_pipeline *p,
 	return 0;
 }
 
-static int
-pipeline_compile(struct rte_swx_pipeline *p);
-
 int
 rte_swx_pipeline_build(struct rte_swx_pipeline *p)
 {
@@ -9899,8 +9896,6 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
 
 	p->build_done = 1;
 
-	pipeline_compile(p);
-
 	return 0;
 
 error:
@@ -13222,160 +13217,6 @@ instruction_group_list_custom_instructions_count(struct instruction_group_list *
 	return n_custom_instr;
 }
 
-static int
-pipeline_codegen(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
-{
-	struct action *a;
-	FILE *f = NULL;
-
-	/* Create the .c file. */
-	f = fopen("/tmp/pipeline.c", "w");
-	if (!f)
-		return -EIO;
-
-	/* Include the .h file. */
-	fprintf(f, "#include \"rte_swx_pipeline_internal.h\"\n");
-
-	/* Add the code for each action. */
-	TAILQ_FOREACH(a, &p->actions, node) {
-		fprintf(f, "/**\n * Action %s\n */\n\n", a->name);
-
-		action_data_codegen(a, f);
-
-		fprintf(f, "\n");
-
-		action_instr_codegen(a, f);
-
-		fprintf(f, "\n");
-	}
-
-	/* Add the pipeline code. */
-	instruction_group_list_codegen(igl, p, f);
-
-	/* Close the .c file. */
-	fclose(f);
-
-	return 0;
-}
-
-#ifndef RTE_SWX_PIPELINE_CMD_MAX_SIZE
-#define RTE_SWX_PIPELINE_CMD_MAX_SIZE 4096
-#endif
-
-static int
-pipeline_libload(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
-{
-	struct action *a;
-	struct instruction_group *g;
-	char *dir_in, *buffer = NULL;
-	const char *dir_out;
-	int status = 0;
-
-	/* Get the environment variables. */
-	dir_in = getenv("RTE_INSTALL_DIR");
-	if (!dir_in) {
-		status = -EINVAL;
-		goto free;
-	}
-
-	dir_out = "/tmp";
-
-	/* Memory allocation for the command buffer. */
-	buffer = malloc(RTE_SWX_PIPELINE_CMD_MAX_SIZE);
-	if (!buffer) {
-		status = -ENOMEM;
-		goto free;
-	}
-
-	snprintf(buffer,
-		 RTE_SWX_PIPELINE_CMD_MAX_SIZE,
-		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s/pipeline.o %s/pipeline.c "
-		 "-I %s/lib/pipeline "
-		 "-I %s/lib/eal/include "
-		 "-I %s/lib/eal/x86/include "
-		 "-I %s/lib/eal/include/generic "
-		 "-I %s/lib/meter "
-		 "-I %s/lib/port "
-		 "-I %s/lib/table "
-		 "-I %s/lib/pipeline "
-		 "-I %s/config "
-		 "-I %s/build "
-		 "-I %s/lib/eal/linux/include "
-		 ">%s/pipeline.log 2>&1 "
-		 "&& "
-		 "gcc -shared %s/pipeline.o -o %s/libpipeline.so "
-		 ">>%s/pipeline.log 2>&1",
-		 dir_out,
-		 dir_out,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_out,
-		 dir_out,
-		 dir_out,
-		 dir_out);
-
-	/* Build the shared object library. */
-	status = system(buffer);
-	if (status)
-		goto free;
-
-	/* Open library. */
-	snprintf(buffer,
-		 RTE_SWX_PIPELINE_CMD_MAX_SIZE,
-		 "%s/libpipeline.so",
-		 dir_out);
-
-	p->lib = dlopen(buffer, RTLD_LAZY);
-	if (!p->lib) {
-		status = -EIO;
-		goto free;
-	}
-
-	/* Get the action function symbols. */
-	TAILQ_FOREACH(a, &p->actions, node) {
-		snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "action_%s_run", a->name);
-
-		p->action_funcs[a->id] = dlsym(p->lib, buffer);
-		if (!p->action_funcs[a->id]) {
-			status = -EINVAL;
-			goto free;
-		}
-	}
-
-	/* Get the pipeline function symbols. */
-	TAILQ_FOREACH(g, igl, node) {
-		if (g->first_instr_id == g->last_instr_id)
-			continue;
-
-		snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "pipeline_func_%u", g->group_id);
-
-		g->func = dlsym(p->lib, buffer);
-		if (!g->func) {
-			status = -EINVAL;
-			goto free;
-		}
-	}
-
-free:
-	if (status && p->lib) {
-		dlclose(p->lib);
-		p->lib = NULL;
-	}
-
-	free(buffer);
-
-	return status;
-}
-
 static int
 pipeline_adjust_check(struct rte_swx_pipeline *p __rte_unused,
 		      struct instruction_group_list *igl)
@@ -13443,41 +13284,6 @@ pipeline_adjust(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
 	instr_jmp_resolve(p->instructions, p->instruction_data, p->n_instructions);
 }
 
-static int
-pipeline_compile(struct rte_swx_pipeline *p)
-{
-	struct instruction_group_list *igl = NULL;
-	int status = 0;
-
-	igl = instruction_group_list_create(p);
-	if (!igl) {
-		status = -ENOMEM;
-		goto free;
-	}
-
-	/* Code generation. */
-	status = pipeline_codegen(p, igl);
-	if (status)
-		goto free;
-
-	/* Build and load the shared object library. */
-	status = pipeline_libload(p, igl);
-	if (status)
-		goto free;
-
-	/* Adjust instructions. */
-	status = pipeline_adjust_check(p, igl);
-	if (status)
-		goto free;
-
-	pipeline_adjust(p, igl);
-
-free:
-	instruction_group_list_free(igl);
-
-	return status;
-}
-
 int
 rte_swx_pipeline_codegen(FILE *spec_file,
 			 FILE *code_file,
@@ -13570,3 +13376,98 @@ rte_swx_pipeline_codegen(FILE *spec_file,
 
 	return status;
 }
+
+int
+rte_swx_pipeline_build_from_lib(struct rte_swx_pipeline *p,
+				const char *lib_file_name)
+{
+	void *lib = NULL;
+	struct pipeline_spec *s = NULL;
+	struct instruction_group_list *igl = NULL;
+	struct action *a;
+	struct instruction_group *g;
+	int status = 0;
+
+	/* Check input arguments. */
+	if (!p || p->build_done || !lib_file_name || !lib_file_name[0]) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Open the library. */
+	lib = dlopen(lib_file_name, RTLD_LAZY);
+	if (!lib) {
+		status = -EIO;
+		goto free;
+	}
+
+	/* Get the pipeline specification structure from the library. */
+	s = dlsym(lib, "pipeline_spec");
+	if (!s) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Pipeline configuration based on the specification structure. */
+	status = pipeline_spec_configure(p, s, NULL);
+	if (status)
+		goto free;
+
+	/* Pipeline build. */
+	status = rte_swx_pipeline_build(p);
+	if (status)
+		goto free;
+
+	/* Action instructions. */
+	TAILQ_FOREACH(a, &p->actions, node) {
+		char name[RTE_SWX_NAME_SIZE * 2];
+
+		snprintf(name, sizeof(name), "action_%s_run", a->name);
+
+		p->action_funcs[a->id] = dlsym(lib, name);
+		if (!p->action_funcs[a->id]) {
+			status = -EINVAL;
+			goto free;
+		}
+	}
+
+	/* Pipeline instructions. */
+	igl = instruction_group_list_create(p);
+	if (!igl) {
+		status = -ENOMEM;
+		goto free;
+	}
+
+	TAILQ_FOREACH(g, igl, node) {
+		char name[RTE_SWX_NAME_SIZE * 2];
+
+		if (g->first_instr_id == g->last_instr_id)
+			continue;
+
+		snprintf(name, sizeof(name), "pipeline_func_%u", g->group_id);
+
+		g->func = dlsym(lib, name);
+		if (!g->func) {
+			status = -EINVAL;
+			goto free;
+		}
+	}
+
+	status = pipeline_adjust_check(p, igl);
+	if (status)
+		goto free;
+
+	pipeline_adjust(p, igl);
+
+	p->lib = lib;
+
+free:
+	if (status && lib) {
+		dlclose(lib);
+		p->lib = NULL;
+	}
+
+	instruction_group_list_free(igl);
+
+	return status;
+}
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index 2bd019b05f..cb834cd64d 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -967,30 +967,26 @@ rte_swx_pipeline_codegen(FILE *spec_file,
 			 const char **err_msg);
 
 /**
- * Pipeline build from specification file
+ * Pipeline build from shared object library
+ *
+ * The shared object library must be built from the C language source code file
+ * previously generated by the rte_swx_pipeline_codegen() API function.
  *
  * @param[in] p
  *   Pipeline handle.
- * @param[in] spec
- *   Pipeline specification file.
- * @param[out] err_line
- *   In case of error and non-NULL, the line number within the *spec* file where
- *   the error occurred. The first line number in the file is 1.
- * @param[out] err_msg
- *   In case of error and non-NULL, the error message.
+ * @param[in] lib_file_name
+ *   Shared object library file name.
  * @return
  *   0 on success or the following error codes otherwise:
  *   -EINVAL: Invalid argument;
  *   -ENOMEM: Not enough space/cannot allocate memory;
- *   -EEXIST: Resource with the same name already exists;
+ *   -EEXIST: Pipeline was already built successfully;
  *   -ENODEV: Extern object or table creation error.
  */
 __rte_experimental
 int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec,
-				 uint32_t *err_line,
-				 const char **err_msg);
+rte_swx_pipeline_build_from_lib(struct rte_swx_pipeline *p,
+				const char *lib_file_name);
 
 /**
  * Pipeline run
diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index bf21fe17ba..d6650fcc80 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -3515,54 +3515,3 @@ pipeline_spec_configure(struct rte_swx_pipeline *p,
 
 	return 0;
 }
-
-int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec_file,
-				 uint32_t *err_line,
-				 const char **err_msg)
-{
-	struct pipeline_spec *s = NULL;
-	int status = 0;
-
-	/* Check the input arguments. */
-	if (!p || !spec_file) {
-		if (err_line)
-			*err_line = 0;
-		if (err_msg)
-			*err_msg = "Invalid input argument.";
-		status = -EINVAL;
-		goto error;
-	}
-
-	/* Spec file parse. */
-	s = pipeline_spec_parse(spec_file, err_line, err_msg);
-	if (!s) {
-		status = -EINVAL;
-		goto error;
-	}
-
-	/* Pipeline configure. */
-	status = pipeline_spec_configure(p, s, err_msg);
-	if (status) {
-		if (err_line)
-			*err_line = 0;
-		goto error;
-	}
-
-	/* Pipeline build. */
-	status = rte_swx_pipeline_build(p);
-	if (status) {
-		if (err_line)
-			*err_line = 0;
-		if (err_msg)
-			*err_msg = "Pipeline build error.";
-		goto error;
-	}
-
-	return 0;
-
-error:
-	pipeline_spec_free(s);
-	return status;
-}
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 51165d48cf..810cc56467 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -82,7 +82,6 @@ EXPERIMENTAL {
 	rte_swx_ctl_table_ops_get;
 	rte_swx_pipeline_action_config;
 	rte_swx_pipeline_build;
-	rte_swx_pipeline_build_from_spec;
 	rte_swx_pipeline_config;
 	rte_swx_pipeline_extern_func_register;
 	rte_swx_pipeline_extern_object_config;
@@ -148,4 +147,5 @@ EXPERIMENTAL {
 
 	#added in 22.11
 	rte_swx_pipeline_codegen;
+	rte_swx_pipeline_build_from_lib;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V2 7/9] examples/pipeline: add CLI command for pipeline code generation
  2022-07-18 13:25 ` [PATCH V2 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
                     ` (4 preceding siblings ...)
  2022-07-18 13:26   ` [PATCH V2 6/9] pipeline: add API for shared library-based pipeline build Cristian Dumitrescu
@ 2022-07-18 13:26   ` Cristian Dumitrescu
  2022-07-18 13:26   ` [PATCH V2 8/9] examples/pipeline: add CLI command for shared library build Cristian Dumitrescu
                     ` (2 subsequent siblings)
  8 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:26 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add CLI command for the pipeline code generation operation.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 61 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 1f75b5dc9d..fdaf5dd16b 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -983,6 +983,53 @@ cmd_pipeline_port_out(char **tokens,
 	}
 }
 
+static const char cmd_pipeline_codegen_help[] =
+"pipeline codegen <spec_file> <code_file>\n";
+
+static void
+cmd_pipeline_codegen(char **tokens,
+	uint32_t n_tokens,
+	char *out,
+	size_t out_size,
+	void *obj __rte_unused)
+{
+	FILE *spec_file = NULL;
+	FILE *code_file = NULL;
+	uint32_t err_line;
+	const char *err_msg;
+	int status;
+
+	if (n_tokens != 4) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	spec_file = fopen(tokens[2], "r");
+	if (!spec_file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", tokens[2]);
+		return;
+	}
+
+	code_file = fopen(tokens[3], "w");
+	if (!code_file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
+		return;
+	}
+
+	status = rte_swx_pipeline_codegen(spec_file,
+					  code_file,
+					  &err_line,
+					  &err_msg);
+
+	fclose(spec_file);
+	fclose(code_file);
+
+	if (status) {
+		snprintf(out, out_size, "Error %d at line %u: %s\n.",
+			status, err_line, err_msg);
+		return;
+	}
+}
 static const char cmd_pipeline_build_help[] =
 "pipeline <pipeline_name> build <lib_file>\n";
 
@@ -2962,6 +3009,7 @@ cmd_help(char **tokens,
 			"\tpipeline create\n"
 			"\tpipeline port in\n"
 			"\tpipeline port out\n"
+			"\tpipeline codegen\n"
 			"\tpipeline build\n"
 			"\tpipeline table add\n"
 			"\tpipeline table delete\n"
@@ -3031,6 +3079,12 @@ cmd_help(char **tokens,
 		}
 	}
 
+	if ((strcmp(tokens[0], "pipeline") == 0) &&
+		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
@@ -3309,6 +3363,13 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			return;
 		}
 
+		if ((n_tokens >= 3) &&
+			(strcmp(tokens[1], "codegen") == 0)) {
+			cmd_pipeline_codegen(tokens, n_tokens, out, out_size,
+				obj);
+			return;
+		}
+
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[2], "build") == 0)) {
 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V2 8/9] examples/pipeline: add CLI command for shared library build
  2022-07-18 13:25 ` [PATCH V2 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
                     ` (5 preceding siblings ...)
  2022-07-18 13:26   ` [PATCH V2 7/9] examples/pipeline: add CLI command for pipeline code generation Cristian Dumitrescu
@ 2022-07-18 13:26   ` Cristian Dumitrescu
  2022-07-18 13:26   ` [PATCH V2 9/9] examples/pipeline: call CLI commands for code generation and build Cristian Dumitrescu
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
  8 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:26 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add CLI command for the shared object library build operation.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 147 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 143 insertions(+), 4 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index fdaf5dd16b..f1d2fbf52d 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -6,6 +6,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
@@ -25,6 +26,10 @@
 #define CMD_MAX_TOKENS     256
 #endif
 
+#ifndef MAX_LINE_SIZE
+#define MAX_LINE_SIZE 2048
+#endif
+
 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
@@ -1030,6 +1035,130 @@ cmd_pipeline_codegen(char **tokens,
 		return;
 	}
 }
+
+static const char cmd_pipeline_libbuild_help[] =
+"pipeline libbuild <code_file> <lib_file>\n";
+
+static void
+cmd_pipeline_libbuild(char **tokens,
+	uint32_t n_tokens,
+	char *out,
+	size_t out_size,
+	void *obj __rte_unused)
+{
+	char *code_file, *lib_file, *obj_file = NULL, *log_file = NULL;
+	char *install_dir, *buffer = NULL;
+	size_t length;
+	int status = 0;
+
+	if (n_tokens != 4) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		goto free;
+	}
+
+	install_dir = getenv("RTE_INSTALL_DIR");
+	if (!install_dir) {
+		snprintf(out, out_size, "Error: Environment variable RTE_INSTALL_DIR is not set.");
+		return;
+	}
+
+	snprintf(out, out_size, "Using DPDK source code from \"%s\".\n", install_dir);
+	out_size -= strlen(out);
+	out += strlen(out);
+
+	code_file = tokens[2];
+	length = strnlen(code_file, MAX_LINE_SIZE);
+	if ((length < 3) ||
+	    (code_file[length - 2] != '.') ||
+	    (code_file[length - 1] != 'c')) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "code_file");
+		goto free;
+	}
+
+	lib_file = tokens[3];
+	length = strnlen(lib_file, MAX_LINE_SIZE);
+	if ((length < 4) ||
+	    (lib_file[length - 3] != '.') ||
+	    (lib_file[length - 2] != 's') ||
+	    (lib_file[length - 1] != 'o')) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "lib_file");
+		goto free;
+	}
+
+	obj_file = malloc(length);
+	log_file = malloc(length + 2);
+	if (!obj_file || !log_file) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		goto free;
+	}
+
+	memcpy(obj_file, lib_file, length - 2);
+	obj_file[length - 2] = 'o';
+	obj_file[length - 1] = 0;
+
+	memcpy(log_file, lib_file, length - 2);
+	log_file[length - 2] = 'l';
+	log_file[length - 1] = 'o';
+	log_file[length] = 'g';
+	log_file[length + 1] = 0;
+
+	buffer = malloc(MAX_LINE_SIZE);
+	if (!buffer) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		return;
+	}
+
+	snprintf(buffer,
+		 MAX_LINE_SIZE,
+		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s %s "
+		 "-I %s/lib/pipeline "
+		 "-I %s/lib/eal/include "
+		 "-I %s/lib/eal/x86/include "
+		 "-I %s/lib/eal/include/generic "
+		 "-I %s/lib/meter "
+		 "-I %s/lib/port "
+		 "-I %s/lib/table "
+		 "-I %s/lib/pipeline "
+		 "-I %s/config "
+		 "-I %s/build "
+		 "-I %s/lib/eal/linux/include "
+		 ">%s 2>&1 "
+		 "&& "
+		 "gcc -shared %s -o %s "
+		 ">>%s 2>&1",
+		 obj_file,
+		 code_file,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 log_file,
+		 obj_file,
+		 lib_file,
+		 log_file);
+
+	status = system(buffer);
+	if (status) {
+		snprintf(out,
+			 out_size,
+			 "Library build failed, see file \"%s\" for details.\n",
+			 log_file);
+		goto free;
+	}
+
+free:
+	free(obj_file);
+	free(log_file);
+	free(buffer);
+}
+
 static const char cmd_pipeline_build_help[] =
 "pipeline <pipeline_name> build <lib_file>\n";
 
@@ -1080,10 +1209,6 @@ table_entry_free(struct rte_swx_table_entry *entry)
 	free(entry);
 }
 
-#ifndef MAX_LINE_SIZE
-#define MAX_LINE_SIZE 2048
-#endif
-
 static int
 pipeline_table_entries_add(struct rte_swx_ctl_pipeline *p,
 			   const char *table_name,
@@ -3010,6 +3135,7 @@ cmd_help(char **tokens,
 			"\tpipeline port in\n"
 			"\tpipeline port out\n"
 			"\tpipeline codegen\n"
+			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
 			"\tpipeline table add\n"
 			"\tpipeline table delete\n"
@@ -3085,6 +3211,12 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if ((strcmp(tokens[0], "pipeline") == 0) &&
+		(n_tokens == 2) && (strcmp(tokens[1], "libbuild") == 0)) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_libbuild_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
@@ -3370,6 +3502,13 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			return;
 		}
 
+		if ((n_tokens >= 3) &&
+			(strcmp(tokens[1], "libbuild") == 0)) {
+			cmd_pipeline_libbuild(tokens, n_tokens, out, out_size,
+				obj);
+			return;
+		}
+
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[2], "build") == 0)) {
 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V2 9/9] examples/pipeline: call CLI commands for code generation and build
  2022-07-18 13:25 ` [PATCH V2 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
                     ` (6 preceding siblings ...)
  2022-07-18 13:26   ` [PATCH V2 8/9] examples/pipeline: add CLI command for shared library build Cristian Dumitrescu
@ 2022-07-18 13:26   ` Cristian Dumitrescu
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
  8 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-18 13:26 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Update the example CLI scripts with the commands for code generation
and shared object library build.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/examples/fib.cli               | 8 +++++++-
 examples/pipeline/examples/hash_func.cli         | 8 +++++++-
 examples/pipeline/examples/l2fwd.cli             | 8 +++++++-
 examples/pipeline/examples/l2fwd_macswp.cli      | 8 +++++++-
 examples/pipeline/examples/l2fwd_macswp_pcap.cli | 8 +++++++-
 examples/pipeline/examples/l2fwd_pcap.cli        | 8 +++++++-
 examples/pipeline/examples/learner.cli           | 8 +++++++-
 examples/pipeline/examples/meter.cli             | 8 +++++++-
 examples/pipeline/examples/mirroring.cli         | 8 +++++++-
 examples/pipeline/examples/recirculation.cli     | 8 +++++++-
 examples/pipeline/examples/registers.cli         | 8 +++++++-
 examples/pipeline/examples/selector.cli          | 8 +++++++-
 examples/pipeline/examples/varbit.cli            | 8 +++++++-
 examples/pipeline/examples/vxlan.cli             | 8 +++++++-
 examples/pipeline/examples/vxlan_pcap.cli        | 8 +++++++-
 15 files changed, 105 insertions(+), 15 deletions(-)

diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 93ab2b08f8..8b55175bf3 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/fib.spec /tmp/fib.c
+pipeline libbuild /tmp/fib.c /tmp/fib.so
+
 ;
 ; Customize the LINK parameters to match your setup.
 ;
@@ -26,7 +32,7 @@ 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/fib.spec
+pipeline PIPELINE0 build /tmp/fib.so
 
 ;
 ; Initial set of table entries.
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index d65cd62d17..f7bb28b28b 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2022 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/hash_func.spec /tmp/hash_func.c
+pipeline libbuild /tmp/hash_func.c /tmp/hash_func.so
+
 ;
 ; Customize the LINK parameters to match your setup.
 ;
@@ -26,7 +32,7 @@ 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
+pipeline PIPELINE0 build /tmp/hash_func.so
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index d89caf2d0a..a71727309b 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd.spec /tmp/l2fwd.c
+pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
+
 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
@@ -20,6 +26,6 @@ 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/l2fwd.spec
+pipeline PIPELINE0 build /tmp/l2fwd.so
 
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index 0f2a89ac5b..d8f5f9f735 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd_macswp.spec /tmp/l2fwd_macswp.c
+pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
+
 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
@@ -20,6 +26,6 @@ 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/l2fwd_macswp.spec
+pipeline PIPELINE0 build /tmp/l2fwd_macswp.so
 
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index e9656fe3c2..bd077876ff 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd_macswp.spec /tmp/l2fwd_macswp.c
+pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
+
 mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
 
 pipeline PIPELINE0 create 0
@@ -15,6 +21,6 @@ pipeline PIPELINE0 port out 1 sink none
 pipeline PIPELINE0 port out 2 sink none
 pipeline PIPELINE0 port out 3 sink none
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/l2fwd_macswp.spec
+pipeline PIPELINE0 build /tmp/l2fwd_macswp.so
 
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 23fcb199f1..2e56a116af 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd.spec /tmp/l2fwd.c
+pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
+
 mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
 
 pipeline PIPELINE0 create 0
@@ -15,6 +21,6 @@ pipeline PIPELINE0 port out 1 sink none
 pipeline PIPELINE0 port out 2 sink none
 pipeline PIPELINE0 port out 3 sink none
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/l2fwd.spec
+pipeline PIPELINE0 build /tmp/l2fwd.so
 
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 688ce34f34..10eb2af417 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/learner.spec /tmp/learner.c
+pipeline libbuild /tmp/learner.c /tmp/learner.so
+
 ;
 ; Customize the LINK parameters to match your setup.
 ;
@@ -26,7 +32,7 @@ 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/learner.spec
+pipeline PIPELINE0 build /tmp/learner.so
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index b29ed24022..9c22014f46 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -4,6 +4,12 @@
 ; Example command line:
 ;	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/meter.cli
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/meter.spec /tmp/meter.c
+pipeline libbuild /tmp/meter.c /tmp/meter.so
+
 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
@@ -23,7 +29,7 @@ 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/meter.spec
+pipeline PIPELINE0 build /tmp/meter.so
 
 pipeline PIPELINE0 meter profile platinum add cir 46000000 pir 138000000 cbs 1000000 pbs 1000000
 pipeline PIPELINE0 meter meters from 0 to 15 set profile platinum
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index 46d57db4ec..9614f64d38 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2022 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/mirroring.spec /tmp/mirroring.c
+pipeline libbuild /tmp/mirroring.c /tmp/mirroring.so
+
 ;
 ; Customize the LINK parameters to match your setup.
 ;
@@ -27,7 +33,7 @@ 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/mirroring.spec
+pipeline PIPELINE0 build /tmp/mirroring.so
 
 ;
 ; Packet mirroring sessions.
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index f855c5c327..bd114e91cd 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2022 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/recirculation.spec /tmp/recirculation.c
+pipeline libbuild /tmp/recirculation.c /tmp/recirculation.so
+
 ;
 ; Customize the LINK parameters to match your setup.
 ;
@@ -26,7 +32,7 @@ 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/recirculation.spec
+pipeline PIPELINE0 build /tmp/recirculation.so
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index 8d026294cb..3d9eeb0d5c 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -4,6 +4,12 @@
 ; Example command line:
 ;	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/registers.cli
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/registers.spec /tmp/registers.c
+pipeline libbuild /tmp/registers.c /tmp/registers.so
+
 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
@@ -23,6 +29,6 @@ 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/registers.spec
+pipeline PIPELINE0 build /tmp/registers.so
 
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index 123782c57b..6c7d032b10 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/selector.spec /tmp/selector.c
+pipeline libbuild /tmp/selector.c /tmp/selector.so
+
 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
@@ -20,7 +26,7 @@ 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/selector.spec
+pipeline PIPELINE0 build /tmp/selector.so
 
 pipeline PIPELINE0 selector s group add
 pipeline PIPELINE0 selector s group member add ./examples/pipeline/examples/selector.txt
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 9caeb9ca26..545cde262e 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/varbit.spec /tmp/varbit.c
+pipeline libbuild /tmp/varbit.c /tmp/varbit.so
+
 ;
 ; Customize the LINK parameters to match your setup.
 ;
@@ -26,7 +32,7 @@ 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/varbit.spec
+pipeline PIPELINE0 build /tmp/varbit.so
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 444f3f7bd8..321a28ba44 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/vxlan.spec /tmp/vxlan.c
+pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
+
 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
@@ -20,7 +26,7 @@ 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/vxlan.spec
+pipeline PIPELINE0 build /tmp/vxlan.so
 pipeline PIPELINE0 table vxlan_table add ./examples/pipeline/examples/vxlan_table.txt
 pipeline PIPELINE0 commit
 
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index 83fca8d0d9..596169f933 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -1,6 +1,12 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+;
+; Pipeline code generation & shared object library build
+;
+pipeline codegen ./examples/pipeline/examples/vxlan.spec /tmp/vxlan.c
+pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
+
 mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
 
 pipeline PIPELINE0 create 0
@@ -15,7 +21,7 @@ pipeline PIPELINE0 port out 1 sink none
 pipeline PIPELINE0 port out 2 sink none
 pipeline PIPELINE0 port out 3 sink none
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/vxlan.spec
+pipeline PIPELINE0 build /tmp/vxlan.so
 pipeline PIPELINE0 table vxlan_table add ./examples/pipeline/examples/vxlan_table.txt
 pipeline PIPELINE0 commit
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 01/17] pipeline: add pipeline name
  2022-07-18 13:25 ` [PATCH V2 1/9] pipeline: move specification data structures to internal header Cristian Dumitrescu
                     ` (7 preceding siblings ...)
  2022-07-18 13:26   ` [PATCH V2 9/9] examples/pipeline: call CLI commands for code generation and build Cristian Dumitrescu
@ 2022-07-27 22:36   ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 02/17] pipeline: move specification data structures to internal header Cristian Dumitrescu
                       ` (16 more replies)
  8 siblings, 17 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add an unique name to every pipeline. This enables the library to
maintain a list of the existing pipeline objects, which can be
queried by the application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/obj.c                  |   2 +-
 lib/pipeline/rte_swx_ctl.c               |  99 +++++++++++++++++++++
 lib/pipeline/rte_swx_ctl.h               |  15 ++++
 lib/pipeline/rte_swx_pipeline.c          | 107 ++++++++++++++++++++++-
 lib/pipeline/rte_swx_pipeline.h          |  18 +++-
 lib/pipeline/rte_swx_pipeline_internal.h |   2 +
 lib/pipeline/version.map                 |   4 +
 7 files changed, 244 insertions(+), 3 deletions(-)

diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index b79f044ac7..967342c580 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -533,7 +533,7 @@ pipeline_create(struct obj *obj, const char *name, int numa_node)
 		return NULL;
 
 	/* Resource create */
-	status = rte_swx_pipeline_config(&p, numa_node);
+	status = rte_swx_pipeline_config(&p, name, numa_node);
 	if (status)
 		goto error;
 
diff --git a/lib/pipeline/rte_swx_ctl.c b/lib/pipeline/rte_swx_ctl.c
index 710e89a46a..1b776fc543 100644
--- a/lib/pipeline/rte_swx_ctl.c
+++ b/lib/pipeline/rte_swx_ctl.c
@@ -9,6 +9,8 @@
 
 #include <rte_common.h>
 #include <rte_byteorder.h>
+#include <rte_tailq.h>
+#include <rte_eal_memconfig.h>
 
 #include <rte_swx_table_selector.h>
 
@@ -1157,12 +1159,103 @@ table_state_create(struct rte_swx_ctl_pipeline *ctl)
 	return status;
 }
 
+/* Global list of pipeline instances. */
+TAILQ_HEAD(rte_swx_ctl_pipeline_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_swx_ctl_pipeline_tailq = {
+	.name = "RTE_SWX_CTL_PIPELINE",
+};
+
+EAL_REGISTER_TAILQ(rte_swx_ctl_pipeline_tailq)
+
+struct rte_swx_ctl_pipeline *
+rte_swx_ctl_pipeline_find(const char *name)
+{
+	struct rte_swx_ctl_pipeline_list *ctl_list;
+	struct rte_tailq_entry *te = NULL;
+
+	if (!name || !name[0] || (strnlen(name, RTE_SWX_CTL_NAME_SIZE) >= RTE_SWX_CTL_NAME_SIZE))
+		return NULL;
+
+	ctl_list = RTE_TAILQ_CAST(rte_swx_ctl_pipeline_tailq.head, rte_swx_ctl_pipeline_list);
+
+	rte_mcfg_tailq_read_lock();
+
+	TAILQ_FOREACH(te, ctl_list, next) {
+		struct rte_swx_ctl_pipeline *ctl = (struct rte_swx_ctl_pipeline *)te->data;
+
+		if (!strncmp(name, ctl->info.name, sizeof(ctl->info.name))) {
+			rte_mcfg_tailq_read_unlock();
+			return ctl;
+		}
+	}
+
+	rte_mcfg_tailq_read_unlock();
+	return NULL;
+}
+
+static int
+ctl_register(struct rte_swx_ctl_pipeline *ctl)
+{
+	struct rte_swx_ctl_pipeline_list *ctl_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ctl_list = RTE_TAILQ_CAST(rte_swx_ctl_pipeline_tailq.head, rte_swx_ctl_pipeline_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ctl_list, next) {
+		struct rte_swx_ctl_pipeline *ctl_crt = (struct rte_swx_ctl_pipeline *)te->data;
+
+		if (!strncmp(ctl->info.name, ctl_crt->info.name, sizeof(ctl->info.name))) {
+			rte_mcfg_tailq_write_unlock();
+			return -EEXIST;
+		}
+	}
+
+	te = calloc(1, sizeof(struct rte_tailq_entry));
+	if (!te) {
+		rte_mcfg_tailq_write_unlock();
+		return -ENOMEM;
+	}
+
+	te->data = (void *)ctl;
+	TAILQ_INSERT_TAIL(ctl_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return 0;
+}
+
+static void
+ctl_unregister(struct rte_swx_ctl_pipeline *ctl)
+{
+	struct rte_swx_ctl_pipeline_list *ctl_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ctl_list = RTE_TAILQ_CAST(rte_swx_ctl_pipeline_tailq.head, rte_swx_ctl_pipeline_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ctl_list, next) {
+		if (te->data == (void *)ctl) {
+			TAILQ_REMOVE(ctl_list, te, next);
+			rte_mcfg_tailq_write_unlock();
+			free(te);
+			return;
+		}
+	}
+
+	rte_mcfg_tailq_write_unlock();
+}
+
 void
 rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline *ctl)
 {
 	if (!ctl)
 		return;
 
+	if (ctl->info.name[0])
+		ctl_unregister(ctl);
+
 	action_free(ctl);
 
 	table_state_free(ctl);
@@ -1441,6 +1534,12 @@ rte_swx_ctl_pipeline_create(struct rte_swx_pipeline *p)
 	if (status)
 		goto error;
 
+	if (ctl->info.name[0]) {
+		status = ctl_register(ctl);
+		if (status)
+			goto error;
+	}
+
 	return ctl;
 
 error:
diff --git a/lib/pipeline/rte_swx_ctl.h b/lib/pipeline/rte_swx_ctl.h
index d771389d26..63ee479e47 100644
--- a/lib/pipeline/rte_swx_ctl.h
+++ b/lib/pipeline/rte_swx_ctl.h
@@ -35,6 +35,9 @@ struct rte_swx_pipeline;
 
 /** Pipeline info. */
 struct rte_swx_ctl_pipeline_info {
+	/** Pipeline name. */
+	char name[RTE_SWX_CTL_NAME_SIZE];
+
 	/** Number of input ports. */
 	uint32_t n_ports_in;
 
@@ -812,6 +815,18 @@ rte_swx_pipeline_table_state_set(struct rte_swx_pipeline *p,
 /** Pipeline control opaque data structure. */
 struct rte_swx_ctl_pipeline;
 
+/**
+ * Pipeline control find
+ *
+ * @param[in] name
+ *   Pipeline name.
+ * @return
+ *   Valid pipeline control handle if found or NULL otherwise.
+ */
+__rte_experimental
+struct rte_swx_ctl_pipeline *
+rte_swx_ctl_pipeline_find(const char *name);
+
 /**
  * Pipeline control create
  *
diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 3e1c6e9edb..c8ccded4f8 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -6,6 +6,8 @@
 #include <errno.h>
 #include <dlfcn.h>
 
+#include <rte_tailq.h>
+#include <rte_eal_memconfig.h>
 #include <rte_jhash.h>
 #include <rte_hash_crc.h>
 
@@ -9578,6 +9580,95 @@ metarray_free(struct rte_swx_pipeline *p)
 /*
  * Pipeline.
  */
+
+/* Global list of pipeline instances. */
+TAILQ_HEAD(rte_swx_pipeline_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_swx_pipeline_tailq = {
+	.name = "RTE_SWX_PIPELINE",
+};
+
+EAL_REGISTER_TAILQ(rte_swx_pipeline_tailq)
+
+struct rte_swx_pipeline *
+rte_swx_pipeline_find(const char *name)
+{
+	struct rte_swx_pipeline_list *pipeline_list;
+	struct rte_tailq_entry *te = NULL;
+
+	if (!name || !name[0] || (strnlen(name, RTE_SWX_NAME_SIZE) >= RTE_SWX_NAME_SIZE))
+		return NULL;
+
+	pipeline_list = RTE_TAILQ_CAST(rte_swx_pipeline_tailq.head, rte_swx_pipeline_list);
+
+	rte_mcfg_tailq_read_lock();
+
+	TAILQ_FOREACH(te, pipeline_list, next) {
+		struct rte_swx_pipeline *p = (struct rte_swx_pipeline *)te->data;
+
+		if (!strncmp(name, p->name, sizeof(p->name))) {
+			rte_mcfg_tailq_read_unlock();
+			return p;
+		}
+	}
+
+	rte_mcfg_tailq_read_unlock();
+	return NULL;
+}
+
+static int
+pipeline_register(struct rte_swx_pipeline *p)
+{
+	struct rte_swx_pipeline_list *pipeline_list;
+	struct rte_tailq_entry *te = NULL;
+
+	pipeline_list = RTE_TAILQ_CAST(rte_swx_pipeline_tailq.head, rte_swx_pipeline_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, pipeline_list, next) {
+		struct rte_swx_pipeline *pipeline = (struct rte_swx_pipeline *)te->data;
+
+		if (!strncmp(p->name, pipeline->name, sizeof(p->name))) {
+			rte_mcfg_tailq_write_unlock();
+			return -EEXIST;
+		}
+	}
+
+	te = calloc(1, sizeof(struct rte_tailq_entry));
+	if (!te) {
+		rte_mcfg_tailq_write_unlock();
+		return -ENOMEM;
+	}
+
+	te->data = (void *)p;
+	TAILQ_INSERT_TAIL(pipeline_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return 0;
+}
+
+static void
+pipeline_unregister(struct rte_swx_pipeline *p)
+{
+	struct rte_swx_pipeline_list *pipeline_list;
+	struct rte_tailq_entry *te = NULL;
+
+	pipeline_list = RTE_TAILQ_CAST(rte_swx_pipeline_tailq.head, rte_swx_pipeline_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, pipeline_list, next) {
+		if (te->data == (void *)p) {
+			TAILQ_REMOVE(pipeline_list, te, next);
+			rte_mcfg_tailq_write_unlock();
+			free(te);
+			return;
+		}
+	}
+
+	rte_mcfg_tailq_write_unlock();
+}
+
 void
 rte_swx_pipeline_free(struct rte_swx_pipeline *p)
 {
@@ -9586,6 +9677,9 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)
 	if (!p)
 		return;
 
+	if (p->name[0])
+		pipeline_unregister(p);
+
 	lib = p->lib;
 
 	free(p->instruction_data);
@@ -9720,13 +9814,14 @@ hash_funcs_register(struct rte_swx_pipeline *p)
 }
 
 int
-rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
+rte_swx_pipeline_config(struct rte_swx_pipeline **p, const char *name, int numa_node)
 {
 	struct rte_swx_pipeline *pipeline = NULL;
 	int status = 0;
 
 	/* Check input parameters. */
 	CHECK(p, EINVAL);
+	CHECK(!name || (strnlen(name, RTE_SWX_NAME_SIZE) < RTE_SWX_NAME_SIZE), EINVAL);
 
 	/* Memory allocation. */
 	pipeline = calloc(1, sizeof(struct rte_swx_pipeline));
@@ -9736,6 +9831,9 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
 	}
 
 	/* Initialization. */
+	if (name)
+		strcpy(pipeline->name, name);
+
 	TAILQ_INIT(&pipeline->struct_types);
 	TAILQ_INIT(&pipeline->port_in_types);
 	TAILQ_INIT(&pipeline->ports_in);
@@ -9776,6 +9874,12 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
 	if (status)
 		goto error;
 
+	if (pipeline->name[0]) {
+		status = pipeline_register(pipeline);
+		if (status)
+			goto error;
+	}
+
 	*p = pipeline;
 	return 0;
 
@@ -9966,6 +10070,7 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p,
 	TAILQ_FOREACH(table, &p->tables, node)
 		n_tables++;
 
+	strcpy(pipeline->name, p->name);
 	pipeline->n_ports_in = p->n_ports_in;
 	pipeline->n_ports_out = p->n_ports_out;
 	pipeline->n_mirroring_slots = p->n_mirroring_slots;
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index c41ca5cb15..ef50a0fa70 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -44,22 +44,38 @@ extern "C" {
 /** Pipeline opaque data structure. */
 struct rte_swx_pipeline;
 
+/**
+ * Pipeline find
+ *
+ * @param[in] name
+ *   Pipeline name.
+ * @return
+ *   Valid pipeline handle if found or NULL otherwise.
+ */
+__rte_experimental
+struct rte_swx_pipeline *
+rte_swx_pipeline_find(const char *name);
+
 /**
  * Pipeline configure
  *
  * @param[out] p
  *   Pipeline handle. Must point to valid memory. Contains valid pipeline handle
  *   when the function returns successfully.
+ * @param[in] name
+ *   Pipeline unique name.
  * @param[in] numa_node
  *   Non-Uniform Memory Access (NUMA) node.
  * @return
  *   0 on success or the following error codes otherwise:
  *   -EINVAL: Invalid argument;
- *   -ENOMEM: Not enough space/cannot allocate memory.
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Pipeline with this name already exists.
  */
 __rte_experimental
 int
 rte_swx_pipeline_config(struct rte_swx_pipeline **p,
+			const char *name,
 			int numa_node);
 
 /*
diff --git a/lib/pipeline/rte_swx_pipeline_internal.h b/lib/pipeline/rte_swx_pipeline_internal.h
index a35635efb7..588cad62b5 100644
--- a/lib/pipeline/rte_swx_pipeline_internal.h
+++ b/lib/pipeline/rte_swx_pipeline_internal.h
@@ -1459,6 +1459,8 @@ instr_operand_nbo(struct thread *t, const struct instr_operand *x)
 #endif
 
 struct rte_swx_pipeline {
+	char name[RTE_SWX_NAME_SIZE];
+
 	struct struct_type_tailq struct_types;
 	struct port_in_type_tailq port_in_types;
 	struct port_in_tailq ports_in;
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 8312307a7a..50029aadcf 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -145,4 +145,8 @@ EXPERIMENTAL {
 	rte_swx_ctl_pipeline_learner_timeout_get;
 	rte_swx_ctl_pipeline_learner_timeout_set;
 	rte_swx_pipeline_hash_func_register;
+
+	#added in 22.11
+	rte_swx_ctl_pipeline_find;
+	rte_swx_pipeline_find;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 02/17] pipeline: move specification data structures to internal header
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 03/17] pipeline: add pipeline specification data structure Cristian Dumitrescu
                       ` (15 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Move all the pipeline object specification data structures to an
internal header file.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 126 +------------------
 lib/pipeline/rte_swx_pipeline_spec.h | 176 +++++++++++++++++++++++++++
 2 files changed, 177 insertions(+), 125 deletions(-)
 create mode 100644 lib/pipeline/rte_swx_pipeline_spec.h

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 904b9eb471..5e07b4f794 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -9,7 +9,7 @@
 
 #include <rte_common.h>
 
-#include "rte_swx_pipeline.h"
+#include "rte_swx_pipeline_spec.h"
 
 #ifndef MAX_LINE_LENGTH
 #define MAX_LINE_LENGTH 2048
@@ -34,15 +34,7 @@
 
 /*
  * extobj.
- *
- * extobj OBJ_NAME instanceof OBJ_TYPE [ pragma OBJ_CREATE_ARGS ]
  */
-struct extobj_spec {
-	char *name;
-	char *extern_type_name;
-	char *pragma;
-};
-
 static void
 extobj_spec_free(struct extobj_spec *s)
 {
@@ -104,18 +96,7 @@ extobj_statement_parse(struct extobj_spec *s,
 /*
  * struct.
  *
- * struct STRUCT_TYPE_NAME {
- *	bit<SIZE> | varbit<SIZE> FIELD_NAME
- *	...
- * }
  */
-struct struct_spec {
-	char *name;
-	struct rte_swx_field_params *fields;
-	uint32_t n_fields;
-	int varbit;
-};
-
 static void
 struct_spec_free(struct struct_spec *s)
 {
@@ -293,13 +274,7 @@ struct_block_parse(struct struct_spec *s,
 /*
  * header.
  *
- * header HEADER_NAME instanceof STRUCT_TYPE_NAME
  */
-struct header_spec {
-	char *name;
-	char *struct_type_name;
-};
-
 static void
 header_spec_free(struct header_spec *s)
 {
@@ -351,12 +326,7 @@ header_statement_parse(struct header_spec *s,
 /*
  * metadata.
  *
- * metadata instanceof STRUCT_TYPE_NAME
  */
-struct metadata_spec {
-	char *struct_type_name;
-};
-
 static void
 metadata_spec_free(struct metadata_spec *s)
 {
@@ -400,18 +370,7 @@ metadata_statement_parse(struct metadata_spec *s,
 /*
  * action.
  *
- * action ACTION_NAME args none | instanceof STRUCT_TYPE_NAME {
- *	INSTRUCTION
- *	...
- * }
  */
-struct action_spec {
-	char *name;
-	char *args_struct_type_name;
-	const char **instructions;
-	uint32_t n_instructions;
-};
-
 static void
 action_spec_free(struct action_spec *s)
 {
@@ -540,29 +499,7 @@ action_block_parse(struct action_spec *s,
 /*
  * table.
  *
- * table TABLE_NAME {
- *	key {
- *		MATCH_FIELD_NAME exact | wildcard | lpm
- *		...
- *	}
- *	actions {
- *		ACTION_NAME [ @tableonly | @defaultonly ]
- *		...
- *	}
- *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
- *	instanceof TABLE_TYPE_NAME
- *	pragma ARGS
- *	size SIZE
- * }
  */
-struct table_spec {
-	char *name;
-	struct rte_swx_pipeline_table_params params;
-	char *recommended_table_type_name;
-	char *args;
-	uint32_t size;
-};
-
 static void
 table_spec_free(struct table_spec *s)
 {
@@ -1084,22 +1021,7 @@ table_block_parse(struct table_spec *s,
 /*
  * selector.
  *
- * selector SELECTOR_NAME {
- *	group_id FIELD_NAME
- *	selector {
- *		FIELD_NAME
- *		...
- *	}
- *	member_id FIELD_NAME
- *	n_groups N_GROUPS
- *	n_members_per_group N_MEMBERS_PER_GROUP
- * }
  */
-struct selector_spec {
-	char *name;
-	struct rte_swx_pipeline_selector_params params;
-};
-
 static void
 selector_spec_free(struct selector_spec *s)
 {
@@ -1385,31 +1307,7 @@ selector_block_parse(struct selector_spec *s,
 /*
  * learner.
  *
- * learner LEARNER_NAME {
- *	key {
- *		MATCH_FIELD_NAME
- *		...
- *	}
- *	actions {
- *		ACTION_NAME [ @tableonly | @defaultonly]
- *		...
- *	}
- *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
- *	size SIZE
- *	timeout {
- *		TIMEOUT_IN_SECONDS
- *		...
- *	}
- * }
  */
-struct learner_spec {
-	char *name;
-	struct rte_swx_pipeline_learner_params params;
-	uint32_t size;
-	uint32_t *timeout;
-	uint32_t n_timeouts;
-};
-
 static void
 learner_spec_free(struct learner_spec *s)
 {
@@ -1958,14 +1856,7 @@ learner_block_parse(struct learner_spec *s,
 /*
  * regarray.
  *
- * regarray NAME size SIZE initval INITVAL
  */
-struct regarray_spec {
-	char *name;
-	uint64_t init_val;
-	uint32_t size;
-};
-
 static void
 regarray_spec_free(struct regarray_spec *s)
 {
@@ -2033,13 +1924,7 @@ regarray_statement_parse(struct regarray_spec *s,
 /*
  * metarray.
  *
- * metarray NAME size SIZE
  */
-struct metarray_spec {
-	char *name;
-	uint32_t size;
-};
-
 static void
 metarray_spec_free(struct metarray_spec *s)
 {
@@ -2095,16 +1980,7 @@ metarray_statement_parse(struct metarray_spec *s,
 /*
  * apply.
  *
- * apply {
- *	INSTRUCTION
- *	...
- * }
  */
-struct apply_spec {
-	const char **instructions;
-	uint32_t n_instructions;
-};
-
 static void
 apply_spec_free(struct apply_spec *s)
 {
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
new file mode 100644
index 0000000000..8458de878a
--- /dev/null
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#include <stdint.h>
+#include <stdio.h>
+
+#include <rte_common.h>
+
+#include <rte_swx_pipeline.h>
+
+/*
+ * extobj.
+ *
+ * extobj OBJ_NAME instanceof OBJ_TYPE [ pragma OBJ_CREATE_ARGS ]
+ */
+struct extobj_spec {
+	char *name;
+	char *extern_type_name;
+	char *pragma;
+};
+
+/*
+ * struct.
+ *
+ * struct STRUCT_TYPE_NAME {
+ *	bit<SIZE> | varbit<SIZE> FIELD_NAME
+ *	...
+ * }
+ */
+struct struct_spec {
+	char *name;
+	struct rte_swx_field_params *fields;
+	uint32_t n_fields;
+	int varbit;
+};
+
+/*
+ * header.
+ *
+ * header HEADER_NAME instanceof STRUCT_TYPE_NAME
+ */
+struct header_spec {
+	char *name;
+	char *struct_type_name;
+};
+
+/*
+ * metadata.
+ *
+ * metadata instanceof STRUCT_TYPE_NAME
+ */
+struct metadata_spec {
+	char *struct_type_name;
+};
+
+/*
+ * action.
+ *
+ * action ACTION_NAME args none | instanceof STRUCT_TYPE_NAME {
+ *	INSTRUCTION
+ *	...
+ * }
+ */
+struct action_spec {
+	char *name;
+	char *args_struct_type_name;
+	const char **instructions;
+	uint32_t n_instructions;
+};
+
+/*
+ * table.
+ *
+ * table TABLE_NAME {
+ *	key {
+ *		MATCH_FIELD_NAME exact | wildcard | lpm
+ *		...
+ *	}
+ *	actions {
+ *		ACTION_NAME [ @tableonly | @defaultonly ]
+ *		...
+ *	}
+ *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
+ *	instanceof TABLE_TYPE_NAME
+ *	pragma ARGS
+ *	size SIZE
+ * }
+ */
+struct table_spec {
+	char *name;
+	struct rte_swx_pipeline_table_params params;
+	char *recommended_table_type_name;
+	char *args;
+	uint32_t size;
+};
+
+/*
+ * selector.
+ *
+ * selector SELECTOR_NAME {
+ *	group_id FIELD_NAME
+ *	selector {
+ *		FIELD_NAME
+ *		...
+ *	}
+ *	member_id FIELD_NAME
+ *	n_groups N_GROUPS
+ *	n_members_per_group N_MEMBERS_PER_GROUP
+ * }
+ */
+struct selector_spec {
+	char *name;
+	struct rte_swx_pipeline_selector_params params;
+};
+
+/*
+ * learner.
+ *
+ * learner LEARNER_NAME {
+ *	key {
+ *		MATCH_FIELD_NAME
+ *		...
+ *	}
+ *	actions {
+ *		ACTION_NAME [ @tableonly | @defaultonly]
+ *		...
+ *	}
+ *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
+ *	size SIZE
+ *	timeout {
+ *		TIMEOUT_IN_SECONDS
+ *		...
+ *	}
+ * }
+ */
+struct learner_spec {
+	char *name;
+	struct rte_swx_pipeline_learner_params params;
+	uint32_t size;
+	uint32_t *timeout;
+	uint32_t n_timeouts;
+};
+
+/*
+ * regarray.
+ *
+ * regarray NAME size SIZE initval INITVAL
+ */
+struct regarray_spec {
+	char *name;
+	uint64_t init_val;
+	uint32_t size;
+};
+
+/*
+ * metarray.
+ *
+ * metarray NAME size SIZE
+ */
+struct metarray_spec {
+	char *name;
+	uint32_t size;
+};
+
+/*
+ * apply.
+ *
+ * apply {
+ *	INSTRUCTION
+ *	...
+ * }
+ */
+struct apply_spec {
+	const char **instructions;
+	uint32_t n_instructions;
+};
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 03/17] pipeline: add pipeline specification data structure
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 02/17] pipeline: move specification data structures to internal header Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 04/17] pipeline: rework the specification file-based pipeline build Cristian Dumitrescu
                       ` (14 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add specification data structure for the entire pipeline.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 21 ++++++++++++++++++
 lib/pipeline/rte_swx_pipeline_spec.h | 32 ++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 5e07b4f794..642091b678 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2082,6 +2082,27 @@ apply_block_parse(struct apply_spec *s,
 /*
  * Pipeline.
  */
+void
+pipeline_spec_free(struct pipeline_spec *s)
+{
+	if (!s)
+		return;
+
+	free(s->extobjs);
+	free(s->structs);
+	free(s->headers);
+	free(s->metadata);
+	free(s->actions);
+	free(s->tables);
+	free(s->selectors);
+	free(s->learners);
+	free(s->regarrays);
+	free(s->metarrays);
+	free(s->apply);
+
+	memset(s, 0, sizeof(struct pipeline_spec));
+}
+
 int
 rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				 FILE *spec,
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 8458de878a..e1170a33b1 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -174,3 +174,35 @@ struct apply_spec {
 	const char **instructions;
 	uint32_t n_instructions;
 };
+
+/*
+ * Pipeline.
+ */
+struct pipeline_spec {
+	struct extobj_spec *extobjs;
+	struct struct_spec *structs;
+	struct header_spec *headers;
+	struct metadata_spec *metadata;
+	struct action_spec *actions;
+	struct table_spec *tables;
+	struct selector_spec *selectors;
+	struct learner_spec *learners;
+	struct regarray_spec *regarrays;
+	struct metarray_spec *metarrays;
+	struct apply_spec *apply;
+
+	uint32_t n_extobjs;
+	uint32_t n_structs;
+	uint32_t n_headers;
+	uint32_t n_metadata;
+	uint32_t n_actions;
+	uint32_t n_tables;
+	uint32_t n_selectors;
+	uint32_t n_learners;
+	uint32_t n_regarrays;
+	uint32_t n_metarrays;
+	uint32_t n_apply;
+};
+
+void
+pipeline_spec_free(struct pipeline_spec *s);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 04/17] pipeline: rework the specification file-based pipeline build
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 02/17] pipeline: move specification data structures to internal header Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 03/17] pipeline: add pipeline specification data structure Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 05/17] pipeline: generate the code for pipeline specification structure Cristian Dumitrescu
                       ` (13 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Rework the specification file-based pipeline build operation to first
parse the specification file into the previously introduced pipeline
specification data structure, then use this structure to configure
and build the pipeline.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 478 +++++++++++++++++++++------
 lib/pipeline/rte_swx_pipeline_spec.h |   9 +
 2 files changed, 385 insertions(+), 102 deletions(-)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 642091b678..62929a9da6 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2103,11 +2103,10 @@ pipeline_spec_free(struct pipeline_spec *s)
 	memset(s, 0, sizeof(struct pipeline_spec));
 }
 
-int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec,
-				 uint32_t *err_line,
-				 const char **err_msg)
+struct pipeline_spec *
+pipeline_spec_parse(FILE *spec,
+		    uint32_t *err_line,
+		    const char **err_msg)
 {
 	struct extobj_spec extobj_spec = {0};
 	struct struct_spec struct_spec = {0};
@@ -2120,26 +2119,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 	struct regarray_spec regarray_spec = {0};
 	struct metarray_spec metarray_spec = {0};
 	struct apply_spec apply_spec = {0};
-	uint32_t n_lines;
+	struct pipeline_spec *s = NULL;
+	uint32_t n_lines = 0;
 	uint32_t block_mask = 0;
-	int status;
+	int status = 0;
 
 	/* Check the input arguments. */
-	if (!p) {
+	if (!spec) {
 		if (err_line)
-			*err_line = 0;
+			*err_line = n_lines;
 		if (err_msg)
-			*err_msg = "Null pipeline argument.";
+			*err_msg = "Invalid input argument.";
 		status = -EINVAL;
 		goto error;
 	}
 
-	if (!spec) {
+	/* Memory allocation. */
+	s = calloc(sizeof(struct pipeline_spec), 1);
+	if (!s) {
 		if (err_line)
-			*err_line = 0;
+			*err_line = n_lines;
 		if (err_msg)
-			*err_msg = "Null specification file argument.";
-		status = -EINVAL;
+			*err_msg = "Memory allocation failed.";
+		status = -ENOMEM;
 		goto error;
 	}
 
@@ -2200,6 +2202,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* struct block. */
 		if (block_mask & (1 << STRUCT_BLOCK)) {
+			struct struct_spec *new_structs;
+
 			status = struct_block_parse(&struct_spec,
 						    &block_mask,
 						    tokens,
@@ -2214,26 +2218,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_struct_type_register(p,
-				struct_spec.name,
-				struct_spec.fields,
-				struct_spec.n_fields,
-				struct_spec.varbit);
-			if (status) {
+			new_structs = realloc(s->structs,
+					      (s->n_structs + 1) * sizeof(struct struct_spec));
+			if (!new_structs) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Struct registration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			struct_spec_free(&struct_spec);
+			s->structs = new_structs;
+			memcpy(&s->structs[s->n_structs], &struct_spec, sizeof(struct struct_spec));
+			s->n_structs++;
+			memset(&struct_spec, 0, sizeof(struct struct_spec));
 
 			continue;
 		}
 
 		/* action block. */
 		if (block_mask & (1 << ACTION_BLOCK)) {
+			struct action_spec *new_actions;
+
 			status = action_block_parse(&action_spec,
 						    &block_mask,
 						    tokens,
@@ -2248,26 +2255,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_action_config(p,
-				action_spec.name,
-				action_spec.args_struct_type_name,
-				action_spec.instructions,
-				action_spec.n_instructions);
-			if (status) {
+			new_actions = realloc(s->actions,
+					      (s->n_actions + 1) * sizeof(struct action_spec));
+			if (!new_actions) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Action config error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			action_spec_free(&action_spec);
+			s->actions = new_actions;
+			memcpy(&s->actions[s->n_actions], &action_spec, sizeof(struct action_spec));
+			s->n_actions++;
+			memset(&action_spec, 0, sizeof(struct action_spec));
 
 			continue;
 		}
 
 		/* table block. */
 		if (block_mask & (1 << TABLE_BLOCK)) {
+			struct table_spec *new_tables;
+
 			status = table_block_parse(&table_spec,
 						   &block_mask,
 						   tokens,
@@ -2282,27 +2292,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_table_config(p,
-				table_spec.name,
-				&table_spec.params,
-				table_spec.recommended_table_type_name,
-				table_spec.args,
-				table_spec.size);
-			if (status) {
+			new_tables = realloc(s->tables,
+					     (s->n_tables + 1) * sizeof(struct table_spec));
+			if (!new_tables) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Table configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			table_spec_free(&table_spec);
+			s->tables = new_tables;
+			memcpy(&s->tables[s->n_tables], &table_spec, sizeof(struct table_spec));
+			s->n_tables++;
+			memset(&table_spec, 0, sizeof(struct table_spec));
 
 			continue;
 		}
 
 		/* selector block. */
 		if (block_mask & (1 << SELECTOR_BLOCK)) {
+			struct selector_spec *new_selectors;
+
 			status = selector_block_parse(&selector_spec,
 						      &block_mask,
 						      tokens,
@@ -2317,24 +2329,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_selector_config(p,
-				selector_spec.name,
-				&selector_spec.params);
-			if (status) {
+			new_selectors = realloc(s->selectors,
+				(s->n_selectors + 1) * sizeof(struct selector_spec));
+			if (!new_selectors) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Selector configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			selector_spec_free(&selector_spec);
+			s->selectors = new_selectors;
+			memcpy(&s->selectors[s->n_selectors],
+			       &selector_spec,
+			       sizeof(struct selector_spec));
+			s->n_selectors++;
+			memset(&selector_spec, 0, sizeof(struct selector_spec));
 
 			continue;
 		}
 
 		/* learner block. */
 		if (block_mask & (1 << LEARNER_BLOCK)) {
+			struct learner_spec *new_learners;
+
 			status = learner_block_parse(&learner_spec,
 						     &block_mask,
 						     tokens,
@@ -2349,27 +2368,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_learner_config(p,
-				learner_spec.name,
-				&learner_spec.params,
-				learner_spec.size,
-				learner_spec.timeout,
-				learner_spec.n_timeouts);
-			if (status) {
+			new_learners = realloc(s->learners,
+					       (s->n_learners + 1) * sizeof(struct learner_spec));
+			if (!new_learners) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Learner table configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			learner_spec_free(&learner_spec);
+			s->learners = new_learners;
+			memcpy(&s->learners[s->n_learners],
+			       &learner_spec,
+			       sizeof(struct learner_spec));
+			s->n_learners++;
+			memset(&learner_spec, 0, sizeof(struct learner_spec));
 
 			continue;
 		}
 
 		/* apply block. */
 		if (block_mask & (1 << APPLY_BLOCK)) {
+			struct apply_spec *new_apply;
+
 			status = apply_block_parse(&apply_spec,
 						   &block_mask,
 						   tokens,
@@ -2384,24 +2407,28 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_instructions_config(p,
-				apply_spec.instructions,
-				apply_spec.n_instructions);
-			if (status) {
+			new_apply = realloc(s->apply, (s->n_apply + 1) * sizeof(struct apply_spec));
+			if (!new_apply) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Pipeline instructions err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			apply_spec_free(&apply_spec);
+			s->apply = new_apply;
+			memcpy(&s->apply[s->n_apply], &apply_spec, sizeof(struct apply_spec));
+			s->n_apply++;
+			memset(&apply_spec, 0, sizeof(struct apply_spec));
 
 			continue;
 		}
 
 		/* extobj. */
 		if (!strcmp(tokens[0], "extobj")) {
+			struct extobj_spec *new_extobjs;
+
 			status = extobj_statement_parse(&extobj_spec,
 							tokens,
 							n_tokens,
@@ -2411,19 +2438,21 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_extern_object_config(p,
-				extobj_spec.name,
-				extobj_spec.extern_type_name,
-				extobj_spec.pragma);
-			if (status) {
+			new_extobjs = realloc(s->extobjs,
+					      (s->n_extobjs + 1) * sizeof(struct extobj_spec));
+			if (!new_extobjs) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Extern object config err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			extobj_spec_free(&extobj_spec);
+			s->extobjs = new_extobjs;
+			memcpy(&s->extobjs[s->n_extobjs], &extobj_spec, sizeof(struct extobj_spec));
+			s->n_extobjs++;
+			memset(&extobj_spec, 0, sizeof(struct extobj_spec));
 
 			continue;
 		}
@@ -2445,6 +2474,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* header. */
 		if (!strcmp(tokens[0], "header")) {
+			struct header_spec *new_headers;
+
 			status = header_statement_parse(&header_spec,
 							tokens,
 							n_tokens,
@@ -2454,24 +2485,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_packet_header_register(p,
-				header_spec.name,
-				header_spec.struct_type_name);
-			if (status) {
+			new_headers = realloc(s->headers,
+					      (s->n_headers + 1) * sizeof(struct header_spec));
+			if (!new_headers) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Header registration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			header_spec_free(&header_spec);
+			s->headers = new_headers;
+			memcpy(&s->headers[s->n_headers], &header_spec, sizeof(struct header_spec));
+			s->n_headers++;
+			memset(&header_spec, 0, sizeof(struct header_spec));
 
 			continue;
 		}
 
 		/* metadata. */
 		if (!strcmp(tokens[0], "metadata")) {
+			struct metadata_spec *new_metadata;
+
 			status = metadata_statement_parse(&metadata_spec,
 							  tokens,
 							  n_tokens,
@@ -2481,17 +2517,23 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_packet_metadata_register(p,
-				metadata_spec.struct_type_name);
-			if (status) {
+			new_metadata = realloc(s->metadata,
+					       (s->n_metadata + 1) * sizeof(struct metadata_spec));
+			if (!new_metadata) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Meta-data reg err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			metadata_spec_free(&metadata_spec);
+			s->metadata = new_metadata;
+			memcpy(&s->metadata[s->n_metadata],
+			       &metadata_spec,
+			       sizeof(struct metadata_spec));
+			s->n_metadata++;
+			memset(&metadata_spec, 0, sizeof(struct metadata_spec));
 
 			continue;
 		}
@@ -2558,6 +2600,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* regarray. */
 		if (!strcmp(tokens[0], "regarray")) {
+			struct regarray_spec *new_regarrays;
+
 			status = regarray_statement_parse(&regarray_spec,
 							  tokens,
 							  n_tokens,
@@ -2567,25 +2611,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_regarray_config(p,
-				regarray_spec.name,
-				regarray_spec.size,
-				regarray_spec.init_val);
-			if (status) {
+			new_regarrays = realloc(s->regarrays,
+				(s->n_regarrays + 1) * sizeof(struct regarray_spec));
+			if (!new_regarrays) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Register array configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			regarray_spec_free(&regarray_spec);
+			s->regarrays = new_regarrays;
+			memcpy(&s->regarrays[s->n_regarrays],
+			       &regarray_spec,
+			       sizeof(struct regarray_spec));
+			s->n_regarrays++;
+			memset(&regarray_spec, 0, sizeof(struct regarray_spec));
 
 			continue;
 		}
 
 		/* metarray. */
 		if (!strcmp(tokens[0], "metarray")) {
+			struct metarray_spec *new_metarrays;
+
 			status = metarray_statement_parse(&metarray_spec,
 							  tokens,
 							  n_tokens,
@@ -2595,18 +2645,23 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_metarray_config(p,
-				metarray_spec.name,
-				metarray_spec.size);
-			if (status) {
+			new_metarrays = realloc(s->metarrays,
+				(s->n_metarrays + 1) * sizeof(struct metarray_spec));
+			if (!new_metarrays) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Meter array configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			metarray_spec_free(&metarray_spec);
+			s->metarrays = new_metarrays;
+			memcpy(&s->metarrays[s->n_metarrays],
+			       &metarray_spec,
+			       sizeof(struct metarray_spec));
+			s->n_metarrays++;
+			memset(&metarray_spec, 0, sizeof(struct metarray_spec));
 
 			continue;
 		}
@@ -2644,17 +2699,7 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 		goto error;
 	}
 
-	/* Pipeline build. */
-	status = rte_swx_pipeline_build(p);
-	if (status) {
-		if (err_line)
-			*err_line = n_lines;
-		if (err_msg)
-			*err_msg = "Pipeline build error.";
-		goto error;
-	}
-
-	return 0;
+	return s;
 
 error:
 	extobj_spec_free(&extobj_spec);
@@ -2668,5 +2713,234 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 	regarray_spec_free(&regarray_spec);
 	metarray_spec_free(&metarray_spec);
 	apply_spec_free(&apply_spec);
+	pipeline_spec_free(s);
+
+	return NULL;
+}
+
+int
+pipeline_spec_configure(struct rte_swx_pipeline *p,
+			struct pipeline_spec *s,
+			const char **err_msg)
+{
+	uint32_t i;
+	int status = 0;
+
+	/* extobj. */
+	for (i = 0; i < s->n_extobjs; i++) {
+		struct extobj_spec *extobj_spec = &s->extobjs[i];
+
+		status = rte_swx_pipeline_extern_object_config(p,
+			extobj_spec->name,
+			extobj_spec->extern_type_name,
+			extobj_spec->pragma);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Extern object configuration error.";
+			return status;
+		}
+	}
+
+	/* regarray. */
+	for (i = 0; i < s->n_regarrays; i++) {
+		struct regarray_spec *regarray_spec = &s->regarrays[i];
+
+		status = rte_swx_pipeline_regarray_config(p,
+			regarray_spec->name,
+			regarray_spec->size,
+			regarray_spec->init_val);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Register array configuration error.";
+			return status;
+		}
+	}
+
+	/* metarray. */
+	for (i = 0; i < s->n_metarrays; i++) {
+		struct metarray_spec *metarray_spec = &s->metarrays[i];
+
+		status = rte_swx_pipeline_metarray_config(p,
+			metarray_spec->name,
+			metarray_spec->size);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Meter array configuration error.";
+			return status;
+		}
+	}
+
+	/* struct. */
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+
+		status = rte_swx_pipeline_struct_type_register(p,
+			struct_spec->name,
+			struct_spec->fields,
+			struct_spec->n_fields,
+			struct_spec->varbit);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Struct type registration error.";
+			return status;
+		}
+	}
+
+	/* header. */
+	for (i = 0; i < s->n_headers; i++) {
+		struct header_spec *header_spec = &s->headers[i];
+
+		status = rte_swx_pipeline_packet_header_register(p,
+			header_spec->name,
+			header_spec->struct_type_name);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Header configuration error.";
+			return status;
+		}
+	}
+
+	/* metadata. */
+	for (i = 0; i < s->n_metadata; i++) {
+		struct metadata_spec *metadata_spec = &s->metadata[i];
+
+		status = rte_swx_pipeline_packet_metadata_register(p,
+			metadata_spec->struct_type_name);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Meta-data registration error.";
+			return status;
+		}
+	}
+
+	/* action. */
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+
+		status = rte_swx_pipeline_action_config(p,
+			action_spec->name,
+			action_spec->args_struct_type_name,
+			action_spec->instructions,
+			action_spec->n_instructions);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Action configuration error.";
+			return status;
+		}
+	}
+
+	/* table. */
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+
+		status = rte_swx_pipeline_table_config(p,
+			table_spec->name,
+			&table_spec->params,
+			table_spec->recommended_table_type_name,
+			table_spec->args,
+			table_spec->size);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Table configuration error.";
+			return status;
+		}
+	}
+
+	/* selector. */
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+
+		status = rte_swx_pipeline_selector_config(p,
+			selector_spec->name,
+			&selector_spec->params);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Selector table configuration error.";
+			return status;
+		}
+	}
+
+	/* learner. */
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+
+		status = rte_swx_pipeline_learner_config(p,
+			learner_spec->name,
+			&learner_spec->params,
+			learner_spec->size,
+			learner_spec->timeout,
+			learner_spec->n_timeouts);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Learner table configuration error.";
+			return status;
+		}
+	}
+
+	/* apply. */
+	for (i = 0; i < s->n_apply; i++) {
+		struct apply_spec *apply_spec = &s->apply[i];
+
+		status = rte_swx_pipeline_instructions_config(p,
+			apply_spec->instructions,
+			apply_spec->n_instructions);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Pipeline instructions configuration error.";
+			return status;
+		}
+	}
+
+	return 0;
+}
+
+int
+rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
+				 FILE *spec_file,
+				 uint32_t *err_line,
+				 const char **err_msg)
+{
+	struct pipeline_spec *s = NULL;
+	int status = 0;
+
+	/* Check the input arguments. */
+	if (!p || !spec_file) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Invalid input argument.";
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Spec file parse. */
+	s = pipeline_spec_parse(spec_file, err_line, err_msg);
+	if (!s) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Pipeline configure. */
+	status = pipeline_spec_configure(p, s, err_msg);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		goto error;
+	}
+
+	/* Pipeline build. */
+	status = rte_swx_pipeline_build(p);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Pipeline build error.";
+		goto error;
+	}
+
+	return 0;
+
+error:
+	pipeline_spec_free(s);
 	return status;
 }
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index e1170a33b1..4f3a0b5958 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -206,3 +206,12 @@ struct pipeline_spec {
 
 void
 pipeline_spec_free(struct pipeline_spec *s);
+struct pipeline_spec *
+pipeline_spec_parse(FILE *spec,
+		    uint32_t *err_line,
+		    const char **err_msg);
+
+int
+pipeline_spec_configure(struct rte_swx_pipeline *p,
+			struct pipeline_spec *s,
+			const char **err_msg);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 05/17] pipeline: generate the code for pipeline specification structure
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (2 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 04/17] pipeline: rework the specification file-based pipeline build Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 06/17] pipeline: add support for pipeline I/O specification Cristian Dumitrescu
                       ` (12 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add support to export the pipeline specification data structure to a C
source code file.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 622 +++++++++++++++++++++++++++
 lib/pipeline/rte_swx_pipeline_spec.h |   5 +
 2 files changed, 627 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 62929a9da6..bf21fe17ba 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2020 Intel Corporation
  */
 #include <stdint.h>
+#include <inttypes.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -2103,6 +2104,627 @@ pipeline_spec_free(struct pipeline_spec *s)
 	memset(s, 0, sizeof(struct pipeline_spec));
 }
 
+static const char *
+match_type_string_get(enum rte_swx_table_match_type match_type)
+{
+	switch (match_type) {
+	case RTE_SWX_TABLE_MATCH_WILDCARD: return "RTE_SWX_TABLE_MATCH_WILDCARD";
+	case RTE_SWX_TABLE_MATCH_LPM: return "RTE_SWX_TABLE_MATCH_LPM";
+	case RTE_SWX_TABLE_MATCH_EXACT: return "RTE_SWX_TABLE_MATCH_EXACT";
+	default: return "RTE_SWX_TABLE_MATCH_UNKNOWN";
+	}
+}
+
+void
+pipeline_spec_codegen(FILE *f,
+		      struct pipeline_spec *s)
+{
+	uint32_t i;
+
+	/* Check the input arguments. */
+	if (!f || !s)
+		return;
+
+	/* extobj. */
+	fprintf(f, "static struct extobj_spec extobjs[] = {\n");
+
+	for (i = 0; i < s->n_extobjs; i++) {
+		struct extobj_spec *extobj_spec = &s->extobjs[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", extobj_spec->name);
+		fprintf(f, "\t\t.extern_type_name = \"%s\",\n", extobj_spec->extern_type_name);
+		if (extobj_spec->pragma)
+			fprintf(f, "\t\t.pragma = \"%s\",\n", extobj_spec->pragma);
+		else
+			fprintf(f, "\t\t.pragma = NULL,\n");
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* regarray. */
+	fprintf(f, "static struct regarray_spec regarrays[] = {\n");
+
+	for (i = 0; i < s->n_regarrays; i++) {
+		struct regarray_spec *regarray_spec = &s->regarrays[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", regarray_spec->name);
+		fprintf(f, "\t\t.init_val = %" PRIu64 ",\n", regarray_spec->init_val);
+		fprintf(f, "\t\t.size = %u,\n", regarray_spec->size);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* metarray. */
+	fprintf(f, "static struct metarray_spec metarrays[] = {\n");
+
+	for (i = 0; i < s->n_metarrays; i++) {
+		struct metarray_spec *metarray_spec = &s->metarrays[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", metarray_spec->name);
+		fprintf(f, "\t\t.size = %u,\n", metarray_spec->size);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* struct. */
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+		uint32_t j;
+
+		fprintf(f, "static struct rte_swx_field_params struct_%s_fields[] = {\n",
+			struct_spec->name);
+
+		for (j = 0; j < struct_spec->n_fields; j++) {
+			struct rte_swx_field_params *field = &struct_spec->fields[j];
+
+			fprintf(f, "\t[%d] = {\n", j);
+			fprintf(f, "\t\t.name = \"%s\",\n", field->name);
+			fprintf(f, "\t\t.n_bits = %u,\n", field->n_bits);
+			fprintf(f, "\t},\n");
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct struct_spec structs[] = {\n");
+
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", struct_spec->name);
+		fprintf(f, "\t\t.fields = struct_%s_fields,\n", struct_spec->name);
+		fprintf(f, "\t\t.n_fields = "
+			"sizeof(struct_%s_fields) / sizeof(struct_%s_fields[0]),\n",
+			struct_spec->name,
+			struct_spec->name);
+		fprintf(f, "\t\t.varbit = %d,\n", struct_spec->varbit);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* header. */
+	fprintf(f, "static struct header_spec headers[] = {\n");
+
+	for (i = 0; i < s->n_headers; i++) {
+		struct header_spec *header_spec = &s->headers[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", header_spec->name);
+		fprintf(f, "\t\t.struct_type_name = \"%s\",\n", header_spec->struct_type_name);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* metadata. */
+	fprintf(f, "static struct metadata_spec metadata[] = {\n");
+
+	for (i = 0; i < s->n_metadata; i++) {
+		struct metadata_spec *metadata_spec = &s->metadata[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.struct_type_name = \"%s\",\n", metadata_spec->struct_type_name);
+		fprintf(f, "\t},\n");
+
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* action. */
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+		uint32_t j;
+
+		fprintf(f, "static const char *action_%s_initial_instructions[] = {\n",
+			action_spec->name);
+
+		for (j = 0; j < action_spec->n_instructions; j++) {
+			const char *instr = action_spec->instructions[j];
+
+			fprintf(f, "\t[%d] = \"%s\",\n", j, instr);
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct action_spec actions[] = {\n");
+
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", action_spec->name);
+
+		if (action_spec->args_struct_type_name)
+			fprintf(f, "\t\t.args_struct_type_name = \"%s\",\n",
+				action_spec->args_struct_type_name);
+		else
+			fprintf(f, "\t\t.args_struct_type_name = NULL,\n");
+
+		fprintf(f, "\t\t.instructions = action_%s_initial_instructions,\n",
+			action_spec->name);
+		fprintf(f, "\t\t.n_instructions = "
+			"sizeof(action_%s_initial_instructions) / "
+			"sizeof(action_%s_initial_instructions[0]),\n",
+			action_spec->name,
+			action_spec->name);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* table. */
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+		uint32_t j;
+
+		/* fields. */
+		if (table_spec->params.fields && table_spec->params.n_fields) {
+			fprintf(f, "static struct rte_swx_match_field_params "
+				"table_%s_fields[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_fields; j++) {
+				struct rte_swx_match_field_params *field =
+					&table_spec->params.fields[j];
+
+				fprintf(f, "\t[%d] = {\n", j);
+				fprintf(f, "\t\t.name = \"%s\",\n", field->name);
+				fprintf(f, "\t\t.match_type = %s,\n",
+					match_type_string_get(field->match_type));
+				fprintf(f, "\t},\n");
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_names. */
+		if (table_spec->params.action_names && table_spec->params.n_actions) {
+			fprintf(f, "static const char *table_%s_action_names[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				const char *action_name = table_spec->params.action_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, action_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_table_entries. */
+		if (table_spec->params.action_is_for_table_entries &&
+		    table_spec->params.n_actions) {
+			fprintf(f, "static int table_%s_action_is_for_table_entries[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				int value = table_spec->params.action_is_for_table_entries[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_default_entry. */
+		if (table_spec->params.action_is_for_default_entry &&
+		    table_spec->params.n_actions) {
+			fprintf(f, "static int table_%s_action_is_for_default_entry[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				int value = table_spec->params.action_is_for_default_entry[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct table_spec tables[] = {\n");
+
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", table_spec->name);
+
+		fprintf(f, "\t\t.params = {\n");
+
+		if (table_spec->params.fields && table_spec->params.n_fields) {
+			fprintf(f, "\t\t\t.fields = table_%s_fields,\n", table_spec->name);
+			fprintf(f, "\t\t\t.n_fields = "
+				"sizeof(table_%s_fields) / sizeof(table_%s_fields[0]),\n",
+				table_spec->name,
+				table_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.fields = NULL,\n");
+			fprintf(f, "\t\t\t.n_fields = 0,\n");
+		}
+
+		if (table_spec->params.action_names && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_names = table_%s_action_names,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_names = NULL,\n");
+
+		if (table_spec->params.action_is_for_table_entries && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_table_entries = "
+				"table_%s_action_is_for_table_entries,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_table_entries = NULL,\n");
+
+		if (table_spec->params.action_is_for_default_entry && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_default_entry = "
+				"table_%s_action_is_for_default_entry,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_default_entry = NULL,\n");
+
+		if (table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.n_actions = sizeof(table_%s_action_names) / "
+				"sizeof(table_%s_action_names[0]),\n",
+				table_spec->name,
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.n_actions = 0,\n");
+
+		if (table_spec->params.default_action_name)
+			fprintf(f, "\t\t\t.default_action_name = \"%s\",\n",
+				table_spec->params.default_action_name);
+		else
+			fprintf(f, "\t\t\t.default_action_name = NULL,\n");
+
+		if (table_spec->params.default_action_args)
+			fprintf(f, "\t\t\t.default_action_args = \"%s\",\n",
+				table_spec->params.default_action_args);
+		else
+			fprintf(f, "\t\t\t.default_action_args = NULL,\n");
+
+		fprintf(f, "\t\t\t.default_action_is_const = %d,\n",
+			table_spec->params.default_action_is_const);
+		fprintf(f, "\t\t},\n");
+
+		if (table_spec->recommended_table_type_name)
+			fprintf(f, "\t\t.recommended_table_type_name = \"%s\",\n",
+				table_spec->recommended_table_type_name);
+		else
+			fprintf(f, "\t\t.recommended_table_type_name = NULL,\n");
+
+		if (table_spec->args)
+			fprintf(f, "\t\t.args = \"%s\",\n", table_spec->args);
+		else
+			fprintf(f, "\t\t.args = NULL,\n");
+
+		fprintf(f, "\t\t.size = %u,\n", table_spec->size);
+
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* selector. */
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+		uint32_t j;
+
+		if (selector_spec->params.selector_field_names &&
+		    selector_spec->params.n_selector_fields) {
+			fprintf(f, "static const char *selector_%s_field_names[] = {\n",
+				selector_spec->name);
+
+			for (j = 0; j < selector_spec->params.n_selector_fields; j++) {
+				const char *field_name =
+					selector_spec->params.selector_field_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, field_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct selector_spec selectors[] = {\n");
+
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+
+		fprintf(f, "\t\t.name = \"%s\",\n", selector_spec->name);
+		fprintf(f, "\t\t.params = {\n");
+
+		if (selector_spec->params.group_id_field_name)
+			fprintf(f, "\t\t\t.group_id_field_name = \"%s\",\n",
+				selector_spec->params.group_id_field_name);
+		else
+			fprintf(f, "\t\t\t.group_id_field_name = NULL,\n");
+
+		if (selector_spec->params.selector_field_names &&
+		    selector_spec->params.n_selector_fields) {
+			fprintf(f, "\t\t\t.selector_field_names = selector_%s_field_names,\n",
+				selector_spec->name);
+			fprintf(f, "\t\t\t.n_selector_fields = "
+				"sizeof(selector_%s_field_names) / sizeof(selector_%s_field_names[0]),\n",
+				selector_spec->name,
+				selector_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.selector_field_names = NULL,\n");
+			fprintf(f, "\t\t\t.n_selector_fields = 0,\n");
+		}
+
+		if (selector_spec->params.member_id_field_name)
+			fprintf(f, "\t\t\t.member_id_field_name = \"%s\",\n",
+				selector_spec->params.member_id_field_name);
+		else
+			fprintf(f, "\t\t\t.member_id_field_name = NULL,\n");
+
+		fprintf(f, "\t\t\t.n_groups_max = %u,\n", selector_spec->params.n_groups_max);
+
+		fprintf(f, "\t\t\t.n_members_per_group_max = %u,\n",
+			selector_spec->params.n_members_per_group_max);
+
+		fprintf(f, "\t\t},\n");
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* learner. */
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+		uint32_t j;
+
+		/* field_names. */
+		if (learner_spec->params.field_names && learner_spec->params.n_fields) {
+			fprintf(f, "static const char *learner_%s_field_names[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_fields; j++) {
+				const char *field_name = learner_spec->params.field_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, field_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_names. */
+		if (learner_spec->params.action_names && learner_spec->params.n_actions) {
+			fprintf(f, "static const char *learner_%s_action_names[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				const char *action_name = learner_spec->params.action_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, action_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_table_entries. */
+		if (learner_spec->params.action_is_for_table_entries &&
+		    learner_spec->params.n_actions) {
+			fprintf(f, "static int learner_%s_action_is_for_table_entries[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				int value = learner_spec->params.action_is_for_table_entries[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_default_entry. */
+		if (learner_spec->params.action_is_for_default_entry &&
+		    learner_spec->params.n_actions) {
+			fprintf(f, "static int learner_%s_action_is_for_default_entry[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				int value = learner_spec->params.action_is_for_default_entry[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* timeout. */
+		if (learner_spec->timeout && learner_spec->n_timeouts) {
+			fprintf(f, "static uint32_t learner_%s_timeout[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->n_timeouts; j++) {
+				uint32_t value = learner_spec->timeout[j];
+
+				fprintf(f, "\t[%d] = %u,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct learner_spec learners[] = {\n");
+
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", learner_spec->name);
+
+		fprintf(f, "\t\t.params = {\n");
+
+		if (learner_spec->params.field_names && learner_spec->params.n_fields) {
+			fprintf(f, "\t\t\t.field_names = learner_%s_field_names,\n",
+				learner_spec->name);
+			fprintf(f, "\t\t\t.n_fields = "
+				"sizeof(learner_%s_field_names) / "
+				"sizeof(learner_%s_field_names[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.field_names = NULL,\n");
+			fprintf(f, "\t\t\t.n_fields = 0,\n");
+		}
+
+		if (learner_spec->params.action_names && learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_names = learner_%s_action_names,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_names = NULL,\n");
+
+		if (learner_spec->params.action_is_for_table_entries &&
+		    learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_table_entries = "
+				"learner_%s_action_is_for_table_entries,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_table_entries = NULL,\n");
+
+		if (learner_spec->params.action_is_for_default_entry &&
+		    learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_default_entry = "
+				"learner_%s_action_is_for_default_entry,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_default_entry = NULL,\n");
+
+		if (learner_spec->params.action_names && learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.n_actions = "
+				"sizeof(learner_%s_action_names) / sizeof(learner_%s_action_names[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.n_actions = NULL,\n");
+
+		if (learner_spec->params.default_action_name)
+			fprintf(f, "\t\t\t.default_action_name = \"%s\",\n",
+				learner_spec->params.default_action_name);
+		else
+			fprintf(f, "\t\t\t.default_action_name = NULL,\n");
+
+		if (learner_spec->params.default_action_args)
+			fprintf(f, "\t\t\t.default_action_args = \"%s\",\n",
+				learner_spec->params.default_action_args);
+		else
+			fprintf(f, "\t\t\t.default_action_args = NULL,\n");
+
+		fprintf(f, "\t\t\t.default_action_is_const = %d,\n",
+			learner_spec->params.default_action_is_const);
+
+		fprintf(f, "\t\t},\n");
+
+		fprintf(f, "\t\t.size = %u,\n", learner_spec->size);
+
+		if (learner_spec->timeout && learner_spec->n_timeouts) {
+			fprintf(f, "\t\t.timeout = learner_%s_timeout,\n", learner_spec->name);
+			fprintf(f, "\t\t\t.n_timeouts = "
+				"sizeof(learner_%s_timeout) / sizeof(learner_%s_timeout[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		} else {
+			fprintf(f, "\t\t.timeout = NULL,\n");
+			fprintf(f, "\t\t\t.n_timeouts = 0,\n");
+		}
+
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* apply. */
+	for (i = 0; i < s->n_apply; i++) {
+		struct apply_spec *apply_spec = &s->apply[i];
+		uint32_t j;
+
+		fprintf(f, "static const char *apply%u_initial_instructions[] = {\n", i);
+
+		for (j = 0; j < apply_spec->n_instructions; j++) {
+			const char *instr = apply_spec->instructions[j];
+
+			fprintf(f, "\t[%d] = \"%s\",\n", j, instr);
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct apply_spec apply[] = {\n");
+
+	for (i = 0; i < s->n_apply; i++) {
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t.instructions = apply%u_initial_instructions,\n", i);
+		fprintf(f, "\t.n_instructions = "
+			"sizeof(apply%u_initial_instructions) / "
+			"sizeof(apply%u_initial_instructions[0]),\n",
+			i,
+			i);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* pipeline. */
+	fprintf(f, "struct pipeline_spec pipeline_spec = {\n");
+	fprintf(f, "\t.extobjs = extobjs,\n");
+	fprintf(f, "\t.structs = structs,\n");
+	fprintf(f, "\t.headers = headers,\n");
+	fprintf(f, "\t.metadata = metadata,\n");
+	fprintf(f, "\t.actions = actions,\n");
+	fprintf(f, "\t.tables = tables,\n");
+	fprintf(f, "\t.selectors = selectors,\n");
+	fprintf(f, "\t.learners = learners,\n");
+	fprintf(f, "\t.regarrays = regarrays,\n");
+	fprintf(f, "\t.metarrays = metarrays,\n");
+	fprintf(f, "\t.apply = apply,\n");
+	fprintf(f, "\t.n_extobjs = sizeof(extobjs) / sizeof(extobjs[0]),\n");
+	fprintf(f, "\t.n_structs = sizeof(structs) / sizeof(structs[0]),\n");
+	fprintf(f, "\t.n_headers = sizeof(headers) / sizeof(headers[0]),\n");
+	fprintf(f, "\t.n_metadata = sizeof(metadata) / sizeof(metadata[0]),\n");
+	fprintf(f, "\t.n_actions = sizeof(actions) / sizeof(actions[0]),\n");
+	fprintf(f, "\t.n_tables = sizeof(tables) / sizeof(tables[0]),\n");
+	fprintf(f, "\t.n_selectors = sizeof(selectors) / sizeof(selectors[0]),\n");
+	fprintf(f, "\t.n_learners = sizeof(learners) / sizeof(learners[0]),\n");
+	fprintf(f, "\t.n_regarrays = sizeof(regarrays) / sizeof(regarrays[0]),\n");
+	fprintf(f, "\t.n_metarrays = sizeof(metarrays) / sizeof(metarrays[0]),\n");
+	fprintf(f, "\t.n_apply = sizeof(apply) / sizeof(apply[0]),\n");
+	fprintf(f, "};\n");
+}
+
 struct pipeline_spec *
 pipeline_spec_parse(FILE *spec,
 		    uint32_t *err_line,
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 4f3a0b5958..707b99ba09 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -206,6 +206,11 @@ struct pipeline_spec {
 
 void
 pipeline_spec_free(struct pipeline_spec *s);
+
+void
+pipeline_spec_codegen(FILE *f,
+		      struct pipeline_spec *s);
+
 struct pipeline_spec *
 pipeline_spec_parse(FILE *spec,
 		    uint32_t *err_line,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 06/17] pipeline: add support for pipeline I/O specification
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (3 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 05/17] pipeline: generate the code for pipeline specification structure Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 07/17] pipeline: add API for pipeline code generation Cristian Dumitrescu
                       ` (11 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add specification data structure and API for the pipeline I/O ports
and related pipeline configuration such as packet mirroring.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 852 +++++++++++++++++++++++++++
 lib/pipeline/rte_swx_pipeline_spec.h |  58 ++
 2 files changed, 910 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index bf21fe17ba..89cf194b19 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -9,6 +9,12 @@
 #include <errno.h>
 
 #include <rte_common.h>
+#include <rte_mempool.h>
+
+#include <rte_swx_port_ethdev.h>
+#include <rte_swx_port_ring.h>
+#include <rte_swx_port_source_sink.h>
+#include <rte_swx_port_fd.h>
 
 #include "rte_swx_pipeline_spec.h"
 
@@ -3566,3 +3572,849 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 	pipeline_spec_free(s);
 	return status;
 }
+
+static void
+port_in_params_free(void *params, const char *port_type)
+{
+	uintptr_t dev_name;
+
+	if (!params || !port_type)
+		return;
+
+	if (!strcmp(port_type, "ethdev")) {
+		struct rte_swx_port_ethdev_reader_params *p = params;
+
+		dev_name = (uintptr_t)p->dev_name;
+	} else if (!strcmp(port_type, "ring")) {
+		struct rte_swx_port_ring_reader_params *p = params;
+
+		dev_name = (uintptr_t)p->name;
+	} else if (!strcmp(port_type, "source")) {
+		struct rte_swx_port_source_params *p = params;
+
+		dev_name = (uintptr_t)p->file_name;
+	} else
+		dev_name = (uintptr_t)NULL;
+
+	free((void *)dev_name);
+	free(params);
+}
+
+static void
+port_out_params_free(void *params, const char *port_type)
+{
+	uintptr_t dev_name;
+
+	if (!params || !port_type)
+		return;
+
+	if (!strcmp(port_type, "ethdev")) {
+		struct rte_swx_port_ethdev_writer_params *p = params;
+
+		dev_name = (uintptr_t)p->dev_name;
+	} else if (!strcmp(port_type, "ring")) {
+		struct rte_swx_port_ring_writer_params *p = params;
+
+		dev_name = (uintptr_t)p->name;
+	} else if (!strcmp(port_type, "sink")) {
+		struct rte_swx_port_sink_params *p = params;
+
+		dev_name = (uintptr_t)p->file_name;
+	} else
+		dev_name = (uintptr_t)NULL;
+
+	free((void *)dev_name);
+	free(params);
+}
+
+void
+pipeline_iospec_free(struct pipeline_iospec *s)
+{
+	uint32_t i;
+
+	if (!s)
+		return;
+
+	/* Input ports. */
+	for (i = 0; i < s->n_ports_in; i++) {
+		uintptr_t name = (uintptr_t)s->port_in_type[i];
+
+		port_in_params_free(s->port_in_params[i], s->port_in_type[i]);
+		free((void *)name);
+	}
+
+	free(s->port_in_type);
+	free(s->port_in_params);
+
+	/* Output ports. */
+	for (i = 0; i < s->n_ports_out; i++) {
+		uintptr_t name = (uintptr_t)s->port_out_type[i];
+
+		port_out_params_free(s->port_out_params[i], s->port_out_type[i]);
+		free((void *)name);
+	}
+
+	free(s->port_out_type);
+	free(s->port_out_params);
+
+	free(s);
+}
+
+static int
+mirroring_parse(struct rte_swx_pipeline_mirroring_params *p,
+		char **tokens,
+		uint32_t n_tokens,
+		const char **err_msg)
+{
+	char *token;
+
+	if ((n_tokens != 4) || strcmp(tokens[0], "slots") || strcmp(tokens[2], "sessions")) {
+		if (err_msg)
+		    *err_msg = "Invalid statement.";
+		return -EINVAL;
+	}
+
+	/* <n_slots>. */
+	token = tokens[1];
+	p->n_slots = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <n_slots> parameter.";
+		return -EINVAL;
+	}
+
+	/* <n_sessions>. */
+	token = tokens[3];
+	p->n_sessions = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <n_sessions> parameter.";
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+port_in_ethdev_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_ethdev_reader_params *p = NULL;
+	char *token, *dev_name = NULL;
+	uint32_t queue_id, burst_size;
+
+	if ((n_tokens != 5) || strcmp(tokens[1], "rxq") || strcmp(tokens[3], "bsz")) {
+		if (err_msg)
+		    *err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <queue_id>. */
+	token = tokens[2];
+	queue_id = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <queue_id> parameter.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[4];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	dev_name = strdup(tokens[0]);
+	p = malloc(sizeof(struct rte_swx_port_ethdev_reader_params));
+	if (!dev_name || !p) {
+		free(dev_name);
+		free(p);
+
+		if (err_msg)
+		    *err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->dev_name = dev_name;
+	p->queue_id = queue_id;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_in_ring_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_ring_reader_params *p = NULL;
+	char *token, *name = NULL;
+	uint32_t burst_size;
+
+	if ((n_tokens != 3) || strcmp(tokens[1], "bsz")) {
+		if (err_msg)
+		    *err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[2];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	name = strdup(tokens[0]);
+	p = malloc(sizeof(struct rte_swx_port_ring_reader_params));
+	if (!name || !p) {
+		free(name);
+		free(p);
+
+		if (err_msg)
+		    *err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->name = name;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_in_source_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_source_params *p = NULL;
+	struct rte_mempool *pool = NULL;
+	char *token, *file_name = NULL;
+	uint32_t n_loops, n_pkts_max;
+
+	if ((n_tokens != 8) ||
+	    strcmp(tokens[0], "mempool") ||
+	    strcmp(tokens[2], "file") ||
+	    strcmp(tokens[4], "loop") ||
+	    strcmp(tokens[6], "packets")) {
+		if (err_msg)
+		    *err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <mempool_name>. */
+	pool = rte_mempool_lookup(tokens[1]);
+	if (!pool) {
+		if (err_msg)
+		    *err_msg = "Invalid <mempool_name> parameter.";
+		return NULL;
+	}
+
+	/* <n_loops>. */
+	token = tokens[5];
+	n_loops = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <n_loops> parameter.";
+		return NULL;
+	}
+
+	/* <n_pkts_max>. */
+	token = tokens[7];
+	n_pkts_max = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <n_pkts_max> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	file_name = strdup(tokens[3]);
+	p = malloc(sizeof(struct rte_swx_port_source_params));
+	if (!file_name || !p) {
+		free(file_name);
+		free(p);
+
+		if (err_msg)
+		    *err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->pool = pool;
+	p->file_name = file_name;
+	p->n_loops = n_loops;
+	p->n_pkts_max = n_pkts_max;
+
+	return p;
+}
+
+static void *
+port_in_fd_parse(char **tokens,
+		 uint32_t n_tokens,
+		 const char **err_msg)
+{
+	struct rte_swx_port_fd_reader_params *p = NULL;
+	struct rte_mempool *mempool = NULL;
+	char *token;
+	uint32_t mtu, burst_size;
+	int fd;
+
+	if ((n_tokens != 7) ||
+	    strcmp(tokens[1], "mtu") ||
+	    strcmp(tokens[3], "mempool") ||
+	    strcmp(tokens[5], "bsz")) {
+		if (err_msg)
+		    *err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <file_descriptor>. */
+	token = tokens[0];
+	fd = strtol(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <file_descriptor> parameter.";
+		return NULL;
+	}
+
+	/* <mtu>. */
+	token = tokens[2];
+	mtu = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <mtu> parameter.";
+		return NULL;
+	}
+
+	/* <mempool_name>. */
+	mempool = rte_mempool_lookup(tokens[4]);
+	if (!mempool) {
+		if (err_msg)
+		    *err_msg = "Invalid <mempool_name> parameter.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[6];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	p = malloc(sizeof(struct rte_swx_port_fd_reader_params));
+	if (!p) {
+		if (err_msg)
+		    *err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->fd = fd;
+	p->mtu = mtu;
+	p->mempool = mempool;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_out_ethdev_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_ethdev_writer_params *p = NULL;
+	char *token, *dev_name = NULL;
+	uint32_t queue_id, burst_size;
+
+	if ((n_tokens != 5) || strcmp(tokens[1], "txq") || strcmp(tokens[3], "bsz")) {
+		if (err_msg)
+		    *err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <queue_id>. */
+	token = tokens[2];
+	queue_id = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <queue_id> parameter.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[4];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	dev_name = strdup(tokens[0]);
+	p = malloc(sizeof(struct rte_swx_port_ethdev_writer_params));
+	if (!dev_name || !p) {
+		free(dev_name);
+		free(p);
+
+		if (err_msg)
+		    *err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->dev_name = dev_name;
+	p->queue_id = queue_id;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_out_ring_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_ring_writer_params *p = NULL;
+	char *token, *name = NULL;
+	uint32_t burst_size;
+
+	if ((n_tokens != 3) || strcmp(tokens[1], "bsz")) {
+		if (err_msg)
+		    *err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[2];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	name = strdup(tokens[0]);
+	p = malloc(sizeof(struct rte_swx_port_ring_writer_params));
+	if (!name || !p) {
+		free(name);
+		free(p);
+
+		if (err_msg)
+		    *err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->name = name;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_out_sink_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_sink_params *p = NULL;
+	char *file_name = NULL;
+	int file_name_valid = 0;
+
+	if ((n_tokens != 2) || strcmp(tokens[0], "file")){
+		if (err_msg)
+		    *err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	if (strcmp(tokens[1], "none")) {
+		file_name_valid = 1;
+		file_name = strdup(tokens[1]);
+	}
+
+	p = malloc(sizeof(struct rte_swx_port_ring_writer_params));
+	if ((file_name_valid && !file_name) || !p) {
+		free(file_name);
+		free(p);
+
+		if (err_msg)
+		    *err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->file_name = file_name;
+
+	return p;
+}
+
+static void *
+port_out_fd_parse(char **tokens,
+		  uint32_t n_tokens,
+		  const char **err_msg)
+{
+	struct rte_swx_port_fd_writer_params *p = NULL;
+	char *token;
+	uint32_t burst_size;
+	int fd;
+
+	if ((n_tokens != 3) || strcmp(tokens[1], "bsz")) {
+		if (err_msg)
+		    *err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <file_descriptor>. */
+	token = tokens[0];
+	fd = strtol(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <file_descriptor> parameter.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[2];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+		    *err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	p = malloc(sizeof(struct rte_swx_port_fd_writer_params));
+	if (!p) {
+		if (err_msg)
+		    *err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->fd = fd;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+struct pipeline_iospec *
+pipeline_iospec_parse(FILE *spec,
+		      uint32_t *err_line,
+		      const char **err_msg)
+{
+	struct pipeline_iospec *s = NULL;
+	uint32_t n_lines = 0;
+
+	/* Check the input arguments. */
+	if (!spec) {
+		if (err_line)
+			*err_line = n_lines;
+		if (err_msg)
+			*err_msg = "Invalid input argument.";
+		goto error;
+	}
+
+	/* Memory allocation. */
+	s = calloc(sizeof(struct pipeline_iospec), 1);
+	if (!s) {
+		if (err_line)
+			*err_line = n_lines;
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		goto error;
+	}
+
+	/* Initialize with the defaut values. */
+	s->mirroring_params.n_slots = RTE_SWX_PACKET_MIRRORING_SLOTS_DEFAULT;
+	s->mirroring_params.n_sessions = RTE_SWX_PACKET_MIRRORING_SESSIONS_DEFAULT;
+
+	for (n_lines = 1; ; n_lines++) {
+		char line[MAX_LINE_LENGTH];
+		char *tokens[MAX_TOKENS], *ptr = line;
+		uint32_t n_tokens = 0;
+
+		/* Read next line. */
+		if (!fgets(line, sizeof(line), spec))
+			break;
+
+		/* Parse the line into tokens. */
+		for ( ; ; ) {
+			char *token;
+
+			/* Get token. */
+			token = strtok_r(ptr, " \f\n\r\t\v", &ptr);
+			if (!token)
+				break;
+
+			/* Handle comments. */
+			if ((token[0] == '#') ||
+			    (token[0] == ';') ||
+			    ((token[0] == '/') && (token[1] == '/'))) {
+				break;
+			}
+
+			/* Handle excessively long lines. */
+			if (n_tokens >= RTE_DIM(tokens)) {
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Too many tokens.";
+				goto error;
+			}
+
+			/* Handle excessively long tokens. */
+			if (strnlen(token, RTE_SWX_NAME_SIZE) >=
+			    RTE_SWX_NAME_SIZE) {
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Token too big.";
+				goto error;
+			}
+
+			/* Save token. */
+			tokens[n_tokens] = token;
+			n_tokens++;
+		}
+
+		/* Handle empty lines. */
+		if (!n_tokens)
+			continue;
+
+		/* mirroring. */
+		if ((n_tokens >= 1) && !strcmp(tokens[0], "mirroring")) {
+			int status = 0;
+
+			status = mirroring_parse(&s->mirroring_params,
+						 &tokens[1],
+						 n_tokens - 1,
+						 err_msg);
+			if (status) {
+				if (err_line)
+					*err_line = n_lines;
+				goto error;
+			}
+
+			continue;
+		}
+
+		/* port in. */
+		if ((n_tokens >= 4) && !strcmp(tokens[0], "port") && !strcmp(tokens[1], "in")) {
+			char *token = tokens[2];
+			uint32_t *new_id = NULL;
+			const char **new_type = NULL, *port_type = NULL;
+			void **new_params = NULL, *p = NULL;
+			uint32_t port_id;
+
+			/* <port_id>. */
+			port_id = strtoul(token, &token, 0);
+			if (token[0]) {
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Invalid port ID.";
+				goto error;
+			}
+
+			/* <port_type>. */
+			if (!strcmp(tokens[3], "ethdev"))
+				p = port_in_ethdev_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "ring"))
+				p = port_in_ring_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "source"))
+				p = port_in_source_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "fd"))
+				p = port_in_fd_parse(&tokens[4], n_tokens - 4, err_msg);
+			else {
+				p = NULL;
+				if (err_msg)
+					*err_msg = "Invalid port type.";
+			}
+
+			if (!p) {
+				if (err_line)
+					*err_line = n_lines;
+				goto error;
+			}
+
+			/* New port. */
+			port_type = strdup(tokens[3]);
+			new_id = realloc(s->port_in_id,
+					 (s->n_ports_in + 1) * sizeof(uint32_t));
+			new_type = realloc(s->port_in_type,
+					   (s->n_ports_in + 1) * sizeof(char *));
+			new_params = realloc(s->port_in_params,
+					     (s->n_ports_in + 1) * sizeof(void *));
+			if (!port_type || !new_id || !new_type || !new_params) {
+				uintptr_t pt = (uintptr_t)port_type;
+
+				port_in_params_free(p, tokens[3]);
+				free((void *)pt);
+				free(new_id);
+				free(new_type);
+				free(new_params);
+
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Memory allocation failed.";
+				goto error;
+			}
+
+			s->port_in_id = new_id;
+			s->port_in_type = new_type;
+			s->port_in_params = new_params;
+
+			s->port_in_id[s->n_ports_in] = port_id;
+			s->port_in_type[s->n_ports_in] = port_type;
+			s->port_in_params[s->n_ports_in] = p;
+			s->n_ports_in++;
+
+			continue;
+		}
+
+		/* port out. */
+		if ((n_tokens >= 4) && !strcmp(tokens[0], "port") && !strcmp(tokens[1], "out")) {
+			char *token = tokens[2];
+			uint32_t *new_id = NULL;
+			const char **new_type = NULL, *port_type = NULL;
+			void **new_params = NULL, *p = NULL;
+			uint32_t port_id;
+
+			/* <port_id>. */
+			port_id = strtoul(token, &token, 0);
+			if (token[0]) {
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Invalid port ID.";
+				goto error;
+			}
+
+			/* <port_type>. */
+			if (!strcmp(tokens[3], "ethdev"))
+				p = port_out_ethdev_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "ring"))
+				p = port_out_ring_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "sink"))
+				p = port_out_sink_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "fd"))
+				p = port_out_fd_parse(&tokens[4], n_tokens - 4, err_msg);
+			else {
+				p = NULL;
+				if (err_msg)
+					*err_msg = "Invalid port type.";
+			}
+
+			if (!p) {
+				if (err_line)
+					*err_line = n_lines;
+				goto error;
+			}
+
+			/* New port. */
+			port_type = strdup(tokens[3]);
+			new_id = realloc(s->port_out_id,
+					 (s->n_ports_out + 1) * sizeof(uint32_t));
+			new_type = realloc(s->port_out_type,
+					   (s->n_ports_out + 1) * sizeof(char *));
+			new_params = realloc(s->port_out_params,
+					     (s->n_ports_out + 1) * sizeof(void *));
+			if (!port_type || !new_id || !new_type || !new_params) {
+				uintptr_t pt = (uintptr_t)port_type;
+
+				port_out_params_free(p, tokens[3]);
+				free((void *)pt);
+				free(new_id);
+				free(new_type);
+				free(new_params);
+
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Memory allocation failed.";
+				goto error;
+			}
+
+			s->port_out_id = new_id;
+			s->port_out_type = new_type;
+			s->port_out_params = new_params;
+
+			s->port_out_id[s->n_ports_out] = port_id;
+			s->port_out_type[s->n_ports_out] = port_type;
+			s->port_out_params[s->n_ports_out] = p;
+			s->n_ports_out++;
+
+			continue;
+		}
+
+		/* Anything else. */
+		if (err_line)
+			*err_line = n_lines;
+		if (err_msg)
+			*err_msg = "Unknown I/O statement.";
+		goto error;
+	}
+
+	return s;
+
+error:
+	pipeline_iospec_free(s);
+
+	return NULL;
+}
+
+int
+pipeline_iospec_configure(struct rte_swx_pipeline *p,
+			  struct pipeline_iospec *s,
+			  const char **err_msg)
+{
+	uint32_t i;
+	int status = 0;
+
+	/* Check input arguments. */
+	if (!p || !s) {
+		if (err_msg)
+			*err_msg = "Invalid input argument";
+		return -EINVAL;
+	}
+
+	/* Mirroring. */
+	status = rte_swx_pipeline_mirroring_config(p, &s->mirroring_params);
+	if (status) {
+		if (err_msg)
+			*err_msg = "Pipeline mirroring configuration error.";
+		return status;
+	}
+
+	/* Input ports. */
+	for (i = 0; i < s->n_ports_in; i++) {
+		status = rte_swx_pipeline_port_in_config(p,
+							 i,
+							 s->port_in_type[i],
+							 s->port_in_params[i]);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Pipeline input port configuration error.";
+			return status;
+		}
+	}
+
+	/* Output ports. */
+	for (i = 0; i < s->n_ports_out; i++) {
+		status = rte_swx_pipeline_port_out_config(p,
+							  i,
+							  s->port_out_type[i],
+							  s->port_out_params[i]);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Pipeline output port configuration error.";
+			return status;
+		}
+	}
+
+	return 0;
+}
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 707b99ba09..62ac4ecfc4 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -1,6 +1,13 @@
 /* SPDX-License-Identifier: BSD-3-Clause
  * Copyright(c) 2022 Intel Corporation
  */
+#ifndef __INCLUDE_RTE_SWX_PIPELINE_SPEC_H__
+#define __INCLUDE_RTE_SWX_PIPELINE_SPEC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include <stdint.h>
 #include <stdio.h>
 
@@ -204,6 +211,38 @@ struct pipeline_spec {
 	uint32_t n_apply;
 };
 
+/*
+ * Mirroring:
+ *      mirroring slots <n_slots> sessions <n_sessions>
+ *
+ * Input ports:
+ *      port in <port_id> ethdev <ethdev_name> rxq <queue_id> bsz <burst_size>
+ *      port in <port_id> ring <ring_name> bsz <burst_size>
+ *      port in <port_id> source mempool <mempool_name> file <file_name> loop <n_loops>
+ *                               packets <n_pkts_max>
+ *      port in <port_id> fd <file_descriptor> mtu <mtu> mempool <mempool_name> bsz <burst_size>
+ *
+ * Output ports:
+ *      port out <port_id> ethdev <ethdev_name> txq <queue_id> bsz <burst_size>
+ *      port out <port_id> ring <ring_name> bsz <burst_size>
+ *      port out <port_id> sink file <file_name> | none
+ *      port out <port_id> fd <file_descriptor> bsz <burst_size>
+ */
+struct pipeline_iospec {
+	struct rte_swx_pipeline_mirroring_params mirroring_params;
+
+	uint32_t *port_in_id;
+	const char **port_in_type;
+	void **port_in_params;
+
+	uint32_t *port_out_id;
+	const char **port_out_type;
+	void **port_out_params;
+
+	uint32_t n_ports_in;
+	uint32_t n_ports_out;
+};
+
 void
 pipeline_spec_free(struct pipeline_spec *s);
 
@@ -220,3 +259,22 @@ int
 pipeline_spec_configure(struct rte_swx_pipeline *p,
 			struct pipeline_spec *s,
 			const char **err_msg);
+
+void
+pipeline_iospec_free(struct pipeline_iospec *s);
+
+struct pipeline_iospec *
+pipeline_iospec_parse(FILE *spec,
+		      uint32_t *err_line,
+		      const char **err_msg);
+
+int
+pipeline_iospec_configure(struct rte_swx_pipeline *p,
+			  struct pipeline_iospec *s,
+			  const char **err_msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 07/17] pipeline: add API for pipeline code generation
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (4 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 06/17] pipeline: add support for pipeline I/O specification Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 08/17] pipeline: add API for shared library-based pipeline build Cristian Dumitrescu
                       ` (10 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Previously, the C code generation for the pipeline was hidden under
the hood; now, we make this an explicit API operation. Besides the
functions for the pipeline actions and the pipeline instructions,
the generated C source code now includes the pipeline specification
structure required for the pipeline configuration operations.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline.c | 94 +++++++++++++++++++++++++++++++++
 lib/pipeline/rte_swx_pipeline.h | 25 +++++++++
 lib/pipeline/version.map        |  1 +
 3 files changed, 120 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index c8ccded4f8..dd5f7107fa 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -20,6 +20,7 @@
 #include <rte_swx_table_wm.h>
 
 #include "rte_swx_pipeline_internal.h"
+#include "rte_swx_pipeline_spec.h"
 
 #define CHECK(condition, err_code)                                             \
 do {                                                                           \
@@ -13581,3 +13582,96 @@ pipeline_compile(struct rte_swx_pipeline *p)
 
 	return status;
 }
+
+int
+rte_swx_pipeline_codegen(FILE *spec_file,
+			 FILE *code_file,
+			 uint32_t *err_line,
+			 const char **err_msg)
+
+{
+	struct rte_swx_pipeline *p = NULL;
+	struct pipeline_spec *s = NULL;
+	struct instruction_group_list *igl = NULL;
+	struct action *a;
+	int status = 0;
+
+	/* Check input arguments. */
+	if (!spec_file || !code_file) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Invalid input argument.";
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Pipeline configuration. */
+	s = pipeline_spec_parse(spec_file, err_line, err_msg);
+	if (!s) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	status = rte_swx_pipeline_config(&p, NULL, 0);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Pipeline configuration error.";
+		goto free;
+	}
+
+	status = pipeline_spec_configure(p, s, err_msg);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		goto free;
+	}
+
+	/*
+	 * Pipeline code generation.
+	 */
+
+	/* Instruction Group List (IGL) computation: the pipeline configuration must be done first,
+	 * but there is no need for the pipeline build to be done as well.
+	 */
+	igl = instruction_group_list_create(p);
+	if (!igl) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		status = -ENOMEM;
+		goto free;
+	}
+
+	/* Header file inclusion. */
+	fprintf(code_file, "#include \"rte_swx_pipeline_internal.h\"\n");
+	fprintf(code_file, "#include \"rte_swx_pipeline_spec.h\"\n\n");
+
+	/* Code generation for the pipeline specification. */
+	pipeline_spec_codegen(code_file, s);
+	fprintf(code_file, "\n");
+
+	/* Code generation for the action instructions. */
+	TAILQ_FOREACH(a, &p->actions, node) {
+		fprintf(code_file, "/**\n * Action %s\n */\n\n", a->name);
+
+		action_data_codegen(a, code_file);
+		fprintf(code_file, "\n");
+
+		action_instr_codegen(a, code_file);
+		fprintf(code_file, "\n");
+	}
+
+	/* Code generation for the pipeline instructions. */
+	instruction_group_list_codegen(igl, p, code_file);
+
+free:
+	instruction_group_list_free(igl);
+	rte_swx_pipeline_free(p);
+	pipeline_spec_free(s);
+
+	return status;
+}
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index ef50a0fa70..724607b87c 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -957,6 +957,31 @@ __rte_experimental
 int
 rte_swx_pipeline_build(struct rte_swx_pipeline *p);
 
+/**
+ * Pipeline C code generate based on input specification file
+ *
+ * @param[in] spec_file
+ *   Pipeline specification file (.spec) provided as input.
+ * @param[in] code_file
+ *   Pipeline C language file (.c) to be generated.
+ * @param[out] err_line
+ *   In case of error and non-NULL, the line number within the *spec* file where
+ *   the error occurred. The first line number in the file is 1.
+ * @param[out] err_msg
+ *   In case of error and non-NULL, the error message.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Resource with the same name already exists.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_codegen(FILE *spec_file,
+			 FILE *code_file,
+			 uint32_t *err_line,
+			 const char **err_msg);
+
 /**
  * Pipeline build from specification file
  *
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 50029aadcf..8d95005a5b 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -148,5 +148,6 @@ EXPERIMENTAL {
 
 	#added in 22.11
 	rte_swx_ctl_pipeline_find;
+	rte_swx_pipeline_codegen;
 	rte_swx_pipeline_find;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 08/17] pipeline: add API for shared library-based pipeline build
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (5 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 07/17] pipeline: add API for pipeline code generation Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 09/17] examples/pipeline: add CLI command for pipeline code generation Cristian Dumitrescu
                       ` (9 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Previously, the pipeline build operation was done based on the
specification file (typically produced by the P4 compiler), then the C
code with optimized functions for the pipeline actions and
instructions was generated, built into a shared object library, loaded
and installed into the pipeline in a completely hardcoded and
non-customizable way.

Now, this process is split into three explicit stages:
i) code generation (specification file -> C file);
ii) code build (C file -> shared object library);
iii) code installation (library load into the pipeline).

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c              |  85 ++++---
 lib/pipeline/rte_swx_pipeline.c      | 319 +++++++++++----------------
 lib/pipeline/rte_swx_pipeline.h      |  38 ++--
 lib/pipeline/rte_swx_pipeline_spec.c |  51 -----
 lib/pipeline/version.map             |   2 +-
 5 files changed, 208 insertions(+), 287 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index ad553f19ab..f0285675b3 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -984,55 +984,88 @@ cmd_pipeline_port_out(char **tokens,
 }
 
 static const char cmd_pipeline_build_help[] =
-"pipeline <pipeline_name> build <spec_file>\n";
+"pipeline <pipeline_name> build lib <lib_file> io <iospec_file> numa <numa_node>\n";
 
 static void
 cmd_pipeline_build(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p = NULL;
-	FILE *spec = NULL;
-	uint32_t err_line;
-	const char *err_msg;
-	int status;
+	struct rte_swx_pipeline *p = NULL;
+	struct rte_swx_ctl_pipeline *ctl = NULL;
+	char *pipeline_name, *lib_file_name, *iospec_file_name;
+	FILE *iospec_file = NULL;
+	uint32_t numa_node = 0;
+	int status = 0;
 
-	if (n_tokens != 4) {
+	/* Parsing. */
+	if (n_tokens != 9) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || p->ctl) {
-		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+	pipeline_name = tokens[1];
+
+	if (strcmp(tokens[2], "build")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "build");
 		return;
 	}
 
-	spec = fopen(tokens[3], "r");
-	if (!spec) {
-		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
+	if (strcmp(tokens[3], "lib")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "lib");
 		return;
 	}
 
-	status = rte_swx_pipeline_build_from_spec(p->p,
-		spec,
-		&err_line,
-		&err_msg);
-	fclose(spec);
-	if (status) {
-		snprintf(out, out_size, "Error %d at line %u: %s\n.",
-			status, err_line, err_msg);
+	lib_file_name = tokens[4];
+
+	if (strcmp(tokens[5], "io")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "io");
 		return;
 	}
 
-	p->ctl = rte_swx_ctl_pipeline_create(p->p);
-	if (!p->ctl) {
-		snprintf(out, out_size, "Pipeline control create failed.");
-		rte_swx_pipeline_free(p->p);
+	iospec_file_name = tokens[6];
+
+	if (strcmp(tokens[7], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
+
+	if (parser_read_uint32(&numa_node, tokens[8])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
+		return;
+	}
+
+	/* I/O spec file open. */
+	iospec_file = fopen(iospec_file_name, "r");
+	if (!iospec_file) {
+		snprintf(out, out_size, "Cannot open file \"%s\".\n", iospec_file_name);
+		return;
+	}
+
+	status = rte_swx_pipeline_build_from_lib(&p,
+						 pipeline_name,
+						 lib_file_name,
+						 iospec_file,
+						 (int)numa_node);
+	if (status) {
+		snprintf(out, out_size, "Pipeline build failed (%d).", status);
+		goto free;
+	}
+
+	ctl = rte_swx_ctl_pipeline_create(p);
+	if (!ctl) {
+		snprintf(out, out_size, "Pipeline control create failed.");
+		goto free;
+	}
+
+free:
+	if (status)
+		rte_swx_pipeline_free(p);
+
+	if (iospec_file)
+		fclose(iospec_file);
 }
 
 static void
diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index dd5f7107fa..12e156b00b 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -9911,9 +9911,6 @@ rte_swx_pipeline_instructions_config(struct rte_swx_pipeline *p,
 	return 0;
 }
 
-static int
-pipeline_compile(struct rte_swx_pipeline *p);
-
 int
 rte_swx_pipeline_build(struct rte_swx_pipeline *p)
 {
@@ -10003,8 +10000,6 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
 
 	p->build_done = 1;
 
-	pipeline_compile(p);
-
 	return 0;
 
 error:
@@ -13327,160 +13322,6 @@ instruction_group_list_custom_instructions_count(struct instruction_group_list *
 	return n_custom_instr;
 }
 
-static int
-pipeline_codegen(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
-{
-	struct action *a;
-	FILE *f = NULL;
-
-	/* Create the .c file. */
-	f = fopen("/tmp/pipeline.c", "w");
-	if (!f)
-		return -EIO;
-
-	/* Include the .h file. */
-	fprintf(f, "#include \"rte_swx_pipeline_internal.h\"\n");
-
-	/* Add the code for each action. */
-	TAILQ_FOREACH(a, &p->actions, node) {
-		fprintf(f, "/**\n * Action %s\n */\n\n", a->name);
-
-		action_data_codegen(a, f);
-
-		fprintf(f, "\n");
-
-		action_instr_codegen(a, f);
-
-		fprintf(f, "\n");
-	}
-
-	/* Add the pipeline code. */
-	instruction_group_list_codegen(igl, p, f);
-
-	/* Close the .c file. */
-	fclose(f);
-
-	return 0;
-}
-
-#ifndef RTE_SWX_PIPELINE_CMD_MAX_SIZE
-#define RTE_SWX_PIPELINE_CMD_MAX_SIZE 4096
-#endif
-
-static int
-pipeline_libload(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
-{
-	struct action *a;
-	struct instruction_group *g;
-	char *dir_in, *buffer = NULL;
-	const char *dir_out;
-	int status = 0;
-
-	/* Get the environment variables. */
-	dir_in = getenv("RTE_INSTALL_DIR");
-	if (!dir_in) {
-		status = -EINVAL;
-		goto free;
-	}
-
-	dir_out = "/tmp";
-
-	/* Memory allocation for the command buffer. */
-	buffer = malloc(RTE_SWX_PIPELINE_CMD_MAX_SIZE);
-	if (!buffer) {
-		status = -ENOMEM;
-		goto free;
-	}
-
-	snprintf(buffer,
-		 RTE_SWX_PIPELINE_CMD_MAX_SIZE,
-		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s/pipeline.o %s/pipeline.c "
-		 "-I %s/lib/pipeline "
-		 "-I %s/lib/eal/include "
-		 "-I %s/lib/eal/x86/include "
-		 "-I %s/lib/eal/include/generic "
-		 "-I %s/lib/meter "
-		 "-I %s/lib/port "
-		 "-I %s/lib/table "
-		 "-I %s/lib/pipeline "
-		 "-I %s/config "
-		 "-I %s/build "
-		 "-I %s/lib/eal/linux/include "
-		 ">%s/pipeline.log 2>&1 "
-		 "&& "
-		 "gcc -shared %s/pipeline.o -o %s/libpipeline.so "
-		 ">>%s/pipeline.log 2>&1",
-		 dir_out,
-		 dir_out,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_out,
-		 dir_out,
-		 dir_out,
-		 dir_out);
-
-	/* Build the shared object library. */
-	status = system(buffer);
-	if (status)
-		goto free;
-
-	/* Open library. */
-	snprintf(buffer,
-		 RTE_SWX_PIPELINE_CMD_MAX_SIZE,
-		 "%s/libpipeline.so",
-		 dir_out);
-
-	p->lib = dlopen(buffer, RTLD_LAZY);
-	if (!p->lib) {
-		status = -EIO;
-		goto free;
-	}
-
-	/* Get the action function symbols. */
-	TAILQ_FOREACH(a, &p->actions, node) {
-		snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "action_%s_run", a->name);
-
-		p->action_funcs[a->id] = dlsym(p->lib, buffer);
-		if (!p->action_funcs[a->id]) {
-			status = -EINVAL;
-			goto free;
-		}
-	}
-
-	/* Get the pipeline function symbols. */
-	TAILQ_FOREACH(g, igl, node) {
-		if (g->first_instr_id == g->last_instr_id)
-			continue;
-
-		snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "pipeline_func_%u", g->group_id);
-
-		g->func = dlsym(p->lib, buffer);
-		if (!g->func) {
-			status = -EINVAL;
-			goto free;
-		}
-	}
-
-free:
-	if (status && p->lib) {
-		dlclose(p->lib);
-		p->lib = NULL;
-	}
-
-	free(buffer);
-
-	return status;
-}
-
 static int
 pipeline_adjust_check(struct rte_swx_pipeline *p __rte_unused,
 		      struct instruction_group_list *igl)
@@ -13548,41 +13389,6 @@ pipeline_adjust(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
 	instr_jmp_resolve(p->instructions, p->instruction_data, p->n_instructions);
 }
 
-static int
-pipeline_compile(struct rte_swx_pipeline *p)
-{
-	struct instruction_group_list *igl = NULL;
-	int status = 0;
-
-	igl = instruction_group_list_create(p);
-	if (!igl) {
-		status = -ENOMEM;
-		goto free;
-	}
-
-	/* Code generation. */
-	status = pipeline_codegen(p, igl);
-	if (status)
-		goto free;
-
-	/* Build and load the shared object library. */
-	status = pipeline_libload(p, igl);
-	if (status)
-		goto free;
-
-	/* Adjust instructions. */
-	status = pipeline_adjust_check(p, igl);
-	if (status)
-		goto free;
-
-	pipeline_adjust(p, igl);
-
-free:
-	instruction_group_list_free(igl);
-
-	return status;
-}
-
 int
 rte_swx_pipeline_codegen(FILE *spec_file,
 			 FILE *code_file,
@@ -13675,3 +13481,128 @@ rte_swx_pipeline_codegen(FILE *spec_file,
 
 	return status;
 }
+
+int
+rte_swx_pipeline_build_from_lib(struct rte_swx_pipeline **pipeline,
+				const char *name,
+				const char *lib_file_name,
+				FILE *iospec_file,
+				int numa_node)
+{
+	struct rte_swx_pipeline *p = NULL;
+	void *lib = NULL;
+	struct pipeline_iospec *sio = NULL;
+	struct pipeline_spec *s = NULL;
+	struct instruction_group_list *igl = NULL;
+	struct action *a;
+	struct instruction_group *g;
+	int status = 0;
+
+	/* Check input arguments. */
+	if (!pipeline ||
+	    !name ||
+	    !name[0] ||
+	    !lib_file_name ||
+	    !lib_file_name[0] ||
+	    !iospec_file) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Open the library. */
+	lib = dlopen(lib_file_name, RTLD_LAZY);
+	if (!lib) {
+		status = -EIO;
+		goto free;
+	}
+
+	/* Get the pipeline specification structures. */
+	s = dlsym(lib, "pipeline_spec");
+	if (!s) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	sio = pipeline_iospec_parse(iospec_file, NULL, NULL);
+	if (!sio) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Pipeline configuration based on the specification structures. */
+	status = rte_swx_pipeline_config(&p, name, numa_node);
+	if (status)
+		goto free;
+
+	status = pipeline_iospec_configure(p, sio, NULL);
+	if (status)
+		goto free;
+
+	status = pipeline_spec_configure(p, s, NULL);
+	if (status)
+		goto free;
+
+	/* Pipeline build. */
+	status = rte_swx_pipeline_build(p);
+	if (status)
+		goto free;
+
+	/* Action instructions. */
+	TAILQ_FOREACH(a, &p->actions, node) {
+		char name[RTE_SWX_NAME_SIZE * 2];
+
+		snprintf(name, sizeof(name), "action_%s_run", a->name);
+
+		p->action_funcs[a->id] = dlsym(lib, name);
+		if (!p->action_funcs[a->id]) {
+			status = -EINVAL;
+			goto free;
+		}
+	}
+
+	/* Pipeline instructions. */
+	igl = instruction_group_list_create(p);
+	if (!igl) {
+		status = -ENOMEM;
+		goto free;
+	}
+
+	TAILQ_FOREACH(g, igl, node) {
+		char name[RTE_SWX_NAME_SIZE * 2];
+
+		if (g->first_instr_id == g->last_instr_id)
+			continue;
+
+		snprintf(name, sizeof(name), "pipeline_func_%u", g->group_id);
+
+		g->func = dlsym(lib, name);
+		if (!g->func) {
+			status = -EINVAL;
+			goto free;
+		}
+	}
+
+	status = pipeline_adjust_check(p, igl);
+	if (status)
+		goto free;
+
+	pipeline_adjust(p, igl);
+
+	p->lib = lib;
+
+	*pipeline = p;
+
+free:
+	instruction_group_list_free(igl);
+
+	pipeline_iospec_free(sio);
+
+	if (status) {
+		rte_swx_pipeline_free(p);
+
+		if (lib)
+			dlclose(lib);
+	}
+
+	return status;
+}
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index 724607b87c..9c629d4118 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -983,30 +983,38 @@ rte_swx_pipeline_codegen(FILE *spec_file,
 			 const char **err_msg);
 
 /**
- * Pipeline build from specification file
+ * Pipeline build from shared object library
  *
- * @param[in] p
- *   Pipeline handle.
- * @param[in] spec
- *   Pipeline specification file.
- * @param[out] err_line
- *   In case of error and non-NULL, the line number within the *spec* file where
- *   the error occurred. The first line number in the file is 1.
- * @param[out] err_msg
- *   In case of error and non-NULL, the error message.
+ * The shared object library must be built from the C language source code file
+ * previously generated by the rte_swx_pipeline_codegen() API function.
+ *
+ * The pipeline I/O specification file defines the I/O ports of the pipeline.
+ *
+ * @param[out] p
+ *   Pipeline handle. Must point to valid memory. Contains valid pipeline handle
+ *   when the function returns successfully.
+ * @param[in] name
+ *   Pipeline unique name.
+ * @param[in] lib_file_name
+ *   Shared object library file name.
+ * @param[in] iospec_file
+ *   Pipeline I/O specification file.
+ * @param[in] numa_node
+ *   Non-Uniform Memory Access (NUMA) node.
  * @return
  *   0 on success or the following error codes otherwise:
  *   -EINVAL: Invalid argument;
  *   -ENOMEM: Not enough space/cannot allocate memory;
- *   -EEXIST: Resource with the same name already exists;
+ *   -EEXIST: Pipeline with this name already exists;
  *   -ENODEV: Extern object or table creation error.
  */
 __rte_experimental
 int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec,
-				 uint32_t *err_line,
-				 const char **err_msg);
+rte_swx_pipeline_build_from_lib(struct rte_swx_pipeline **p,
+				const char *name,
+				const char *lib_file_name,
+				FILE *iospec_file,
+				int numa_node);
 
 /**
  * Pipeline run
diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 89cf194b19..c0ca3dc131 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -3522,57 +3522,6 @@ pipeline_spec_configure(struct rte_swx_pipeline *p,
 	return 0;
 }
 
-int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec_file,
-				 uint32_t *err_line,
-				 const char **err_msg)
-{
-	struct pipeline_spec *s = NULL;
-	int status = 0;
-
-	/* Check the input arguments. */
-	if (!p || !spec_file) {
-		if (err_line)
-			*err_line = 0;
-		if (err_msg)
-			*err_msg = "Invalid input argument.";
-		status = -EINVAL;
-		goto error;
-	}
-
-	/* Spec file parse. */
-	s = pipeline_spec_parse(spec_file, err_line, err_msg);
-	if (!s) {
-		status = -EINVAL;
-		goto error;
-	}
-
-	/* Pipeline configure. */
-	status = pipeline_spec_configure(p, s, err_msg);
-	if (status) {
-		if (err_line)
-			*err_line = 0;
-		goto error;
-	}
-
-	/* Pipeline build. */
-	status = rte_swx_pipeline_build(p);
-	if (status) {
-		if (err_line)
-			*err_line = 0;
-		if (err_msg)
-			*err_msg = "Pipeline build error.";
-		goto error;
-	}
-
-	return 0;
-
-error:
-	pipeline_spec_free(s);
-	return status;
-}
-
 static void
 port_in_params_free(void *params, const char *port_type)
 {
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 8d95005a5b..16806e6802 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -82,7 +82,6 @@ EXPERIMENTAL {
 	rte_swx_ctl_table_ops_get;
 	rte_swx_pipeline_action_config;
 	rte_swx_pipeline_build;
-	rte_swx_pipeline_build_from_spec;
 	rte_swx_pipeline_config;
 	rte_swx_pipeline_extern_func_register;
 	rte_swx_pipeline_extern_object_config;
@@ -148,6 +147,7 @@ EXPERIMENTAL {
 
 	#added in 22.11
 	rte_swx_ctl_pipeline_find;
+	rte_swx_pipeline_build_from_lib;
 	rte_swx_pipeline_codegen;
 	rte_swx_pipeline_find;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 09/17] examples/pipeline: add CLI command for pipeline code generation
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (6 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 08/17] pipeline: add API for shared library-based pipeline build Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 10/17] examples/pipeline: add CLI command for shared library build Cristian Dumitrescu
                       ` (8 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add CLI command for the pipeline code generation operation.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 61 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index f0285675b3..2b38977be1 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -983,6 +983,53 @@ cmd_pipeline_port_out(char **tokens,
 	}
 }
 
+static const char cmd_pipeline_codegen_help[] =
+"pipeline codegen <spec_file> <code_file>\n";
+
+static void
+cmd_pipeline_codegen(char **tokens,
+	uint32_t n_tokens,
+	char *out,
+	size_t out_size,
+	void *obj __rte_unused)
+{
+	FILE *spec_file = NULL;
+	FILE *code_file = NULL;
+	uint32_t err_line;
+	const char *err_msg;
+	int status;
+
+	if (n_tokens != 4) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	spec_file = fopen(tokens[2], "r");
+	if (!spec_file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", tokens[2]);
+		return;
+	}
+
+	code_file = fopen(tokens[3], "w");
+	if (!code_file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
+		return;
+	}
+
+	status = rte_swx_pipeline_codegen(spec_file,
+					  code_file,
+					  &err_line,
+					  &err_msg);
+
+	fclose(spec_file);
+	fclose(code_file);
+
+	if (status) {
+		snprintf(out, out_size, "Error %d at line %u: %s\n.",
+			status, err_line, err_msg);
+		return;
+	}
+}
 static const char cmd_pipeline_build_help[] =
 "pipeline <pipeline_name> build lib <lib_file> io <iospec_file> numa <numa_node>\n";
 
@@ -3009,6 +3056,7 @@ cmd_help(char **tokens,
 			"\tpipeline create\n"
 			"\tpipeline port in\n"
 			"\tpipeline port out\n"
+			"\tpipeline codegen\n"
 			"\tpipeline build\n"
 			"\tpipeline table add\n"
 			"\tpipeline table delete\n"
@@ -3078,6 +3126,12 @@ cmd_help(char **tokens,
 		}
 	}
 
+	if ((strcmp(tokens[0], "pipeline") == 0) &&
+		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
@@ -3356,6 +3410,13 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			return;
 		}
 
+		if ((n_tokens >= 3) &&
+			(strcmp(tokens[1], "codegen") == 0)) {
+			cmd_pipeline_codegen(tokens, n_tokens, out, out_size,
+				obj);
+			return;
+		}
+
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[2], "build") == 0)) {
 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 10/17] examples/pipeline: add CLI command for shared library build
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (7 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 09/17] examples/pipeline: add CLI command for pipeline code generation Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 11/17] examples/pipeline: remove the obsolete pipeline create CLI command Cristian Dumitrescu
                       ` (7 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add CLI command for the shared object library build operation.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 157 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 153 insertions(+), 4 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 2b38977be1..28cf8d4178 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -6,6 +6,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
@@ -25,6 +26,10 @@
 #define CMD_MAX_TOKENS     256
 #endif
 
+#ifndef MAX_LINE_SIZE
+#define MAX_LINE_SIZE 2048
+#endif
+
 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
@@ -1030,6 +1035,140 @@ cmd_pipeline_codegen(char **tokens,
 		return;
 	}
 }
+
+static const char cmd_pipeline_libbuild_help[] =
+"pipeline libbuild <code_file> <lib_file>\n";
+
+static void
+cmd_pipeline_libbuild(char **tokens,
+	uint32_t n_tokens,
+	char *out,
+	size_t out_size,
+	void *obj __rte_unused)
+{
+	char *code_file, *lib_file, *obj_file = NULL, *log_file = NULL;
+	char *install_dir, *cwd = NULL, *buffer = NULL;
+	size_t length;
+	int status = 0;
+
+	if (n_tokens != 4) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		goto free;
+	}
+
+	install_dir = getenv("RTE_INSTALL_DIR");
+	if (!install_dir) {
+		cwd = malloc(MAX_LINE_SIZE);
+		if (!cwd) {
+			snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+			goto free;
+		}
+
+		install_dir = getcwd(cwd, MAX_LINE_SIZE);
+		if (!install_dir) {
+			snprintf(out, out_size, "Error: Path too long.\n");
+			goto free;
+		}
+	}
+
+	snprintf(out, out_size, "Using DPDK source code from \"%s\".\n", install_dir);
+	out_size -= strlen(out);
+	out += strlen(out);
+
+	code_file = tokens[2];
+	length = strnlen(code_file, MAX_LINE_SIZE);
+	if ((length < 3) ||
+	    (code_file[length - 2] != '.') ||
+	    (code_file[length - 1] != 'c')) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "code_file");
+		goto free;
+	}
+
+	lib_file = tokens[3];
+	length = strnlen(lib_file, MAX_LINE_SIZE);
+	if ((length < 4) ||
+	    (lib_file[length - 3] != '.') ||
+	    (lib_file[length - 2] != 's') ||
+	    (lib_file[length - 1] != 'o')) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "lib_file");
+		goto free;
+	}
+
+	obj_file = malloc(length);
+	log_file = malloc(length + 2);
+	if (!obj_file || !log_file) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		goto free;
+	}
+
+	memcpy(obj_file, lib_file, length - 2);
+	obj_file[length - 2] = 'o';
+	obj_file[length - 1] = 0;
+
+	memcpy(log_file, lib_file, length - 2);
+	log_file[length - 2] = 'l';
+	log_file[length - 1] = 'o';
+	log_file[length] = 'g';
+	log_file[length + 1] = 0;
+
+	buffer = malloc(MAX_LINE_SIZE);
+	if (!buffer) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		return;
+	}
+
+	snprintf(buffer,
+		 MAX_LINE_SIZE,
+		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s %s "
+		 "-I %s/lib/pipeline "
+		 "-I %s/lib/eal/include "
+		 "-I %s/lib/eal/x86/include "
+		 "-I %s/lib/eal/include/generic "
+		 "-I %s/lib/meter "
+		 "-I %s/lib/port "
+		 "-I %s/lib/table "
+		 "-I %s/lib/pipeline "
+		 "-I %s/config "
+		 "-I %s/build "
+		 "-I %s/lib/eal/linux/include "
+		 ">%s 2>&1 "
+		 "&& "
+		 "gcc -shared %s -o %s "
+		 ">>%s 2>&1",
+		 obj_file,
+		 code_file,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 log_file,
+		 obj_file,
+		 lib_file,
+		 log_file);
+
+	status = system(buffer);
+	if (status) {
+		snprintf(out,
+			 out_size,
+			 "Library build failed, see file \"%s\" for details.\n",
+			 log_file);
+		goto free;
+	}
+
+free:
+	free(cwd);
+	free(obj_file);
+	free(log_file);
+	free(buffer);
+}
+
 static const char cmd_pipeline_build_help[] =
 "pipeline <pipeline_name> build lib <lib_file> io <iospec_file> numa <numa_node>\n";
 
@@ -1127,10 +1266,6 @@ table_entry_free(struct rte_swx_table_entry *entry)
 	free(entry);
 }
 
-#ifndef MAX_LINE_SIZE
-#define MAX_LINE_SIZE 2048
-#endif
-
 static int
 pipeline_table_entries_add(struct rte_swx_ctl_pipeline *p,
 			   const char *table_name,
@@ -3057,6 +3192,7 @@ cmd_help(char **tokens,
 			"\tpipeline port in\n"
 			"\tpipeline port out\n"
 			"\tpipeline codegen\n"
+			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
 			"\tpipeline table add\n"
 			"\tpipeline table delete\n"
@@ -3132,6 +3268,12 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if ((strcmp(tokens[0], "pipeline") == 0) &&
+		(n_tokens == 2) && (strcmp(tokens[1], "libbuild") == 0)) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_libbuild_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
@@ -3417,6 +3559,13 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			return;
 		}
 
+		if ((n_tokens >= 3) &&
+			(strcmp(tokens[1], "libbuild") == 0)) {
+			cmd_pipeline_libbuild(tokens, n_tokens, out, out_size,
+				obj);
+			return;
+		}
+
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[2], "build") == 0)) {
 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 11/17] examples/pipeline: remove the obsolete pipeline create CLI command
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (8 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 10/17] examples/pipeline: add CLI command for shared library build Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 12/17] examples/pipeline: remove the obsolete port configuration CLI commands Cristian Dumitrescu
                       ` (6 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

The pipeline configuration is now done through the I/O specification
file, hence this CLI command is no longer needed.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 47 -----------------------------------------
 1 file changed, 47 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 28cf8d4178..7b725a9c27 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -523,39 +523,6 @@ cmd_tap(char **tokens,
 	}
 }
 
-static const char cmd_pipeline_create_help[] =
-"pipeline <pipeline_name> create <numa_node>\n";
-
-static void
-cmd_pipeline_create(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
-{
-	struct pipeline *p;
-	char *name;
-	uint32_t numa_node;
-
-	if (n_tokens != 4) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-
-	name = tokens[1];
-
-	if (parser_read_uint32(&numa_node, tokens[3]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
-		return;
-	}
-
-	p = pipeline_create(obj, name, (int)numa_node);
-	if (!p) {
-		snprintf(out, out_size, "pipeline create error.");
-		return;
-	}
-}
-
 static const char cmd_pipeline_port_in_help[] =
 "pipeline <pipeline_name> port in <port_id>\n"
 "   link <link_name> rxq <queue_id> bsz <burst_size>\n"
@@ -3188,7 +3155,6 @@ cmd_help(char **tokens,
 			"\tmempool\n"
 			"\tlink\n"
 			"\ttap\n"
-			"\tpipeline create\n"
 			"\tpipeline port in\n"
 			"\tpipeline port out\n"
 			"\tpipeline codegen\n"
@@ -3241,12 +3207,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if ((strcmp(tokens[0], "pipeline") == 0) &&
-		(n_tokens == 2) && (strcmp(tokens[1], "create") == 0)) {
-		snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help);
-		return;
-	}
-
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) {
 		if (strcmp(tokens[2], "in") == 0) {
@@ -3529,13 +3489,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 	}
 
 	if (strcmp(tokens[0], "pipeline") == 0) {
-		if ((n_tokens >= 3) &&
-			(strcmp(tokens[2], "create") == 0)) {
-			cmd_pipeline_create(tokens, n_tokens, out, out_size,
-				obj);
-			return;
-		}
-
 		if ((n_tokens >= 4) &&
 			(strcmp(tokens[2], "port") == 0) &&
 			(strcmp(tokens[3], "in") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 12/17] examples/pipeline: remove the obsolete port configuration CLI commands
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (9 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 11/17] examples/pipeline: remove the obsolete pipeline create CLI command Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 13/17] examples/pipeline: remove the obsolete mirroring configuration CLI command Cristian Dumitrescu
                       ` (5 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

The pipeline I/O ports configuration is now done through the I/O
specification file, hence these CLI commands are no longer needed.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 465 ----------------------------------------
 1 file changed, 465 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 7b725a9c27..b26e73c706 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -523,438 +523,6 @@ cmd_tap(char **tokens,
 	}
 }
 
-static const char cmd_pipeline_port_in_help[] =
-"pipeline <pipeline_name> port in <port_id>\n"
-"   link <link_name> rxq <queue_id> bsz <burst_size>\n"
-"   ring <ring_name> bsz <burst_size>\n"
-"   | source <mempool_name> <file_name> loop <n_loops>\n"
-"   | tap <tap_name> mempool <mempool_name> mtu <mtu> bsz <burst_size>\n";
-
-static void
-cmd_pipeline_port_in(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
-{
-	struct pipeline *p;
-	int status;
-	uint32_t port_id = 0, t0;
-
-	if (n_tokens < 6) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || p->ctl) {
-		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
-		return;
-	}
-
-	if (strcmp(tokens[2], "port") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
-		return;
-	}
-
-	if (strcmp(tokens[3], "in") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
-		return;
-	}
-
-	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
-		return;
-	}
-
-	t0 = 5;
-
-	if (strcmp(tokens[t0], "link") == 0) {
-		struct rte_swx_port_ethdev_reader_params params;
-		struct link *link;
-
-		if (n_tokens < t0 + 6) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port in link");
-			return;
-		}
-
-		link = link_find(obj, tokens[t0 + 1]);
-		if (!link) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"link_name");
-			return;
-		}
-		params.dev_name = link->dev_name;
-
-		if (strcmp(tokens[t0 + 2], "rxq") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
-			return;
-		}
-
-		if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"queue_id");
-			return;
-		}
-
-		if (strcmp(tokens[t0 + 4], "bsz") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
-			return;
-		}
-
-		if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"burst_size");
-			return;
-		}
-
-		t0 += 6;
-
-		status = rte_swx_pipeline_port_in_config(p->p,
-			port_id,
-			"ethdev",
-			&params);
-	} else if (strcmp(tokens[t0], "ring") == 0) {
-		struct rte_swx_port_ring_reader_params params;
-		struct ring *ring;
-
-		if (n_tokens < t0 + 4) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port in ring");
-			return;
-		}
-
-		ring = ring_find(obj, tokens[t0 + 1]);
-		if (!ring) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"ring_name");
-			return;
-		}
-		params.name = ring->name;
-
-		if (strcmp(tokens[t0 + 2], "bsz") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
-			return;
-		}
-
-		if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"burst_size");
-			return;
-		}
-
-		t0 += 4;
-
-		status = rte_swx_pipeline_port_in_config(p->p,
-			port_id,
-			"ring",
-			&params);
-	} else if (strcmp(tokens[t0], "source") == 0) {
-		struct rte_swx_port_source_params params;
-		struct mempool *mp;
-
-		if (n_tokens < t0 + 5) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port in source");
-			return;
-		}
-
-		mp = mempool_find(obj, tokens[t0 + 1]);
-		if (!mp) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"mempool_name");
-			return;
-		}
-		params.pool = mp->m;
-
-		params.file_name = tokens[t0 + 2];
-
-		if (strcmp(tokens[t0 + 3], "loop") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "loop");
-			return;
-		}
-
-		if (parser_read_uint64(&params.n_loops, tokens[t0 + 4])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"n_loops");
-			return;
-		}
-
-		t0 += 5;
-
-		status = rte_swx_pipeline_port_in_config(p->p,
-			port_id,
-			"source",
-			&params);
-	} else if (strcmp(tokens[t0], "tap") == 0) {
-		struct rte_swx_port_fd_reader_params params;
-		struct tap *tap;
-		struct mempool *mp;
-
-		if (n_tokens < t0 + 8) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port in tap");
-			return;
-		}
-
-		tap = tap_find(obj, tokens[t0 + 1]);
-		if (!tap) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"tap_name");
-			return;
-		}
-		params.fd = tap->fd;
-
-		if (strcmp(tokens[t0 + 2], "mempool") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-				"mempool");
-			return;
-		}
-
-		mp = mempool_find(obj, tokens[t0 + 3]);
-		if (!mp) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"mempool_name");
-			return;
-		}
-		params.mempool = mp->m;
-
-		if (strcmp(tokens[t0 + 4], "mtu") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-				"mtu");
-			return;
-		}
-
-		if (parser_read_uint32(&params.mtu, tokens[t0 + 5]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
-			return;
-		}
-
-		if (strcmp(tokens[t0 + 6], "bsz") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
-			return;
-		}
-
-		if (parser_read_uint32(&params.burst_size, tokens[t0 + 7])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"burst_size");
-			return;
-		}
-
-		t0 += 8;
-
-		status = rte_swx_pipeline_port_in_config(p->p,
-			port_id,
-			"fd",
-			&params);
-
-	} else {
-		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
-		return;
-	}
-
-	if (status) {
-		snprintf(out, out_size, "port in error.");
-		return;
-	}
-
-	if (n_tokens != t0) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-}
-
-static const char cmd_pipeline_port_out_help[] =
-"pipeline <pipeline_name> port out <port_id>\n"
-"   link <link_name> txq <txq_id> bsz <burst_size>\n"
-"   ring <ring_name> bsz <burst_size>\n"
-"   | sink <file_name> | none\n"
-"   | tap <tap_name> bsz <burst_size>\n";
-
-static void
-cmd_pipeline_port_out(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
-{
-	struct pipeline *p;
-	int status;
-	uint32_t port_id = 0, t0;
-
-	if (n_tokens < 6) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || p->ctl) {
-		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
-		return;
-	}
-
-	if (strcmp(tokens[2], "port") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
-		return;
-	}
-
-	if (strcmp(tokens[3], "out") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
-		return;
-	}
-
-	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
-		return;
-	}
-
-	t0 = 5;
-
-	if (strcmp(tokens[t0], "link") == 0) {
-		struct rte_swx_port_ethdev_writer_params params;
-		struct link *link;
-
-		if (n_tokens < t0 + 6) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port out link");
-			return;
-		}
-
-		link = link_find(obj, tokens[t0 + 1]);
-		if (!link) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"link_name");
-			return;
-		}
-		params.dev_name = link->dev_name;
-
-		if (strcmp(tokens[t0 + 2], "txq") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
-			return;
-		}
-
-		if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"queue_id");
-			return;
-		}
-
-		if (strcmp(tokens[t0 + 4], "bsz") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
-			return;
-		}
-
-		if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"burst_size");
-			return;
-		}
-
-		t0 += 6;
-
-		status = rte_swx_pipeline_port_out_config(p->p,
-			port_id,
-			"ethdev",
-			&params);
-	} else if (strcmp(tokens[t0], "ring") == 0) {
-		struct rte_swx_port_ring_writer_params params;
-		struct ring *ring;
-
-		if (n_tokens < t0 + 4) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port out link");
-			return;
-		}
-
-		ring = ring_find(obj, tokens[t0 + 1]);
-		if (!ring) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"ring_name");
-			return;
-		}
-		params.name = ring->name;
-
-		if (strcmp(tokens[t0 + 2], "bsz") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
-			return;
-		}
-
-		if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"burst_size");
-			return;
-		}
-
-		t0 += 4;
-
-		status = rte_swx_pipeline_port_out_config(p->p,
-			port_id,
-			"ring",
-			&params);
-	} else if (strcmp(tokens[t0], "sink") == 0) {
-		struct rte_swx_port_sink_params params;
-
-		params.file_name = strcmp(tokens[t0 + 1], "none") ?
-			tokens[t0 + 1] : NULL;
-
-		t0 += 2;
-
-		status = rte_swx_pipeline_port_out_config(p->p,
-			port_id,
-			"sink",
-			&params);
-	} else if (strcmp(tokens[t0], "tap") == 0) {
-		struct rte_swx_port_fd_writer_params params;
-		struct tap *tap;
-
-		if (n_tokens < t0 + 4) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port out tap");
-			return;
-		}
-
-		tap = tap_find(obj, tokens[t0 + 1]);
-		if (!tap) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"tap_name");
-			return;
-		}
-		params.fd = tap->fd;
-
-		if (strcmp(tokens[t0 + 2], "bsz") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
-			return;
-		}
-
-		if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"burst_size");
-			return;
-		}
-
-		t0 += 4;
-
-		status = rte_swx_pipeline_port_out_config(p->p,
-			port_id,
-			"fd",
-			&params);
-	} else {
-		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
-		return;
-	}
-
-	if (status) {
-		snprintf(out, out_size, "port out error.");
-		return;
-	}
-
-	if (n_tokens != t0) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-}
-
 static const char cmd_pipeline_codegen_help[] =
 "pipeline codegen <spec_file> <code_file>\n";
 
@@ -3155,8 +2723,6 @@ cmd_help(char **tokens,
 			"\tmempool\n"
 			"\tlink\n"
 			"\ttap\n"
-			"\tpipeline port in\n"
-			"\tpipeline port out\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
@@ -3207,21 +2773,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if ((strcmp(tokens[0], "pipeline") == 0) &&
-		(n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) {
-		if (strcmp(tokens[2], "in") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_pipeline_port_in_help);
-			return;
-		}
-
-		if (strcmp(tokens[2], "out") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_pipeline_port_out_help);
-			return;
-		}
-	}
-
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
@@ -3489,22 +3040,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 	}
 
 	if (strcmp(tokens[0], "pipeline") == 0) {
-		if ((n_tokens >= 4) &&
-			(strcmp(tokens[2], "port") == 0) &&
-			(strcmp(tokens[3], "in") == 0)) {
-			cmd_pipeline_port_in(tokens, n_tokens, out, out_size,
-				obj);
-			return;
-		}
-
-		if ((n_tokens >= 4) &&
-			(strcmp(tokens[2], "port") == 0) &&
-			(strcmp(tokens[3], "out") == 0)) {
-			cmd_pipeline_port_out(tokens, n_tokens, out, out_size,
-				obj);
-			return;
-		}
-
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[1], "codegen") == 0)) {
 			cmd_pipeline_codegen(tokens, n_tokens, out, out_size,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 13/17] examples/pipeline: remove the obsolete mirroring configuration CLI command
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (10 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 12/17] examples/pipeline: remove the obsolete port configuration CLI commands Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 14/17] examples/pipeline: use the pipeline name query API Cristian Dumitrescu
                       ` (4 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

The pipeline mirroring configuration is done through the I/O
specification file, so this CLI command is no longer needed.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 76 -----------------------------------------
 1 file changed, 76 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index b26e73c706..fa828c008b 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -2457,68 +2457,6 @@ cmd_pipeline_stats(char **tokens,
 	}
 }
 
-static const char cmd_pipeline_mirror_help[] =
-"pipeline <pipeline_name> mirror slots <n_slots> sessions <n_sessions>\n";
-
-static void
-cmd_pipeline_mirror(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
-{
-	struct rte_swx_pipeline_mirroring_params params;
-	struct pipeline *p;
-	int status;
-
-	if (n_tokens != 7) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-
-	if (strcmp(tokens[0], "pipeline")) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	p = pipeline_find(obj, tokens[1]);
-	if (!p) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
-		return;
-	}
-
-	if (strcmp(tokens[2], "mirror")) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mirror");
-		return;
-	}
-
-	if (strcmp(tokens[3], "slots")) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "slots");
-		return;
-	}
-
-	if (parser_read_uint32(&params.n_slots, tokens[4])) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "n_slots");
-		return;
-	}
-
-	if (strcmp(tokens[5], "sessions")) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sessions");
-		return;
-	}
-
-	if (parser_read_uint32(&params.n_sessions, tokens[6])) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "n_sessions");
-		return;
-	}
-
-	status = rte_swx_pipeline_mirroring_config(p->p, &params);
-	if (status) {
-		snprintf(out, out_size, "Command failed!\n");
-		return;
-	}
-}
-
 static const char cmd_pipeline_mirror_session_help[] =
 "pipeline <pipeline_name> mirror session <session_id> port <port_id> clone fast | slow "
 "truncate <truncation_length>\n";
@@ -2746,7 +2684,6 @@ cmd_help(char **tokens,
 			"\tpipeline meter set\n"
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
-			"\tpipeline mirror\n"
 			"\tpipeline mirror session\n"
 			"\tthread pipeline enable\n"
 			"\tthread pipeline disable\n\n");
@@ -2958,12 +2895,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if (!strcmp(tokens[0], "pipeline") &&
-		(n_tokens == 2) && !strcmp(tokens[1], "mirror")) {
-		snprintf(out, out_size, "\n%s\n", cmd_pipeline_mirror_help);
-		return;
-	}
-
 	if (!strcmp(tokens[0], "pipeline") &&
 		(n_tokens == 3) && !strcmp(tokens[1], "mirror")
 		&& !strcmp(tokens[2], "session")) {
@@ -3217,13 +3148,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			return;
 		}
 
-		if ((n_tokens >= 4) &&
-			(strcmp(tokens[2], "mirror") == 0) &&
-			(strcmp(tokens[3], "slots") == 0)) {
-			cmd_pipeline_mirror(tokens, n_tokens, out, out_size, obj);
-			return;
-		}
-
 		if ((n_tokens >= 4) &&
 			(strcmp(tokens[2], "mirror") == 0) &&
 			(strcmp(tokens[3], "session") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 14/17] examples/pipeline: use the pipeline name query API
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (11 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 13/17] examples/pipeline: remove the obsolete mirroring configuration CLI command Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 15/17] examples/pipeline: rework the link CLI command Cristian Dumitrescu
                       ` (3 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Convert the CLI commands to use the pipeline name query API and remove
the linked list of pipeline objects previously maintained by the
application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c    | 268 ++++++++++++++++++++-----------------
 examples/pipeline/obj.c    |  67 ----------
 examples/pipeline/obj.h    |  24 ----
 examples/pipeline/thread.c |  65 ++++-----
 examples/pipeline/thread.h |   9 +-
 5 files changed, 175 insertions(+), 258 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index fa828c008b..f48ff326be 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -858,9 +858,9 @@ cmd_pipeline_table_add(char **tokens,
 		       uint32_t n_tokens,
 		       char *out,
 		       size_t out_size,
-		       void *obj)
+		       void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *table_name, *file_name;
 	FILE *file = NULL;
 	uint32_t file_line_number = 0;
@@ -872,8 +872,8 @@ cmd_pipeline_table_add(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -887,7 +887,7 @@ cmd_pipeline_table_add(char **tokens,
 		return;
 	}
 
-	status = pipeline_table_entries_add(p->ctl,
+	status = pipeline_table_entries_add(ctl,
 					    table_name,
 					    file,
 					    &file_line_number);
@@ -956,9 +956,9 @@ cmd_pipeline_table_delete(char **tokens,
 			  uint32_t n_tokens,
 			  char *out,
 			  size_t out_size,
-			  void *obj)
+			  void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *table_name, *file_name;
 	FILE *file = NULL;
 	uint32_t file_line_number = 0;
@@ -970,8 +970,8 @@ cmd_pipeline_table_delete(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -985,7 +985,7 @@ cmd_pipeline_table_delete(char **tokens,
 		return;
 	}
 
-	status = pipeline_table_entries_delete(p->ctl,
+	status = pipeline_table_entries_delete(ctl,
 					       table_name,
 					       file,
 					       &file_line_number);
@@ -1054,9 +1054,9 @@ cmd_pipeline_table_default(char **tokens,
 			   uint32_t n_tokens,
 			   char *out,
 			   size_t out_size,
-			   void *obj)
+			   void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *table_name, *file_name;
 	FILE *file = NULL;
 	uint32_t file_line_number = 0;
@@ -1068,8 +1068,8 @@ cmd_pipeline_table_default(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1083,7 +1083,7 @@ cmd_pipeline_table_default(char **tokens,
 		return;
 	}
 
-	status = pipeline_table_default_entry_add(p->ctl,
+	status = pipeline_table_default_entry_add(ctl,
 						  table_name,
 						  file,
 						  &file_line_number);
@@ -1103,9 +1103,9 @@ cmd_pipeline_table_show(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *table_name;
 	FILE *file = NULL;
 	int status;
@@ -1116,8 +1116,8 @@ cmd_pipeline_table_show(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1129,7 +1129,7 @@ cmd_pipeline_table_show(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_table_fprintf(file, p->ctl, table_name);
+	status = rte_swx_ctl_pipeline_table_fprintf(file, ctl, table_name);
 	if (status)
 		snprintf(out, out_size, MSG_ARG_INVALID, "table_name");
 
@@ -1145,9 +1145,9 @@ cmd_pipeline_selector_group_add(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *selector_name;
 	uint32_t group_id;
 	int status;
@@ -1158,8 +1158,8 @@ cmd_pipeline_selector_group_add(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1177,7 +1177,7 @@ cmd_pipeline_selector_group_add(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_selector_group_add(p->ctl,
+	status = rte_swx_ctl_pipeline_selector_group_add(ctl,
 		selector_name,
 		&group_id);
 	if (status)
@@ -1194,9 +1194,9 @@ cmd_pipeline_selector_group_delete(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *selector_name;
 	uint32_t group_id;
 	int status;
@@ -1207,8 +1207,8 @@ cmd_pipeline_selector_group_delete(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1231,7 +1231,7 @@ cmd_pipeline_selector_group_delete(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_selector_group_delete(p->ctl,
+	status = rte_swx_ctl_pipeline_selector_group_delete(ctl,
 		selector_name,
 		group_id);
 	if (status)
@@ -1402,9 +1402,9 @@ cmd_pipeline_selector_group_member_add(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *selector_name, *file_name;
 	FILE *file = NULL;
 	uint32_t file_line_number = 0;
@@ -1416,8 +1416,8 @@ cmd_pipeline_selector_group_member_add(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1443,7 +1443,7 @@ cmd_pipeline_selector_group_member_add(char **tokens,
 		return;
 	}
 
-	status = pipeline_selector_group_members_add(p->ctl,
+	status = pipeline_selector_group_members_add(ctl,
 					    selector_name,
 					    file,
 					    &file_line_number);
@@ -1512,9 +1512,9 @@ cmd_pipeline_selector_group_member_delete(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *selector_name, *file_name;
 	FILE *file = NULL;
 	uint32_t file_line_number = 0;
@@ -1526,8 +1526,8 @@ cmd_pipeline_selector_group_member_delete(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1553,7 +1553,7 @@ cmd_pipeline_selector_group_member_delete(char **tokens,
 		return;
 	}
 
-	status = pipeline_selector_group_members_delete(p->ctl,
+	status = pipeline_selector_group_members_delete(ctl,
 					    selector_name,
 					    file,
 					    &file_line_number);
@@ -1573,9 +1573,9 @@ cmd_pipeline_selector_show(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *selector_name;
 	FILE *file = NULL;
 	int status;
@@ -1586,8 +1586,8 @@ cmd_pipeline_selector_show(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1600,7 +1600,7 @@ cmd_pipeline_selector_show(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_selector_fprintf(file, p->ctl, selector_name);
+	status = rte_swx_ctl_pipeline_selector_fprintf(file, ctl, selector_name);
 	if (status)
 		snprintf(out, out_size, MSG_ARG_INVALID, "selector_name");
 
@@ -1665,9 +1665,9 @@ cmd_pipeline_learner_default(char **tokens,
 			     uint32_t n_tokens,
 			     char *out,
 			     size_t out_size,
-			     void *obj)
+			     void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *learner_name, *file_name;
 	FILE *file = NULL;
 	uint32_t file_line_number = 0;
@@ -1679,8 +1679,8 @@ cmd_pipeline_learner_default(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1694,7 +1694,7 @@ cmd_pipeline_learner_default(char **tokens,
 		return;
 	}
 
-	status = pipeline_learner_default_entry_add(p->ctl,
+	status = pipeline_learner_default_entry_add(ctl,
 						    learner_name,
 						    file,
 						    &file_line_number);
@@ -1714,9 +1714,9 @@ cmd_pipeline_commit(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name;
 	int status;
 
@@ -1726,13 +1726,13 @@ cmd_pipeline_commit(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
+	status = rte_swx_ctl_pipeline_commit(ctl, 1);
 	if (status)
 		snprintf(out, out_size, "Commit failed. "
 			"Use \"commit\" to retry or \"abort\" to discard the pending work.\n");
@@ -1746,9 +1746,9 @@ cmd_pipeline_abort(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name;
 
 	if (n_tokens != 3) {
@@ -1757,13 +1757,13 @@ cmd_pipeline_abort(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	rte_swx_ctl_pipeline_abort(p->ctl);
+	rte_swx_ctl_pipeline_abort(ctl);
 }
 
 static const char cmd_pipeline_regrd_help[] =
@@ -1774,9 +1774,9 @@ cmd_pipeline_regrd(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *name;
 	uint64_t value;
 	uint32_t idx;
@@ -1787,8 +1787,8 @@ cmd_pipeline_regrd(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1805,7 +1805,7 @@ cmd_pipeline_regrd(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_regarray_read(p->p, name, idx, &value);
+	status = rte_swx_ctl_pipeline_regarray_read(p, name, idx, &value);
 	if (status) {
 		snprintf(out, out_size, "Command failed.\n");
 		return;
@@ -1822,9 +1822,9 @@ cmd_pipeline_regwr(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *name;
 	uint64_t value;
 	uint32_t idx;
@@ -1835,8 +1835,8 @@ cmd_pipeline_regwr(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1858,7 +1858,7 @@ cmd_pipeline_regwr(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_regarray_write(p->p, name, idx, value);
+	status = rte_swx_ctl_pipeline_regarray_write(p, name, idx, value);
 	if (status) {
 		snprintf(out, out_size, "Command failed.\n");
 		return;
@@ -1874,10 +1874,10 @@ cmd_pipeline_meter_profile_add(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
 	struct rte_meter_trtcm_params params;
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *profile_name;
 	int status;
 
@@ -1886,8 +1886,8 @@ cmd_pipeline_meter_profile_add(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1949,7 +1949,7 @@ cmd_pipeline_meter_profile_add(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_meter_profile_add(p->p, profile_name, &params);
+	status = rte_swx_ctl_meter_profile_add(p, profile_name, &params);
 	if (status) {
 		snprintf(out, out_size, "Command failed.\n");
 		return;
@@ -1964,9 +1964,9 @@ cmd_pipeline_meter_profile_delete(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *profile_name;
 	int status;
 
@@ -1975,8 +1975,8 @@ cmd_pipeline_meter_profile_delete(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1998,7 +1998,7 @@ cmd_pipeline_meter_profile_delete(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_meter_profile_delete(p->p, profile_name);
+	status = rte_swx_ctl_meter_profile_delete(p, profile_name);
 	if (status) {
 		snprintf(out, out_size, "Command failed.\n");
 		return;
@@ -2014,9 +2014,9 @@ cmd_pipeline_meter_reset(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *name;
 	uint32_t idx0 = 0, idx1 = 0;
 
@@ -2025,8 +2025,8 @@ cmd_pipeline_meter_reset(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2066,7 +2066,7 @@ cmd_pipeline_meter_reset(char **tokens,
 	for ( ; idx0 <= idx1; idx0++) {
 		int status;
 
-		status = rte_swx_ctl_meter_reset(p->p, name, idx0);
+		status = rte_swx_ctl_meter_reset(p, name, idx0);
 		if (status) {
 			snprintf(out, out_size, "Command failed for index %u.\n", idx0);
 			return;
@@ -2083,9 +2083,9 @@ cmd_pipeline_meter_set(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *name, *profile_name;
 	uint32_t idx0 = 0, idx1 = 0;
 
@@ -2094,8 +2094,8 @@ cmd_pipeline_meter_set(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2142,7 +2142,7 @@ cmd_pipeline_meter_set(char **tokens,
 	for ( ; idx0 <= idx1; idx0++) {
 		int status;
 
-		status = rte_swx_ctl_meter_set(p->p, name, idx0, profile_name);
+		status = rte_swx_ctl_meter_set(p, name, idx0, profile_name);
 		if (status) {
 			snprintf(out, out_size, "Command failed for index %u.\n", idx0);
 			return;
@@ -2159,10 +2159,10 @@ cmd_pipeline_meter_stats(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
 	struct rte_swx_ctl_meter_stats stats;
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *name;
 	uint32_t idx0 = 0, idx1 = 0;
 
@@ -2171,8 +2171,8 @@ cmd_pipeline_meter_stats(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2235,7 +2235,7 @@ cmd_pipeline_meter_stats(char **tokens,
 	for ( ; idx0 <= idx1; idx0++) {
 		int status;
 
-		status = rte_swx_ctl_meter_stats_read(p->p, name, idx0, &stats);
+		status = rte_swx_ctl_meter_stats_read(p, name, idx0, &stats);
 		if (status) {
 			snprintf(out, out_size, "Pipeline meter stats error at index %u.\n", idx0);
 			out_size -= strlen(out);
@@ -2265,10 +2265,10 @@ cmd_pipeline_stats(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
 	struct rte_swx_ctl_pipeline_info info;
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	uint32_t i;
 	int status;
 
@@ -2277,8 +2277,8 @@ cmd_pipeline_stats(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2288,7 +2288,7 @@ cmd_pipeline_stats(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_info_get(p->p, &info);
+	status = rte_swx_ctl_pipeline_info_get(p, &info);
 	if (status) {
 		snprintf(out, out_size, "Pipeline info get error.");
 		return;
@@ -2301,7 +2301,7 @@ cmd_pipeline_stats(char **tokens,
 	for (i = 0; i < info.n_ports_in; i++) {
 		struct rte_swx_port_in_stats stats;
 
-		rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats);
+		rte_swx_ctl_pipeline_port_in_stats_read(p, i, &stats);
 
 		snprintf(out, out_size, "\tPort %u:"
 			" packets %" PRIu64
@@ -2319,7 +2319,7 @@ cmd_pipeline_stats(char **tokens,
 	for (i = 0; i < info.n_ports_out; i++) {
 		struct rte_swx_port_out_stats stats;
 
-		rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats);
+		rte_swx_ctl_pipeline_port_out_stats_read(p, i, &stats);
 
 		if (i != info.n_ports_out - 1)
 			snprintf(out, out_size, "\tPort %u:", i);
@@ -2358,13 +2358,13 @@ cmd_pipeline_stats(char **tokens,
 		};
 		uint32_t j;
 
-		status = rte_swx_ctl_table_info_get(p->p, i, &table_info);
+		status = rte_swx_ctl_table_info_get(p, i, &table_info);
 		if (status) {
 			snprintf(out, out_size, "Table info get error.");
 			return;
 		}
 
-		status = rte_swx_ctl_pipeline_table_stats_read(p->p, table_info.name, &stats);
+		status = rte_swx_ctl_pipeline_table_stats_read(p, table_info.name, &stats);
 		if (status) {
 			snprintf(out, out_size, "Table stats read error.");
 			return;
@@ -2382,7 +2382,7 @@ cmd_pipeline_stats(char **tokens,
 		for (j = 0; j < info.n_actions; j++) {
 			struct rte_swx_ctl_action_info action_info;
 
-			status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
+			status = rte_swx_ctl_action_info_get(p, j, &action_info);
 			if (status) {
 				snprintf(out, out_size, "Action info get error.");
 				return;
@@ -2410,13 +2410,13 @@ cmd_pipeline_stats(char **tokens,
 		};
 		uint32_t j;
 
-		status = rte_swx_ctl_learner_info_get(p->p, i, &learner_info);
+		status = rte_swx_ctl_learner_info_get(p, i, &learner_info);
 		if (status) {
 			snprintf(out, out_size, "Learner table info get error.");
 			return;
 		}
 
-		status = rte_swx_ctl_pipeline_learner_stats_read(p->p, learner_info.name, &stats);
+		status = rte_swx_ctl_pipeline_learner_stats_read(p, learner_info.name, &stats);
 		if (status) {
 			snprintf(out, out_size, "Learner table stats read error.");
 			return;
@@ -2442,7 +2442,7 @@ cmd_pipeline_stats(char **tokens,
 		for (j = 0; j < info.n_actions; j++) {
 			struct rte_swx_ctl_action_info action_info;
 
-			status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
+			status = rte_swx_ctl_action_info_get(p, j, &action_info);
 			if (status) {
 				snprintf(out, out_size, "Action info get error.");
 				return;
@@ -2466,10 +2466,10 @@ cmd_pipeline_mirror_session(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
 	struct rte_swx_pipeline_mirroring_session_params params;
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	uint32_t session_id = 0;
 	int status;
 
@@ -2483,8 +2483,8 @@ cmd_pipeline_mirror_session(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2538,7 +2538,7 @@ cmd_pipeline_mirror_session(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_mirroring_session_set(p->p, session_id, &params);
+	status = rte_swx_ctl_pipeline_mirroring_session_set(p, session_id, &params);
 	if (status) {
 		snprintf(out, out_size, "Command failed!\n");
 		return;
@@ -2546,21 +2546,25 @@ cmd_pipeline_mirror_session(char **tokens,
 }
 
 static const char cmd_thread_pipeline_enable_help[] =
-"thread <thread_id> pipeline <pipeline_name> enable\n";
+"thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
+
+#ifndef TIMER_PERIOD_MS_DEFAULT
+#define TIMER_PERIOD_MS_DEFAULT 10
+#endif
 
 static void
 cmd_thread_pipeline_enable(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
 	char *pipeline_name;
-	struct pipeline *p;
-	uint32_t thread_id;
+	struct rte_swx_pipeline *p;
+	uint32_t thread_id, timer_period_ms = TIMER_PERIOD_MS_DEFAULT;
 	int status;
 
-	if (n_tokens != 5) {
+	if ((n_tokens != 5) && (n_tokens != 7)) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
@@ -2576,8 +2580,8 @@ cmd_thread_pipeline_enable(char **tokens,
 	}
 
 	pipeline_name = tokens[3];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(pipeline_name);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2587,7 +2591,19 @@ cmd_thread_pipeline_enable(char **tokens,
 		return;
 	}
 
-	status = thread_pipeline_enable(thread_id, obj, pipeline_name);
+	if (n_tokens == 7) {
+		if (strcmp(tokens[5], "period") != 0) {
+			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
+			return;
+		}
+
+		if (parser_read_uint32(&timer_period_ms, tokens[6]) != 0) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
+			return;
+		}
+	}
+
+	status = thread_pipeline_enable(thread_id, p, timer_period_ms);
 	if (status) {
 		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
 		return;
@@ -2602,9 +2618,9 @@ cmd_thread_pipeline_disable(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	char *pipeline_name;
 	uint32_t thread_id;
 	int status;
@@ -2625,8 +2641,8 @@ cmd_thread_pipeline_disable(char **tokens,
 	}
 
 	pipeline_name = tokens[3];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(pipeline_name);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2636,7 +2652,7 @@ cmd_thread_pipeline_disable(char **tokens,
 		return;
 	}
 
-	status = thread_pipeline_disable(thread_id, obj, pipeline_name);
+	status = thread_pipeline_disable(thread_id, p);
 	if (status) {
 		snprintf(out, out_size, MSG_CMD_FAIL,
 			"thread pipeline disable");
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 967342c580..d1f519180e 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -16,7 +16,6 @@
 #include <rte_mempool.h>
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
-#include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
 
 #include "obj.h"
@@ -41,11 +40,6 @@ TAILQ_HEAD(ring_list, ring);
  */
 TAILQ_HEAD(tap_list, tap);
 
-/*
- * pipeline
- */
-TAILQ_HEAD(pipeline_list, pipeline);
-
 /*
  * obj
  */
@@ -53,7 +47,6 @@ struct obj {
 	struct mempool_list mempool_list;
 	struct link_list link_list;
 	struct ring_list ring_list;
-	struct pipeline_list pipeline_list;
 	struct tap_list tap_list;
 };
 
@@ -513,65 +506,6 @@ tap_create(struct obj *obj, const char *name)
 
 #endif
 
-/*
- * pipeline
- */
-#ifndef PIPELINE_MSGQ_SIZE
-#define PIPELINE_MSGQ_SIZE                                 64
-#endif
-
-struct pipeline *
-pipeline_create(struct obj *obj, const char *name, int numa_node)
-{
-	struct pipeline *pipeline;
-	struct rte_swx_pipeline *p = NULL;
-	int status;
-
-	/* Check input params */
-	if ((name == NULL) ||
-		pipeline_find(obj, name))
-		return NULL;
-
-	/* Resource create */
-	status = rte_swx_pipeline_config(&p, name, numa_node);
-	if (status)
-		goto error;
-
-	/* Node allocation */
-	pipeline = calloc(1, sizeof(struct pipeline));
-	if (pipeline == NULL)
-		goto error;
-
-	/* Node fill in */
-	strlcpy(pipeline->name, name, sizeof(pipeline->name));
-	pipeline->p = p;
-	pipeline->timer_period_ms = 10;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->pipeline_list, pipeline, node);
-
-	return pipeline;
-
-error:
-	rte_swx_pipeline_free(p);
-	return NULL;
-}
-
-struct pipeline *
-pipeline_find(struct obj *obj, const char *name)
-{
-	struct pipeline *pipeline;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(pipeline, &obj->pipeline_list, node)
-		if (strcmp(name, pipeline->name) == 0)
-			return pipeline;
-
-	return NULL;
-}
-
 /*
  * obj
  */
@@ -587,7 +521,6 @@ obj_init(void)
 	TAILQ_INIT(&obj->mempool_list);
 	TAILQ_INIT(&obj->link_list);
 	TAILQ_INIT(&obj->ring_list);
-	TAILQ_INIT(&obj->pipeline_list);
 	TAILQ_INIT(&obj->tap_list);
 
 	return obj;
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index b921610554..e63a9c0e9a 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -143,28 +143,4 @@ tap_next(struct obj *obj, struct tap *tap);
 struct tap *
 tap_create(struct obj *obj, const char *name);
 
-/*
- * pipeline
- */
-struct pipeline {
-	TAILQ_ENTRY(pipeline) node;
-	char name[NAME_SIZE];
-
-	struct rte_swx_pipeline *p;
-	struct rte_swx_ctl_pipeline *ctl;
-
-	uint32_t timer_period_ms;
-	int enabled;
-	uint32_t thread_id;
-	uint32_t cpu_id;
-};
-
-struct pipeline *
-pipeline_create(struct obj *obj,
-		const char *name,
-		int numa_node);
-
-struct pipeline *
-pipeline_find(struct obj *obj, const char *name);
-
 #endif /* _INCLUDE_OBJ_H_ */
diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 5fe7eae00e..6d15f51fb2 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -228,20 +228,33 @@ thread_msg_send_recv(uint32_t thread_id,
 	return rsp;
 }
 
+static int
+thread_is_pipeline_enabled(uint32_t thread_id, struct rte_swx_pipeline *p)
+{
+	struct thread *t = &thread[thread_id];
+	struct thread_data *td = &thread_data[thread_id];
+	uint32_t i;
+
+	if (!t->enabled)
+		return 0; /* Pipeline NOT enabled on this thread. */
+
+	for (i = 0; i < td->n_pipelines; i++)
+		if (td->p[i] == p)
+			return 1; /* Pipeline enabled on this thread. */
+
+	return 0 /* Pipeline NOT enabled on this thread. */;
+}
+
 int
-thread_pipeline_enable(uint32_t thread_id,
-	struct obj *obj,
-	const char *pipeline_name)
+thread_pipeline_enable(uint32_t thread_id, struct rte_swx_pipeline *p, uint32_t timer_period_ms)
 {
-	struct pipeline *p = pipeline_find(obj, pipeline_name);
 	struct thread *t;
 	struct thread_msg_req *req;
 	struct thread_msg_rsp *rsp;
 	int status;
 
 	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) ||
-		(p == NULL))
+	if ((thread_id >= RTE_MAX_LCORE) || !p || !timer_period_ms)
 		return -1;
 
 	t = &thread[thread_id];
@@ -256,19 +269,14 @@ thread_pipeline_enable(uint32_t thread_id,
 			return -1;
 
 		/* Data plane thread */
-		td->p[td->n_pipelines] = p->p;
+		td->p[td->n_pipelines] = p;
 
-		tdp->p = p->p;
-		tdp->timer_period =
-			(rte_get_tsc_hz() * p->timer_period_ms) / 1000;
+		tdp->p = p;
+		tdp->timer_period = (rte_get_tsc_hz() * timer_period_ms) / 1000;
 		tdp->time_next = rte_get_tsc_cycles() + tdp->timer_period;
 
 		td->n_pipelines++;
 
-		/* Pipeline */
-		p->thread_id = thread_id;
-		p->enabled = 1;
-
 		return 0;
 	}
 
@@ -279,8 +287,8 @@ thread_pipeline_enable(uint32_t thread_id,
 
 	/* Write request */
 	req->type = THREAD_REQ_PIPELINE_ENABLE;
-	req->pipeline_enable.p = p->p;
-	req->pipeline_enable.timer_period_ms = p->timer_period_ms;
+	req->pipeline_enable.p = p;
+	req->pipeline_enable.timer_period_ms = timer_period_ms;
 
 	/* Send request and wait for response */
 	rsp = thread_msg_send_recv(thread_id, req);
@@ -295,38 +303,28 @@ thread_pipeline_enable(uint32_t thread_id,
 	if (status)
 		return status;
 
-	p->thread_id = thread_id;
-	p->enabled = 1;
-
 	return 0;
 }
 
 int
-thread_pipeline_disable(uint32_t thread_id,
-	struct obj *obj,
-	const char *pipeline_name)
+thread_pipeline_disable(uint32_t thread_id, struct rte_swx_pipeline *p)
 {
-	struct pipeline *p = pipeline_find(obj, pipeline_name);
 	struct thread *t;
 	struct thread_msg_req *req;
 	struct thread_msg_rsp *rsp;
 	int status;
 
 	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) ||
-		(p == NULL))
+	if ((thread_id >= RTE_MAX_LCORE) || !p)
 		return -1;
 
 	t = &thread[thread_id];
 	if (t->enabled == 0)
 		return -1;
 
-	if (p->enabled == 0)
+	if (!thread_is_pipeline_enabled(thread_id, p))
 		return 0;
 
-	if (p->thread_id != thread_id)
-		return -1;
-
 	if (!thread_is_running(thread_id)) {
 		struct thread_data *td = &thread_data[thread_id];
 		uint32_t i;
@@ -334,7 +332,7 @@ thread_pipeline_disable(uint32_t thread_id,
 		for (i = 0; i < td->n_pipelines; i++) {
 			struct pipeline_data *tdp = &td->pipeline_data[i];
 
-			if (tdp->p != p->p)
+			if (tdp->p != p)
 				continue;
 
 			/* Data plane thread */
@@ -350,9 +348,6 @@ thread_pipeline_disable(uint32_t thread_id,
 
 			td->n_pipelines--;
 
-			/* Pipeline */
-			p->enabled = 0;
-
 			break;
 		}
 
@@ -366,7 +361,7 @@ thread_pipeline_disable(uint32_t thread_id,
 
 	/* Write request */
 	req->type = THREAD_REQ_PIPELINE_DISABLE;
-	req->pipeline_disable.p = p->p;
+	req->pipeline_disable.p = p;
 
 	/* Send request and wait for response */
 	rsp = thread_msg_send_recv(thread_id, req);
@@ -381,8 +376,6 @@ thread_pipeline_disable(uint32_t thread_id,
 	if (status)
 		return status;
 
-	p->enabled = 0;
-
 	return 0;
 }
 
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index d9d8645d4c..712cb25bbb 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -7,17 +7,16 @@
 
 #include <stdint.h>
 
-#include "obj.h"
+#include <rte_swx_pipeline.h>
 
 int
 thread_pipeline_enable(uint32_t thread_id,
-	struct obj *obj,
-	const char *pipeline_name);
+		       struct rte_swx_pipeline *p,
+		       uint32_t timer_period_ms);
 
 int
 thread_pipeline_disable(uint32_t thread_id,
-	struct obj *obj,
-	const char *pipeline_name);
+			struct rte_swx_pipeline *p);
 
 int
 thread_init(void);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 15/17] examples/pipeline: rework the link CLI command
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (12 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 14/17] examples/pipeline: use the pipeline name query API Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 16/17] examples/pipelines: remove obsolete tap " Cristian Dumitrescu
                       ` (2 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Rework the link CLI command for better alignment with the naming
conventions used in the pipeline I/O specification file. Use the
library linked list of devices and remove the application list.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 81 +++++++++++++----------------------------
 examples/pipeline/obj.c | 16 ++------
 examples/pipeline/obj.h |  4 --
 3 files changed, 29 insertions(+), 72 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index f48ff326be..d56a830fb7 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -106,22 +106,6 @@ parser_read_uint32(uint32_t *value, const char *p)
 	return 0;
 }
 
-static int
-parser_read_uint16(uint16_t *value, const char *p)
-{
-	uint64_t val = 0;
-	int ret = parser_read_uint64(&val, p);
-
-	if (ret < 0)
-		return ret;
-
-	if (val > UINT16_MAX)
-		return -ERANGE;
-
-	*value = val;
-	return 0;
-}
-
 #define PARSE_DELIMITER " \f\n\r\t\v"
 
 static int
@@ -230,16 +214,15 @@ cmd_mempool(char **tokens,
 	}
 }
 
-static const char cmd_link_help[] =
-"link <link_name>\n"
-"   dev <device_name> | port <port_id>\n"
+static const char cmd_ethdev_help[] =
+"ethdev <ethdev_name>\n"
 "   rxq <n_queues> <queue_size> <mempool_name>\n"
 "   txq <n_queues> <queue_size>\n"
 "   promiscuous on | off\n"
 "   [rss <qid_0> ... <qid_n>]\n";
 
 static void
-cmd_link(char **tokens,
+cmd_ethdev(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
@@ -252,65 +235,51 @@ cmd_link(char **tokens,
 
 	memset(&p, 0, sizeof(p));
 
-	if ((n_tokens < 13) || (n_tokens > 14 + LINK_RXQ_RSS_MAX)) {
+	if ((n_tokens < 11) || (n_tokens > 12 + LINK_RXQ_RSS_MAX)) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 	name = tokens[1];
 
-	if (strcmp(tokens[2], "dev") == 0)
-		p.dev_name = tokens[3];
-	else if (strcmp(tokens[2], "port") == 0) {
-		p.dev_name = NULL;
-
-		if (parser_read_uint16(&p.port_id, tokens[3]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
-			return;
-		}
-	} else {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port");
-		return;
-	}
-
-	if (strcmp(tokens[4], "rxq") != 0) {
+	if (strcmp(tokens[2], "rxq") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
 		return;
 	}
 
-	if (parser_read_uint32(&p.rx.n_queues, tokens[5]) != 0) {
+	if (parser_read_uint32(&p.rx.n_queues, tokens[3]) != 0) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
 		return;
 	}
-	if (parser_read_uint32(&p.rx.queue_size, tokens[6]) != 0) {
+	if (parser_read_uint32(&p.rx.queue_size, tokens[4]) != 0) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
 		return;
 	}
 
-	p.rx.mempool_name = tokens[7];
+	p.rx.mempool_name = tokens[5];
 
-	if (strcmp(tokens[8], "txq") != 0) {
+	if (strcmp(tokens[6], "txq") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
 		return;
 	}
 
-	if (parser_read_uint32(&p.tx.n_queues, tokens[9]) != 0) {
+	if (parser_read_uint32(&p.tx.n_queues, tokens[7]) != 0) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
 		return;
 	}
 
-	if (parser_read_uint32(&p.tx.queue_size, tokens[10]) != 0) {
+	if (parser_read_uint32(&p.tx.queue_size, tokens[8]) != 0) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
 		return;
 	}
 
-	if (strcmp(tokens[11], "promiscuous") != 0) {
+	if (strcmp(tokens[9], "promiscuous") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous");
 		return;
 	}
 
-	if (strcmp(tokens[12], "on") == 0)
+	if (strcmp(tokens[10], "on") == 0)
 		p.promiscuous = 1;
-	else if (strcmp(tokens[12], "off") == 0)
+	else if (strcmp(tokens[10], "off") == 0)
 		p.promiscuous = 0;
 	else {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off");
@@ -319,10 +288,10 @@ cmd_link(char **tokens,
 
 	/* RSS */
 	p.rx.rss = NULL;
-	if (n_tokens > 13) {
+	if (n_tokens > 11) {
 		uint32_t queue_id, i;
 
-		if (strcmp(tokens[13], "rss") != 0) {
+		if (strcmp(tokens[11], "rss") != 0) {
 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
 			return;
 		}
@@ -330,7 +299,7 @@ cmd_link(char **tokens,
 		p.rx.rss = &rss;
 
 		rss.n_queues = 0;
-		for (i = 14; i < n_tokens; i++) {
+		for (i = 12; i < n_tokens; i++) {
 			if (parser_read_uint32(&queue_id, tokens[i]) != 0) {
 				snprintf(out, out_size, MSG_ARG_INVALID,
 					"queue_id");
@@ -406,10 +375,10 @@ print_link_info(struct link *link, char *out, size_t out_size)
 }
 
 /*
- * link show [<link_name>]
+ * ethdev show [<ethdev_name>]
  */
 static void
-cmd_link_show(char **tokens,
+cmd_ethdev_show(char **tokens,
 	      uint32_t n_tokens,
 	      char *out,
 	      size_t out_size,
@@ -2675,7 +2644,7 @@ cmd_help(char **tokens,
 			"Type 'help <command>' for command details.\n\n"
 			"List of commands:\n"
 			"\tmempool\n"
-			"\tlink\n"
+			"\tethdev\n"
 			"\ttap\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
@@ -2711,8 +2680,8 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if (strcmp(tokens[0], "link") == 0) {
-		snprintf(out, out_size, "\n%s\n", cmd_link_help);
+	if (strcmp(tokens[0], "ethdev") == 0) {
+		snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
 		return;
 	}
 
@@ -2966,13 +2935,13 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		return;
 	}
 
-	if (strcmp(tokens[0], "link") == 0) {
+	if (strcmp(tokens[0], "ethdev") == 0) {
 		if ((n_tokens >= 2) && (strcmp(tokens[1], "show") == 0)) {
-			cmd_link_show(tokens, n_tokens, out, out_size, obj);
+			cmd_ethdev_show(tokens, n_tokens, out, out_size, obj);
 			return;
 		}
 
-		cmd_link(tokens, n_tokens, out, out_size, obj);
+		cmd_ethdev(tokens, n_tokens, out, out_size, obj);
 		return;
 	}
 
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index d1f519180e..950ab831fb 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -181,7 +181,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	struct mempool *mempool;
 	uint32_t cpu_id, i;
 	int status;
-	uint16_t port_id;
+	uint16_t port_id = 0;
 
 	/* Check input params */
 	if ((name == NULL) ||
@@ -193,16 +193,9 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 		(params->tx.queue_size == 0))
 		return NULL;
 
-	port_id = params->port_id;
-	if (params->dev_name) {
-		status = rte_eth_dev_get_port_by_name(params->dev_name,
-			&port_id);
-
-		if (status)
-			return NULL;
-	} else
-		if (!rte_eth_dev_is_valid_port(port_id))
-			return NULL;
+	status = rte_eth_dev_get_port_by_name(name, &port_id);
+	if (status)
+		return NULL;
 
 	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
 		return NULL;
@@ -315,7 +308,6 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	/* Node fill in */
 	strlcpy(link->name, name, sizeof(link->name));
 	link->port_id = port_id;
-	rte_eth_dev_get_name_by_port(port_id, link->dev_name);
 	link->n_rxq = params->rx.n_queues;
 	link->n_txq = params->tx.n_queues;
 
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index e63a9c0e9a..af270a8e57 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -63,9 +63,6 @@ struct link_params_rss {
 };
 
 struct link_params {
-	const char *dev_name;
-	uint16_t port_id; /**< Valid only when *dev_name* is NULL. */
-
 	struct {
 		uint32_t n_queues;
 		uint32_t queue_size;
@@ -84,7 +81,6 @@ struct link_params {
 struct link {
 	TAILQ_ENTRY(link) node;
 	char name[NAME_SIZE];
-	char dev_name[NAME_SIZE];
 	uint16_t port_id;
 	uint32_t n_rxq;
 	uint32_t n_txq;
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 16/17] examples/pipelines: remove obsolete tap CLI command
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (13 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 15/17] examples/pipeline: rework the link CLI command Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:36     ` [PATCH V3 17/17] examples/pipeline: call the code generation and build CLI commands Cristian Dumitrescu
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Remove the tap CLI command, as the file descriptor I/O ports of the
pipeline are now configured trough the I/O specification file.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 37 -----------------
 examples/pipeline/obj.c | 89 -----------------------------------------
 examples/pipeline/obj.h | 18 ---------
 3 files changed, 144 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index d56a830fb7..75c32b9089 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -466,32 +466,6 @@ cmd_ring(char **tokens,
 	}
 }
 
-static const char cmd_tap_help[] =
-"tap <tap_name>\n";
-
-static void
-cmd_tap(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
-{
-	struct tap *tap;
-	char *name;
-
-	if (n_tokens < 2) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-	name = tokens[1];
-
-	tap = tap_create(obj, name);
-	if (tap == NULL) {
-		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
-		return;
-	}
-}
-
 static const char cmd_pipeline_codegen_help[] =
 "pipeline codegen <spec_file> <code_file>\n";
 
@@ -2645,7 +2619,6 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
-			"\ttap\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
@@ -2690,11 +2663,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if (strcmp(tokens[0], "tap") == 0) {
-		snprintf(out, out_size, "\n%s\n", cmd_tap_help);
-		return;
-	}
-
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
@@ -2950,11 +2918,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		return;
 	}
 
-	if (strcmp(tokens[0], "tap") == 0) {
-		cmd_tap(tokens, n_tokens, out, out_size, obj);
-		return;
-	}
-
 	if (strcmp(tokens[0], "pipeline") == 0) {
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[1], "codegen") == 0)) {
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 950ab831fb..b7e2316eec 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -35,11 +35,6 @@ TAILQ_HEAD(link_list, link);
  */
 TAILQ_HEAD(ring_list, ring);
 
-/*
- * tap
- */
-TAILQ_HEAD(tap_list, tap);
-
 /*
  * obj
  */
@@ -47,7 +42,6 @@ struct obj {
 	struct mempool_list mempool_list;
 	struct link_list link_list;
 	struct ring_list ring_list;
-	struct tap_list tap_list;
 };
 
 /*
@@ -416,88 +410,6 @@ ring_find(struct obj *obj, const char *name)
 	return NULL;
 }
 
-/*
- * tap
- */
-#define TAP_DEV		"/dev/net/tun"
-
-struct tap *
-tap_find(struct obj *obj, const char *name)
-{
-	struct tap *tap;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(tap, &obj->tap_list, node)
-		if (strcmp(tap->name, name) == 0)
-			return tap;
-
-	return NULL;
-}
-
-struct tap *
-tap_next(struct obj *obj, struct tap *tap)
-{
-	return (tap == NULL) ?
-		TAILQ_FIRST(&obj->tap_list) : TAILQ_NEXT(tap, node);
-}
-
-#ifndef RTE_EXEC_ENV_LINUX
-
-struct tap *
-tap_create(struct obj *obj __rte_unused, const char *name __rte_unused)
-{
-	return NULL;
-}
-
-#else
-
-struct tap *
-tap_create(struct obj *obj, const char *name)
-{
-	struct tap *tap;
-	struct ifreq ifr;
-	int fd, status;
-
-	/* Check input params */
-	if ((name == NULL) ||
-		tap_find(obj, name))
-		return NULL;
-
-	/* Resource create */
-	fd = open(TAP_DEV, O_RDWR | O_NONBLOCK);
-	if (fd < 0)
-		return NULL;
-
-	memset(&ifr, 0, sizeof(ifr));
-	ifr.ifr_flags = IFF_TAP | IFF_NO_PI; /* No packet information */
-	strlcpy(ifr.ifr_name, name, IFNAMSIZ);
-
-	status = ioctl(fd, TUNSETIFF, (void *) &ifr);
-	if (status < 0) {
-		close(fd);
-		return NULL;
-	}
-
-	/* Node allocation */
-	tap = calloc(1, sizeof(struct tap));
-	if (tap == NULL) {
-		close(fd);
-		return NULL;
-	}
-	/* Node fill in */
-	strlcpy(tap->name, name, sizeof(tap->name));
-	tap->fd = fd;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->tap_list, tap, node);
-
-	return tap;
-}
-
-#endif
-
 /*
  * obj
  */
@@ -513,7 +425,6 @@ obj_init(void)
 	TAILQ_INIT(&obj->mempool_list);
 	TAILQ_INIT(&obj->link_list);
 	TAILQ_INIT(&obj->ring_list);
-	TAILQ_INIT(&obj->tap_list);
 
 	return obj;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index af270a8e57..8ea1c414c2 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -121,22 +121,4 @@ ring_create(struct obj *obj,
 struct ring *
 ring_find(struct obj *obj, const char *name);
 
-/*
- * tap
- */
-struct tap {
-	TAILQ_ENTRY(tap) node;
-	char name[NAME_SIZE];
-	int fd;
-};
-
-struct tap *
-tap_find(struct obj *obj, const char *name);
-
-struct tap *
-tap_next(struct obj *obj, struct tap *tap);
-
-struct tap *
-tap_create(struct obj *obj, const char *name);
-
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V3 17/17] examples/pipeline: call the code generation and build CLI commands
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (14 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 16/17] examples/pipelines: remove obsolete tap " Cristian Dumitrescu
@ 2022-07-27 22:36     ` Cristian Dumitrescu
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:36 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Update the example CLI scripts with the commands for code generation
and shared object library build.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/examples/ethdev.io          | 27 +++++++++
 examples/pipeline/examples/fib.cli            | 44 +++++++-------
 examples/pipeline/examples/hash_func.cli      | 41 ++++++-------
 examples/pipeline/examples/l2fwd.cli          | 44 ++++++++------
 examples/pipeline/examples/l2fwd_macswp.cli   | 44 ++++++++------
 .../pipeline/examples/l2fwd_macswp_pcap.cli   | 35 +++++++----
 examples/pipeline/examples/l2fwd_pcap.cli     | 35 +++++++----
 examples/pipeline/examples/learner.cli        | 43 +++++++-------
 examples/pipeline/examples/meter.cli          | 58 +++++++++++--------
 examples/pipeline/examples/mirroring.cli      | 46 ++++++++-------
 examples/pipeline/examples/pcap.io            | 27 +++++++++
 examples/pipeline/examples/recirculation.cli  | 41 ++++++-------
 examples/pipeline/examples/registers.cli      | 53 +++++++++--------
 examples/pipeline/examples/selector.cli       | 55 +++++++++++-------
 examples/pipeline/examples/varbit.cli         | 41 ++++++-------
 examples/pipeline/examples/vxlan.cli          | 48 ++++++++++-----
 examples/pipeline/examples/vxlan_pcap.cli     | 39 +++++++++----
 17 files changed, 444 insertions(+), 277 deletions(-)
 create mode 100644 examples/pipeline/examples/ethdev.io
 create mode 100644 examples/pipeline/examples/pcap.io

diff --git a/examples/pipeline/examples/ethdev.io b/examples/pipeline/examples/ethdev.io
new file mode 100644
index 0000000000..cf2f3e20bd
--- /dev/null
+++ b/examples/pipeline/examples/ethdev.io
@@ -0,0 +1,27 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+;
+; Pipeline packet mirroring.
+;
+mirroring slots 4 sessions 64
+
+;
+; Pipeline input ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port in 0 ethdev 0000:18:00.0 rxq 0 bsz 32
+port in 1 ethdev 0000:18:00.1 rxq 0 bsz 32
+port in 2 ethdev 0000:3b:00.0 rxq 0 bsz 32
+port in 3 ethdev 0000:3b:00.1 rxq 0 bsz 32
+
+;
+; Pipeline output ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port out 0 ethdev 0000:18:00.0 txq 0 bsz 32
+port out 1 ethdev 0000:18:00.1 txq 0 bsz 32
+port out 2 ethdev 0000:3b:00.0 txq 0 bsz 32
+port out 3 ethdev 0000:3b:00.1 txq 0 bsz 32
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 93ab2b08f8..4e30c1320f 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -1,38 +1,38 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/fib.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
 ;
-; Customize the LINK parameters to match your setup.
+; Pipeline code generation & shared object library build.
 ;
-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
+pipeline codegen ./examples/pipeline/examples/fib.spec /tmp/fib.c
+pipeline libbuild /tmp/fib.c /tmp/fib.so
 
 ;
-; PIPELINE0 setup.
+; List of DPDK devices.
 ;
-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
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/fib.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/fib.so io ./examples/pipeline/examples/ethdev.io numa 0
 
 ;
 ; Initial set of table entries.
 ;
-; The table entries can later be updated at run-time through the CLI commands. Once the application
-; has been successfully started, the command to get the CLI prompt is: telnet 0.0.0.0 8086.
+; The table entries can later be updated at run-time through the CLI commands.
 ;
 pipeline PIPELINE0 table routing_table add ./examples/pipeline/examples/fib_routing_table.txt
 pipeline PIPELINE0 selector nexthop_group_table group add
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index d65cd62d17..b2e219e4c9 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -1,32 +1,33 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2022 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/hash_func.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
 ;
-; Customize the LINK parameters to match your setup.
+; Pipeline code generation & shared object library build.
 ;
-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
+pipeline codegen ./examples/pipeline/examples/hash_func.spec /tmp/hash_func.c
+pipeline libbuild /tmp/hash_func.c /tmp/hash_func.so
 
 ;
-; PIPELINE0 setup.
+; List of DPDK devices.
 ;
-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
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/hash_func.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/hash_func.so io ./examples/pipeline/examples/ethdev.io numa 0
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index d89caf2d0a..27e37021b9 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -1,25 +1,35 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-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
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/l2fwd.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
 
-pipeline PIPELINE0 create 0
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd.spec /tmp/l2fwd.c
+pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 
-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
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/l2fwd.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/ethdev.io numa 0
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index 0f2a89ac5b..11bb4543b9 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -1,25 +1,35 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-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
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/l2fwd_macswp.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
 
-pipeline PIPELINE0 create 0
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd_macswp.spec /tmp/l2fwd_macswp.c
+pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 
-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
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/l2fwd_macswp.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/examples/ethdev.io numa 0
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index e9656fe3c2..8724dae3b0 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -1,20 +1,31 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
-
-pipeline PIPELINE0 create 0
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/l2fwd_macswp_pcap.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
 
-pipeline PIPELINE0 port in 0 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 1 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 2 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 3 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd_macswp.spec /tmp/l2fwd_macswp.c
+pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 
-pipeline PIPELINE0 port out 0 sink none
-pipeline PIPELINE0 port out 1 sink none
-pipeline PIPELINE0 port out 2 sink none
-pipeline PIPELINE0 port out 3 sink none
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/l2fwd_macswp.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/examples/pcap.io numa 0
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 23fcb199f1..4db0a0dc56 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -1,20 +1,31 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
-
-pipeline PIPELINE0 create 0
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/l2fwd_pcap.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
 
-pipeline PIPELINE0 port in 0 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 1 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 2 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 3 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd.spec /tmp/l2fwd.c
+pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 
-pipeline PIPELINE0 port out 0 sink none
-pipeline PIPELINE0 port out 1 sink none
-pipeline PIPELINE0 port out 2 sink none
-pipeline PIPELINE0 port out 3 sink none
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/l2fwd.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/pcap.io numa 0
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 688ce34f34..6c8aa3921e 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -1,36 +1,35 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/learner.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
 ;
-; Customize the LINK parameters to match your setup.
+; Pipeline code generation & shared object library build.
 ;
-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
+pipeline codegen ./examples/pipeline/examples/learner.spec /tmp/learner.c
+pipeline libbuild /tmp/learner.c /tmp/learner.so
 
 ;
-; PIPELINE0 setup.
+; List of DPDK devices.
 ;
-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
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/learner.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/learner.so io ./examples/pipeline/examples/ethdev.io numa 0
 
 ;
 ; Pipelines-to-threads mapping.
 ;
 thread 1 pipeline PIPELINE0 enable
-
-; Once the application has started, the command to get the CLI prompt is: telnet 0.0.0.0 8086
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index b29ed24022..c1b88c882a 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -1,31 +1,43 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-; Example command line:
-;	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/meter.cli
-
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/meter.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/meter.spec /tmp/meter.c
+pipeline libbuild /tmp/meter.c /tmp/meter.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below 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
-
-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/meter.spec
-
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/meter.so io ./examples/pipeline/examples/ethdev.io numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
 pipeline PIPELINE0 meter profile platinum add cir 46000000 pir 138000000 cbs 1000000 pbs 1000000
 pipeline PIPELINE0 meter meters from 0 to 15 set profile platinum
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index 46d57db4ec..1d439e04d3 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -1,36 +1,38 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2022 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/mirroring.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
 ;
-; Customize the LINK parameters to match your setup.
+; Pipeline code generation & shared object library build.
 ;
-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
+pipeline codegen ./examples/pipeline/examples/mirroring.spec /tmp/mirroring.c
+pipeline libbuild /tmp/mirroring.c /tmp/mirroring.so
 
 ;
-; PIPELINE0 setup.
+; List of DPDK devices.
 ;
-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
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/mirroring.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/mirroring.so io ./examples/pipeline/examples/ethdev.io numa 0
 
 ;
-; Packet mirroring sessions.
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
 ;
 pipeline PIPELINE0 mirror session 0 port 1 clone fast truncate 0
 pipeline PIPELINE0 mirror session 1 port 2 clone slow truncate 0
diff --git a/examples/pipeline/examples/pcap.io b/examples/pipeline/examples/pcap.io
new file mode 100644
index 0000000000..111f61afae
--- /dev/null
+++ b/examples/pipeline/examples/pcap.io
@@ -0,0 +1,27 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+;
+; Pipeline packet mirroring.
+;
+mirroring slots 4 sessions 64
+
+;
+; Pipeline input ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port in 0 source mempool MEMPOOL0 file ./examples/pipeline/examples/packet.pcap loop 1 packets 0
+port in 1 source mempool MEMPOOL0 file ./examples/pipeline/examples/packet.pcap loop 1 packets 0
+port in 2 source mempool MEMPOOL0 file ./examples/pipeline/examples/packet.pcap loop 1 packets 0
+port in 3 source mempool MEMPOOL0 file ./examples/pipeline/examples/packet.pcap loop 1 packets 0
+
+;
+; Pipeline output ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port out 0 sink file none
+port out 1 sink file none
+port out 2 sink file none
+port out 3 sink file none
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index f855c5c327..52d0894f12 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -1,32 +1,33 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2022 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/recirculation.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
 ;
-; Customize the LINK parameters to match your setup.
+; Pipeline code generation & shared object library build.
 ;
-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
+pipeline codegen ./examples/pipeline/examples/recirculation.spec /tmp/recirculation.c
+pipeline libbuild /tmp/recirculation.c /tmp/recirculation.so
 
 ;
-; PIPELINE0 setup.
+; List of DPDK devices.
 ;
-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
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/recirculation.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/recirculation.so io ./examples/pipeline/examples/ethdev.io numa 0
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index 8d026294cb..3516f76a5b 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -1,28 +1,35 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-; Example command line:
-;	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/registers.cli
-
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/registers.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/registers.spec /tmp/registers.c
+pipeline libbuild /tmp/registers.c /tmp/registers.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below 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
-
-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/registers.spec
-
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/registers.so io ./examples/pipeline/examples/ethdev.io numa 0
+
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index 123782c57b..f0e251b657 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -1,30 +1,45 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/selector.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/selector.spec /tmp/selector.c
+pipeline libbuild /tmp/selector.c /tmp/selector.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below 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
-
-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/selector.spec
-
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/selector.so io ./examples/pipeline/examples/ethdev.io numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
 pipeline PIPELINE0 selector s group add
 pipeline PIPELINE0 selector s group member add ./examples/pipeline/examples/selector.txt
 pipeline PIPELINE0 commit
 pipeline PIPELINE0 selector s show
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 9caeb9ca26..0f89990471 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -1,32 +1,33 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/varbit.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
 ;
-; Customize the LINK parameters to match your setup.
+; Pipeline code generation & shared object library build.
 ;
-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
+pipeline codegen ./examples/pipeline/examples/varbit.spec /tmp/varbit.c
+pipeline libbuild /tmp/varbit.c /tmp/varbit.so
 
 ;
-; PIPELINE0 setup.
+; List of DPDK devices.
 ;
-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
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/varbit.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/varbit.so io ./examples/pipeline/examples/ethdev.io numa 0
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 444f3f7bd8..1fbd1be6e4 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -1,27 +1,43 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-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
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/vxlan.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
 
-pipeline PIPELINE0 create 0
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/vxlan.spec /tmp/vxlan.c
+pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 
-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
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-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
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/vxlan.so io ./examples/pipeline/examples/ethdev.io numa 0
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/vxlan.spec
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
 pipeline PIPELINE0 table vxlan_table add ./examples/pipeline/examples/vxlan_table.txt
 pipeline PIPELINE0 commit
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index 83fca8d0d9..adc7f73312 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -1,22 +1,39 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/vxlan_pcap.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
 
-pipeline PIPELINE0 create 0
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/vxlan.spec /tmp/vxlan.c
+pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 
-pipeline PIPELINE0 port in 0 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 1 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 2 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 3 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
 
-pipeline PIPELINE0 port out 0 sink none
-pipeline PIPELINE0 port out 1 sink none
-pipeline PIPELINE0 port out 2 sink none
-pipeline PIPELINE0 port out 3 sink none
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/vxlan.so io ./examples/pipeline/examples/pcap.io numa 0
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/vxlan.spec
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
 pipeline PIPELINE0 table vxlan_table add ./examples/pipeline/examples/vxlan_table.txt
 pipeline PIPELINE0 commit
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 01/17] pipeline: add pipeline name
  2022-07-27 22:36   ` [PATCH V3 01/17] pipeline: add pipeline name Cristian Dumitrescu
                       ` (15 preceding siblings ...)
  2022-07-27 22:36     ` [PATCH V3 17/17] examples/pipeline: call the code generation and build CLI commands Cristian Dumitrescu
@ 2022-07-27 22:54     ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 02/17] pipeline: move specification data structures to internal header Cristian Dumitrescu
                         ` (16 more replies)
  16 siblings, 17 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add an unique name to every pipeline. This enables the library to
maintain a list of the existing pipeline objects, which can be
queried by the application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/obj.c                  |   2 +-
 lib/pipeline/rte_swx_ctl.c               |  99 +++++++++++++++++++++
 lib/pipeline/rte_swx_ctl.h               |  15 ++++
 lib/pipeline/rte_swx_pipeline.c          | 107 ++++++++++++++++++++++-
 lib/pipeline/rte_swx_pipeline.h          |  18 +++-
 lib/pipeline/rte_swx_pipeline_internal.h |   2 +
 lib/pipeline/version.map                 |   4 +
 7 files changed, 244 insertions(+), 3 deletions(-)

diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index b79f044ac7..967342c580 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -533,7 +533,7 @@ pipeline_create(struct obj *obj, const char *name, int numa_node)
 		return NULL;
 
 	/* Resource create */
-	status = rte_swx_pipeline_config(&p, numa_node);
+	status = rte_swx_pipeline_config(&p, name, numa_node);
 	if (status)
 		goto error;
 
diff --git a/lib/pipeline/rte_swx_ctl.c b/lib/pipeline/rte_swx_ctl.c
index 710e89a46a..1b776fc543 100644
--- a/lib/pipeline/rte_swx_ctl.c
+++ b/lib/pipeline/rte_swx_ctl.c
@@ -9,6 +9,8 @@
 
 #include <rte_common.h>
 #include <rte_byteorder.h>
+#include <rte_tailq.h>
+#include <rte_eal_memconfig.h>
 
 #include <rte_swx_table_selector.h>
 
@@ -1157,12 +1159,103 @@ table_state_create(struct rte_swx_ctl_pipeline *ctl)
 	return status;
 }
 
+/* Global list of pipeline instances. */
+TAILQ_HEAD(rte_swx_ctl_pipeline_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_swx_ctl_pipeline_tailq = {
+	.name = "RTE_SWX_CTL_PIPELINE",
+};
+
+EAL_REGISTER_TAILQ(rte_swx_ctl_pipeline_tailq)
+
+struct rte_swx_ctl_pipeline *
+rte_swx_ctl_pipeline_find(const char *name)
+{
+	struct rte_swx_ctl_pipeline_list *ctl_list;
+	struct rte_tailq_entry *te = NULL;
+
+	if (!name || !name[0] || (strnlen(name, RTE_SWX_CTL_NAME_SIZE) >= RTE_SWX_CTL_NAME_SIZE))
+		return NULL;
+
+	ctl_list = RTE_TAILQ_CAST(rte_swx_ctl_pipeline_tailq.head, rte_swx_ctl_pipeline_list);
+
+	rte_mcfg_tailq_read_lock();
+
+	TAILQ_FOREACH(te, ctl_list, next) {
+		struct rte_swx_ctl_pipeline *ctl = (struct rte_swx_ctl_pipeline *)te->data;
+
+		if (!strncmp(name, ctl->info.name, sizeof(ctl->info.name))) {
+			rte_mcfg_tailq_read_unlock();
+			return ctl;
+		}
+	}
+
+	rte_mcfg_tailq_read_unlock();
+	return NULL;
+}
+
+static int
+ctl_register(struct rte_swx_ctl_pipeline *ctl)
+{
+	struct rte_swx_ctl_pipeline_list *ctl_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ctl_list = RTE_TAILQ_CAST(rte_swx_ctl_pipeline_tailq.head, rte_swx_ctl_pipeline_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ctl_list, next) {
+		struct rte_swx_ctl_pipeline *ctl_crt = (struct rte_swx_ctl_pipeline *)te->data;
+
+		if (!strncmp(ctl->info.name, ctl_crt->info.name, sizeof(ctl->info.name))) {
+			rte_mcfg_tailq_write_unlock();
+			return -EEXIST;
+		}
+	}
+
+	te = calloc(1, sizeof(struct rte_tailq_entry));
+	if (!te) {
+		rte_mcfg_tailq_write_unlock();
+		return -ENOMEM;
+	}
+
+	te->data = (void *)ctl;
+	TAILQ_INSERT_TAIL(ctl_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return 0;
+}
+
+static void
+ctl_unregister(struct rte_swx_ctl_pipeline *ctl)
+{
+	struct rte_swx_ctl_pipeline_list *ctl_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ctl_list = RTE_TAILQ_CAST(rte_swx_ctl_pipeline_tailq.head, rte_swx_ctl_pipeline_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ctl_list, next) {
+		if (te->data == (void *)ctl) {
+			TAILQ_REMOVE(ctl_list, te, next);
+			rte_mcfg_tailq_write_unlock();
+			free(te);
+			return;
+		}
+	}
+
+	rte_mcfg_tailq_write_unlock();
+}
+
 void
 rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline *ctl)
 {
 	if (!ctl)
 		return;
 
+	if (ctl->info.name[0])
+		ctl_unregister(ctl);
+
 	action_free(ctl);
 
 	table_state_free(ctl);
@@ -1441,6 +1534,12 @@ rte_swx_ctl_pipeline_create(struct rte_swx_pipeline *p)
 	if (status)
 		goto error;
 
+	if (ctl->info.name[0]) {
+		status = ctl_register(ctl);
+		if (status)
+			goto error;
+	}
+
 	return ctl;
 
 error:
diff --git a/lib/pipeline/rte_swx_ctl.h b/lib/pipeline/rte_swx_ctl.h
index d771389d26..63ee479e47 100644
--- a/lib/pipeline/rte_swx_ctl.h
+++ b/lib/pipeline/rte_swx_ctl.h
@@ -35,6 +35,9 @@ struct rte_swx_pipeline;
 
 /** Pipeline info. */
 struct rte_swx_ctl_pipeline_info {
+	/** Pipeline name. */
+	char name[RTE_SWX_CTL_NAME_SIZE];
+
 	/** Number of input ports. */
 	uint32_t n_ports_in;
 
@@ -812,6 +815,18 @@ rte_swx_pipeline_table_state_set(struct rte_swx_pipeline *p,
 /** Pipeline control opaque data structure. */
 struct rte_swx_ctl_pipeline;
 
+/**
+ * Pipeline control find
+ *
+ * @param[in] name
+ *   Pipeline name.
+ * @return
+ *   Valid pipeline control handle if found or NULL otherwise.
+ */
+__rte_experimental
+struct rte_swx_ctl_pipeline *
+rte_swx_ctl_pipeline_find(const char *name);
+
 /**
  * Pipeline control create
  *
diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 3e1c6e9edb..c8ccded4f8 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -6,6 +6,8 @@
 #include <errno.h>
 #include <dlfcn.h>
 
+#include <rte_tailq.h>
+#include <rte_eal_memconfig.h>
 #include <rte_jhash.h>
 #include <rte_hash_crc.h>
 
@@ -9578,6 +9580,95 @@ metarray_free(struct rte_swx_pipeline *p)
 /*
  * Pipeline.
  */
+
+/* Global list of pipeline instances. */
+TAILQ_HEAD(rte_swx_pipeline_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_swx_pipeline_tailq = {
+	.name = "RTE_SWX_PIPELINE",
+};
+
+EAL_REGISTER_TAILQ(rte_swx_pipeline_tailq)
+
+struct rte_swx_pipeline *
+rte_swx_pipeline_find(const char *name)
+{
+	struct rte_swx_pipeline_list *pipeline_list;
+	struct rte_tailq_entry *te = NULL;
+
+	if (!name || !name[0] || (strnlen(name, RTE_SWX_NAME_SIZE) >= RTE_SWX_NAME_SIZE))
+		return NULL;
+
+	pipeline_list = RTE_TAILQ_CAST(rte_swx_pipeline_tailq.head, rte_swx_pipeline_list);
+
+	rte_mcfg_tailq_read_lock();
+
+	TAILQ_FOREACH(te, pipeline_list, next) {
+		struct rte_swx_pipeline *p = (struct rte_swx_pipeline *)te->data;
+
+		if (!strncmp(name, p->name, sizeof(p->name))) {
+			rte_mcfg_tailq_read_unlock();
+			return p;
+		}
+	}
+
+	rte_mcfg_tailq_read_unlock();
+	return NULL;
+}
+
+static int
+pipeline_register(struct rte_swx_pipeline *p)
+{
+	struct rte_swx_pipeline_list *pipeline_list;
+	struct rte_tailq_entry *te = NULL;
+
+	pipeline_list = RTE_TAILQ_CAST(rte_swx_pipeline_tailq.head, rte_swx_pipeline_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, pipeline_list, next) {
+		struct rte_swx_pipeline *pipeline = (struct rte_swx_pipeline *)te->data;
+
+		if (!strncmp(p->name, pipeline->name, sizeof(p->name))) {
+			rte_mcfg_tailq_write_unlock();
+			return -EEXIST;
+		}
+	}
+
+	te = calloc(1, sizeof(struct rte_tailq_entry));
+	if (!te) {
+		rte_mcfg_tailq_write_unlock();
+		return -ENOMEM;
+	}
+
+	te->data = (void *)p;
+	TAILQ_INSERT_TAIL(pipeline_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return 0;
+}
+
+static void
+pipeline_unregister(struct rte_swx_pipeline *p)
+{
+	struct rte_swx_pipeline_list *pipeline_list;
+	struct rte_tailq_entry *te = NULL;
+
+	pipeline_list = RTE_TAILQ_CAST(rte_swx_pipeline_tailq.head, rte_swx_pipeline_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, pipeline_list, next) {
+		if (te->data == (void *)p) {
+			TAILQ_REMOVE(pipeline_list, te, next);
+			rte_mcfg_tailq_write_unlock();
+			free(te);
+			return;
+		}
+	}
+
+	rte_mcfg_tailq_write_unlock();
+}
+
 void
 rte_swx_pipeline_free(struct rte_swx_pipeline *p)
 {
@@ -9586,6 +9677,9 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)
 	if (!p)
 		return;
 
+	if (p->name[0])
+		pipeline_unregister(p);
+
 	lib = p->lib;
 
 	free(p->instruction_data);
@@ -9720,13 +9814,14 @@ hash_funcs_register(struct rte_swx_pipeline *p)
 }
 
 int
-rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
+rte_swx_pipeline_config(struct rte_swx_pipeline **p, const char *name, int numa_node)
 {
 	struct rte_swx_pipeline *pipeline = NULL;
 	int status = 0;
 
 	/* Check input parameters. */
 	CHECK(p, EINVAL);
+	CHECK(!name || (strnlen(name, RTE_SWX_NAME_SIZE) < RTE_SWX_NAME_SIZE), EINVAL);
 
 	/* Memory allocation. */
 	pipeline = calloc(1, sizeof(struct rte_swx_pipeline));
@@ -9736,6 +9831,9 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
 	}
 
 	/* Initialization. */
+	if (name)
+		strcpy(pipeline->name, name);
+
 	TAILQ_INIT(&pipeline->struct_types);
 	TAILQ_INIT(&pipeline->port_in_types);
 	TAILQ_INIT(&pipeline->ports_in);
@@ -9776,6 +9874,12 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
 	if (status)
 		goto error;
 
+	if (pipeline->name[0]) {
+		status = pipeline_register(pipeline);
+		if (status)
+			goto error;
+	}
+
 	*p = pipeline;
 	return 0;
 
@@ -9966,6 +10070,7 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p,
 	TAILQ_FOREACH(table, &p->tables, node)
 		n_tables++;
 
+	strcpy(pipeline->name, p->name);
 	pipeline->n_ports_in = p->n_ports_in;
 	pipeline->n_ports_out = p->n_ports_out;
 	pipeline->n_mirroring_slots = p->n_mirroring_slots;
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index c41ca5cb15..ef50a0fa70 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -44,22 +44,38 @@ extern "C" {
 /** Pipeline opaque data structure. */
 struct rte_swx_pipeline;
 
+/**
+ * Pipeline find
+ *
+ * @param[in] name
+ *   Pipeline name.
+ * @return
+ *   Valid pipeline handle if found or NULL otherwise.
+ */
+__rte_experimental
+struct rte_swx_pipeline *
+rte_swx_pipeline_find(const char *name);
+
 /**
  * Pipeline configure
  *
  * @param[out] p
  *   Pipeline handle. Must point to valid memory. Contains valid pipeline handle
  *   when the function returns successfully.
+ * @param[in] name
+ *   Pipeline unique name.
  * @param[in] numa_node
  *   Non-Uniform Memory Access (NUMA) node.
  * @return
  *   0 on success or the following error codes otherwise:
  *   -EINVAL: Invalid argument;
- *   -ENOMEM: Not enough space/cannot allocate memory.
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Pipeline with this name already exists.
  */
 __rte_experimental
 int
 rte_swx_pipeline_config(struct rte_swx_pipeline **p,
+			const char *name,
 			int numa_node);
 
 /*
diff --git a/lib/pipeline/rte_swx_pipeline_internal.h b/lib/pipeline/rte_swx_pipeline_internal.h
index a35635efb7..588cad62b5 100644
--- a/lib/pipeline/rte_swx_pipeline_internal.h
+++ b/lib/pipeline/rte_swx_pipeline_internal.h
@@ -1459,6 +1459,8 @@ instr_operand_nbo(struct thread *t, const struct instr_operand *x)
 #endif
 
 struct rte_swx_pipeline {
+	char name[RTE_SWX_NAME_SIZE];
+
 	struct struct_type_tailq struct_types;
 	struct port_in_type_tailq port_in_types;
 	struct port_in_tailq ports_in;
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 8312307a7a..50029aadcf 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -145,4 +145,8 @@ EXPERIMENTAL {
 	rte_swx_ctl_pipeline_learner_timeout_get;
 	rte_swx_ctl_pipeline_learner_timeout_set;
 	rte_swx_pipeline_hash_func_register;
+
+	#added in 22.11
+	rte_swx_ctl_pipeline_find;
+	rte_swx_pipeline_find;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 02/17] pipeline: move specification data structures to internal header
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 03/17] pipeline: add pipeline specification data structure Cristian Dumitrescu
                         ` (15 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Move all the pipeline object specification data structures to an
internal header file.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 126 +------------------
 lib/pipeline/rte_swx_pipeline_spec.h | 176 +++++++++++++++++++++++++++
 2 files changed, 177 insertions(+), 125 deletions(-)
 create mode 100644 lib/pipeline/rte_swx_pipeline_spec.h

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 904b9eb471..5e07b4f794 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -9,7 +9,7 @@
 
 #include <rte_common.h>
 
-#include "rte_swx_pipeline.h"
+#include "rte_swx_pipeline_spec.h"
 
 #ifndef MAX_LINE_LENGTH
 #define MAX_LINE_LENGTH 2048
@@ -34,15 +34,7 @@
 
 /*
  * extobj.
- *
- * extobj OBJ_NAME instanceof OBJ_TYPE [ pragma OBJ_CREATE_ARGS ]
  */
-struct extobj_spec {
-	char *name;
-	char *extern_type_name;
-	char *pragma;
-};
-
 static void
 extobj_spec_free(struct extobj_spec *s)
 {
@@ -104,18 +96,7 @@ extobj_statement_parse(struct extobj_spec *s,
 /*
  * struct.
  *
- * struct STRUCT_TYPE_NAME {
- *	bit<SIZE> | varbit<SIZE> FIELD_NAME
- *	...
- * }
  */
-struct struct_spec {
-	char *name;
-	struct rte_swx_field_params *fields;
-	uint32_t n_fields;
-	int varbit;
-};
-
 static void
 struct_spec_free(struct struct_spec *s)
 {
@@ -293,13 +274,7 @@ struct_block_parse(struct struct_spec *s,
 /*
  * header.
  *
- * header HEADER_NAME instanceof STRUCT_TYPE_NAME
  */
-struct header_spec {
-	char *name;
-	char *struct_type_name;
-};
-
 static void
 header_spec_free(struct header_spec *s)
 {
@@ -351,12 +326,7 @@ header_statement_parse(struct header_spec *s,
 /*
  * metadata.
  *
- * metadata instanceof STRUCT_TYPE_NAME
  */
-struct metadata_spec {
-	char *struct_type_name;
-};
-
 static void
 metadata_spec_free(struct metadata_spec *s)
 {
@@ -400,18 +370,7 @@ metadata_statement_parse(struct metadata_spec *s,
 /*
  * action.
  *
- * action ACTION_NAME args none | instanceof STRUCT_TYPE_NAME {
- *	INSTRUCTION
- *	...
- * }
  */
-struct action_spec {
-	char *name;
-	char *args_struct_type_name;
-	const char **instructions;
-	uint32_t n_instructions;
-};
-
 static void
 action_spec_free(struct action_spec *s)
 {
@@ -540,29 +499,7 @@ action_block_parse(struct action_spec *s,
 /*
  * table.
  *
- * table TABLE_NAME {
- *	key {
- *		MATCH_FIELD_NAME exact | wildcard | lpm
- *		...
- *	}
- *	actions {
- *		ACTION_NAME [ @tableonly | @defaultonly ]
- *		...
- *	}
- *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
- *	instanceof TABLE_TYPE_NAME
- *	pragma ARGS
- *	size SIZE
- * }
  */
-struct table_spec {
-	char *name;
-	struct rte_swx_pipeline_table_params params;
-	char *recommended_table_type_name;
-	char *args;
-	uint32_t size;
-};
-
 static void
 table_spec_free(struct table_spec *s)
 {
@@ -1084,22 +1021,7 @@ table_block_parse(struct table_spec *s,
 /*
  * selector.
  *
- * selector SELECTOR_NAME {
- *	group_id FIELD_NAME
- *	selector {
- *		FIELD_NAME
- *		...
- *	}
- *	member_id FIELD_NAME
- *	n_groups N_GROUPS
- *	n_members_per_group N_MEMBERS_PER_GROUP
- * }
  */
-struct selector_spec {
-	char *name;
-	struct rte_swx_pipeline_selector_params params;
-};
-
 static void
 selector_spec_free(struct selector_spec *s)
 {
@@ -1385,31 +1307,7 @@ selector_block_parse(struct selector_spec *s,
 /*
  * learner.
  *
- * learner LEARNER_NAME {
- *	key {
- *		MATCH_FIELD_NAME
- *		...
- *	}
- *	actions {
- *		ACTION_NAME [ @tableonly | @defaultonly]
- *		...
- *	}
- *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
- *	size SIZE
- *	timeout {
- *		TIMEOUT_IN_SECONDS
- *		...
- *	}
- * }
  */
-struct learner_spec {
-	char *name;
-	struct rte_swx_pipeline_learner_params params;
-	uint32_t size;
-	uint32_t *timeout;
-	uint32_t n_timeouts;
-};
-
 static void
 learner_spec_free(struct learner_spec *s)
 {
@@ -1958,14 +1856,7 @@ learner_block_parse(struct learner_spec *s,
 /*
  * regarray.
  *
- * regarray NAME size SIZE initval INITVAL
  */
-struct regarray_spec {
-	char *name;
-	uint64_t init_val;
-	uint32_t size;
-};
-
 static void
 regarray_spec_free(struct regarray_spec *s)
 {
@@ -2033,13 +1924,7 @@ regarray_statement_parse(struct regarray_spec *s,
 /*
  * metarray.
  *
- * metarray NAME size SIZE
  */
-struct metarray_spec {
-	char *name;
-	uint32_t size;
-};
-
 static void
 metarray_spec_free(struct metarray_spec *s)
 {
@@ -2095,16 +1980,7 @@ metarray_statement_parse(struct metarray_spec *s,
 /*
  * apply.
  *
- * apply {
- *	INSTRUCTION
- *	...
- * }
  */
-struct apply_spec {
-	const char **instructions;
-	uint32_t n_instructions;
-};
-
 static void
 apply_spec_free(struct apply_spec *s)
 {
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
new file mode 100644
index 0000000000..8458de878a
--- /dev/null
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#include <stdint.h>
+#include <stdio.h>
+
+#include <rte_common.h>
+
+#include <rte_swx_pipeline.h>
+
+/*
+ * extobj.
+ *
+ * extobj OBJ_NAME instanceof OBJ_TYPE [ pragma OBJ_CREATE_ARGS ]
+ */
+struct extobj_spec {
+	char *name;
+	char *extern_type_name;
+	char *pragma;
+};
+
+/*
+ * struct.
+ *
+ * struct STRUCT_TYPE_NAME {
+ *	bit<SIZE> | varbit<SIZE> FIELD_NAME
+ *	...
+ * }
+ */
+struct struct_spec {
+	char *name;
+	struct rte_swx_field_params *fields;
+	uint32_t n_fields;
+	int varbit;
+};
+
+/*
+ * header.
+ *
+ * header HEADER_NAME instanceof STRUCT_TYPE_NAME
+ */
+struct header_spec {
+	char *name;
+	char *struct_type_name;
+};
+
+/*
+ * metadata.
+ *
+ * metadata instanceof STRUCT_TYPE_NAME
+ */
+struct metadata_spec {
+	char *struct_type_name;
+};
+
+/*
+ * action.
+ *
+ * action ACTION_NAME args none | instanceof STRUCT_TYPE_NAME {
+ *	INSTRUCTION
+ *	...
+ * }
+ */
+struct action_spec {
+	char *name;
+	char *args_struct_type_name;
+	const char **instructions;
+	uint32_t n_instructions;
+};
+
+/*
+ * table.
+ *
+ * table TABLE_NAME {
+ *	key {
+ *		MATCH_FIELD_NAME exact | wildcard | lpm
+ *		...
+ *	}
+ *	actions {
+ *		ACTION_NAME [ @tableonly | @defaultonly ]
+ *		...
+ *	}
+ *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
+ *	instanceof TABLE_TYPE_NAME
+ *	pragma ARGS
+ *	size SIZE
+ * }
+ */
+struct table_spec {
+	char *name;
+	struct rte_swx_pipeline_table_params params;
+	char *recommended_table_type_name;
+	char *args;
+	uint32_t size;
+};
+
+/*
+ * selector.
+ *
+ * selector SELECTOR_NAME {
+ *	group_id FIELD_NAME
+ *	selector {
+ *		FIELD_NAME
+ *		...
+ *	}
+ *	member_id FIELD_NAME
+ *	n_groups N_GROUPS
+ *	n_members_per_group N_MEMBERS_PER_GROUP
+ * }
+ */
+struct selector_spec {
+	char *name;
+	struct rte_swx_pipeline_selector_params params;
+};
+
+/*
+ * learner.
+ *
+ * learner LEARNER_NAME {
+ *	key {
+ *		MATCH_FIELD_NAME
+ *		...
+ *	}
+ *	actions {
+ *		ACTION_NAME [ @tableonly | @defaultonly]
+ *		...
+ *	}
+ *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
+ *	size SIZE
+ *	timeout {
+ *		TIMEOUT_IN_SECONDS
+ *		...
+ *	}
+ * }
+ */
+struct learner_spec {
+	char *name;
+	struct rte_swx_pipeline_learner_params params;
+	uint32_t size;
+	uint32_t *timeout;
+	uint32_t n_timeouts;
+};
+
+/*
+ * regarray.
+ *
+ * regarray NAME size SIZE initval INITVAL
+ */
+struct regarray_spec {
+	char *name;
+	uint64_t init_val;
+	uint32_t size;
+};
+
+/*
+ * metarray.
+ *
+ * metarray NAME size SIZE
+ */
+struct metarray_spec {
+	char *name;
+	uint32_t size;
+};
+
+/*
+ * apply.
+ *
+ * apply {
+ *	INSTRUCTION
+ *	...
+ * }
+ */
+struct apply_spec {
+	const char **instructions;
+	uint32_t n_instructions;
+};
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 03/17] pipeline: add pipeline specification data structure
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 02/17] pipeline: move specification data structures to internal header Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 04/17] pipeline: rework the specification file-based pipeline build Cristian Dumitrescu
                         ` (14 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add specification data structure for the entire pipeline.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 21 ++++++++++++++++++
 lib/pipeline/rte_swx_pipeline_spec.h | 32 ++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 5e07b4f794..642091b678 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2082,6 +2082,27 @@ apply_block_parse(struct apply_spec *s,
 /*
  * Pipeline.
  */
+void
+pipeline_spec_free(struct pipeline_spec *s)
+{
+	if (!s)
+		return;
+
+	free(s->extobjs);
+	free(s->structs);
+	free(s->headers);
+	free(s->metadata);
+	free(s->actions);
+	free(s->tables);
+	free(s->selectors);
+	free(s->learners);
+	free(s->regarrays);
+	free(s->metarrays);
+	free(s->apply);
+
+	memset(s, 0, sizeof(struct pipeline_spec));
+}
+
 int
 rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				 FILE *spec,
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 8458de878a..e1170a33b1 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -174,3 +174,35 @@ struct apply_spec {
 	const char **instructions;
 	uint32_t n_instructions;
 };
+
+/*
+ * Pipeline.
+ */
+struct pipeline_spec {
+	struct extobj_spec *extobjs;
+	struct struct_spec *structs;
+	struct header_spec *headers;
+	struct metadata_spec *metadata;
+	struct action_spec *actions;
+	struct table_spec *tables;
+	struct selector_spec *selectors;
+	struct learner_spec *learners;
+	struct regarray_spec *regarrays;
+	struct metarray_spec *metarrays;
+	struct apply_spec *apply;
+
+	uint32_t n_extobjs;
+	uint32_t n_structs;
+	uint32_t n_headers;
+	uint32_t n_metadata;
+	uint32_t n_actions;
+	uint32_t n_tables;
+	uint32_t n_selectors;
+	uint32_t n_learners;
+	uint32_t n_regarrays;
+	uint32_t n_metarrays;
+	uint32_t n_apply;
+};
+
+void
+pipeline_spec_free(struct pipeline_spec *s);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 04/17] pipeline: rework the specification file-based pipeline build
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 02/17] pipeline: move specification data structures to internal header Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 03/17] pipeline: add pipeline specification data structure Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 05/17] pipeline: generate the code for pipeline specification structure Cristian Dumitrescu
                         ` (13 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Rework the specification file-based pipeline build operation to first
parse the specification file into the previously introduced pipeline
specification data structure, then use this structure to configure
and build the pipeline.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 478 +++++++++++++++++++++------
 lib/pipeline/rte_swx_pipeline_spec.h |   9 +
 2 files changed, 385 insertions(+), 102 deletions(-)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 642091b678..62929a9da6 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2103,11 +2103,10 @@ pipeline_spec_free(struct pipeline_spec *s)
 	memset(s, 0, sizeof(struct pipeline_spec));
 }
 
-int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec,
-				 uint32_t *err_line,
-				 const char **err_msg)
+struct pipeline_spec *
+pipeline_spec_parse(FILE *spec,
+		    uint32_t *err_line,
+		    const char **err_msg)
 {
 	struct extobj_spec extobj_spec = {0};
 	struct struct_spec struct_spec = {0};
@@ -2120,26 +2119,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 	struct regarray_spec regarray_spec = {0};
 	struct metarray_spec metarray_spec = {0};
 	struct apply_spec apply_spec = {0};
-	uint32_t n_lines;
+	struct pipeline_spec *s = NULL;
+	uint32_t n_lines = 0;
 	uint32_t block_mask = 0;
-	int status;
+	int status = 0;
 
 	/* Check the input arguments. */
-	if (!p) {
+	if (!spec) {
 		if (err_line)
-			*err_line = 0;
+			*err_line = n_lines;
 		if (err_msg)
-			*err_msg = "Null pipeline argument.";
+			*err_msg = "Invalid input argument.";
 		status = -EINVAL;
 		goto error;
 	}
 
-	if (!spec) {
+	/* Memory allocation. */
+	s = calloc(sizeof(struct pipeline_spec), 1);
+	if (!s) {
 		if (err_line)
-			*err_line = 0;
+			*err_line = n_lines;
 		if (err_msg)
-			*err_msg = "Null specification file argument.";
-		status = -EINVAL;
+			*err_msg = "Memory allocation failed.";
+		status = -ENOMEM;
 		goto error;
 	}
 
@@ -2200,6 +2202,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* struct block. */
 		if (block_mask & (1 << STRUCT_BLOCK)) {
+			struct struct_spec *new_structs;
+
 			status = struct_block_parse(&struct_spec,
 						    &block_mask,
 						    tokens,
@@ -2214,26 +2218,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_struct_type_register(p,
-				struct_spec.name,
-				struct_spec.fields,
-				struct_spec.n_fields,
-				struct_spec.varbit);
-			if (status) {
+			new_structs = realloc(s->structs,
+					      (s->n_structs + 1) * sizeof(struct struct_spec));
+			if (!new_structs) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Struct registration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			struct_spec_free(&struct_spec);
+			s->structs = new_structs;
+			memcpy(&s->structs[s->n_structs], &struct_spec, sizeof(struct struct_spec));
+			s->n_structs++;
+			memset(&struct_spec, 0, sizeof(struct struct_spec));
 
 			continue;
 		}
 
 		/* action block. */
 		if (block_mask & (1 << ACTION_BLOCK)) {
+			struct action_spec *new_actions;
+
 			status = action_block_parse(&action_spec,
 						    &block_mask,
 						    tokens,
@@ -2248,26 +2255,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_action_config(p,
-				action_spec.name,
-				action_spec.args_struct_type_name,
-				action_spec.instructions,
-				action_spec.n_instructions);
-			if (status) {
+			new_actions = realloc(s->actions,
+					      (s->n_actions + 1) * sizeof(struct action_spec));
+			if (!new_actions) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Action config error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			action_spec_free(&action_spec);
+			s->actions = new_actions;
+			memcpy(&s->actions[s->n_actions], &action_spec, sizeof(struct action_spec));
+			s->n_actions++;
+			memset(&action_spec, 0, sizeof(struct action_spec));
 
 			continue;
 		}
 
 		/* table block. */
 		if (block_mask & (1 << TABLE_BLOCK)) {
+			struct table_spec *new_tables;
+
 			status = table_block_parse(&table_spec,
 						   &block_mask,
 						   tokens,
@@ -2282,27 +2292,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_table_config(p,
-				table_spec.name,
-				&table_spec.params,
-				table_spec.recommended_table_type_name,
-				table_spec.args,
-				table_spec.size);
-			if (status) {
+			new_tables = realloc(s->tables,
+					     (s->n_tables + 1) * sizeof(struct table_spec));
+			if (!new_tables) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Table configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			table_spec_free(&table_spec);
+			s->tables = new_tables;
+			memcpy(&s->tables[s->n_tables], &table_spec, sizeof(struct table_spec));
+			s->n_tables++;
+			memset(&table_spec, 0, sizeof(struct table_spec));
 
 			continue;
 		}
 
 		/* selector block. */
 		if (block_mask & (1 << SELECTOR_BLOCK)) {
+			struct selector_spec *new_selectors;
+
 			status = selector_block_parse(&selector_spec,
 						      &block_mask,
 						      tokens,
@@ -2317,24 +2329,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_selector_config(p,
-				selector_spec.name,
-				&selector_spec.params);
-			if (status) {
+			new_selectors = realloc(s->selectors,
+				(s->n_selectors + 1) * sizeof(struct selector_spec));
+			if (!new_selectors) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Selector configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			selector_spec_free(&selector_spec);
+			s->selectors = new_selectors;
+			memcpy(&s->selectors[s->n_selectors],
+			       &selector_spec,
+			       sizeof(struct selector_spec));
+			s->n_selectors++;
+			memset(&selector_spec, 0, sizeof(struct selector_spec));
 
 			continue;
 		}
 
 		/* learner block. */
 		if (block_mask & (1 << LEARNER_BLOCK)) {
+			struct learner_spec *new_learners;
+
 			status = learner_block_parse(&learner_spec,
 						     &block_mask,
 						     tokens,
@@ -2349,27 +2368,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_learner_config(p,
-				learner_spec.name,
-				&learner_spec.params,
-				learner_spec.size,
-				learner_spec.timeout,
-				learner_spec.n_timeouts);
-			if (status) {
+			new_learners = realloc(s->learners,
+					       (s->n_learners + 1) * sizeof(struct learner_spec));
+			if (!new_learners) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Learner table configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			learner_spec_free(&learner_spec);
+			s->learners = new_learners;
+			memcpy(&s->learners[s->n_learners],
+			       &learner_spec,
+			       sizeof(struct learner_spec));
+			s->n_learners++;
+			memset(&learner_spec, 0, sizeof(struct learner_spec));
 
 			continue;
 		}
 
 		/* apply block. */
 		if (block_mask & (1 << APPLY_BLOCK)) {
+			struct apply_spec *new_apply;
+
 			status = apply_block_parse(&apply_spec,
 						   &block_mask,
 						   tokens,
@@ -2384,24 +2407,28 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_instructions_config(p,
-				apply_spec.instructions,
-				apply_spec.n_instructions);
-			if (status) {
+			new_apply = realloc(s->apply, (s->n_apply + 1) * sizeof(struct apply_spec));
+			if (!new_apply) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Pipeline instructions err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			apply_spec_free(&apply_spec);
+			s->apply = new_apply;
+			memcpy(&s->apply[s->n_apply], &apply_spec, sizeof(struct apply_spec));
+			s->n_apply++;
+			memset(&apply_spec, 0, sizeof(struct apply_spec));
 
 			continue;
 		}
 
 		/* extobj. */
 		if (!strcmp(tokens[0], "extobj")) {
+			struct extobj_spec *new_extobjs;
+
 			status = extobj_statement_parse(&extobj_spec,
 							tokens,
 							n_tokens,
@@ -2411,19 +2438,21 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_extern_object_config(p,
-				extobj_spec.name,
-				extobj_spec.extern_type_name,
-				extobj_spec.pragma);
-			if (status) {
+			new_extobjs = realloc(s->extobjs,
+					      (s->n_extobjs + 1) * sizeof(struct extobj_spec));
+			if (!new_extobjs) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Extern object config err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			extobj_spec_free(&extobj_spec);
+			s->extobjs = new_extobjs;
+			memcpy(&s->extobjs[s->n_extobjs], &extobj_spec, sizeof(struct extobj_spec));
+			s->n_extobjs++;
+			memset(&extobj_spec, 0, sizeof(struct extobj_spec));
 
 			continue;
 		}
@@ -2445,6 +2474,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* header. */
 		if (!strcmp(tokens[0], "header")) {
+			struct header_spec *new_headers;
+
 			status = header_statement_parse(&header_spec,
 							tokens,
 							n_tokens,
@@ -2454,24 +2485,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_packet_header_register(p,
-				header_spec.name,
-				header_spec.struct_type_name);
-			if (status) {
+			new_headers = realloc(s->headers,
+					      (s->n_headers + 1) * sizeof(struct header_spec));
+			if (!new_headers) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Header registration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			header_spec_free(&header_spec);
+			s->headers = new_headers;
+			memcpy(&s->headers[s->n_headers], &header_spec, sizeof(struct header_spec));
+			s->n_headers++;
+			memset(&header_spec, 0, sizeof(struct header_spec));
 
 			continue;
 		}
 
 		/* metadata. */
 		if (!strcmp(tokens[0], "metadata")) {
+			struct metadata_spec *new_metadata;
+
 			status = metadata_statement_parse(&metadata_spec,
 							  tokens,
 							  n_tokens,
@@ -2481,17 +2517,23 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_packet_metadata_register(p,
-				metadata_spec.struct_type_name);
-			if (status) {
+			new_metadata = realloc(s->metadata,
+					       (s->n_metadata + 1) * sizeof(struct metadata_spec));
+			if (!new_metadata) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Meta-data reg err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			metadata_spec_free(&metadata_spec);
+			s->metadata = new_metadata;
+			memcpy(&s->metadata[s->n_metadata],
+			       &metadata_spec,
+			       sizeof(struct metadata_spec));
+			s->n_metadata++;
+			memset(&metadata_spec, 0, sizeof(struct metadata_spec));
 
 			continue;
 		}
@@ -2558,6 +2600,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* regarray. */
 		if (!strcmp(tokens[0], "regarray")) {
+			struct regarray_spec *new_regarrays;
+
 			status = regarray_statement_parse(&regarray_spec,
 							  tokens,
 							  n_tokens,
@@ -2567,25 +2611,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_regarray_config(p,
-				regarray_spec.name,
-				regarray_spec.size,
-				regarray_spec.init_val);
-			if (status) {
+			new_regarrays = realloc(s->regarrays,
+				(s->n_regarrays + 1) * sizeof(struct regarray_spec));
+			if (!new_regarrays) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Register array configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			regarray_spec_free(&regarray_spec);
+			s->regarrays = new_regarrays;
+			memcpy(&s->regarrays[s->n_regarrays],
+			       &regarray_spec,
+			       sizeof(struct regarray_spec));
+			s->n_regarrays++;
+			memset(&regarray_spec, 0, sizeof(struct regarray_spec));
 
 			continue;
 		}
 
 		/* metarray. */
 		if (!strcmp(tokens[0], "metarray")) {
+			struct metarray_spec *new_metarrays;
+
 			status = metarray_statement_parse(&metarray_spec,
 							  tokens,
 							  n_tokens,
@@ -2595,18 +2645,23 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_metarray_config(p,
-				metarray_spec.name,
-				metarray_spec.size);
-			if (status) {
+			new_metarrays = realloc(s->metarrays,
+				(s->n_metarrays + 1) * sizeof(struct metarray_spec));
+			if (!new_metarrays) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Meter array configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			metarray_spec_free(&metarray_spec);
+			s->metarrays = new_metarrays;
+			memcpy(&s->metarrays[s->n_metarrays],
+			       &metarray_spec,
+			       sizeof(struct metarray_spec));
+			s->n_metarrays++;
+			memset(&metarray_spec, 0, sizeof(struct metarray_spec));
 
 			continue;
 		}
@@ -2644,17 +2699,7 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 		goto error;
 	}
 
-	/* Pipeline build. */
-	status = rte_swx_pipeline_build(p);
-	if (status) {
-		if (err_line)
-			*err_line = n_lines;
-		if (err_msg)
-			*err_msg = "Pipeline build error.";
-		goto error;
-	}
-
-	return 0;
+	return s;
 
 error:
 	extobj_spec_free(&extobj_spec);
@@ -2668,5 +2713,234 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 	regarray_spec_free(&regarray_spec);
 	metarray_spec_free(&metarray_spec);
 	apply_spec_free(&apply_spec);
+	pipeline_spec_free(s);
+
+	return NULL;
+}
+
+int
+pipeline_spec_configure(struct rte_swx_pipeline *p,
+			struct pipeline_spec *s,
+			const char **err_msg)
+{
+	uint32_t i;
+	int status = 0;
+
+	/* extobj. */
+	for (i = 0; i < s->n_extobjs; i++) {
+		struct extobj_spec *extobj_spec = &s->extobjs[i];
+
+		status = rte_swx_pipeline_extern_object_config(p,
+			extobj_spec->name,
+			extobj_spec->extern_type_name,
+			extobj_spec->pragma);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Extern object configuration error.";
+			return status;
+		}
+	}
+
+	/* regarray. */
+	for (i = 0; i < s->n_regarrays; i++) {
+		struct regarray_spec *regarray_spec = &s->regarrays[i];
+
+		status = rte_swx_pipeline_regarray_config(p,
+			regarray_spec->name,
+			regarray_spec->size,
+			regarray_spec->init_val);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Register array configuration error.";
+			return status;
+		}
+	}
+
+	/* metarray. */
+	for (i = 0; i < s->n_metarrays; i++) {
+		struct metarray_spec *metarray_spec = &s->metarrays[i];
+
+		status = rte_swx_pipeline_metarray_config(p,
+			metarray_spec->name,
+			metarray_spec->size);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Meter array configuration error.";
+			return status;
+		}
+	}
+
+	/* struct. */
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+
+		status = rte_swx_pipeline_struct_type_register(p,
+			struct_spec->name,
+			struct_spec->fields,
+			struct_spec->n_fields,
+			struct_spec->varbit);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Struct type registration error.";
+			return status;
+		}
+	}
+
+	/* header. */
+	for (i = 0; i < s->n_headers; i++) {
+		struct header_spec *header_spec = &s->headers[i];
+
+		status = rte_swx_pipeline_packet_header_register(p,
+			header_spec->name,
+			header_spec->struct_type_name);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Header configuration error.";
+			return status;
+		}
+	}
+
+	/* metadata. */
+	for (i = 0; i < s->n_metadata; i++) {
+		struct metadata_spec *metadata_spec = &s->metadata[i];
+
+		status = rte_swx_pipeline_packet_metadata_register(p,
+			metadata_spec->struct_type_name);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Meta-data registration error.";
+			return status;
+		}
+	}
+
+	/* action. */
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+
+		status = rte_swx_pipeline_action_config(p,
+			action_spec->name,
+			action_spec->args_struct_type_name,
+			action_spec->instructions,
+			action_spec->n_instructions);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Action configuration error.";
+			return status;
+		}
+	}
+
+	/* table. */
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+
+		status = rte_swx_pipeline_table_config(p,
+			table_spec->name,
+			&table_spec->params,
+			table_spec->recommended_table_type_name,
+			table_spec->args,
+			table_spec->size);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Table configuration error.";
+			return status;
+		}
+	}
+
+	/* selector. */
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+
+		status = rte_swx_pipeline_selector_config(p,
+			selector_spec->name,
+			&selector_spec->params);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Selector table configuration error.";
+			return status;
+		}
+	}
+
+	/* learner. */
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+
+		status = rte_swx_pipeline_learner_config(p,
+			learner_spec->name,
+			&learner_spec->params,
+			learner_spec->size,
+			learner_spec->timeout,
+			learner_spec->n_timeouts);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Learner table configuration error.";
+			return status;
+		}
+	}
+
+	/* apply. */
+	for (i = 0; i < s->n_apply; i++) {
+		struct apply_spec *apply_spec = &s->apply[i];
+
+		status = rte_swx_pipeline_instructions_config(p,
+			apply_spec->instructions,
+			apply_spec->n_instructions);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Pipeline instructions configuration error.";
+			return status;
+		}
+	}
+
+	return 0;
+}
+
+int
+rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
+				 FILE *spec_file,
+				 uint32_t *err_line,
+				 const char **err_msg)
+{
+	struct pipeline_spec *s = NULL;
+	int status = 0;
+
+	/* Check the input arguments. */
+	if (!p || !spec_file) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Invalid input argument.";
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Spec file parse. */
+	s = pipeline_spec_parse(spec_file, err_line, err_msg);
+	if (!s) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Pipeline configure. */
+	status = pipeline_spec_configure(p, s, err_msg);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		goto error;
+	}
+
+	/* Pipeline build. */
+	status = rte_swx_pipeline_build(p);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Pipeline build error.";
+		goto error;
+	}
+
+	return 0;
+
+error:
+	pipeline_spec_free(s);
 	return status;
 }
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index e1170a33b1..4f3a0b5958 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -206,3 +206,12 @@ struct pipeline_spec {
 
 void
 pipeline_spec_free(struct pipeline_spec *s);
+struct pipeline_spec *
+pipeline_spec_parse(FILE *spec,
+		    uint32_t *err_line,
+		    const char **err_msg);
+
+int
+pipeline_spec_configure(struct rte_swx_pipeline *p,
+			struct pipeline_spec *s,
+			const char **err_msg);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 05/17] pipeline: generate the code for pipeline specification structure
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (2 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 04/17] pipeline: rework the specification file-based pipeline build Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 06/17] pipeline: add support for pipeline I/O specification Cristian Dumitrescu
                         ` (12 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add support to export the pipeline specification data structure to a C
source code file.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 622 +++++++++++++++++++++++++++
 lib/pipeline/rte_swx_pipeline_spec.h |   5 +
 2 files changed, 627 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 62929a9da6..bf21fe17ba 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2020 Intel Corporation
  */
 #include <stdint.h>
+#include <inttypes.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -2103,6 +2104,627 @@ pipeline_spec_free(struct pipeline_spec *s)
 	memset(s, 0, sizeof(struct pipeline_spec));
 }
 
+static const char *
+match_type_string_get(enum rte_swx_table_match_type match_type)
+{
+	switch (match_type) {
+	case RTE_SWX_TABLE_MATCH_WILDCARD: return "RTE_SWX_TABLE_MATCH_WILDCARD";
+	case RTE_SWX_TABLE_MATCH_LPM: return "RTE_SWX_TABLE_MATCH_LPM";
+	case RTE_SWX_TABLE_MATCH_EXACT: return "RTE_SWX_TABLE_MATCH_EXACT";
+	default: return "RTE_SWX_TABLE_MATCH_UNKNOWN";
+	}
+}
+
+void
+pipeline_spec_codegen(FILE *f,
+		      struct pipeline_spec *s)
+{
+	uint32_t i;
+
+	/* Check the input arguments. */
+	if (!f || !s)
+		return;
+
+	/* extobj. */
+	fprintf(f, "static struct extobj_spec extobjs[] = {\n");
+
+	for (i = 0; i < s->n_extobjs; i++) {
+		struct extobj_spec *extobj_spec = &s->extobjs[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", extobj_spec->name);
+		fprintf(f, "\t\t.extern_type_name = \"%s\",\n", extobj_spec->extern_type_name);
+		if (extobj_spec->pragma)
+			fprintf(f, "\t\t.pragma = \"%s\",\n", extobj_spec->pragma);
+		else
+			fprintf(f, "\t\t.pragma = NULL,\n");
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* regarray. */
+	fprintf(f, "static struct regarray_spec regarrays[] = {\n");
+
+	for (i = 0; i < s->n_regarrays; i++) {
+		struct regarray_spec *regarray_spec = &s->regarrays[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", regarray_spec->name);
+		fprintf(f, "\t\t.init_val = %" PRIu64 ",\n", regarray_spec->init_val);
+		fprintf(f, "\t\t.size = %u,\n", regarray_spec->size);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* metarray. */
+	fprintf(f, "static struct metarray_spec metarrays[] = {\n");
+
+	for (i = 0; i < s->n_metarrays; i++) {
+		struct metarray_spec *metarray_spec = &s->metarrays[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", metarray_spec->name);
+		fprintf(f, "\t\t.size = %u,\n", metarray_spec->size);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* struct. */
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+		uint32_t j;
+
+		fprintf(f, "static struct rte_swx_field_params struct_%s_fields[] = {\n",
+			struct_spec->name);
+
+		for (j = 0; j < struct_spec->n_fields; j++) {
+			struct rte_swx_field_params *field = &struct_spec->fields[j];
+
+			fprintf(f, "\t[%d] = {\n", j);
+			fprintf(f, "\t\t.name = \"%s\",\n", field->name);
+			fprintf(f, "\t\t.n_bits = %u,\n", field->n_bits);
+			fprintf(f, "\t},\n");
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct struct_spec structs[] = {\n");
+
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", struct_spec->name);
+		fprintf(f, "\t\t.fields = struct_%s_fields,\n", struct_spec->name);
+		fprintf(f, "\t\t.n_fields = "
+			"sizeof(struct_%s_fields) / sizeof(struct_%s_fields[0]),\n",
+			struct_spec->name,
+			struct_spec->name);
+		fprintf(f, "\t\t.varbit = %d,\n", struct_spec->varbit);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* header. */
+	fprintf(f, "static struct header_spec headers[] = {\n");
+
+	for (i = 0; i < s->n_headers; i++) {
+		struct header_spec *header_spec = &s->headers[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", header_spec->name);
+		fprintf(f, "\t\t.struct_type_name = \"%s\",\n", header_spec->struct_type_name);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* metadata. */
+	fprintf(f, "static struct metadata_spec metadata[] = {\n");
+
+	for (i = 0; i < s->n_metadata; i++) {
+		struct metadata_spec *metadata_spec = &s->metadata[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.struct_type_name = \"%s\",\n", metadata_spec->struct_type_name);
+		fprintf(f, "\t},\n");
+
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* action. */
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+		uint32_t j;
+
+		fprintf(f, "static const char *action_%s_initial_instructions[] = {\n",
+			action_spec->name);
+
+		for (j = 0; j < action_spec->n_instructions; j++) {
+			const char *instr = action_spec->instructions[j];
+
+			fprintf(f, "\t[%d] = \"%s\",\n", j, instr);
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct action_spec actions[] = {\n");
+
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", action_spec->name);
+
+		if (action_spec->args_struct_type_name)
+			fprintf(f, "\t\t.args_struct_type_name = \"%s\",\n",
+				action_spec->args_struct_type_name);
+		else
+			fprintf(f, "\t\t.args_struct_type_name = NULL,\n");
+
+		fprintf(f, "\t\t.instructions = action_%s_initial_instructions,\n",
+			action_spec->name);
+		fprintf(f, "\t\t.n_instructions = "
+			"sizeof(action_%s_initial_instructions) / "
+			"sizeof(action_%s_initial_instructions[0]),\n",
+			action_spec->name,
+			action_spec->name);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* table. */
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+		uint32_t j;
+
+		/* fields. */
+		if (table_spec->params.fields && table_spec->params.n_fields) {
+			fprintf(f, "static struct rte_swx_match_field_params "
+				"table_%s_fields[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_fields; j++) {
+				struct rte_swx_match_field_params *field =
+					&table_spec->params.fields[j];
+
+				fprintf(f, "\t[%d] = {\n", j);
+				fprintf(f, "\t\t.name = \"%s\",\n", field->name);
+				fprintf(f, "\t\t.match_type = %s,\n",
+					match_type_string_get(field->match_type));
+				fprintf(f, "\t},\n");
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_names. */
+		if (table_spec->params.action_names && table_spec->params.n_actions) {
+			fprintf(f, "static const char *table_%s_action_names[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				const char *action_name = table_spec->params.action_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, action_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_table_entries. */
+		if (table_spec->params.action_is_for_table_entries &&
+		    table_spec->params.n_actions) {
+			fprintf(f, "static int table_%s_action_is_for_table_entries[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				int value = table_spec->params.action_is_for_table_entries[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_default_entry. */
+		if (table_spec->params.action_is_for_default_entry &&
+		    table_spec->params.n_actions) {
+			fprintf(f, "static int table_%s_action_is_for_default_entry[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				int value = table_spec->params.action_is_for_default_entry[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct table_spec tables[] = {\n");
+
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", table_spec->name);
+
+		fprintf(f, "\t\t.params = {\n");
+
+		if (table_spec->params.fields && table_spec->params.n_fields) {
+			fprintf(f, "\t\t\t.fields = table_%s_fields,\n", table_spec->name);
+			fprintf(f, "\t\t\t.n_fields = "
+				"sizeof(table_%s_fields) / sizeof(table_%s_fields[0]),\n",
+				table_spec->name,
+				table_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.fields = NULL,\n");
+			fprintf(f, "\t\t\t.n_fields = 0,\n");
+		}
+
+		if (table_spec->params.action_names && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_names = table_%s_action_names,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_names = NULL,\n");
+
+		if (table_spec->params.action_is_for_table_entries && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_table_entries = "
+				"table_%s_action_is_for_table_entries,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_table_entries = NULL,\n");
+
+		if (table_spec->params.action_is_for_default_entry && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_default_entry = "
+				"table_%s_action_is_for_default_entry,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_default_entry = NULL,\n");
+
+		if (table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.n_actions = sizeof(table_%s_action_names) / "
+				"sizeof(table_%s_action_names[0]),\n",
+				table_spec->name,
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.n_actions = 0,\n");
+
+		if (table_spec->params.default_action_name)
+			fprintf(f, "\t\t\t.default_action_name = \"%s\",\n",
+				table_spec->params.default_action_name);
+		else
+			fprintf(f, "\t\t\t.default_action_name = NULL,\n");
+
+		if (table_spec->params.default_action_args)
+			fprintf(f, "\t\t\t.default_action_args = \"%s\",\n",
+				table_spec->params.default_action_args);
+		else
+			fprintf(f, "\t\t\t.default_action_args = NULL,\n");
+
+		fprintf(f, "\t\t\t.default_action_is_const = %d,\n",
+			table_spec->params.default_action_is_const);
+		fprintf(f, "\t\t},\n");
+
+		if (table_spec->recommended_table_type_name)
+			fprintf(f, "\t\t.recommended_table_type_name = \"%s\",\n",
+				table_spec->recommended_table_type_name);
+		else
+			fprintf(f, "\t\t.recommended_table_type_name = NULL,\n");
+
+		if (table_spec->args)
+			fprintf(f, "\t\t.args = \"%s\",\n", table_spec->args);
+		else
+			fprintf(f, "\t\t.args = NULL,\n");
+
+		fprintf(f, "\t\t.size = %u,\n", table_spec->size);
+
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* selector. */
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+		uint32_t j;
+
+		if (selector_spec->params.selector_field_names &&
+		    selector_spec->params.n_selector_fields) {
+			fprintf(f, "static const char *selector_%s_field_names[] = {\n",
+				selector_spec->name);
+
+			for (j = 0; j < selector_spec->params.n_selector_fields; j++) {
+				const char *field_name =
+					selector_spec->params.selector_field_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, field_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct selector_spec selectors[] = {\n");
+
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+
+		fprintf(f, "\t\t.name = \"%s\",\n", selector_spec->name);
+		fprintf(f, "\t\t.params = {\n");
+
+		if (selector_spec->params.group_id_field_name)
+			fprintf(f, "\t\t\t.group_id_field_name = \"%s\",\n",
+				selector_spec->params.group_id_field_name);
+		else
+			fprintf(f, "\t\t\t.group_id_field_name = NULL,\n");
+
+		if (selector_spec->params.selector_field_names &&
+		    selector_spec->params.n_selector_fields) {
+			fprintf(f, "\t\t\t.selector_field_names = selector_%s_field_names,\n",
+				selector_spec->name);
+			fprintf(f, "\t\t\t.n_selector_fields = "
+				"sizeof(selector_%s_field_names) / sizeof(selector_%s_field_names[0]),\n",
+				selector_spec->name,
+				selector_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.selector_field_names = NULL,\n");
+			fprintf(f, "\t\t\t.n_selector_fields = 0,\n");
+		}
+
+		if (selector_spec->params.member_id_field_name)
+			fprintf(f, "\t\t\t.member_id_field_name = \"%s\",\n",
+				selector_spec->params.member_id_field_name);
+		else
+			fprintf(f, "\t\t\t.member_id_field_name = NULL,\n");
+
+		fprintf(f, "\t\t\t.n_groups_max = %u,\n", selector_spec->params.n_groups_max);
+
+		fprintf(f, "\t\t\t.n_members_per_group_max = %u,\n",
+			selector_spec->params.n_members_per_group_max);
+
+		fprintf(f, "\t\t},\n");
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* learner. */
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+		uint32_t j;
+
+		/* field_names. */
+		if (learner_spec->params.field_names && learner_spec->params.n_fields) {
+			fprintf(f, "static const char *learner_%s_field_names[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_fields; j++) {
+				const char *field_name = learner_spec->params.field_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, field_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_names. */
+		if (learner_spec->params.action_names && learner_spec->params.n_actions) {
+			fprintf(f, "static const char *learner_%s_action_names[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				const char *action_name = learner_spec->params.action_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, action_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_table_entries. */
+		if (learner_spec->params.action_is_for_table_entries &&
+		    learner_spec->params.n_actions) {
+			fprintf(f, "static int learner_%s_action_is_for_table_entries[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				int value = learner_spec->params.action_is_for_table_entries[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_default_entry. */
+		if (learner_spec->params.action_is_for_default_entry &&
+		    learner_spec->params.n_actions) {
+			fprintf(f, "static int learner_%s_action_is_for_default_entry[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				int value = learner_spec->params.action_is_for_default_entry[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* timeout. */
+		if (learner_spec->timeout && learner_spec->n_timeouts) {
+			fprintf(f, "static uint32_t learner_%s_timeout[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->n_timeouts; j++) {
+				uint32_t value = learner_spec->timeout[j];
+
+				fprintf(f, "\t[%d] = %u,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct learner_spec learners[] = {\n");
+
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", learner_spec->name);
+
+		fprintf(f, "\t\t.params = {\n");
+
+		if (learner_spec->params.field_names && learner_spec->params.n_fields) {
+			fprintf(f, "\t\t\t.field_names = learner_%s_field_names,\n",
+				learner_spec->name);
+			fprintf(f, "\t\t\t.n_fields = "
+				"sizeof(learner_%s_field_names) / "
+				"sizeof(learner_%s_field_names[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.field_names = NULL,\n");
+			fprintf(f, "\t\t\t.n_fields = 0,\n");
+		}
+
+		if (learner_spec->params.action_names && learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_names = learner_%s_action_names,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_names = NULL,\n");
+
+		if (learner_spec->params.action_is_for_table_entries &&
+		    learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_table_entries = "
+				"learner_%s_action_is_for_table_entries,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_table_entries = NULL,\n");
+
+		if (learner_spec->params.action_is_for_default_entry &&
+		    learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_default_entry = "
+				"learner_%s_action_is_for_default_entry,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_default_entry = NULL,\n");
+
+		if (learner_spec->params.action_names && learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.n_actions = "
+				"sizeof(learner_%s_action_names) / sizeof(learner_%s_action_names[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.n_actions = NULL,\n");
+
+		if (learner_spec->params.default_action_name)
+			fprintf(f, "\t\t\t.default_action_name = \"%s\",\n",
+				learner_spec->params.default_action_name);
+		else
+			fprintf(f, "\t\t\t.default_action_name = NULL,\n");
+
+		if (learner_spec->params.default_action_args)
+			fprintf(f, "\t\t\t.default_action_args = \"%s\",\n",
+				learner_spec->params.default_action_args);
+		else
+			fprintf(f, "\t\t\t.default_action_args = NULL,\n");
+
+		fprintf(f, "\t\t\t.default_action_is_const = %d,\n",
+			learner_spec->params.default_action_is_const);
+
+		fprintf(f, "\t\t},\n");
+
+		fprintf(f, "\t\t.size = %u,\n", learner_spec->size);
+
+		if (learner_spec->timeout && learner_spec->n_timeouts) {
+			fprintf(f, "\t\t.timeout = learner_%s_timeout,\n", learner_spec->name);
+			fprintf(f, "\t\t\t.n_timeouts = "
+				"sizeof(learner_%s_timeout) / sizeof(learner_%s_timeout[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		} else {
+			fprintf(f, "\t\t.timeout = NULL,\n");
+			fprintf(f, "\t\t\t.n_timeouts = 0,\n");
+		}
+
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* apply. */
+	for (i = 0; i < s->n_apply; i++) {
+		struct apply_spec *apply_spec = &s->apply[i];
+		uint32_t j;
+
+		fprintf(f, "static const char *apply%u_initial_instructions[] = {\n", i);
+
+		for (j = 0; j < apply_spec->n_instructions; j++) {
+			const char *instr = apply_spec->instructions[j];
+
+			fprintf(f, "\t[%d] = \"%s\",\n", j, instr);
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct apply_spec apply[] = {\n");
+
+	for (i = 0; i < s->n_apply; i++) {
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t.instructions = apply%u_initial_instructions,\n", i);
+		fprintf(f, "\t.n_instructions = "
+			"sizeof(apply%u_initial_instructions) / "
+			"sizeof(apply%u_initial_instructions[0]),\n",
+			i,
+			i);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* pipeline. */
+	fprintf(f, "struct pipeline_spec pipeline_spec = {\n");
+	fprintf(f, "\t.extobjs = extobjs,\n");
+	fprintf(f, "\t.structs = structs,\n");
+	fprintf(f, "\t.headers = headers,\n");
+	fprintf(f, "\t.metadata = metadata,\n");
+	fprintf(f, "\t.actions = actions,\n");
+	fprintf(f, "\t.tables = tables,\n");
+	fprintf(f, "\t.selectors = selectors,\n");
+	fprintf(f, "\t.learners = learners,\n");
+	fprintf(f, "\t.regarrays = regarrays,\n");
+	fprintf(f, "\t.metarrays = metarrays,\n");
+	fprintf(f, "\t.apply = apply,\n");
+	fprintf(f, "\t.n_extobjs = sizeof(extobjs) / sizeof(extobjs[0]),\n");
+	fprintf(f, "\t.n_structs = sizeof(structs) / sizeof(structs[0]),\n");
+	fprintf(f, "\t.n_headers = sizeof(headers) / sizeof(headers[0]),\n");
+	fprintf(f, "\t.n_metadata = sizeof(metadata) / sizeof(metadata[0]),\n");
+	fprintf(f, "\t.n_actions = sizeof(actions) / sizeof(actions[0]),\n");
+	fprintf(f, "\t.n_tables = sizeof(tables) / sizeof(tables[0]),\n");
+	fprintf(f, "\t.n_selectors = sizeof(selectors) / sizeof(selectors[0]),\n");
+	fprintf(f, "\t.n_learners = sizeof(learners) / sizeof(learners[0]),\n");
+	fprintf(f, "\t.n_regarrays = sizeof(regarrays) / sizeof(regarrays[0]),\n");
+	fprintf(f, "\t.n_metarrays = sizeof(metarrays) / sizeof(metarrays[0]),\n");
+	fprintf(f, "\t.n_apply = sizeof(apply) / sizeof(apply[0]),\n");
+	fprintf(f, "};\n");
+}
+
 struct pipeline_spec *
 pipeline_spec_parse(FILE *spec,
 		    uint32_t *err_line,
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 4f3a0b5958..707b99ba09 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -206,6 +206,11 @@ struct pipeline_spec {
 
 void
 pipeline_spec_free(struct pipeline_spec *s);
+
+void
+pipeline_spec_codegen(FILE *f,
+		      struct pipeline_spec *s);
+
 struct pipeline_spec *
 pipeline_spec_parse(FILE *spec,
 		    uint32_t *err_line,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 06/17] pipeline: add support for pipeline I/O specification
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (3 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 05/17] pipeline: generate the code for pipeline specification structure Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 07/17] pipeline: add API for pipeline code generation Cristian Dumitrescu
                         ` (11 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add specification data structure and API for the pipeline I/O ports
and related pipeline configuration such as packet mirroring.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 852 +++++++++++++++++++++++++++
 lib/pipeline/rte_swx_pipeline_spec.h |  58 ++
 2 files changed, 910 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index bf21fe17ba..aea69b500b 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -9,6 +9,12 @@
 #include <errno.h>
 
 #include <rte_common.h>
+#include <rte_mempool.h>
+
+#include <rte_swx_port_ethdev.h>
+#include <rte_swx_port_ring.h>
+#include <rte_swx_port_source_sink.h>
+#include <rte_swx_port_fd.h>
 
 #include "rte_swx_pipeline_spec.h"
 
@@ -3566,3 +3572,849 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 	pipeline_spec_free(s);
 	return status;
 }
+
+static void
+port_in_params_free(void *params, const char *port_type)
+{
+	uintptr_t dev_name;
+
+	if (!params || !port_type)
+		return;
+
+	if (!strcmp(port_type, "ethdev")) {
+		struct rte_swx_port_ethdev_reader_params *p = params;
+
+		dev_name = (uintptr_t)p->dev_name;
+	} else if (!strcmp(port_type, "ring")) {
+		struct rte_swx_port_ring_reader_params *p = params;
+
+		dev_name = (uintptr_t)p->name;
+	} else if (!strcmp(port_type, "source")) {
+		struct rte_swx_port_source_params *p = params;
+
+		dev_name = (uintptr_t)p->file_name;
+	} else
+		dev_name = (uintptr_t)NULL;
+
+	free((void *)dev_name);
+	free(params);
+}
+
+static void
+port_out_params_free(void *params, const char *port_type)
+{
+	uintptr_t dev_name;
+
+	if (!params || !port_type)
+		return;
+
+	if (!strcmp(port_type, "ethdev")) {
+		struct rte_swx_port_ethdev_writer_params *p = params;
+
+		dev_name = (uintptr_t)p->dev_name;
+	} else if (!strcmp(port_type, "ring")) {
+		struct rte_swx_port_ring_writer_params *p = params;
+
+		dev_name = (uintptr_t)p->name;
+	} else if (!strcmp(port_type, "sink")) {
+		struct rte_swx_port_sink_params *p = params;
+
+		dev_name = (uintptr_t)p->file_name;
+	} else
+		dev_name = (uintptr_t)NULL;
+
+	free((void *)dev_name);
+	free(params);
+}
+
+void
+pipeline_iospec_free(struct pipeline_iospec *s)
+{
+	uint32_t i;
+
+	if (!s)
+		return;
+
+	/* Input ports. */
+	for (i = 0; i < s->n_ports_in; i++) {
+		uintptr_t name = (uintptr_t)s->port_in_type[i];
+
+		port_in_params_free(s->port_in_params[i], s->port_in_type[i]);
+		free((void *)name);
+	}
+
+	free(s->port_in_type);
+	free(s->port_in_params);
+
+	/* Output ports. */
+	for (i = 0; i < s->n_ports_out; i++) {
+		uintptr_t name = (uintptr_t)s->port_out_type[i];
+
+		port_out_params_free(s->port_out_params[i], s->port_out_type[i]);
+		free((void *)name);
+	}
+
+	free(s->port_out_type);
+	free(s->port_out_params);
+
+	free(s);
+}
+
+static int
+mirroring_parse(struct rte_swx_pipeline_mirroring_params *p,
+		char **tokens,
+		uint32_t n_tokens,
+		const char **err_msg)
+{
+	char *token;
+
+	if ((n_tokens != 4) || strcmp(tokens[0], "slots") || strcmp(tokens[2], "sessions")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return -EINVAL;
+	}
+
+	/* <n_slots>. */
+	token = tokens[1];
+	p->n_slots = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <n_slots> parameter.";
+		return -EINVAL;
+	}
+
+	/* <n_sessions>. */
+	token = tokens[3];
+	p->n_sessions = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <n_sessions> parameter.";
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+port_in_ethdev_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_ethdev_reader_params *p = NULL;
+	char *token, *dev_name = NULL;
+	uint32_t queue_id, burst_size;
+
+	if ((n_tokens != 5) || strcmp(tokens[1], "rxq") || strcmp(tokens[3], "bsz")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <queue_id>. */
+	token = tokens[2];
+	queue_id = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <queue_id> parameter.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[4];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	dev_name = strdup(tokens[0]);
+	p = malloc(sizeof(struct rte_swx_port_ethdev_reader_params));
+	if (!dev_name || !p) {
+		free(dev_name);
+		free(p);
+
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->dev_name = dev_name;
+	p->queue_id = queue_id;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_in_ring_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_ring_reader_params *p = NULL;
+	char *token, *name = NULL;
+	uint32_t burst_size;
+
+	if ((n_tokens != 3) || strcmp(tokens[1], "bsz")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[2];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	name = strdup(tokens[0]);
+	p = malloc(sizeof(struct rte_swx_port_ring_reader_params));
+	if (!name || !p) {
+		free(name);
+		free(p);
+
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->name = name;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_in_source_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_source_params *p = NULL;
+	struct rte_mempool *pool = NULL;
+	char *token, *file_name = NULL;
+	uint32_t n_loops, n_pkts_max;
+
+	if ((n_tokens != 8) ||
+	    strcmp(tokens[0], "mempool") ||
+	    strcmp(tokens[2], "file") ||
+	    strcmp(tokens[4], "loop") ||
+	    strcmp(tokens[6], "packets")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <mempool_name>. */
+	pool = rte_mempool_lookup(tokens[1]);
+	if (!pool) {
+		if (err_msg)
+			*err_msg = "Invalid <mempool_name> parameter.";
+		return NULL;
+	}
+
+	/* <n_loops>. */
+	token = tokens[5];
+	n_loops = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <n_loops> parameter.";
+		return NULL;
+	}
+
+	/* <n_pkts_max>. */
+	token = tokens[7];
+	n_pkts_max = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <n_pkts_max> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	file_name = strdup(tokens[3]);
+	p = malloc(sizeof(struct rte_swx_port_source_params));
+	if (!file_name || !p) {
+		free(file_name);
+		free(p);
+
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->pool = pool;
+	p->file_name = file_name;
+	p->n_loops = n_loops;
+	p->n_pkts_max = n_pkts_max;
+
+	return p;
+}
+
+static void *
+port_in_fd_parse(char **tokens,
+		 uint32_t n_tokens,
+		 const char **err_msg)
+{
+	struct rte_swx_port_fd_reader_params *p = NULL;
+	struct rte_mempool *mempool = NULL;
+	char *token;
+	uint32_t mtu, burst_size;
+	int fd;
+
+	if ((n_tokens != 7) ||
+	    strcmp(tokens[1], "mtu") ||
+	    strcmp(tokens[3], "mempool") ||
+	    strcmp(tokens[5], "bsz")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <file_descriptor>. */
+	token = tokens[0];
+	fd = strtol(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <file_descriptor> parameter.";
+		return NULL;
+	}
+
+	/* <mtu>. */
+	token = tokens[2];
+	mtu = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <mtu> parameter.";
+		return NULL;
+	}
+
+	/* <mempool_name>. */
+	mempool = rte_mempool_lookup(tokens[4]);
+	if (!mempool) {
+		if (err_msg)
+			*err_msg = "Invalid <mempool_name> parameter.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[6];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	p = malloc(sizeof(struct rte_swx_port_fd_reader_params));
+	if (!p) {
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->fd = fd;
+	p->mtu = mtu;
+	p->mempool = mempool;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_out_ethdev_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_ethdev_writer_params *p = NULL;
+	char *token, *dev_name = NULL;
+	uint32_t queue_id, burst_size;
+
+	if ((n_tokens != 5) || strcmp(tokens[1], "txq") || strcmp(tokens[3], "bsz")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <queue_id>. */
+	token = tokens[2];
+	queue_id = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <queue_id> parameter.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[4];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	dev_name = strdup(tokens[0]);
+	p = malloc(sizeof(struct rte_swx_port_ethdev_writer_params));
+	if (!dev_name || !p) {
+		free(dev_name);
+		free(p);
+
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->dev_name = dev_name;
+	p->queue_id = queue_id;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_out_ring_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_ring_writer_params *p = NULL;
+	char *token, *name = NULL;
+	uint32_t burst_size;
+
+	if ((n_tokens != 3) || strcmp(tokens[1], "bsz")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[2];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	name = strdup(tokens[0]);
+	p = malloc(sizeof(struct rte_swx_port_ring_writer_params));
+	if (!name || !p) {
+		free(name);
+		free(p);
+
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->name = name;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_out_sink_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_sink_params *p = NULL;
+	char *file_name = NULL;
+	int file_name_valid = 0;
+
+	if ((n_tokens != 2) || strcmp(tokens[0], "file")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	if (strcmp(tokens[1], "none")) {
+		file_name_valid = 1;
+		file_name = strdup(tokens[1]);
+	}
+
+	p = malloc(sizeof(struct rte_swx_port_ring_writer_params));
+	if ((file_name_valid && !file_name) || !p) {
+		free(file_name);
+		free(p);
+
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->file_name = file_name;
+
+	return p;
+}
+
+static void *
+port_out_fd_parse(char **tokens,
+		  uint32_t n_tokens,
+		  const char **err_msg)
+{
+	struct rte_swx_port_fd_writer_params *p = NULL;
+	char *token;
+	uint32_t burst_size;
+	int fd;
+
+	if ((n_tokens != 3) || strcmp(tokens[1], "bsz")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <file_descriptor>. */
+	token = tokens[0];
+	fd = strtol(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <file_descriptor> parameter.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[2];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	p = malloc(sizeof(struct rte_swx_port_fd_writer_params));
+	if (!p) {
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->fd = fd;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+struct pipeline_iospec *
+pipeline_iospec_parse(FILE *spec,
+		      uint32_t *err_line,
+		      const char **err_msg)
+{
+	struct pipeline_iospec *s = NULL;
+	uint32_t n_lines = 0;
+
+	/* Check the input arguments. */
+	if (!spec) {
+		if (err_line)
+			*err_line = n_lines;
+		if (err_msg)
+			*err_msg = "Invalid input argument.";
+		goto error;
+	}
+
+	/* Memory allocation. */
+	s = calloc(sizeof(struct pipeline_iospec), 1);
+	if (!s) {
+		if (err_line)
+			*err_line = n_lines;
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		goto error;
+	}
+
+	/* Initialize with the defaut values. */
+	s->mirroring_params.n_slots = RTE_SWX_PACKET_MIRRORING_SLOTS_DEFAULT;
+	s->mirroring_params.n_sessions = RTE_SWX_PACKET_MIRRORING_SESSIONS_DEFAULT;
+
+	for (n_lines = 1; ; n_lines++) {
+		char line[MAX_LINE_LENGTH];
+		char *tokens[MAX_TOKENS], *ptr = line;
+		uint32_t n_tokens = 0;
+
+		/* Read next line. */
+		if (!fgets(line, sizeof(line), spec))
+			break;
+
+		/* Parse the line into tokens. */
+		for ( ; ; ) {
+			char *token;
+
+			/* Get token. */
+			token = strtok_r(ptr, " \f\n\r\t\v", &ptr);
+			if (!token)
+				break;
+
+			/* Handle comments. */
+			if ((token[0] == '#') ||
+			    (token[0] == ';') ||
+			    ((token[0] == '/') && (token[1] == '/'))) {
+				break;
+			}
+
+			/* Handle excessively long lines. */
+			if (n_tokens >= RTE_DIM(tokens)) {
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Too many tokens.";
+				goto error;
+			}
+
+			/* Handle excessively long tokens. */
+			if (strnlen(token, RTE_SWX_NAME_SIZE) >=
+			    RTE_SWX_NAME_SIZE) {
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Token too big.";
+				goto error;
+			}
+
+			/* Save token. */
+			tokens[n_tokens] = token;
+			n_tokens++;
+		}
+
+		/* Handle empty lines. */
+		if (!n_tokens)
+			continue;
+
+		/* mirroring. */
+		if ((n_tokens >= 1) && !strcmp(tokens[0], "mirroring")) {
+			int status = 0;
+
+			status = mirroring_parse(&s->mirroring_params,
+						 &tokens[1],
+						 n_tokens - 1,
+						 err_msg);
+			if (status) {
+				if (err_line)
+					*err_line = n_lines;
+				goto error;
+			}
+
+			continue;
+		}
+
+		/* port in. */
+		if ((n_tokens >= 4) && !strcmp(tokens[0], "port") && !strcmp(tokens[1], "in")) {
+			char *token = tokens[2];
+			uint32_t *new_id = NULL;
+			const char **new_type = NULL, *port_type = NULL;
+			void **new_params = NULL, *p = NULL;
+			uint32_t port_id;
+
+			/* <port_id>. */
+			port_id = strtoul(token, &token, 0);
+			if (token[0]) {
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Invalid port ID.";
+				goto error;
+			}
+
+			/* <port_type>. */
+			if (!strcmp(tokens[3], "ethdev"))
+				p = port_in_ethdev_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "ring"))
+				p = port_in_ring_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "source"))
+				p = port_in_source_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "fd"))
+				p = port_in_fd_parse(&tokens[4], n_tokens - 4, err_msg);
+			else {
+				p = NULL;
+				if (err_msg)
+					*err_msg = "Invalid port type.";
+			}
+
+			if (!p) {
+				if (err_line)
+					*err_line = n_lines;
+				goto error;
+			}
+
+			/* New port. */
+			port_type = strdup(tokens[3]);
+			new_id = realloc(s->port_in_id,
+					 (s->n_ports_in + 1) * sizeof(uint32_t));
+			new_type = realloc(s->port_in_type,
+					   (s->n_ports_in + 1) * sizeof(char *));
+			new_params = realloc(s->port_in_params,
+					     (s->n_ports_in + 1) * sizeof(void *));
+			if (!port_type || !new_id || !new_type || !new_params) {
+				uintptr_t pt = (uintptr_t)port_type;
+
+				port_in_params_free(p, tokens[3]);
+				free((void *)pt);
+				free(new_id);
+				free(new_type);
+				free(new_params);
+
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Memory allocation failed.";
+				goto error;
+			}
+
+			s->port_in_id = new_id;
+			s->port_in_type = new_type;
+			s->port_in_params = new_params;
+
+			s->port_in_id[s->n_ports_in] = port_id;
+			s->port_in_type[s->n_ports_in] = port_type;
+			s->port_in_params[s->n_ports_in] = p;
+			s->n_ports_in++;
+
+			continue;
+		}
+
+		/* port out. */
+		if ((n_tokens >= 4) && !strcmp(tokens[0], "port") && !strcmp(tokens[1], "out")) {
+			char *token = tokens[2];
+			uint32_t *new_id = NULL;
+			const char **new_type = NULL, *port_type = NULL;
+			void **new_params = NULL, *p = NULL;
+			uint32_t port_id;
+
+			/* <port_id>. */
+			port_id = strtoul(token, &token, 0);
+			if (token[0]) {
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Invalid port ID.";
+				goto error;
+			}
+
+			/* <port_type>. */
+			if (!strcmp(tokens[3], "ethdev"))
+				p = port_out_ethdev_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "ring"))
+				p = port_out_ring_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "sink"))
+				p = port_out_sink_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "fd"))
+				p = port_out_fd_parse(&tokens[4], n_tokens - 4, err_msg);
+			else {
+				p = NULL;
+				if (err_msg)
+					*err_msg = "Invalid port type.";
+			}
+
+			if (!p) {
+				if (err_line)
+					*err_line = n_lines;
+				goto error;
+			}
+
+			/* New port. */
+			port_type = strdup(tokens[3]);
+			new_id = realloc(s->port_out_id,
+					 (s->n_ports_out + 1) * sizeof(uint32_t));
+			new_type = realloc(s->port_out_type,
+					   (s->n_ports_out + 1) * sizeof(char *));
+			new_params = realloc(s->port_out_params,
+					     (s->n_ports_out + 1) * sizeof(void *));
+			if (!port_type || !new_id || !new_type || !new_params) {
+				uintptr_t pt = (uintptr_t)port_type;
+
+				port_out_params_free(p, tokens[3]);
+				free((void *)pt);
+				free(new_id);
+				free(new_type);
+				free(new_params);
+
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Memory allocation failed.";
+				goto error;
+			}
+
+			s->port_out_id = new_id;
+			s->port_out_type = new_type;
+			s->port_out_params = new_params;
+
+			s->port_out_id[s->n_ports_out] = port_id;
+			s->port_out_type[s->n_ports_out] = port_type;
+			s->port_out_params[s->n_ports_out] = p;
+			s->n_ports_out++;
+
+			continue;
+		}
+
+		/* Anything else. */
+		if (err_line)
+			*err_line = n_lines;
+		if (err_msg)
+			*err_msg = "Unknown I/O statement.";
+		goto error;
+	}
+
+	return s;
+
+error:
+	pipeline_iospec_free(s);
+
+	return NULL;
+}
+
+int
+pipeline_iospec_configure(struct rte_swx_pipeline *p,
+			  struct pipeline_iospec *s,
+			  const char **err_msg)
+{
+	uint32_t i;
+	int status = 0;
+
+	/* Check input arguments. */
+	if (!p || !s) {
+		if (err_msg)
+			*err_msg = "Invalid input argument";
+		return -EINVAL;
+	}
+
+	/* Mirroring. */
+	status = rte_swx_pipeline_mirroring_config(p, &s->mirroring_params);
+	if (status) {
+		if (err_msg)
+			*err_msg = "Pipeline mirroring configuration error.";
+		return status;
+	}
+
+	/* Input ports. */
+	for (i = 0; i < s->n_ports_in; i++) {
+		status = rte_swx_pipeline_port_in_config(p,
+							 i,
+							 s->port_in_type[i],
+							 s->port_in_params[i]);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Pipeline input port configuration error.";
+			return status;
+		}
+	}
+
+	/* Output ports. */
+	for (i = 0; i < s->n_ports_out; i++) {
+		status = rte_swx_pipeline_port_out_config(p,
+							  i,
+							  s->port_out_type[i],
+							  s->port_out_params[i]);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Pipeline output port configuration error.";
+			return status;
+		}
+	}
+
+	return 0;
+}
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 707b99ba09..62ac4ecfc4 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -1,6 +1,13 @@
 /* SPDX-License-Identifier: BSD-3-Clause
  * Copyright(c) 2022 Intel Corporation
  */
+#ifndef __INCLUDE_RTE_SWX_PIPELINE_SPEC_H__
+#define __INCLUDE_RTE_SWX_PIPELINE_SPEC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include <stdint.h>
 #include <stdio.h>
 
@@ -204,6 +211,38 @@ struct pipeline_spec {
 	uint32_t n_apply;
 };
 
+/*
+ * Mirroring:
+ *      mirroring slots <n_slots> sessions <n_sessions>
+ *
+ * Input ports:
+ *      port in <port_id> ethdev <ethdev_name> rxq <queue_id> bsz <burst_size>
+ *      port in <port_id> ring <ring_name> bsz <burst_size>
+ *      port in <port_id> source mempool <mempool_name> file <file_name> loop <n_loops>
+ *                               packets <n_pkts_max>
+ *      port in <port_id> fd <file_descriptor> mtu <mtu> mempool <mempool_name> bsz <burst_size>
+ *
+ * Output ports:
+ *      port out <port_id> ethdev <ethdev_name> txq <queue_id> bsz <burst_size>
+ *      port out <port_id> ring <ring_name> bsz <burst_size>
+ *      port out <port_id> sink file <file_name> | none
+ *      port out <port_id> fd <file_descriptor> bsz <burst_size>
+ */
+struct pipeline_iospec {
+	struct rte_swx_pipeline_mirroring_params mirroring_params;
+
+	uint32_t *port_in_id;
+	const char **port_in_type;
+	void **port_in_params;
+
+	uint32_t *port_out_id;
+	const char **port_out_type;
+	void **port_out_params;
+
+	uint32_t n_ports_in;
+	uint32_t n_ports_out;
+};
+
 void
 pipeline_spec_free(struct pipeline_spec *s);
 
@@ -220,3 +259,22 @@ int
 pipeline_spec_configure(struct rte_swx_pipeline *p,
 			struct pipeline_spec *s,
 			const char **err_msg);
+
+void
+pipeline_iospec_free(struct pipeline_iospec *s);
+
+struct pipeline_iospec *
+pipeline_iospec_parse(FILE *spec,
+		      uint32_t *err_line,
+		      const char **err_msg);
+
+int
+pipeline_iospec_configure(struct rte_swx_pipeline *p,
+			  struct pipeline_iospec *s,
+			  const char **err_msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 07/17] pipeline: add API for pipeline code generation
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (4 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 06/17] pipeline: add support for pipeline I/O specification Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 08/17] pipeline: add API for shared library-based pipeline build Cristian Dumitrescu
                         ` (10 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Previously, the C code generation for the pipeline was hidden under
the hood; now, we make this an explicit API operation. Besides the
functions for the pipeline actions and the pipeline instructions,
the generated C source code now includes the pipeline specification
structure required for the pipeline configuration operations.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline.c | 94 +++++++++++++++++++++++++++++++++
 lib/pipeline/rte_swx_pipeline.h | 25 +++++++++
 lib/pipeline/version.map        |  1 +
 3 files changed, 120 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index c8ccded4f8..dd5f7107fa 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -20,6 +20,7 @@
 #include <rte_swx_table_wm.h>
 
 #include "rte_swx_pipeline_internal.h"
+#include "rte_swx_pipeline_spec.h"
 
 #define CHECK(condition, err_code)                                             \
 do {                                                                           \
@@ -13581,3 +13582,96 @@ pipeline_compile(struct rte_swx_pipeline *p)
 
 	return status;
 }
+
+int
+rte_swx_pipeline_codegen(FILE *spec_file,
+			 FILE *code_file,
+			 uint32_t *err_line,
+			 const char **err_msg)
+
+{
+	struct rte_swx_pipeline *p = NULL;
+	struct pipeline_spec *s = NULL;
+	struct instruction_group_list *igl = NULL;
+	struct action *a;
+	int status = 0;
+
+	/* Check input arguments. */
+	if (!spec_file || !code_file) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Invalid input argument.";
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Pipeline configuration. */
+	s = pipeline_spec_parse(spec_file, err_line, err_msg);
+	if (!s) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	status = rte_swx_pipeline_config(&p, NULL, 0);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Pipeline configuration error.";
+		goto free;
+	}
+
+	status = pipeline_spec_configure(p, s, err_msg);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		goto free;
+	}
+
+	/*
+	 * Pipeline code generation.
+	 */
+
+	/* Instruction Group List (IGL) computation: the pipeline configuration must be done first,
+	 * but there is no need for the pipeline build to be done as well.
+	 */
+	igl = instruction_group_list_create(p);
+	if (!igl) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		status = -ENOMEM;
+		goto free;
+	}
+
+	/* Header file inclusion. */
+	fprintf(code_file, "#include \"rte_swx_pipeline_internal.h\"\n");
+	fprintf(code_file, "#include \"rte_swx_pipeline_spec.h\"\n\n");
+
+	/* Code generation for the pipeline specification. */
+	pipeline_spec_codegen(code_file, s);
+	fprintf(code_file, "\n");
+
+	/* Code generation for the action instructions. */
+	TAILQ_FOREACH(a, &p->actions, node) {
+		fprintf(code_file, "/**\n * Action %s\n */\n\n", a->name);
+
+		action_data_codegen(a, code_file);
+		fprintf(code_file, "\n");
+
+		action_instr_codegen(a, code_file);
+		fprintf(code_file, "\n");
+	}
+
+	/* Code generation for the pipeline instructions. */
+	instruction_group_list_codegen(igl, p, code_file);
+
+free:
+	instruction_group_list_free(igl);
+	rte_swx_pipeline_free(p);
+	pipeline_spec_free(s);
+
+	return status;
+}
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index ef50a0fa70..724607b87c 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -957,6 +957,31 @@ __rte_experimental
 int
 rte_swx_pipeline_build(struct rte_swx_pipeline *p);
 
+/**
+ * Pipeline C code generate based on input specification file
+ *
+ * @param[in] spec_file
+ *   Pipeline specification file (.spec) provided as input.
+ * @param[in] code_file
+ *   Pipeline C language file (.c) to be generated.
+ * @param[out] err_line
+ *   In case of error and non-NULL, the line number within the *spec* file where
+ *   the error occurred. The first line number in the file is 1.
+ * @param[out] err_msg
+ *   In case of error and non-NULL, the error message.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Resource with the same name already exists.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_codegen(FILE *spec_file,
+			 FILE *code_file,
+			 uint32_t *err_line,
+			 const char **err_msg);
+
 /**
  * Pipeline build from specification file
  *
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 50029aadcf..8d95005a5b 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -148,5 +148,6 @@ EXPERIMENTAL {
 
 	#added in 22.11
 	rte_swx_ctl_pipeline_find;
+	rte_swx_pipeline_codegen;
 	rte_swx_pipeline_find;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 08/17] pipeline: add API for shared library-based pipeline build
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (5 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 07/17] pipeline: add API for pipeline code generation Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 09/17] examples/pipeline: add CLI command for pipeline code generation Cristian Dumitrescu
                         ` (9 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Previously, the pipeline build operation was done based on the
specification file (typically produced by the P4 compiler), then the C
code with optimized functions for the pipeline actions and
instructions was generated, built into a shared object library, loaded
and installed into the pipeline in a completely hardcoded and
non-customizable way.

Now, this process is split into three explicit stages:
i) code generation (specification file -> C file);
ii) code build (C file -> shared object library);
iii) code installation (library load into the pipeline).

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c              |  85 ++++---
 lib/pipeline/rte_swx_pipeline.c      | 319 +++++++++++----------------
 lib/pipeline/rte_swx_pipeline.h      |  38 ++--
 lib/pipeline/rte_swx_pipeline_spec.c |  51 -----
 lib/pipeline/version.map             |   2 +-
 5 files changed, 208 insertions(+), 287 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index ad553f19ab..f0285675b3 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -984,55 +984,88 @@ cmd_pipeline_port_out(char **tokens,
 }
 
 static const char cmd_pipeline_build_help[] =
-"pipeline <pipeline_name> build <spec_file>\n";
+"pipeline <pipeline_name> build lib <lib_file> io <iospec_file> numa <numa_node>\n";
 
 static void
 cmd_pipeline_build(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p = NULL;
-	FILE *spec = NULL;
-	uint32_t err_line;
-	const char *err_msg;
-	int status;
+	struct rte_swx_pipeline *p = NULL;
+	struct rte_swx_ctl_pipeline *ctl = NULL;
+	char *pipeline_name, *lib_file_name, *iospec_file_name;
+	FILE *iospec_file = NULL;
+	uint32_t numa_node = 0;
+	int status = 0;
 
-	if (n_tokens != 4) {
+	/* Parsing. */
+	if (n_tokens != 9) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || p->ctl) {
-		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+	pipeline_name = tokens[1];
+
+	if (strcmp(tokens[2], "build")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "build");
 		return;
 	}
 
-	spec = fopen(tokens[3], "r");
-	if (!spec) {
-		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
+	if (strcmp(tokens[3], "lib")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "lib");
 		return;
 	}
 
-	status = rte_swx_pipeline_build_from_spec(p->p,
-		spec,
-		&err_line,
-		&err_msg);
-	fclose(spec);
-	if (status) {
-		snprintf(out, out_size, "Error %d at line %u: %s\n.",
-			status, err_line, err_msg);
+	lib_file_name = tokens[4];
+
+	if (strcmp(tokens[5], "io")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "io");
 		return;
 	}
 
-	p->ctl = rte_swx_ctl_pipeline_create(p->p);
-	if (!p->ctl) {
-		snprintf(out, out_size, "Pipeline control create failed.");
-		rte_swx_pipeline_free(p->p);
+	iospec_file_name = tokens[6];
+
+	if (strcmp(tokens[7], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
+
+	if (parser_read_uint32(&numa_node, tokens[8])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
+		return;
+	}
+
+	/* I/O spec file open. */
+	iospec_file = fopen(iospec_file_name, "r");
+	if (!iospec_file) {
+		snprintf(out, out_size, "Cannot open file \"%s\".\n", iospec_file_name);
+		return;
+	}
+
+	status = rte_swx_pipeline_build_from_lib(&p,
+						 pipeline_name,
+						 lib_file_name,
+						 iospec_file,
+						 (int)numa_node);
+	if (status) {
+		snprintf(out, out_size, "Pipeline build failed (%d).", status);
+		goto free;
+	}
+
+	ctl = rte_swx_ctl_pipeline_create(p);
+	if (!ctl) {
+		snprintf(out, out_size, "Pipeline control create failed.");
+		goto free;
+	}
+
+free:
+	if (status)
+		rte_swx_pipeline_free(p);
+
+	if (iospec_file)
+		fclose(iospec_file);
 }
 
 static void
diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index dd5f7107fa..12e156b00b 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -9911,9 +9911,6 @@ rte_swx_pipeline_instructions_config(struct rte_swx_pipeline *p,
 	return 0;
 }
 
-static int
-pipeline_compile(struct rte_swx_pipeline *p);
-
 int
 rte_swx_pipeline_build(struct rte_swx_pipeline *p)
 {
@@ -10003,8 +10000,6 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
 
 	p->build_done = 1;
 
-	pipeline_compile(p);
-
 	return 0;
 
 error:
@@ -13327,160 +13322,6 @@ instruction_group_list_custom_instructions_count(struct instruction_group_list *
 	return n_custom_instr;
 }
 
-static int
-pipeline_codegen(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
-{
-	struct action *a;
-	FILE *f = NULL;
-
-	/* Create the .c file. */
-	f = fopen("/tmp/pipeline.c", "w");
-	if (!f)
-		return -EIO;
-
-	/* Include the .h file. */
-	fprintf(f, "#include \"rte_swx_pipeline_internal.h\"\n");
-
-	/* Add the code for each action. */
-	TAILQ_FOREACH(a, &p->actions, node) {
-		fprintf(f, "/**\n * Action %s\n */\n\n", a->name);
-
-		action_data_codegen(a, f);
-
-		fprintf(f, "\n");
-
-		action_instr_codegen(a, f);
-
-		fprintf(f, "\n");
-	}
-
-	/* Add the pipeline code. */
-	instruction_group_list_codegen(igl, p, f);
-
-	/* Close the .c file. */
-	fclose(f);
-
-	return 0;
-}
-
-#ifndef RTE_SWX_PIPELINE_CMD_MAX_SIZE
-#define RTE_SWX_PIPELINE_CMD_MAX_SIZE 4096
-#endif
-
-static int
-pipeline_libload(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
-{
-	struct action *a;
-	struct instruction_group *g;
-	char *dir_in, *buffer = NULL;
-	const char *dir_out;
-	int status = 0;
-
-	/* Get the environment variables. */
-	dir_in = getenv("RTE_INSTALL_DIR");
-	if (!dir_in) {
-		status = -EINVAL;
-		goto free;
-	}
-
-	dir_out = "/tmp";
-
-	/* Memory allocation for the command buffer. */
-	buffer = malloc(RTE_SWX_PIPELINE_CMD_MAX_SIZE);
-	if (!buffer) {
-		status = -ENOMEM;
-		goto free;
-	}
-
-	snprintf(buffer,
-		 RTE_SWX_PIPELINE_CMD_MAX_SIZE,
-		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s/pipeline.o %s/pipeline.c "
-		 "-I %s/lib/pipeline "
-		 "-I %s/lib/eal/include "
-		 "-I %s/lib/eal/x86/include "
-		 "-I %s/lib/eal/include/generic "
-		 "-I %s/lib/meter "
-		 "-I %s/lib/port "
-		 "-I %s/lib/table "
-		 "-I %s/lib/pipeline "
-		 "-I %s/config "
-		 "-I %s/build "
-		 "-I %s/lib/eal/linux/include "
-		 ">%s/pipeline.log 2>&1 "
-		 "&& "
-		 "gcc -shared %s/pipeline.o -o %s/libpipeline.so "
-		 ">>%s/pipeline.log 2>&1",
-		 dir_out,
-		 dir_out,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_out,
-		 dir_out,
-		 dir_out,
-		 dir_out);
-
-	/* Build the shared object library. */
-	status = system(buffer);
-	if (status)
-		goto free;
-
-	/* Open library. */
-	snprintf(buffer,
-		 RTE_SWX_PIPELINE_CMD_MAX_SIZE,
-		 "%s/libpipeline.so",
-		 dir_out);
-
-	p->lib = dlopen(buffer, RTLD_LAZY);
-	if (!p->lib) {
-		status = -EIO;
-		goto free;
-	}
-
-	/* Get the action function symbols. */
-	TAILQ_FOREACH(a, &p->actions, node) {
-		snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "action_%s_run", a->name);
-
-		p->action_funcs[a->id] = dlsym(p->lib, buffer);
-		if (!p->action_funcs[a->id]) {
-			status = -EINVAL;
-			goto free;
-		}
-	}
-
-	/* Get the pipeline function symbols. */
-	TAILQ_FOREACH(g, igl, node) {
-		if (g->first_instr_id == g->last_instr_id)
-			continue;
-
-		snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "pipeline_func_%u", g->group_id);
-
-		g->func = dlsym(p->lib, buffer);
-		if (!g->func) {
-			status = -EINVAL;
-			goto free;
-		}
-	}
-
-free:
-	if (status && p->lib) {
-		dlclose(p->lib);
-		p->lib = NULL;
-	}
-
-	free(buffer);
-
-	return status;
-}
-
 static int
 pipeline_adjust_check(struct rte_swx_pipeline *p __rte_unused,
 		      struct instruction_group_list *igl)
@@ -13548,41 +13389,6 @@ pipeline_adjust(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
 	instr_jmp_resolve(p->instructions, p->instruction_data, p->n_instructions);
 }
 
-static int
-pipeline_compile(struct rte_swx_pipeline *p)
-{
-	struct instruction_group_list *igl = NULL;
-	int status = 0;
-
-	igl = instruction_group_list_create(p);
-	if (!igl) {
-		status = -ENOMEM;
-		goto free;
-	}
-
-	/* Code generation. */
-	status = pipeline_codegen(p, igl);
-	if (status)
-		goto free;
-
-	/* Build and load the shared object library. */
-	status = pipeline_libload(p, igl);
-	if (status)
-		goto free;
-
-	/* Adjust instructions. */
-	status = pipeline_adjust_check(p, igl);
-	if (status)
-		goto free;
-
-	pipeline_adjust(p, igl);
-
-free:
-	instruction_group_list_free(igl);
-
-	return status;
-}
-
 int
 rte_swx_pipeline_codegen(FILE *spec_file,
 			 FILE *code_file,
@@ -13675,3 +13481,128 @@ rte_swx_pipeline_codegen(FILE *spec_file,
 
 	return status;
 }
+
+int
+rte_swx_pipeline_build_from_lib(struct rte_swx_pipeline **pipeline,
+				const char *name,
+				const char *lib_file_name,
+				FILE *iospec_file,
+				int numa_node)
+{
+	struct rte_swx_pipeline *p = NULL;
+	void *lib = NULL;
+	struct pipeline_iospec *sio = NULL;
+	struct pipeline_spec *s = NULL;
+	struct instruction_group_list *igl = NULL;
+	struct action *a;
+	struct instruction_group *g;
+	int status = 0;
+
+	/* Check input arguments. */
+	if (!pipeline ||
+	    !name ||
+	    !name[0] ||
+	    !lib_file_name ||
+	    !lib_file_name[0] ||
+	    !iospec_file) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Open the library. */
+	lib = dlopen(lib_file_name, RTLD_LAZY);
+	if (!lib) {
+		status = -EIO;
+		goto free;
+	}
+
+	/* Get the pipeline specification structures. */
+	s = dlsym(lib, "pipeline_spec");
+	if (!s) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	sio = pipeline_iospec_parse(iospec_file, NULL, NULL);
+	if (!sio) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Pipeline configuration based on the specification structures. */
+	status = rte_swx_pipeline_config(&p, name, numa_node);
+	if (status)
+		goto free;
+
+	status = pipeline_iospec_configure(p, sio, NULL);
+	if (status)
+		goto free;
+
+	status = pipeline_spec_configure(p, s, NULL);
+	if (status)
+		goto free;
+
+	/* Pipeline build. */
+	status = rte_swx_pipeline_build(p);
+	if (status)
+		goto free;
+
+	/* Action instructions. */
+	TAILQ_FOREACH(a, &p->actions, node) {
+		char name[RTE_SWX_NAME_SIZE * 2];
+
+		snprintf(name, sizeof(name), "action_%s_run", a->name);
+
+		p->action_funcs[a->id] = dlsym(lib, name);
+		if (!p->action_funcs[a->id]) {
+			status = -EINVAL;
+			goto free;
+		}
+	}
+
+	/* Pipeline instructions. */
+	igl = instruction_group_list_create(p);
+	if (!igl) {
+		status = -ENOMEM;
+		goto free;
+	}
+
+	TAILQ_FOREACH(g, igl, node) {
+		char name[RTE_SWX_NAME_SIZE * 2];
+
+		if (g->first_instr_id == g->last_instr_id)
+			continue;
+
+		snprintf(name, sizeof(name), "pipeline_func_%u", g->group_id);
+
+		g->func = dlsym(lib, name);
+		if (!g->func) {
+			status = -EINVAL;
+			goto free;
+		}
+	}
+
+	status = pipeline_adjust_check(p, igl);
+	if (status)
+		goto free;
+
+	pipeline_adjust(p, igl);
+
+	p->lib = lib;
+
+	*pipeline = p;
+
+free:
+	instruction_group_list_free(igl);
+
+	pipeline_iospec_free(sio);
+
+	if (status) {
+		rte_swx_pipeline_free(p);
+
+		if (lib)
+			dlclose(lib);
+	}
+
+	return status;
+}
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index 724607b87c..9c629d4118 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -983,30 +983,38 @@ rte_swx_pipeline_codegen(FILE *spec_file,
 			 const char **err_msg);
 
 /**
- * Pipeline build from specification file
+ * Pipeline build from shared object library
  *
- * @param[in] p
- *   Pipeline handle.
- * @param[in] spec
- *   Pipeline specification file.
- * @param[out] err_line
- *   In case of error and non-NULL, the line number within the *spec* file where
- *   the error occurred. The first line number in the file is 1.
- * @param[out] err_msg
- *   In case of error and non-NULL, the error message.
+ * The shared object library must be built from the C language source code file
+ * previously generated by the rte_swx_pipeline_codegen() API function.
+ *
+ * The pipeline I/O specification file defines the I/O ports of the pipeline.
+ *
+ * @param[out] p
+ *   Pipeline handle. Must point to valid memory. Contains valid pipeline handle
+ *   when the function returns successfully.
+ * @param[in] name
+ *   Pipeline unique name.
+ * @param[in] lib_file_name
+ *   Shared object library file name.
+ * @param[in] iospec_file
+ *   Pipeline I/O specification file.
+ * @param[in] numa_node
+ *   Non-Uniform Memory Access (NUMA) node.
  * @return
  *   0 on success or the following error codes otherwise:
  *   -EINVAL: Invalid argument;
  *   -ENOMEM: Not enough space/cannot allocate memory;
- *   -EEXIST: Resource with the same name already exists;
+ *   -EEXIST: Pipeline with this name already exists;
  *   -ENODEV: Extern object or table creation error.
  */
 __rte_experimental
 int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec,
-				 uint32_t *err_line,
-				 const char **err_msg);
+rte_swx_pipeline_build_from_lib(struct rte_swx_pipeline **p,
+				const char *name,
+				const char *lib_file_name,
+				FILE *iospec_file,
+				int numa_node);
 
 /**
  * Pipeline run
diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index aea69b500b..63c919d16e 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -3522,57 +3522,6 @@ pipeline_spec_configure(struct rte_swx_pipeline *p,
 	return 0;
 }
 
-int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec_file,
-				 uint32_t *err_line,
-				 const char **err_msg)
-{
-	struct pipeline_spec *s = NULL;
-	int status = 0;
-
-	/* Check the input arguments. */
-	if (!p || !spec_file) {
-		if (err_line)
-			*err_line = 0;
-		if (err_msg)
-			*err_msg = "Invalid input argument.";
-		status = -EINVAL;
-		goto error;
-	}
-
-	/* Spec file parse. */
-	s = pipeline_spec_parse(spec_file, err_line, err_msg);
-	if (!s) {
-		status = -EINVAL;
-		goto error;
-	}
-
-	/* Pipeline configure. */
-	status = pipeline_spec_configure(p, s, err_msg);
-	if (status) {
-		if (err_line)
-			*err_line = 0;
-		goto error;
-	}
-
-	/* Pipeline build. */
-	status = rte_swx_pipeline_build(p);
-	if (status) {
-		if (err_line)
-			*err_line = 0;
-		if (err_msg)
-			*err_msg = "Pipeline build error.";
-		goto error;
-	}
-
-	return 0;
-
-error:
-	pipeline_spec_free(s);
-	return status;
-}
-
 static void
 port_in_params_free(void *params, const char *port_type)
 {
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 8d95005a5b..16806e6802 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -82,7 +82,6 @@ EXPERIMENTAL {
 	rte_swx_ctl_table_ops_get;
 	rte_swx_pipeline_action_config;
 	rte_swx_pipeline_build;
-	rte_swx_pipeline_build_from_spec;
 	rte_swx_pipeline_config;
 	rte_swx_pipeline_extern_func_register;
 	rte_swx_pipeline_extern_object_config;
@@ -148,6 +147,7 @@ EXPERIMENTAL {
 
 	#added in 22.11
 	rte_swx_ctl_pipeline_find;
+	rte_swx_pipeline_build_from_lib;
 	rte_swx_pipeline_codegen;
 	rte_swx_pipeline_find;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 09/17] examples/pipeline: add CLI command for pipeline code generation
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (6 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 08/17] pipeline: add API for shared library-based pipeline build Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 10/17] examples/pipeline: add CLI command for shared library build Cristian Dumitrescu
                         ` (8 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add CLI command for the pipeline code generation operation.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 61 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index f0285675b3..2b38977be1 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -983,6 +983,53 @@ cmd_pipeline_port_out(char **tokens,
 	}
 }
 
+static const char cmd_pipeline_codegen_help[] =
+"pipeline codegen <spec_file> <code_file>\n";
+
+static void
+cmd_pipeline_codegen(char **tokens,
+	uint32_t n_tokens,
+	char *out,
+	size_t out_size,
+	void *obj __rte_unused)
+{
+	FILE *spec_file = NULL;
+	FILE *code_file = NULL;
+	uint32_t err_line;
+	const char *err_msg;
+	int status;
+
+	if (n_tokens != 4) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	spec_file = fopen(tokens[2], "r");
+	if (!spec_file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", tokens[2]);
+		return;
+	}
+
+	code_file = fopen(tokens[3], "w");
+	if (!code_file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
+		return;
+	}
+
+	status = rte_swx_pipeline_codegen(spec_file,
+					  code_file,
+					  &err_line,
+					  &err_msg);
+
+	fclose(spec_file);
+	fclose(code_file);
+
+	if (status) {
+		snprintf(out, out_size, "Error %d at line %u: %s\n.",
+			status, err_line, err_msg);
+		return;
+	}
+}
 static const char cmd_pipeline_build_help[] =
 "pipeline <pipeline_name> build lib <lib_file> io <iospec_file> numa <numa_node>\n";
 
@@ -3009,6 +3056,7 @@ cmd_help(char **tokens,
 			"\tpipeline create\n"
 			"\tpipeline port in\n"
 			"\tpipeline port out\n"
+			"\tpipeline codegen\n"
 			"\tpipeline build\n"
 			"\tpipeline table add\n"
 			"\tpipeline table delete\n"
@@ -3078,6 +3126,12 @@ cmd_help(char **tokens,
 		}
 	}
 
+	if ((strcmp(tokens[0], "pipeline") == 0) &&
+		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
@@ -3356,6 +3410,13 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			return;
 		}
 
+		if ((n_tokens >= 3) &&
+			(strcmp(tokens[1], "codegen") == 0)) {
+			cmd_pipeline_codegen(tokens, n_tokens, out, out_size,
+				obj);
+			return;
+		}
+
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[2], "build") == 0)) {
 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 10/17] examples/pipeline: add CLI command for shared library build
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (7 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 09/17] examples/pipeline: add CLI command for pipeline code generation Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 11/17] examples/pipeline: remove the obsolete pipeline create CLI command Cristian Dumitrescu
                         ` (7 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add CLI command for the shared object library build operation.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 157 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 153 insertions(+), 4 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 2b38977be1..28cf8d4178 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -6,6 +6,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
@@ -25,6 +26,10 @@
 #define CMD_MAX_TOKENS     256
 #endif
 
+#ifndef MAX_LINE_SIZE
+#define MAX_LINE_SIZE 2048
+#endif
+
 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
@@ -1030,6 +1035,140 @@ cmd_pipeline_codegen(char **tokens,
 		return;
 	}
 }
+
+static const char cmd_pipeline_libbuild_help[] =
+"pipeline libbuild <code_file> <lib_file>\n";
+
+static void
+cmd_pipeline_libbuild(char **tokens,
+	uint32_t n_tokens,
+	char *out,
+	size_t out_size,
+	void *obj __rte_unused)
+{
+	char *code_file, *lib_file, *obj_file = NULL, *log_file = NULL;
+	char *install_dir, *cwd = NULL, *buffer = NULL;
+	size_t length;
+	int status = 0;
+
+	if (n_tokens != 4) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		goto free;
+	}
+
+	install_dir = getenv("RTE_INSTALL_DIR");
+	if (!install_dir) {
+		cwd = malloc(MAX_LINE_SIZE);
+		if (!cwd) {
+			snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+			goto free;
+		}
+
+		install_dir = getcwd(cwd, MAX_LINE_SIZE);
+		if (!install_dir) {
+			snprintf(out, out_size, "Error: Path too long.\n");
+			goto free;
+		}
+	}
+
+	snprintf(out, out_size, "Using DPDK source code from \"%s\".\n", install_dir);
+	out_size -= strlen(out);
+	out += strlen(out);
+
+	code_file = tokens[2];
+	length = strnlen(code_file, MAX_LINE_SIZE);
+	if ((length < 3) ||
+	    (code_file[length - 2] != '.') ||
+	    (code_file[length - 1] != 'c')) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "code_file");
+		goto free;
+	}
+
+	lib_file = tokens[3];
+	length = strnlen(lib_file, MAX_LINE_SIZE);
+	if ((length < 4) ||
+	    (lib_file[length - 3] != '.') ||
+	    (lib_file[length - 2] != 's') ||
+	    (lib_file[length - 1] != 'o')) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "lib_file");
+		goto free;
+	}
+
+	obj_file = malloc(length);
+	log_file = malloc(length + 2);
+	if (!obj_file || !log_file) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		goto free;
+	}
+
+	memcpy(obj_file, lib_file, length - 2);
+	obj_file[length - 2] = 'o';
+	obj_file[length - 1] = 0;
+
+	memcpy(log_file, lib_file, length - 2);
+	log_file[length - 2] = 'l';
+	log_file[length - 1] = 'o';
+	log_file[length] = 'g';
+	log_file[length + 1] = 0;
+
+	buffer = malloc(MAX_LINE_SIZE);
+	if (!buffer) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		return;
+	}
+
+	snprintf(buffer,
+		 MAX_LINE_SIZE,
+		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s %s "
+		 "-I %s/lib/pipeline "
+		 "-I %s/lib/eal/include "
+		 "-I %s/lib/eal/x86/include "
+		 "-I %s/lib/eal/include/generic "
+		 "-I %s/lib/meter "
+		 "-I %s/lib/port "
+		 "-I %s/lib/table "
+		 "-I %s/lib/pipeline "
+		 "-I %s/config "
+		 "-I %s/build "
+		 "-I %s/lib/eal/linux/include "
+		 ">%s 2>&1 "
+		 "&& "
+		 "gcc -shared %s -o %s "
+		 ">>%s 2>&1",
+		 obj_file,
+		 code_file,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 log_file,
+		 obj_file,
+		 lib_file,
+		 log_file);
+
+	status = system(buffer);
+	if (status) {
+		snprintf(out,
+			 out_size,
+			 "Library build failed, see file \"%s\" for details.\n",
+			 log_file);
+		goto free;
+	}
+
+free:
+	free(cwd);
+	free(obj_file);
+	free(log_file);
+	free(buffer);
+}
+
 static const char cmd_pipeline_build_help[] =
 "pipeline <pipeline_name> build lib <lib_file> io <iospec_file> numa <numa_node>\n";
 
@@ -1127,10 +1266,6 @@ table_entry_free(struct rte_swx_table_entry *entry)
 	free(entry);
 }
 
-#ifndef MAX_LINE_SIZE
-#define MAX_LINE_SIZE 2048
-#endif
-
 static int
 pipeline_table_entries_add(struct rte_swx_ctl_pipeline *p,
 			   const char *table_name,
@@ -3057,6 +3192,7 @@ cmd_help(char **tokens,
 			"\tpipeline port in\n"
 			"\tpipeline port out\n"
 			"\tpipeline codegen\n"
+			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
 			"\tpipeline table add\n"
 			"\tpipeline table delete\n"
@@ -3132,6 +3268,12 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if ((strcmp(tokens[0], "pipeline") == 0) &&
+		(n_tokens == 2) && (strcmp(tokens[1], "libbuild") == 0)) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_libbuild_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
@@ -3417,6 +3559,13 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			return;
 		}
 
+		if ((n_tokens >= 3) &&
+			(strcmp(tokens[1], "libbuild") == 0)) {
+			cmd_pipeline_libbuild(tokens, n_tokens, out, out_size,
+				obj);
+			return;
+		}
+
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[2], "build") == 0)) {
 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 11/17] examples/pipeline: remove the obsolete pipeline create CLI command
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (8 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 10/17] examples/pipeline: add CLI command for shared library build Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 12/17] examples/pipeline: remove the obsolete port configuration CLI commands Cristian Dumitrescu
                         ` (6 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

The pipeline configuration is now done through the I/O specification
file, hence this CLI command is no longer needed.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 47 -----------------------------------------
 1 file changed, 47 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 28cf8d4178..7b725a9c27 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -523,39 +523,6 @@ cmd_tap(char **tokens,
 	}
 }
 
-static const char cmd_pipeline_create_help[] =
-"pipeline <pipeline_name> create <numa_node>\n";
-
-static void
-cmd_pipeline_create(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
-{
-	struct pipeline *p;
-	char *name;
-	uint32_t numa_node;
-
-	if (n_tokens != 4) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-
-	name = tokens[1];
-
-	if (parser_read_uint32(&numa_node, tokens[3]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
-		return;
-	}
-
-	p = pipeline_create(obj, name, (int)numa_node);
-	if (!p) {
-		snprintf(out, out_size, "pipeline create error.");
-		return;
-	}
-}
-
 static const char cmd_pipeline_port_in_help[] =
 "pipeline <pipeline_name> port in <port_id>\n"
 "   link <link_name> rxq <queue_id> bsz <burst_size>\n"
@@ -3188,7 +3155,6 @@ cmd_help(char **tokens,
 			"\tmempool\n"
 			"\tlink\n"
 			"\ttap\n"
-			"\tpipeline create\n"
 			"\tpipeline port in\n"
 			"\tpipeline port out\n"
 			"\tpipeline codegen\n"
@@ -3241,12 +3207,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if ((strcmp(tokens[0], "pipeline") == 0) &&
-		(n_tokens == 2) && (strcmp(tokens[1], "create") == 0)) {
-		snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help);
-		return;
-	}
-
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) {
 		if (strcmp(tokens[2], "in") == 0) {
@@ -3529,13 +3489,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 	}
 
 	if (strcmp(tokens[0], "pipeline") == 0) {
-		if ((n_tokens >= 3) &&
-			(strcmp(tokens[2], "create") == 0)) {
-			cmd_pipeline_create(tokens, n_tokens, out, out_size,
-				obj);
-			return;
-		}
-
 		if ((n_tokens >= 4) &&
 			(strcmp(tokens[2], "port") == 0) &&
 			(strcmp(tokens[3], "in") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 12/17] examples/pipeline: remove the obsolete port configuration CLI commands
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (9 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 11/17] examples/pipeline: remove the obsolete pipeline create CLI command Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 13/17] examples/pipeline: remove the obsolete mirroring configuration CLI command Cristian Dumitrescu
                         ` (5 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

The pipeline I/O ports configuration is now done through the I/O
specification file, hence these CLI commands are no longer needed.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 465 ----------------------------------------
 1 file changed, 465 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 7b725a9c27..b26e73c706 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -523,438 +523,6 @@ cmd_tap(char **tokens,
 	}
 }
 
-static const char cmd_pipeline_port_in_help[] =
-"pipeline <pipeline_name> port in <port_id>\n"
-"   link <link_name> rxq <queue_id> bsz <burst_size>\n"
-"   ring <ring_name> bsz <burst_size>\n"
-"   | source <mempool_name> <file_name> loop <n_loops>\n"
-"   | tap <tap_name> mempool <mempool_name> mtu <mtu> bsz <burst_size>\n";
-
-static void
-cmd_pipeline_port_in(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
-{
-	struct pipeline *p;
-	int status;
-	uint32_t port_id = 0, t0;
-
-	if (n_tokens < 6) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || p->ctl) {
-		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
-		return;
-	}
-
-	if (strcmp(tokens[2], "port") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
-		return;
-	}
-
-	if (strcmp(tokens[3], "in") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
-		return;
-	}
-
-	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
-		return;
-	}
-
-	t0 = 5;
-
-	if (strcmp(tokens[t0], "link") == 0) {
-		struct rte_swx_port_ethdev_reader_params params;
-		struct link *link;
-
-		if (n_tokens < t0 + 6) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port in link");
-			return;
-		}
-
-		link = link_find(obj, tokens[t0 + 1]);
-		if (!link) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"link_name");
-			return;
-		}
-		params.dev_name = link->dev_name;
-
-		if (strcmp(tokens[t0 + 2], "rxq") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
-			return;
-		}
-
-		if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"queue_id");
-			return;
-		}
-
-		if (strcmp(tokens[t0 + 4], "bsz") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
-			return;
-		}
-
-		if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"burst_size");
-			return;
-		}
-
-		t0 += 6;
-
-		status = rte_swx_pipeline_port_in_config(p->p,
-			port_id,
-			"ethdev",
-			&params);
-	} else if (strcmp(tokens[t0], "ring") == 0) {
-		struct rte_swx_port_ring_reader_params params;
-		struct ring *ring;
-
-		if (n_tokens < t0 + 4) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port in ring");
-			return;
-		}
-
-		ring = ring_find(obj, tokens[t0 + 1]);
-		if (!ring) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"ring_name");
-			return;
-		}
-		params.name = ring->name;
-
-		if (strcmp(tokens[t0 + 2], "bsz") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
-			return;
-		}
-
-		if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"burst_size");
-			return;
-		}
-
-		t0 += 4;
-
-		status = rte_swx_pipeline_port_in_config(p->p,
-			port_id,
-			"ring",
-			&params);
-	} else if (strcmp(tokens[t0], "source") == 0) {
-		struct rte_swx_port_source_params params;
-		struct mempool *mp;
-
-		if (n_tokens < t0 + 5) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port in source");
-			return;
-		}
-
-		mp = mempool_find(obj, tokens[t0 + 1]);
-		if (!mp) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"mempool_name");
-			return;
-		}
-		params.pool = mp->m;
-
-		params.file_name = tokens[t0 + 2];
-
-		if (strcmp(tokens[t0 + 3], "loop") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "loop");
-			return;
-		}
-
-		if (parser_read_uint64(&params.n_loops, tokens[t0 + 4])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"n_loops");
-			return;
-		}
-
-		t0 += 5;
-
-		status = rte_swx_pipeline_port_in_config(p->p,
-			port_id,
-			"source",
-			&params);
-	} else if (strcmp(tokens[t0], "tap") == 0) {
-		struct rte_swx_port_fd_reader_params params;
-		struct tap *tap;
-		struct mempool *mp;
-
-		if (n_tokens < t0 + 8) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port in tap");
-			return;
-		}
-
-		tap = tap_find(obj, tokens[t0 + 1]);
-		if (!tap) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"tap_name");
-			return;
-		}
-		params.fd = tap->fd;
-
-		if (strcmp(tokens[t0 + 2], "mempool") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-				"mempool");
-			return;
-		}
-
-		mp = mempool_find(obj, tokens[t0 + 3]);
-		if (!mp) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"mempool_name");
-			return;
-		}
-		params.mempool = mp->m;
-
-		if (strcmp(tokens[t0 + 4], "mtu") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-				"mtu");
-			return;
-		}
-
-		if (parser_read_uint32(&params.mtu, tokens[t0 + 5]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
-			return;
-		}
-
-		if (strcmp(tokens[t0 + 6], "bsz") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
-			return;
-		}
-
-		if (parser_read_uint32(&params.burst_size, tokens[t0 + 7])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"burst_size");
-			return;
-		}
-
-		t0 += 8;
-
-		status = rte_swx_pipeline_port_in_config(p->p,
-			port_id,
-			"fd",
-			&params);
-
-	} else {
-		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
-		return;
-	}
-
-	if (status) {
-		snprintf(out, out_size, "port in error.");
-		return;
-	}
-
-	if (n_tokens != t0) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-}
-
-static const char cmd_pipeline_port_out_help[] =
-"pipeline <pipeline_name> port out <port_id>\n"
-"   link <link_name> txq <txq_id> bsz <burst_size>\n"
-"   ring <ring_name> bsz <burst_size>\n"
-"   | sink <file_name> | none\n"
-"   | tap <tap_name> bsz <burst_size>\n";
-
-static void
-cmd_pipeline_port_out(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
-{
-	struct pipeline *p;
-	int status;
-	uint32_t port_id = 0, t0;
-
-	if (n_tokens < 6) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || p->ctl) {
-		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
-		return;
-	}
-
-	if (strcmp(tokens[2], "port") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
-		return;
-	}
-
-	if (strcmp(tokens[3], "out") != 0) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
-		return;
-	}
-
-	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
-		return;
-	}
-
-	t0 = 5;
-
-	if (strcmp(tokens[t0], "link") == 0) {
-		struct rte_swx_port_ethdev_writer_params params;
-		struct link *link;
-
-		if (n_tokens < t0 + 6) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port out link");
-			return;
-		}
-
-		link = link_find(obj, tokens[t0 + 1]);
-		if (!link) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"link_name");
-			return;
-		}
-		params.dev_name = link->dev_name;
-
-		if (strcmp(tokens[t0 + 2], "txq") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
-			return;
-		}
-
-		if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"queue_id");
-			return;
-		}
-
-		if (strcmp(tokens[t0 + 4], "bsz") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
-			return;
-		}
-
-		if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"burst_size");
-			return;
-		}
-
-		t0 += 6;
-
-		status = rte_swx_pipeline_port_out_config(p->p,
-			port_id,
-			"ethdev",
-			&params);
-	} else if (strcmp(tokens[t0], "ring") == 0) {
-		struct rte_swx_port_ring_writer_params params;
-		struct ring *ring;
-
-		if (n_tokens < t0 + 4) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port out link");
-			return;
-		}
-
-		ring = ring_find(obj, tokens[t0 + 1]);
-		if (!ring) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"ring_name");
-			return;
-		}
-		params.name = ring->name;
-
-		if (strcmp(tokens[t0 + 2], "bsz") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
-			return;
-		}
-
-		if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"burst_size");
-			return;
-		}
-
-		t0 += 4;
-
-		status = rte_swx_pipeline_port_out_config(p->p,
-			port_id,
-			"ring",
-			&params);
-	} else if (strcmp(tokens[t0], "sink") == 0) {
-		struct rte_swx_port_sink_params params;
-
-		params.file_name = strcmp(tokens[t0 + 1], "none") ?
-			tokens[t0 + 1] : NULL;
-
-		t0 += 2;
-
-		status = rte_swx_pipeline_port_out_config(p->p,
-			port_id,
-			"sink",
-			&params);
-	} else if (strcmp(tokens[t0], "tap") == 0) {
-		struct rte_swx_port_fd_writer_params params;
-		struct tap *tap;
-
-		if (n_tokens < t0 + 4) {
-			snprintf(out, out_size, MSG_ARG_MISMATCH,
-				"pipeline port out tap");
-			return;
-		}
-
-		tap = tap_find(obj, tokens[t0 + 1]);
-		if (!tap) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"tap_name");
-			return;
-		}
-		params.fd = tap->fd;
-
-		if (strcmp(tokens[t0 + 2], "bsz") != 0) {
-			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
-			return;
-		}
-
-		if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
-			snprintf(out, out_size, MSG_ARG_INVALID,
-				"burst_size");
-			return;
-		}
-
-		t0 += 4;
-
-		status = rte_swx_pipeline_port_out_config(p->p,
-			port_id,
-			"fd",
-			&params);
-	} else {
-		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
-		return;
-	}
-
-	if (status) {
-		snprintf(out, out_size, "port out error.");
-		return;
-	}
-
-	if (n_tokens != t0) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-}
-
 static const char cmd_pipeline_codegen_help[] =
 "pipeline codegen <spec_file> <code_file>\n";
 
@@ -3155,8 +2723,6 @@ cmd_help(char **tokens,
 			"\tmempool\n"
 			"\tlink\n"
 			"\ttap\n"
-			"\tpipeline port in\n"
-			"\tpipeline port out\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
@@ -3207,21 +2773,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if ((strcmp(tokens[0], "pipeline") == 0) &&
-		(n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) {
-		if (strcmp(tokens[2], "in") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_pipeline_port_in_help);
-			return;
-		}
-
-		if (strcmp(tokens[2], "out") == 0) {
-			snprintf(out, out_size, "\n%s\n",
-				cmd_pipeline_port_out_help);
-			return;
-		}
-	}
-
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
@@ -3489,22 +3040,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 	}
 
 	if (strcmp(tokens[0], "pipeline") == 0) {
-		if ((n_tokens >= 4) &&
-			(strcmp(tokens[2], "port") == 0) &&
-			(strcmp(tokens[3], "in") == 0)) {
-			cmd_pipeline_port_in(tokens, n_tokens, out, out_size,
-				obj);
-			return;
-		}
-
-		if ((n_tokens >= 4) &&
-			(strcmp(tokens[2], "port") == 0) &&
-			(strcmp(tokens[3], "out") == 0)) {
-			cmd_pipeline_port_out(tokens, n_tokens, out, out_size,
-				obj);
-			return;
-		}
-
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[1], "codegen") == 0)) {
 			cmd_pipeline_codegen(tokens, n_tokens, out, out_size,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 13/17] examples/pipeline: remove the obsolete mirroring configuration CLI command
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (10 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 12/17] examples/pipeline: remove the obsolete port configuration CLI commands Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 14/17] examples/pipeline: use the pipeline name query API Cristian Dumitrescu
                         ` (4 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

The pipeline mirroring configuration is done through the I/O
specification file, so this CLI command is no longer needed.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 76 -----------------------------------------
 1 file changed, 76 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index b26e73c706..fa828c008b 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -2457,68 +2457,6 @@ cmd_pipeline_stats(char **tokens,
 	}
 }
 
-static const char cmd_pipeline_mirror_help[] =
-"pipeline <pipeline_name> mirror slots <n_slots> sessions <n_sessions>\n";
-
-static void
-cmd_pipeline_mirror(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
-{
-	struct rte_swx_pipeline_mirroring_params params;
-	struct pipeline *p;
-	int status;
-
-	if (n_tokens != 7) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-
-	if (strcmp(tokens[0], "pipeline")) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
-		return;
-	}
-
-	p = pipeline_find(obj, tokens[1]);
-	if (!p) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
-		return;
-	}
-
-	if (strcmp(tokens[2], "mirror")) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mirror");
-		return;
-	}
-
-	if (strcmp(tokens[3], "slots")) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "slots");
-		return;
-	}
-
-	if (parser_read_uint32(&params.n_slots, tokens[4])) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "n_slots");
-		return;
-	}
-
-	if (strcmp(tokens[5], "sessions")) {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sessions");
-		return;
-	}
-
-	if (parser_read_uint32(&params.n_sessions, tokens[6])) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "n_sessions");
-		return;
-	}
-
-	status = rte_swx_pipeline_mirroring_config(p->p, &params);
-	if (status) {
-		snprintf(out, out_size, "Command failed!\n");
-		return;
-	}
-}
-
 static const char cmd_pipeline_mirror_session_help[] =
 "pipeline <pipeline_name> mirror session <session_id> port <port_id> clone fast | slow "
 "truncate <truncation_length>\n";
@@ -2746,7 +2684,6 @@ cmd_help(char **tokens,
 			"\tpipeline meter set\n"
 			"\tpipeline meter stats\n"
 			"\tpipeline stats\n"
-			"\tpipeline mirror\n"
 			"\tpipeline mirror session\n"
 			"\tthread pipeline enable\n"
 			"\tthread pipeline disable\n\n");
@@ -2958,12 +2895,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if (!strcmp(tokens[0], "pipeline") &&
-		(n_tokens == 2) && !strcmp(tokens[1], "mirror")) {
-		snprintf(out, out_size, "\n%s\n", cmd_pipeline_mirror_help);
-		return;
-	}
-
 	if (!strcmp(tokens[0], "pipeline") &&
 		(n_tokens == 3) && !strcmp(tokens[1], "mirror")
 		&& !strcmp(tokens[2], "session")) {
@@ -3217,13 +3148,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			return;
 		}
 
-		if ((n_tokens >= 4) &&
-			(strcmp(tokens[2], "mirror") == 0) &&
-			(strcmp(tokens[3], "slots") == 0)) {
-			cmd_pipeline_mirror(tokens, n_tokens, out, out_size, obj);
-			return;
-		}
-
 		if ((n_tokens >= 4) &&
 			(strcmp(tokens[2], "mirror") == 0) &&
 			(strcmp(tokens[3], "session") == 0)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 14/17] examples/pipeline: use the pipeline name query API
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (11 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 13/17] examples/pipeline: remove the obsolete mirroring configuration CLI command Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 15/17] examples/pipeline: rework the link CLI command Cristian Dumitrescu
                         ` (3 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Convert the CLI commands to use the pipeline name query API and remove
the linked list of pipeline objects previously maintained by the
application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c    | 268 ++++++++++++++++++++-----------------
 examples/pipeline/obj.c    |  67 ----------
 examples/pipeline/obj.h    |  24 ----
 examples/pipeline/thread.c |  65 ++++-----
 examples/pipeline/thread.h |   9 +-
 5 files changed, 175 insertions(+), 258 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index fa828c008b..f48ff326be 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -858,9 +858,9 @@ cmd_pipeline_table_add(char **tokens,
 		       uint32_t n_tokens,
 		       char *out,
 		       size_t out_size,
-		       void *obj)
+		       void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *table_name, *file_name;
 	FILE *file = NULL;
 	uint32_t file_line_number = 0;
@@ -872,8 +872,8 @@ cmd_pipeline_table_add(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -887,7 +887,7 @@ cmd_pipeline_table_add(char **tokens,
 		return;
 	}
 
-	status = pipeline_table_entries_add(p->ctl,
+	status = pipeline_table_entries_add(ctl,
 					    table_name,
 					    file,
 					    &file_line_number);
@@ -956,9 +956,9 @@ cmd_pipeline_table_delete(char **tokens,
 			  uint32_t n_tokens,
 			  char *out,
 			  size_t out_size,
-			  void *obj)
+			  void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *table_name, *file_name;
 	FILE *file = NULL;
 	uint32_t file_line_number = 0;
@@ -970,8 +970,8 @@ cmd_pipeline_table_delete(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -985,7 +985,7 @@ cmd_pipeline_table_delete(char **tokens,
 		return;
 	}
 
-	status = pipeline_table_entries_delete(p->ctl,
+	status = pipeline_table_entries_delete(ctl,
 					       table_name,
 					       file,
 					       &file_line_number);
@@ -1054,9 +1054,9 @@ cmd_pipeline_table_default(char **tokens,
 			   uint32_t n_tokens,
 			   char *out,
 			   size_t out_size,
-			   void *obj)
+			   void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *table_name, *file_name;
 	FILE *file = NULL;
 	uint32_t file_line_number = 0;
@@ -1068,8 +1068,8 @@ cmd_pipeline_table_default(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1083,7 +1083,7 @@ cmd_pipeline_table_default(char **tokens,
 		return;
 	}
 
-	status = pipeline_table_default_entry_add(p->ctl,
+	status = pipeline_table_default_entry_add(ctl,
 						  table_name,
 						  file,
 						  &file_line_number);
@@ -1103,9 +1103,9 @@ cmd_pipeline_table_show(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *table_name;
 	FILE *file = NULL;
 	int status;
@@ -1116,8 +1116,8 @@ cmd_pipeline_table_show(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1129,7 +1129,7 @@ cmd_pipeline_table_show(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_table_fprintf(file, p->ctl, table_name);
+	status = rte_swx_ctl_pipeline_table_fprintf(file, ctl, table_name);
 	if (status)
 		snprintf(out, out_size, MSG_ARG_INVALID, "table_name");
 
@@ -1145,9 +1145,9 @@ cmd_pipeline_selector_group_add(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *selector_name;
 	uint32_t group_id;
 	int status;
@@ -1158,8 +1158,8 @@ cmd_pipeline_selector_group_add(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1177,7 +1177,7 @@ cmd_pipeline_selector_group_add(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_selector_group_add(p->ctl,
+	status = rte_swx_ctl_pipeline_selector_group_add(ctl,
 		selector_name,
 		&group_id);
 	if (status)
@@ -1194,9 +1194,9 @@ cmd_pipeline_selector_group_delete(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *selector_name;
 	uint32_t group_id;
 	int status;
@@ -1207,8 +1207,8 @@ cmd_pipeline_selector_group_delete(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1231,7 +1231,7 @@ cmd_pipeline_selector_group_delete(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_selector_group_delete(p->ctl,
+	status = rte_swx_ctl_pipeline_selector_group_delete(ctl,
 		selector_name,
 		group_id);
 	if (status)
@@ -1402,9 +1402,9 @@ cmd_pipeline_selector_group_member_add(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *selector_name, *file_name;
 	FILE *file = NULL;
 	uint32_t file_line_number = 0;
@@ -1416,8 +1416,8 @@ cmd_pipeline_selector_group_member_add(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1443,7 +1443,7 @@ cmd_pipeline_selector_group_member_add(char **tokens,
 		return;
 	}
 
-	status = pipeline_selector_group_members_add(p->ctl,
+	status = pipeline_selector_group_members_add(ctl,
 					    selector_name,
 					    file,
 					    &file_line_number);
@@ -1512,9 +1512,9 @@ cmd_pipeline_selector_group_member_delete(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *selector_name, *file_name;
 	FILE *file = NULL;
 	uint32_t file_line_number = 0;
@@ -1526,8 +1526,8 @@ cmd_pipeline_selector_group_member_delete(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1553,7 +1553,7 @@ cmd_pipeline_selector_group_member_delete(char **tokens,
 		return;
 	}
 
-	status = pipeline_selector_group_members_delete(p->ctl,
+	status = pipeline_selector_group_members_delete(ctl,
 					    selector_name,
 					    file,
 					    &file_line_number);
@@ -1573,9 +1573,9 @@ cmd_pipeline_selector_show(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *selector_name;
 	FILE *file = NULL;
 	int status;
@@ -1586,8 +1586,8 @@ cmd_pipeline_selector_show(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1600,7 +1600,7 @@ cmd_pipeline_selector_show(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_selector_fprintf(file, p->ctl, selector_name);
+	status = rte_swx_ctl_pipeline_selector_fprintf(file, ctl, selector_name);
 	if (status)
 		snprintf(out, out_size, MSG_ARG_INVALID, "selector_name");
 
@@ -1665,9 +1665,9 @@ cmd_pipeline_learner_default(char **tokens,
 			     uint32_t n_tokens,
 			     char *out,
 			     size_t out_size,
-			     void *obj)
+			     void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name, *learner_name, *file_name;
 	FILE *file = NULL;
 	uint32_t file_line_number = 0;
@@ -1679,8 +1679,8 @@ cmd_pipeline_learner_default(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1694,7 +1694,7 @@ cmd_pipeline_learner_default(char **tokens,
 		return;
 	}
 
-	status = pipeline_learner_default_entry_add(p->ctl,
+	status = pipeline_learner_default_entry_add(ctl,
 						    learner_name,
 						    file,
 						    &file_line_number);
@@ -1714,9 +1714,9 @@ cmd_pipeline_commit(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name;
 	int status;
 
@@ -1726,13 +1726,13 @@ cmd_pipeline_commit(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
+	status = rte_swx_ctl_pipeline_commit(ctl, 1);
 	if (status)
 		snprintf(out, out_size, "Commit failed. "
 			"Use \"commit\" to retry or \"abort\" to discard the pending work.\n");
@@ -1746,9 +1746,9 @@ cmd_pipeline_abort(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_ctl_pipeline *ctl;
 	char *pipeline_name;
 
 	if (n_tokens != 3) {
@@ -1757,13 +1757,13 @@ cmd_pipeline_abort(char **tokens,
 	}
 
 	pipeline_name = tokens[1];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
+	if (!ctl) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
 
-	rte_swx_ctl_pipeline_abort(p->ctl);
+	rte_swx_ctl_pipeline_abort(ctl);
 }
 
 static const char cmd_pipeline_regrd_help[] =
@@ -1774,9 +1774,9 @@ cmd_pipeline_regrd(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *name;
 	uint64_t value;
 	uint32_t idx;
@@ -1787,8 +1787,8 @@ cmd_pipeline_regrd(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1805,7 +1805,7 @@ cmd_pipeline_regrd(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_regarray_read(p->p, name, idx, &value);
+	status = rte_swx_ctl_pipeline_regarray_read(p, name, idx, &value);
 	if (status) {
 		snprintf(out, out_size, "Command failed.\n");
 		return;
@@ -1822,9 +1822,9 @@ cmd_pipeline_regwr(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *name;
 	uint64_t value;
 	uint32_t idx;
@@ -1835,8 +1835,8 @@ cmd_pipeline_regwr(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1858,7 +1858,7 @@ cmd_pipeline_regwr(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_regarray_write(p->p, name, idx, value);
+	status = rte_swx_ctl_pipeline_regarray_write(p, name, idx, value);
 	if (status) {
 		snprintf(out, out_size, "Command failed.\n");
 		return;
@@ -1874,10 +1874,10 @@ cmd_pipeline_meter_profile_add(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
 	struct rte_meter_trtcm_params params;
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *profile_name;
 	int status;
 
@@ -1886,8 +1886,8 @@ cmd_pipeline_meter_profile_add(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1949,7 +1949,7 @@ cmd_pipeline_meter_profile_add(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_meter_profile_add(p->p, profile_name, &params);
+	status = rte_swx_ctl_meter_profile_add(p, profile_name, &params);
 	if (status) {
 		snprintf(out, out_size, "Command failed.\n");
 		return;
@@ -1964,9 +1964,9 @@ cmd_pipeline_meter_profile_delete(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *profile_name;
 	int status;
 
@@ -1975,8 +1975,8 @@ cmd_pipeline_meter_profile_delete(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -1998,7 +1998,7 @@ cmd_pipeline_meter_profile_delete(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_meter_profile_delete(p->p, profile_name);
+	status = rte_swx_ctl_meter_profile_delete(p, profile_name);
 	if (status) {
 		snprintf(out, out_size, "Command failed.\n");
 		return;
@@ -2014,9 +2014,9 @@ cmd_pipeline_meter_reset(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *name;
 	uint32_t idx0 = 0, idx1 = 0;
 
@@ -2025,8 +2025,8 @@ cmd_pipeline_meter_reset(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2066,7 +2066,7 @@ cmd_pipeline_meter_reset(char **tokens,
 	for ( ; idx0 <= idx1; idx0++) {
 		int status;
 
-		status = rte_swx_ctl_meter_reset(p->p, name, idx0);
+		status = rte_swx_ctl_meter_reset(p, name, idx0);
 		if (status) {
 			snprintf(out, out_size, "Command failed for index %u.\n", idx0);
 			return;
@@ -2083,9 +2083,9 @@ cmd_pipeline_meter_set(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *name, *profile_name;
 	uint32_t idx0 = 0, idx1 = 0;
 
@@ -2094,8 +2094,8 @@ cmd_pipeline_meter_set(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2142,7 +2142,7 @@ cmd_pipeline_meter_set(char **tokens,
 	for ( ; idx0 <= idx1; idx0++) {
 		int status;
 
-		status = rte_swx_ctl_meter_set(p->p, name, idx0, profile_name);
+		status = rte_swx_ctl_meter_set(p, name, idx0, profile_name);
 		if (status) {
 			snprintf(out, out_size, "Command failed for index %u.\n", idx0);
 			return;
@@ -2159,10 +2159,10 @@ cmd_pipeline_meter_stats(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
 	struct rte_swx_ctl_meter_stats stats;
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	const char *name;
 	uint32_t idx0 = 0, idx1 = 0;
 
@@ -2171,8 +2171,8 @@ cmd_pipeline_meter_stats(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2235,7 +2235,7 @@ cmd_pipeline_meter_stats(char **tokens,
 	for ( ; idx0 <= idx1; idx0++) {
 		int status;
 
-		status = rte_swx_ctl_meter_stats_read(p->p, name, idx0, &stats);
+		status = rte_swx_ctl_meter_stats_read(p, name, idx0, &stats);
 		if (status) {
 			snprintf(out, out_size, "Pipeline meter stats error at index %u.\n", idx0);
 			out_size -= strlen(out);
@@ -2265,10 +2265,10 @@ cmd_pipeline_stats(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
 	struct rte_swx_ctl_pipeline_info info;
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	uint32_t i;
 	int status;
 
@@ -2277,8 +2277,8 @@ cmd_pipeline_stats(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2288,7 +2288,7 @@ cmd_pipeline_stats(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_info_get(p->p, &info);
+	status = rte_swx_ctl_pipeline_info_get(p, &info);
 	if (status) {
 		snprintf(out, out_size, "Pipeline info get error.");
 		return;
@@ -2301,7 +2301,7 @@ cmd_pipeline_stats(char **tokens,
 	for (i = 0; i < info.n_ports_in; i++) {
 		struct rte_swx_port_in_stats stats;
 
-		rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats);
+		rte_swx_ctl_pipeline_port_in_stats_read(p, i, &stats);
 
 		snprintf(out, out_size, "\tPort %u:"
 			" packets %" PRIu64
@@ -2319,7 +2319,7 @@ cmd_pipeline_stats(char **tokens,
 	for (i = 0; i < info.n_ports_out; i++) {
 		struct rte_swx_port_out_stats stats;
 
-		rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats);
+		rte_swx_ctl_pipeline_port_out_stats_read(p, i, &stats);
 
 		if (i != info.n_ports_out - 1)
 			snprintf(out, out_size, "\tPort %u:", i);
@@ -2358,13 +2358,13 @@ cmd_pipeline_stats(char **tokens,
 		};
 		uint32_t j;
 
-		status = rte_swx_ctl_table_info_get(p->p, i, &table_info);
+		status = rte_swx_ctl_table_info_get(p, i, &table_info);
 		if (status) {
 			snprintf(out, out_size, "Table info get error.");
 			return;
 		}
 
-		status = rte_swx_ctl_pipeline_table_stats_read(p->p, table_info.name, &stats);
+		status = rte_swx_ctl_pipeline_table_stats_read(p, table_info.name, &stats);
 		if (status) {
 			snprintf(out, out_size, "Table stats read error.");
 			return;
@@ -2382,7 +2382,7 @@ cmd_pipeline_stats(char **tokens,
 		for (j = 0; j < info.n_actions; j++) {
 			struct rte_swx_ctl_action_info action_info;
 
-			status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
+			status = rte_swx_ctl_action_info_get(p, j, &action_info);
 			if (status) {
 				snprintf(out, out_size, "Action info get error.");
 				return;
@@ -2410,13 +2410,13 @@ cmd_pipeline_stats(char **tokens,
 		};
 		uint32_t j;
 
-		status = rte_swx_ctl_learner_info_get(p->p, i, &learner_info);
+		status = rte_swx_ctl_learner_info_get(p, i, &learner_info);
 		if (status) {
 			snprintf(out, out_size, "Learner table info get error.");
 			return;
 		}
 
-		status = rte_swx_ctl_pipeline_learner_stats_read(p->p, learner_info.name, &stats);
+		status = rte_swx_ctl_pipeline_learner_stats_read(p, learner_info.name, &stats);
 		if (status) {
 			snprintf(out, out_size, "Learner table stats read error.");
 			return;
@@ -2442,7 +2442,7 @@ cmd_pipeline_stats(char **tokens,
 		for (j = 0; j < info.n_actions; j++) {
 			struct rte_swx_ctl_action_info action_info;
 
-			status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
+			status = rte_swx_ctl_action_info_get(p, j, &action_info);
 			if (status) {
 				snprintf(out, out_size, "Action info get error.");
 				return;
@@ -2466,10 +2466,10 @@ cmd_pipeline_mirror_session(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
 	struct rte_swx_pipeline_mirroring_session_params params;
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	uint32_t session_id = 0;
 	int status;
 
@@ -2483,8 +2483,8 @@ cmd_pipeline_mirror_session(char **tokens,
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(tokens[1]);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2538,7 +2538,7 @@ cmd_pipeline_mirror_session(char **tokens,
 		return;
 	}
 
-	status = rte_swx_ctl_pipeline_mirroring_session_set(p->p, session_id, &params);
+	status = rte_swx_ctl_pipeline_mirroring_session_set(p, session_id, &params);
 	if (status) {
 		snprintf(out, out_size, "Command failed!\n");
 		return;
@@ -2546,21 +2546,25 @@ cmd_pipeline_mirror_session(char **tokens,
 }
 
 static const char cmd_thread_pipeline_enable_help[] =
-"thread <thread_id> pipeline <pipeline_name> enable\n";
+"thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
+
+#ifndef TIMER_PERIOD_MS_DEFAULT
+#define TIMER_PERIOD_MS_DEFAULT 10
+#endif
 
 static void
 cmd_thread_pipeline_enable(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
 	char *pipeline_name;
-	struct pipeline *p;
-	uint32_t thread_id;
+	struct rte_swx_pipeline *p;
+	uint32_t thread_id, timer_period_ms = TIMER_PERIOD_MS_DEFAULT;
 	int status;
 
-	if (n_tokens != 5) {
+	if ((n_tokens != 5) && (n_tokens != 7)) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
@@ -2576,8 +2580,8 @@ cmd_thread_pipeline_enable(char **tokens,
 	}
 
 	pipeline_name = tokens[3];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(pipeline_name);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2587,7 +2591,19 @@ cmd_thread_pipeline_enable(char **tokens,
 		return;
 	}
 
-	status = thread_pipeline_enable(thread_id, obj, pipeline_name);
+	if (n_tokens == 7) {
+		if (strcmp(tokens[5], "period") != 0) {
+			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
+			return;
+		}
+
+		if (parser_read_uint32(&timer_period_ms, tokens[6]) != 0) {
+			snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
+			return;
+		}
+	}
+
+	status = thread_pipeline_enable(thread_id, p, timer_period_ms);
 	if (status) {
 		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
 		return;
@@ -2602,9 +2618,9 @@ cmd_thread_pipeline_disable(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p;
+	struct rte_swx_pipeline *p;
 	char *pipeline_name;
 	uint32_t thread_id;
 	int status;
@@ -2625,8 +2641,8 @@ cmd_thread_pipeline_disable(char **tokens,
 	}
 
 	pipeline_name = tokens[3];
-	p = pipeline_find(obj, pipeline_name);
-	if (!p || !p->ctl) {
+	p = rte_swx_pipeline_find(pipeline_name);
+	if (!p) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
 		return;
 	}
@@ -2636,7 +2652,7 @@ cmd_thread_pipeline_disable(char **tokens,
 		return;
 	}
 
-	status = thread_pipeline_disable(thread_id, obj, pipeline_name);
+	status = thread_pipeline_disable(thread_id, p);
 	if (status) {
 		snprintf(out, out_size, MSG_CMD_FAIL,
 			"thread pipeline disable");
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 967342c580..d1f519180e 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -16,7 +16,6 @@
 #include <rte_mempool.h>
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
-#include <rte_swx_pipeline.h>
 #include <rte_swx_ctl.h>
 
 #include "obj.h"
@@ -41,11 +40,6 @@ TAILQ_HEAD(ring_list, ring);
  */
 TAILQ_HEAD(tap_list, tap);
 
-/*
- * pipeline
- */
-TAILQ_HEAD(pipeline_list, pipeline);
-
 /*
  * obj
  */
@@ -53,7 +47,6 @@ struct obj {
 	struct mempool_list mempool_list;
 	struct link_list link_list;
 	struct ring_list ring_list;
-	struct pipeline_list pipeline_list;
 	struct tap_list tap_list;
 };
 
@@ -513,65 +506,6 @@ tap_create(struct obj *obj, const char *name)
 
 #endif
 
-/*
- * pipeline
- */
-#ifndef PIPELINE_MSGQ_SIZE
-#define PIPELINE_MSGQ_SIZE                                 64
-#endif
-
-struct pipeline *
-pipeline_create(struct obj *obj, const char *name, int numa_node)
-{
-	struct pipeline *pipeline;
-	struct rte_swx_pipeline *p = NULL;
-	int status;
-
-	/* Check input params */
-	if ((name == NULL) ||
-		pipeline_find(obj, name))
-		return NULL;
-
-	/* Resource create */
-	status = rte_swx_pipeline_config(&p, name, numa_node);
-	if (status)
-		goto error;
-
-	/* Node allocation */
-	pipeline = calloc(1, sizeof(struct pipeline));
-	if (pipeline == NULL)
-		goto error;
-
-	/* Node fill in */
-	strlcpy(pipeline->name, name, sizeof(pipeline->name));
-	pipeline->p = p;
-	pipeline->timer_period_ms = 10;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->pipeline_list, pipeline, node);
-
-	return pipeline;
-
-error:
-	rte_swx_pipeline_free(p);
-	return NULL;
-}
-
-struct pipeline *
-pipeline_find(struct obj *obj, const char *name)
-{
-	struct pipeline *pipeline;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(pipeline, &obj->pipeline_list, node)
-		if (strcmp(name, pipeline->name) == 0)
-			return pipeline;
-
-	return NULL;
-}
-
 /*
  * obj
  */
@@ -587,7 +521,6 @@ obj_init(void)
 	TAILQ_INIT(&obj->mempool_list);
 	TAILQ_INIT(&obj->link_list);
 	TAILQ_INIT(&obj->ring_list);
-	TAILQ_INIT(&obj->pipeline_list);
 	TAILQ_INIT(&obj->tap_list);
 
 	return obj;
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index b921610554..e63a9c0e9a 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -143,28 +143,4 @@ tap_next(struct obj *obj, struct tap *tap);
 struct tap *
 tap_create(struct obj *obj, const char *name);
 
-/*
- * pipeline
- */
-struct pipeline {
-	TAILQ_ENTRY(pipeline) node;
-	char name[NAME_SIZE];
-
-	struct rte_swx_pipeline *p;
-	struct rte_swx_ctl_pipeline *ctl;
-
-	uint32_t timer_period_ms;
-	int enabled;
-	uint32_t thread_id;
-	uint32_t cpu_id;
-};
-
-struct pipeline *
-pipeline_create(struct obj *obj,
-		const char *name,
-		int numa_node);
-
-struct pipeline *
-pipeline_find(struct obj *obj, const char *name);
-
 #endif /* _INCLUDE_OBJ_H_ */
diff --git a/examples/pipeline/thread.c b/examples/pipeline/thread.c
index 5fe7eae00e..6d15f51fb2 100644
--- a/examples/pipeline/thread.c
+++ b/examples/pipeline/thread.c
@@ -228,20 +228,33 @@ thread_msg_send_recv(uint32_t thread_id,
 	return rsp;
 }
 
+static int
+thread_is_pipeline_enabled(uint32_t thread_id, struct rte_swx_pipeline *p)
+{
+	struct thread *t = &thread[thread_id];
+	struct thread_data *td = &thread_data[thread_id];
+	uint32_t i;
+
+	if (!t->enabled)
+		return 0; /* Pipeline NOT enabled on this thread. */
+
+	for (i = 0; i < td->n_pipelines; i++)
+		if (td->p[i] == p)
+			return 1; /* Pipeline enabled on this thread. */
+
+	return 0 /* Pipeline NOT enabled on this thread. */;
+}
+
 int
-thread_pipeline_enable(uint32_t thread_id,
-	struct obj *obj,
-	const char *pipeline_name)
+thread_pipeline_enable(uint32_t thread_id, struct rte_swx_pipeline *p, uint32_t timer_period_ms)
 {
-	struct pipeline *p = pipeline_find(obj, pipeline_name);
 	struct thread *t;
 	struct thread_msg_req *req;
 	struct thread_msg_rsp *rsp;
 	int status;
 
 	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) ||
-		(p == NULL))
+	if ((thread_id >= RTE_MAX_LCORE) || !p || !timer_period_ms)
 		return -1;
 
 	t = &thread[thread_id];
@@ -256,19 +269,14 @@ thread_pipeline_enable(uint32_t thread_id,
 			return -1;
 
 		/* Data plane thread */
-		td->p[td->n_pipelines] = p->p;
+		td->p[td->n_pipelines] = p;
 
-		tdp->p = p->p;
-		tdp->timer_period =
-			(rte_get_tsc_hz() * p->timer_period_ms) / 1000;
+		tdp->p = p;
+		tdp->timer_period = (rte_get_tsc_hz() * timer_period_ms) / 1000;
 		tdp->time_next = rte_get_tsc_cycles() + tdp->timer_period;
 
 		td->n_pipelines++;
 
-		/* Pipeline */
-		p->thread_id = thread_id;
-		p->enabled = 1;
-
 		return 0;
 	}
 
@@ -279,8 +287,8 @@ thread_pipeline_enable(uint32_t thread_id,
 
 	/* Write request */
 	req->type = THREAD_REQ_PIPELINE_ENABLE;
-	req->pipeline_enable.p = p->p;
-	req->pipeline_enable.timer_period_ms = p->timer_period_ms;
+	req->pipeline_enable.p = p;
+	req->pipeline_enable.timer_period_ms = timer_period_ms;
 
 	/* Send request and wait for response */
 	rsp = thread_msg_send_recv(thread_id, req);
@@ -295,38 +303,28 @@ thread_pipeline_enable(uint32_t thread_id,
 	if (status)
 		return status;
 
-	p->thread_id = thread_id;
-	p->enabled = 1;
-
 	return 0;
 }
 
 int
-thread_pipeline_disable(uint32_t thread_id,
-	struct obj *obj,
-	const char *pipeline_name)
+thread_pipeline_disable(uint32_t thread_id, struct rte_swx_pipeline *p)
 {
-	struct pipeline *p = pipeline_find(obj, pipeline_name);
 	struct thread *t;
 	struct thread_msg_req *req;
 	struct thread_msg_rsp *rsp;
 	int status;
 
 	/* Check input params */
-	if ((thread_id >= RTE_MAX_LCORE) ||
-		(p == NULL))
+	if ((thread_id >= RTE_MAX_LCORE) || !p)
 		return -1;
 
 	t = &thread[thread_id];
 	if (t->enabled == 0)
 		return -1;
 
-	if (p->enabled == 0)
+	if (!thread_is_pipeline_enabled(thread_id, p))
 		return 0;
 
-	if (p->thread_id != thread_id)
-		return -1;
-
 	if (!thread_is_running(thread_id)) {
 		struct thread_data *td = &thread_data[thread_id];
 		uint32_t i;
@@ -334,7 +332,7 @@ thread_pipeline_disable(uint32_t thread_id,
 		for (i = 0; i < td->n_pipelines; i++) {
 			struct pipeline_data *tdp = &td->pipeline_data[i];
 
-			if (tdp->p != p->p)
+			if (tdp->p != p)
 				continue;
 
 			/* Data plane thread */
@@ -350,9 +348,6 @@ thread_pipeline_disable(uint32_t thread_id,
 
 			td->n_pipelines--;
 
-			/* Pipeline */
-			p->enabled = 0;
-
 			break;
 		}
 
@@ -366,7 +361,7 @@ thread_pipeline_disable(uint32_t thread_id,
 
 	/* Write request */
 	req->type = THREAD_REQ_PIPELINE_DISABLE;
-	req->pipeline_disable.p = p->p;
+	req->pipeline_disable.p = p;
 
 	/* Send request and wait for response */
 	rsp = thread_msg_send_recv(thread_id, req);
@@ -381,8 +376,6 @@ thread_pipeline_disable(uint32_t thread_id,
 	if (status)
 		return status;
 
-	p->enabled = 0;
-
 	return 0;
 }
 
diff --git a/examples/pipeline/thread.h b/examples/pipeline/thread.h
index d9d8645d4c..712cb25bbb 100644
--- a/examples/pipeline/thread.h
+++ b/examples/pipeline/thread.h
@@ -7,17 +7,16 @@
 
 #include <stdint.h>
 
-#include "obj.h"
+#include <rte_swx_pipeline.h>
 
 int
 thread_pipeline_enable(uint32_t thread_id,
-	struct obj *obj,
-	const char *pipeline_name);
+		       struct rte_swx_pipeline *p,
+		       uint32_t timer_period_ms);
 
 int
 thread_pipeline_disable(uint32_t thread_id,
-	struct obj *obj,
-	const char *pipeline_name);
+			struct rte_swx_pipeline *p);
 
 int
 thread_init(void);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 15/17] examples/pipeline: rework the link CLI command
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (12 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 14/17] examples/pipeline: use the pipeline name query API Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 16/17] examples/pipelines: remove obsolete tap " Cristian Dumitrescu
                         ` (2 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Rework the link CLI command for better alignment with the naming
conventions used in the pipeline I/O specification file. Use the
library linked list of devices and remove the application list.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 81 +++++++++++++----------------------------
 examples/pipeline/obj.c | 16 ++------
 examples/pipeline/obj.h |  4 --
 3 files changed, 29 insertions(+), 72 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index f48ff326be..d56a830fb7 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -106,22 +106,6 @@ parser_read_uint32(uint32_t *value, const char *p)
 	return 0;
 }
 
-static int
-parser_read_uint16(uint16_t *value, const char *p)
-{
-	uint64_t val = 0;
-	int ret = parser_read_uint64(&val, p);
-
-	if (ret < 0)
-		return ret;
-
-	if (val > UINT16_MAX)
-		return -ERANGE;
-
-	*value = val;
-	return 0;
-}
-
 #define PARSE_DELIMITER " \f\n\r\t\v"
 
 static int
@@ -230,16 +214,15 @@ cmd_mempool(char **tokens,
 	}
 }
 
-static const char cmd_link_help[] =
-"link <link_name>\n"
-"   dev <device_name> | port <port_id>\n"
+static const char cmd_ethdev_help[] =
+"ethdev <ethdev_name>\n"
 "   rxq <n_queues> <queue_size> <mempool_name>\n"
 "   txq <n_queues> <queue_size>\n"
 "   promiscuous on | off\n"
 "   [rss <qid_0> ... <qid_n>]\n";
 
 static void
-cmd_link(char **tokens,
+cmd_ethdev(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
@@ -252,65 +235,51 @@ cmd_link(char **tokens,
 
 	memset(&p, 0, sizeof(p));
 
-	if ((n_tokens < 13) || (n_tokens > 14 + LINK_RXQ_RSS_MAX)) {
+	if ((n_tokens < 11) || (n_tokens > 12 + LINK_RXQ_RSS_MAX)) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 	name = tokens[1];
 
-	if (strcmp(tokens[2], "dev") == 0)
-		p.dev_name = tokens[3];
-	else if (strcmp(tokens[2], "port") == 0) {
-		p.dev_name = NULL;
-
-		if (parser_read_uint16(&p.port_id, tokens[3]) != 0) {
-			snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
-			return;
-		}
-	} else {
-		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port");
-		return;
-	}
-
-	if (strcmp(tokens[4], "rxq") != 0) {
+	if (strcmp(tokens[2], "rxq") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
 		return;
 	}
 
-	if (parser_read_uint32(&p.rx.n_queues, tokens[5]) != 0) {
+	if (parser_read_uint32(&p.rx.n_queues, tokens[3]) != 0) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
 		return;
 	}
-	if (parser_read_uint32(&p.rx.queue_size, tokens[6]) != 0) {
+	if (parser_read_uint32(&p.rx.queue_size, tokens[4]) != 0) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
 		return;
 	}
 
-	p.rx.mempool_name = tokens[7];
+	p.rx.mempool_name = tokens[5];
 
-	if (strcmp(tokens[8], "txq") != 0) {
+	if (strcmp(tokens[6], "txq") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
 		return;
 	}
 
-	if (parser_read_uint32(&p.tx.n_queues, tokens[9]) != 0) {
+	if (parser_read_uint32(&p.tx.n_queues, tokens[7]) != 0) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
 		return;
 	}
 
-	if (parser_read_uint32(&p.tx.queue_size, tokens[10]) != 0) {
+	if (parser_read_uint32(&p.tx.queue_size, tokens[8]) != 0) {
 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
 		return;
 	}
 
-	if (strcmp(tokens[11], "promiscuous") != 0) {
+	if (strcmp(tokens[9], "promiscuous") != 0) {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous");
 		return;
 	}
 
-	if (strcmp(tokens[12], "on") == 0)
+	if (strcmp(tokens[10], "on") == 0)
 		p.promiscuous = 1;
-	else if (strcmp(tokens[12], "off") == 0)
+	else if (strcmp(tokens[10], "off") == 0)
 		p.promiscuous = 0;
 	else {
 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off");
@@ -319,10 +288,10 @@ cmd_link(char **tokens,
 
 	/* RSS */
 	p.rx.rss = NULL;
-	if (n_tokens > 13) {
+	if (n_tokens > 11) {
 		uint32_t queue_id, i;
 
-		if (strcmp(tokens[13], "rss") != 0) {
+		if (strcmp(tokens[11], "rss") != 0) {
 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
 			return;
 		}
@@ -330,7 +299,7 @@ cmd_link(char **tokens,
 		p.rx.rss = &rss;
 
 		rss.n_queues = 0;
-		for (i = 14; i < n_tokens; i++) {
+		for (i = 12; i < n_tokens; i++) {
 			if (parser_read_uint32(&queue_id, tokens[i]) != 0) {
 				snprintf(out, out_size, MSG_ARG_INVALID,
 					"queue_id");
@@ -406,10 +375,10 @@ print_link_info(struct link *link, char *out, size_t out_size)
 }
 
 /*
- * link show [<link_name>]
+ * ethdev show [<ethdev_name>]
  */
 static void
-cmd_link_show(char **tokens,
+cmd_ethdev_show(char **tokens,
 	      uint32_t n_tokens,
 	      char *out,
 	      size_t out_size,
@@ -2675,7 +2644,7 @@ cmd_help(char **tokens,
 			"Type 'help <command>' for command details.\n\n"
 			"List of commands:\n"
 			"\tmempool\n"
-			"\tlink\n"
+			"\tethdev\n"
 			"\ttap\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
@@ -2711,8 +2680,8 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if (strcmp(tokens[0], "link") == 0) {
-		snprintf(out, out_size, "\n%s\n", cmd_link_help);
+	if (strcmp(tokens[0], "ethdev") == 0) {
+		snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
 		return;
 	}
 
@@ -2966,13 +2935,13 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		return;
 	}
 
-	if (strcmp(tokens[0], "link") == 0) {
+	if (strcmp(tokens[0], "ethdev") == 0) {
 		if ((n_tokens >= 2) && (strcmp(tokens[1], "show") == 0)) {
-			cmd_link_show(tokens, n_tokens, out, out_size, obj);
+			cmd_ethdev_show(tokens, n_tokens, out, out_size, obj);
 			return;
 		}
 
-		cmd_link(tokens, n_tokens, out, out_size, obj);
+		cmd_ethdev(tokens, n_tokens, out, out_size, obj);
 		return;
 	}
 
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index d1f519180e..950ab831fb 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -181,7 +181,7 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	struct mempool *mempool;
 	uint32_t cpu_id, i;
 	int status;
-	uint16_t port_id;
+	uint16_t port_id = 0;
 
 	/* Check input params */
 	if ((name == NULL) ||
@@ -193,16 +193,9 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 		(params->tx.queue_size == 0))
 		return NULL;
 
-	port_id = params->port_id;
-	if (params->dev_name) {
-		status = rte_eth_dev_get_port_by_name(params->dev_name,
-			&port_id);
-
-		if (status)
-			return NULL;
-	} else
-		if (!rte_eth_dev_is_valid_port(port_id))
-			return NULL;
+	status = rte_eth_dev_get_port_by_name(name, &port_id);
+	if (status)
+		return NULL;
 
 	if (rte_eth_dev_info_get(port_id, &port_info) != 0)
 		return NULL;
@@ -315,7 +308,6 @@ link_create(struct obj *obj, const char *name, struct link_params *params)
 	/* Node fill in */
 	strlcpy(link->name, name, sizeof(link->name));
 	link->port_id = port_id;
-	rte_eth_dev_get_name_by_port(port_id, link->dev_name);
 	link->n_rxq = params->rx.n_queues;
 	link->n_txq = params->tx.n_queues;
 
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index e63a9c0e9a..af270a8e57 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -63,9 +63,6 @@ struct link_params_rss {
 };
 
 struct link_params {
-	const char *dev_name;
-	uint16_t port_id; /**< Valid only when *dev_name* is NULL. */
-
 	struct {
 		uint32_t n_queues;
 		uint32_t queue_size;
@@ -84,7 +81,6 @@ struct link_params {
 struct link {
 	TAILQ_ENTRY(link) node;
 	char name[NAME_SIZE];
-	char dev_name[NAME_SIZE];
 	uint16_t port_id;
 	uint32_t n_rxq;
 	uint32_t n_txq;
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 16/17] examples/pipelines: remove obsolete tap CLI command
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (13 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 15/17] examples/pipeline: rework the link CLI command Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 22:54       ` [PATCH V4 17/17] examples/pipeline: call the code generation and build CLI commands Cristian Dumitrescu
  2022-07-27 23:01       ` [PATCH V5 01/17] pipeline: add pipeline name Cristian Dumitrescu
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Remove the tap CLI command, as the file descriptor I/O ports of the
pipeline are now configured trough the I/O specification file.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 37 -----------------
 examples/pipeline/obj.c | 89 -----------------------------------------
 examples/pipeline/obj.h | 18 ---------
 3 files changed, 144 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index d56a830fb7..75c32b9089 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -466,32 +466,6 @@ cmd_ring(char **tokens,
 	}
 }
 
-static const char cmd_tap_help[] =
-"tap <tap_name>\n";
-
-static void
-cmd_tap(char **tokens,
-	uint32_t n_tokens,
-	char *out,
-	size_t out_size,
-	void *obj)
-{
-	struct tap *tap;
-	char *name;
-
-	if (n_tokens < 2) {
-		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-		return;
-	}
-	name = tokens[1];
-
-	tap = tap_create(obj, name);
-	if (tap == NULL) {
-		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
-		return;
-	}
-}
-
 static const char cmd_pipeline_codegen_help[] =
 "pipeline codegen <spec_file> <code_file>\n";
 
@@ -2645,7 +2619,6 @@ cmd_help(char **tokens,
 			"List of commands:\n"
 			"\tmempool\n"
 			"\tethdev\n"
-			"\ttap\n"
 			"\tpipeline codegen\n"
 			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
@@ -2690,11 +2663,6 @@ cmd_help(char **tokens,
 		return;
 	}
 
-	if (strcmp(tokens[0], "tap") == 0) {
-		snprintf(out, out_size, "\n%s\n", cmd_tap_help);
-		return;
-	}
-
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
@@ -2950,11 +2918,6 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 		return;
 	}
 
-	if (strcmp(tokens[0], "tap") == 0) {
-		cmd_tap(tokens, n_tokens, out, out_size, obj);
-		return;
-	}
-
 	if (strcmp(tokens[0], "pipeline") == 0) {
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[1], "codegen") == 0)) {
diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index 950ab831fb..b7e2316eec 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -35,11 +35,6 @@ TAILQ_HEAD(link_list, link);
  */
 TAILQ_HEAD(ring_list, ring);
 
-/*
- * tap
- */
-TAILQ_HEAD(tap_list, tap);
-
 /*
  * obj
  */
@@ -47,7 +42,6 @@ struct obj {
 	struct mempool_list mempool_list;
 	struct link_list link_list;
 	struct ring_list ring_list;
-	struct tap_list tap_list;
 };
 
 /*
@@ -416,88 +410,6 @@ ring_find(struct obj *obj, const char *name)
 	return NULL;
 }
 
-/*
- * tap
- */
-#define TAP_DEV		"/dev/net/tun"
-
-struct tap *
-tap_find(struct obj *obj, const char *name)
-{
-	struct tap *tap;
-
-	if (!obj || !name)
-		return NULL;
-
-	TAILQ_FOREACH(tap, &obj->tap_list, node)
-		if (strcmp(tap->name, name) == 0)
-			return tap;
-
-	return NULL;
-}
-
-struct tap *
-tap_next(struct obj *obj, struct tap *tap)
-{
-	return (tap == NULL) ?
-		TAILQ_FIRST(&obj->tap_list) : TAILQ_NEXT(tap, node);
-}
-
-#ifndef RTE_EXEC_ENV_LINUX
-
-struct tap *
-tap_create(struct obj *obj __rte_unused, const char *name __rte_unused)
-{
-	return NULL;
-}
-
-#else
-
-struct tap *
-tap_create(struct obj *obj, const char *name)
-{
-	struct tap *tap;
-	struct ifreq ifr;
-	int fd, status;
-
-	/* Check input params */
-	if ((name == NULL) ||
-		tap_find(obj, name))
-		return NULL;
-
-	/* Resource create */
-	fd = open(TAP_DEV, O_RDWR | O_NONBLOCK);
-	if (fd < 0)
-		return NULL;
-
-	memset(&ifr, 0, sizeof(ifr));
-	ifr.ifr_flags = IFF_TAP | IFF_NO_PI; /* No packet information */
-	strlcpy(ifr.ifr_name, name, IFNAMSIZ);
-
-	status = ioctl(fd, TUNSETIFF, (void *) &ifr);
-	if (status < 0) {
-		close(fd);
-		return NULL;
-	}
-
-	/* Node allocation */
-	tap = calloc(1, sizeof(struct tap));
-	if (tap == NULL) {
-		close(fd);
-		return NULL;
-	}
-	/* Node fill in */
-	strlcpy(tap->name, name, sizeof(tap->name));
-	tap->fd = fd;
-
-	/* Node add to list */
-	TAILQ_INSERT_TAIL(&obj->tap_list, tap, node);
-
-	return tap;
-}
-
-#endif
-
 /*
  * obj
  */
@@ -513,7 +425,6 @@ obj_init(void)
 	TAILQ_INIT(&obj->mempool_list);
 	TAILQ_INIT(&obj->link_list);
 	TAILQ_INIT(&obj->ring_list);
-	TAILQ_INIT(&obj->tap_list);
 
 	return obj;
 }
diff --git a/examples/pipeline/obj.h b/examples/pipeline/obj.h
index af270a8e57..8ea1c414c2 100644
--- a/examples/pipeline/obj.h
+++ b/examples/pipeline/obj.h
@@ -121,22 +121,4 @@ ring_create(struct obj *obj,
 struct ring *
 ring_find(struct obj *obj, const char *name);
 
-/*
- * tap
- */
-struct tap {
-	TAILQ_ENTRY(tap) node;
-	char name[NAME_SIZE];
-	int fd;
-};
-
-struct tap *
-tap_find(struct obj *obj, const char *name);
-
-struct tap *
-tap_next(struct obj *obj, struct tap *tap);
-
-struct tap *
-tap_create(struct obj *obj, const char *name);
-
 #endif /* _INCLUDE_OBJ_H_ */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V4 17/17] examples/pipeline: call the code generation and build CLI commands
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (14 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 16/17] examples/pipelines: remove obsolete tap " Cristian Dumitrescu
@ 2022-07-27 22:54       ` Cristian Dumitrescu
  2022-07-27 23:01       ` [PATCH V5 01/17] pipeline: add pipeline name Cristian Dumitrescu
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 22:54 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Update the example CLI scripts with the commands for code generation
and shared object library build.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/examples/ethdev.io          | 27 +++++++++
 examples/pipeline/examples/fib.cli            | 44 +++++++-------
 examples/pipeline/examples/hash_func.cli      | 41 ++++++-------
 examples/pipeline/examples/l2fwd.cli          | 44 ++++++++------
 examples/pipeline/examples/l2fwd_macswp.cli   | 44 ++++++++------
 .../pipeline/examples/l2fwd_macswp_pcap.cli   | 35 +++++++----
 examples/pipeline/examples/l2fwd_pcap.cli     | 35 +++++++----
 examples/pipeline/examples/learner.cli        | 43 +++++++-------
 examples/pipeline/examples/meter.cli          | 58 +++++++++++--------
 examples/pipeline/examples/mirroring.cli      | 46 ++++++++-------
 examples/pipeline/examples/pcap.io            | 27 +++++++++
 examples/pipeline/examples/recirculation.cli  | 41 ++++++-------
 examples/pipeline/examples/registers.cli      | 53 +++++++++--------
 examples/pipeline/examples/selector.cli       | 55 +++++++++++-------
 examples/pipeline/examples/varbit.cli         | 41 ++++++-------
 examples/pipeline/examples/vxlan.cli          | 48 ++++++++++-----
 examples/pipeline/examples/vxlan_pcap.cli     | 39 +++++++++----
 17 files changed, 444 insertions(+), 277 deletions(-)
 create mode 100644 examples/pipeline/examples/ethdev.io
 create mode 100644 examples/pipeline/examples/pcap.io

diff --git a/examples/pipeline/examples/ethdev.io b/examples/pipeline/examples/ethdev.io
new file mode 100644
index 0000000000..cf2f3e20bd
--- /dev/null
+++ b/examples/pipeline/examples/ethdev.io
@@ -0,0 +1,27 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+;
+; Pipeline packet mirroring.
+;
+mirroring slots 4 sessions 64
+
+;
+; Pipeline input ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port in 0 ethdev 0000:18:00.0 rxq 0 bsz 32
+port in 1 ethdev 0000:18:00.1 rxq 0 bsz 32
+port in 2 ethdev 0000:3b:00.0 rxq 0 bsz 32
+port in 3 ethdev 0000:3b:00.1 rxq 0 bsz 32
+
+;
+; Pipeline output ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port out 0 ethdev 0000:18:00.0 txq 0 bsz 32
+port out 1 ethdev 0000:18:00.1 txq 0 bsz 32
+port out 2 ethdev 0000:3b:00.0 txq 0 bsz 32
+port out 3 ethdev 0000:3b:00.1 txq 0 bsz 32
diff --git a/examples/pipeline/examples/fib.cli b/examples/pipeline/examples/fib.cli
index 93ab2b08f8..4e30c1320f 100644
--- a/examples/pipeline/examples/fib.cli
+++ b/examples/pipeline/examples/fib.cli
@@ -1,38 +1,38 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/fib.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
 ;
-; Customize the LINK parameters to match your setup.
+; Pipeline code generation & shared object library build.
 ;
-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
+pipeline codegen ./examples/pipeline/examples/fib.spec /tmp/fib.c
+pipeline libbuild /tmp/fib.c /tmp/fib.so
 
 ;
-; PIPELINE0 setup.
+; List of DPDK devices.
 ;
-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
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/fib.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/fib.so io ./examples/pipeline/examples/ethdev.io numa 0
 
 ;
 ; Initial set of table entries.
 ;
-; The table entries can later be updated at run-time through the CLI commands. Once the application
-; has been successfully started, the command to get the CLI prompt is: telnet 0.0.0.0 8086.
+; The table entries can later be updated at run-time through the CLI commands.
 ;
 pipeline PIPELINE0 table routing_table add ./examples/pipeline/examples/fib_routing_table.txt
 pipeline PIPELINE0 selector nexthop_group_table group add
diff --git a/examples/pipeline/examples/hash_func.cli b/examples/pipeline/examples/hash_func.cli
index d65cd62d17..b2e219e4c9 100644
--- a/examples/pipeline/examples/hash_func.cli
+++ b/examples/pipeline/examples/hash_func.cli
@@ -1,32 +1,33 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2022 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/hash_func.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
 ;
-; Customize the LINK parameters to match your setup.
+; Pipeline code generation & shared object library build.
 ;
-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
+pipeline codegen ./examples/pipeline/examples/hash_func.spec /tmp/hash_func.c
+pipeline libbuild /tmp/hash_func.c /tmp/hash_func.so
 
 ;
-; PIPELINE0 setup.
+; List of DPDK devices.
 ;
-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
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/hash_func.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/hash_func.so io ./examples/pipeline/examples/ethdev.io numa 0
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/l2fwd.cli b/examples/pipeline/examples/l2fwd.cli
index d89caf2d0a..27e37021b9 100644
--- a/examples/pipeline/examples/l2fwd.cli
+++ b/examples/pipeline/examples/l2fwd.cli
@@ -1,25 +1,35 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-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
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/l2fwd.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
 
-pipeline PIPELINE0 create 0
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd.spec /tmp/l2fwd.c
+pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 
-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
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/l2fwd.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/ethdev.io numa 0
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/l2fwd_macswp.cli b/examples/pipeline/examples/l2fwd_macswp.cli
index 0f2a89ac5b..11bb4543b9 100644
--- a/examples/pipeline/examples/l2fwd_macswp.cli
+++ b/examples/pipeline/examples/l2fwd_macswp.cli
@@ -1,25 +1,35 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-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
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/l2fwd_macswp.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
 
-pipeline PIPELINE0 create 0
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd_macswp.spec /tmp/l2fwd_macswp.c
+pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 
-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
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/l2fwd_macswp.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/examples/ethdev.io numa 0
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/l2fwd_macswp_pcap.cli b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
index e9656fe3c2..8724dae3b0 100644
--- a/examples/pipeline/examples/l2fwd_macswp_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_macswp_pcap.cli
@@ -1,20 +1,31 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
-
-pipeline PIPELINE0 create 0
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/l2fwd_macswp_pcap.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
 
-pipeline PIPELINE0 port in 0 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 1 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 2 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 3 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd_macswp.spec /tmp/l2fwd_macswp.c
+pipeline libbuild /tmp/l2fwd_macswp.c /tmp/l2fwd_macswp.so
 
-pipeline PIPELINE0 port out 0 sink none
-pipeline PIPELINE0 port out 1 sink none
-pipeline PIPELINE0 port out 2 sink none
-pipeline PIPELINE0 port out 3 sink none
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/l2fwd_macswp.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/l2fwd_macswp.so io ./examples/pipeline/examples/pcap.io numa 0
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/l2fwd_pcap.cli b/examples/pipeline/examples/l2fwd_pcap.cli
index 23fcb199f1..4db0a0dc56 100644
--- a/examples/pipeline/examples/l2fwd_pcap.cli
+++ b/examples/pipeline/examples/l2fwd_pcap.cli
@@ -1,20 +1,31 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
-
-pipeline PIPELINE0 create 0
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/l2fwd_pcap.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
 
-pipeline PIPELINE0 port in 0 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 1 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 2 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 3 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/l2fwd.spec /tmp/l2fwd.c
+pipeline libbuild /tmp/l2fwd.c /tmp/l2fwd.so
 
-pipeline PIPELINE0 port out 0 sink none
-pipeline PIPELINE0 port out 1 sink none
-pipeline PIPELINE0 port out 2 sink none
-pipeline PIPELINE0 port out 3 sink none
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/l2fwd.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/l2fwd.so io ./examples/pipeline/examples/pcap.io numa 0
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/learner.cli b/examples/pipeline/examples/learner.cli
index 688ce34f34..6c8aa3921e 100644
--- a/examples/pipeline/examples/learner.cli
+++ b/examples/pipeline/examples/learner.cli
@@ -1,36 +1,35 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/learner.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
 ;
-; Customize the LINK parameters to match your setup.
+; Pipeline code generation & shared object library build.
 ;
-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
+pipeline codegen ./examples/pipeline/examples/learner.spec /tmp/learner.c
+pipeline libbuild /tmp/learner.c /tmp/learner.so
 
 ;
-; PIPELINE0 setup.
+; List of DPDK devices.
 ;
-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
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/learner.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/learner.so io ./examples/pipeline/examples/ethdev.io numa 0
 
 ;
 ; Pipelines-to-threads mapping.
 ;
 thread 1 pipeline PIPELINE0 enable
-
-; Once the application has started, the command to get the CLI prompt is: telnet 0.0.0.0 8086
diff --git a/examples/pipeline/examples/meter.cli b/examples/pipeline/examples/meter.cli
index b29ed24022..c1b88c882a 100644
--- a/examples/pipeline/examples/meter.cli
+++ b/examples/pipeline/examples/meter.cli
@@ -1,31 +1,43 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-; Example command line:
-;	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/meter.cli
-
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/meter.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/meter.spec /tmp/meter.c
+pipeline libbuild /tmp/meter.c /tmp/meter.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below 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
-
-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/meter.spec
-
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/meter.so io ./examples/pipeline/examples/ethdev.io numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
 pipeline PIPELINE0 meter profile platinum add cir 46000000 pir 138000000 cbs 1000000 pbs 1000000
 pipeline PIPELINE0 meter meters from 0 to 15 set profile platinum
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/mirroring.cli b/examples/pipeline/examples/mirroring.cli
index 46d57db4ec..1d439e04d3 100644
--- a/examples/pipeline/examples/mirroring.cli
+++ b/examples/pipeline/examples/mirroring.cli
@@ -1,36 +1,38 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2022 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/mirroring.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
 ;
-; Customize the LINK parameters to match your setup.
+; Pipeline code generation & shared object library build.
 ;
-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
+pipeline codegen ./examples/pipeline/examples/mirroring.spec /tmp/mirroring.c
+pipeline libbuild /tmp/mirroring.c /tmp/mirroring.so
 
 ;
-; PIPELINE0 setup.
+; List of DPDK devices.
 ;
-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
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/mirroring.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/mirroring.so io ./examples/pipeline/examples/ethdev.io numa 0
 
 ;
-; Packet mirroring sessions.
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
 ;
 pipeline PIPELINE0 mirror session 0 port 1 clone fast truncate 0
 pipeline PIPELINE0 mirror session 1 port 2 clone slow truncate 0
diff --git a/examples/pipeline/examples/pcap.io b/examples/pipeline/examples/pcap.io
new file mode 100644
index 0000000000..111f61afae
--- /dev/null
+++ b/examples/pipeline/examples/pcap.io
@@ -0,0 +1,27 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2022 Intel Corporation
+
+;
+; Pipeline packet mirroring.
+;
+mirroring slots 4 sessions 64
+
+;
+; Pipeline input ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port in 0 source mempool MEMPOOL0 file ./examples/pipeline/examples/packet.pcap loop 1 packets 0
+port in 1 source mempool MEMPOOL0 file ./examples/pipeline/examples/packet.pcap loop 1 packets 0
+port in 2 source mempool MEMPOOL0 file ./examples/pipeline/examples/packet.pcap loop 1 packets 0
+port in 3 source mempool MEMPOOL0 file ./examples/pipeline/examples/packet.pcap loop 1 packets 0
+
+;
+; Pipeline output ports.
+;
+; Note: Customize the parameters below to match your setup.
+;
+port out 0 sink file none
+port out 1 sink file none
+port out 2 sink file none
+port out 3 sink file none
diff --git a/examples/pipeline/examples/recirculation.cli b/examples/pipeline/examples/recirculation.cli
index f855c5c327..52d0894f12 100644
--- a/examples/pipeline/examples/recirculation.cli
+++ b/examples/pipeline/examples/recirculation.cli
@@ -1,32 +1,33 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2022 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/recirculation.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
 ;
-; Customize the LINK parameters to match your setup.
+; Pipeline code generation & shared object library build.
 ;
-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
+pipeline codegen ./examples/pipeline/examples/recirculation.spec /tmp/recirculation.c
+pipeline libbuild /tmp/recirculation.c /tmp/recirculation.so
 
 ;
-; PIPELINE0 setup.
+; List of DPDK devices.
 ;
-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
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/recirculation.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/recirculation.so io ./examples/pipeline/examples/ethdev.io numa 0
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/registers.cli b/examples/pipeline/examples/registers.cli
index 8d026294cb..3516f76a5b 100644
--- a/examples/pipeline/examples/registers.cli
+++ b/examples/pipeline/examples/registers.cli
@@ -1,28 +1,35 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-; Example command line:
-;	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/registers.cli
-
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/registers.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/registers.spec /tmp/registers.c
+pipeline libbuild /tmp/registers.c /tmp/registers.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below 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
-
-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/registers.spec
-
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/registers.so io ./examples/pipeline/examples/ethdev.io numa 0
+
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/selector.cli b/examples/pipeline/examples/selector.cli
index 123782c57b..f0e251b657 100644
--- a/examples/pipeline/examples/selector.cli
+++ b/examples/pipeline/examples/selector.cli
@@ -1,30 +1,45 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/selector.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/selector.spec /tmp/selector.c
+pipeline libbuild /tmp/selector.c /tmp/selector.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below 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
-
-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/selector.spec
-
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/selector.so io ./examples/pipeline/examples/ethdev.io numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
 pipeline PIPELINE0 selector s group add
 pipeline PIPELINE0 selector s group member add ./examples/pipeline/examples/selector.txt
 pipeline PIPELINE0 commit
 pipeline PIPELINE0 selector s show
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/varbit.cli b/examples/pipeline/examples/varbit.cli
index 9caeb9ca26..0f89990471 100644
--- a/examples/pipeline/examples/varbit.cli
+++ b/examples/pipeline/examples/varbit.cli
@@ -1,32 +1,33 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/varbit.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
+
 ;
-; Customize the LINK parameters to match your setup.
+; Pipeline code generation & shared object library build.
 ;
-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
+pipeline codegen ./examples/pipeline/examples/varbit.spec /tmp/varbit.c
+pipeline libbuild /tmp/varbit.c /tmp/varbit.so
 
 ;
-; PIPELINE0 setup.
+; List of DPDK devices.
 ;
-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
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/varbit.spec
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/varbit.so io ./examples/pipeline/examples/ethdev.io numa 0
 
 ;
 ; Pipelines-to-threads mapping.
diff --git a/examples/pipeline/examples/vxlan.cli b/examples/pipeline/examples/vxlan.cli
index 444f3f7bd8..1fbd1be6e4 100644
--- a/examples/pipeline/examples/vxlan.cli
+++ b/examples/pipeline/examples/vxlan.cli
@@ -1,27 +1,43 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-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
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/vxlan.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
 
-pipeline PIPELINE0 create 0
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/vxlan.spec /tmp/vxlan.c
+pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 
-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
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
 
-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
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/vxlan.so io ./examples/pipeline/examples/ethdev.io numa 0
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/vxlan.spec
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
 pipeline PIPELINE0 table vxlan_table add ./examples/pipeline/examples/vxlan_table.txt
 pipeline PIPELINE0 commit
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
diff --git a/examples/pipeline/examples/vxlan_pcap.cli b/examples/pipeline/examples/vxlan_pcap.cli
index 83fca8d0d9..adc7f73312 100644
--- a/examples/pipeline/examples/vxlan_pcap.cli
+++ b/examples/pipeline/examples/vxlan_pcap.cli
@@ -1,22 +1,39 @@
 ; SPDX-License-Identifier: BSD-3-Clause
 ; Copyright(c) 2020 Intel Corporation
 
-mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
+# Example command line:
+#	./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/vxlan_pcap.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+#	telnet 0.0.0.0 8086
 
-pipeline PIPELINE0 create 0
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/vxlan.spec /tmp/vxlan.c
+pipeline libbuild /tmp/vxlan.c /tmp/vxlan.so
 
-pipeline PIPELINE0 port in 0 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 1 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 2 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
-pipeline PIPELINE0 port in 3 source MEMPOOL0 ./examples/pipeline/examples/packet.pcap loop 1
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 buffer 2304 pool 32K cache 256 cpu 0
 
-pipeline PIPELINE0 port out 0 sink none
-pipeline PIPELINE0 port out 1 sink none
-pipeline PIPELINE0 port out 2 sink none
-pipeline PIPELINE0 port out 3 sink none
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/vxlan.so io ./examples/pipeline/examples/pcap.io numa 0
 
-pipeline PIPELINE0 build ./examples/pipeline/examples/vxlan.spec
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
 pipeline PIPELINE0 table vxlan_table add ./examples/pipeline/examples/vxlan_table.txt
 pipeline PIPELINE0 commit
 
+;
+; Pipelines-to-threads mapping.
+;
 thread 1 pipeline PIPELINE0 enable
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V5 01/17] pipeline: add pipeline name
  2022-07-27 22:54     ` [PATCH V4 01/17] pipeline: add pipeline name Cristian Dumitrescu
                         ` (15 preceding siblings ...)
  2022-07-27 22:54       ` [PATCH V4 17/17] examples/pipeline: call the code generation and build CLI commands Cristian Dumitrescu
@ 2022-07-27 23:01       ` Cristian Dumitrescu
  2022-07-27 23:01         ` [PATCH V5 02/17] pipeline: move specification data structures to internal header Cristian Dumitrescu
                           ` (16 more replies)
  16 siblings, 17 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 23:01 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add an unique name to every pipeline. This enables the library to
maintain a list of the existing pipeline objects, which can be
queried by the application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/obj.c                  |   2 +-
 lib/pipeline/rte_swx_ctl.c               |  99 +++++++++++++++++++++
 lib/pipeline/rte_swx_ctl.h               |  15 ++++
 lib/pipeline/rte_swx_pipeline.c          | 107 ++++++++++++++++++++++-
 lib/pipeline/rte_swx_pipeline.h          |  18 +++-
 lib/pipeline/rte_swx_pipeline_internal.h |   2 +
 lib/pipeline/version.map                 |   4 +
 7 files changed, 244 insertions(+), 3 deletions(-)

diff --git a/examples/pipeline/obj.c b/examples/pipeline/obj.c
index b79f044ac7..967342c580 100644
--- a/examples/pipeline/obj.c
+++ b/examples/pipeline/obj.c
@@ -533,7 +533,7 @@ pipeline_create(struct obj *obj, const char *name, int numa_node)
 		return NULL;
 
 	/* Resource create */
-	status = rte_swx_pipeline_config(&p, numa_node);
+	status = rte_swx_pipeline_config(&p, name, numa_node);
 	if (status)
 		goto error;
 
diff --git a/lib/pipeline/rte_swx_ctl.c b/lib/pipeline/rte_swx_ctl.c
index 710e89a46a..1b776fc543 100644
--- a/lib/pipeline/rte_swx_ctl.c
+++ b/lib/pipeline/rte_swx_ctl.c
@@ -9,6 +9,8 @@
 
 #include <rte_common.h>
 #include <rte_byteorder.h>
+#include <rte_tailq.h>
+#include <rte_eal_memconfig.h>
 
 #include <rte_swx_table_selector.h>
 
@@ -1157,12 +1159,103 @@ table_state_create(struct rte_swx_ctl_pipeline *ctl)
 	return status;
 }
 
+/* Global list of pipeline instances. */
+TAILQ_HEAD(rte_swx_ctl_pipeline_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_swx_ctl_pipeline_tailq = {
+	.name = "RTE_SWX_CTL_PIPELINE",
+};
+
+EAL_REGISTER_TAILQ(rte_swx_ctl_pipeline_tailq)
+
+struct rte_swx_ctl_pipeline *
+rte_swx_ctl_pipeline_find(const char *name)
+{
+	struct rte_swx_ctl_pipeline_list *ctl_list;
+	struct rte_tailq_entry *te = NULL;
+
+	if (!name || !name[0] || (strnlen(name, RTE_SWX_CTL_NAME_SIZE) >= RTE_SWX_CTL_NAME_SIZE))
+		return NULL;
+
+	ctl_list = RTE_TAILQ_CAST(rte_swx_ctl_pipeline_tailq.head, rte_swx_ctl_pipeline_list);
+
+	rte_mcfg_tailq_read_lock();
+
+	TAILQ_FOREACH(te, ctl_list, next) {
+		struct rte_swx_ctl_pipeline *ctl = (struct rte_swx_ctl_pipeline *)te->data;
+
+		if (!strncmp(name, ctl->info.name, sizeof(ctl->info.name))) {
+			rte_mcfg_tailq_read_unlock();
+			return ctl;
+		}
+	}
+
+	rte_mcfg_tailq_read_unlock();
+	return NULL;
+}
+
+static int
+ctl_register(struct rte_swx_ctl_pipeline *ctl)
+{
+	struct rte_swx_ctl_pipeline_list *ctl_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ctl_list = RTE_TAILQ_CAST(rte_swx_ctl_pipeline_tailq.head, rte_swx_ctl_pipeline_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ctl_list, next) {
+		struct rte_swx_ctl_pipeline *ctl_crt = (struct rte_swx_ctl_pipeline *)te->data;
+
+		if (!strncmp(ctl->info.name, ctl_crt->info.name, sizeof(ctl->info.name))) {
+			rte_mcfg_tailq_write_unlock();
+			return -EEXIST;
+		}
+	}
+
+	te = calloc(1, sizeof(struct rte_tailq_entry));
+	if (!te) {
+		rte_mcfg_tailq_write_unlock();
+		return -ENOMEM;
+	}
+
+	te->data = (void *)ctl;
+	TAILQ_INSERT_TAIL(ctl_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return 0;
+}
+
+static void
+ctl_unregister(struct rte_swx_ctl_pipeline *ctl)
+{
+	struct rte_swx_ctl_pipeline_list *ctl_list;
+	struct rte_tailq_entry *te = NULL;
+
+	ctl_list = RTE_TAILQ_CAST(rte_swx_ctl_pipeline_tailq.head, rte_swx_ctl_pipeline_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, ctl_list, next) {
+		if (te->data == (void *)ctl) {
+			TAILQ_REMOVE(ctl_list, te, next);
+			rte_mcfg_tailq_write_unlock();
+			free(te);
+			return;
+		}
+	}
+
+	rte_mcfg_tailq_write_unlock();
+}
+
 void
 rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline *ctl)
 {
 	if (!ctl)
 		return;
 
+	if (ctl->info.name[0])
+		ctl_unregister(ctl);
+
 	action_free(ctl);
 
 	table_state_free(ctl);
@@ -1441,6 +1534,12 @@ rte_swx_ctl_pipeline_create(struct rte_swx_pipeline *p)
 	if (status)
 		goto error;
 
+	if (ctl->info.name[0]) {
+		status = ctl_register(ctl);
+		if (status)
+			goto error;
+	}
+
 	return ctl;
 
 error:
diff --git a/lib/pipeline/rte_swx_ctl.h b/lib/pipeline/rte_swx_ctl.h
index d771389d26..63ee479e47 100644
--- a/lib/pipeline/rte_swx_ctl.h
+++ b/lib/pipeline/rte_swx_ctl.h
@@ -35,6 +35,9 @@ struct rte_swx_pipeline;
 
 /** Pipeline info. */
 struct rte_swx_ctl_pipeline_info {
+	/** Pipeline name. */
+	char name[RTE_SWX_CTL_NAME_SIZE];
+
 	/** Number of input ports. */
 	uint32_t n_ports_in;
 
@@ -812,6 +815,18 @@ rte_swx_pipeline_table_state_set(struct rte_swx_pipeline *p,
 /** Pipeline control opaque data structure. */
 struct rte_swx_ctl_pipeline;
 
+/**
+ * Pipeline control find
+ *
+ * @param[in] name
+ *   Pipeline name.
+ * @return
+ *   Valid pipeline control handle if found or NULL otherwise.
+ */
+__rte_experimental
+struct rte_swx_ctl_pipeline *
+rte_swx_ctl_pipeline_find(const char *name);
+
 /**
  * Pipeline control create
  *
diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 3e1c6e9edb..c8ccded4f8 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -6,6 +6,8 @@
 #include <errno.h>
 #include <dlfcn.h>
 
+#include <rte_tailq.h>
+#include <rte_eal_memconfig.h>
 #include <rte_jhash.h>
 #include <rte_hash_crc.h>
 
@@ -9578,6 +9580,95 @@ metarray_free(struct rte_swx_pipeline *p)
 /*
  * Pipeline.
  */
+
+/* Global list of pipeline instances. */
+TAILQ_HEAD(rte_swx_pipeline_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_swx_pipeline_tailq = {
+	.name = "RTE_SWX_PIPELINE",
+};
+
+EAL_REGISTER_TAILQ(rte_swx_pipeline_tailq)
+
+struct rte_swx_pipeline *
+rte_swx_pipeline_find(const char *name)
+{
+	struct rte_swx_pipeline_list *pipeline_list;
+	struct rte_tailq_entry *te = NULL;
+
+	if (!name || !name[0] || (strnlen(name, RTE_SWX_NAME_SIZE) >= RTE_SWX_NAME_SIZE))
+		return NULL;
+
+	pipeline_list = RTE_TAILQ_CAST(rte_swx_pipeline_tailq.head, rte_swx_pipeline_list);
+
+	rte_mcfg_tailq_read_lock();
+
+	TAILQ_FOREACH(te, pipeline_list, next) {
+		struct rte_swx_pipeline *p = (struct rte_swx_pipeline *)te->data;
+
+		if (!strncmp(name, p->name, sizeof(p->name))) {
+			rte_mcfg_tailq_read_unlock();
+			return p;
+		}
+	}
+
+	rte_mcfg_tailq_read_unlock();
+	return NULL;
+}
+
+static int
+pipeline_register(struct rte_swx_pipeline *p)
+{
+	struct rte_swx_pipeline_list *pipeline_list;
+	struct rte_tailq_entry *te = NULL;
+
+	pipeline_list = RTE_TAILQ_CAST(rte_swx_pipeline_tailq.head, rte_swx_pipeline_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, pipeline_list, next) {
+		struct rte_swx_pipeline *pipeline = (struct rte_swx_pipeline *)te->data;
+
+		if (!strncmp(p->name, pipeline->name, sizeof(p->name))) {
+			rte_mcfg_tailq_write_unlock();
+			return -EEXIST;
+		}
+	}
+
+	te = calloc(1, sizeof(struct rte_tailq_entry));
+	if (!te) {
+		rte_mcfg_tailq_write_unlock();
+		return -ENOMEM;
+	}
+
+	te->data = (void *)p;
+	TAILQ_INSERT_TAIL(pipeline_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return 0;
+}
+
+static void
+pipeline_unregister(struct rte_swx_pipeline *p)
+{
+	struct rte_swx_pipeline_list *pipeline_list;
+	struct rte_tailq_entry *te = NULL;
+
+	pipeline_list = RTE_TAILQ_CAST(rte_swx_pipeline_tailq.head, rte_swx_pipeline_list);
+
+	rte_mcfg_tailq_write_lock();
+
+	TAILQ_FOREACH(te, pipeline_list, next) {
+		if (te->data == (void *)p) {
+			TAILQ_REMOVE(pipeline_list, te, next);
+			rte_mcfg_tailq_write_unlock();
+			free(te);
+			return;
+		}
+	}
+
+	rte_mcfg_tailq_write_unlock();
+}
+
 void
 rte_swx_pipeline_free(struct rte_swx_pipeline *p)
 {
@@ -9586,6 +9677,9 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)
 	if (!p)
 		return;
 
+	if (p->name[0])
+		pipeline_unregister(p);
+
 	lib = p->lib;
 
 	free(p->instruction_data);
@@ -9720,13 +9814,14 @@ hash_funcs_register(struct rte_swx_pipeline *p)
 }
 
 int
-rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
+rte_swx_pipeline_config(struct rte_swx_pipeline **p, const char *name, int numa_node)
 {
 	struct rte_swx_pipeline *pipeline = NULL;
 	int status = 0;
 
 	/* Check input parameters. */
 	CHECK(p, EINVAL);
+	CHECK(!name || (strnlen(name, RTE_SWX_NAME_SIZE) < RTE_SWX_NAME_SIZE), EINVAL);
 
 	/* Memory allocation. */
 	pipeline = calloc(1, sizeof(struct rte_swx_pipeline));
@@ -9736,6 +9831,9 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
 	}
 
 	/* Initialization. */
+	if (name)
+		strcpy(pipeline->name, name);
+
 	TAILQ_INIT(&pipeline->struct_types);
 	TAILQ_INIT(&pipeline->port_in_types);
 	TAILQ_INIT(&pipeline->ports_in);
@@ -9776,6 +9874,12 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
 	if (status)
 		goto error;
 
+	if (pipeline->name[0]) {
+		status = pipeline_register(pipeline);
+		if (status)
+			goto error;
+	}
+
 	*p = pipeline;
 	return 0;
 
@@ -9966,6 +10070,7 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p,
 	TAILQ_FOREACH(table, &p->tables, node)
 		n_tables++;
 
+	strcpy(pipeline->name, p->name);
 	pipeline->n_ports_in = p->n_ports_in;
 	pipeline->n_ports_out = p->n_ports_out;
 	pipeline->n_mirroring_slots = p->n_mirroring_slots;
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index c41ca5cb15..ef50a0fa70 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -44,22 +44,38 @@ extern "C" {
 /** Pipeline opaque data structure. */
 struct rte_swx_pipeline;
 
+/**
+ * Pipeline find
+ *
+ * @param[in] name
+ *   Pipeline name.
+ * @return
+ *   Valid pipeline handle if found or NULL otherwise.
+ */
+__rte_experimental
+struct rte_swx_pipeline *
+rte_swx_pipeline_find(const char *name);
+
 /**
  * Pipeline configure
  *
  * @param[out] p
  *   Pipeline handle. Must point to valid memory. Contains valid pipeline handle
  *   when the function returns successfully.
+ * @param[in] name
+ *   Pipeline unique name.
  * @param[in] numa_node
  *   Non-Uniform Memory Access (NUMA) node.
  * @return
  *   0 on success or the following error codes otherwise:
  *   -EINVAL: Invalid argument;
- *   -ENOMEM: Not enough space/cannot allocate memory.
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Pipeline with this name already exists.
  */
 __rte_experimental
 int
 rte_swx_pipeline_config(struct rte_swx_pipeline **p,
+			const char *name,
 			int numa_node);
 
 /*
diff --git a/lib/pipeline/rte_swx_pipeline_internal.h b/lib/pipeline/rte_swx_pipeline_internal.h
index a35635efb7..588cad62b5 100644
--- a/lib/pipeline/rte_swx_pipeline_internal.h
+++ b/lib/pipeline/rte_swx_pipeline_internal.h
@@ -1459,6 +1459,8 @@ instr_operand_nbo(struct thread *t, const struct instr_operand *x)
 #endif
 
 struct rte_swx_pipeline {
+	char name[RTE_SWX_NAME_SIZE];
+
 	struct struct_type_tailq struct_types;
 	struct port_in_type_tailq port_in_types;
 	struct port_in_tailq ports_in;
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 8312307a7a..50029aadcf 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -145,4 +145,8 @@ EXPERIMENTAL {
 	rte_swx_ctl_pipeline_learner_timeout_get;
 	rte_swx_ctl_pipeline_learner_timeout_set;
 	rte_swx_pipeline_hash_func_register;
+
+	#added in 22.11
+	rte_swx_ctl_pipeline_find;
+	rte_swx_pipeline_find;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V5 02/17] pipeline: move specification data structures to internal header
  2022-07-27 23:01       ` [PATCH V5 01/17] pipeline: add pipeline name Cristian Dumitrescu
@ 2022-07-27 23:01         ` Cristian Dumitrescu
  2022-07-27 23:01         ` [PATCH V5 03/17] pipeline: add pipeline specification data structure Cristian Dumitrescu
                           ` (15 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 23:01 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Move all the pipeline object specification data structures to an
internal header file.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 126 +------------------
 lib/pipeline/rte_swx_pipeline_spec.h | 176 +++++++++++++++++++++++++++
 2 files changed, 177 insertions(+), 125 deletions(-)
 create mode 100644 lib/pipeline/rte_swx_pipeline_spec.h

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 904b9eb471..5e07b4f794 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -9,7 +9,7 @@
 
 #include <rte_common.h>
 
-#include "rte_swx_pipeline.h"
+#include "rte_swx_pipeline_spec.h"
 
 #ifndef MAX_LINE_LENGTH
 #define MAX_LINE_LENGTH 2048
@@ -34,15 +34,7 @@
 
 /*
  * extobj.
- *
- * extobj OBJ_NAME instanceof OBJ_TYPE [ pragma OBJ_CREATE_ARGS ]
  */
-struct extobj_spec {
-	char *name;
-	char *extern_type_name;
-	char *pragma;
-};
-
 static void
 extobj_spec_free(struct extobj_spec *s)
 {
@@ -104,18 +96,7 @@ extobj_statement_parse(struct extobj_spec *s,
 /*
  * struct.
  *
- * struct STRUCT_TYPE_NAME {
- *	bit<SIZE> | varbit<SIZE> FIELD_NAME
- *	...
- * }
  */
-struct struct_spec {
-	char *name;
-	struct rte_swx_field_params *fields;
-	uint32_t n_fields;
-	int varbit;
-};
-
 static void
 struct_spec_free(struct struct_spec *s)
 {
@@ -293,13 +274,7 @@ struct_block_parse(struct struct_spec *s,
 /*
  * header.
  *
- * header HEADER_NAME instanceof STRUCT_TYPE_NAME
  */
-struct header_spec {
-	char *name;
-	char *struct_type_name;
-};
-
 static void
 header_spec_free(struct header_spec *s)
 {
@@ -351,12 +326,7 @@ header_statement_parse(struct header_spec *s,
 /*
  * metadata.
  *
- * metadata instanceof STRUCT_TYPE_NAME
  */
-struct metadata_spec {
-	char *struct_type_name;
-};
-
 static void
 metadata_spec_free(struct metadata_spec *s)
 {
@@ -400,18 +370,7 @@ metadata_statement_parse(struct metadata_spec *s,
 /*
  * action.
  *
- * action ACTION_NAME args none | instanceof STRUCT_TYPE_NAME {
- *	INSTRUCTION
- *	...
- * }
  */
-struct action_spec {
-	char *name;
-	char *args_struct_type_name;
-	const char **instructions;
-	uint32_t n_instructions;
-};
-
 static void
 action_spec_free(struct action_spec *s)
 {
@@ -540,29 +499,7 @@ action_block_parse(struct action_spec *s,
 /*
  * table.
  *
- * table TABLE_NAME {
- *	key {
- *		MATCH_FIELD_NAME exact | wildcard | lpm
- *		...
- *	}
- *	actions {
- *		ACTION_NAME [ @tableonly | @defaultonly ]
- *		...
- *	}
- *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
- *	instanceof TABLE_TYPE_NAME
- *	pragma ARGS
- *	size SIZE
- * }
  */
-struct table_spec {
-	char *name;
-	struct rte_swx_pipeline_table_params params;
-	char *recommended_table_type_name;
-	char *args;
-	uint32_t size;
-};
-
 static void
 table_spec_free(struct table_spec *s)
 {
@@ -1084,22 +1021,7 @@ table_block_parse(struct table_spec *s,
 /*
  * selector.
  *
- * selector SELECTOR_NAME {
- *	group_id FIELD_NAME
- *	selector {
- *		FIELD_NAME
- *		...
- *	}
- *	member_id FIELD_NAME
- *	n_groups N_GROUPS
- *	n_members_per_group N_MEMBERS_PER_GROUP
- * }
  */
-struct selector_spec {
-	char *name;
-	struct rte_swx_pipeline_selector_params params;
-};
-
 static void
 selector_spec_free(struct selector_spec *s)
 {
@@ -1385,31 +1307,7 @@ selector_block_parse(struct selector_spec *s,
 /*
  * learner.
  *
- * learner LEARNER_NAME {
- *	key {
- *		MATCH_FIELD_NAME
- *		...
- *	}
- *	actions {
- *		ACTION_NAME [ @tableonly | @defaultonly]
- *		...
- *	}
- *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
- *	size SIZE
- *	timeout {
- *		TIMEOUT_IN_SECONDS
- *		...
- *	}
- * }
  */
-struct learner_spec {
-	char *name;
-	struct rte_swx_pipeline_learner_params params;
-	uint32_t size;
-	uint32_t *timeout;
-	uint32_t n_timeouts;
-};
-
 static void
 learner_spec_free(struct learner_spec *s)
 {
@@ -1958,14 +1856,7 @@ learner_block_parse(struct learner_spec *s,
 /*
  * regarray.
  *
- * regarray NAME size SIZE initval INITVAL
  */
-struct regarray_spec {
-	char *name;
-	uint64_t init_val;
-	uint32_t size;
-};
-
 static void
 regarray_spec_free(struct regarray_spec *s)
 {
@@ -2033,13 +1924,7 @@ regarray_statement_parse(struct regarray_spec *s,
 /*
  * metarray.
  *
- * metarray NAME size SIZE
  */
-struct metarray_spec {
-	char *name;
-	uint32_t size;
-};
-
 static void
 metarray_spec_free(struct metarray_spec *s)
 {
@@ -2095,16 +1980,7 @@ metarray_statement_parse(struct metarray_spec *s,
 /*
  * apply.
  *
- * apply {
- *	INSTRUCTION
- *	...
- * }
  */
-struct apply_spec {
-	const char **instructions;
-	uint32_t n_instructions;
-};
-
 static void
 apply_spec_free(struct apply_spec *s)
 {
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
new file mode 100644
index 0000000000..8458de878a
--- /dev/null
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+#include <stdint.h>
+#include <stdio.h>
+
+#include <rte_common.h>
+
+#include <rte_swx_pipeline.h>
+
+/*
+ * extobj.
+ *
+ * extobj OBJ_NAME instanceof OBJ_TYPE [ pragma OBJ_CREATE_ARGS ]
+ */
+struct extobj_spec {
+	char *name;
+	char *extern_type_name;
+	char *pragma;
+};
+
+/*
+ * struct.
+ *
+ * struct STRUCT_TYPE_NAME {
+ *	bit<SIZE> | varbit<SIZE> FIELD_NAME
+ *	...
+ * }
+ */
+struct struct_spec {
+	char *name;
+	struct rte_swx_field_params *fields;
+	uint32_t n_fields;
+	int varbit;
+};
+
+/*
+ * header.
+ *
+ * header HEADER_NAME instanceof STRUCT_TYPE_NAME
+ */
+struct header_spec {
+	char *name;
+	char *struct_type_name;
+};
+
+/*
+ * metadata.
+ *
+ * metadata instanceof STRUCT_TYPE_NAME
+ */
+struct metadata_spec {
+	char *struct_type_name;
+};
+
+/*
+ * action.
+ *
+ * action ACTION_NAME args none | instanceof STRUCT_TYPE_NAME {
+ *	INSTRUCTION
+ *	...
+ * }
+ */
+struct action_spec {
+	char *name;
+	char *args_struct_type_name;
+	const char **instructions;
+	uint32_t n_instructions;
+};
+
+/*
+ * table.
+ *
+ * table TABLE_NAME {
+ *	key {
+ *		MATCH_FIELD_NAME exact | wildcard | lpm
+ *		...
+ *	}
+ *	actions {
+ *		ACTION_NAME [ @tableonly | @defaultonly ]
+ *		...
+ *	}
+ *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
+ *	instanceof TABLE_TYPE_NAME
+ *	pragma ARGS
+ *	size SIZE
+ * }
+ */
+struct table_spec {
+	char *name;
+	struct rte_swx_pipeline_table_params params;
+	char *recommended_table_type_name;
+	char *args;
+	uint32_t size;
+};
+
+/*
+ * selector.
+ *
+ * selector SELECTOR_NAME {
+ *	group_id FIELD_NAME
+ *	selector {
+ *		FIELD_NAME
+ *		...
+ *	}
+ *	member_id FIELD_NAME
+ *	n_groups N_GROUPS
+ *	n_members_per_group N_MEMBERS_PER_GROUP
+ * }
+ */
+struct selector_spec {
+	char *name;
+	struct rte_swx_pipeline_selector_params params;
+};
+
+/*
+ * learner.
+ *
+ * learner LEARNER_NAME {
+ *	key {
+ *		MATCH_FIELD_NAME
+ *		...
+ *	}
+ *	actions {
+ *		ACTION_NAME [ @tableonly | @defaultonly]
+ *		...
+ *	}
+ *	default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
+ *	size SIZE
+ *	timeout {
+ *		TIMEOUT_IN_SECONDS
+ *		...
+ *	}
+ * }
+ */
+struct learner_spec {
+	char *name;
+	struct rte_swx_pipeline_learner_params params;
+	uint32_t size;
+	uint32_t *timeout;
+	uint32_t n_timeouts;
+};
+
+/*
+ * regarray.
+ *
+ * regarray NAME size SIZE initval INITVAL
+ */
+struct regarray_spec {
+	char *name;
+	uint64_t init_val;
+	uint32_t size;
+};
+
+/*
+ * metarray.
+ *
+ * metarray NAME size SIZE
+ */
+struct metarray_spec {
+	char *name;
+	uint32_t size;
+};
+
+/*
+ * apply.
+ *
+ * apply {
+ *	INSTRUCTION
+ *	...
+ * }
+ */
+struct apply_spec {
+	const char **instructions;
+	uint32_t n_instructions;
+};
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V5 03/17] pipeline: add pipeline specification data structure
  2022-07-27 23:01       ` [PATCH V5 01/17] pipeline: add pipeline name Cristian Dumitrescu
  2022-07-27 23:01         ` [PATCH V5 02/17] pipeline: move specification data structures to internal header Cristian Dumitrescu
@ 2022-07-27 23:01         ` Cristian Dumitrescu
  2022-07-27 23:01         ` [PATCH V5 04/17] pipeline: rework the specification file-based pipeline build Cristian Dumitrescu
                           ` (14 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 23:01 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add specification data structure for the entire pipeline.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 21 ++++++++++++++++++
 lib/pipeline/rte_swx_pipeline_spec.h | 32 ++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 5e07b4f794..642091b678 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2082,6 +2082,27 @@ apply_block_parse(struct apply_spec *s,
 /*
  * Pipeline.
  */
+void
+pipeline_spec_free(struct pipeline_spec *s)
+{
+	if (!s)
+		return;
+
+	free(s->extobjs);
+	free(s->structs);
+	free(s->headers);
+	free(s->metadata);
+	free(s->actions);
+	free(s->tables);
+	free(s->selectors);
+	free(s->learners);
+	free(s->regarrays);
+	free(s->metarrays);
+	free(s->apply);
+
+	memset(s, 0, sizeof(struct pipeline_spec));
+}
+
 int
 rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				 FILE *spec,
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 8458de878a..e1170a33b1 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -174,3 +174,35 @@ struct apply_spec {
 	const char **instructions;
 	uint32_t n_instructions;
 };
+
+/*
+ * Pipeline.
+ */
+struct pipeline_spec {
+	struct extobj_spec *extobjs;
+	struct struct_spec *structs;
+	struct header_spec *headers;
+	struct metadata_spec *metadata;
+	struct action_spec *actions;
+	struct table_spec *tables;
+	struct selector_spec *selectors;
+	struct learner_spec *learners;
+	struct regarray_spec *regarrays;
+	struct metarray_spec *metarrays;
+	struct apply_spec *apply;
+
+	uint32_t n_extobjs;
+	uint32_t n_structs;
+	uint32_t n_headers;
+	uint32_t n_metadata;
+	uint32_t n_actions;
+	uint32_t n_tables;
+	uint32_t n_selectors;
+	uint32_t n_learners;
+	uint32_t n_regarrays;
+	uint32_t n_metarrays;
+	uint32_t n_apply;
+};
+
+void
+pipeline_spec_free(struct pipeline_spec *s);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V5 04/17] pipeline: rework the specification file-based pipeline build
  2022-07-27 23:01       ` [PATCH V5 01/17] pipeline: add pipeline name Cristian Dumitrescu
  2022-07-27 23:01         ` [PATCH V5 02/17] pipeline: move specification data structures to internal header Cristian Dumitrescu
  2022-07-27 23:01         ` [PATCH V5 03/17] pipeline: add pipeline specification data structure Cristian Dumitrescu
@ 2022-07-27 23:01         ` Cristian Dumitrescu
  2022-07-27 23:01         ` [PATCH V5 05/17] pipeline: generate the code for pipeline specification structure Cristian Dumitrescu
                           ` (13 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 23:01 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Rework the specification file-based pipeline build operation to first
parse the specification file into the previously introduced pipeline
specification data structure, then use this structure to configure
and build the pipeline.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 478 +++++++++++++++++++++------
 lib/pipeline/rte_swx_pipeline_spec.h |   9 +
 2 files changed, 385 insertions(+), 102 deletions(-)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 642091b678..62929a9da6 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2103,11 +2103,10 @@ pipeline_spec_free(struct pipeline_spec *s)
 	memset(s, 0, sizeof(struct pipeline_spec));
 }
 
-int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec,
-				 uint32_t *err_line,
-				 const char **err_msg)
+struct pipeline_spec *
+pipeline_spec_parse(FILE *spec,
+		    uint32_t *err_line,
+		    const char **err_msg)
 {
 	struct extobj_spec extobj_spec = {0};
 	struct struct_spec struct_spec = {0};
@@ -2120,26 +2119,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 	struct regarray_spec regarray_spec = {0};
 	struct metarray_spec metarray_spec = {0};
 	struct apply_spec apply_spec = {0};
-	uint32_t n_lines;
+	struct pipeline_spec *s = NULL;
+	uint32_t n_lines = 0;
 	uint32_t block_mask = 0;
-	int status;
+	int status = 0;
 
 	/* Check the input arguments. */
-	if (!p) {
+	if (!spec) {
 		if (err_line)
-			*err_line = 0;
+			*err_line = n_lines;
 		if (err_msg)
-			*err_msg = "Null pipeline argument.";
+			*err_msg = "Invalid input argument.";
 		status = -EINVAL;
 		goto error;
 	}
 
-	if (!spec) {
+	/* Memory allocation. */
+	s = calloc(sizeof(struct pipeline_spec), 1);
+	if (!s) {
 		if (err_line)
-			*err_line = 0;
+			*err_line = n_lines;
 		if (err_msg)
-			*err_msg = "Null specification file argument.";
-		status = -EINVAL;
+			*err_msg = "Memory allocation failed.";
+		status = -ENOMEM;
 		goto error;
 	}
 
@@ -2200,6 +2202,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* struct block. */
 		if (block_mask & (1 << STRUCT_BLOCK)) {
+			struct struct_spec *new_structs;
+
 			status = struct_block_parse(&struct_spec,
 						    &block_mask,
 						    tokens,
@@ -2214,26 +2218,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_struct_type_register(p,
-				struct_spec.name,
-				struct_spec.fields,
-				struct_spec.n_fields,
-				struct_spec.varbit);
-			if (status) {
+			new_structs = realloc(s->structs,
+					      (s->n_structs + 1) * sizeof(struct struct_spec));
+			if (!new_structs) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Struct registration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			struct_spec_free(&struct_spec);
+			s->structs = new_structs;
+			memcpy(&s->structs[s->n_structs], &struct_spec, sizeof(struct struct_spec));
+			s->n_structs++;
+			memset(&struct_spec, 0, sizeof(struct struct_spec));
 
 			continue;
 		}
 
 		/* action block. */
 		if (block_mask & (1 << ACTION_BLOCK)) {
+			struct action_spec *new_actions;
+
 			status = action_block_parse(&action_spec,
 						    &block_mask,
 						    tokens,
@@ -2248,26 +2255,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_action_config(p,
-				action_spec.name,
-				action_spec.args_struct_type_name,
-				action_spec.instructions,
-				action_spec.n_instructions);
-			if (status) {
+			new_actions = realloc(s->actions,
+					      (s->n_actions + 1) * sizeof(struct action_spec));
+			if (!new_actions) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Action config error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			action_spec_free(&action_spec);
+			s->actions = new_actions;
+			memcpy(&s->actions[s->n_actions], &action_spec, sizeof(struct action_spec));
+			s->n_actions++;
+			memset(&action_spec, 0, sizeof(struct action_spec));
 
 			continue;
 		}
 
 		/* table block. */
 		if (block_mask & (1 << TABLE_BLOCK)) {
+			struct table_spec *new_tables;
+
 			status = table_block_parse(&table_spec,
 						   &block_mask,
 						   tokens,
@@ -2282,27 +2292,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_table_config(p,
-				table_spec.name,
-				&table_spec.params,
-				table_spec.recommended_table_type_name,
-				table_spec.args,
-				table_spec.size);
-			if (status) {
+			new_tables = realloc(s->tables,
+					     (s->n_tables + 1) * sizeof(struct table_spec));
+			if (!new_tables) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Table configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			table_spec_free(&table_spec);
+			s->tables = new_tables;
+			memcpy(&s->tables[s->n_tables], &table_spec, sizeof(struct table_spec));
+			s->n_tables++;
+			memset(&table_spec, 0, sizeof(struct table_spec));
 
 			continue;
 		}
 
 		/* selector block. */
 		if (block_mask & (1 << SELECTOR_BLOCK)) {
+			struct selector_spec *new_selectors;
+
 			status = selector_block_parse(&selector_spec,
 						      &block_mask,
 						      tokens,
@@ -2317,24 +2329,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_selector_config(p,
-				selector_spec.name,
-				&selector_spec.params);
-			if (status) {
+			new_selectors = realloc(s->selectors,
+				(s->n_selectors + 1) * sizeof(struct selector_spec));
+			if (!new_selectors) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Selector configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			selector_spec_free(&selector_spec);
+			s->selectors = new_selectors;
+			memcpy(&s->selectors[s->n_selectors],
+			       &selector_spec,
+			       sizeof(struct selector_spec));
+			s->n_selectors++;
+			memset(&selector_spec, 0, sizeof(struct selector_spec));
 
 			continue;
 		}
 
 		/* learner block. */
 		if (block_mask & (1 << LEARNER_BLOCK)) {
+			struct learner_spec *new_learners;
+
 			status = learner_block_parse(&learner_spec,
 						     &block_mask,
 						     tokens,
@@ -2349,27 +2368,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_learner_config(p,
-				learner_spec.name,
-				&learner_spec.params,
-				learner_spec.size,
-				learner_spec.timeout,
-				learner_spec.n_timeouts);
-			if (status) {
+			new_learners = realloc(s->learners,
+					       (s->n_learners + 1) * sizeof(struct learner_spec));
+			if (!new_learners) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Learner table configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			learner_spec_free(&learner_spec);
+			s->learners = new_learners;
+			memcpy(&s->learners[s->n_learners],
+			       &learner_spec,
+			       sizeof(struct learner_spec));
+			s->n_learners++;
+			memset(&learner_spec, 0, sizeof(struct learner_spec));
 
 			continue;
 		}
 
 		/* apply block. */
 		if (block_mask & (1 << APPLY_BLOCK)) {
+			struct apply_spec *new_apply;
+
 			status = apply_block_parse(&apply_spec,
 						   &block_mask,
 						   tokens,
@@ -2384,24 +2407,28 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 				continue;
 
 			/* End of block. */
-			status = rte_swx_pipeline_instructions_config(p,
-				apply_spec.instructions,
-				apply_spec.n_instructions);
-			if (status) {
+			new_apply = realloc(s->apply, (s->n_apply + 1) * sizeof(struct apply_spec));
+			if (!new_apply) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Pipeline instructions err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			apply_spec_free(&apply_spec);
+			s->apply = new_apply;
+			memcpy(&s->apply[s->n_apply], &apply_spec, sizeof(struct apply_spec));
+			s->n_apply++;
+			memset(&apply_spec, 0, sizeof(struct apply_spec));
 
 			continue;
 		}
 
 		/* extobj. */
 		if (!strcmp(tokens[0], "extobj")) {
+			struct extobj_spec *new_extobjs;
+
 			status = extobj_statement_parse(&extobj_spec,
 							tokens,
 							n_tokens,
@@ -2411,19 +2438,21 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_extern_object_config(p,
-				extobj_spec.name,
-				extobj_spec.extern_type_name,
-				extobj_spec.pragma);
-			if (status) {
+			new_extobjs = realloc(s->extobjs,
+					      (s->n_extobjs + 1) * sizeof(struct extobj_spec));
+			if (!new_extobjs) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Extern object config err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			extobj_spec_free(&extobj_spec);
+			s->extobjs = new_extobjs;
+			memcpy(&s->extobjs[s->n_extobjs], &extobj_spec, sizeof(struct extobj_spec));
+			s->n_extobjs++;
+			memset(&extobj_spec, 0, sizeof(struct extobj_spec));
 
 			continue;
 		}
@@ -2445,6 +2474,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* header. */
 		if (!strcmp(tokens[0], "header")) {
+			struct header_spec *new_headers;
+
 			status = header_statement_parse(&header_spec,
 							tokens,
 							n_tokens,
@@ -2454,24 +2485,29 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_packet_header_register(p,
-				header_spec.name,
-				header_spec.struct_type_name);
-			if (status) {
+			new_headers = realloc(s->headers,
+					      (s->n_headers + 1) * sizeof(struct header_spec));
+			if (!new_headers) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Header registration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			header_spec_free(&header_spec);
+			s->headers = new_headers;
+			memcpy(&s->headers[s->n_headers], &header_spec, sizeof(struct header_spec));
+			s->n_headers++;
+			memset(&header_spec, 0, sizeof(struct header_spec));
 
 			continue;
 		}
 
 		/* metadata. */
 		if (!strcmp(tokens[0], "metadata")) {
+			struct metadata_spec *new_metadata;
+
 			status = metadata_statement_parse(&metadata_spec,
 							  tokens,
 							  n_tokens,
@@ -2481,17 +2517,23 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_packet_metadata_register(p,
-				metadata_spec.struct_type_name);
-			if (status) {
+			new_metadata = realloc(s->metadata,
+					       (s->n_metadata + 1) * sizeof(struct metadata_spec));
+			if (!new_metadata) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Meta-data reg err.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			metadata_spec_free(&metadata_spec);
+			s->metadata = new_metadata;
+			memcpy(&s->metadata[s->n_metadata],
+			       &metadata_spec,
+			       sizeof(struct metadata_spec));
+			s->n_metadata++;
+			memset(&metadata_spec, 0, sizeof(struct metadata_spec));
 
 			continue;
 		}
@@ -2558,6 +2600,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 
 		/* regarray. */
 		if (!strcmp(tokens[0], "regarray")) {
+			struct regarray_spec *new_regarrays;
+
 			status = regarray_statement_parse(&regarray_spec,
 							  tokens,
 							  n_tokens,
@@ -2567,25 +2611,31 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_regarray_config(p,
-				regarray_spec.name,
-				regarray_spec.size,
-				regarray_spec.init_val);
-			if (status) {
+			new_regarrays = realloc(s->regarrays,
+				(s->n_regarrays + 1) * sizeof(struct regarray_spec));
+			if (!new_regarrays) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Register array configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			regarray_spec_free(&regarray_spec);
+			s->regarrays = new_regarrays;
+			memcpy(&s->regarrays[s->n_regarrays],
+			       &regarray_spec,
+			       sizeof(struct regarray_spec));
+			s->n_regarrays++;
+			memset(&regarray_spec, 0, sizeof(struct regarray_spec));
 
 			continue;
 		}
 
 		/* metarray. */
 		if (!strcmp(tokens[0], "metarray")) {
+			struct metarray_spec *new_metarrays;
+
 			status = metarray_statement_parse(&metarray_spec,
 							  tokens,
 							  n_tokens,
@@ -2595,18 +2645,23 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 			if (status)
 				goto error;
 
-			status = rte_swx_pipeline_metarray_config(p,
-				metarray_spec.name,
-				metarray_spec.size);
-			if (status) {
+			new_metarrays = realloc(s->metarrays,
+				(s->n_metarrays + 1) * sizeof(struct metarray_spec));
+			if (!new_metarrays) {
 				if (err_line)
 					*err_line = n_lines;
 				if (err_msg)
-					*err_msg = "Meter array configuration error.";
+					*err_msg = "Memory allocation failed.";
+				status = -ENOMEM;
 				goto error;
 			}
 
-			metarray_spec_free(&metarray_spec);
+			s->metarrays = new_metarrays;
+			memcpy(&s->metarrays[s->n_metarrays],
+			       &metarray_spec,
+			       sizeof(struct metarray_spec));
+			s->n_metarrays++;
+			memset(&metarray_spec, 0, sizeof(struct metarray_spec));
 
 			continue;
 		}
@@ -2644,17 +2699,7 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 		goto error;
 	}
 
-	/* Pipeline build. */
-	status = rte_swx_pipeline_build(p);
-	if (status) {
-		if (err_line)
-			*err_line = n_lines;
-		if (err_msg)
-			*err_msg = "Pipeline build error.";
-		goto error;
-	}
-
-	return 0;
+	return s;
 
 error:
 	extobj_spec_free(&extobj_spec);
@@ -2668,5 +2713,234 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 	regarray_spec_free(&regarray_spec);
 	metarray_spec_free(&metarray_spec);
 	apply_spec_free(&apply_spec);
+	pipeline_spec_free(s);
+
+	return NULL;
+}
+
+int
+pipeline_spec_configure(struct rte_swx_pipeline *p,
+			struct pipeline_spec *s,
+			const char **err_msg)
+{
+	uint32_t i;
+	int status = 0;
+
+	/* extobj. */
+	for (i = 0; i < s->n_extobjs; i++) {
+		struct extobj_spec *extobj_spec = &s->extobjs[i];
+
+		status = rte_swx_pipeline_extern_object_config(p,
+			extobj_spec->name,
+			extobj_spec->extern_type_name,
+			extobj_spec->pragma);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Extern object configuration error.";
+			return status;
+		}
+	}
+
+	/* regarray. */
+	for (i = 0; i < s->n_regarrays; i++) {
+		struct regarray_spec *regarray_spec = &s->regarrays[i];
+
+		status = rte_swx_pipeline_regarray_config(p,
+			regarray_spec->name,
+			regarray_spec->size,
+			regarray_spec->init_val);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Register array configuration error.";
+			return status;
+		}
+	}
+
+	/* metarray. */
+	for (i = 0; i < s->n_metarrays; i++) {
+		struct metarray_spec *metarray_spec = &s->metarrays[i];
+
+		status = rte_swx_pipeline_metarray_config(p,
+			metarray_spec->name,
+			metarray_spec->size);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Meter array configuration error.";
+			return status;
+		}
+	}
+
+	/* struct. */
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+
+		status = rte_swx_pipeline_struct_type_register(p,
+			struct_spec->name,
+			struct_spec->fields,
+			struct_spec->n_fields,
+			struct_spec->varbit);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Struct type registration error.";
+			return status;
+		}
+	}
+
+	/* header. */
+	for (i = 0; i < s->n_headers; i++) {
+		struct header_spec *header_spec = &s->headers[i];
+
+		status = rte_swx_pipeline_packet_header_register(p,
+			header_spec->name,
+			header_spec->struct_type_name);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Header configuration error.";
+			return status;
+		}
+	}
+
+	/* metadata. */
+	for (i = 0; i < s->n_metadata; i++) {
+		struct metadata_spec *metadata_spec = &s->metadata[i];
+
+		status = rte_swx_pipeline_packet_metadata_register(p,
+			metadata_spec->struct_type_name);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Meta-data registration error.";
+			return status;
+		}
+	}
+
+	/* action. */
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+
+		status = rte_swx_pipeline_action_config(p,
+			action_spec->name,
+			action_spec->args_struct_type_name,
+			action_spec->instructions,
+			action_spec->n_instructions);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Action configuration error.";
+			return status;
+		}
+	}
+
+	/* table. */
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+
+		status = rte_swx_pipeline_table_config(p,
+			table_spec->name,
+			&table_spec->params,
+			table_spec->recommended_table_type_name,
+			table_spec->args,
+			table_spec->size);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Table configuration error.";
+			return status;
+		}
+	}
+
+	/* selector. */
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+
+		status = rte_swx_pipeline_selector_config(p,
+			selector_spec->name,
+			&selector_spec->params);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Selector table configuration error.";
+			return status;
+		}
+	}
+
+	/* learner. */
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+
+		status = rte_swx_pipeline_learner_config(p,
+			learner_spec->name,
+			&learner_spec->params,
+			learner_spec->size,
+			learner_spec->timeout,
+			learner_spec->n_timeouts);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Learner table configuration error.";
+			return status;
+		}
+	}
+
+	/* apply. */
+	for (i = 0; i < s->n_apply; i++) {
+		struct apply_spec *apply_spec = &s->apply[i];
+
+		status = rte_swx_pipeline_instructions_config(p,
+			apply_spec->instructions,
+			apply_spec->n_instructions);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Pipeline instructions configuration error.";
+			return status;
+		}
+	}
+
+	return 0;
+}
+
+int
+rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
+				 FILE *spec_file,
+				 uint32_t *err_line,
+				 const char **err_msg)
+{
+	struct pipeline_spec *s = NULL;
+	int status = 0;
+
+	/* Check the input arguments. */
+	if (!p || !spec_file) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Invalid input argument.";
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Spec file parse. */
+	s = pipeline_spec_parse(spec_file, err_line, err_msg);
+	if (!s) {
+		status = -EINVAL;
+		goto error;
+	}
+
+	/* Pipeline configure. */
+	status = pipeline_spec_configure(p, s, err_msg);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		goto error;
+	}
+
+	/* Pipeline build. */
+	status = rte_swx_pipeline_build(p);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Pipeline build error.";
+		goto error;
+	}
+
+	return 0;
+
+error:
+	pipeline_spec_free(s);
 	return status;
 }
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index e1170a33b1..4f3a0b5958 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -206,3 +206,12 @@ struct pipeline_spec {
 
 void
 pipeline_spec_free(struct pipeline_spec *s);
+struct pipeline_spec *
+pipeline_spec_parse(FILE *spec,
+		    uint32_t *err_line,
+		    const char **err_msg);
+
+int
+pipeline_spec_configure(struct rte_swx_pipeline *p,
+			struct pipeline_spec *s,
+			const char **err_msg);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V5 05/17] pipeline: generate the code for pipeline specification structure
  2022-07-27 23:01       ` [PATCH V5 01/17] pipeline: add pipeline name Cristian Dumitrescu
                           ` (2 preceding siblings ...)
  2022-07-27 23:01         ` [PATCH V5 04/17] pipeline: rework the specification file-based pipeline build Cristian Dumitrescu
@ 2022-07-27 23:01         ` Cristian Dumitrescu
  2022-07-27 23:01         ` [PATCH V5 06/17] pipeline: add support for pipeline I/O specification Cristian Dumitrescu
                           ` (12 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 23:01 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add support to export the pipeline specification data structure to a C
source code file.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 622 +++++++++++++++++++++++++++
 lib/pipeline/rte_swx_pipeline_spec.h |   5 +
 2 files changed, 627 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 62929a9da6..bf21fe17ba 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2020 Intel Corporation
  */
 #include <stdint.h>
+#include <inttypes.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -2103,6 +2104,627 @@ pipeline_spec_free(struct pipeline_spec *s)
 	memset(s, 0, sizeof(struct pipeline_spec));
 }
 
+static const char *
+match_type_string_get(enum rte_swx_table_match_type match_type)
+{
+	switch (match_type) {
+	case RTE_SWX_TABLE_MATCH_WILDCARD: return "RTE_SWX_TABLE_MATCH_WILDCARD";
+	case RTE_SWX_TABLE_MATCH_LPM: return "RTE_SWX_TABLE_MATCH_LPM";
+	case RTE_SWX_TABLE_MATCH_EXACT: return "RTE_SWX_TABLE_MATCH_EXACT";
+	default: return "RTE_SWX_TABLE_MATCH_UNKNOWN";
+	}
+}
+
+void
+pipeline_spec_codegen(FILE *f,
+		      struct pipeline_spec *s)
+{
+	uint32_t i;
+
+	/* Check the input arguments. */
+	if (!f || !s)
+		return;
+
+	/* extobj. */
+	fprintf(f, "static struct extobj_spec extobjs[] = {\n");
+
+	for (i = 0; i < s->n_extobjs; i++) {
+		struct extobj_spec *extobj_spec = &s->extobjs[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", extobj_spec->name);
+		fprintf(f, "\t\t.extern_type_name = \"%s\",\n", extobj_spec->extern_type_name);
+		if (extobj_spec->pragma)
+			fprintf(f, "\t\t.pragma = \"%s\",\n", extobj_spec->pragma);
+		else
+			fprintf(f, "\t\t.pragma = NULL,\n");
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* regarray. */
+	fprintf(f, "static struct regarray_spec regarrays[] = {\n");
+
+	for (i = 0; i < s->n_regarrays; i++) {
+		struct regarray_spec *regarray_spec = &s->regarrays[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", regarray_spec->name);
+		fprintf(f, "\t\t.init_val = %" PRIu64 ",\n", regarray_spec->init_val);
+		fprintf(f, "\t\t.size = %u,\n", regarray_spec->size);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* metarray. */
+	fprintf(f, "static struct metarray_spec metarrays[] = {\n");
+
+	for (i = 0; i < s->n_metarrays; i++) {
+		struct metarray_spec *metarray_spec = &s->metarrays[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", metarray_spec->name);
+		fprintf(f, "\t\t.size = %u,\n", metarray_spec->size);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* struct. */
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+		uint32_t j;
+
+		fprintf(f, "static struct rte_swx_field_params struct_%s_fields[] = {\n",
+			struct_spec->name);
+
+		for (j = 0; j < struct_spec->n_fields; j++) {
+			struct rte_swx_field_params *field = &struct_spec->fields[j];
+
+			fprintf(f, "\t[%d] = {\n", j);
+			fprintf(f, "\t\t.name = \"%s\",\n", field->name);
+			fprintf(f, "\t\t.n_bits = %u,\n", field->n_bits);
+			fprintf(f, "\t},\n");
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct struct_spec structs[] = {\n");
+
+	for (i = 0; i < s->n_structs; i++) {
+		struct struct_spec *struct_spec = &s->structs[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", struct_spec->name);
+		fprintf(f, "\t\t.fields = struct_%s_fields,\n", struct_spec->name);
+		fprintf(f, "\t\t.n_fields = "
+			"sizeof(struct_%s_fields) / sizeof(struct_%s_fields[0]),\n",
+			struct_spec->name,
+			struct_spec->name);
+		fprintf(f, "\t\t.varbit = %d,\n", struct_spec->varbit);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* header. */
+	fprintf(f, "static struct header_spec headers[] = {\n");
+
+	for (i = 0; i < s->n_headers; i++) {
+		struct header_spec *header_spec = &s->headers[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", header_spec->name);
+		fprintf(f, "\t\t.struct_type_name = \"%s\",\n", header_spec->struct_type_name);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* metadata. */
+	fprintf(f, "static struct metadata_spec metadata[] = {\n");
+
+	for (i = 0; i < s->n_metadata; i++) {
+		struct metadata_spec *metadata_spec = &s->metadata[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.struct_type_name = \"%s\",\n", metadata_spec->struct_type_name);
+		fprintf(f, "\t},\n");
+
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* action. */
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+		uint32_t j;
+
+		fprintf(f, "static const char *action_%s_initial_instructions[] = {\n",
+			action_spec->name);
+
+		for (j = 0; j < action_spec->n_instructions; j++) {
+			const char *instr = action_spec->instructions[j];
+
+			fprintf(f, "\t[%d] = \"%s\",\n", j, instr);
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct action_spec actions[] = {\n");
+
+	for (i = 0; i < s->n_actions; i++) {
+		struct action_spec *action_spec = &s->actions[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", action_spec->name);
+
+		if (action_spec->args_struct_type_name)
+			fprintf(f, "\t\t.args_struct_type_name = \"%s\",\n",
+				action_spec->args_struct_type_name);
+		else
+			fprintf(f, "\t\t.args_struct_type_name = NULL,\n");
+
+		fprintf(f, "\t\t.instructions = action_%s_initial_instructions,\n",
+			action_spec->name);
+		fprintf(f, "\t\t.n_instructions = "
+			"sizeof(action_%s_initial_instructions) / "
+			"sizeof(action_%s_initial_instructions[0]),\n",
+			action_spec->name,
+			action_spec->name);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* table. */
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+		uint32_t j;
+
+		/* fields. */
+		if (table_spec->params.fields && table_spec->params.n_fields) {
+			fprintf(f, "static struct rte_swx_match_field_params "
+				"table_%s_fields[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_fields; j++) {
+				struct rte_swx_match_field_params *field =
+					&table_spec->params.fields[j];
+
+				fprintf(f, "\t[%d] = {\n", j);
+				fprintf(f, "\t\t.name = \"%s\",\n", field->name);
+				fprintf(f, "\t\t.match_type = %s,\n",
+					match_type_string_get(field->match_type));
+				fprintf(f, "\t},\n");
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_names. */
+		if (table_spec->params.action_names && table_spec->params.n_actions) {
+			fprintf(f, "static const char *table_%s_action_names[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				const char *action_name = table_spec->params.action_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, action_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_table_entries. */
+		if (table_spec->params.action_is_for_table_entries &&
+		    table_spec->params.n_actions) {
+			fprintf(f, "static int table_%s_action_is_for_table_entries[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				int value = table_spec->params.action_is_for_table_entries[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_default_entry. */
+		if (table_spec->params.action_is_for_default_entry &&
+		    table_spec->params.n_actions) {
+			fprintf(f, "static int table_%s_action_is_for_default_entry[] = {\n",
+				table_spec->name);
+
+			for (j = 0; j < table_spec->params.n_actions; j++) {
+				int value = table_spec->params.action_is_for_default_entry[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct table_spec tables[] = {\n");
+
+	for (i = 0; i < s->n_tables; i++) {
+		struct table_spec *table_spec = &s->tables[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", table_spec->name);
+
+		fprintf(f, "\t\t.params = {\n");
+
+		if (table_spec->params.fields && table_spec->params.n_fields) {
+			fprintf(f, "\t\t\t.fields = table_%s_fields,\n", table_spec->name);
+			fprintf(f, "\t\t\t.n_fields = "
+				"sizeof(table_%s_fields) / sizeof(table_%s_fields[0]),\n",
+				table_spec->name,
+				table_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.fields = NULL,\n");
+			fprintf(f, "\t\t\t.n_fields = 0,\n");
+		}
+
+		if (table_spec->params.action_names && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_names = table_%s_action_names,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_names = NULL,\n");
+
+		if (table_spec->params.action_is_for_table_entries && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_table_entries = "
+				"table_%s_action_is_for_table_entries,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_table_entries = NULL,\n");
+
+		if (table_spec->params.action_is_for_default_entry && table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_default_entry = "
+				"table_%s_action_is_for_default_entry,\n",
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_default_entry = NULL,\n");
+
+		if (table_spec->params.n_actions)
+			fprintf(f, "\t\t\t.n_actions = sizeof(table_%s_action_names) / "
+				"sizeof(table_%s_action_names[0]),\n",
+				table_spec->name,
+				table_spec->name);
+		else
+			fprintf(f, "\t\t\t.n_actions = 0,\n");
+
+		if (table_spec->params.default_action_name)
+			fprintf(f, "\t\t\t.default_action_name = \"%s\",\n",
+				table_spec->params.default_action_name);
+		else
+			fprintf(f, "\t\t\t.default_action_name = NULL,\n");
+
+		if (table_spec->params.default_action_args)
+			fprintf(f, "\t\t\t.default_action_args = \"%s\",\n",
+				table_spec->params.default_action_args);
+		else
+			fprintf(f, "\t\t\t.default_action_args = NULL,\n");
+
+		fprintf(f, "\t\t\t.default_action_is_const = %d,\n",
+			table_spec->params.default_action_is_const);
+		fprintf(f, "\t\t},\n");
+
+		if (table_spec->recommended_table_type_name)
+			fprintf(f, "\t\t.recommended_table_type_name = \"%s\",\n",
+				table_spec->recommended_table_type_name);
+		else
+			fprintf(f, "\t\t.recommended_table_type_name = NULL,\n");
+
+		if (table_spec->args)
+			fprintf(f, "\t\t.args = \"%s\",\n", table_spec->args);
+		else
+			fprintf(f, "\t\t.args = NULL,\n");
+
+		fprintf(f, "\t\t.size = %u,\n", table_spec->size);
+
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* selector. */
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+		uint32_t j;
+
+		if (selector_spec->params.selector_field_names &&
+		    selector_spec->params.n_selector_fields) {
+			fprintf(f, "static const char *selector_%s_field_names[] = {\n",
+				selector_spec->name);
+
+			for (j = 0; j < selector_spec->params.n_selector_fields; j++) {
+				const char *field_name =
+					selector_spec->params.selector_field_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, field_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct selector_spec selectors[] = {\n");
+
+	for (i = 0; i < s->n_selectors; i++) {
+		struct selector_spec *selector_spec = &s->selectors[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+
+		fprintf(f, "\t\t.name = \"%s\",\n", selector_spec->name);
+		fprintf(f, "\t\t.params = {\n");
+
+		if (selector_spec->params.group_id_field_name)
+			fprintf(f, "\t\t\t.group_id_field_name = \"%s\",\n",
+				selector_spec->params.group_id_field_name);
+		else
+			fprintf(f, "\t\t\t.group_id_field_name = NULL,\n");
+
+		if (selector_spec->params.selector_field_names &&
+		    selector_spec->params.n_selector_fields) {
+			fprintf(f, "\t\t\t.selector_field_names = selector_%s_field_names,\n",
+				selector_spec->name);
+			fprintf(f, "\t\t\t.n_selector_fields = "
+				"sizeof(selector_%s_field_names) / sizeof(selector_%s_field_names[0]),\n",
+				selector_spec->name,
+				selector_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.selector_field_names = NULL,\n");
+			fprintf(f, "\t\t\t.n_selector_fields = 0,\n");
+		}
+
+		if (selector_spec->params.member_id_field_name)
+			fprintf(f, "\t\t\t.member_id_field_name = \"%s\",\n",
+				selector_spec->params.member_id_field_name);
+		else
+			fprintf(f, "\t\t\t.member_id_field_name = NULL,\n");
+
+		fprintf(f, "\t\t\t.n_groups_max = %u,\n", selector_spec->params.n_groups_max);
+
+		fprintf(f, "\t\t\t.n_members_per_group_max = %u,\n",
+			selector_spec->params.n_members_per_group_max);
+
+		fprintf(f, "\t\t},\n");
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* learner. */
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+		uint32_t j;
+
+		/* field_names. */
+		if (learner_spec->params.field_names && learner_spec->params.n_fields) {
+			fprintf(f, "static const char *learner_%s_field_names[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_fields; j++) {
+				const char *field_name = learner_spec->params.field_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, field_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_names. */
+		if (learner_spec->params.action_names && learner_spec->params.n_actions) {
+			fprintf(f, "static const char *learner_%s_action_names[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				const char *action_name = learner_spec->params.action_names[j];
+
+				fprintf(f, "\t[%d] = \"%s\",\n", j, action_name);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_table_entries. */
+		if (learner_spec->params.action_is_for_table_entries &&
+		    learner_spec->params.n_actions) {
+			fprintf(f, "static int learner_%s_action_is_for_table_entries[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				int value = learner_spec->params.action_is_for_table_entries[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* action_is_for_default_entry. */
+		if (learner_spec->params.action_is_for_default_entry &&
+		    learner_spec->params.n_actions) {
+			fprintf(f, "static int learner_%s_action_is_for_default_entry[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->params.n_actions; j++) {
+				int value = learner_spec->params.action_is_for_default_entry[j];
+
+				fprintf(f, "\t[%d] = %d,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+
+		/* timeout. */
+		if (learner_spec->timeout && learner_spec->n_timeouts) {
+			fprintf(f, "static uint32_t learner_%s_timeout[] = {\n",
+				learner_spec->name);
+
+			for (j = 0; j < learner_spec->n_timeouts; j++) {
+				uint32_t value = learner_spec->timeout[j];
+
+				fprintf(f, "\t[%d] = %u,\n", j, value);
+			}
+
+			fprintf(f, "};\n\n");
+		}
+	}
+
+	fprintf(f, "static struct learner_spec learners[] = {\n");
+
+	for (i = 0; i < s->n_learners; i++) {
+		struct learner_spec *learner_spec = &s->learners[i];
+
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t\t.name = \"%s\",\n", learner_spec->name);
+
+		fprintf(f, "\t\t.params = {\n");
+
+		if (learner_spec->params.field_names && learner_spec->params.n_fields) {
+			fprintf(f, "\t\t\t.field_names = learner_%s_field_names,\n",
+				learner_spec->name);
+			fprintf(f, "\t\t\t.n_fields = "
+				"sizeof(learner_%s_field_names) / "
+				"sizeof(learner_%s_field_names[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		} else {
+			fprintf(f, "\t\t\t.field_names = NULL,\n");
+			fprintf(f, "\t\t\t.n_fields = 0,\n");
+		}
+
+		if (learner_spec->params.action_names && learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_names = learner_%s_action_names,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_names = NULL,\n");
+
+		if (learner_spec->params.action_is_for_table_entries &&
+		    learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_table_entries = "
+				"learner_%s_action_is_for_table_entries,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_table_entries = NULL,\n");
+
+		if (learner_spec->params.action_is_for_default_entry &&
+		    learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.action_is_for_default_entry = "
+				"learner_%s_action_is_for_default_entry,\n",
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.action_is_for_default_entry = NULL,\n");
+
+		if (learner_spec->params.action_names && learner_spec->params.n_actions)
+			fprintf(f, "\t\t\t.n_actions = "
+				"sizeof(learner_%s_action_names) / sizeof(learner_%s_action_names[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		else
+			fprintf(f, "\t\t\t.n_actions = NULL,\n");
+
+		if (learner_spec->params.default_action_name)
+			fprintf(f, "\t\t\t.default_action_name = \"%s\",\n",
+				learner_spec->params.default_action_name);
+		else
+			fprintf(f, "\t\t\t.default_action_name = NULL,\n");
+
+		if (learner_spec->params.default_action_args)
+			fprintf(f, "\t\t\t.default_action_args = \"%s\",\n",
+				learner_spec->params.default_action_args);
+		else
+			fprintf(f, "\t\t\t.default_action_args = NULL,\n");
+
+		fprintf(f, "\t\t\t.default_action_is_const = %d,\n",
+			learner_spec->params.default_action_is_const);
+
+		fprintf(f, "\t\t},\n");
+
+		fprintf(f, "\t\t.size = %u,\n", learner_spec->size);
+
+		if (learner_spec->timeout && learner_spec->n_timeouts) {
+			fprintf(f, "\t\t.timeout = learner_%s_timeout,\n", learner_spec->name);
+			fprintf(f, "\t\t\t.n_timeouts = "
+				"sizeof(learner_%s_timeout) / sizeof(learner_%s_timeout[0]),\n",
+				learner_spec->name,
+				learner_spec->name);
+		} else {
+			fprintf(f, "\t\t.timeout = NULL,\n");
+			fprintf(f, "\t\t\t.n_timeouts = 0,\n");
+		}
+
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* apply. */
+	for (i = 0; i < s->n_apply; i++) {
+		struct apply_spec *apply_spec = &s->apply[i];
+		uint32_t j;
+
+		fprintf(f, "static const char *apply%u_initial_instructions[] = {\n", i);
+
+		for (j = 0; j < apply_spec->n_instructions; j++) {
+			const char *instr = apply_spec->instructions[j];
+
+			fprintf(f, "\t[%d] = \"%s\",\n", j, instr);
+		}
+
+		fprintf(f, "};\n\n");
+	}
+
+	fprintf(f, "static struct apply_spec apply[] = {\n");
+
+	for (i = 0; i < s->n_apply; i++) {
+		fprintf(f, "\t[%d] = {\n", i);
+		fprintf(f, "\t.instructions = apply%u_initial_instructions,\n", i);
+		fprintf(f, "\t.n_instructions = "
+			"sizeof(apply%u_initial_instructions) / "
+			"sizeof(apply%u_initial_instructions[0]),\n",
+			i,
+			i);
+		fprintf(f, "\t},\n");
+	}
+
+	fprintf(f, "};\n\n");
+
+	/* pipeline. */
+	fprintf(f, "struct pipeline_spec pipeline_spec = {\n");
+	fprintf(f, "\t.extobjs = extobjs,\n");
+	fprintf(f, "\t.structs = structs,\n");
+	fprintf(f, "\t.headers = headers,\n");
+	fprintf(f, "\t.metadata = metadata,\n");
+	fprintf(f, "\t.actions = actions,\n");
+	fprintf(f, "\t.tables = tables,\n");
+	fprintf(f, "\t.selectors = selectors,\n");
+	fprintf(f, "\t.learners = learners,\n");
+	fprintf(f, "\t.regarrays = regarrays,\n");
+	fprintf(f, "\t.metarrays = metarrays,\n");
+	fprintf(f, "\t.apply = apply,\n");
+	fprintf(f, "\t.n_extobjs = sizeof(extobjs) / sizeof(extobjs[0]),\n");
+	fprintf(f, "\t.n_structs = sizeof(structs) / sizeof(structs[0]),\n");
+	fprintf(f, "\t.n_headers = sizeof(headers) / sizeof(headers[0]),\n");
+	fprintf(f, "\t.n_metadata = sizeof(metadata) / sizeof(metadata[0]),\n");
+	fprintf(f, "\t.n_actions = sizeof(actions) / sizeof(actions[0]),\n");
+	fprintf(f, "\t.n_tables = sizeof(tables) / sizeof(tables[0]),\n");
+	fprintf(f, "\t.n_selectors = sizeof(selectors) / sizeof(selectors[0]),\n");
+	fprintf(f, "\t.n_learners = sizeof(learners) / sizeof(learners[0]),\n");
+	fprintf(f, "\t.n_regarrays = sizeof(regarrays) / sizeof(regarrays[0]),\n");
+	fprintf(f, "\t.n_metarrays = sizeof(metarrays) / sizeof(metarrays[0]),\n");
+	fprintf(f, "\t.n_apply = sizeof(apply) / sizeof(apply[0]),\n");
+	fprintf(f, "};\n");
+}
+
 struct pipeline_spec *
 pipeline_spec_parse(FILE *spec,
 		    uint32_t *err_line,
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 4f3a0b5958..707b99ba09 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -206,6 +206,11 @@ struct pipeline_spec {
 
 void
 pipeline_spec_free(struct pipeline_spec *s);
+
+void
+pipeline_spec_codegen(FILE *f,
+		      struct pipeline_spec *s);
+
 struct pipeline_spec *
 pipeline_spec_parse(FILE *spec,
 		    uint32_t *err_line,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V5 06/17] pipeline: add support for pipeline I/O specification
  2022-07-27 23:01       ` [PATCH V5 01/17] pipeline: add pipeline name Cristian Dumitrescu
                           ` (3 preceding siblings ...)
  2022-07-27 23:01         ` [PATCH V5 05/17] pipeline: generate the code for pipeline specification structure Cristian Dumitrescu
@ 2022-07-27 23:01         ` Cristian Dumitrescu
  2022-07-27 23:01         ` [PATCH V5 07/17] pipeline: add API for pipeline code generation Cristian Dumitrescu
                           ` (11 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 23:01 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add specification data structure and API for the pipeline I/O ports
and related pipeline configuration such as packet mirroring.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline_spec.c | 852 +++++++++++++++++++++++++++
 lib/pipeline/rte_swx_pipeline_spec.h |  58 ++
 2 files changed, 910 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index bf21fe17ba..f34803793d 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -9,6 +9,12 @@
 #include <errno.h>
 
 #include <rte_common.h>
+#include <rte_mempool.h>
+
+#include <rte_swx_port_ethdev.h>
+#include <rte_swx_port_ring.h>
+#include <rte_swx_port_source_sink.h>
+#include <rte_swx_port_fd.h>
 
 #include "rte_swx_pipeline_spec.h"
 
@@ -3566,3 +3572,849 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
 	pipeline_spec_free(s);
 	return status;
 }
+
+static void
+port_in_params_free(void *params, const char *port_type)
+{
+	uintptr_t dev_name;
+
+	if (!params || !port_type)
+		return;
+
+	if (!strcmp(port_type, "ethdev")) {
+		struct rte_swx_port_ethdev_reader_params *p = params;
+
+		dev_name = (uintptr_t)p->dev_name;
+	} else if (!strcmp(port_type, "ring")) {
+		struct rte_swx_port_ring_reader_params *p = params;
+
+		dev_name = (uintptr_t)p->name;
+	} else if (!strcmp(port_type, "source")) {
+		struct rte_swx_port_source_params *p = params;
+
+		dev_name = (uintptr_t)p->file_name;
+	} else
+		dev_name = (uintptr_t)NULL;
+
+	free((void *)dev_name);
+	free(params);
+}
+
+static void
+port_out_params_free(void *params, const char *port_type)
+{
+	uintptr_t dev_name;
+
+	if (!params || !port_type)
+		return;
+
+	if (!strcmp(port_type, "ethdev")) {
+		struct rte_swx_port_ethdev_writer_params *p = params;
+
+		dev_name = (uintptr_t)p->dev_name;
+	} else if (!strcmp(port_type, "ring")) {
+		struct rte_swx_port_ring_writer_params *p = params;
+
+		dev_name = (uintptr_t)p->name;
+	} else if (!strcmp(port_type, "sink")) {
+		struct rte_swx_port_sink_params *p = params;
+
+		dev_name = (uintptr_t)p->file_name;
+	} else
+		dev_name = (uintptr_t)NULL;
+
+	free((void *)dev_name);
+	free(params);
+}
+
+void
+pipeline_iospec_free(struct pipeline_iospec *s)
+{
+	uint32_t i;
+
+	if (!s)
+		return;
+
+	/* Input ports. */
+	for (i = 0; i < s->n_ports_in; i++) {
+		uintptr_t name = (uintptr_t)s->port_in_type[i];
+
+		port_in_params_free(s->port_in_params[i], s->port_in_type[i]);
+		free((void *)name);
+	}
+
+	free(s->port_in_type);
+	free(s->port_in_params);
+
+	/* Output ports. */
+	for (i = 0; i < s->n_ports_out; i++) {
+		uintptr_t name = (uintptr_t)s->port_out_type[i];
+
+		port_out_params_free(s->port_out_params[i], s->port_out_type[i]);
+		free((void *)name);
+	}
+
+	free(s->port_out_type);
+	free(s->port_out_params);
+
+	free(s);
+}
+
+static int
+mirroring_parse(struct rte_swx_pipeline_mirroring_params *p,
+		char **tokens,
+		uint32_t n_tokens,
+		const char **err_msg)
+{
+	char *token;
+
+	if ((n_tokens != 4) || strcmp(tokens[0], "slots") || strcmp(tokens[2], "sessions")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return -EINVAL;
+	}
+
+	/* <n_slots>. */
+	token = tokens[1];
+	p->n_slots = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <n_slots> parameter.";
+		return -EINVAL;
+	}
+
+	/* <n_sessions>. */
+	token = tokens[3];
+	p->n_sessions = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <n_sessions> parameter.";
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+port_in_ethdev_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_ethdev_reader_params *p = NULL;
+	char *token, *dev_name = NULL;
+	uint32_t queue_id, burst_size;
+
+	if ((n_tokens != 5) || strcmp(tokens[1], "rxq") || strcmp(tokens[3], "bsz")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <queue_id>. */
+	token = tokens[2];
+	queue_id = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <queue_id> parameter.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[4];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	dev_name = strdup(tokens[0]);
+	p = malloc(sizeof(struct rte_swx_port_ethdev_reader_params));
+	if (!dev_name || !p) {
+		free(dev_name);
+		free(p);
+
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->dev_name = dev_name;
+	p->queue_id = queue_id;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_in_ring_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_ring_reader_params *p = NULL;
+	char *token, *name = NULL;
+	uint32_t burst_size;
+
+	if ((n_tokens != 3) || strcmp(tokens[1], "bsz")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[2];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	name = strdup(tokens[0]);
+	p = malloc(sizeof(struct rte_swx_port_ring_reader_params));
+	if (!name || !p) {
+		free(name);
+		free(p);
+
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->name = name;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_in_source_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_source_params *p = NULL;
+	struct rte_mempool *pool = NULL;
+	char *token, *file_name = NULL;
+	uint32_t n_loops, n_pkts_max;
+
+	if ((n_tokens != 8) ||
+	    strcmp(tokens[0], "mempool") ||
+	    strcmp(tokens[2], "file") ||
+	    strcmp(tokens[4], "loop") ||
+	    strcmp(tokens[6], "packets")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <mempool_name>. */
+	pool = rte_mempool_lookup(tokens[1]);
+	if (!pool) {
+		if (err_msg)
+			*err_msg = "Invalid <mempool_name> parameter.";
+		return NULL;
+	}
+
+	/* <n_loops>. */
+	token = tokens[5];
+	n_loops = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <n_loops> parameter.";
+		return NULL;
+	}
+
+	/* <n_pkts_max>. */
+	token = tokens[7];
+	n_pkts_max = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <n_pkts_max> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	file_name = strdup(tokens[3]);
+	p = malloc(sizeof(struct rte_swx_port_source_params));
+	if (!file_name || !p) {
+		free(file_name);
+		free(p);
+
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->pool = pool;
+	p->file_name = file_name;
+	p->n_loops = n_loops;
+	p->n_pkts_max = n_pkts_max;
+
+	return p;
+}
+
+static void *
+port_in_fd_parse(char **tokens,
+		 uint32_t n_tokens,
+		 const char **err_msg)
+{
+	struct rte_swx_port_fd_reader_params *p = NULL;
+	struct rte_mempool *mempool = NULL;
+	char *token;
+	uint32_t mtu, burst_size;
+	int fd;
+
+	if ((n_tokens != 7) ||
+	    strcmp(tokens[1], "mtu") ||
+	    strcmp(tokens[3], "mempool") ||
+	    strcmp(tokens[5], "bsz")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <file_descriptor>. */
+	token = tokens[0];
+	fd = strtol(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <file_descriptor> parameter.";
+		return NULL;
+	}
+
+	/* <mtu>. */
+	token = tokens[2];
+	mtu = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <mtu> parameter.";
+		return NULL;
+	}
+
+	/* <mempool_name>. */
+	mempool = rte_mempool_lookup(tokens[4]);
+	if (!mempool) {
+		if (err_msg)
+			*err_msg = "Invalid <mempool_name> parameter.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[6];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	p = malloc(sizeof(struct rte_swx_port_fd_reader_params));
+	if (!p) {
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->fd = fd;
+	p->mtu = mtu;
+	p->mempool = mempool;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_out_ethdev_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_ethdev_writer_params *p = NULL;
+	char *token, *dev_name = NULL;
+	uint32_t queue_id, burst_size;
+
+	if ((n_tokens != 5) || strcmp(tokens[1], "txq") || strcmp(tokens[3], "bsz")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <queue_id>. */
+	token = tokens[2];
+	queue_id = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <queue_id> parameter.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[4];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	dev_name = strdup(tokens[0]);
+	p = malloc(sizeof(struct rte_swx_port_ethdev_writer_params));
+	if (!dev_name || !p) {
+		free(dev_name);
+		free(p);
+
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->dev_name = dev_name;
+	p->queue_id = queue_id;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_out_ring_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_ring_writer_params *p = NULL;
+	char *token, *name = NULL;
+	uint32_t burst_size;
+
+	if ((n_tokens != 3) || strcmp(tokens[1], "bsz")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[2];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	name = strdup(tokens[0]);
+	p = malloc(sizeof(struct rte_swx_port_ring_writer_params));
+	if (!name || !p) {
+		free(name);
+		free(p);
+
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->name = name;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+static void *
+port_out_sink_parse(char **tokens, uint32_t n_tokens, const char **err_msg)
+{
+	struct rte_swx_port_sink_params *p = NULL;
+	char *file_name = NULL;
+	int file_name_valid = 0;
+
+	if ((n_tokens != 2) || strcmp(tokens[0], "file")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	if (strcmp(tokens[1], "none")) {
+		file_name_valid = 1;
+		file_name = strdup(tokens[1]);
+	}
+
+	p = malloc(sizeof(struct rte_swx_port_ring_writer_params));
+	if ((file_name_valid && !file_name) || !p) {
+		free(file_name);
+		free(p);
+
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->file_name = file_name;
+
+	return p;
+}
+
+static void *
+port_out_fd_parse(char **tokens,
+		  uint32_t n_tokens,
+		  const char **err_msg)
+{
+	struct rte_swx_port_fd_writer_params *p = NULL;
+	char *token;
+	uint32_t burst_size;
+	int fd;
+
+	if ((n_tokens != 3) || strcmp(tokens[1], "bsz")) {
+		if (err_msg)
+			*err_msg = "Invalid statement.";
+		return NULL;
+	}
+
+	/* <file_descriptor>. */
+	token = tokens[0];
+	fd = strtol(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <file_descriptor> parameter.";
+		return NULL;
+	}
+
+	/* <burst_size>. */
+	token = tokens[2];
+	burst_size = strtoul(token, &token, 0);
+	if (token[0]) {
+		if (err_msg)
+			*err_msg = "Invalid <burst_size> parameter.";
+		return NULL;
+	}
+
+	/* Memory allocation. */
+	p = malloc(sizeof(struct rte_swx_port_fd_writer_params));
+	if (!p) {
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		return NULL;
+	}
+
+	/* Initialization. */
+	p->fd = fd;
+	p->burst_size = burst_size;
+
+	return p;
+}
+
+struct pipeline_iospec *
+pipeline_iospec_parse(FILE *spec,
+		      uint32_t *err_line,
+		      const char **err_msg)
+{
+	struct pipeline_iospec *s = NULL;
+	uint32_t n_lines = 0;
+
+	/* Check the input arguments. */
+	if (!spec) {
+		if (err_line)
+			*err_line = n_lines;
+		if (err_msg)
+			*err_msg = "Invalid input argument.";
+		goto error;
+	}
+
+	/* Memory allocation. */
+	s = calloc(sizeof(struct pipeline_iospec), 1);
+	if (!s) {
+		if (err_line)
+			*err_line = n_lines;
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		goto error;
+	}
+
+	/* Initialize with the default values. */
+	s->mirroring_params.n_slots = RTE_SWX_PACKET_MIRRORING_SLOTS_DEFAULT;
+	s->mirroring_params.n_sessions = RTE_SWX_PACKET_MIRRORING_SESSIONS_DEFAULT;
+
+	for (n_lines = 1; ; n_lines++) {
+		char line[MAX_LINE_LENGTH];
+		char *tokens[MAX_TOKENS], *ptr = line;
+		uint32_t n_tokens = 0;
+
+		/* Read next line. */
+		if (!fgets(line, sizeof(line), spec))
+			break;
+
+		/* Parse the line into tokens. */
+		for ( ; ; ) {
+			char *token;
+
+			/* Get token. */
+			token = strtok_r(ptr, " \f\n\r\t\v", &ptr);
+			if (!token)
+				break;
+
+			/* Handle comments. */
+			if ((token[0] == '#') ||
+			    (token[0] == ';') ||
+			    ((token[0] == '/') && (token[1] == '/'))) {
+				break;
+			}
+
+			/* Handle excessively long lines. */
+			if (n_tokens >= RTE_DIM(tokens)) {
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Too many tokens.";
+				goto error;
+			}
+
+			/* Handle excessively long tokens. */
+			if (strnlen(token, RTE_SWX_NAME_SIZE) >=
+			    RTE_SWX_NAME_SIZE) {
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Token too big.";
+				goto error;
+			}
+
+			/* Save token. */
+			tokens[n_tokens] = token;
+			n_tokens++;
+		}
+
+		/* Handle empty lines. */
+		if (!n_tokens)
+			continue;
+
+		/* mirroring. */
+		if ((n_tokens >= 1) && !strcmp(tokens[0], "mirroring")) {
+			int status = 0;
+
+			status = mirroring_parse(&s->mirroring_params,
+						 &tokens[1],
+						 n_tokens - 1,
+						 err_msg);
+			if (status) {
+				if (err_line)
+					*err_line = n_lines;
+				goto error;
+			}
+
+			continue;
+		}
+
+		/* port in. */
+		if ((n_tokens >= 4) && !strcmp(tokens[0], "port") && !strcmp(tokens[1], "in")) {
+			char *token = tokens[2];
+			uint32_t *new_id = NULL;
+			const char **new_type = NULL, *port_type = NULL;
+			void **new_params = NULL, *p = NULL;
+			uint32_t port_id;
+
+			/* <port_id>. */
+			port_id = strtoul(token, &token, 0);
+			if (token[0]) {
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Invalid port ID.";
+				goto error;
+			}
+
+			/* <port_type>. */
+			if (!strcmp(tokens[3], "ethdev"))
+				p = port_in_ethdev_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "ring"))
+				p = port_in_ring_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "source"))
+				p = port_in_source_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "fd"))
+				p = port_in_fd_parse(&tokens[4], n_tokens - 4, err_msg);
+			else {
+				p = NULL;
+				if (err_msg)
+					*err_msg = "Invalid port type.";
+			}
+
+			if (!p) {
+				if (err_line)
+					*err_line = n_lines;
+				goto error;
+			}
+
+			/* New port. */
+			port_type = strdup(tokens[3]);
+			new_id = realloc(s->port_in_id,
+					 (s->n_ports_in + 1) * sizeof(uint32_t));
+			new_type = realloc(s->port_in_type,
+					   (s->n_ports_in + 1) * sizeof(char *));
+			new_params = realloc(s->port_in_params,
+					     (s->n_ports_in + 1) * sizeof(void *));
+			if (!port_type || !new_id || !new_type || !new_params) {
+				uintptr_t pt = (uintptr_t)port_type;
+
+				port_in_params_free(p, tokens[3]);
+				free((void *)pt);
+				free(new_id);
+				free(new_type);
+				free(new_params);
+
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Memory allocation failed.";
+				goto error;
+			}
+
+			s->port_in_id = new_id;
+			s->port_in_type = new_type;
+			s->port_in_params = new_params;
+
+			s->port_in_id[s->n_ports_in] = port_id;
+			s->port_in_type[s->n_ports_in] = port_type;
+			s->port_in_params[s->n_ports_in] = p;
+			s->n_ports_in++;
+
+			continue;
+		}
+
+		/* port out. */
+		if ((n_tokens >= 4) && !strcmp(tokens[0], "port") && !strcmp(tokens[1], "out")) {
+			char *token = tokens[2];
+			uint32_t *new_id = NULL;
+			const char **new_type = NULL, *port_type = NULL;
+			void **new_params = NULL, *p = NULL;
+			uint32_t port_id;
+
+			/* <port_id>. */
+			port_id = strtoul(token, &token, 0);
+			if (token[0]) {
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Invalid port ID.";
+				goto error;
+			}
+
+			/* <port_type>. */
+			if (!strcmp(tokens[3], "ethdev"))
+				p = port_out_ethdev_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "ring"))
+				p = port_out_ring_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "sink"))
+				p = port_out_sink_parse(&tokens[4], n_tokens - 4, err_msg);
+			else if (!strcmp(tokens[3], "fd"))
+				p = port_out_fd_parse(&tokens[4], n_tokens - 4, err_msg);
+			else {
+				p = NULL;
+				if (err_msg)
+					*err_msg = "Invalid port type.";
+			}
+
+			if (!p) {
+				if (err_line)
+					*err_line = n_lines;
+				goto error;
+			}
+
+			/* New port. */
+			port_type = strdup(tokens[3]);
+			new_id = realloc(s->port_out_id,
+					 (s->n_ports_out + 1) * sizeof(uint32_t));
+			new_type = realloc(s->port_out_type,
+					   (s->n_ports_out + 1) * sizeof(char *));
+			new_params = realloc(s->port_out_params,
+					     (s->n_ports_out + 1) * sizeof(void *));
+			if (!port_type || !new_id || !new_type || !new_params) {
+				uintptr_t pt = (uintptr_t)port_type;
+
+				port_out_params_free(p, tokens[3]);
+				free((void *)pt);
+				free(new_id);
+				free(new_type);
+				free(new_params);
+
+				if (err_line)
+					*err_line = n_lines;
+				if (err_msg)
+					*err_msg = "Memory allocation failed.";
+				goto error;
+			}
+
+			s->port_out_id = new_id;
+			s->port_out_type = new_type;
+			s->port_out_params = new_params;
+
+			s->port_out_id[s->n_ports_out] = port_id;
+			s->port_out_type[s->n_ports_out] = port_type;
+			s->port_out_params[s->n_ports_out] = p;
+			s->n_ports_out++;
+
+			continue;
+		}
+
+		/* Anything else. */
+		if (err_line)
+			*err_line = n_lines;
+		if (err_msg)
+			*err_msg = "Unknown I/O statement.";
+		goto error;
+	}
+
+	return s;
+
+error:
+	pipeline_iospec_free(s);
+
+	return NULL;
+}
+
+int
+pipeline_iospec_configure(struct rte_swx_pipeline *p,
+			  struct pipeline_iospec *s,
+			  const char **err_msg)
+{
+	uint32_t i;
+	int status = 0;
+
+	/* Check input arguments. */
+	if (!p || !s) {
+		if (err_msg)
+			*err_msg = "Invalid input argument";
+		return -EINVAL;
+	}
+
+	/* Mirroring. */
+	status = rte_swx_pipeline_mirroring_config(p, &s->mirroring_params);
+	if (status) {
+		if (err_msg)
+			*err_msg = "Pipeline mirroring configuration error.";
+		return status;
+	}
+
+	/* Input ports. */
+	for (i = 0; i < s->n_ports_in; i++) {
+		status = rte_swx_pipeline_port_in_config(p,
+							 i,
+							 s->port_in_type[i],
+							 s->port_in_params[i]);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Pipeline input port configuration error.";
+			return status;
+		}
+	}
+
+	/* Output ports. */
+	for (i = 0; i < s->n_ports_out; i++) {
+		status = rte_swx_pipeline_port_out_config(p,
+							  i,
+							  s->port_out_type[i],
+							  s->port_out_params[i]);
+		if (status) {
+			if (err_msg)
+				*err_msg = "Pipeline output port configuration error.";
+			return status;
+		}
+	}
+
+	return 0;
+}
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 707b99ba09..62ac4ecfc4 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -1,6 +1,13 @@
 /* SPDX-License-Identifier: BSD-3-Clause
  * Copyright(c) 2022 Intel Corporation
  */
+#ifndef __INCLUDE_RTE_SWX_PIPELINE_SPEC_H__
+#define __INCLUDE_RTE_SWX_PIPELINE_SPEC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include <stdint.h>
 #include <stdio.h>
 
@@ -204,6 +211,38 @@ struct pipeline_spec {
 	uint32_t n_apply;
 };
 
+/*
+ * Mirroring:
+ *      mirroring slots <n_slots> sessions <n_sessions>
+ *
+ * Input ports:
+ *      port in <port_id> ethdev <ethdev_name> rxq <queue_id> bsz <burst_size>
+ *      port in <port_id> ring <ring_name> bsz <burst_size>
+ *      port in <port_id> source mempool <mempool_name> file <file_name> loop <n_loops>
+ *                               packets <n_pkts_max>
+ *      port in <port_id> fd <file_descriptor> mtu <mtu> mempool <mempool_name> bsz <burst_size>
+ *
+ * Output ports:
+ *      port out <port_id> ethdev <ethdev_name> txq <queue_id> bsz <burst_size>
+ *      port out <port_id> ring <ring_name> bsz <burst_size>
+ *      port out <port_id> sink file <file_name> | none
+ *      port out <port_id> fd <file_descriptor> bsz <burst_size>
+ */
+struct pipeline_iospec {
+	struct rte_swx_pipeline_mirroring_params mirroring_params;
+
+	uint32_t *port_in_id;
+	const char **port_in_type;
+	void **port_in_params;
+
+	uint32_t *port_out_id;
+	const char **port_out_type;
+	void **port_out_params;
+
+	uint32_t n_ports_in;
+	uint32_t n_ports_out;
+};
+
 void
 pipeline_spec_free(struct pipeline_spec *s);
 
@@ -220,3 +259,22 @@ int
 pipeline_spec_configure(struct rte_swx_pipeline *p,
 			struct pipeline_spec *s,
 			const char **err_msg);
+
+void
+pipeline_iospec_free(struct pipeline_iospec *s);
+
+struct pipeline_iospec *
+pipeline_iospec_parse(FILE *spec,
+		      uint32_t *err_line,
+		      const char **err_msg);
+
+int
+pipeline_iospec_configure(struct rte_swx_pipeline *p,
+			  struct pipeline_iospec *s,
+			  const char **err_msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V5 07/17] pipeline: add API for pipeline code generation
  2022-07-27 23:01       ` [PATCH V5 01/17] pipeline: add pipeline name Cristian Dumitrescu
                           ` (4 preceding siblings ...)
  2022-07-27 23:01         ` [PATCH V5 06/17] pipeline: add support for pipeline I/O specification Cristian Dumitrescu
@ 2022-07-27 23:01         ` Cristian Dumitrescu
  2022-07-27 23:01         ` [PATCH V5 08/17] pipeline: add API for shared library-based pipeline build Cristian Dumitrescu
                           ` (10 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 23:01 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Previously, the C code generation for the pipeline was hidden under
the hood; now, we make this an explicit API operation. Besides the
functions for the pipeline actions and the pipeline instructions,
the generated C source code now includes the pipeline specification
structure required for the pipeline configuration operations.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 lib/pipeline/rte_swx_pipeline.c | 94 +++++++++++++++++++++++++++++++++
 lib/pipeline/rte_swx_pipeline.h | 25 +++++++++
 lib/pipeline/version.map        |  1 +
 3 files changed, 120 insertions(+)

diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index c8ccded4f8..dd5f7107fa 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -20,6 +20,7 @@
 #include <rte_swx_table_wm.h>
 
 #include "rte_swx_pipeline_internal.h"
+#include "rte_swx_pipeline_spec.h"
 
 #define CHECK(condition, err_code)                                             \
 do {                                                                           \
@@ -13581,3 +13582,96 @@ pipeline_compile(struct rte_swx_pipeline *p)
 
 	return status;
 }
+
+int
+rte_swx_pipeline_codegen(FILE *spec_file,
+			 FILE *code_file,
+			 uint32_t *err_line,
+			 const char **err_msg)
+
+{
+	struct rte_swx_pipeline *p = NULL;
+	struct pipeline_spec *s = NULL;
+	struct instruction_group_list *igl = NULL;
+	struct action *a;
+	int status = 0;
+
+	/* Check input arguments. */
+	if (!spec_file || !code_file) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Invalid input argument.";
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Pipeline configuration. */
+	s = pipeline_spec_parse(spec_file, err_line, err_msg);
+	if (!s) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	status = rte_swx_pipeline_config(&p, NULL, 0);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Pipeline configuration error.";
+		goto free;
+	}
+
+	status = pipeline_spec_configure(p, s, err_msg);
+	if (status) {
+		if (err_line)
+			*err_line = 0;
+		goto free;
+	}
+
+	/*
+	 * Pipeline code generation.
+	 */
+
+	/* Instruction Group List (IGL) computation: the pipeline configuration must be done first,
+	 * but there is no need for the pipeline build to be done as well.
+	 */
+	igl = instruction_group_list_create(p);
+	if (!igl) {
+		if (err_line)
+			*err_line = 0;
+		if (err_msg)
+			*err_msg = "Memory allocation failed.";
+		status = -ENOMEM;
+		goto free;
+	}
+
+	/* Header file inclusion. */
+	fprintf(code_file, "#include \"rte_swx_pipeline_internal.h\"\n");
+	fprintf(code_file, "#include \"rte_swx_pipeline_spec.h\"\n\n");
+
+	/* Code generation for the pipeline specification. */
+	pipeline_spec_codegen(code_file, s);
+	fprintf(code_file, "\n");
+
+	/* Code generation for the action instructions. */
+	TAILQ_FOREACH(a, &p->actions, node) {
+		fprintf(code_file, "/**\n * Action %s\n */\n\n", a->name);
+
+		action_data_codegen(a, code_file);
+		fprintf(code_file, "\n");
+
+		action_instr_codegen(a, code_file);
+		fprintf(code_file, "\n");
+	}
+
+	/* Code generation for the pipeline instructions. */
+	instruction_group_list_codegen(igl, p, code_file);
+
+free:
+	instruction_group_list_free(igl);
+	rte_swx_pipeline_free(p);
+	pipeline_spec_free(s);
+
+	return status;
+}
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index ef50a0fa70..724607b87c 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -957,6 +957,31 @@ __rte_experimental
 int
 rte_swx_pipeline_build(struct rte_swx_pipeline *p);
 
+/**
+ * Pipeline C code generate based on input specification file
+ *
+ * @param[in] spec_file
+ *   Pipeline specification file (.spec) provided as input.
+ * @param[in] code_file
+ *   Pipeline C language file (.c) to be generated.
+ * @param[out] err_line
+ *   In case of error and non-NULL, the line number within the *spec* file where
+ *   the error occurred. The first line number in the file is 1.
+ * @param[out] err_msg
+ *   In case of error and non-NULL, the error message.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Resource with the same name already exists.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_codegen(FILE *spec_file,
+			 FILE *code_file,
+			 uint32_t *err_line,
+			 const char **err_msg);
+
 /**
  * Pipeline build from specification file
  *
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 50029aadcf..8d95005a5b 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -148,5 +148,6 @@ EXPERIMENTAL {
 
 	#added in 22.11
 	rte_swx_ctl_pipeline_find;
+	rte_swx_pipeline_codegen;
 	rte_swx_pipeline_find;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V5 08/17] pipeline: add API for shared library-based pipeline build
  2022-07-27 23:01       ` [PATCH V5 01/17] pipeline: add pipeline name Cristian Dumitrescu
                           ` (5 preceding siblings ...)
  2022-07-27 23:01         ` [PATCH V5 07/17] pipeline: add API for pipeline code generation Cristian Dumitrescu
@ 2022-07-27 23:01         ` Cristian Dumitrescu
  2022-07-27 23:01         ` [PATCH V5 09/17] examples/pipeline: add CLI command for pipeline code generation Cristian Dumitrescu
                           ` (9 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 23:01 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Previously, the pipeline build operation was done based on the
specification file (typically produced by the P4 compiler), then the C
code with optimized functions for the pipeline actions and
instructions was generated, built into a shared object library, loaded
and installed into the pipeline in a completely hardcoded and
non-customizable way.

Now, this process is split into three explicit stages:
i) code generation (specification file -> C file);
ii) code build (C file -> shared object library);
iii) code installation (library load into the pipeline).

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c              |  85 ++++---
 lib/pipeline/rte_swx_pipeline.c      | 319 +++++++++++----------------
 lib/pipeline/rte_swx_pipeline.h      |  38 ++--
 lib/pipeline/rte_swx_pipeline_spec.c |  51 -----
 lib/pipeline/version.map             |   2 +-
 5 files changed, 208 insertions(+), 287 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index ad553f19ab..f0285675b3 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -984,55 +984,88 @@ cmd_pipeline_port_out(char **tokens,
 }
 
 static const char cmd_pipeline_build_help[] =
-"pipeline <pipeline_name> build <spec_file>\n";
+"pipeline <pipeline_name> build lib <lib_file> io <iospec_file> numa <numa_node>\n";
 
 static void
 cmd_pipeline_build(char **tokens,
 	uint32_t n_tokens,
 	char *out,
 	size_t out_size,
-	void *obj)
+	void *obj __rte_unused)
 {
-	struct pipeline *p = NULL;
-	FILE *spec = NULL;
-	uint32_t err_line;
-	const char *err_msg;
-	int status;
+	struct rte_swx_pipeline *p = NULL;
+	struct rte_swx_ctl_pipeline *ctl = NULL;
+	char *pipeline_name, *lib_file_name, *iospec_file_name;
+	FILE *iospec_file = NULL;
+	uint32_t numa_node = 0;
+	int status = 0;
 
-	if (n_tokens != 4) {
+	/* Parsing. */
+	if (n_tokens != 9) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
 
-	p = pipeline_find(obj, tokens[1]);
-	if (!p || p->ctl) {
-		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+	pipeline_name = tokens[1];
+
+	if (strcmp(tokens[2], "build")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "build");
 		return;
 	}
 
-	spec = fopen(tokens[3], "r");
-	if (!spec) {
-		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
+	if (strcmp(tokens[3], "lib")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "lib");
 		return;
 	}
 
-	status = rte_swx_pipeline_build_from_spec(p->p,
-		spec,
-		&err_line,
-		&err_msg);
-	fclose(spec);
-	if (status) {
-		snprintf(out, out_size, "Error %d at line %u: %s\n.",
-			status, err_line, err_msg);
+	lib_file_name = tokens[4];
+
+	if (strcmp(tokens[5], "io")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "io");
 		return;
 	}
 
-	p->ctl = rte_swx_ctl_pipeline_create(p->p);
-	if (!p->ctl) {
-		snprintf(out, out_size, "Pipeline control create failed.");
-		rte_swx_pipeline_free(p->p);
+	iospec_file_name = tokens[6];
+
+	if (strcmp(tokens[7], "numa")) {
+		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
 		return;
 	}
+
+	if (parser_read_uint32(&numa_node, tokens[8])) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
+		return;
+	}
+
+	/* I/O spec file open. */
+	iospec_file = fopen(iospec_file_name, "r");
+	if (!iospec_file) {
+		snprintf(out, out_size, "Cannot open file \"%s\".\n", iospec_file_name);
+		return;
+	}
+
+	status = rte_swx_pipeline_build_from_lib(&p,
+						 pipeline_name,
+						 lib_file_name,
+						 iospec_file,
+						 (int)numa_node);
+	if (status) {
+		snprintf(out, out_size, "Pipeline build failed (%d).", status);
+		goto free;
+	}
+
+	ctl = rte_swx_ctl_pipeline_create(p);
+	if (!ctl) {
+		snprintf(out, out_size, "Pipeline control create failed.");
+		goto free;
+	}
+
+free:
+	if (status)
+		rte_swx_pipeline_free(p);
+
+	if (iospec_file)
+		fclose(iospec_file);
 }
 
 static void
diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index dd5f7107fa..12e156b00b 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -9911,9 +9911,6 @@ rte_swx_pipeline_instructions_config(struct rte_swx_pipeline *p,
 	return 0;
 }
 
-static int
-pipeline_compile(struct rte_swx_pipeline *p);
-
 int
 rte_swx_pipeline_build(struct rte_swx_pipeline *p)
 {
@@ -10003,8 +10000,6 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
 
 	p->build_done = 1;
 
-	pipeline_compile(p);
-
 	return 0;
 
 error:
@@ -13327,160 +13322,6 @@ instruction_group_list_custom_instructions_count(struct instruction_group_list *
 	return n_custom_instr;
 }
 
-static int
-pipeline_codegen(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
-{
-	struct action *a;
-	FILE *f = NULL;
-
-	/* Create the .c file. */
-	f = fopen("/tmp/pipeline.c", "w");
-	if (!f)
-		return -EIO;
-
-	/* Include the .h file. */
-	fprintf(f, "#include \"rte_swx_pipeline_internal.h\"\n");
-
-	/* Add the code for each action. */
-	TAILQ_FOREACH(a, &p->actions, node) {
-		fprintf(f, "/**\n * Action %s\n */\n\n", a->name);
-
-		action_data_codegen(a, f);
-
-		fprintf(f, "\n");
-
-		action_instr_codegen(a, f);
-
-		fprintf(f, "\n");
-	}
-
-	/* Add the pipeline code. */
-	instruction_group_list_codegen(igl, p, f);
-
-	/* Close the .c file. */
-	fclose(f);
-
-	return 0;
-}
-
-#ifndef RTE_SWX_PIPELINE_CMD_MAX_SIZE
-#define RTE_SWX_PIPELINE_CMD_MAX_SIZE 4096
-#endif
-
-static int
-pipeline_libload(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
-{
-	struct action *a;
-	struct instruction_group *g;
-	char *dir_in, *buffer = NULL;
-	const char *dir_out;
-	int status = 0;
-
-	/* Get the environment variables. */
-	dir_in = getenv("RTE_INSTALL_DIR");
-	if (!dir_in) {
-		status = -EINVAL;
-		goto free;
-	}
-
-	dir_out = "/tmp";
-
-	/* Memory allocation for the command buffer. */
-	buffer = malloc(RTE_SWX_PIPELINE_CMD_MAX_SIZE);
-	if (!buffer) {
-		status = -ENOMEM;
-		goto free;
-	}
-
-	snprintf(buffer,
-		 RTE_SWX_PIPELINE_CMD_MAX_SIZE,
-		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s/pipeline.o %s/pipeline.c "
-		 "-I %s/lib/pipeline "
-		 "-I %s/lib/eal/include "
-		 "-I %s/lib/eal/x86/include "
-		 "-I %s/lib/eal/include/generic "
-		 "-I %s/lib/meter "
-		 "-I %s/lib/port "
-		 "-I %s/lib/table "
-		 "-I %s/lib/pipeline "
-		 "-I %s/config "
-		 "-I %s/build "
-		 "-I %s/lib/eal/linux/include "
-		 ">%s/pipeline.log 2>&1 "
-		 "&& "
-		 "gcc -shared %s/pipeline.o -o %s/libpipeline.so "
-		 ">>%s/pipeline.log 2>&1",
-		 dir_out,
-		 dir_out,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_in,
-		 dir_out,
-		 dir_out,
-		 dir_out,
-		 dir_out);
-
-	/* Build the shared object library. */
-	status = system(buffer);
-	if (status)
-		goto free;
-
-	/* Open library. */
-	snprintf(buffer,
-		 RTE_SWX_PIPELINE_CMD_MAX_SIZE,
-		 "%s/libpipeline.so",
-		 dir_out);
-
-	p->lib = dlopen(buffer, RTLD_LAZY);
-	if (!p->lib) {
-		status = -EIO;
-		goto free;
-	}
-
-	/* Get the action function symbols. */
-	TAILQ_FOREACH(a, &p->actions, node) {
-		snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "action_%s_run", a->name);
-
-		p->action_funcs[a->id] = dlsym(p->lib, buffer);
-		if (!p->action_funcs[a->id]) {
-			status = -EINVAL;
-			goto free;
-		}
-	}
-
-	/* Get the pipeline function symbols. */
-	TAILQ_FOREACH(g, igl, node) {
-		if (g->first_instr_id == g->last_instr_id)
-			continue;
-
-		snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "pipeline_func_%u", g->group_id);
-
-		g->func = dlsym(p->lib, buffer);
-		if (!g->func) {
-			status = -EINVAL;
-			goto free;
-		}
-	}
-
-free:
-	if (status && p->lib) {
-		dlclose(p->lib);
-		p->lib = NULL;
-	}
-
-	free(buffer);
-
-	return status;
-}
-
 static int
 pipeline_adjust_check(struct rte_swx_pipeline *p __rte_unused,
 		      struct instruction_group_list *igl)
@@ -13548,41 +13389,6 @@ pipeline_adjust(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
 	instr_jmp_resolve(p->instructions, p->instruction_data, p->n_instructions);
 }
 
-static int
-pipeline_compile(struct rte_swx_pipeline *p)
-{
-	struct instruction_group_list *igl = NULL;
-	int status = 0;
-
-	igl = instruction_group_list_create(p);
-	if (!igl) {
-		status = -ENOMEM;
-		goto free;
-	}
-
-	/* Code generation. */
-	status = pipeline_codegen(p, igl);
-	if (status)
-		goto free;
-
-	/* Build and load the shared object library. */
-	status = pipeline_libload(p, igl);
-	if (status)
-		goto free;
-
-	/* Adjust instructions. */
-	status = pipeline_adjust_check(p, igl);
-	if (status)
-		goto free;
-
-	pipeline_adjust(p, igl);
-
-free:
-	instruction_group_list_free(igl);
-
-	return status;
-}
-
 int
 rte_swx_pipeline_codegen(FILE *spec_file,
 			 FILE *code_file,
@@ -13675,3 +13481,128 @@ rte_swx_pipeline_codegen(FILE *spec_file,
 
 	return status;
 }
+
+int
+rte_swx_pipeline_build_from_lib(struct rte_swx_pipeline **pipeline,
+				const char *name,
+				const char *lib_file_name,
+				FILE *iospec_file,
+				int numa_node)
+{
+	struct rte_swx_pipeline *p = NULL;
+	void *lib = NULL;
+	struct pipeline_iospec *sio = NULL;
+	struct pipeline_spec *s = NULL;
+	struct instruction_group_list *igl = NULL;
+	struct action *a;
+	struct instruction_group *g;
+	int status = 0;
+
+	/* Check input arguments. */
+	if (!pipeline ||
+	    !name ||
+	    !name[0] ||
+	    !lib_file_name ||
+	    !lib_file_name[0] ||
+	    !iospec_file) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Open the library. */
+	lib = dlopen(lib_file_name, RTLD_LAZY);
+	if (!lib) {
+		status = -EIO;
+		goto free;
+	}
+
+	/* Get the pipeline specification structures. */
+	s = dlsym(lib, "pipeline_spec");
+	if (!s) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	sio = pipeline_iospec_parse(iospec_file, NULL, NULL);
+	if (!sio) {
+		status = -EINVAL;
+		goto free;
+	}
+
+	/* Pipeline configuration based on the specification structures. */
+	status = rte_swx_pipeline_config(&p, name, numa_node);
+	if (status)
+		goto free;
+
+	status = pipeline_iospec_configure(p, sio, NULL);
+	if (status)
+		goto free;
+
+	status = pipeline_spec_configure(p, s, NULL);
+	if (status)
+		goto free;
+
+	/* Pipeline build. */
+	status = rte_swx_pipeline_build(p);
+	if (status)
+		goto free;
+
+	/* Action instructions. */
+	TAILQ_FOREACH(a, &p->actions, node) {
+		char name[RTE_SWX_NAME_SIZE * 2];
+
+		snprintf(name, sizeof(name), "action_%s_run", a->name);
+
+		p->action_funcs[a->id] = dlsym(lib, name);
+		if (!p->action_funcs[a->id]) {
+			status = -EINVAL;
+			goto free;
+		}
+	}
+
+	/* Pipeline instructions. */
+	igl = instruction_group_list_create(p);
+	if (!igl) {
+		status = -ENOMEM;
+		goto free;
+	}
+
+	TAILQ_FOREACH(g, igl, node) {
+		char name[RTE_SWX_NAME_SIZE * 2];
+
+		if (g->first_instr_id == g->last_instr_id)
+			continue;
+
+		snprintf(name, sizeof(name), "pipeline_func_%u", g->group_id);
+
+		g->func = dlsym(lib, name);
+		if (!g->func) {
+			status = -EINVAL;
+			goto free;
+		}
+	}
+
+	status = pipeline_adjust_check(p, igl);
+	if (status)
+		goto free;
+
+	pipeline_adjust(p, igl);
+
+	p->lib = lib;
+
+	*pipeline = p;
+
+free:
+	instruction_group_list_free(igl);
+
+	pipeline_iospec_free(sio);
+
+	if (status) {
+		rte_swx_pipeline_free(p);
+
+		if (lib)
+			dlclose(lib);
+	}
+
+	return status;
+}
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index 724607b87c..9c629d4118 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -983,30 +983,38 @@ rte_swx_pipeline_codegen(FILE *spec_file,
 			 const char **err_msg);
 
 /**
- * Pipeline build from specification file
+ * Pipeline build from shared object library
  *
- * @param[in] p
- *   Pipeline handle.
- * @param[in] spec
- *   Pipeline specification file.
- * @param[out] err_line
- *   In case of error and non-NULL, the line number within the *spec* file where
- *   the error occurred. The first line number in the file is 1.
- * @param[out] err_msg
- *   In case of error and non-NULL, the error message.
+ * The shared object library must be built from the C language source code file
+ * previously generated by the rte_swx_pipeline_codegen() API function.
+ *
+ * The pipeline I/O specification file defines the I/O ports of the pipeline.
+ *
+ * @param[out] p
+ *   Pipeline handle. Must point to valid memory. Contains valid pipeline handle
+ *   when the function returns successfully.
+ * @param[in] name
+ *   Pipeline unique name.
+ * @param[in] lib_file_name
+ *   Shared object library file name.
+ * @param[in] iospec_file
+ *   Pipeline I/O specification file.
+ * @param[in] numa_node
+ *   Non-Uniform Memory Access (NUMA) node.
  * @return
  *   0 on success or the following error codes otherwise:
  *   -EINVAL: Invalid argument;
  *   -ENOMEM: Not enough space/cannot allocate memory;
- *   -EEXIST: Resource with the same name already exists;
+ *   -EEXIST: Pipeline with this name already exists;
  *   -ENODEV: Extern object or table creation error.
  */
 __rte_experimental
 int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec,
-				 uint32_t *err_line,
-				 const char **err_msg);
+rte_swx_pipeline_build_from_lib(struct rte_swx_pipeline **p,
+				const char *name,
+				const char *lib_file_name,
+				FILE *iospec_file,
+				int numa_node);
 
 /**
  * Pipeline run
diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index f34803793d..3c16daf7de 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -3522,57 +3522,6 @@ pipeline_spec_configure(struct rte_swx_pipeline *p,
 	return 0;
 }
 
-int
-rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
-				 FILE *spec_file,
-				 uint32_t *err_line,
-				 const char **err_msg)
-{
-	struct pipeline_spec *s = NULL;
-	int status = 0;
-
-	/* Check the input arguments. */
-	if (!p || !spec_file) {
-		if (err_line)
-			*err_line = 0;
-		if (err_msg)
-			*err_msg = "Invalid input argument.";
-		status = -EINVAL;
-		goto error;
-	}
-
-	/* Spec file parse. */
-	s = pipeline_spec_parse(spec_file, err_line, err_msg);
-	if (!s) {
-		status = -EINVAL;
-		goto error;
-	}
-
-	/* Pipeline configure. */
-	status = pipeline_spec_configure(p, s, err_msg);
-	if (status) {
-		if (err_line)
-			*err_line = 0;
-		goto error;
-	}
-
-	/* Pipeline build. */
-	status = rte_swx_pipeline_build(p);
-	if (status) {
-		if (err_line)
-			*err_line = 0;
-		if (err_msg)
-			*err_msg = "Pipeline build error.";
-		goto error;
-	}
-
-	return 0;
-
-error:
-	pipeline_spec_free(s);
-	return status;
-}
-
 static void
 port_in_params_free(void *params, const char *port_type)
 {
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 8d95005a5b..16806e6802 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -82,7 +82,6 @@ EXPERIMENTAL {
 	rte_swx_ctl_table_ops_get;
 	rte_swx_pipeline_action_config;
 	rte_swx_pipeline_build;
-	rte_swx_pipeline_build_from_spec;
 	rte_swx_pipeline_config;
 	rte_swx_pipeline_extern_func_register;
 	rte_swx_pipeline_extern_object_config;
@@ -148,6 +147,7 @@ EXPERIMENTAL {
 
 	#added in 22.11
 	rte_swx_ctl_pipeline_find;
+	rte_swx_pipeline_build_from_lib;
 	rte_swx_pipeline_codegen;
 	rte_swx_pipeline_find;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V5 09/17] examples/pipeline: add CLI command for pipeline code generation
  2022-07-27 23:01       ` [PATCH V5 01/17] pipeline: add pipeline name Cristian Dumitrescu
                           ` (6 preceding siblings ...)
  2022-07-27 23:01         ` [PATCH V5 08/17] pipeline: add API for shared library-based pipeline build Cristian Dumitrescu
@ 2022-07-27 23:01         ` Cristian Dumitrescu
  2022-07-27 23:01         ` [PATCH V5 10/17] examples/pipeline: add CLI command for shared library build Cristian Dumitrescu
                           ` (8 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 23:01 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add CLI command for the pipeline code generation operation.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 61 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index f0285675b3..2b38977be1 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -983,6 +983,53 @@ cmd_pipeline_port_out(char **tokens,
 	}
 }
 
+static const char cmd_pipeline_codegen_help[] =
+"pipeline codegen <spec_file> <code_file>\n";
+
+static void
+cmd_pipeline_codegen(char **tokens,
+	uint32_t n_tokens,
+	char *out,
+	size_t out_size,
+	void *obj __rte_unused)
+{
+	FILE *spec_file = NULL;
+	FILE *code_file = NULL;
+	uint32_t err_line;
+	const char *err_msg;
+	int status;
+
+	if (n_tokens != 4) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		return;
+	}
+
+	spec_file = fopen(tokens[2], "r");
+	if (!spec_file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", tokens[2]);
+		return;
+	}
+
+	code_file = fopen(tokens[3], "w");
+	if (!code_file) {
+		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
+		return;
+	}
+
+	status = rte_swx_pipeline_codegen(spec_file,
+					  code_file,
+					  &err_line,
+					  &err_msg);
+
+	fclose(spec_file);
+	fclose(code_file);
+
+	if (status) {
+		snprintf(out, out_size, "Error %d at line %u: %s\n.",
+			status, err_line, err_msg);
+		return;
+	}
+}
 static const char cmd_pipeline_build_help[] =
 "pipeline <pipeline_name> build lib <lib_file> io <iospec_file> numa <numa_node>\n";
 
@@ -3009,6 +3056,7 @@ cmd_help(char **tokens,
 			"\tpipeline create\n"
 			"\tpipeline port in\n"
 			"\tpipeline port out\n"
+			"\tpipeline codegen\n"
 			"\tpipeline build\n"
 			"\tpipeline table add\n"
 			"\tpipeline table delete\n"
@@ -3078,6 +3126,12 @@ cmd_help(char **tokens,
 		}
 	}
 
+	if ((strcmp(tokens[0], "pipeline") == 0) &&
+		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
@@ -3356,6 +3410,13 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			return;
 		}
 
+		if ((n_tokens >= 3) &&
+			(strcmp(tokens[1], "codegen") == 0)) {
+			cmd_pipeline_codegen(tokens, n_tokens, out, out_size,
+				obj);
+			return;
+		}
+
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[2], "build") == 0)) {
 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 90+ messages in thread

* [PATCH V5 10/17] examples/pipeline: add CLI command for shared library build
  2022-07-27 23:01       ` [PATCH V5 01/17] pipeline: add pipeline name Cristian Dumitrescu
                           ` (7 preceding siblings ...)
  2022-07-27 23:01         ` [PATCH V5 09/17] examples/pipeline: add CLI command for pipeline code generation Cristian Dumitrescu
@ 2022-07-27 23:01         ` Cristian Dumitrescu
  2022-07-27 23:01         ` [PATCH V5 11/17] examples/pipeline: remove the obsolete pipeline create CLI command Cristian Dumitrescu
                           ` (7 subsequent siblings)
  16 siblings, 0 replies; 90+ messages in thread
From: Cristian Dumitrescu @ 2022-07-27 23:01 UTC (permalink / raw)
  To: dev; +Cc: Kamalakannan R .

Add CLI command for the shared object library build operation.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R. <kamalakannan.r@intel.com>
---
 examples/pipeline/cli.c | 157 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 153 insertions(+), 4 deletions(-)

diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 2b38977be1..28cf8d4178 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -6,6 +6,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
@@ -25,6 +26,10 @@
 #define CMD_MAX_TOKENS     256
 #endif
 
+#ifndef MAX_LINE_SIZE
+#define MAX_LINE_SIZE 2048
+#endif
+
 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
@@ -1030,6 +1035,140 @@ cmd_pipeline_codegen(char **tokens,
 		return;
 	}
 }
+
+static const char cmd_pipeline_libbuild_help[] =
+"pipeline libbuild <code_file> <lib_file>\n";
+
+static void
+cmd_pipeline_libbuild(char **tokens,
+	uint32_t n_tokens,
+	char *out,
+	size_t out_size,
+	void *obj __rte_unused)
+{
+	char *code_file, *lib_file, *obj_file = NULL, *log_file = NULL;
+	char *install_dir, *cwd = NULL, *buffer = NULL;
+	size_t length;
+	int status = 0;
+
+	if (n_tokens != 4) {
+		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+		goto free;
+	}
+
+	install_dir = getenv("RTE_INSTALL_DIR");
+	if (!install_dir) {
+		cwd = malloc(MAX_LINE_SIZE);
+		if (!cwd) {
+			snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+			goto free;
+		}
+
+		install_dir = getcwd(cwd, MAX_LINE_SIZE);
+		if (!install_dir) {
+			snprintf(out, out_size, "Error: Path too long.\n");
+			goto free;
+		}
+	}
+
+	snprintf(out, out_size, "Using DPDK source code from \"%s\".\n", install_dir);
+	out_size -= strlen(out);
+	out += strlen(out);
+
+	code_file = tokens[2];
+	length = strnlen(code_file, MAX_LINE_SIZE);
+	if ((length < 3) ||
+	    (code_file[length - 2] != '.') ||
+	    (code_file[length - 1] != 'c')) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "code_file");
+		goto free;
+	}
+
+	lib_file = tokens[3];
+	length = strnlen(lib_file, MAX_LINE_SIZE);
+	if ((length < 4) ||
+	    (lib_file[length - 3] != '.') ||
+	    (lib_file[length - 2] != 's') ||
+	    (lib_file[length - 1] != 'o')) {
+		snprintf(out, out_size, MSG_ARG_INVALID, "lib_file");
+		goto free;
+	}
+
+	obj_file = malloc(length);
+	log_file = malloc(length + 2);
+	if (!obj_file || !log_file) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		goto free;
+	}
+
+	memcpy(obj_file, lib_file, length - 2);
+	obj_file[length - 2] = 'o';
+	obj_file[length - 1] = 0;
+
+	memcpy(log_file, lib_file, length - 2);
+	log_file[length - 2] = 'l';
+	log_file[length - 1] = 'o';
+	log_file[length] = 'g';
+	log_file[length + 1] = 0;
+
+	buffer = malloc(MAX_LINE_SIZE);
+	if (!buffer) {
+		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+		return;
+	}
+
+	snprintf(buffer,
+		 MAX_LINE_SIZE,
+		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s %s "
+		 "-I %s/lib/pipeline "
+		 "-I %s/lib/eal/include "
+		 "-I %s/lib/eal/x86/include "
+		 "-I %s/lib/eal/include/generic "
+		 "-I %s/lib/meter "
+		 "-I %s/lib/port "
+		 "-I %s/lib/table "
+		 "-I %s/lib/pipeline "
+		 "-I %s/config "
+		 "-I %s/build "
+		 "-I %s/lib/eal/linux/include "
+		 ">%s 2>&1 "
+		 "&& "
+		 "gcc -shared %s -o %s "
+		 ">>%s 2>&1",
+		 obj_file,
+		 code_file,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 install_dir,
+		 log_file,
+		 obj_file,
+		 lib_file,
+		 log_file);
+
+	status = system(buffer);
+	if (status) {
+		snprintf(out,
+			 out_size,
+			 "Library build failed, see file \"%s\" for details.\n",
+			 log_file);
+		goto free;
+	}
+
+free:
+	free(cwd);
+	free(obj_file);
+	free(log_file);
+	free(buffer);
+}
+
 static const char cmd_pipeline_build_help[] =
 "pipeline <pipeline_name> build lib <lib_file> io <iospec_file> numa <numa_node>\n";
 
@@ -1127,10 +1266,6 @@ table_entry_free(struct rte_swx_table_entry *entry)
 	free(entry);
 }
 
-#ifndef MAX_LINE_SIZE
-#define MAX_LINE_SIZE 2048
-#endif
-
 static int
 pipeline_table_entries_add(struct rte_swx_ctl_pipeline *p,
 			   const char *table_name,
@@ -3057,6 +3192,7 @@ cmd_help(char **tokens,
 			"\tpipeline port in\n"
 			"\tpipeline port out\n"
 			"\tpipeline codegen\n"
+			"\tpipeline libbuild\n"
 			"\tpipeline build\n"
 			"\tpipeline table add\n"
 			"\tpipeline table delete\n"
@@ -3132,6 +3268,12 @@ cmd_help(char **tokens,
 		return;
 	}
 
+	if ((strcmp(tokens[0], "pipeline") == 0) &&
+		(n_tokens == 2) && (strcmp(tokens[1], "libbuild") == 0)) {
+		snprintf(out, out_size, "\n%s\n", cmd_pipeline_libbuild_help);
+		return;
+	}
+
 	if ((strcmp(tokens[0], "pipeline") == 0) &&
 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
@@ -3417,6 +3559,13 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
 			return;
 		}
 
+		if ((n_tokens >= 3) &&
+			(strcmp(tokens[1], "libbuild") == 0)) {
+			cmd_pipeline_libbuild(tokens, n_tokens, out, out_size,
+				obj);
+			return;
+		}
+
 		if ((n_tokens >= 3) &&
 			(strcmp(tokens[2], "build") == 0)) {
 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
-- 
2.34.1


^ permalink raw reply	[