All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] port: add KNI interface support 1. add KNI port type to the packet framework 2. add KNI support to the IP Pipeline sample Application
@ 2016-05-27  5:07 WeiJie Zhuang
  2016-05-28 11:25 ` [PATCH] port: add kni interface support WeiJie Zhuang
  0 siblings, 1 reply; 22+ messages in thread
From: WeiJie Zhuang @ 2016-05-27  5:07 UTC (permalink / raw)
  To: dev; +Cc: WeiJie Zhuang

Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
---
 doc/api/doxy-api-index.md           |   3 +-
 examples/ip_pipeline/Makefile       |   6 +-
 examples/ip_pipeline/app.h          |  74 ++++++++
 examples/ip_pipeline/config/kni.cfg |  12 ++
 examples/ip_pipeline/config_check.c |  30 ++++
 examples/ip_pipeline/config_parse.c | 129 ++++++++++++++
 examples/ip_pipeline/init.c         |  70 ++++++++
 examples/ip_pipeline/kni/kni.c      |  73 ++++++++
 examples/ip_pipeline/kni/kni.h      |  15 ++
 examples/ip_pipeline/pipeline_be.h  |  13 ++
 examples/ip_pipeline/thread.c       |   8 +
 lib/librte_port/Makefile            |   7 +
 lib/librte_port/rte_port_kni.c      | 325 ++++++++++++++++++++++++++++++++++++
 lib/librte_port/rte_port_kni.h      |  81 +++++++++
 14 files changed, 843 insertions(+), 3 deletions(-)
 create mode 100644 examples/ip_pipeline/config/kni.cfg
 create mode 100644 examples/ip_pipeline/kni/kni.c
 create mode 100644 examples/ip_pipeline/kni/kni.h
 create mode 100644 lib/librte_port/rte_port_kni.c
 create mode 100644 lib/librte_port/rte_port_kni.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index f626386..06802c2 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -118,7 +118,8 @@ There are many libraries, so their headers may be grouped by topics:
     [frag]             (@ref rte_port_frag.h),
     [reass]            (@ref rte_port_ras.h),
     [sched]            (@ref rte_port_sched.h),
-    [src/sink]         (@ref rte_port_source_sink.h)
+    [src/sink]         (@ref rte_port_source_sink.h),
+    [kni]              (@ref rte_port_kni.h)
   * [table]            (@ref rte_table.h):
     [lpm IPv4]         (@ref rte_table_lpm.h),
     [lpm IPv6]         (@ref rte_table_lpm_ipv6.h),
diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index 10fe1ba..848c2aa 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -43,9 +43,10 @@ include $(RTE_SDK)/mk/rte.vars.mk
 # binary name
 APP = ip_pipeline
 
+VPATH += $(SRCDIR)/kni
 VPATH += $(SRCDIR)/pipeline
 
-INC += $(wildcard *.h) $(wildcard pipeline/*.h)
+INC += $(wildcard *.h) $(wildcard pipeline/*.h) $(wildcard kni/*.h)
 
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) := main.c
@@ -56,6 +57,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread_fe.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += kni.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_fe.c
@@ -72,7 +74,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_flow_actions.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing.c
 
-CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline
+CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline -I$(SRCDIR)/kni
 CFLAGS += -O3
 CFLAGS += $(WERROR_FLAGS) -Wno-error=unused-function -Wno-error=unused-variable
 
diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
index e775024..a45976c 100644
--- a/examples/ip_pipeline/app.h
+++ b/examples/ip_pipeline/app.h
@@ -44,7 +44,9 @@
 #include <cmdline_parse.h>
 
 #include <rte_ethdev.h>
+#include <rte_kni.h>
 
+#include "kni.h"
 #include "cpu_core_map.h"
 #include "pipeline.h"
 
@@ -99,6 +101,18 @@ struct app_pktq_hwq_out_params {
 	struct rte_eth_txconf conf;
 };
 
+struct app_kni_params {
+	char *name;
+	uint32_t parsed;
+
+	uint32_t socket_id;
+	uint32_t core_id;
+	uint32_t hyper_th_id;
+
+	uint32_t mempool_id;
+	uint32_t burst;
+};
+
 struct app_pktq_swq_params {
 	char *name;
 	uint32_t parsed;
@@ -172,6 +186,7 @@ enum app_pktq_in_type {
 	APP_PKTQ_IN_SWQ,
 	APP_PKTQ_IN_TM,
 	APP_PKTQ_IN_SOURCE,
+	APP_PKTQ_IN_KNI,
 };
 
 struct app_pktq_in_params {
@@ -184,6 +199,7 @@ enum app_pktq_out_type {
 	APP_PKTQ_OUT_SWQ,
 	APP_PKTQ_OUT_TM,
 	APP_PKTQ_OUT_SINK,
+	APP_PKTQ_OUT_KNI,
 };
 
 struct app_pktq_out_params {
@@ -434,6 +450,10 @@ struct app_eal_params {
 #define APP_THREAD_HEADROOM_STATS_COLLECT        1
 #endif
 
+#ifndef APP_MAX_KNI
+#define APP_MAX_KNI                              8
+#endif
+
 struct app_params {
 	/* Config */
 	char app_name[APP_APPNAME_SIZE];
@@ -457,6 +477,7 @@ struct app_params {
 	struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
 	struct app_msgq_params msgq_params[APP_MAX_MSGQ];
 	struct app_pipeline_params pipeline_params[APP_MAX_PIPELINES];
+	struct app_kni_params kni_params[APP_MAX_KNI];
 
 	uint32_t n_mempools;
 	uint32_t n_links;
@@ -468,6 +489,7 @@ struct app_params {
 	uint32_t n_pktq_sink;
 	uint32_t n_msgq;
 	uint32_t n_pipelines;
+	uint32_t n_kni;
 
 	/* Init */
 	char *eal_argv[1 + APP_EAL_ARGC];
@@ -480,6 +502,7 @@ struct app_params {
 	struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
 	struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
 	struct app_thread_data thread_data[APP_MAX_THREADS];
+	struct rte_kni *kni[APP_MAX_KNI];
 	cmdline_parse_ctx_t cmds[APP_MAX_CMDS + 1];
 
 	int eal_argc;
@@ -738,6 +761,31 @@ app_msgq_get_readers(struct app_params *app, struct app_msgq_params *msgq)
 }
 
 static inline uint32_t
+app_kni_get_readers(struct app_params *app, struct app_kni_params *kni)
+{
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+								   RTE_DIM(app->pipeline_params));
+	uint32_t n_readers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq = &p->pktq_in[j];
+
+			if ((pktq->type == APP_PKTQ_IN_KNI) &&
+				(pktq->id == pos))
+				n_readers++;
+		}
+	}
+
+	return n_readers;
+}
+
+static inline uint32_t
 app_txq_get_writers(struct app_params *app, struct app_pktq_hwq_out_params *txq)
 {
 	uint32_t pos = txq - app->hwq_out_params;
@@ -863,6 +911,32 @@ app_msgq_get_writers(struct app_params *app, struct app_msgq_params *msgq)
 	return n_writers;
 }
 
+static inline uint32_t
+app_kni_get_writers(struct app_params *app, struct app_kni_params *kni)
+{
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+								   RTE_DIM(app->pipeline_params));
+	uint32_t n_writers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
+									  RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq = &p->pktq_out[j];
+
+			if ((pktq->type == APP_PKTQ_OUT_KNI) &&
+				(pktq->id == pos))
+				n_writers++;
+		}
+	}
+
+	return n_writers;
+}
+
 static inline struct app_link_params *
 app_get_link_for_rxq(struct app_params *app, struct app_pktq_hwq_in_params *p)
 {
diff --git a/examples/ip_pipeline/config/kni.cfg b/examples/ip_pipeline/config/kni.cfg
new file mode 100644
index 0000000..30466b0
--- /dev/null
+++ b/examples/ip_pipeline/config/kni.cfg
@@ -0,0 +1,12 @@
+[KNI0]
+core = 2
+
+[PIPELINE0]
+type = MASTER
+core = 0
+
+[PIPELINE1]
+type = PASS-THROUGH
+core = 1
+pktq_in = RXQ0.0 KNI0
+pktq_out = KNI0 TXQ0.0
diff --git a/examples/ip_pipeline/config_check.c b/examples/ip_pipeline/config_check.c
index fd9ff49..2981e65 100644
--- a/examples/ip_pipeline/config_check.c
+++ b/examples/ip_pipeline/config_check.c
@@ -426,6 +426,35 @@ check_pipelines(struct app_params *app)
 	}
 }
 
+static void
+check_kni(struct app_params *app) {
+	uint32_t i;
+	uint32_t port_id;
+
+	for (i = 0; i < app->n_kni; i++) {
+		struct app_kni_params *p = &app->kni_params[i];
+		uint32_t n_readers = app_kni_get_readers(app, p);
+		uint32_t n_writers = app_kni_get_writers(app, p);
+		sscanf(p->name, "KNI%" PRIu32, &port_id);
+
+		APP_CHECK((n_readers != 0),
+				  "%s has no reader\n", p->name);
+
+		if (n_readers > 1)
+			APP_LOG(app, LOW, "%s has more than one reader", p->name);
+
+		APP_CHECK((n_writers != 0),
+				  "%s has no writer\n", p->name);
+
+		if (n_writers > 1)
+			APP_LOG(app, LOW, "%s has more than one writer", p->name);
+
+		APP_CHECK(port_id < app->n_links,
+				  "kni %s is not associated with a valid link\n",
+				  p->name);
+	}
+}
+
 int
 app_config_check(struct app_params *app)
 {
@@ -439,6 +468,7 @@ app_config_check(struct app_params *app)
 	check_sinks(app);
 	check_msgqs(app);
 	check_pipelines(app);
+	check_kni(app);
 
 	return 0;
 }
diff --git a/examples/ip_pipeline/config_parse.c b/examples/ip_pipeline/config_parse.c
index e5efd03..7e706e4 100644
--- a/examples/ip_pipeline/config_parse.c
+++ b/examples/ip_pipeline/config_parse.c
@@ -209,6 +209,15 @@ struct app_pipeline_params default_pipeline_params = {
 	.n_args = 0,
 };
 
+struct app_kni_params default_kni_params = {
+	.parsed = 0,
+	.socket_id = 0,
+	.core_id = 0,
+	.hyper_th_id = 0,
+	.mempool_id = 0,
+	.burst = 32,
+};
+
 static const char app_usage[] =
 	"Usage: %s [-f CONFIG_FILE] [-s SCRIPT_FILE] [-p PORT_MASK] "
 	"[-l LOG_LEVEL] [--preproc PREPROCESSOR] [--preproc-args ARGS]\n"
@@ -1169,6 +1178,9 @@ parse_pipeline_pktq_in(struct app_params *app,
 		} else if (validate_name(name, "SOURCE", 1) == 0) {
 			type = APP_PKTQ_IN_SOURCE;
 			id = APP_PARAM_ADD(app->source_params, name);
+		} else if (validate_name(name, "KNI", 1) == 0) {
+			type = APP_PKTQ_IN_KNI;
+			id = APP_PARAM_ADD(app->kni_params, name);
 		} else
 			return -EINVAL;
 
@@ -1240,6 +1252,9 @@ parse_pipeline_pktq_out(struct app_params *app,
 		} else if (validate_name(name, "SINK", 1) == 0) {
 			type = APP_PKTQ_OUT_SINK;
 			id = APP_PARAM_ADD(app->sink_params, name);
+		} else if (validate_name(name, "KNI", 1) == 0) {
+			type = APP_PKTQ_OUT_KNI;
+			id = APP_PARAM_ADD(app->kni_params, name);
 		} else
 			return -EINVAL;
 
@@ -2459,6 +2474,75 @@ parse_msgq(struct app_params *app,
 	free(entries);
 }
 
+static void
+parse_kni(struct app_params *app,
+		   const char *section_name,
+		   struct rte_cfgfile *cfg)
+{
+	struct app_kni_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->kni_params, section_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->kni_params, section_name);
+
+	param = &app->kni_params[param_idx];
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		if (strcmp(ent->name, "core") == 0) {
+			int status = parse_pipeline_core(
+					&param->socket_id, &param->core_id,
+					&param->hyper_th_id, ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+						ent->name);
+			continue;
+		}
+
+		if (strcmp(ent->name, "mempool") == 0) {
+			int status = validate_name(ent->value,
+									   "MEMPOOL", 1);
+			ssize_t idx;
+
+			PARSE_ERROR((status == 0), section_name,
+						ent->name);
+			idx = APP_PARAM_ADD(app->mempool_params,
+								ent->value);
+			PARSER_PARAM_ADD_CHECK(idx, app->mempool_params,
+								   section_name);
+			param->mempool_id = idx;
+			continue;
+		}
+
+		if (strcmp(ent->name, "burst") == 0) {
+			int status = parser_read_uint32(&param->burst,
+											ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+						ent->name);
+			continue;
+		}
+
+		/* unrecognized */
+		PARSE_ERROR_INVALID(0, section_name, ent->name);
+	}
+
+	param->parsed = 1;
+
+	free(entries);
+}
+
 typedef void (*config_section_load)(struct app_params *p,
 	const char *section_name,
 	struct rte_cfgfile *cfg);
@@ -2483,6 +2567,7 @@ static const struct config_section cfg_file_scheme[] = {
 	{"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
 	{"MSGQ-RSP-PIPELINE", 1, parse_msgq_rsp_pipeline},
 	{"MSGQ", 1, parse_msgq},
+	{"KNI", 1, parse_kni},
 };
 
 static void
@@ -2619,6 +2704,7 @@ app_config_parse(struct app_params *app, const char *file_name)
 	APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
 	APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
 	APP_PARAM_COUNT(app->pipeline_params, app->n_pipelines);
+	APP_PARAM_COUNT(app->kni_params, app->n_kni);
 
 #ifdef RTE_PORT_PCAP
 	for (i = 0; i < (int)app->n_pktq_source; i++) {
@@ -3025,6 +3111,9 @@ save_pipeline_params(struct app_params *app, FILE *f)
 				case APP_PKTQ_IN_SOURCE:
 					name = app->source_params[pp->id].name;
 					break;
+				case APP_PKTQ_IN_KNI:
+					name = app->kni_params[pp->id].name;
+					break;
 				default:
 					APP_CHECK(0, "System error "
 						"occurred while saving "
@@ -3059,6 +3148,9 @@ save_pipeline_params(struct app_params *app, FILE *f)
 				case APP_PKTQ_OUT_SINK:
 					name = app->sink_params[pp->id].name;
 					break;
+				case APP_PKTQ_OUT_KNI:
+					name = app->kni_params[pp->id].name;
+					break;
 				default:
 					APP_CHECK(0, "System error "
 						"occurred while saving "
@@ -3114,6 +3206,37 @@ save_pipeline_params(struct app_params *app, FILE *f)
 	}
 }
 
+static void
+save_kni_params(struct app_params *app, FILE *f)
+{
+	struct app_kni_params *p;
+	size_t i, count;
+
+	count = RTE_DIM(app->kni_params);
+	for (i = 0; i < count; i++) {
+		p = &app->kni_params[i];
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		/* section name */
+		fprintf(f, "[%s]\n", p->name);
+
+		/* core */
+		fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
+				p->socket_id,
+				p->core_id,
+				(p->hyper_th_id) ? "h" : "");
+
+		/* mempool */
+		fprintf(f, "%s = %" PRIu32 "\n", "mempool_id", p->mempool_id);
+
+		/* burst */
+		fprintf(f, "%s = %" PRIu32 "\n", "burst", p->burst);
+
+		fputc('\n', f);
+	}
+}
+
 void
 app_config_save(struct app_params *app, const char *file_name)
 {
@@ -3144,6 +3267,7 @@ app_config_save(struct app_params *app, const char *file_name)
 	save_source_params(app, file);
 	save_sink_params(app, file);
 	save_msgq_params(app, file);
+	save_kni_params(app, file);
 
 	fclose(file);
 	free(name);
@@ -3206,6 +3330,11 @@ app_config_init(struct app_params *app)
 			&default_pipeline_params,
 			sizeof(default_pipeline_params));
 
+	for (i = 0; i < RTE_DIM(app->kni_params); i++)
+		memcpy(&app->kni_params[i],
+			&default_kni_params,
+			sizeof(default_kni_params));
+
 	return 0;
 }
 
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
index 02351f6..d866464 100644
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -89,6 +89,24 @@ app_init_core_mask(struct app_params *app)
 		mask |= 1LLU << lcore_id;
 	}
 
+	for (i = 0; i < app->n_kni; i++) {
+		struct app_kni_params *p = &app->kni_params[i];
+		int lcore_id;
+
+		lcore_id = cpu_core_map_get_lcore_id(app->core_map,
+			p->socket_id,
+			p->core_id,
+			p->hyper_th_id);
+
+		if (lcore_id < 0)
+			rte_panic("Cannot create CPU core mask\n");
+
+		if (mask & 1LLU << lcore_id)
+			rte_panic("KNI interface must use a dedicated lcore\n");
+
+		mask |= 1LLU << lcore_id;
+	}
+
 	app->core_mask = mask;
 	APP_LOG(app, HIGH, "CPU core mask = 0x%016" PRIx64, app->core_mask);
 }
@@ -1236,6 +1254,11 @@ static void app_pipeline_params_get(struct app_params *app,
 					n_bytes_per_pkt;
 			}
 			break;
+		case APP_PKTQ_IN_KNI:
+			out->type = PIPELINE_PORT_IN_KNI;
+			out->params.kni.kni = app->kni[in->id];
+			out->burst_size = app->kni_params[in->id].burst;
+			break;
 		default:
 			break;
 		}
@@ -1374,6 +1397,11 @@ static void app_pipeline_params_get(struct app_params *app,
 				out->params.sink.max_n_pkts = 0;
 			}
 			break;
+		case APP_PKTQ_OUT_KNI:
+			out->type = PIPELINE_PORT_OUT_KNI;
+			out->params.kni.kni = app->kni[in->id];
+			out->params.kni.tx_burst_sz = app->kni_params[in->id].burst;
+			break;
 		default:
 			break;
 		}
@@ -1397,6 +1425,47 @@ static void app_pipeline_params_get(struct app_params *app,
 }
 
 static void
+app_init_kni(struct app_params *app) {
+	uint32_t i;
+	struct rte_kni_conf conf;
+
+	rte_kni_init((unsigned int)app->n_kni);
+
+	for (i = 0; i < app->n_kni; i++) {
+		struct app_kni_params *p_kni = &app->kni_params[i];
+		uint32_t port_id;
+
+		sscanf(p_kni->name, "KNI%" PRIu32, &port_id);
+
+		memset(&conf, 0, sizeof(conf));
+		snprintf(conf.name, RTE_KNI_NAMESIZE,
+				 "vEth%u", port_id);
+		conf.core_id = p_kni->core_id;
+		conf.force_bind = 1;
+
+		conf.group_id = (uint16_t) port_id;
+		conf.mbuf_size = app->mempool_params[p_kni->mempool_id].buffer_size;
+
+		struct rte_kni_ops ops;
+		struct rte_eth_dev_info dev_info;
+
+		memset(&dev_info, 0, sizeof(dev_info));
+		rte_eth_dev_info_get(app->link_params[port_id].pmd_id, &dev_info);
+		conf.addr = dev_info.pci_dev->addr;
+		conf.id = dev_info.pci_dev->id;
+
+		memset(&ops, 0, sizeof(ops));
+		ops.port_id = app->link_params[port_id].pmd_id;
+		ops.change_mtu = kni_change_mtu;
+		ops.config_network_if = kni_config_network_interface;
+
+		app->kni[i] = rte_kni_alloc(app->mempool[p_kni->mempool_id], &conf, &ops);
+		if (!app->kni[i])
+			rte_panic("Fail to create kni for port: %d\n", port_id);
+	}
+}
+
+static void
 app_init_pipelines(struct app_params *app)
 {
 	uint32_t p_id;
@@ -1531,6 +1600,7 @@ int app_init(struct app_params *app)
 	app_init_swq(app);
 	app_init_tm(app);
 	app_init_msgq(app);
+	app_init_kni(app);
 
 	app_pipeline_common_cmd_push(app);
 	app_pipeline_thread_cmd_push(app);
diff --git a/examples/ip_pipeline/kni/kni.c b/examples/ip_pipeline/kni/kni.c
new file mode 100644
index 0000000..bfa596e
--- /dev/null
+++ b/examples/ip_pipeline/kni/kni.c
@@ -0,0 +1,73 @@
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_table_array.h>
+#include <rte_kni.h>
+#include <rte_ethdev.h>
+
+#include "rte_port_kni.h"
+#include "kni.h"
+
+int
+kni_config_network_interface(uint8_t port_id, uint8_t if_up)
+{
+    int ret = 0;
+
+    if (port_id >= rte_eth_dev_count() || port_id >= RTE_MAX_ETHPORTS) {
+        RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n", __func__, port_id);
+        return -EINVAL;
+    }
+
+    RTE_LOG(INFO, PORT, "%s: Configure network interface of %d %s\n",
+            __func__, port_id, if_up ? "up" : "down");
+
+    if (if_up != 0) { /* Configure network interface up */
+        rte_eth_dev_stop(port_id);
+        ret = rte_eth_dev_start(port_id);
+    } else /* Configure network interface down */
+        rte_eth_dev_stop(port_id);
+
+    if (ret < 0)
+        RTE_LOG(ERR, PORT, "%s: Failed to start port %d\n", __func__, port_id);
+
+    return ret;
+}
+
+int
+kni_change_mtu(uint8_t port_id, unsigned new_mtu)
+{
+    int ret;
+
+    if (port_id >= rte_eth_dev_count()) {
+        RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n", __func__, port_id);
+        return -EINVAL;
+    }
+
+    if (new_mtu > ETHER_MAX_LEN) {
+        RTE_LOG(ERR, PORT, "%s: Fail to reconfigure port %d, the new MTU is too big\n", __func__, port_id);
+        return -EINVAL;
+    }
+
+    RTE_LOG(INFO, PORT, "%s: Change MTU of port %d to %u\n", __func__, port_id, new_mtu);
+
+    /* Stop specific port */
+    rte_eth_dev_stop(port_id);
+
+    /* Set new MTU */
+    ret = rte_eth_dev_set_mtu(port_id, new_mtu);
+    if (ret < 0) {
+        RTE_LOG(ERR, PORT, "%s: Fail to reconfigure port %d\n", __func__, port_id);
+        return ret;
+    }
+
+    /* Restart specific port */
+    ret = rte_eth_dev_start(port_id);
+    if (ret < 0) {
+        RTE_LOG(ERR, PORT, "%s: Fail to restart port %d\n", __func__, port_id);
+        return ret;
+    }
+
+    return 0;
+}
+
diff --git a/examples/ip_pipeline/kni/kni.h b/examples/ip_pipeline/kni/kni.h
new file mode 100644
index 0000000..932cccf
--- /dev/null
+++ b/examples/ip_pipeline/kni/kni.h
@@ -0,0 +1,15 @@
+#ifndef __INCLUDE_KNI_H__
+#define __INCLUDE_KNI_H__
+
+#include <rte_common.h>
+
+/* Total octets in ethernet header */
+#define KNI_ENET_HEADER_SIZE    14
+
+/* Total octets in the FCS */
+#define KNI_ENET_FCS_SIZE       4
+
+int kni_config_network_interface(uint8_t port_id, uint8_t if_up);
+int kni_change_mtu(uint8_t port_id, unsigned new_mtu);
+
+#endif
\ No newline at end of file
diff --git a/examples/ip_pipeline/pipeline_be.h b/examples/ip_pipeline/pipeline_be.h
index f4ff262..23f0438 100644
--- a/examples/ip_pipeline/pipeline_be.h
+++ b/examples/ip_pipeline/pipeline_be.h
@@ -40,6 +40,7 @@
 #include <rte_port_ras.h>
 #include <rte_port_sched.h>
 #include <rte_port_source_sink.h>
+#include <rte_port_kni.h>
 #include <rte_pipeline.h>
 
 enum pipeline_port_in_type {
@@ -50,6 +51,7 @@ enum pipeline_port_in_type {
 	PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
 	PIPELINE_PORT_IN_SCHED_READER,
 	PIPELINE_PORT_IN_SOURCE,
+	PIPELINE_PORT_IN_KNI,
 };
 
 struct pipeline_port_in_params {
@@ -62,6 +64,7 @@ struct pipeline_port_in_params {
 		struct rte_port_ring_reader_ipv6_frag_params ring_ipv6_frag;
 		struct rte_port_sched_reader_params sched;
 		struct rte_port_source_params source;
+		struct rte_port_kni_reader_params kni;
 	} params;
 	uint32_t burst_size;
 };
@@ -84,6 +87,8 @@ pipeline_port_in_params_convert(struct pipeline_port_in_params  *p)
 		return (void *) &p->params.sched;
 	case PIPELINE_PORT_IN_SOURCE:
 		return (void *) &p->params.source;
+	case PIPELINE_PORT_IN_KNI:
+		return (void *) &p->params.kni;
 	default:
 		return NULL;
 	}
@@ -107,6 +112,8 @@ pipeline_port_in_params_get_ops(struct pipeline_port_in_params  *p)
 		return &rte_port_sched_reader_ops;
 	case PIPELINE_PORT_IN_SOURCE:
 		return &rte_port_source_ops;
+	case PIPELINE_PORT_IN_KNI:
+		return &rte_port_kni_reader_ops;
 	default:
 		return NULL;
 	}
@@ -123,6 +130,7 @@ enum pipeline_port_out_type {
 	PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
 	PIPELINE_PORT_OUT_SCHED_WRITER,
 	PIPELINE_PORT_OUT_SINK,
+	PIPELINE_PORT_OUT_KNI,
 };
 
 struct pipeline_port_out_params {
@@ -138,6 +146,7 @@ struct pipeline_port_out_params {
 		struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
 		struct rte_port_sched_writer_params sched;
 		struct rte_port_sink_params sink;
+		struct rte_port_kni_writer_params kni;
 	} params;
 };
 
@@ -165,6 +174,8 @@ pipeline_port_out_params_convert(struct pipeline_port_out_params  *p)
 		return (void *) &p->params.sched;
 	case PIPELINE_PORT_OUT_SINK:
 		return (void *) &p->params.sink;
+	case PIPELINE_PORT_OUT_KNI:
+		return (void *) &p->params.kni;
 	default:
 		return NULL;
 	}
@@ -194,6 +205,8 @@ pipeline_port_out_params_get_ops(struct pipeline_port_out_params  *p)
 		return &rte_port_sched_writer_ops;
 	case PIPELINE_PORT_OUT_SINK:
 		return &rte_port_sink_ops;
+	case PIPELINE_PORT_OUT_KNI:
+		return &rte_port_kni_writer_ops;
 	default:
 		return NULL;
 	}
diff --git a/examples/ip_pipeline/thread.c b/examples/ip_pipeline/thread.c
index a0f1f12..f16f642 100644
--- a/examples/ip_pipeline/thread.c
+++ b/examples/ip_pipeline/thread.c
@@ -239,6 +239,14 @@ app_thread(void *arg)
 	uint32_t core_id = rte_lcore_id(), i, j;
 	struct app_thread_data *t = &app->thread_data[core_id];
 
+	for (i = 0; i < app->n_kni; i++) {
+		if (core_id == (uint32_t)cpu_core_map_get_lcore_id(app->core_map,
+			app->kni_params[i].socket_id,
+			app->kni_params[i].core_id,
+			app->kni_params[i].hyper_th_id))
+			return 0;
+	}
+
 	for (i = 0; ; i++) {
 		uint32_t n_regular = RTE_MIN(t->n_regular, RTE_DIM(t->regular));
 		uint32_t n_custom = RTE_MIN(t->n_custom, RTE_DIM(t->custom));
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index d4de5af..f18253d 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -57,6 +57,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
 endif
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
+endif
 
 # install includes
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port.h
@@ -68,6 +71,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_ras.h
 endif
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
+endif
 
 # this lib depends upon:
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
@@ -75,5 +81,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mbuf
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/rte_port_kni.c b/lib/librte_port/rte_port_kni.c
new file mode 100644
index 0000000..0771ed2
--- /dev/null
+++ b/lib/librte_port/rte_port_kni.c
@@ -0,0 +1,325 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_kni.h>
+
+#include "rte_port_kni.h"
+
+/*
+ * Port KNI Reader
+ */
+#ifdef RTE_PORT_STATS_COLLECT
+
+#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
+	port->stats.n_pkts_in += val
+#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
+	port->stats.n_pkts_drop += val
+
+#else
+
+#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
+#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
+
+#endif
+
+struct rte_port_kni_reader {
+	struct rte_port_in_stats stats;
+
+    struct rte_kni *kni;
+};
+
+static void *
+rte_port_kni_reader_create(void *params, int socket_id)
+{
+	struct rte_port_kni_reader_params *conf =
+			(struct rte_port_kni_reader_params *) params;
+	struct rte_port_kni_reader *port;
+
+	/* Check input parameters */
+	if (conf == NULL) {
+		RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	port = rte_zmalloc_socket("PORT", sizeof(*port),
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni = conf->kni;
+
+	return port;
+}
+
+static int
+rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t n_pkts)
+{
+	struct rte_port_kni_reader *p =
+		(struct rte_port_kni_reader *) port;
+	uint16_t rx_pkt_cnt;
+
+	rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
+	RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
+	return rx_pkt_cnt;
+}
+
+static int
+rte_port_kni_reader_free(void *port)
+{
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rte_free(port);
+
+	return 0;
+}
+
+static int rte_port_kni_reader_stats_read(void *port,
+		struct rte_port_in_stats *stats, int clear)
+{
+	struct rte_port_kni_reader *p =
+			(struct rte_port_kni_reader *) port;
+
+	if (stats != NULL)
+		memcpy(stats, &p->stats, sizeof(p->stats));
+
+	if (clear)
+		memset(&p->stats, 0, sizeof(p->stats));
+
+	return 0;
+}
+
+/*
+ * Port KNI Writer
+ */
+#ifdef RTE_PORT_STATS_COLLECT
+
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
+	port->stats.n_pkts_in += val
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
+	port->stats.n_pkts_drop += val
+
+#else
+
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
+
+#endif
+
+struct rte_port_kni_writer {
+	struct rte_port_out_stats stats;
+
+	struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
+	uint32_t tx_burst_sz;
+	uint16_t tx_buf_count;
+	uint64_t bsz_mask;
+	struct rte_kni *kni;
+};
+
+static void *
+rte_port_kni_writer_create(void *params, int socket_id)
+{
+	struct rte_port_kni_writer_params *conf =
+		(struct rte_port_kni_writer_params *) params;
+	struct rte_port_kni_writer *port;
+
+	/* Check input parameters */
+	if ((conf == NULL) ||
+		(conf->tx_burst_sz == 0) ||
+		(conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
+		(!rte_is_power_of_2(conf->tx_burst_sz))) {
+		RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n", __func__);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	port = rte_zmalloc_socket("PORT", sizeof(*port),
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni = conf->kni;
+	port->tx_burst_sz = conf->tx_burst_sz;
+	port->tx_buf_count = 0;
+	port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
+
+	return port;
+}
+
+static inline void
+send_burst(struct rte_port_kni_writer *p)
+{
+	uint32_t nb_tx;
+
+	nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
+			rte_kni_handle_request(p->kni);
+
+	RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p->tx_buf_count - nb_tx);
+	for ( ; nb_tx < p->tx_buf_count; nb_tx++)
+		rte_pktmbuf_free(p->tx_buf[nb_tx]);
+
+	p->tx_buf_count = 0;
+}
+
+static int
+rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt)
+{
+	struct rte_port_kni_writer *p =
+		(struct rte_port_kni_writer *) port;
+
+	p->tx_buf[p->tx_buf_count++] = pkt;
+	RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+	if (p->tx_buf_count >= p->tx_burst_sz)
+		send_burst(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_tx_bulk(void *port,
+		struct rte_mbuf **pkts,
+		uint64_t pkts_mask)
+{
+	struct rte_port_kni_writer *p =
+		(struct rte_port_kni_writer *) port;
+	uint64_t bsz_mask = p->bsz_mask;
+	uint32_t tx_buf_count = p->tx_buf_count;
+	uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
+			((pkts_mask & bsz_mask) ^ bsz_mask);
+
+	if (expr == 0) {
+		uint64_t n_pkts = __builtin_popcountll(pkts_mask);
+		uint32_t n_pkts_ok;
+
+		if (tx_buf_count)
+			send_burst(p);
+
+		RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
+		n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
+
+		RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts - n_pkts_ok);
+		for ( ; n_pkts_ok < n_pkts; n_pkts_ok++) {
+			struct rte_mbuf *pkt = pkts[n_pkts_ok];
+
+			rte_pktmbuf_free(pkt);
+		}
+	} else {
+		for ( ; pkts_mask; ) {
+			uint32_t pkt_index = __builtin_ctzll(pkts_mask);
+			uint64_t pkt_mask = 1LLU << pkt_index;
+			struct rte_mbuf *pkt = pkts[pkt_index];
+
+			p->tx_buf[tx_buf_count++] = pkt;
+			RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+			pkts_mask &= ~pkt_mask;
+		}
+
+		p->tx_buf_count = tx_buf_count;
+		if (tx_buf_count >= p->tx_burst_sz)
+			send_burst(p);
+	}
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_flush(void *port)
+{
+	struct rte_port_kni_writer *p =
+		(struct rte_port_kni_writer *) port;
+
+	if (p->tx_buf_count > 0)
+		send_burst(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_free(void *port)
+{
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rte_port_kni_writer_flush(port);
+	rte_free(port);
+
+	return 0;
+}
+
+static int rte_port_kni_writer_stats_read(void *port,
+		struct rte_port_out_stats *stats, int clear)
+{
+	struct rte_port_kni_writer *p =
+		(struct rte_port_kni_writer *) port;
+
+	if (stats != NULL)
+		memcpy(stats, &p->stats, sizeof(p->stats));
+
+	if (clear)
+		memset(&p->stats, 0, sizeof(p->stats));
+
+	return 0;
+}
+
+/*
+ * Summary of port operations
+ */
+struct rte_port_in_ops rte_port_kni_reader_ops = {
+	.f_create = rte_port_kni_reader_create,
+	.f_free = rte_port_kni_reader_free,
+	.f_rx = rte_port_kni_reader_rx,
+	.f_stats = rte_port_kni_reader_stats_read,
+};
+
+struct rte_port_out_ops rte_port_kni_writer_ops = {
+	.f_create = rte_port_kni_writer_create,
+	.f_free = rte_port_kni_writer_free,
+	.f_tx = rte_port_kni_writer_tx,
+	.f_tx_bulk = rte_port_kni_writer_tx_bulk,
+	.f_flush = rte_port_kni_writer_flush,
+	.f_stats = rte_port_kni_writer_stats_read,
+};
diff --git a/lib/librte_port/rte_port_kni.h b/lib/librte_port/rte_port_kni.h
new file mode 100644
index 0000000..5cb1134
--- /dev/null
+++ b/lib/librte_port/rte_port_kni.h
@@ -0,0 +1,81 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_RTE_PORT_KNI_H__
+#define __INCLUDE_RTE_PORT_KNI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * RTE Port KNI Interface
+ *
+ * kni_reader: input port built on top of pre-initialized KNI interface
+ * kni_writer: output port built on top of pre-initialized KNI interface
+ *
+ ***/
+
+#include <stdint.h>
+
+#include <rte_kni.h>
+
+#include "rte_port.h"
+
+/** kni_reader port parameters */
+struct rte_port_kni_reader_params {
+    /** KNI interface reference */
+    struct rte_kni *kni;
+};
+
+/** kni_reader port operations */
+extern struct rte_port_in_ops rte_port_kni_reader_ops;
+
+
+/** kni_writer port parameters */
+struct rte_port_kni_writer_params {
+    /** KNI interface reference */
+    struct rte_kni *kni;
+    /** Burst size to KNI interface. */
+    uint32_t tx_burst_sz;
+};
+
+/** kni_writer port operations */
+extern struct rte_port_out_ops rte_port_kni_writer_ops;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
-- 
2.6.4.windows.1

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

* [PATCH] port: add kni interface support
  2016-05-27  5:07 [PATCH] port: add KNI interface support 1. add KNI port type to the packet framework 2. add KNI support to the IP Pipeline sample Application WeiJie Zhuang
@ 2016-05-28 11:25 ` WeiJie Zhuang
  2016-05-30 14:40   ` Dumitrescu, Cristian
                     ` (3 more replies)
  0 siblings, 4 replies; 22+ messages in thread
From: WeiJie Zhuang @ 2016-05-28 11:25 UTC (permalink / raw)
  To: cristian.dumitrescu; +Cc: dev, WeiJie Zhuang

1. add KNI port type to the packet framework
2. add KNI support to the IP Pipeline sample Application

Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
---
v2:
* Fix check patch error.
---
 doc/api/doxy-api-index.md           |   1 +
 examples/ip_pipeline/Makefile       |   6 +-
 examples/ip_pipeline/app.h          |  74 +++++++++
 examples/ip_pipeline/config/kni.cfg |  12 ++
 examples/ip_pipeline/config_check.c |  34 ++++
 examples/ip_pipeline/config_parse.c | 130 +++++++++++++++
 examples/ip_pipeline/init.c         |  79 +++++++++
 examples/ip_pipeline/kni/kni.c      |  80 +++++++++
 examples/ip_pipeline/kni/kni.h      |  16 ++
 examples/ip_pipeline/pipeline_be.h  |  13 ++
 examples/ip_pipeline/thread.c       |   9 +
 lib/librte_port/Makefile            |   7 +
 lib/librte_port/rte_port_kni.c      | 316 ++++++++++++++++++++++++++++++++++++
 lib/librte_port/rte_port_kni.h      |  81 +++++++++
 14 files changed, 856 insertions(+), 2 deletions(-)
 create mode 100644 examples/ip_pipeline/config/kni.cfg
 create mode 100644 examples/ip_pipeline/kni/kni.c
 create mode 100644 examples/ip_pipeline/kni/kni.h
 create mode 100644 lib/librte_port/rte_port_kni.c
 create mode 100644 lib/librte_port/rte_port_kni.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index f626386..e38a959 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -119,6 +119,7 @@ There are many libraries, so their headers may be grouped by topics:
     [reass]            (@ref rte_port_ras.h),
     [sched]            (@ref rte_port_sched.h),
     [src/sink]         (@ref rte_port_source_sink.h)
+    [kni]              (@ref rte_port_kni.h)
   * [table]            (@ref rte_table.h):
     [lpm IPv4]         (@ref rte_table_lpm.h),
     [lpm IPv6]         (@ref rte_table_lpm_ipv6.h),
diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index 10fe1ba..848c2aa 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -43,9 +43,10 @@ include $(RTE_SDK)/mk/rte.vars.mk
 # binary name
 APP = ip_pipeline
 
+VPATH += $(SRCDIR)/kni
 VPATH += $(SRCDIR)/pipeline
 
-INC += $(wildcard *.h) $(wildcard pipeline/*.h)
+INC += $(wildcard *.h) $(wildcard pipeline/*.h) $(wildcard kni/*.h)
 
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) := main.c
@@ -56,6 +57,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread_fe.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += kni.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_fe.c
@@ -72,7 +74,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_flow_actions.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing.c
 
-CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline
+CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline -I$(SRCDIR)/kni
 CFLAGS += -O3
 CFLAGS += $(WERROR_FLAGS) -Wno-error=unused-function -Wno-error=unused-variable
 
diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
index e775024..a86ce57 100644
--- a/examples/ip_pipeline/app.h
+++ b/examples/ip_pipeline/app.h
@@ -44,7 +44,9 @@
 #include <cmdline_parse.h>
 
 #include <rte_ethdev.h>
+#include <rte_kni.h>
 
+#include "kni.h"
 #include "cpu_core_map.h"
 #include "pipeline.h"
 
@@ -99,6 +101,18 @@ struct app_pktq_hwq_out_params {
 	struct rte_eth_txconf conf;
 };
 
+struct app_kni_params {
+	char *name;
+	uint32_t parsed;
+
+	uint32_t socket_id;
+	uint32_t core_id;
+	uint32_t hyper_th_id;
+
+	uint32_t mempool_id;
+	uint32_t burst;
+};
+
 struct app_pktq_swq_params {
 	char *name;
 	uint32_t parsed;
@@ -172,6 +186,7 @@ enum app_pktq_in_type {
 	APP_PKTQ_IN_SWQ,
 	APP_PKTQ_IN_TM,
 	APP_PKTQ_IN_SOURCE,
+	APP_PKTQ_IN_KNI,
 };
 
 struct app_pktq_in_params {
@@ -184,6 +199,7 @@ enum app_pktq_out_type {
 	APP_PKTQ_OUT_SWQ,
 	APP_PKTQ_OUT_TM,
 	APP_PKTQ_OUT_SINK,
+	APP_PKTQ_OUT_KNI,
 };
 
 struct app_pktq_out_params {
@@ -434,6 +450,10 @@ struct app_eal_params {
 #define APP_THREAD_HEADROOM_STATS_COLLECT        1
 #endif
 
+#ifndef APP_MAX_KNI
+#define APP_MAX_KNI                              8
+#endif
+
 struct app_params {
 	/* Config */
 	char app_name[APP_APPNAME_SIZE];
@@ -457,6 +477,7 @@ struct app_params {
 	struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
 	struct app_msgq_params msgq_params[APP_MAX_MSGQ];
 	struct app_pipeline_params pipeline_params[APP_MAX_PIPELINES];
+	struct app_kni_params kni_params[APP_MAX_KNI];
 
 	uint32_t n_mempools;
 	uint32_t n_links;
@@ -468,6 +489,7 @@ struct app_params {
 	uint32_t n_pktq_sink;
 	uint32_t n_msgq;
 	uint32_t n_pipelines;
+	uint32_t n_kni;
 
 	/* Init */
 	char *eal_argv[1 + APP_EAL_ARGC];
@@ -480,6 +502,7 @@ struct app_params {
 	struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
 	struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
 	struct app_thread_data thread_data[APP_MAX_THREADS];
+	struct rte_kni *kni[APP_MAX_KNI];
 	cmdline_parse_ctx_t cmds[APP_MAX_CMDS + 1];
 
 	int eal_argc;
@@ -738,6 +761,31 @@ app_msgq_get_readers(struct app_params *app, struct app_msgq_params *msgq)
 }
 
 static inline uint32_t
+app_kni_get_readers(struct app_params *app, struct app_kni_params *kni)
+{
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq = &p->pktq_in[j];
+
+			if ((pktq->type == APP_PKTQ_IN_KNI) &&
+				(pktq->id == pos))
+				n_readers++;
+		}
+	}
+
+	return n_readers;
+}
+
+static inline uint32_t
 app_txq_get_writers(struct app_params *app, struct app_pktq_hwq_out_params *txq)
 {
 	uint32_t pos = txq - app->hwq_out_params;
@@ -863,6 +911,32 @@ app_msgq_get_writers(struct app_params *app, struct app_msgq_params *msgq)
 	return n_writers;
 }
 
+static inline uint32_t
+app_kni_get_writers(struct app_params *app, struct app_kni_params *kni)
+{
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
+			RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq = &p->pktq_out[j];
+
+			if ((pktq->type == APP_PKTQ_OUT_KNI) &&
+				(pktq->id == pos))
+				n_writers++;
+		}
+	}
+
+	return n_writers;
+}
+
 static inline struct app_link_params *
 app_get_link_for_rxq(struct app_params *app, struct app_pktq_hwq_in_params *p)
 {
diff --git a/examples/ip_pipeline/config/kni.cfg b/examples/ip_pipeline/config/kni.cfg
new file mode 100644
index 0000000..30466b0
--- /dev/null
+++ b/examples/ip_pipeline/config/kni.cfg
@@ -0,0 +1,12 @@
+[KNI0]
+core = 2
+
+[PIPELINE0]
+type = MASTER
+core = 0
+
+[PIPELINE1]
+type = PASS-THROUGH
+core = 1
+pktq_in = RXQ0.0 KNI0
+pktq_out = KNI0 TXQ0.0
diff --git a/examples/ip_pipeline/config_check.c b/examples/ip_pipeline/config_check.c
index fd9ff49..3e300f9 100644
--- a/examples/ip_pipeline/config_check.c
+++ b/examples/ip_pipeline/config_check.c
@@ -426,6 +426,39 @@ check_pipelines(struct app_params *app)
 	}
 }
 
+static void
+check_kni(struct app_params *app) {
+	uint32_t i;
+	uint32_t port_id;
+
+	for (i = 0; i < app->n_kni; i++) {
+		struct app_kni_params *p = &app->kni_params[i];
+		uint32_t n_readers = app_kni_get_readers(app, p);
+		uint32_t n_writers = app_kni_get_writers(app, p);
+
+		APP_CHECK((n_readers != 0),
+				  "%s has no reader\n", p->name);
+
+		if (n_readers > 1)
+			APP_LOG(app, LOW,
+					"%s has more than one reader", p->name);
+
+		APP_CHECK((n_writers != 0),
+				  "%s has no writer\n", p->name);
+
+		if (n_writers > 1)
+			APP_LOG(app, LOW,
+					"%s has more than one writer", p->name);
+
+		APP_CHECK(sscanf(p->name, "KNI%" PRIu32, &port_id) == 1,
+				  "%s's port id is invalid\n", p->name);
+
+		APP_CHECK(port_id < app->n_links,
+				  "kni %s is not associated with a valid link\n",
+				  p->name);
+	}
+}
+
 int
 app_config_check(struct app_params *app)
 {
@@ -439,6 +472,7 @@ app_config_check(struct app_params *app)
 	check_sinks(app);
 	check_msgqs(app);
 	check_pipelines(app);
+	check_kni(app);
 
 	return 0;
 }
diff --git a/examples/ip_pipeline/config_parse.c b/examples/ip_pipeline/config_parse.c
index e5efd03..e9cd5a4 100644
--- a/examples/ip_pipeline/config_parse.c
+++ b/examples/ip_pipeline/config_parse.c
@@ -209,6 +209,15 @@ struct app_pipeline_params default_pipeline_params = {
 	.n_args = 0,
 };
 
+struct app_kni_params default_kni_params = {
+	.parsed = 0,
+	.socket_id = 0,
+	.core_id = 0,
+	.hyper_th_id = 0,
+	.mempool_id = 0,
+	.burst = 32,
+};
+
 static const char app_usage[] =
 	"Usage: %s [-f CONFIG_FILE] [-s SCRIPT_FILE] [-p PORT_MASK] "
 	"[-l LOG_LEVEL] [--preproc PREPROCESSOR] [--preproc-args ARGS]\n"
@@ -1169,6 +1178,9 @@ parse_pipeline_pktq_in(struct app_params *app,
 		} else if (validate_name(name, "SOURCE", 1) == 0) {
 			type = APP_PKTQ_IN_SOURCE;
 			id = APP_PARAM_ADD(app->source_params, name);
+		} else if (validate_name(name, "KNI", 1) == 0) {
+			type = APP_PKTQ_IN_KNI;
+			id = APP_PARAM_ADD(app->kni_params, name);
 		} else
 			return -EINVAL;
 
@@ -1240,6 +1252,9 @@ parse_pipeline_pktq_out(struct app_params *app,
 		} else if (validate_name(name, "SINK", 1) == 0) {
 			type = APP_PKTQ_OUT_SINK;
 			id = APP_PARAM_ADD(app->sink_params, name);
+		} else if (validate_name(name, "KNI", 1) == 0) {
+			type = APP_PKTQ_OUT_KNI;
+			id = APP_PARAM_ADD(app->kni_params, name);
 		} else
 			return -EINVAL;
 
@@ -2459,6 +2474,76 @@ parse_msgq(struct app_params *app,
 	free(entries);
 }
 
+static void
+parse_kni(struct app_params *app,
+		   const char *section_name,
+		   struct rte_cfgfile *cfg)
+{
+	struct app_kni_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->kni_params, section_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->kni_params, section_name);
+
+	param = &app->kni_params[param_idx];
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		if (strcmp(ent->name, "core") == 0) {
+			int status = parse_pipeline_core(
+				&param->socket_id, &param->core_id,
+				&param->hyper_th_id, ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+				ent->name);
+			continue;
+		}
+
+		if (strcmp(ent->name, "mempool") == 0) {
+			int status = validate_name(ent->value,
+				"MEMPOOL", 1);
+			ssize_t idx;
+
+			PARSE_ERROR((status == 0), section_name,
+				ent->name);
+			idx = APP_PARAM_ADD(app->mempool_params,
+				ent->value);
+			PARSER_PARAM_ADD_CHECK(idx,
+				app->mempool_params,
+				section_name);
+			param->mempool_id = idx;
+			continue;
+		}
+
+		if (strcmp(ent->name, "burst") == 0) {
+			int status = parser_read_uint32(&param->burst,
+				ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+				ent->name);
+			continue;
+		}
+
+		/* unrecognized */
+		PARSE_ERROR_INVALID(0, section_name, ent->name);
+	}
+
+	param->parsed = 1;
+
+	free(entries);
+}
+
 typedef void (*config_section_load)(struct app_params *p,
 	const char *section_name,
 	struct rte_cfgfile *cfg);
@@ -2483,6 +2568,7 @@ static const struct config_section cfg_file_scheme[] = {
 	{"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
 	{"MSGQ-RSP-PIPELINE", 1, parse_msgq_rsp_pipeline},
 	{"MSGQ", 1, parse_msgq},
+	{"KNI", 1, parse_kni},
 };
 
 static void
@@ -2619,6 +2705,7 @@ app_config_parse(struct app_params *app, const char *file_name)
 	APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
 	APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
 	APP_PARAM_COUNT(app->pipeline_params, app->n_pipelines);
+	APP_PARAM_COUNT(app->kni_params, app->n_kni);
 
 #ifdef RTE_PORT_PCAP
 	for (i = 0; i < (int)app->n_pktq_source; i++) {
@@ -3025,6 +3112,9 @@ save_pipeline_params(struct app_params *app, FILE *f)
 				case APP_PKTQ_IN_SOURCE:
 					name = app->source_params[pp->id].name;
 					break;
+				case APP_PKTQ_IN_KNI:
+					name = app->kni_params[pp->id].name;
+					break;
 				default:
 					APP_CHECK(0, "System error "
 						"occurred while saving "
@@ -3059,6 +3149,9 @@ save_pipeline_params(struct app_params *app, FILE *f)
 				case APP_PKTQ_OUT_SINK:
 					name = app->sink_params[pp->id].name;
 					break;
+				case APP_PKTQ_OUT_KNI:
+					name = app->kni_params[pp->id].name;
+					break;
 				default:
 					APP_CHECK(0, "System error "
 						"occurred while saving "
@@ -3114,6 +3207,37 @@ save_pipeline_params(struct app_params *app, FILE *f)
 	}
 }
 
+static void
+save_kni_params(struct app_params *app, FILE *f)
+{
+	struct app_kni_params *p;
+	size_t i, count;
+
+	count = RTE_DIM(app->kni_params);
+	for (i = 0; i < count; i++) {
+		p = &app->kni_params[i];
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		/* section name */
+		fprintf(f, "[%s]\n", p->name);
+
+		/* core */
+		fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
+				p->socket_id,
+				p->core_id,
+				(p->hyper_th_id) ? "h" : "");
+
+		/* mempool */
+		fprintf(f, "%s = %" PRIu32 "\n", "mempool_id", p->mempool_id);
+
+		/* burst */
+		fprintf(f, "%s = %" PRIu32 "\n", "burst", p->burst);
+
+		fputc('\n', f);
+	}
+}
+
 void
 app_config_save(struct app_params *app, const char *file_name)
 {
@@ -3144,6 +3268,7 @@ app_config_save(struct app_params *app, const char *file_name)
 	save_source_params(app, file);
 	save_sink_params(app, file);
 	save_msgq_params(app, file);
+	save_kni_params(app, file);
 
 	fclose(file);
 	free(name);
@@ -3206,6 +3331,11 @@ app_config_init(struct app_params *app)
 			&default_pipeline_params,
 			sizeof(default_pipeline_params));
 
+	for (i = 0; i < RTE_DIM(app->kni_params); i++)
+		memcpy(&app->kni_params[i],
+			&default_kni_params,
+			sizeof(default_kni_params));
+
 	return 0;
 }
 
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
index 02351f6..8ff9118 100644
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -89,6 +89,24 @@ app_init_core_mask(struct app_params *app)
 		mask |= 1LLU << lcore_id;
 	}
 
+	for (i = 0; i < app->n_kni; i++) {
+		struct app_kni_params *p = &app->kni_params[i];
+		int lcore_id;
+
+		lcore_id = cpu_core_map_get_lcore_id(app->core_map,
+			p->socket_id,
+			p->core_id,
+			p->hyper_th_id);
+
+		if (lcore_id < 0)
+			rte_panic("Cannot create CPU core mask\n");
+
+		if (mask & 1LLU << lcore_id)
+			rte_panic("KNI interface must use a dedicated lcore\n");
+
+		mask |= 1LLU << lcore_id;
+	}
+
 	app->core_mask = mask;
 	APP_LOG(app, HIGH, "CPU core mask = 0x%016" PRIx64, app->core_mask);
 }
@@ -1236,6 +1254,11 @@ static void app_pipeline_params_get(struct app_params *app,
 					n_bytes_per_pkt;
 			}
 			break;
+		case APP_PKTQ_IN_KNI:
+			out->type = PIPELINE_PORT_IN_KNI;
+			out->params.kni.kni = app->kni[in->id];
+			out->burst_size = app->kni_params[in->id].burst;
+			break;
 		default:
 			break;
 		}
@@ -1374,6 +1397,12 @@ static void app_pipeline_params_get(struct app_params *app,
 				out->params.sink.max_n_pkts = 0;
 			}
 			break;
+		case APP_PKTQ_OUT_KNI:
+			out->type = PIPELINE_PORT_OUT_KNI;
+			out->params.kni.kni = app->kni[in->id];
+			out->params.kni.tx_burst_sz =
+					app->kni_params[in->id].burst;
+			break;
 		default:
 			break;
 		}
@@ -1397,6 +1426,55 @@ static void app_pipeline_params_get(struct app_params *app,
 }
 
 static void
+app_init_kni(struct app_params *app) {
+	uint32_t i;
+	struct rte_kni_conf conf;
+
+	rte_kni_init((unsigned int)app->n_kni);
+
+	for (i = 0; i < app->n_kni; i++) {
+		struct app_kni_params *p_kni = &app->kni_params[i];
+		uint32_t port_id;
+		struct app_mempool_params *mempool_params;
+		struct rte_mempool *mempool;
+
+		if (sscanf(p_kni->name, "KNI%" PRIu32, &port_id) != 1)
+			rte_panic("%s's port id is invalid\n", p_kni->name);
+
+		mempool_params = &app->mempool_params[p_kni->mempool_id];
+		mempool = app->mempool[p_kni->mempool_id];
+
+		memset(&conf, 0, sizeof(conf));
+		snprintf(conf.name, RTE_KNI_NAMESIZE,
+				 "vEth%u", port_id);
+		conf.core_id = p_kni->core_id;
+		conf.force_bind = 1;
+
+		conf.group_id = (uint16_t) port_id;
+		conf.mbuf_size = mempool_params->buffer_size;
+
+		struct rte_kni_ops ops;
+		struct rte_eth_dev_info dev_info;
+
+		memset(&dev_info, 0, sizeof(dev_info));
+		rte_eth_dev_info_get(app->link_params[port_id].pmd_id,
+							 &dev_info);
+		conf.addr = dev_info.pci_dev->addr;
+		conf.id = dev_info.pci_dev->id;
+
+		memset(&ops, 0, sizeof(ops));
+		ops.port_id = app->link_params[port_id].pmd_id;
+		ops.change_mtu = kni_change_mtu;
+		ops.config_network_if = kni_config_network_interface;
+
+		app->kni[i] = rte_kni_alloc(mempool,
+			&conf, &ops);
+		if (!app->kni[i])
+			rte_panic("Fail to create kni for port: %d\n", port_id);
+	}
+}
+
+static void
 app_init_pipelines(struct app_params *app)
 {
 	uint32_t p_id;
@@ -1531,6 +1609,7 @@ int app_init(struct app_params *app)
 	app_init_swq(app);
 	app_init_tm(app);
 	app_init_msgq(app);
+	app_init_kni(app);
 
 	app_pipeline_common_cmd_push(app);
 	app_pipeline_thread_cmd_push(app);
diff --git a/examples/ip_pipeline/kni/kni.c b/examples/ip_pipeline/kni/kni.c
new file mode 100644
index 0000000..c58e146
--- /dev/null
+++ b/examples/ip_pipeline/kni/kni.c
@@ -0,0 +1,80 @@
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_table_array.h>
+#include <rte_kni.h>
+#include <rte_ethdev.h>
+
+#include "rte_port_kni.h"
+#include "kni.h"
+
+int
+kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
+	int ret = 0;
+
+	if (port_id >= rte_eth_dev_count() || port_id >= RTE_MAX_ETHPORTS) {
+		RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
+				__func__, port_id);
+		return -EINVAL;
+	}
+
+	RTE_LOG(INFO, PORT, "%s: Configure network interface of %d %s\n",
+			__func__, port_id, if_up ? "up" : "down");
+
+	if (if_up != 0) { /* Configure network interface up */
+		rte_eth_dev_stop(port_id);
+		ret = rte_eth_dev_start(port_id);
+	} else /* Configure network interface down */
+		rte_eth_dev_stop(port_id);
+
+	if (ret < 0)
+		RTE_LOG(ERR, PORT, "%s: Failed to start port %d\n",
+				__func__, port_id);
+
+	return ret;
+}
+
+int
+kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
+	int ret;
+
+	if (port_id >= rte_eth_dev_count()) {
+		RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
+				__func__, port_id);
+		return -EINVAL;
+	}
+
+	if (new_mtu > ETHER_MAX_LEN) {
+		RTE_LOG(ERR, PORT,
+				"%s: Fail to reconfigure port %d, the new MTU is too big\n",
+				__func__, port_id);
+		return -EINVAL;
+	}
+
+	RTE_LOG(INFO, PORT, "%s: Change MTU of port %d to %u\n",
+			__func__, port_id,
+			new_mtu);
+
+	/* Stop specific port */
+	rte_eth_dev_stop(port_id);
+
+	/* Set new MTU */
+	ret = rte_eth_dev_set_mtu(port_id, new_mtu);
+	if (ret < 0) {
+		RTE_LOG(ERR, PORT, "%s: Fail to reconfigure port %d\n",
+				__func__, port_id);
+		return ret;
+	}
+
+	/* Restart specific port */
+	ret = rte_eth_dev_start(port_id);
+	if (ret < 0) {
+		RTE_LOG(ERR, PORT, "%s: Fail to restart port %d\n",
+				__func__, port_id);
+		return ret;
+	}
+
+	return 0;
+}
+
diff --git a/examples/ip_pipeline/kni/kni.h b/examples/ip_pipeline/kni/kni.h
new file mode 100644
index 0000000..04c8429
--- /dev/null
+++ b/examples/ip_pipeline/kni/kni.h
@@ -0,0 +1,16 @@
+#ifndef __INCLUDE_KNI_H__
+#define __INCLUDE_KNI_H__
+
+#include <rte_common.h>
+
+/* Total octets in ethernet header */
+#define KNI_ENET_HEADER_SIZE    14
+
+/* Total octets in the FCS */
+#define KNI_ENET_FCS_SIZE       4
+
+int kni_config_network_interface(uint8_t port_id, uint8_t if_up);
+
+int kni_change_mtu(uint8_t port_id, unsigned new_mtu);
+
+#endif
diff --git a/examples/ip_pipeline/pipeline_be.h b/examples/ip_pipeline/pipeline_be.h
index f4ff262..23f0438 100644
--- a/examples/ip_pipeline/pipeline_be.h
+++ b/examples/ip_pipeline/pipeline_be.h
@@ -40,6 +40,7 @@
 #include <rte_port_ras.h>
 #include <rte_port_sched.h>
 #include <rte_port_source_sink.h>
+#include <rte_port_kni.h>
 #include <rte_pipeline.h>
 
 enum pipeline_port_in_type {
@@ -50,6 +51,7 @@ enum pipeline_port_in_type {
 	PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
 	PIPELINE_PORT_IN_SCHED_READER,
 	PIPELINE_PORT_IN_SOURCE,
+	PIPELINE_PORT_IN_KNI,
 };
 
 struct pipeline_port_in_params {
@@ -62,6 +64,7 @@ struct pipeline_port_in_params {
 		struct rte_port_ring_reader_ipv6_frag_params ring_ipv6_frag;
 		struct rte_port_sched_reader_params sched;
 		struct rte_port_source_params source;
+		struct rte_port_kni_reader_params kni;
 	} params;
 	uint32_t burst_size;
 };
@@ -84,6 +87,8 @@ pipeline_port_in_params_convert(struct pipeline_port_in_params  *p)
 		return (void *) &p->params.sched;
 	case PIPELINE_PORT_IN_SOURCE:
 		return (void *) &p->params.source;
+	case PIPELINE_PORT_IN_KNI:
+		return (void *) &p->params.kni;
 	default:
 		return NULL;
 	}
@@ -107,6 +112,8 @@ pipeline_port_in_params_get_ops(struct pipeline_port_in_params  *p)
 		return &rte_port_sched_reader_ops;
 	case PIPELINE_PORT_IN_SOURCE:
 		return &rte_port_source_ops;
+	case PIPELINE_PORT_IN_KNI:
+		return &rte_port_kni_reader_ops;
 	default:
 		return NULL;
 	}
@@ -123,6 +130,7 @@ enum pipeline_port_out_type {
 	PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
 	PIPELINE_PORT_OUT_SCHED_WRITER,
 	PIPELINE_PORT_OUT_SINK,
+	PIPELINE_PORT_OUT_KNI,
 };
 
 struct pipeline_port_out_params {
@@ -138,6 +146,7 @@ struct pipeline_port_out_params {
 		struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
 		struct rte_port_sched_writer_params sched;
 		struct rte_port_sink_params sink;
+		struct rte_port_kni_writer_params kni;
 	} params;
 };
 
@@ -165,6 +174,8 @@ pipeline_port_out_params_convert(struct pipeline_port_out_params  *p)
 		return (void *) &p->params.sched;
 	case PIPELINE_PORT_OUT_SINK:
 		return (void *) &p->params.sink;
+	case PIPELINE_PORT_OUT_KNI:
+		return (void *) &p->params.kni;
 	default:
 		return NULL;
 	}
@@ -194,6 +205,8 @@ pipeline_port_out_params_get_ops(struct pipeline_port_out_params  *p)
 		return &rte_port_sched_writer_ops;
 	case PIPELINE_PORT_OUT_SINK:
 		return &rte_port_sink_ops;
+	case PIPELINE_PORT_OUT_KNI:
+		return &rte_port_kni_writer_ops;
 	default:
 		return NULL;
 	}
diff --git a/examples/ip_pipeline/thread.c b/examples/ip_pipeline/thread.c
index a0f1f12..534864a 100644
--- a/examples/ip_pipeline/thread.c
+++ b/examples/ip_pipeline/thread.c
@@ -239,6 +239,15 @@ app_thread(void *arg)
 	uint32_t core_id = rte_lcore_id(), i, j;
 	struct app_thread_data *t = &app->thread_data[core_id];
 
+	for (i = 0; i < app->n_kni; i++) {
+		if (core_id == (uint32_t)cpu_core_map_get_lcore_id(
+			app->core_map,
+			app->kni_params[i].socket_id,
+			app->kni_params[i].core_id,
+			app->kni_params[i].hyper_th_id))
+			return 0;
+	}
+
 	for (i = 0; ; i++) {
 		uint32_t n_regular = RTE_MIN(t->n_regular, RTE_DIM(t->regular));
 		uint32_t n_custom = RTE_MIN(t->n_custom, RTE_DIM(t->custom));
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index d4de5af..f18253d 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -57,6 +57,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
 endif
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
+endif
 
 # install includes
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port.h
@@ -68,6 +71,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_ras.h
 endif
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
+endif
 
 # this lib depends upon:
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
@@ -75,5 +81,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mbuf
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/rte_port_kni.c b/lib/librte_port/rte_port_kni.c
new file mode 100644
index 0000000..8c5e404
--- /dev/null
+++ b/lib/librte_port/rte_port_kni.c
@@ -0,0 +1,316 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_kni.h>
+
+#include "rte_port_kni.h"
+
+/*
+ * Port KNI Reader
+ */
+#ifdef RTE_PORT_STATS_COLLECT
+
+#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
+	{port->stats.n_pkts_in += val}
+#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
+	{port->stats.n_pkts_drop += val}
+
+#else
+
+#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
+#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
+
+#endif
+
+struct rte_port_kni_reader {
+	struct rte_port_in_stats stats;
+
+	struct rte_kni *kni;
+};
+
+static void *
+rte_port_kni_reader_create(void *params, int socket_id) {
+	struct rte_port_kni_reader_params *conf =
+			(struct rte_port_kni_reader_params *) params;
+	struct rte_port_kni_reader *port;
+
+	/* Check input parameters */
+	if (conf == NULL) {
+		RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	port = rte_zmalloc_socket("PORT", sizeof(*port),
+		RTE_CACHE_LINE_SIZE, socket_id);
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni = conf->kni;
+
+	return port;
+}
+
+static int
+rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t n_pkts) {
+	struct rte_port_kni_reader *p =
+			(struct rte_port_kni_reader *) port;
+	uint16_t rx_pkt_cnt;
+
+	rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
+	RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
+	return rx_pkt_cnt;
+}
+
+static int
+rte_port_kni_reader_free(void *port) {
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rte_free(port);
+
+	return 0;
+}
+
+static int rte_port_kni_reader_stats_read(void *port,
+	struct rte_port_in_stats *stats, int clear)
+{
+	struct rte_port_kni_reader *p =
+			(struct rte_port_kni_reader *) port;
+
+	if (stats != NULL)
+		memcpy(stats, &p->stats, sizeof(p->stats));
+
+	if (clear)
+		memset(&p->stats, 0, sizeof(p->stats));
+
+	return 0;
+}
+
+/*
+ * Port KNI Writer
+ */
+#ifdef RTE_PORT_STATS_COLLECT
+
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
+	{port->stats.n_pkts_in += val}
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
+	{port->stats.n_pkts_drop += val}
+
+#else
+
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
+
+#endif
+
+struct rte_port_kni_writer {
+	struct rte_port_out_stats stats;
+
+	struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
+	uint32_t tx_burst_sz;
+	uint16_t tx_buf_count;
+	uint64_t bsz_mask;
+	struct rte_kni *kni;
+};
+
+static void *
+rte_port_kni_writer_create(void *params, int socket_id) {
+	struct rte_port_kni_writer_params *conf =
+			(struct rte_port_kni_writer_params *) params;
+	struct rte_port_kni_writer *port;
+
+	/* Check input parameters */
+	if ((conf == NULL) ||
+		(conf->tx_burst_sz == 0) ||
+		(conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
+		(!rte_is_power_of_2(conf->tx_burst_sz))) {
+		RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n", __func__);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	port = rte_zmalloc_socket("PORT", sizeof(*port),
+		RTE_CACHE_LINE_SIZE, socket_id);
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni = conf->kni;
+	port->tx_burst_sz = conf->tx_burst_sz;
+	port->tx_buf_count = 0;
+	port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
+
+	return port;
+}
+
+static inline void
+send_burst(struct rte_port_kni_writer *p) {
+	uint32_t nb_tx;
+
+	nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
+	rte_kni_handle_request(p->kni);
+
+	RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p->tx_buf_count - nb_tx);
+	for (; nb_tx < p->tx_buf_count; nb_tx++)
+		rte_pktmbuf_free(p->tx_buf[nb_tx]);
+
+	p->tx_buf_count = 0;
+}
+
+static int
+rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt) {
+	struct rte_port_kni_writer *p =
+			(struct rte_port_kni_writer *) port;
+
+	p->tx_buf[p->tx_buf_count++] = pkt;
+	RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+	if (p->tx_buf_count >= p->tx_burst_sz)
+		send_burst(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_tx_bulk(void *port,
+							struct rte_mbuf **pkts,
+							uint64_t pkts_mask) {
+	struct rte_port_kni_writer *p =
+			(struct rte_port_kni_writer *) port;
+	uint64_t bsz_mask = p->bsz_mask;
+	uint32_t tx_buf_count = p->tx_buf_count;
+	uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
+					((pkts_mask & bsz_mask) ^ bsz_mask);
+
+	if (expr == 0) {
+		uint64_t n_pkts = __builtin_popcountll(pkts_mask);
+		uint32_t n_pkts_ok;
+
+		if (tx_buf_count)
+			send_burst(p);
+
+		RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
+		n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
+
+		RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts - n_pkts_ok);
+		for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
+			struct rte_mbuf *pkt = pkts[n_pkts_ok];
+
+			rte_pktmbuf_free(pkt);
+		}
+	} else {
+		for (; pkts_mask;) {
+			uint32_t pkt_index = __builtin_ctzll(pkts_mask);
+			uint64_t pkt_mask = 1LLU << pkt_index;
+			struct rte_mbuf *pkt = pkts[pkt_index];
+
+			p->tx_buf[tx_buf_count++] = pkt;
+			RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+			pkts_mask &= ~pkt_mask;
+		}
+
+		p->tx_buf_count = tx_buf_count;
+		if (tx_buf_count >= p->tx_burst_sz)
+			send_burst(p);
+	}
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_flush(void *port) {
+	struct rte_port_kni_writer *p =
+			(struct rte_port_kni_writer *) port;
+
+	if (p->tx_buf_count > 0)
+		send_burst(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_free(void *port) {
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rte_port_kni_writer_flush(port);
+	rte_free(port);
+
+	return 0;
+}
+
+static int rte_port_kni_writer_stats_read(void *port,
+	struct rte_port_out_stats *stats, int clear)
+{
+	struct rte_port_kni_writer *p =
+			(struct rte_port_kni_writer *) port;
+
+	if (stats != NULL)
+		memcpy(stats, &p->stats, sizeof(p->stats));
+
+	if (clear)
+		memset(&p->stats, 0, sizeof(p->stats));
+
+	return 0;
+}
+
+/*
+ * Summary of port operations
+ */
+struct rte_port_in_ops rte_port_kni_reader_ops = {
+	.f_create = rte_port_kni_reader_create,
+	.f_free = rte_port_kni_reader_free,
+	.f_rx = rte_port_kni_reader_rx,
+	.f_stats = rte_port_kni_reader_stats_read,
+};
+
+struct rte_port_out_ops rte_port_kni_writer_ops = {
+	.f_create = rte_port_kni_writer_create,
+	.f_free = rte_port_kni_writer_free,
+	.f_tx = rte_port_kni_writer_tx,
+	.f_tx_bulk = rte_port_kni_writer_tx_bulk,
+	.f_flush = rte_port_kni_writer_flush,
+	.f_stats = rte_port_kni_writer_stats_read,
+};
diff --git a/lib/librte_port/rte_port_kni.h b/lib/librte_port/rte_port_kni.h
new file mode 100644
index 0000000..7623798
--- /dev/null
+++ b/lib/librte_port/rte_port_kni.h
@@ -0,0 +1,81 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_RTE_PORT_KNI_H__
+#define __INCLUDE_RTE_PORT_KNI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * RTE Port KNI Interface
+ *
+ * kni_reader: input port built on top of pre-initialized KNI interface
+ * kni_writer: output port built on top of pre-initialized KNI interface
+ *
+ ***/
+
+#include <stdint.h>
+
+#include <rte_kni.h>
+
+#include "rte_port.h"
+
+/** kni_reader port parameters */
+struct rte_port_kni_reader_params {
+	/** KNI interface reference */
+	struct rte_kni *kni;
+};
+
+/** kni_reader port operations */
+extern struct rte_port_in_ops rte_port_kni_reader_ops;
+
+
+/** kni_writer port parameters */
+struct rte_port_kni_writer_params {
+	/** KNI interface reference */
+	struct rte_kni *kni;
+	/** Burst size to KNI interface. */
+	uint32_t tx_burst_sz;
+};
+
+/** kni_writer port operations */
+extern struct rte_port_out_ops rte_port_kni_writer_ops;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
-- 
2.7.4

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

* Re: [PATCH] port: add kni interface support
  2016-05-28 11:25 ` [PATCH] port: add kni interface support WeiJie Zhuang
@ 2016-05-30 14:40   ` Dumitrescu, Cristian
  2016-06-01  4:18     ` Ethan
  2016-06-09 23:42   ` Dumitrescu, Cristian
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 22+ messages in thread
From: Dumitrescu, Cristian @ 2016-05-30 14:40 UTC (permalink / raw)
  To: WeiJie Zhuang; +Cc: dev

Hi Wei Jie,

Thank you for submitting this patch. I am currently travelling, I will review your patch and come back to you hopefully later this week or early next week.

Regards,
Cristian

> -----Original Message-----
> From: WeiJie Zhuang [mailto:zhuangwj@gmail.com]
> Sent: Saturday, May 28, 2016 12:26 PM
> To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>
> Cc: dev@dpdk.org; WeiJie Zhuang <zhuangwj@gmail.com>
> Subject: [PATCH] port: add kni interface support
> 
> 1. add KNI port type to the packet framework
> 2. add KNI support to the IP Pipeline sample Application
> 
> Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
> ---
> v2:
> * Fix check patch error.
> ---
>  doc/api/doxy-api-index.md           |   1 +
>  examples/ip_pipeline/Makefile       |   6 +-
>  examples/ip_pipeline/app.h          |  74 +++++++++
>  examples/ip_pipeline/config/kni.cfg |  12 ++
>  examples/ip_pipeline/config_check.c |  34 ++++
>  examples/ip_pipeline/config_parse.c | 130 +++++++++++++++
>  examples/ip_pipeline/init.c         |  79 +++++++++
>  examples/ip_pipeline/kni/kni.c      |  80 +++++++++
>  examples/ip_pipeline/kni/kni.h      |  16 ++
>  examples/ip_pipeline/pipeline_be.h  |  13 ++
>  examples/ip_pipeline/thread.c       |   9 +
>  lib/librte_port/Makefile            |   7 +
>  lib/librte_port/rte_port_kni.c      | 316
> ++++++++++++++++++++++++++++++++++++
>  lib/librte_port/rte_port_kni.h      |  81 +++++++++
>  14 files changed, 856 insertions(+), 2 deletions(-)
>  create mode 100644 examples/ip_pipeline/config/kni.cfg
>  create mode 100644 examples/ip_pipeline/kni/kni.c
>  create mode 100644 examples/ip_pipeline/kni/kni.h
>  create mode 100644 lib/librte_port/rte_port_kni.c
>  create mode 100644 lib/librte_port/rte_port_kni.h
> 
> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> index f626386..e38a959 100644
> --- a/doc/api/doxy-api-index.md
> +++ b/doc/api/doxy-api-index.md
> @@ -119,6 +119,7 @@ There are many libraries, so their headers may be
> grouped by topics:
>      [reass]            (@ref rte_port_ras.h),
>      [sched]            (@ref rte_port_sched.h),
>      [src/sink]         (@ref rte_port_source_sink.h)
> +    [kni]              (@ref rte_port_kni.h)
>    * [table]            (@ref rte_table.h):
>      [lpm IPv4]         (@ref rte_table_lpm.h),
>      [lpm IPv6]         (@ref rte_table_lpm_ipv6.h),
> diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
> index 10fe1ba..848c2aa 100644
> --- a/examples/ip_pipeline/Makefile
> +++ b/examples/ip_pipeline/Makefile
> @@ -43,9 +43,10 @@ include $(RTE_SDK)/mk/rte.vars.mk
>  # binary name
>  APP = ip_pipeline
> 
> +VPATH += $(SRCDIR)/kni
>  VPATH += $(SRCDIR)/pipeline
> 
> -INC += $(wildcard *.h) $(wildcard pipeline/*.h)
> +INC += $(wildcard *.h) $(wildcard pipeline/*.h) $(wildcard kni/*.h)
> 
>  # all source are stored in SRCS-y
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) := main.c
> @@ -56,6 +57,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread_fe.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
> +SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += kni.c
> 
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_be.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_fe.c
> @@ -72,7 +74,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) +=
> pipeline_flow_actions.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing_be.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing.c
> 
> -CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline
> +CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline -I$(SRCDIR)/kni
>  CFLAGS += -O3
>  CFLAGS += $(WERROR_FLAGS) -Wno-error=unused-function -Wno-
> error=unused-variable
> 
> diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
> index e775024..a86ce57 100644
> --- a/examples/ip_pipeline/app.h
> +++ b/examples/ip_pipeline/app.h
> @@ -44,7 +44,9 @@
>  #include <cmdline_parse.h>
> 
>  #include <rte_ethdev.h>
> +#include <rte_kni.h>
> 
> +#include "kni.h"
>  #include "cpu_core_map.h"
>  #include "pipeline.h"
> 
> @@ -99,6 +101,18 @@ struct app_pktq_hwq_out_params {
>  	struct rte_eth_txconf conf;
>  };
> 
> +struct app_kni_params {
> +	char *name;
> +	uint32_t parsed;
> +
> +	uint32_t socket_id;
> +	uint32_t core_id;
> +	uint32_t hyper_th_id;
> +
> +	uint32_t mempool_id;
> +	uint32_t burst;
> +};
> +
>  struct app_pktq_swq_params {
>  	char *name;
>  	uint32_t parsed;
> @@ -172,6 +186,7 @@ enum app_pktq_in_type {
>  	APP_PKTQ_IN_SWQ,
>  	APP_PKTQ_IN_TM,
>  	APP_PKTQ_IN_SOURCE,
> +	APP_PKTQ_IN_KNI,
>  };
> 
>  struct app_pktq_in_params {
> @@ -184,6 +199,7 @@ enum app_pktq_out_type {
>  	APP_PKTQ_OUT_SWQ,
>  	APP_PKTQ_OUT_TM,
>  	APP_PKTQ_OUT_SINK,
> +	APP_PKTQ_OUT_KNI,
>  };
> 
>  struct app_pktq_out_params {
> @@ -434,6 +450,10 @@ struct app_eal_params {
>  #define APP_THREAD_HEADROOM_STATS_COLLECT        1
>  #endif
> 
> +#ifndef APP_MAX_KNI
> +#define APP_MAX_KNI                              8
> +#endif
> +
>  struct app_params {
>  	/* Config */
>  	char app_name[APP_APPNAME_SIZE];
> @@ -457,6 +477,7 @@ struct app_params {
>  	struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
>  	struct app_msgq_params msgq_params[APP_MAX_MSGQ];
>  	struct app_pipeline_params pipeline_params[APP_MAX_PIPELINES];
> +	struct app_kni_params kni_params[APP_MAX_KNI];
> 
>  	uint32_t n_mempools;
>  	uint32_t n_links;
> @@ -468,6 +489,7 @@ struct app_params {
>  	uint32_t n_pktq_sink;
>  	uint32_t n_msgq;
>  	uint32_t n_pipelines;
> +	uint32_t n_kni;
> 
>  	/* Init */
>  	char *eal_argv[1 + APP_EAL_ARGC];
> @@ -480,6 +502,7 @@ struct app_params {
>  	struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
>  	struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
>  	struct app_thread_data thread_data[APP_MAX_THREADS];
> +	struct rte_kni *kni[APP_MAX_KNI];
>  	cmdline_parse_ctx_t cmds[APP_MAX_CMDS + 1];
> 
>  	int eal_argc;
> @@ -738,6 +761,31 @@ app_msgq_get_readers(struct app_params *app,
> struct app_msgq_params *msgq)
>  }
> 
>  static inline uint32_t
> +app_kni_get_readers(struct app_params *app, struct app_kni_params
> *kni)
> +{
> +	uint32_t pos = kni - app->kni_params;
> +	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +		RTE_DIM(app->pipeline_params));
> +	uint32_t n_readers = 0, i;
> +
> +	for (i = 0; i < n_pipelines; i++) {
> +		struct app_pipeline_params *p = &app->pipeline_params[i];
> +		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p-
> >pktq_in));
> +		uint32_t j;
> +
> +		for (j = 0; j < n_pktq_in; j++) {
> +			struct app_pktq_in_params *pktq = &p->pktq_in[j];
> +
> +			if ((pktq->type == APP_PKTQ_IN_KNI) &&
> +				(pktq->id == pos))
> +				n_readers++;
> +		}
> +	}
> +
> +	return n_readers;
> +}
> +
> +static inline uint32_t
>  app_txq_get_writers(struct app_params *app, struct
> app_pktq_hwq_out_params *txq)
>  {
>  	uint32_t pos = txq - app->hwq_out_params;
> @@ -863,6 +911,32 @@ app_msgq_get_writers(struct app_params *app,
> struct app_msgq_params *msgq)
>  	return n_writers;
>  }
> 
> +static inline uint32_t
> +app_kni_get_writers(struct app_params *app, struct app_kni_params *kni)
> +{
> +	uint32_t pos = kni - app->kni_params;
> +	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +		RTE_DIM(app->pipeline_params));
> +	uint32_t n_writers = 0, i;
> +
> +	for (i = 0; i < n_pipelines; i++) {
> +		struct app_pipeline_params *p = &app->pipeline_params[i];
> +		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
> +			RTE_DIM(p->pktq_out));
> +		uint32_t j;
> +
> +		for (j = 0; j < n_pktq_out; j++) {
> +			struct app_pktq_out_params *pktq = &p-
> >pktq_out[j];
> +
> +			if ((pktq->type == APP_PKTQ_OUT_KNI) &&
> +				(pktq->id == pos))
> +				n_writers++;
> +		}
> +	}
> +
> +	return n_writers;
> +}
> +
>  static inline struct app_link_params *
>  app_get_link_for_rxq(struct app_params *app, struct
> app_pktq_hwq_in_params *p)
>  {
> diff --git a/examples/ip_pipeline/config/kni.cfg
> b/examples/ip_pipeline/config/kni.cfg
> new file mode 100644
> index 0000000..30466b0
> --- /dev/null
> +++ b/examples/ip_pipeline/config/kni.cfg
> @@ -0,0 +1,12 @@
> +[KNI0]
> +core = 2
> +
> +[PIPELINE0]
> +type = MASTER
> +core = 0
> +
> +[PIPELINE1]
> +type = PASS-THROUGH
> +core = 1
> +pktq_in = RXQ0.0 KNI0
> +pktq_out = KNI0 TXQ0.0
> diff --git a/examples/ip_pipeline/config_check.c
> b/examples/ip_pipeline/config_check.c
> index fd9ff49..3e300f9 100644
> --- a/examples/ip_pipeline/config_check.c
> +++ b/examples/ip_pipeline/config_check.c
> @@ -426,6 +426,39 @@ check_pipelines(struct app_params *app)
>  	}
>  }
> 
> +static void
> +check_kni(struct app_params *app) {
> +	uint32_t i;
> +	uint32_t port_id;
> +
> +	for (i = 0; i < app->n_kni; i++) {
> +		struct app_kni_params *p = &app->kni_params[i];
> +		uint32_t n_readers = app_kni_get_readers(app, p);
> +		uint32_t n_writers = app_kni_get_writers(app, p);
> +
> +		APP_CHECK((n_readers != 0),
> +				  "%s has no reader\n", p->name);
> +
> +		if (n_readers > 1)
> +			APP_LOG(app, LOW,
> +					"%s has more than one reader", p-
> >name);
> +
> +		APP_CHECK((n_writers != 0),
> +				  "%s has no writer\n", p->name);
> +
> +		if (n_writers > 1)
> +			APP_LOG(app, LOW,
> +					"%s has more than one writer", p-
> >name);
> +
> +		APP_CHECK(sscanf(p->name, "KNI%" PRIu32, &port_id) == 1,
> +				  "%s's port id is invalid\n", p->name);
> +
> +		APP_CHECK(port_id < app->n_links,
> +				  "kni %s is not associated with a valid link\n",
> +				  p->name);
> +	}
> +}
> +
>  int
>  app_config_check(struct app_params *app)
>  {
> @@ -439,6 +472,7 @@ app_config_check(struct app_params *app)
>  	check_sinks(app);
>  	check_msgqs(app);
>  	check_pipelines(app);
> +	check_kni(app);
> 
>  	return 0;
>  }
> diff --git a/examples/ip_pipeline/config_parse.c
> b/examples/ip_pipeline/config_parse.c
> index e5efd03..e9cd5a4 100644
> --- a/examples/ip_pipeline/config_parse.c
> +++ b/examples/ip_pipeline/config_parse.c
> @@ -209,6 +209,15 @@ struct app_pipeline_params
> default_pipeline_params = {
>  	.n_args = 0,
>  };
> 
> +struct app_kni_params default_kni_params = {
> +	.parsed = 0,
> +	.socket_id = 0,
> +	.core_id = 0,
> +	.hyper_th_id = 0,
> +	.mempool_id = 0,
> +	.burst = 32,
> +};
> +
>  static const char app_usage[] =
>  	"Usage: %s [-f CONFIG_FILE] [-s SCRIPT_FILE] [-p PORT_MASK] "
>  	"[-l LOG_LEVEL] [--preproc PREPROCESSOR] [--preproc-args
> ARGS]\n"
> @@ -1169,6 +1178,9 @@ parse_pipeline_pktq_in(struct app_params *app,
>  		} else if (validate_name(name, "SOURCE", 1) == 0) {
>  			type = APP_PKTQ_IN_SOURCE;
>  			id = APP_PARAM_ADD(app->source_params, name);
> +		} else if (validate_name(name, "KNI", 1) == 0) {
> +			type = APP_PKTQ_IN_KNI;
> +			id = APP_PARAM_ADD(app->kni_params, name);
>  		} else
>  			return -EINVAL;
> 
> @@ -1240,6 +1252,9 @@ parse_pipeline_pktq_out(struct app_params *app,
>  		} else if (validate_name(name, "SINK", 1) == 0) {
>  			type = APP_PKTQ_OUT_SINK;
>  			id = APP_PARAM_ADD(app->sink_params, name);
> +		} else if (validate_name(name, "KNI", 1) == 0) {
> +			type = APP_PKTQ_OUT_KNI;
> +			id = APP_PARAM_ADD(app->kni_params, name);
>  		} else
>  			return -EINVAL;
> 
> @@ -2459,6 +2474,76 @@ parse_msgq(struct app_params *app,
>  	free(entries);
>  }
> 
> +static void
> +parse_kni(struct app_params *app,
> +		   const char *section_name,
> +		   struct rte_cfgfile *cfg)
> +{
> +	struct app_kni_params *param;
> +	struct rte_cfgfile_entry *entries;
> +	int n_entries, i;
> +	ssize_t param_idx;
> +
> +	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
> +	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0),
> section_name);
> +
> +	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
> +	PARSE_ERROR_MALLOC(entries != NULL);
> +
> +	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
> +
> +	param_idx = APP_PARAM_ADD(app->kni_params, section_name);
> +	PARSER_PARAM_ADD_CHECK(param_idx, app->kni_params,
> section_name);
> +
> +	param = &app->kni_params[param_idx];
> +
> +	for (i = 0; i < n_entries; i++) {
> +		struct rte_cfgfile_entry *ent = &entries[i];
> +
> +		if (strcmp(ent->name, "core") == 0) {
> +			int status = parse_pipeline_core(
> +				&param->socket_id, &param->core_id,
> +				&param->hyper_th_id, ent->value);
> +
> +			PARSE_ERROR((status == 0), section_name,
> +				ent->name);
> +			continue;
> +		}
> +
> +		if (strcmp(ent->name, "mempool") == 0) {
> +			int status = validate_name(ent->value,
> +				"MEMPOOL", 1);
> +			ssize_t idx;
> +
> +			PARSE_ERROR((status == 0), section_name,
> +				ent->name);
> +			idx = APP_PARAM_ADD(app->mempool_params,
> +				ent->value);
> +			PARSER_PARAM_ADD_CHECK(idx,
> +				app->mempool_params,
> +				section_name);
> +			param->mempool_id = idx;
> +			continue;
> +		}
> +
> +		if (strcmp(ent->name, "burst") == 0) {
> +			int status = parser_read_uint32(&param->burst,
> +				ent->value);
> +
> +			PARSE_ERROR((status == 0), section_name,
> +				ent->name);
> +			continue;
> +		}
> +
> +		/* unrecognized */
> +		PARSE_ERROR_INVALID(0, section_name, ent->name);
> +	}
> +
> +	param->parsed = 1;
> +
> +	free(entries);
> +}
> +
>  typedef void (*config_section_load)(struct app_params *p,
>  	const char *section_name,
>  	struct rte_cfgfile *cfg);
> @@ -2483,6 +2568,7 @@ static const struct config_section cfg_file_scheme[]
> = {
>  	{"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
>  	{"MSGQ-RSP-PIPELINE", 1, parse_msgq_rsp_pipeline},
>  	{"MSGQ", 1, parse_msgq},
> +	{"KNI", 1, parse_kni},
>  };
> 
>  static void
> @@ -2619,6 +2705,7 @@ app_config_parse(struct app_params *app, const
> char *file_name)
>  	APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
>  	APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
>  	APP_PARAM_COUNT(app->pipeline_params, app->n_pipelines);
> +	APP_PARAM_COUNT(app->kni_params, app->n_kni);
> 
>  #ifdef RTE_PORT_PCAP
>  	for (i = 0; i < (int)app->n_pktq_source; i++) {
> @@ -3025,6 +3112,9 @@ save_pipeline_params(struct app_params *app,
> FILE *f)
>  				case APP_PKTQ_IN_SOURCE:
>  					name = app->source_params[pp-
> >id].name;
>  					break;
> +				case APP_PKTQ_IN_KNI:
> +					name = app->kni_params[pp-
> >id].name;
> +					break;
>  				default:
>  					APP_CHECK(0, "System error "
>  						"occurred while saving "
> @@ -3059,6 +3149,9 @@ save_pipeline_params(struct app_params *app,
> FILE *f)
>  				case APP_PKTQ_OUT_SINK:
>  					name = app->sink_params[pp-
> >id].name;
>  					break;
> +				case APP_PKTQ_OUT_KNI:
> +					name = app->kni_params[pp-
> >id].name;
> +					break;
>  				default:
>  					APP_CHECK(0, "System error "
>  						"occurred while saving "
> @@ -3114,6 +3207,37 @@ save_pipeline_params(struct app_params *app,
> FILE *f)
>  	}
>  }
> 
> +static void
> +save_kni_params(struct app_params *app, FILE *f)
> +{
> +	struct app_kni_params *p;
> +	size_t i, count;
> +
> +	count = RTE_DIM(app->kni_params);
> +	for (i = 0; i < count; i++) {
> +		p = &app->kni_params[i];
> +		if (!APP_PARAM_VALID(p))
> +			continue;
> +
> +		/* section name */
> +		fprintf(f, "[%s]\n", p->name);
> +
> +		/* core */
> +		fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
> +				p->socket_id,
> +				p->core_id,
> +				(p->hyper_th_id) ? "h" : "");
> +
> +		/* mempool */
> +		fprintf(f, "%s = %" PRIu32 "\n", "mempool_id", p-
> >mempool_id);
> +
> +		/* burst */
> +		fprintf(f, "%s = %" PRIu32 "\n", "burst", p->burst);
> +
> +		fputc('\n', f);
> +	}
> +}
> +
>  void
>  app_config_save(struct app_params *app, const char *file_name)
>  {
> @@ -3144,6 +3268,7 @@ app_config_save(struct app_params *app, const
> char *file_name)
>  	save_source_params(app, file);
>  	save_sink_params(app, file);
>  	save_msgq_params(app, file);
> +	save_kni_params(app, file);
> 
>  	fclose(file);
>  	free(name);
> @@ -3206,6 +3331,11 @@ app_config_init(struct app_params *app)
>  			&default_pipeline_params,
>  			sizeof(default_pipeline_params));
> 
> +	for (i = 0; i < RTE_DIM(app->kni_params); i++)
> +		memcpy(&app->kni_params[i],
> +			&default_kni_params,
> +			sizeof(default_kni_params));
> +
>  	return 0;
>  }
> 
> diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
> index 02351f6..8ff9118 100644
> --- a/examples/ip_pipeline/init.c
> +++ b/examples/ip_pipeline/init.c
> @@ -89,6 +89,24 @@ app_init_core_mask(struct app_params *app)
>  		mask |= 1LLU << lcore_id;
>  	}
> 
> +	for (i = 0; i < app->n_kni; i++) {
> +		struct app_kni_params *p = &app->kni_params[i];
> +		int lcore_id;
> +
> +		lcore_id = cpu_core_map_get_lcore_id(app->core_map,
> +			p->socket_id,
> +			p->core_id,
> +			p->hyper_th_id);
> +
> +		if (lcore_id < 0)
> +			rte_panic("Cannot create CPU core mask\n");
> +
> +		if (mask & 1LLU << lcore_id)
> +			rte_panic("KNI interface must use a dedicated
> lcore\n");
> +
> +		mask |= 1LLU << lcore_id;
> +	}
> +
>  	app->core_mask = mask;
>  	APP_LOG(app, HIGH, "CPU core mask = 0x%016" PRIx64, app-
> >core_mask);
>  }
> @@ -1236,6 +1254,11 @@ static void app_pipeline_params_get(struct
> app_params *app,
>  					n_bytes_per_pkt;
>  			}
>  			break;
> +		case APP_PKTQ_IN_KNI:
> +			out->type = PIPELINE_PORT_IN_KNI;
> +			out->params.kni.kni = app->kni[in->id];
> +			out->burst_size = app->kni_params[in->id].burst;
> +			break;
>  		default:
>  			break;
>  		}
> @@ -1374,6 +1397,12 @@ static void app_pipeline_params_get(struct
> app_params *app,
>  				out->params.sink.max_n_pkts = 0;
>  			}
>  			break;
> +		case APP_PKTQ_OUT_KNI:
> +			out->type = PIPELINE_PORT_OUT_KNI;
> +			out->params.kni.kni = app->kni[in->id];
> +			out->params.kni.tx_burst_sz =
> +					app->kni_params[in->id].burst;
> +			break;
>  		default:
>  			break;
>  		}
> @@ -1397,6 +1426,55 @@ static void app_pipeline_params_get(struct
> app_params *app,
>  }
> 
>  static void
> +app_init_kni(struct app_params *app) {
> +	uint32_t i;
> +	struct rte_kni_conf conf;
> +
> +	rte_kni_init((unsigned int)app->n_kni);
> +
> +	for (i = 0; i < app->n_kni; i++) {
> +		struct app_kni_params *p_kni = &app->kni_params[i];
> +		uint32_t port_id;
> +		struct app_mempool_params *mempool_params;
> +		struct rte_mempool *mempool;
> +
> +		if (sscanf(p_kni->name, "KNI%" PRIu32, &port_id) != 1)
> +			rte_panic("%s's port id is invalid\n", p_kni->name);
> +
> +		mempool_params = &app->mempool_params[p_kni-
> >mempool_id];
> +		mempool = app->mempool[p_kni->mempool_id];
> +
> +		memset(&conf, 0, sizeof(conf));
> +		snprintf(conf.name, RTE_KNI_NAMESIZE,
> +				 "vEth%u", port_id);
> +		conf.core_id = p_kni->core_id;
> +		conf.force_bind = 1;
> +
> +		conf.group_id = (uint16_t) port_id;
> +		conf.mbuf_size = mempool_params->buffer_size;
> +
> +		struct rte_kni_ops ops;
> +		struct rte_eth_dev_info dev_info;
> +
> +		memset(&dev_info, 0, sizeof(dev_info));
> +		rte_eth_dev_info_get(app->link_params[port_id].pmd_id,
> +							 &dev_info);
> +		conf.addr = dev_info.pci_dev->addr;
> +		conf.id = dev_info.pci_dev->id;
> +
> +		memset(&ops, 0, sizeof(ops));
> +		ops.port_id = app->link_params[port_id].pmd_id;
> +		ops.change_mtu = kni_change_mtu;
> +		ops.config_network_if = kni_config_network_interface;
> +
> +		app->kni[i] = rte_kni_alloc(mempool,
> +			&conf, &ops);
> +		if (!app->kni[i])
> +			rte_panic("Fail to create kni for port: %d\n", port_id);
> +	}
> +}
> +
> +static void
>  app_init_pipelines(struct app_params *app)
>  {
>  	uint32_t p_id;
> @@ -1531,6 +1609,7 @@ int app_init(struct app_params *app)
>  	app_init_swq(app);
>  	app_init_tm(app);
>  	app_init_msgq(app);
> +	app_init_kni(app);
> 
>  	app_pipeline_common_cmd_push(app);
>  	app_pipeline_thread_cmd_push(app);
> diff --git a/examples/ip_pipeline/kni/kni.c b/examples/ip_pipeline/kni/kni.c
> new file mode 100644
> index 0000000..c58e146
> --- /dev/null
> +++ b/examples/ip_pipeline/kni/kni.c
> @@ -0,0 +1,80 @@
> +#include <string.h>
> +
> +#include <rte_common.h>
> +#include <rte_malloc.h>
> +#include <rte_table_array.h>
> +#include <rte_kni.h>
> +#include <rte_ethdev.h>
> +
> +#include "rte_port_kni.h"
> +#include "kni.h"
> +
> +int
> +kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
> +	int ret = 0;
> +
> +	if (port_id >= rte_eth_dev_count() || port_id >=
> RTE_MAX_ETHPORTS) {
> +		RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
> +				__func__, port_id);
> +		return -EINVAL;
> +	}
> +
> +	RTE_LOG(INFO, PORT, "%s: Configure network interface of %d
> %s\n",
> +			__func__, port_id, if_up ? "up" : "down");
> +
> +	if (if_up != 0) { /* Configure network interface up */
> +		rte_eth_dev_stop(port_id);
> +		ret = rte_eth_dev_start(port_id);
> +	} else /* Configure network interface down */
> +		rte_eth_dev_stop(port_id);
> +
> +	if (ret < 0)
> +		RTE_LOG(ERR, PORT, "%s: Failed to start port %d\n",
> +				__func__, port_id);
> +
> +	return ret;
> +}
> +
> +int
> +kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
> +	int ret;
> +
> +	if (port_id >= rte_eth_dev_count()) {
> +		RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
> +				__func__, port_id);
> +		return -EINVAL;
> +	}
> +
> +	if (new_mtu > ETHER_MAX_LEN) {
> +		RTE_LOG(ERR, PORT,
> +				"%s: Fail to reconfigure port %d, the new
> MTU is too big\n",
> +				__func__, port_id);
> +		return -EINVAL;
> +	}
> +
> +	RTE_LOG(INFO, PORT, "%s: Change MTU of port %d to %u\n",
> +			__func__, port_id,
> +			new_mtu);
> +
> +	/* Stop specific port */
> +	rte_eth_dev_stop(port_id);
> +
> +	/* Set new MTU */
> +	ret = rte_eth_dev_set_mtu(port_id, new_mtu);
> +	if (ret < 0) {
> +		RTE_LOG(ERR, PORT, "%s: Fail to reconfigure port %d\n",
> +				__func__, port_id);
> +		return ret;
> +	}
> +
> +	/* Restart specific port */
> +	ret = rte_eth_dev_start(port_id);
> +	if (ret < 0) {
> +		RTE_LOG(ERR, PORT, "%s: Fail to restart port %d\n",
> +				__func__, port_id);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> diff --git a/examples/ip_pipeline/kni/kni.h b/examples/ip_pipeline/kni/kni.h
> new file mode 100644
> index 0000000..04c8429
> --- /dev/null
> +++ b/examples/ip_pipeline/kni/kni.h
> @@ -0,0 +1,16 @@
> +#ifndef __INCLUDE_KNI_H__
> +#define __INCLUDE_KNI_H__
> +
> +#include <rte_common.h>
> +
> +/* Total octets in ethernet header */
> +#define KNI_ENET_HEADER_SIZE    14
> +
> +/* Total octets in the FCS */
> +#define KNI_ENET_FCS_SIZE       4
> +
> +int kni_config_network_interface(uint8_t port_id, uint8_t if_up);
> +
> +int kni_change_mtu(uint8_t port_id, unsigned new_mtu);
> +
> +#endif
> diff --git a/examples/ip_pipeline/pipeline_be.h
> b/examples/ip_pipeline/pipeline_be.h
> index f4ff262..23f0438 100644
> --- a/examples/ip_pipeline/pipeline_be.h
> +++ b/examples/ip_pipeline/pipeline_be.h
> @@ -40,6 +40,7 @@
>  #include <rte_port_ras.h>
>  #include <rte_port_sched.h>
>  #include <rte_port_source_sink.h>
> +#include <rte_port_kni.h>
>  #include <rte_pipeline.h>
> 
>  enum pipeline_port_in_type {
> @@ -50,6 +51,7 @@ enum pipeline_port_in_type {
>  	PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
>  	PIPELINE_PORT_IN_SCHED_READER,
>  	PIPELINE_PORT_IN_SOURCE,
> +	PIPELINE_PORT_IN_KNI,
>  };
> 
>  struct pipeline_port_in_params {
> @@ -62,6 +64,7 @@ struct pipeline_port_in_params {
>  		struct rte_port_ring_reader_ipv6_frag_params
> ring_ipv6_frag;
>  		struct rte_port_sched_reader_params sched;
>  		struct rte_port_source_params source;
> +		struct rte_port_kni_reader_params kni;
>  	} params;
>  	uint32_t burst_size;
>  };
> @@ -84,6 +87,8 @@ pipeline_port_in_params_convert(struct
> pipeline_port_in_params  *p)
>  		return (void *) &p->params.sched;
>  	case PIPELINE_PORT_IN_SOURCE:
>  		return (void *) &p->params.source;
> +	case PIPELINE_PORT_IN_KNI:
> +		return (void *) &p->params.kni;
>  	default:
>  		return NULL;
>  	}
> @@ -107,6 +112,8 @@ pipeline_port_in_params_get_ops(struct
> pipeline_port_in_params  *p)
>  		return &rte_port_sched_reader_ops;
>  	case PIPELINE_PORT_IN_SOURCE:
>  		return &rte_port_source_ops;
> +	case PIPELINE_PORT_IN_KNI:
> +		return &rte_port_kni_reader_ops;
>  	default:
>  		return NULL;
>  	}
> @@ -123,6 +130,7 @@ enum pipeline_port_out_type {
>  	PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
>  	PIPELINE_PORT_OUT_SCHED_WRITER,
>  	PIPELINE_PORT_OUT_SINK,
> +	PIPELINE_PORT_OUT_KNI,
>  };
> 
>  struct pipeline_port_out_params {
> @@ -138,6 +146,7 @@ struct pipeline_port_out_params {
>  		struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
>  		struct rte_port_sched_writer_params sched;
>  		struct rte_port_sink_params sink;
> +		struct rte_port_kni_writer_params kni;
>  	} params;
>  };
> 
> @@ -165,6 +174,8 @@ pipeline_port_out_params_convert(struct
> pipeline_port_out_params  *p)
>  		return (void *) &p->params.sched;
>  	case PIPELINE_PORT_OUT_SINK:
>  		return (void *) &p->params.sink;
> +	case PIPELINE_PORT_OUT_KNI:
> +		return (void *) &p->params.kni;
>  	default:
>  		return NULL;
>  	}
> @@ -194,6 +205,8 @@ pipeline_port_out_params_get_ops(struct
> pipeline_port_out_params  *p)
>  		return &rte_port_sched_writer_ops;
>  	case PIPELINE_PORT_OUT_SINK:
>  		return &rte_port_sink_ops;
> +	case PIPELINE_PORT_OUT_KNI:
> +		return &rte_port_kni_writer_ops;
>  	default:
>  		return NULL;
>  	}
> diff --git a/examples/ip_pipeline/thread.c b/examples/ip_pipeline/thread.c
> index a0f1f12..534864a 100644
> --- a/examples/ip_pipeline/thread.c
> +++ b/examples/ip_pipeline/thread.c
> @@ -239,6 +239,15 @@ app_thread(void *arg)
>  	uint32_t core_id = rte_lcore_id(), i, j;
>  	struct app_thread_data *t = &app->thread_data[core_id];
> 
> +	for (i = 0; i < app->n_kni; i++) {
> +		if (core_id == (uint32_t)cpu_core_map_get_lcore_id(
> +			app->core_map,
> +			app->kni_params[i].socket_id,
> +			app->kni_params[i].core_id,
> +			app->kni_params[i].hyper_th_id))
> +			return 0;
> +	}
> +
>  	for (i = 0; ; i++) {
>  		uint32_t n_regular = RTE_MIN(t->n_regular, RTE_DIM(t-
> >regular));
>  		uint32_t n_custom = RTE_MIN(t->n_custom, RTE_DIM(t-
> >custom));
> diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
> index d4de5af..f18253d 100644
> --- a/lib/librte_port/Makefile
> +++ b/lib/librte_port/Makefile
> @@ -57,6 +57,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
>  endif
>  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
> +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> +SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
> +endif
> 
>  # install includes
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port.h
> @@ -68,6 +71,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=
> rte_port_ras.h
>  endif
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
> +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> +SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
> +endif
> 
>  # this lib depends upon:
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
> @@ -75,5 +81,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=
> lib/librte_mbuf
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
> 
>  include $(RTE_SDK)/mk/rte.lib.mk
> diff --git a/lib/librte_port/rte_port_kni.c b/lib/librte_port/rte_port_kni.c
> new file mode 100644
> index 0000000..8c5e404
> --- /dev/null
> +++ b/lib/librte_port/rte_port_kni.c
> @@ -0,0 +1,316 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> + */
> +#include <string.h>
> +
> +#include <rte_common.h>
> +#include <rte_malloc.h>
> +#include <rte_kni.h>
> +
> +#include "rte_port_kni.h"
> +
> +/*
> + * Port KNI Reader
> + */
> +#ifdef RTE_PORT_STATS_COLLECT
> +
> +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
> +	{port->stats.n_pkts_in += val}
> +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
> +	{port->stats.n_pkts_drop += val}
> +
> +#else
> +
> +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
> +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
> +
> +#endif
> +
> +struct rte_port_kni_reader {
> +	struct rte_port_in_stats stats;
> +
> +	struct rte_kni *kni;
> +};
> +
> +static void *
> +rte_port_kni_reader_create(void *params, int socket_id) {
> +	struct rte_port_kni_reader_params *conf =
> +			(struct rte_port_kni_reader_params *) params;
> +	struct rte_port_kni_reader *port;
> +
> +	/* Check input parameters */
> +	if (conf == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
> +		return NULL;
> +	}
> +
> +	/* Memory allocation */
> +	port = rte_zmalloc_socket("PORT", sizeof(*port),
> +		RTE_CACHE_LINE_SIZE, socket_id);
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> __func__);
> +		return NULL;
> +	}
> +
> +	/* Initialization */
> +	port->kni = conf->kni;
> +
> +	return port;
> +}
> +
> +static int
> +rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t
> n_pkts) {
> +	struct rte_port_kni_reader *p =
> +			(struct rte_port_kni_reader *) port;
> +	uint16_t rx_pkt_cnt;
> +
> +	rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
> +	RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
> +	return rx_pkt_cnt;
> +}
> +
> +static int
> +rte_port_kni_reader_free(void *port) {
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	rte_free(port);
> +
> +	return 0;
> +}
> +
> +static int rte_port_kni_reader_stats_read(void *port,
> +	struct rte_port_in_stats *stats, int clear)
> +{
> +	struct rte_port_kni_reader *p =
> +			(struct rte_port_kni_reader *) port;
> +
> +	if (stats != NULL)
> +		memcpy(stats, &p->stats, sizeof(p->stats));
> +
> +	if (clear)
> +		memset(&p->stats, 0, sizeof(p->stats));
> +
> +	return 0;
> +}
> +
> +/*
> + * Port KNI Writer
> + */
> +#ifdef RTE_PORT_STATS_COLLECT
> +
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
> +	{port->stats.n_pkts_in += val}
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
> +	{port->stats.n_pkts_drop += val}
> +
> +#else
> +
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
> +
> +#endif
> +
> +struct rte_port_kni_writer {
> +	struct rte_port_out_stats stats;
> +
> +	struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
> +	uint32_t tx_burst_sz;
> +	uint16_t tx_buf_count;
> +	uint64_t bsz_mask;
> +	struct rte_kni *kni;
> +};
> +
> +static void *
> +rte_port_kni_writer_create(void *params, int socket_id) {
> +	struct rte_port_kni_writer_params *conf =
> +			(struct rte_port_kni_writer_params *) params;
> +	struct rte_port_kni_writer *port;
> +
> +	/* Check input parameters */
> +	if ((conf == NULL) ||
> +		(conf->tx_burst_sz == 0) ||
> +		(conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
> +		(!rte_is_power_of_2(conf->tx_burst_sz))) {
> +		RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n",
> __func__);
> +		return NULL;
> +	}
> +
> +	/* Memory allocation */
> +	port = rte_zmalloc_socket("PORT", sizeof(*port),
> +		RTE_CACHE_LINE_SIZE, socket_id);
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> __func__);
> +		return NULL;
> +	}
> +
> +	/* Initialization */
> +	port->kni = conf->kni;
> +	port->tx_burst_sz = conf->tx_burst_sz;
> +	port->tx_buf_count = 0;
> +	port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
> +
> +	return port;
> +}
> +
> +static inline void
> +send_burst(struct rte_port_kni_writer *p) {
> +	uint32_t nb_tx;
> +
> +	nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
> +	rte_kni_handle_request(p->kni);
> +
> +	RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p-
> >tx_buf_count - nb_tx);
> +	for (; nb_tx < p->tx_buf_count; nb_tx++)
> +		rte_pktmbuf_free(p->tx_buf[nb_tx]);
> +
> +	p->tx_buf_count = 0;
> +}
> +
> +static int
> +rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt) {
> +	struct rte_port_kni_writer *p =
> +			(struct rte_port_kni_writer *) port;
> +
> +	p->tx_buf[p->tx_buf_count++] = pkt;
> +	RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> +	if (p->tx_buf_count >= p->tx_burst_sz)
> +		send_burst(p);
> +
> +	return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_tx_bulk(void *port,
> +							struct rte_mbuf
> **pkts,
> +							uint64_t pkts_mask) {
> +	struct rte_port_kni_writer *p =
> +			(struct rte_port_kni_writer *) port;
> +	uint64_t bsz_mask = p->bsz_mask;
> +	uint32_t tx_buf_count = p->tx_buf_count;
> +	uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
> +					((pkts_mask & bsz_mask) ^
> bsz_mask);
> +
> +	if (expr == 0) {
> +		uint64_t n_pkts = __builtin_popcountll(pkts_mask);
> +		uint32_t n_pkts_ok;
> +
> +		if (tx_buf_count)
> +			send_burst(p);
> +
> +		RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
> +		n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
> +
> +		RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts
> - n_pkts_ok);
> +		for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
> +			struct rte_mbuf *pkt = pkts[n_pkts_ok];
> +
> +			rte_pktmbuf_free(pkt);
> +		}
> +	} else {
> +		for (; pkts_mask;) {
> +			uint32_t pkt_index = __builtin_ctzll(pkts_mask);
> +			uint64_t pkt_mask = 1LLU << pkt_index;
> +			struct rte_mbuf *pkt = pkts[pkt_index];
> +
> +			p->tx_buf[tx_buf_count++] = pkt;
> +			RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> +			pkts_mask &= ~pkt_mask;
> +		}
> +
> +		p->tx_buf_count = tx_buf_count;
> +		if (tx_buf_count >= p->tx_burst_sz)
> +			send_burst(p);
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_flush(void *port) {
> +	struct rte_port_kni_writer *p =
> +			(struct rte_port_kni_writer *) port;
> +
> +	if (p->tx_buf_count > 0)
> +		send_burst(p);
> +
> +	return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_free(void *port) {
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	rte_port_kni_writer_flush(port);
> +	rte_free(port);
> +
> +	return 0;
> +}
> +
> +static int rte_port_kni_writer_stats_read(void *port,
> +	struct rte_port_out_stats *stats, int clear)
> +{
> +	struct rte_port_kni_writer *p =
> +			(struct rte_port_kni_writer *) port;
> +
> +	if (stats != NULL)
> +		memcpy(stats, &p->stats, sizeof(p->stats));
> +
> +	if (clear)
> +		memset(&p->stats, 0, sizeof(p->stats));
> +
> +	return 0;
> +}
> +
> +/*
> + * Summary of port operations
> + */
> +struct rte_port_in_ops rte_port_kni_reader_ops = {
> +	.f_create = rte_port_kni_reader_create,
> +	.f_free = rte_port_kni_reader_free,
> +	.f_rx = rte_port_kni_reader_rx,
> +	.f_stats = rte_port_kni_reader_stats_read,
> +};
> +
> +struct rte_port_out_ops rte_port_kni_writer_ops = {
> +	.f_create = rte_port_kni_writer_create,
> +	.f_free = rte_port_kni_writer_free,
> +	.f_tx = rte_port_kni_writer_tx,
> +	.f_tx_bulk = rte_port_kni_writer_tx_bulk,
> +	.f_flush = rte_port_kni_writer_flush,
> +	.f_stats = rte_port_kni_writer_stats_read,
> +};
> diff --git a/lib/librte_port/rte_port_kni.h b/lib/librte_port/rte_port_kni.h
> new file mode 100644
> index 0000000..7623798
> --- /dev/null
> +++ b/lib/librte_port/rte_port_kni.h
> @@ -0,0 +1,81 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> + */
> +
> +#ifndef __INCLUDE_RTE_PORT_KNI_H__
> +#define __INCLUDE_RTE_PORT_KNI_H__
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/**
> + * @file
> + * RTE Port KNI Interface
> + *
> + * kni_reader: input port built on top of pre-initialized KNI interface
> + * kni_writer: output port built on top of pre-initialized KNI interface
> + *
> + ***/
> +
> +#include <stdint.h>
> +
> +#include <rte_kni.h>
> +
> +#include "rte_port.h"
> +
> +/** kni_reader port parameters */
> +struct rte_port_kni_reader_params {
> +	/** KNI interface reference */
> +	struct rte_kni *kni;
> +};
> +
> +/** kni_reader port operations */
> +extern struct rte_port_in_ops rte_port_kni_reader_ops;
> +
> +
> +/** kni_writer port parameters */
> +struct rte_port_kni_writer_params {
> +	/** KNI interface reference */
> +	struct rte_kni *kni;
> +	/** Burst size to KNI interface. */
> +	uint32_t tx_burst_sz;
> +};
> +
> +/** kni_writer port operations */
> +extern struct rte_port_out_ops rte_port_kni_writer_ops;
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
> --
> 2.7.4

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

* Re: [PATCH] port: add kni interface support
  2016-05-30 14:40   ` Dumitrescu, Cristian
@ 2016-06-01  4:18     ` Ethan
  0 siblings, 0 replies; 22+ messages in thread
From: Ethan @ 2016-06-01  4:18 UTC (permalink / raw)
  To: Dumitrescu, Cristian; +Cc: dev

Hi Cristian,

No problem. Enjoy your travel.
:-)

B.R.
Ethan

2016-05-30 22:40 GMT+08:00 Dumitrescu, Cristian <
cristian.dumitrescu@intel.com>:

> Hi Wei Jie,
>
> Thank you for submitting this patch. I am currently travelling, I will
> review your patch and come back to you hopefully later this week or early
> next week.
>
> Regards,
> Cristian
>
> > -----Original Message-----
> > From: WeiJie Zhuang [mailto:zhuangwj@gmail.com]
> > Sent: Saturday, May 28, 2016 12:26 PM
> > To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>
> > Cc: dev@dpdk.org; WeiJie Zhuang <zhuangwj@gmail.com>
> > Subject: [PATCH] port: add kni interface support
> >
> > 1. add KNI port type to the packet framework
> > 2. add KNI support to the IP Pipeline sample Application
> >
> > Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
> > ---
> > v2:
> > * Fix check patch error.
> > ---
> >  doc/api/doxy-api-index.md           |   1 +
> >  examples/ip_pipeline/Makefile       |   6 +-
> >  examples/ip_pipeline/app.h          |  74 +++++++++
> >  examples/ip_pipeline/config/kni.cfg |  12 ++
> >  examples/ip_pipeline/config_check.c |  34 ++++
> >  examples/ip_pipeline/config_parse.c | 130 +++++++++++++++
> >  examples/ip_pipeline/init.c         |  79 +++++++++
> >  examples/ip_pipeline/kni/kni.c      |  80 +++++++++
> >  examples/ip_pipeline/kni/kni.h      |  16 ++
> >  examples/ip_pipeline/pipeline_be.h  |  13 ++
> >  examples/ip_pipeline/thread.c       |   9 +
> >  lib/librte_port/Makefile            |   7 +
> >  lib/librte_port/rte_port_kni.c      | 316
> > ++++++++++++++++++++++++++++++++++++
> >  lib/librte_port/rte_port_kni.h      |  81 +++++++++
> >  14 files changed, 856 insertions(+), 2 deletions(-)
> >  create mode 100644 examples/ip_pipeline/config/kni.cfg
> >  create mode 100644 examples/ip_pipeline/kni/kni.c
> >  create mode 100644 examples/ip_pipeline/kni/kni.h
> >  create mode 100644 lib/librte_port/rte_port_kni.c
> >  create mode 100644 lib/librte_port/rte_port_kni.h
> >
> > diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> > index f626386..e38a959 100644
> > --- a/doc/api/doxy-api-index.md
> > +++ b/doc/api/doxy-api-index.md
> > @@ -119,6 +119,7 @@ There are many libraries, so their headers may be
> > grouped by topics:
> >      [reass]            (@ref rte_port_ras.h),
> >      [sched]            (@ref rte_port_sched.h),
> >      [src/sink]         (@ref rte_port_source_sink.h)
> > +    [kni]              (@ref rte_port_kni.h)
> >    * [table]            (@ref rte_table.h):
> >      [lpm IPv4]         (@ref rte_table_lpm.h),
> >      [lpm IPv6]         (@ref rte_table_lpm_ipv6.h),
> > diff --git a/examples/ip_pipeline/Makefile
> b/examples/ip_pipeline/Makefile
> > index 10fe1ba..848c2aa 100644
> > --- a/examples/ip_pipeline/Makefile
> > +++ b/examples/ip_pipeline/Makefile
> > @@ -43,9 +43,10 @@ include $(RTE_SDK)/mk/rte.vars.mk
> >  # binary name
> >  APP = ip_pipeline
> >
> > +VPATH += $(SRCDIR)/kni
> >  VPATH += $(SRCDIR)/pipeline
> >
> > -INC += $(wildcard *.h) $(wildcard pipeline/*.h)
> > +INC += $(wildcard *.h) $(wildcard pipeline/*.h) $(wildcard kni/*.h)
> >
> >  # all source are stored in SRCS-y
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) := main.c
> > @@ -56,6 +57,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread_fe.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
> > +SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += kni.c
> >
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_be.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_fe.c
> > @@ -72,7 +74,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) +=
> > pipeline_flow_actions.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing_be.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing.c
> >
> > -CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline
> > +CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline -I$(SRCDIR)/kni
> >  CFLAGS += -O3
> >  CFLAGS += $(WERROR_FLAGS) -Wno-error=unused-function -Wno-
> > error=unused-variable
> >
> > diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
> > index e775024..a86ce57 100644
> > --- a/examples/ip_pipeline/app.h
> > +++ b/examples/ip_pipeline/app.h
> > @@ -44,7 +44,9 @@
> >  #include <cmdline_parse.h>
> >
> >  #include <rte_ethdev.h>
> > +#include <rte_kni.h>
> >
> > +#include "kni.h"
> >  #include "cpu_core_map.h"
> >  #include "pipeline.h"
> >
> > @@ -99,6 +101,18 @@ struct app_pktq_hwq_out_params {
> >       struct rte_eth_txconf conf;
> >  };
> >
> > +struct app_kni_params {
> > +     char *name;
> > +     uint32_t parsed;
> > +
> > +     uint32_t socket_id;
> > +     uint32_t core_id;
> > +     uint32_t hyper_th_id;
> > +
> > +     uint32_t mempool_id;
> > +     uint32_t burst;
> > +};
> > +
> >  struct app_pktq_swq_params {
> >       char *name;
> >       uint32_t parsed;
> > @@ -172,6 +186,7 @@ enum app_pktq_in_type {
> >       APP_PKTQ_IN_SWQ,
> >       APP_PKTQ_IN_TM,
> >       APP_PKTQ_IN_SOURCE,
> > +     APP_PKTQ_IN_KNI,
> >  };
> >
> >  struct app_pktq_in_params {
> > @@ -184,6 +199,7 @@ enum app_pktq_out_type {
> >       APP_PKTQ_OUT_SWQ,
> >       APP_PKTQ_OUT_TM,
> >       APP_PKTQ_OUT_SINK,
> > +     APP_PKTQ_OUT_KNI,
> >  };
> >
> >  struct app_pktq_out_params {
> > @@ -434,6 +450,10 @@ struct app_eal_params {
> >  #define APP_THREAD_HEADROOM_STATS_COLLECT        1
> >  #endif
> >
> > +#ifndef APP_MAX_KNI
> > +#define APP_MAX_KNI                              8
> > +#endif
> > +
> >  struct app_params {
> >       /* Config */
> >       char app_name[APP_APPNAME_SIZE];
> > @@ -457,6 +477,7 @@ struct app_params {
> >       struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
> >       struct app_msgq_params msgq_params[APP_MAX_MSGQ];
> >       struct app_pipeline_params pipeline_params[APP_MAX_PIPELINES];
> > +     struct app_kni_params kni_params[APP_MAX_KNI];
> >
> >       uint32_t n_mempools;
> >       uint32_t n_links;
> > @@ -468,6 +489,7 @@ struct app_params {
> >       uint32_t n_pktq_sink;
> >       uint32_t n_msgq;
> >       uint32_t n_pipelines;
> > +     uint32_t n_kni;
> >
> >       /* Init */
> >       char *eal_argv[1 + APP_EAL_ARGC];
> > @@ -480,6 +502,7 @@ struct app_params {
> >       struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
> >       struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
> >       struct app_thread_data thread_data[APP_MAX_THREADS];
> > +     struct rte_kni *kni[APP_MAX_KNI];
> >       cmdline_parse_ctx_t cmds[APP_MAX_CMDS + 1];
> >
> >       int eal_argc;
> > @@ -738,6 +761,31 @@ app_msgq_get_readers(struct app_params *app,
> > struct app_msgq_params *msgq)
> >  }
> >
> >  static inline uint32_t
> > +app_kni_get_readers(struct app_params *app, struct app_kni_params
> > *kni)
> > +{
> > +     uint32_t pos = kni - app->kni_params;
> > +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> > +             RTE_DIM(app->pipeline_params));
> > +     uint32_t n_readers = 0, i;
> > +
> > +     for (i = 0; i < n_pipelines; i++) {
> > +             struct app_pipeline_params *p = &app->pipeline_params[i];
> > +             uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p-
> > >pktq_in));
> > +             uint32_t j;
> > +
> > +             for (j = 0; j < n_pktq_in; j++) {
> > +                     struct app_pktq_in_params *pktq = &p->pktq_in[j];
> > +
> > +                     if ((pktq->type == APP_PKTQ_IN_KNI) &&
> > +                             (pktq->id == pos))
> > +                             n_readers++;
> > +             }
> > +     }
> > +
> > +     return n_readers;
> > +}
> > +
> > +static inline uint32_t
> >  app_txq_get_writers(struct app_params *app, struct
> > app_pktq_hwq_out_params *txq)
> >  {
> >       uint32_t pos = txq - app->hwq_out_params;
> > @@ -863,6 +911,32 @@ app_msgq_get_writers(struct app_params *app,
> > struct app_msgq_params *msgq)
> >       return n_writers;
> >  }
> >
> > +static inline uint32_t
> > +app_kni_get_writers(struct app_params *app, struct app_kni_params *kni)
> > +{
> > +     uint32_t pos = kni - app->kni_params;
> > +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> > +             RTE_DIM(app->pipeline_params));
> > +     uint32_t n_writers = 0, i;
> > +
> > +     for (i = 0; i < n_pipelines; i++) {
> > +             struct app_pipeline_params *p = &app->pipeline_params[i];
> > +             uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
> > +                     RTE_DIM(p->pktq_out));
> > +             uint32_t j;
> > +
> > +             for (j = 0; j < n_pktq_out; j++) {
> > +                     struct app_pktq_out_params *pktq = &p-
> > >pktq_out[j];
> > +
> > +                     if ((pktq->type == APP_PKTQ_OUT_KNI) &&
> > +                             (pktq->id == pos))
> > +                             n_writers++;
> > +             }
> > +     }
> > +
> > +     return n_writers;
> > +}
> > +
> >  static inline struct app_link_params *
> >  app_get_link_for_rxq(struct app_params *app, struct
> > app_pktq_hwq_in_params *p)
> >  {
> > diff --git a/examples/ip_pipeline/config/kni.cfg
> > b/examples/ip_pipeline/config/kni.cfg
> > new file mode 100644
> > index 0000000..30466b0
> > --- /dev/null
> > +++ b/examples/ip_pipeline/config/kni.cfg
> > @@ -0,0 +1,12 @@
> > +[KNI0]
> > +core = 2
> > +
> > +[PIPELINE0]
> > +type = MASTER
> > +core = 0
> > +
> > +[PIPELINE1]
> > +type = PASS-THROUGH
> > +core = 1
> > +pktq_in = RXQ0.0 KNI0
> > +pktq_out = KNI0 TXQ0.0
> > diff --git a/examples/ip_pipeline/config_check.c
> > b/examples/ip_pipeline/config_check.c
> > index fd9ff49..3e300f9 100644
> > --- a/examples/ip_pipeline/config_check.c
> > +++ b/examples/ip_pipeline/config_check.c
> > @@ -426,6 +426,39 @@ check_pipelines(struct app_params *app)
> >       }
> >  }
> >
> > +static void
> > +check_kni(struct app_params *app) {
> > +     uint32_t i;
> > +     uint32_t port_id;
> > +
> > +     for (i = 0; i < app->n_kni; i++) {
> > +             struct app_kni_params *p = &app->kni_params[i];
> > +             uint32_t n_readers = app_kni_get_readers(app, p);
> > +             uint32_t n_writers = app_kni_get_writers(app, p);
> > +
> > +             APP_CHECK((n_readers != 0),
> > +                               "%s has no reader\n", p->name);
> > +
> > +             if (n_readers > 1)
> > +                     APP_LOG(app, LOW,
> > +                                     "%s has more than one reader", p-
> > >name);
> > +
> > +             APP_CHECK((n_writers != 0),
> > +                               "%s has no writer\n", p->name);
> > +
> > +             if (n_writers > 1)
> > +                     APP_LOG(app, LOW,
> > +                                     "%s has more than one writer", p-
> > >name);
> > +
> > +             APP_CHECK(sscanf(p->name, "KNI%" PRIu32, &port_id) == 1,
> > +                               "%s's port id is invalid\n", p->name);
> > +
> > +             APP_CHECK(port_id < app->n_links,
> > +                               "kni %s is not associated with a valid
> link\n",
> > +                               p->name);
> > +     }
> > +}
> > +
> >  int
> >  app_config_check(struct app_params *app)
> >  {
> > @@ -439,6 +472,7 @@ app_config_check(struct app_params *app)
> >       check_sinks(app);
> >       check_msgqs(app);
> >       check_pipelines(app);
> > +     check_kni(app);
> >
> >       return 0;
> >  }
> > diff --git a/examples/ip_pipeline/config_parse.c
> > b/examples/ip_pipeline/config_parse.c
> > index e5efd03..e9cd5a4 100644
> > --- a/examples/ip_pipeline/config_parse.c
> > +++ b/examples/ip_pipeline/config_parse.c
> > @@ -209,6 +209,15 @@ struct app_pipeline_params
> > default_pipeline_params = {
> >       .n_args = 0,
> >  };
> >
> > +struct app_kni_params default_kni_params = {
> > +     .parsed = 0,
> > +     .socket_id = 0,
> > +     .core_id = 0,
> > +     .hyper_th_id = 0,
> > +     .mempool_id = 0,
> > +     .burst = 32,
> > +};
> > +
> >  static const char app_usage[] =
> >       "Usage: %s [-f CONFIG_FILE] [-s SCRIPT_FILE] [-p PORT_MASK] "
> >       "[-l LOG_LEVEL] [--preproc PREPROCESSOR] [--preproc-args
> > ARGS]\n"
> > @@ -1169,6 +1178,9 @@ parse_pipeline_pktq_in(struct app_params *app,
> >               } else if (validate_name(name, "SOURCE", 1) == 0) {
> >                       type = APP_PKTQ_IN_SOURCE;
> >                       id = APP_PARAM_ADD(app->source_params, name);
> > +             } else if (validate_name(name, "KNI", 1) == 0) {
> > +                     type = APP_PKTQ_IN_KNI;
> > +                     id = APP_PARAM_ADD(app->kni_params, name);
> >               } else
> >                       return -EINVAL;
> >
> > @@ -1240,6 +1252,9 @@ parse_pipeline_pktq_out(struct app_params *app,
> >               } else if (validate_name(name, "SINK", 1) == 0) {
> >                       type = APP_PKTQ_OUT_SINK;
> >                       id = APP_PARAM_ADD(app->sink_params, name);
> > +             } else if (validate_name(name, "KNI", 1) == 0) {
> > +                     type = APP_PKTQ_OUT_KNI;
> > +                     id = APP_PARAM_ADD(app->kni_params, name);
> >               } else
> >                       return -EINVAL;
> >
> > @@ -2459,6 +2474,76 @@ parse_msgq(struct app_params *app,
> >       free(entries);
> >  }
> >
> > +static void
> > +parse_kni(struct app_params *app,
> > +                const char *section_name,
> > +                struct rte_cfgfile *cfg)
> > +{
> > +     struct app_kni_params *param;
> > +     struct rte_cfgfile_entry *entries;
> > +     int n_entries, i;
> > +     ssize_t param_idx;
> > +
> > +     n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
> > +     PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0),
> > section_name);
> > +
> > +     entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
> > +     PARSE_ERROR_MALLOC(entries != NULL);
> > +
> > +     rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
> > +
> > +     param_idx = APP_PARAM_ADD(app->kni_params, section_name);
> > +     PARSER_PARAM_ADD_CHECK(param_idx, app->kni_params,
> > section_name);
> > +
> > +     param = &app->kni_params[param_idx];
> > +
> > +     for (i = 0; i < n_entries; i++) {
> > +             struct rte_cfgfile_entry *ent = &entries[i];
> > +
> > +             if (strcmp(ent->name, "core") == 0) {
> > +                     int status = parse_pipeline_core(
> > +                             &param->socket_id, &param->core_id,
> > +                             &param->hyper_th_id, ent->value);
> > +
> > +                     PARSE_ERROR((status == 0), section_name,
> > +                             ent->name);
> > +                     continue;
> > +             }
> > +
> > +             if (strcmp(ent->name, "mempool") == 0) {
> > +                     int status = validate_name(ent->value,
> > +                             "MEMPOOL", 1);
> > +                     ssize_t idx;
> > +
> > +                     PARSE_ERROR((status == 0), section_name,
> > +                             ent->name);
> > +                     idx = APP_PARAM_ADD(app->mempool_params,
> > +                             ent->value);
> > +                     PARSER_PARAM_ADD_CHECK(idx,
> > +                             app->mempool_params,
> > +                             section_name);
> > +                     param->mempool_id = idx;
> > +                     continue;
> > +             }
> > +
> > +             if (strcmp(ent->name, "burst") == 0) {
> > +                     int status = parser_read_uint32(&param->burst,
> > +                             ent->value);
> > +
> > +                     PARSE_ERROR((status == 0), section_name,
> > +                             ent->name);
> > +                     continue;
> > +             }
> > +
> > +             /* unrecognized */
> > +             PARSE_ERROR_INVALID(0, section_name, ent->name);
> > +     }
> > +
> > +     param->parsed = 1;
> > +
> > +     free(entries);
> > +}
> > +
> >  typedef void (*config_section_load)(struct app_params *p,
> >       const char *section_name,
> >       struct rte_cfgfile *cfg);
> > @@ -2483,6 +2568,7 @@ static const struct config_section
> cfg_file_scheme[]
> > = {
> >       {"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
> >       {"MSGQ-RSP-PIPELINE", 1, parse_msgq_rsp_pipeline},
> >       {"MSGQ", 1, parse_msgq},
> > +     {"KNI", 1, parse_kni},
> >  };
> >
> >  static void
> > @@ -2619,6 +2705,7 @@ app_config_parse(struct app_params *app, const
> > char *file_name)
> >       APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
> >       APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
> >       APP_PARAM_COUNT(app->pipeline_params, app->n_pipelines);
> > +     APP_PARAM_COUNT(app->kni_params, app->n_kni);
> >
> >  #ifdef RTE_PORT_PCAP
> >       for (i = 0; i < (int)app->n_pktq_source; i++) {
> > @@ -3025,6 +3112,9 @@ save_pipeline_params(struct app_params *app,
> > FILE *f)
> >                               case APP_PKTQ_IN_SOURCE:
> >                                       name = app->source_params[pp-
> > >id].name;
> >                                       break;
> > +                             case APP_PKTQ_IN_KNI:
> > +                                     name = app->kni_params[pp-
> > >id].name;
> > +                                     break;
> >                               default:
> >                                       APP_CHECK(0, "System error "
> >                                               "occurred while saving "
> > @@ -3059,6 +3149,9 @@ save_pipeline_params(struct app_params *app,
> > FILE *f)
> >                               case APP_PKTQ_OUT_SINK:
> >                                       name = app->sink_params[pp-
> > >id].name;
> >                                       break;
> > +                             case APP_PKTQ_OUT_KNI:
> > +                                     name = app->kni_params[pp-
> > >id].name;
> > +                                     break;
> >                               default:
> >                                       APP_CHECK(0, "System error "
> >                                               "occurred while saving "
> > @@ -3114,6 +3207,37 @@ save_pipeline_params(struct app_params *app,
> > FILE *f)
> >       }
> >  }
> >
> > +static void
> > +save_kni_params(struct app_params *app, FILE *f)
> > +{
> > +     struct app_kni_params *p;
> > +     size_t i, count;
> > +
> > +     count = RTE_DIM(app->kni_params);
> > +     for (i = 0; i < count; i++) {
> > +             p = &app->kni_params[i];
> > +             if (!APP_PARAM_VALID(p))
> > +                     continue;
> > +
> > +             /* section name */
> > +             fprintf(f, "[%s]\n", p->name);
> > +
> > +             /* core */
> > +             fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
> > +                             p->socket_id,
> > +                             p->core_id,
> > +                             (p->hyper_th_id) ? "h" : "");
> > +
> > +             /* mempool */
> > +             fprintf(f, "%s = %" PRIu32 "\n", "mempool_id", p-
> > >mempool_id);
> > +
> > +             /* burst */
> > +             fprintf(f, "%s = %" PRIu32 "\n", "burst", p->burst);
> > +
> > +             fputc('\n', f);
> > +     }
> > +}
> > +
> >  void
> >  app_config_save(struct app_params *app, const char *file_name)
> >  {
> > @@ -3144,6 +3268,7 @@ app_config_save(struct app_params *app, const
> > char *file_name)
> >       save_source_params(app, file);
> >       save_sink_params(app, file);
> >       save_msgq_params(app, file);
> > +     save_kni_params(app, file);
> >
> >       fclose(file);
> >       free(name);
> > @@ -3206,6 +3331,11 @@ app_config_init(struct app_params *app)
> >                       &default_pipeline_params,
> >                       sizeof(default_pipeline_params));
> >
> > +     for (i = 0; i < RTE_DIM(app->kni_params); i++)
> > +             memcpy(&app->kni_params[i],
> > +                     &default_kni_params,
> > +                     sizeof(default_kni_params));
> > +
> >       return 0;
> >  }
> >
> > diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
> > index 02351f6..8ff9118 100644
> > --- a/examples/ip_pipeline/init.c
> > +++ b/examples/ip_pipeline/init.c
> > @@ -89,6 +89,24 @@ app_init_core_mask(struct app_params *app)
> >               mask |= 1LLU << lcore_id;
> >       }
> >
> > +     for (i = 0; i < app->n_kni; i++) {
> > +             struct app_kni_params *p = &app->kni_params[i];
> > +             int lcore_id;
> > +
> > +             lcore_id = cpu_core_map_get_lcore_id(app->core_map,
> > +                     p->socket_id,
> > +                     p->core_id,
> > +                     p->hyper_th_id);
> > +
> > +             if (lcore_id < 0)
> > +                     rte_panic("Cannot create CPU core mask\n");
> > +
> > +             if (mask & 1LLU << lcore_id)
> > +                     rte_panic("KNI interface must use a dedicated
> > lcore\n");
> > +
> > +             mask |= 1LLU << lcore_id;
> > +     }
> > +
> >       app->core_mask = mask;
> >       APP_LOG(app, HIGH, "CPU core mask = 0x%016" PRIx64, app-
> > >core_mask);
> >  }
> > @@ -1236,6 +1254,11 @@ static void app_pipeline_params_get(struct
> > app_params *app,
> >                                       n_bytes_per_pkt;
> >                       }
> >                       break;
> > +             case APP_PKTQ_IN_KNI:
> > +                     out->type = PIPELINE_PORT_IN_KNI;
> > +                     out->params.kni.kni = app->kni[in->id];
> > +                     out->burst_size = app->kni_params[in->id].burst;
> > +                     break;
> >               default:
> >                       break;
> >               }
> > @@ -1374,6 +1397,12 @@ static void app_pipeline_params_get(struct
> > app_params *app,
> >                               out->params.sink.max_n_pkts = 0;
> >                       }
> >                       break;
> > +             case APP_PKTQ_OUT_KNI:
> > +                     out->type = PIPELINE_PORT_OUT_KNI;
> > +                     out->params.kni.kni = app->kni[in->id];
> > +                     out->params.kni.tx_burst_sz =
> > +                                     app->kni_params[in->id].burst;
> > +                     break;
> >               default:
> >                       break;
> >               }
> > @@ -1397,6 +1426,55 @@ static void app_pipeline_params_get(struct
> > app_params *app,
> >  }
> >
> >  static void
> > +app_init_kni(struct app_params *app) {
> > +     uint32_t i;
> > +     struct rte_kni_conf conf;
> > +
> > +     rte_kni_init((unsigned int)app->n_kni);
> > +
> > +     for (i = 0; i < app->n_kni; i++) {
> > +             struct app_kni_params *p_kni = &app->kni_params[i];
> > +             uint32_t port_id;
> > +             struct app_mempool_params *mempool_params;
> > +             struct rte_mempool *mempool;
> > +
> > +             if (sscanf(p_kni->name, "KNI%" PRIu32, &port_id) != 1)
> > +                     rte_panic("%s's port id is invalid\n",
> p_kni->name);
> > +
> > +             mempool_params = &app->mempool_params[p_kni-
> > >mempool_id];
> > +             mempool = app->mempool[p_kni->mempool_id];
> > +
> > +             memset(&conf, 0, sizeof(conf));
> > +             snprintf(conf.name, RTE_KNI_NAMESIZE,
> > +                              "vEth%u", port_id);
> > +             conf.core_id = p_kni->core_id;
> > +             conf.force_bind = 1;
> > +
> > +             conf.group_id = (uint16_t) port_id;
> > +             conf.mbuf_size = mempool_params->buffer_size;
> > +
> > +             struct rte_kni_ops ops;
> > +             struct rte_eth_dev_info dev_info;
> > +
> > +             memset(&dev_info, 0, sizeof(dev_info));
> > +             rte_eth_dev_info_get(app->link_params[port_id].pmd_id,
> > +                                                      &dev_info);
> > +             conf.addr = dev_info.pci_dev->addr;
> > +             conf.id = dev_info.pci_dev->id;
> > +
> > +             memset(&ops, 0, sizeof(ops));
> > +             ops.port_id = app->link_params[port_id].pmd_id;
> > +             ops.change_mtu = kni_change_mtu;
> > +             ops.config_network_if = kni_config_network_interface;
> > +
> > +             app->kni[i] = rte_kni_alloc(mempool,
> > +                     &conf, &ops);
> > +             if (!app->kni[i])
> > +                     rte_panic("Fail to create kni for port: %d\n",
> port_id);
> > +     }
> > +}
> > +
> > +static void
> >  app_init_pipelines(struct app_params *app)
> >  {
> >       uint32_t p_id;
> > @@ -1531,6 +1609,7 @@ int app_init(struct app_params *app)
> >       app_init_swq(app);
> >       app_init_tm(app);
> >       app_init_msgq(app);
> > +     app_init_kni(app);
> >
> >       app_pipeline_common_cmd_push(app);
> >       app_pipeline_thread_cmd_push(app);
> > diff --git a/examples/ip_pipeline/kni/kni.c
> b/examples/ip_pipeline/kni/kni.c
> > new file mode 100644
> > index 0000000..c58e146
> > --- /dev/null
> > +++ b/examples/ip_pipeline/kni/kni.c
> > @@ -0,0 +1,80 @@
> > +#include <string.h>
> > +
> > +#include <rte_common.h>
> > +#include <rte_malloc.h>
> > +#include <rte_table_array.h>
> > +#include <rte_kni.h>
> > +#include <rte_ethdev.h>
> > +
> > +#include "rte_port_kni.h"
> > +#include "kni.h"
> > +
> > +int
> > +kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
> > +     int ret = 0;
> > +
> > +     if (port_id >= rte_eth_dev_count() || port_id >=
> > RTE_MAX_ETHPORTS) {
> > +             RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
> > +                             __func__, port_id);
> > +             return -EINVAL;
> > +     }
> > +
> > +     RTE_LOG(INFO, PORT, "%s: Configure network interface of %d
> > %s\n",
> > +                     __func__, port_id, if_up ? "up" : "down");
> > +
> > +     if (if_up != 0) { /* Configure network interface up */
> > +             rte_eth_dev_stop(port_id);
> > +             ret = rte_eth_dev_start(port_id);
> > +     } else /* Configure network interface down */
> > +             rte_eth_dev_stop(port_id);
> > +
> > +     if (ret < 0)
> > +             RTE_LOG(ERR, PORT, "%s: Failed to start port %d\n",
> > +                             __func__, port_id);
> > +
> > +     return ret;
> > +}
> > +
> > +int
> > +kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
> > +     int ret;
> > +
> > +     if (port_id >= rte_eth_dev_count()) {
> > +             RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
> > +                             __func__, port_id);
> > +             return -EINVAL;
> > +     }
> > +
> > +     if (new_mtu > ETHER_MAX_LEN) {
> > +             RTE_LOG(ERR, PORT,
> > +                             "%s: Fail to reconfigure port %d, the new
> > MTU is too big\n",
> > +                             __func__, port_id);
> > +             return -EINVAL;
> > +     }
> > +
> > +     RTE_LOG(INFO, PORT, "%s: Change MTU of port %d to %u\n",
> > +                     __func__, port_id,
> > +                     new_mtu);
> > +
> > +     /* Stop specific port */
> > +     rte_eth_dev_stop(port_id);
> > +
> > +     /* Set new MTU */
> > +     ret = rte_eth_dev_set_mtu(port_id, new_mtu);
> > +     if (ret < 0) {
> > +             RTE_LOG(ERR, PORT, "%s: Fail to reconfigure port %d\n",
> > +                             __func__, port_id);
> > +             return ret;
> > +     }
> > +
> > +     /* Restart specific port */
> > +     ret = rte_eth_dev_start(port_id);
> > +     if (ret < 0) {
> > +             RTE_LOG(ERR, PORT, "%s: Fail to restart port %d\n",
> > +                             __func__, port_id);
> > +             return ret;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > diff --git a/examples/ip_pipeline/kni/kni.h
> b/examples/ip_pipeline/kni/kni.h
> > new file mode 100644
> > index 0000000..04c8429
> > --- /dev/null
> > +++ b/examples/ip_pipeline/kni/kni.h
> > @@ -0,0 +1,16 @@
> > +#ifndef __INCLUDE_KNI_H__
> > +#define __INCLUDE_KNI_H__
> > +
> > +#include <rte_common.h>
> > +
> > +/* Total octets in ethernet header */
> > +#define KNI_ENET_HEADER_SIZE    14
> > +
> > +/* Total octets in the FCS */
> > +#define KNI_ENET_FCS_SIZE       4
> > +
> > +int kni_config_network_interface(uint8_t port_id, uint8_t if_up);
> > +
> > +int kni_change_mtu(uint8_t port_id, unsigned new_mtu);
> > +
> > +#endif
> > diff --git a/examples/ip_pipeline/pipeline_be.h
> > b/examples/ip_pipeline/pipeline_be.h
> > index f4ff262..23f0438 100644
> > --- a/examples/ip_pipeline/pipeline_be.h
> > +++ b/examples/ip_pipeline/pipeline_be.h
> > @@ -40,6 +40,7 @@
> >  #include <rte_port_ras.h>
> >  #include <rte_port_sched.h>
> >  #include <rte_port_source_sink.h>
> > +#include <rte_port_kni.h>
> >  #include <rte_pipeline.h>
> >
> >  enum pipeline_port_in_type {
> > @@ -50,6 +51,7 @@ enum pipeline_port_in_type {
> >       PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
> >       PIPELINE_PORT_IN_SCHED_READER,
> >       PIPELINE_PORT_IN_SOURCE,
> > +     PIPELINE_PORT_IN_KNI,
> >  };
> >
> >  struct pipeline_port_in_params {
> > @@ -62,6 +64,7 @@ struct pipeline_port_in_params {
> >               struct rte_port_ring_reader_ipv6_frag_params
> > ring_ipv6_frag;
> >               struct rte_port_sched_reader_params sched;
> >               struct rte_port_source_params source;
> > +             struct rte_port_kni_reader_params kni;
> >       } params;
> >       uint32_t burst_size;
> >  };
> > @@ -84,6 +87,8 @@ pipeline_port_in_params_convert(struct
> > pipeline_port_in_params  *p)
> >               return (void *) &p->params.sched;
> >       case PIPELINE_PORT_IN_SOURCE:
> >               return (void *) &p->params.source;
> > +     case PIPELINE_PORT_IN_KNI:
> > +             return (void *) &p->params.kni;
> >       default:
> >               return NULL;
> >       }
> > @@ -107,6 +112,8 @@ pipeline_port_in_params_get_ops(struct
> > pipeline_port_in_params  *p)
> >               return &rte_port_sched_reader_ops;
> >       case PIPELINE_PORT_IN_SOURCE:
> >               return &rte_port_source_ops;
> > +     case PIPELINE_PORT_IN_KNI:
> > +             return &rte_port_kni_reader_ops;
> >       default:
> >               return NULL;
> >       }
> > @@ -123,6 +130,7 @@ enum pipeline_port_out_type {
> >       PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
> >       PIPELINE_PORT_OUT_SCHED_WRITER,
> >       PIPELINE_PORT_OUT_SINK,
> > +     PIPELINE_PORT_OUT_KNI,
> >  };
> >
> >  struct pipeline_port_out_params {
> > @@ -138,6 +146,7 @@ struct pipeline_port_out_params {
> >               struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
> >               struct rte_port_sched_writer_params sched;
> >               struct rte_port_sink_params sink;
> > +             struct rte_port_kni_writer_params kni;
> >       } params;
> >  };
> >
> > @@ -165,6 +174,8 @@ pipeline_port_out_params_convert(struct
> > pipeline_port_out_params  *p)
> >               return (void *) &p->params.sched;
> >       case PIPELINE_PORT_OUT_SINK:
> >               return (void *) &p->params.sink;
> > +     case PIPELINE_PORT_OUT_KNI:
> > +             return (void *) &p->params.kni;
> >       default:
> >               return NULL;
> >       }
> > @@ -194,6 +205,8 @@ pipeline_port_out_params_get_ops(struct
> > pipeline_port_out_params  *p)
> >               return &rte_port_sched_writer_ops;
> >       case PIPELINE_PORT_OUT_SINK:
> >               return &rte_port_sink_ops;
> > +     case PIPELINE_PORT_OUT_KNI:
> > +             return &rte_port_kni_writer_ops;
> >       default:
> >               return NULL;
> >       }
> > diff --git a/examples/ip_pipeline/thread.c
> b/examples/ip_pipeline/thread.c
> > index a0f1f12..534864a 100644
> > --- a/examples/ip_pipeline/thread.c
> > +++ b/examples/ip_pipeline/thread.c
> > @@ -239,6 +239,15 @@ app_thread(void *arg)
> >       uint32_t core_id = rte_lcore_id(), i, j;
> >       struct app_thread_data *t = &app->thread_data[core_id];
> >
> > +     for (i = 0; i < app->n_kni; i++) {
> > +             if (core_id == (uint32_t)cpu_core_map_get_lcore_id(
> > +                     app->core_map,
> > +                     app->kni_params[i].socket_id,
> > +                     app->kni_params[i].core_id,
> > +                     app->kni_params[i].hyper_th_id))
> > +                     return 0;
> > +     }
> > +
> >       for (i = 0; ; i++) {
> >               uint32_t n_regular = RTE_MIN(t->n_regular, RTE_DIM(t-
> > >regular));
> >               uint32_t n_custom = RTE_MIN(t->n_custom, RTE_DIM(t-
> > >custom));
> > diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
> > index d4de5af..f18253d 100644
> > --- a/lib/librte_port/Makefile
> > +++ b/lib/librte_port/Makefile
> > @@ -57,6 +57,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
> >  endif
> >  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
> > +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> > +SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
> > +endif
> >
> >  # install includes
> >  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port.h
> > @@ -68,6 +71,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=
> > rte_port_ras.h
> >  endif
> >  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
> >  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
> > +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> > +SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
> > +endif
> >
> >  # this lib depends upon:
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
> > @@ -75,5 +81,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=
> > lib/librte_mbuf
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
> > +DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
> >
> >  include $(RTE_SDK)/mk/rte.lib.mk
> > diff --git a/lib/librte_port/rte_port_kni.c
> b/lib/librte_port/rte_port_kni.c
> > new file mode 100644
> > index 0000000..8c5e404
> > --- /dev/null
> > +++ b/lib/librte_port/rte_port_kni.c
> > @@ -0,0 +1,316 @@
> > +/*-
> > + *   BSD LICENSE
> > + *
> > + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> > + *   All rights reserved.
> > + *
> > + *   Redistribution and use in source and binary forms, with or without
> > + *   modification, are permitted provided that the following conditions
> > + *   are met:
> > + *
> > + *     * Redistributions of source code must retain the above copyright
> > + *       notice, this list of conditions and the following disclaimer.
> > + *     * Redistributions in binary form must reproduce the above
> copyright
> > + *       notice, this list of conditions and the following disclaimer in
> > + *       the documentation and/or other materials provided with the
> > + *       distribution.
> > + *     * Neither the name of Intel Corporation nor the names of its
> > + *       contributors may be used to endorse or promote products derived
> > + *       from this software without specific prior written permission.
> > + *
> > + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> > CONTRIBUTORS
> > + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> > FITNESS FOR
> > + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> > COPYRIGHT
> > + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> > INCIDENTAL,
> > + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> > OF USE,
> > + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> > AND ON ANY
> > + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> > TORT
> > + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> > THE USE
> > + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> > DAMAGE.
> > + */
> > +#include <string.h>
> > +
> > +#include <rte_common.h>
> > +#include <rte_malloc.h>
> > +#include <rte_kni.h>
> > +
> > +#include "rte_port_kni.h"
> > +
> > +/*
> > + * Port KNI Reader
> > + */
> > +#ifdef RTE_PORT_STATS_COLLECT
> > +
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
> > +     {port->stats.n_pkts_in += val}
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
> > +     {port->stats.n_pkts_drop += val}
> > +
> > +#else
> > +
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
> > +
> > +#endif
> > +
> > +struct rte_port_kni_reader {
> > +     struct rte_port_in_stats stats;
> > +
> > +     struct rte_kni *kni;
> > +};
> > +
> > +static void *
> > +rte_port_kni_reader_create(void *params, int socket_id) {
> > +     struct rte_port_kni_reader_params *conf =
> > +                     (struct rte_port_kni_reader_params *) params;
> > +     struct rte_port_kni_reader *port;
> > +
> > +     /* Check input parameters */
> > +     if (conf == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Memory allocation */
> > +     port = rte_zmalloc_socket("PORT", sizeof(*port),
> > +             RTE_CACHE_LINE_SIZE, socket_id);
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> > __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Initialization */
> > +     port->kni = conf->kni;
> > +
> > +     return port;
> > +}
> > +
> > +static int
> > +rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t
> > n_pkts) {
> > +     struct rte_port_kni_reader *p =
> > +                     (struct rte_port_kni_reader *) port;
> > +     uint16_t rx_pkt_cnt;
> > +
> > +     rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
> > +     RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
> > +     return rx_pkt_cnt;
> > +}
> > +
> > +static int
> > +rte_port_kni_reader_free(void *port) {
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
> > +             return -EINVAL;
> > +     }
> > +
> > +     rte_free(port);
> > +
> > +     return 0;
> > +}
> > +
> > +static int rte_port_kni_reader_stats_read(void *port,
> > +     struct rte_port_in_stats *stats, int clear)
> > +{
> > +     struct rte_port_kni_reader *p =
> > +                     (struct rte_port_kni_reader *) port;
> > +
> > +     if (stats != NULL)
> > +             memcpy(stats, &p->stats, sizeof(p->stats));
> > +
> > +     if (clear)
> > +             memset(&p->stats, 0, sizeof(p->stats));
> > +
> > +     return 0;
> > +}
> > +
> > +/*
> > + * Port KNI Writer
> > + */
> > +#ifdef RTE_PORT_STATS_COLLECT
> > +
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
> > +     {port->stats.n_pkts_in += val}
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
> > +     {port->stats.n_pkts_drop += val}
> > +
> > +#else
> > +
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
> > +
> > +#endif
> > +
> > +struct rte_port_kni_writer {
> > +     struct rte_port_out_stats stats;
> > +
> > +     struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
> > +     uint32_t tx_burst_sz;
> > +     uint16_t tx_buf_count;
> > +     uint64_t bsz_mask;
> > +     struct rte_kni *kni;
> > +};
> > +
> > +static void *
> > +rte_port_kni_writer_create(void *params, int socket_id) {
> > +     struct rte_port_kni_writer_params *conf =
> > +                     (struct rte_port_kni_writer_params *) params;
> > +     struct rte_port_kni_writer *port;
> > +
> > +     /* Check input parameters */
> > +     if ((conf == NULL) ||
> > +             (conf->tx_burst_sz == 0) ||
> > +             (conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
> > +             (!rte_is_power_of_2(conf->tx_burst_sz))) {
> > +             RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n",
> > __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Memory allocation */
> > +     port = rte_zmalloc_socket("PORT", sizeof(*port),
> > +             RTE_CACHE_LINE_SIZE, socket_id);
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> > __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Initialization */
> > +     port->kni = conf->kni;
> > +     port->tx_burst_sz = conf->tx_burst_sz;
> > +     port->tx_buf_count = 0;
> > +     port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
> > +
> > +     return port;
> > +}
> > +
> > +static inline void
> > +send_burst(struct rte_port_kni_writer *p) {
> > +     uint32_t nb_tx;
> > +
> > +     nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
> > +     rte_kni_handle_request(p->kni);
> > +
> > +     RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p-
> > >tx_buf_count - nb_tx);
> > +     for (; nb_tx < p->tx_buf_count; nb_tx++)
> > +             rte_pktmbuf_free(p->tx_buf[nb_tx]);
> > +
> > +     p->tx_buf_count = 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt) {
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +
> > +     p->tx_buf[p->tx_buf_count++] = pkt;
> > +     RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> > +     if (p->tx_buf_count >= p->tx_burst_sz)
> > +             send_burst(p);
> > +
> > +     return 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_tx_bulk(void *port,
> > +                                                     struct rte_mbuf
> > **pkts,
> > +                                                     uint64_t
> pkts_mask) {
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +     uint64_t bsz_mask = p->bsz_mask;
> > +     uint32_t tx_buf_count = p->tx_buf_count;
> > +     uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
> > +                                     ((pkts_mask & bsz_mask) ^
> > bsz_mask);
> > +
> > +     if (expr == 0) {
> > +             uint64_t n_pkts = __builtin_popcountll(pkts_mask);
> > +             uint32_t n_pkts_ok;
> > +
> > +             if (tx_buf_count)
> > +                     send_burst(p);
> > +
> > +             RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
> > +             n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
> > +
> > +             RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts
> > - n_pkts_ok);
> > +             for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
> > +                     struct rte_mbuf *pkt = pkts[n_pkts_ok];
> > +
> > +                     rte_pktmbuf_free(pkt);
> > +             }
> > +     } else {
> > +             for (; pkts_mask;) {
> > +                     uint32_t pkt_index = __builtin_ctzll(pkts_mask);
> > +                     uint64_t pkt_mask = 1LLU << pkt_index;
> > +                     struct rte_mbuf *pkt = pkts[pkt_index];
> > +
> > +                     p->tx_buf[tx_buf_count++] = pkt;
> > +                     RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> > +                     pkts_mask &= ~pkt_mask;
> > +             }
> > +
> > +             p->tx_buf_count = tx_buf_count;
> > +             if (tx_buf_count >= p->tx_burst_sz)
> > +                     send_burst(p);
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_flush(void *port) {
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +
> > +     if (p->tx_buf_count > 0)
> > +             send_burst(p);
> > +
> > +     return 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_free(void *port) {
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
> > +             return -EINVAL;
> > +     }
> > +
> > +     rte_port_kni_writer_flush(port);
> > +     rte_free(port);
> > +
> > +     return 0;
> > +}
> > +
> > +static int rte_port_kni_writer_stats_read(void *port,
> > +     struct rte_port_out_stats *stats, int clear)
> > +{
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +
> > +     if (stats != NULL)
> > +             memcpy(stats, &p->stats, sizeof(p->stats));
> > +
> > +     if (clear)
> > +             memset(&p->stats, 0, sizeof(p->stats));
> > +
> > +     return 0;
> > +}
> > +
> > +/*
> > + * Summary of port operations
> > + */
> > +struct rte_port_in_ops rte_port_kni_reader_ops = {
> > +     .f_create = rte_port_kni_reader_create,
> > +     .f_free = rte_port_kni_reader_free,
> > +     .f_rx = rte_port_kni_reader_rx,
> > +     .f_stats = rte_port_kni_reader_stats_read,
> > +};
> > +
> > +struct rte_port_out_ops rte_port_kni_writer_ops = {
> > +     .f_create = rte_port_kni_writer_create,
> > +     .f_free = rte_port_kni_writer_free,
> > +     .f_tx = rte_port_kni_writer_tx,
> > +     .f_tx_bulk = rte_port_kni_writer_tx_bulk,
> > +     .f_flush = rte_port_kni_writer_flush,
> > +     .f_stats = rte_port_kni_writer_stats_read,
> > +};
> > diff --git a/lib/librte_port/rte_port_kni.h
> b/lib/librte_port/rte_port_kni.h
> > new file mode 100644
> > index 0000000..7623798
> > --- /dev/null
> > +++ b/lib/librte_port/rte_port_kni.h
> > @@ -0,0 +1,81 @@
> > +/*-
> > + *   BSD LICENSE
> > + *
> > + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> > + *   All rights reserved.
> > + *
> > + *   Redistribution and use in source and binary forms, with or without
> > + *   modification, are permitted provided that the following conditions
> > + *   are met:
> > + *
> > + *     * Redistributions of source code must retain the above copyright
> > + *       notice, this list of conditions and the following disclaimer.
> > + *     * Redistributions in binary form must reproduce the above
> copyright
> > + *       notice, this list of conditions and the following disclaimer in
> > + *       the documentation and/or other materials provided with the
> > + *       distribution.
> > + *     * Neither the name of Intel Corporation nor the names of its
> > + *       contributors may be used to endorse or promote products derived
> > + *       from this software without specific prior written permission.
> > + *
> > + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> > CONTRIBUTORS
> > + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> > FITNESS FOR
> > + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> > COPYRIGHT
> > + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> > INCIDENTAL,
> > + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> > OF USE,
> > + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> > AND ON ANY
> > + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> > TORT
> > + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> > THE USE
> > + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> > DAMAGE.
> > + */
> > +
> > +#ifndef __INCLUDE_RTE_PORT_KNI_H__
> > +#define __INCLUDE_RTE_PORT_KNI_H__
> > +
> > +#ifdef __cplusplus
> > +extern "C" {
> > +#endif
> > +
> > +/**
> > + * @file
> > + * RTE Port KNI Interface
> > + *
> > + * kni_reader: input port built on top of pre-initialized KNI interface
> > + * kni_writer: output port built on top of pre-initialized KNI interface
> > + *
> > + ***/
> > +
> > +#include <stdint.h>
> > +
> > +#include <rte_kni.h>
> > +
> > +#include "rte_port.h"
> > +
> > +/** kni_reader port parameters */
> > +struct rte_port_kni_reader_params {
> > +     /** KNI interface reference */
> > +     struct rte_kni *kni;
> > +};
> > +
> > +/** kni_reader port operations */
> > +extern struct rte_port_in_ops rte_port_kni_reader_ops;
> > +
> > +
> > +/** kni_writer port parameters */
> > +struct rte_port_kni_writer_params {
> > +     /** KNI interface reference */
> > +     struct rte_kni *kni;
> > +     /** Burst size to KNI interface. */
> > +     uint32_t tx_burst_sz;
> > +};
> > +
> > +/** kni_writer port operations */
> > +extern struct rte_port_out_ops rte_port_kni_writer_ops;
> > +
> > +#ifdef __cplusplus
> > +}
> > +#endif
> > +
> > +#endif
> > --
> > 2.7.4
>
>

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

* Re: [PATCH] port: add kni interface support
  2016-05-28 11:25 ` [PATCH] port: add kni interface support WeiJie Zhuang
  2016-05-30 14:40   ` Dumitrescu, Cristian
@ 2016-06-09 23:42   ` Dumitrescu, Cristian
  2016-06-13 10:25     ` Dumitrescu, Cristian
  2016-06-13 10:47     ` Ethan
  2016-06-16 11:27   ` [PATCH v3 1/3] " WeiJie Zhuang
  2016-06-21 10:55   ` [PATCH v4 1/4] port: " Ethan Zhuang
  3 siblings, 2 replies; 22+ messages in thread
From: Dumitrescu, Cristian @ 2016-06-09 23:42 UTC (permalink / raw)
  To: WeiJie Zhuang; +Cc: dev, Singh, Jasvinder, Yigit, Ferruh

Hi Ethan,

Great work! There are still several comments below that need to be addressed, but I am confident we can close on them quickly. Thank you!

Please rebase the next version on top of the latest code on master branch.

Please also update librte_port/rte_port_version.map file.


> -----Original Message-----
> From: WeiJie Zhuang [mailto:zhuangwj@gmail.com]
> Sent: Saturday, May 28, 2016 12:26 PM
> To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>
> Cc: dev@dpdk.org; WeiJie Zhuang <zhuangwj@gmail.com>
> Subject: [PATCH] port: add kni interface support
> 
> 1. add KNI port type to the packet framework
> 2. add KNI support to the IP Pipeline sample Application
> 
> Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
> ---
> v2:
> * Fix check patch error.
> ---
>  doc/api/doxy-api-index.md           |   1 +
>  examples/ip_pipeline/Makefile       |   6 +-
>  examples/ip_pipeline/app.h          |  74 +++++++++
>  examples/ip_pipeline/config/kni.cfg |  12 ++
>  examples/ip_pipeline/config_check.c |  34 ++++
>  examples/ip_pipeline/config_parse.c | 130 +++++++++++++++
>  examples/ip_pipeline/init.c         |  79 +++++++++
>  examples/ip_pipeline/kni/kni.c      |  80 +++++++++
>  examples/ip_pipeline/kni/kni.h      |  16 ++
>  examples/ip_pipeline/pipeline_be.h  |  13 ++
>  examples/ip_pipeline/thread.c       |   9 +
>  lib/librte_port/Makefile            |   7 +
>  lib/librte_port/rte_port_kni.c      | 316
> ++++++++++++++++++++++++++++++++++++
>  lib/librte_port/rte_port_kni.h      |  81 +++++++++
>  14 files changed, 856 insertions(+), 2 deletions(-)
>  create mode 100644 examples/ip_pipeline/config/kni.cfg
>  create mode 100644 examples/ip_pipeline/kni/kni.c
>  create mode 100644 examples/ip_pipeline/kni/kni.h
>  create mode 100644 lib/librte_port/rte_port_kni.c
>  create mode 100644 lib/librte_port/rte_port_kni.h
> 
> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> index f626386..e38a959 100644
> --- a/doc/api/doxy-api-index.md
> +++ b/doc/api/doxy-api-index.md
> @@ -119,6 +119,7 @@ There are many libraries, so their headers may be
> grouped by topics:
>      [reass]            (@ref rte_port_ras.h),
>      [sched]            (@ref rte_port_sched.h),
>      [src/sink]         (@ref rte_port_source_sink.h)
> +    [kni]              (@ref rte_port_kni.h)
>    * [table]            (@ref rte_table.h):
>      [lpm IPv4]         (@ref rte_table_lpm.h),
>      [lpm IPv6]         (@ref rte_table_lpm_ipv6.h),
> diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
> index 10fe1ba..848c2aa 100644
> --- a/examples/ip_pipeline/Makefile
> +++ b/examples/ip_pipeline/Makefile
> @@ -43,9 +43,10 @@ include $(RTE_SDK)/mk/rte.vars.mk
>  # binary name
>  APP = ip_pipeline
> 
> +VPATH += $(SRCDIR)/kni
>  VPATH += $(SRCDIR)/pipeline
> 
> -INC += $(wildcard *.h) $(wildcard pipeline/*.h)
> +INC += $(wildcard *.h) $(wildcard pipeline/*.h) $(wildcard kni/*.h)
> 
>  # all source are stored in SRCS-y
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) := main.c
> @@ -56,6 +57,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread_fe.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
> +SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += kni.c
> 
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_be.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_fe.c
> @@ -72,7 +74,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) +=
> pipeline_flow_actions.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing_be.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing.c
> 
> -CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline
> +CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline -I$(SRCDIR)/kni
>  CFLAGS += -O3
>  CFLAGS += $(WERROR_FLAGS) -Wno-error=unused-function -Wno-
> error=unused-variable
> 

I would like to avoid creating the kni subfolder. Please move the functions from kni/kni.c to init.c file, just before function app_init_kni(), where they should be declared as static functions.

> diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
> index e775024..a86ce57 100644
> --- a/examples/ip_pipeline/app.h
> +++ b/examples/ip_pipeline/app.h
> @@ -44,7 +44,9 @@
>  #include <cmdline_parse.h>
> 
>  #include <rte_ethdev.h>
> +#include <rte_kni.h>
> 
> +#include "kni.h"
>  #include "cpu_core_map.h"
>  #include "pipeline.h"
> 
> @@ -99,6 +101,18 @@ struct app_pktq_hwq_out_params {
>  	struct rte_eth_txconf conf;
>  };
> 
> +struct app_kni_params {
> +	char *name;
> +	uint32_t parsed;
> +
> +	uint32_t socket_id;
> +	uint32_t core_id;
> +	uint32_t hyper_th_id;
> +
> +	uint32_t mempool_id;

Please add the usual comment on the same line: /* Position in the app->mempool_params */

> +	uint32_t burst;

Why having a unified value for read and write burst size? Please use burst_read and burst_write (both uint32_t) instead and update the config_parse.c and init.c code accordingly (small changes).

> +};
> +
>  struct app_pktq_swq_params {
>  	char *name;
>  	uint32_t parsed;
> @@ -172,6 +186,7 @@ enum app_pktq_in_type {
>  	APP_PKTQ_IN_SWQ,
>  	APP_PKTQ_IN_TM,
>  	APP_PKTQ_IN_SOURCE,
> +	APP_PKTQ_IN_KNI,
>  };
> 
>  struct app_pktq_in_params {
> @@ -184,6 +199,7 @@ enum app_pktq_out_type {
>  	APP_PKTQ_OUT_SWQ,
>  	APP_PKTQ_OUT_TM,
>  	APP_PKTQ_OUT_SINK,
> +	APP_PKTQ_OUT_KNI,
>  };
> 
>  struct app_pktq_out_params {
> @@ -434,6 +450,10 @@ struct app_eal_params {
>  #define APP_THREAD_HEADROOM_STATS_COLLECT        1
>  #endif
> 
> +#ifndef APP_MAX_KNI
> +#define APP_MAX_KNI                              8
> +#endif
> +
>  struct app_params {
>  	/* Config */
>  	char app_name[APP_APPNAME_SIZE];
> @@ -457,6 +477,7 @@ struct app_params {
>  	struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
>  	struct app_msgq_params msgq_params[APP_MAX_MSGQ];
>  	struct app_pipeline_params pipeline_params[APP_MAX_PIPELINES];
> +	struct app_kni_params kni_params[APP_MAX_KNI];
> 
>  	uint32_t n_mempools;
>  	uint32_t n_links;
> @@ -468,6 +489,7 @@ struct app_params {
>  	uint32_t n_pktq_sink;
>  	uint32_t n_msgq;
>  	uint32_t n_pipelines;
> +	uint32_t n_kni;
> 
>  	/* Init */
>  	char *eal_argv[1 + APP_EAL_ARGC];
> @@ -480,6 +502,7 @@ struct app_params {
>  	struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
>  	struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
>  	struct app_thread_data thread_data[APP_MAX_THREADS];
> +	struct rte_kni *kni[APP_MAX_KNI];
>  	cmdline_parse_ctx_t cmds[APP_MAX_CMDS + 1];
> 
>  	int eal_argc;
> @@ -738,6 +761,31 @@ app_msgq_get_readers(struct app_params *app,
> struct app_msgq_params *msgq)
>  }
> 
>  static inline uint32_t
> +app_kni_get_readers(struct app_params *app, struct app_kni_params
> *kni)
> +{
> +	uint32_t pos = kni - app->kni_params;
> +	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +		RTE_DIM(app->pipeline_params));
> +	uint32_t n_readers = 0, i;
> +
> +	for (i = 0; i < n_pipelines; i++) {
> +		struct app_pipeline_params *p = &app->pipeline_params[i];
> +		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p-
> >pktq_in));
> +		uint32_t j;
> +
> +		for (j = 0; j < n_pktq_in; j++) {
> +			struct app_pktq_in_params *pktq = &p->pktq_in[j];
> +
> +			if ((pktq->type == APP_PKTQ_IN_KNI) &&
> +				(pktq->id == pos))
> +				n_readers++;
> +		}
> +	}
> +
> +	return n_readers;
> +}
> +
> +static inline uint32_t
>  app_txq_get_writers(struct app_params *app, struct
> app_pktq_hwq_out_params *txq)
>  {
>  	uint32_t pos = txq - app->hwq_out_params;
> @@ -863,6 +911,32 @@ app_msgq_get_writers(struct app_params *app,
> struct app_msgq_params *msgq)
>  	return n_writers;
>  }
> 
> +static inline uint32_t
> +app_kni_get_writers(struct app_params *app, struct app_kni_params *kni)
> +{
> +	uint32_t pos = kni - app->kni_params;
> +	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +		RTE_DIM(app->pipeline_params));
> +	uint32_t n_writers = 0, i;
> +
> +	for (i = 0; i < n_pipelines; i++) {
> +		struct app_pipeline_params *p = &app->pipeline_params[i];
> +		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
> +			RTE_DIM(p->pktq_out));
> +		uint32_t j;
> +
> +		for (j = 0; j < n_pktq_out; j++) {
> +			struct app_pktq_out_params *pktq = &p-
> >pktq_out[j];
> +
> +			if ((pktq->type == APP_PKTQ_OUT_KNI) &&
> +				(pktq->id == pos))
> +				n_writers++;
> +		}
> +	}
> +
> +	return n_writers;
> +}
> +
>  static inline struct app_link_params *
>  app_get_link_for_rxq(struct app_params *app, struct
> app_pktq_hwq_in_params *p)
>  {
> diff --git a/examples/ip_pipeline/config/kni.cfg
> b/examples/ip_pipeline/config/kni.cfg
> new file mode 100644
> index 0000000..30466b0
> --- /dev/null
> +++ b/examples/ip_pipeline/config/kni.cfg
> @@ -0,0 +1,12 @@
> +[KNI0]
> +core = 2
> +
> +[PIPELINE0]
> +type = MASTER
> +core = 0
> +
> +[PIPELINE1]
> +type = PASS-THROUGH
> +core = 1
> +pktq_in = RXQ0.0 KNI0
> +pktq_out = KNI0 TXQ0.0

I don't think this new config file config/kni.cfg is really useful, so I would like to remove it. We should avoid the proliferation of straightforward config files.

> diff --git a/examples/ip_pipeline/config_check.c
> b/examples/ip_pipeline/config_check.c
> index fd9ff49..3e300f9 100644
> --- a/examples/ip_pipeline/config_check.c
> +++ b/examples/ip_pipeline/config_check.c
> @@ -426,6 +426,39 @@ check_pipelines(struct app_params *app)
>  	}
>  }
> 
> +static void
> +check_kni(struct app_params *app) {
> +	uint32_t i;
> +	uint32_t port_id;
> +
> +	for (i = 0; i < app->n_kni; i++) {
> +		struct app_kni_params *p = &app->kni_params[i];
> +		uint32_t n_readers = app_kni_get_readers(app, p);
> +		uint32_t n_writers = app_kni_get_writers(app, p);
> +
> +		APP_CHECK((n_readers != 0),
> +				  "%s has no reader\n", p->name);
> +
> +		if (n_readers > 1)
> +			APP_LOG(app, LOW,
> +					"%s has more than one reader", p-
> >name);
> +
> +		APP_CHECK((n_writers != 0),
> +				  "%s has no writer\n", p->name);
> +
> +		if (n_writers > 1)
> +			APP_LOG(app, LOW,
> +					"%s has more than one writer", p-
> >name);
> +

We should remove the next two checks. The reason is that in the latest code in config_parse.c (already merged on master branch), we automatically add LINKx for every object associated with it , such as RXQx.y, TXQx.y, TMx. This is the same for KNI, as KNIx is associated with LINKx. As also commented below, we should implement this in config_parse.c. Basically, due to this change in config_parse.c, it is always guaranteed that for every KNIx object, the LINKx object exists as well, so no need to check this here in config_check.c or in init.c.

> +		APP_CHECK(sscanf(p->name, "KNI%" PRIu32, &port_id) == 1,
> +				  "%s's port id is invalid\n", p->name);
> +
> +		APP_CHECK(port_id < app->n_links,
> +				  "kni %s is not associated with a valid link\n",
> +				  p->name);
> +	}
> +}
> +
>  int
>  app_config_check(struct app_params *app)
>  {
> @@ -439,6 +472,7 @@ app_config_check(struct app_params *app)
>  	check_sinks(app);
>  	check_msgqs(app);
>  	check_pipelines(app);
> +	check_kni(app);
> 
>  	return 0;
>  }
> diff --git a/examples/ip_pipeline/config_parse.c
> b/examples/ip_pipeline/config_parse.c
> index e5efd03..e9cd5a4 100644
> --- a/examples/ip_pipeline/config_parse.c
> +++ b/examples/ip_pipeline/config_parse.c
> @@ -209,6 +209,15 @@ struct app_pipeline_params
> default_pipeline_params = {
>  	.n_args = 0,
>  };
> 
> +struct app_kni_params default_kni_params = {
> +	.parsed = 0,
> +	.socket_id = 0,
> +	.core_id = 0,
> +	.hyper_th_id = 0,
> +	.mempool_id = 0,
> +	.burst = 32,

.burst_read = 32,
.burst_write = 32,

> +};
> +
>  static const char app_usage[] =
>  	"Usage: %s [-f CONFIG_FILE] [-s SCRIPT_FILE] [-p PORT_MASK] "
>  	"[-l LOG_LEVEL] [--preproc PREPROCESSOR] [--preproc-args
> ARGS]\n"
> @@ -1169,6 +1178,9 @@ parse_pipeline_pktq_in(struct app_params *app,
>  		} else if (validate_name(name, "SOURCE", 1) == 0) {
>  			type = APP_PKTQ_IN_SOURCE;
>  			id = APP_PARAM_ADD(app->source_params, name);
> +		} else if (validate_name(name, "KNI", 1) == 0) {
> +			type = APP_PKTQ_IN_KNI;
> +			id = APP_PARAM_ADD(app->kni_params, name);
>  		} else
>  			return -EINVAL;
> 
> @@ -1240,6 +1252,9 @@ parse_pipeline_pktq_out(struct app_params *app,
>  		} else if (validate_name(name, "SINK", 1) == 0) {
>  			type = APP_PKTQ_OUT_SINK;
>  			id = APP_PARAM_ADD(app->sink_params, name);
> +		} else if (validate_name(name, "KNI", 1) == 0) {
> +			type = APP_PKTQ_OUT_KNI;
> +			id = APP_PARAM_ADD(app->kni_params, name);
>  		} else
>  			return -EINVAL;
> 
> @@ -2459,6 +2474,76 @@ parse_msgq(struct app_params *app,
>  	free(entries);
>  }
> 

Please rework the below parse_kni() function based on the latest code (rebase). For example, the PARSER_PARAM_ADD_CHECK macro has been removed.

Also, as mentioned above, please add LINKx automatically for KNIx, same as the latest code adds LINKx automatically every time RXQx.y, TXQx.y, TMx objects are met. This has to be done in several places: once here, in function parse_kni(), which is executed for KNI sections, but also in parse_pipeline_pktq_in() and parse_pipeline_pktq_out() functions (please check latest code).

> +static void
> +parse_kni(struct app_params *app,
> +		   const char *section_name,
> +		   struct rte_cfgfile *cfg)
> +{
> +	struct app_kni_params *param;
> +	struct rte_cfgfile_entry *entries;
> +	int n_entries, i;
> +	ssize_t param_idx;
> +
> +	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
> +	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0),
> section_name);
> +
> +	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
> +	PARSE_ERROR_MALLOC(entries != NULL);
> +
> +	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
> +
> +	param_idx = APP_PARAM_ADD(app->kni_params, section_name);
> +	PARSER_PARAM_ADD_CHECK(param_idx, app->kni_params,
> section_name);
> +
> +	param = &app->kni_params[param_idx];
> +
> +	for (i = 0; i < n_entries; i++) {
> +		struct rte_cfgfile_entry *ent = &entries[i];
> +
> +		if (strcmp(ent->name, "core") == 0) {
> +			int status = parse_pipeline_core(
> +				&param->socket_id, &param->core_id,
> +				&param->hyper_th_id, ent->value);
> +
> +			PARSE_ERROR((status == 0), section_name,
> +				ent->name);
> +			continue;
> +		}
> +
> +		if (strcmp(ent->name, "mempool") == 0) {
> +			int status = validate_name(ent->value,
> +				"MEMPOOL", 1);
> +			ssize_t idx;
> +
> +			PARSE_ERROR((status == 0), section_name,
> +				ent->name);
> +			idx = APP_PARAM_ADD(app->mempool_params,
> +				ent->value);
> +			PARSER_PARAM_ADD_CHECK(idx,
> +				app->mempool_params,
> +				section_name);
> +			param->mempool_id = idx;
> +			continue;
> +		}
> +
> +		if (strcmp(ent->name, "burst") == 0) {
> +			int status = parser_read_uint32(&param->burst,
> +				ent->value);
> +
> +			PARSE_ERROR((status == 0), section_name,
> +				ent->name);
> +			continue;
> +		}

As discussed above, we should parse two different entries in KNI section: burst_read and burst_write.

> +
> +		/* unrecognized */
> +		PARSE_ERROR_INVALID(0, section_name, ent->name);
> +	}
> +
> +	param->parsed = 1;
> +
> +	free(entries);
> +}
> +
>  typedef void (*config_section_load)(struct app_params *p,
>  	const char *section_name,
>  	struct rte_cfgfile *cfg);
> @@ -2483,6 +2568,7 @@ static const struct config_section cfg_file_scheme[]
> = {
>  	{"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
>  	{"MSGQ-RSP-PIPELINE", 1, parse_msgq_rsp_pipeline},
>  	{"MSGQ", 1, parse_msgq},
> +	{"KNI", 1, parse_kni},
>  };
> 
>  static void
> @@ -2619,6 +2705,7 @@ app_config_parse(struct app_params *app, const
> char *file_name)
>  	APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
>  	APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
>  	APP_PARAM_COUNT(app->pipeline_params, app->n_pipelines);
> +	APP_PARAM_COUNT(app->kni_params, app->n_kni);
> 
>  #ifdef RTE_PORT_PCAP
>  	for (i = 0; i < (int)app->n_pktq_source; i++) {
> @@ -3025,6 +3112,9 @@ save_pipeline_params(struct app_params *app,
> FILE *f)
>  				case APP_PKTQ_IN_SOURCE:
>  					name = app->source_params[pp-
> >id].name;
>  					break;
> +				case APP_PKTQ_IN_KNI:
> +					name = app->kni_params[pp-
> >id].name;
> +					break;
>  				default:
>  					APP_CHECK(0, "System error "
>  						"occurred while saving "
> @@ -3059,6 +3149,9 @@ save_pipeline_params(struct app_params *app,
> FILE *f)
>  				case APP_PKTQ_OUT_SINK:
>  					name = app->sink_params[pp-
> >id].name;
>  					break;
> +				case APP_PKTQ_OUT_KNI:
> +					name = app->kni_params[pp-
> >id].name;
> +					break;
>  				default:
>  					APP_CHECK(0, "System error "
>  						"occurred while saving "
> @@ -3114,6 +3207,37 @@ save_pipeline_params(struct app_params *app,
> FILE *f)
>  	}
>  }
> 
> +static void
> +save_kni_params(struct app_params *app, FILE *f)
> +{
> +	struct app_kni_params *p;
> +	size_t i, count;
> +
> +	count = RTE_DIM(app->kni_params);
> +	for (i = 0; i < count; i++) {
> +		p = &app->kni_params[i];
> +		if (!APP_PARAM_VALID(p))
> +			continue;
> +
> +		/* section name */
> +		fprintf(f, "[%s]\n", p->name);
> +
> +		/* core */
> +		fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
> +				p->socket_id,
> +				p->core_id,
> +				(p->hyper_th_id) ? "h" : "");
> +
> +		/* mempool */
> +		fprintf(f, "%s = %" PRIu32 "\n", "mempool_id", p-
> >mempool_id);

The name of the entry is "mempool" instead of "mempool_id". The value of the entry is app->mempool_params[p->mempool_id].name (which is a string) instead of p->mempool_id (which is a number).

> +
> +		/* burst */
> +		fprintf(f, "%s = %" PRIu32 "\n", "burst", p->burst);
> +

We should save both p->burst_read and p->burst_write.

> +		fputc('\n', f);
> +	}
> +}
> +
>  void
>  app_config_save(struct app_params *app, const char *file_name)
>  {
> @@ -3144,6 +3268,7 @@ app_config_save(struct app_params *app, const
> char *file_name)
>  	save_source_params(app, file);
>  	save_sink_params(app, file);
>  	save_msgq_params(app, file);
> +	save_kni_params(app, file);
> 
>  	fclose(file);
>  	free(name);
> @@ -3206,6 +3331,11 @@ app_config_init(struct app_params *app)
>  			&default_pipeline_params,
>  			sizeof(default_pipeline_params));
> 
> +	for (i = 0; i < RTE_DIM(app->kni_params); i++)
> +		memcpy(&app->kni_params[i],
> +			&default_kni_params,
> +			sizeof(default_kni_params));
> +
>  	return 0;
>  }
> 
> diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
> index 02351f6..8ff9118 100644
> --- a/examples/ip_pipeline/init.c
> +++ b/examples/ip_pipeline/init.c

This is run-time code, let's have all the KNI code in init.c file enabled only when KNI library is part of the build:
#ifdef RTE_LIBRTE_KNI
...
#endif /* RTE_LIBRTE_KNI */

> @@ -89,6 +89,24 @@ app_init_core_mask(struct app_params *app)
>  		mask |= 1LLU << lcore_id;
>  	}
> 
> +	for (i = 0; i < app->n_kni; i++) {
> +		struct app_kni_params *p = &app->kni_params[i];
> +		int lcore_id;
> +
> +		lcore_id = cpu_core_map_get_lcore_id(app->core_map,
> +			p->socket_id,
> +			p->core_id,
> +			p->hyper_th_id);
> +
> +		if (lcore_id < 0)
> +			rte_panic("Cannot create CPU core mask\n");
> +
> +		if (mask & 1LLU << lcore_id)

Please use parenthesis for improved readability: if (mask & (1LLU << lcore_id)).

> +			rte_panic("KNI interface must use a dedicated
> lcore\n");

The bigger questions are:
- Why do we need to dedicate separate CPU core(s) for KNI interface(s)? Isn't KNI code running in kernel space, why do we need to dedicate separate user space core for it and why do we need to add this core to the user-space application core mask? 
- Even if we need to add this to the core mask (maybe I am missing something here ...), why do we need to dedicate this core entirely to KNI? Can't we have KNI (kernel) code sharing this core with user-space application code (e.g. some pipeline instances?)

> +
> +		mask |= 1LLU << lcore_id;
> +	}
> +
>  	app->core_mask = mask;
>  	APP_LOG(app, HIGH, "CPU core mask = 0x%016" PRIx64, app-
> >core_mask);
>  }
> @@ -1236,6 +1254,11 @@ static void app_pipeline_params_get(struct
> app_params *app,
>  					n_bytes_per_pkt;
>  			}
>  			break;
> +		case APP_PKTQ_IN_KNI:
> +			out->type = PIPELINE_PORT_IN_KNI;
> +			out->params.kni.kni = app->kni[in->id];
> +			out->burst_size = app->kni_params[in->id].burst;
> +			break;
>  		default:
>  			break;
>  		}
> @@ -1374,6 +1397,12 @@ static void app_pipeline_params_get(struct
> app_params *app,
>  				out->params.sink.max_n_pkts = 0;
>  			}
>  			break;
> +		case APP_PKTQ_OUT_KNI:
> +			out->type = PIPELINE_PORT_OUT_KNI;
> +			out->params.kni.kni = app->kni[in->id];
> +			out->params.kni.tx_burst_sz =
> +					app->kni_params[in->id].burst;
> +			break;
>  		default:
>  			break;
>  		}
> @@ -1397,6 +1426,55 @@ static void app_pipeline_params_get(struct
> app_params *app,
>  }
> 
>  static void
> +app_init_kni(struct app_params *app) {
> +	uint32_t i;
> +	struct rte_kni_conf conf;
> +

Avoid calling rte_kni_init() when there is no KNI device:

if (app->n_kni == 0)
	return;

> +	rte_kni_init((unsigned int)app->n_kni);
> +
> +	for (i = 0; i < app->n_kni; i++) {
> +		struct app_kni_params *p_kni = &app->kni_params[i];
> +		uint32_t port_id;

Please rename port_id with link_id, as this index is really the x from LINKx objects.

> +		struct app_mempool_params *mempool_params;
> +		struct rte_mempool *mempool;
> +
> +		if (sscanf(p_kni->name, "KNI%" PRIu32, &port_id) != 1)
> +			rte_panic("%s's port id is invalid\n", p_kni->name);

Same comment as above: we do not need to check the x in KNIx, as LINKx is (should be, after you adjust the config_parse.c code) added automatically for KNIx.

> +
> +		mempool_params = &app->mempool_params[p_kni-
> >mempool_id];
> +		mempool = app->mempool[p_kni->mempool_id];
> +
> +		memset(&conf, 0, sizeof(conf));
> +		snprintf(conf.name, RTE_KNI_NAMESIZE,
> +				 "vEth%u", port_id);
> +		conf.core_id = p_kni->core_id;

The way the conf.core_id is set here is wrong, right? 


conf.core_id = cpu_core_map_get_lcore_id(app->core_map,
	p->socket_id,
	p->core_id,
	p->hyper_th_id);

> +		conf.force_bind = 1;
> +
> +		conf.group_id = (uint16_t) port_id;
> +		conf.mbuf_size = mempool_params->buffer_size;
> +
> +		struct rte_kni_ops ops;
> +		struct rte_eth_dev_info dev_info;
> +

Please move these definitions at the top of the for loop block rather than having them in the middle of the for loop block.

> +		memset(&dev_info, 0, sizeof(dev_info));
> +		rte_eth_dev_info_get(app->link_params[port_id].pmd_id,
> +							 &dev_info);
> +		conf.addr = dev_info.pci_dev->addr;
> +		conf.id = dev_info.pci_dev->id;
> +
> +		memset(&ops, 0, sizeof(ops));
> +		ops.port_id = app->link_params[port_id].pmd_id;
> +		ops.change_mtu = kni_change_mtu;
> +		ops.config_network_if = kni_config_network_interface;
> +
> +		app->kni[i] = rte_kni_alloc(mempool,
> +			&conf, &ops);
> +		if (!app->kni[i])
> +			rte_panic("Fail to create kni for port: %d\n", port_id);

rte_panic("Failed to create %s", p->name);

This should print e.g. "Failed to create KNI5", which users should know it is the KNI associated with LINK5.

> +	}
> +}
> +
> +static void
>  app_init_pipelines(struct app_params *app)
>  {
>  	uint32_t p_id;
> @@ -1531,6 +1609,7 @@ int app_init(struct app_params *app)
>  	app_init_swq(app);
>  	app_init_tm(app);
>  	app_init_msgq(app);
> +	app_init_kni(app);
> 
>  	app_pipeline_common_cmd_push(app);
>  	app_pipeline_thread_cmd_push(app);
> diff --git a/examples/ip_pipeline/kni/kni.c b/examples/ip_pipeline/kni/kni.c
> new file mode 100644
> index 0000000..c58e146

Please move the two functions from kni/kni.c to init.c (just before app_init_kni() function) and make them static functions, then remove kni/kni.h and kni/kni.c files.

> --- /dev/null
> +++ b/examples/ip_pipeline/kni/kni.c
> @@ -0,0 +1,80 @@
> +#include <string.h>
> +
> +#include <rte_common.h>
> +#include <rte_malloc.h>
> +#include <rte_table_array.h>
> +#include <rte_kni.h>
> +#include <rte_ethdev.h>
> +
> +#include "rte_port_kni.h"
> +#include "kni.h"
> +
> +int
> +kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
> +	int ret = 0;
> +
> +	if (port_id >= rte_eth_dev_count() || port_id >=
> RTE_MAX_ETHPORTS) {
> +		RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
> +				__func__, port_id);
> +		return -EINVAL;
> +	}
> +
> +	RTE_LOG(INFO, PORT, "%s: Configure network interface of %d
> %s\n",
> +			__func__, port_id, if_up ? "up" : "down");
> +
> +	if (if_up != 0) { /* Configure network interface up */
> +		rte_eth_dev_stop(port_id);

Why do we need to stop the device first before we start it?

> +		ret = rte_eth_dev_start(port_id);
> +	} else /* Configure network interface down */
> +		rte_eth_dev_stop(port_id);

Do we need to call rte_eth_dev_start/stop() or do we need to call rte_eth_dev_up/down()?

> +
> +	if (ret < 0)
> +		RTE_LOG(ERR, PORT, "%s: Failed to start port %d\n",
> +				__func__, port_id);
> +

This is a callback function, I think we should completely remove any RTE_LOG calls from it, as link can go up and down quite frequently.

> +	return ret;
> +}
> +
> +int
> +kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
> +	int ret;
> +
> +	if (port_id >= rte_eth_dev_count()) {
> +		RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
> +				__func__, port_id);
> +		return -EINVAL;
> +	}
> +
> +	if (new_mtu > ETHER_MAX_LEN) {
> +		RTE_LOG(ERR, PORT,
> +				"%s: Fail to reconfigure port %d, the new
> MTU is too big\n",
> +				__func__, port_id);
> +		return -EINVAL;
> +	}
> +
> +	RTE_LOG(INFO, PORT, "%s: Change MTU of port %d to %u\n",
> +			__func__, port_id,
> +			new_mtu);
> +
> +	/* Stop specific port */
> +	rte_eth_dev_stop(port_id);
> +
> +	/* Set new MTU */
> +	ret = rte_eth_dev_set_mtu(port_id, new_mtu);
> +	if (ret < 0) {
> +		RTE_LOG(ERR, PORT, "%s: Fail to reconfigure port %d\n",
> +				__func__, port_id);
> +		return ret;
> +	}
> +
> +	/* Restart specific port */
> +	ret = rte_eth_dev_start(port_id);
> +	if (ret < 0) {
> +		RTE_LOG(ERR, PORT, "%s: Fail to restart port %d\n",
> +				__func__, port_id);
> +		return ret;
> +	}
> +
> +	return 0;
> +}

This is a callback function, I think we should completely remove all the RTE_LOG calls from it.

> +
> diff --git a/examples/ip_pipeline/kni/kni.h b/examples/ip_pipeline/kni/kni.h
> new file mode 100644
> index 0000000..04c8429
> --- /dev/null
> +++ b/examples/ip_pipeline/kni/kni.h
> @@ -0,0 +1,16 @@
> +#ifndef __INCLUDE_KNI_H__
> +#define __INCLUDE_KNI_H__
> +
> +#include <rte_common.h>
> +
> +/* Total octets in ethernet header */
> +#define KNI_ENET_HEADER_SIZE    14

Are we actually using this macro? I would like to remove it if not needed.

> +
> +/* Total octets in the FCS */
> +#define KNI_ENET_FCS_SIZE       4

Are we actually using this macro? I would like to remove it if not needed.

> +
> +int kni_config_network_interface(uint8_t port_id, uint8_t if_up);
> +
> +int kni_change_mtu(uint8_t port_id, unsigned new_mtu);
> +
> +#endif
> diff --git a/examples/ip_pipeline/pipeline_be.h
> b/examples/ip_pipeline/pipeline_be.h
> index f4ff262..23f0438 100644
> --- a/examples/ip_pipeline/pipeline_be.h
> +++ b/examples/ip_pipeline/pipeline_be.h
> @@ -40,6 +40,7 @@
>  #include <rte_port_ras.h>
>  #include <rte_port_sched.h>
>  #include <rte_port_source_sink.h>
> +#include <rte_port_kni.h>
>  #include <rte_pipeline.h>
> 
>  enum pipeline_port_in_type {
> @@ -50,6 +51,7 @@ enum pipeline_port_in_type {
>  	PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
>  	PIPELINE_PORT_IN_SCHED_READER,
>  	PIPELINE_PORT_IN_SOURCE,
> +	PIPELINE_PORT_IN_KNI,
>  };
> 
>  struct pipeline_port_in_params {
> @@ -62,6 +64,7 @@ struct pipeline_port_in_params {
>  		struct rte_port_ring_reader_ipv6_frag_params
> ring_ipv6_frag;
>  		struct rte_port_sched_reader_params sched;
>  		struct rte_port_source_params source;
> +		struct rte_port_kni_reader_params kni;
>  	} params;
>  	uint32_t burst_size;
>  };
> @@ -84,6 +87,8 @@ pipeline_port_in_params_convert(struct
> pipeline_port_in_params  *p)
>  		return (void *) &p->params.sched;
>  	case PIPELINE_PORT_IN_SOURCE:
>  		return (void *) &p->params.source;
> +	case PIPELINE_PORT_IN_KNI:
> +		return (void *) &p->params.kni;
>  	default:
>  		return NULL;
>  	}
> @@ -107,6 +112,8 @@ pipeline_port_in_params_get_ops(struct
> pipeline_port_in_params  *p)
>  		return &rte_port_sched_reader_ops;
>  	case PIPELINE_PORT_IN_SOURCE:
>  		return &rte_port_source_ops;
> +	case PIPELINE_PORT_IN_KNI:
> +		return &rte_port_kni_reader_ops;
>  	default:
>  		return NULL;
>  	}
> @@ -123,6 +130,7 @@ enum pipeline_port_out_type {
>  	PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
>  	PIPELINE_PORT_OUT_SCHED_WRITER,
>  	PIPELINE_PORT_OUT_SINK,
> +	PIPELINE_PORT_OUT_KNI,
>  };
> 
>  struct pipeline_port_out_params {
> @@ -138,6 +146,7 @@ struct pipeline_port_out_params {
>  		struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
>  		struct rte_port_sched_writer_params sched;
>  		struct rte_port_sink_params sink;
> +		struct rte_port_kni_writer_params kni;
>  	} params;
>  };
> 
> @@ -165,6 +174,8 @@ pipeline_port_out_params_convert(struct
> pipeline_port_out_params  *p)
>  		return (void *) &p->params.sched;
>  	case PIPELINE_PORT_OUT_SINK:
>  		return (void *) &p->params.sink;
> +	case PIPELINE_PORT_OUT_KNI:
> +		return (void *) &p->params.kni;
>  	default:
>  		return NULL;
>  	}
> @@ -194,6 +205,8 @@ pipeline_port_out_params_get_ops(struct
> pipeline_port_out_params  *p)
>  		return &rte_port_sched_writer_ops;
>  	case PIPELINE_PORT_OUT_SINK:
>  		return &rte_port_sink_ops;
> +	case PIPELINE_PORT_OUT_KNI:
> +		return &rte_port_kni_writer_ops;
>  	default:
>  		return NULL;
>  	}
> diff --git a/examples/ip_pipeline/thread.c b/examples/ip_pipeline/thread.c
> index a0f1f12..534864a 100644
> --- a/examples/ip_pipeline/thread.c
> +++ b/examples/ip_pipeline/thread.c
> @@ -239,6 +239,15 @@ app_thread(void *arg)
>  	uint32_t core_id = rte_lcore_id(), i, j;
>  	struct app_thread_data *t = &app->thread_data[core_id];
> 
> +	for (i = 0; i < app->n_kni; i++) {
> +		if (core_id == (uint32_t)cpu_core_map_get_lcore_id(
> +			app->core_map,
> +			app->kni_params[i].socket_id,
> +			app->kni_params[i].core_id,
> +			app->kni_params[i].hyper_th_id))
> +			return 0;
> +	}
> +

Same questions as above:
- Why do we need to dedicate separate CPU core(s) for KNI interface(s)? Isn't KNI code running in kernel space, why do we need to dedicate separate user space core for it and why do we need to add this core to the user-space application core mask? 
- Even if we need to add this to the core mask (maybe I am missing something here ...), why do we need to dedicate this core entirely to KNI? Can't we have KNI (kernel) code sharing this core with user-space application code (e.g. some pipeline instances?)

This is run-time code, let's have it enabled only when KNI library is part of the build:
#ifdef RTE_LIBRTE_KNI
...
#endif /* RTE_LIBRTE_KNI */

>  	for (i = 0; ; i++) {
>  		uint32_t n_regular = RTE_MIN(t->n_regular, RTE_DIM(t-
> >regular));
>  		uint32_t n_custom = RTE_MIN(t->n_custom, RTE_DIM(t-
> >custom));
> diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
> index d4de5af..f18253d 100644
> --- a/lib/librte_port/Makefile
> +++ b/lib/librte_port/Makefile
> @@ -57,6 +57,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
>  endif
>  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
> +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> +SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
> +endif
> 
>  # install includes
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port.h
> @@ -68,6 +71,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=
> rte_port_ras.h
>  endif
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
> +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> +SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
> +endif
> 
>  # this lib depends upon:
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
> @@ -75,5 +81,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=
> lib/librte_mbuf
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
> 
>  include $(RTE_SDK)/mk/rte.lib.mk
> diff --git a/lib/librte_port/rte_port_kni.c b/lib/librte_port/rte_port_kni.c
> new file mode 100644
> index 0000000..8c5e404
> --- /dev/null
> +++ b/lib/librte_port/rte_port_kni.c
> @@ -0,0 +1,316 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.

Please fix the year as 2016.

> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> + */
> +#include <string.h>
> +
> +#include <rte_common.h>
> +#include <rte_malloc.h>
> +#include <rte_kni.h>
> +
> +#include "rte_port_kni.h"
> +
> +/*
> + * Port KNI Reader
> + */
> +#ifdef RTE_PORT_STATS_COLLECT
> +
> +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
> +	{port->stats.n_pkts_in += val}
> +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
> +	{port->stats.n_pkts_drop += val}

This actually results in compiler error when built with RTE_PORT_STATS_COLLECT = ON.

Please add semicolon and remove curly braces (as in e.g. rte_port_ring.c):
#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
	port->stats.n_pkts_in += val;
#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
	port->stats.n_pkts_drop += val;

> +#else
> +
> +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
> +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
> +
> +#endif
> +
> +struct rte_port_kni_reader {
> +	struct rte_port_in_stats stats;
> +
> +	struct rte_kni *kni;
> +};
> +
> +static void *
> +rte_port_kni_reader_create(void *params, int socket_id) {
> +	struct rte_port_kni_reader_params *conf =
> +			(struct rte_port_kni_reader_params *) params;
> +	struct rte_port_kni_reader *port;
> +
> +	/* Check input parameters */
> +	if (conf == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
> +		return NULL;
> +	}
> +
> +	/* Memory allocation */
> +	port = rte_zmalloc_socket("PORT", sizeof(*port),
> +		RTE_CACHE_LINE_SIZE, socket_id);
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> __func__);
> +		return NULL;
> +	}
> +
> +	/* Initialization */
> +	port->kni = conf->kni;
> +
> +	return port;
> +}
> +
> +static int
> +rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t
> n_pkts) {
> +	struct rte_port_kni_reader *p =
> +			(struct rte_port_kni_reader *) port;
> +	uint16_t rx_pkt_cnt;
> +
> +	rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
> +	RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
> +	return rx_pkt_cnt;
> +}
> +
> +static int
> +rte_port_kni_reader_free(void *port) {
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	rte_free(port);
> +
> +	return 0;
> +}
> +
> +static int rte_port_kni_reader_stats_read(void *port,
> +	struct rte_port_in_stats *stats, int clear)
> +{
> +	struct rte_port_kni_reader *p =
> +			(struct rte_port_kni_reader *) port;
> +
> +	if (stats != NULL)
> +		memcpy(stats, &p->stats, sizeof(p->stats));
> +
> +	if (clear)
> +		memset(&p->stats, 0, sizeof(p->stats));
> +
> +	return 0;
> +}
> +
> +/*
> + * Port KNI Writer
> + */
> +#ifdef RTE_PORT_STATS_COLLECT
> +
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
> +	{port->stats.n_pkts_in += val}
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
> +	{port->stats.n_pkts_drop += val}
> +

This actually results in compiler error when built with RTE_PORT_STATS_COLLECT = ON.

Please add semicolon and remove curly braces (as in e.g. rte_port_ring.c).

> +#else
> +
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
> +
> +#endif
> +
> +struct rte_port_kni_writer {
> +	struct rte_port_out_stats stats;
> +
> +	struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
> +	uint32_t tx_burst_sz;
> +	uint16_t tx_buf_count;

Can we actually make tx_buf_count to be uint32_t rather than uint16_t? I see some computation between tx_buf_count and some other variable of type uint32_t later in the code ...

> +	uint64_t bsz_mask;
> +	struct rte_kni *kni;
> +};
> +
> +static void *
> +rte_port_kni_writer_create(void *params, int socket_id) {
> +	struct rte_port_kni_writer_params *conf =
> +			(struct rte_port_kni_writer_params *) params;
> +	struct rte_port_kni_writer *port;
> +
> +	/* Check input parameters */
> +	if ((conf == NULL) ||
> +		(conf->tx_burst_sz == 0) ||
> +		(conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
> +		(!rte_is_power_of_2(conf->tx_burst_sz))) {
> +		RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n",
> __func__);
> +		return NULL;
> +	}
> +
> +	/* Memory allocation */
> +	port = rte_zmalloc_socket("PORT", sizeof(*port),
> +		RTE_CACHE_LINE_SIZE, socket_id);
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> __func__);
> +		return NULL;
> +	}
> +
> +	/* Initialization */
> +	port->kni = conf->kni;
> +	port->tx_burst_sz = conf->tx_burst_sz;
> +	port->tx_buf_count = 0;
> +	port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
> +
> +	return port;
> +}
> +
> +static inline void
> +send_burst(struct rte_port_kni_writer *p) {
> +	uint32_t nb_tx;
> +
> +	nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
> +	rte_kni_handle_request(p->kni);
> +
> +	RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p-
> >tx_buf_count - nb_tx);
> +	for (; nb_tx < p->tx_buf_count; nb_tx++)
> +		rte_pktmbuf_free(p->tx_buf[nb_tx]);
> +
> +	p->tx_buf_count = 0;
> +}
> +
> +static int
> +rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt) {
> +	struct rte_port_kni_writer *p =
> +			(struct rte_port_kni_writer *) port;
> +
> +	p->tx_buf[p->tx_buf_count++] = pkt;
> +	RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> +	if (p->tx_buf_count >= p->tx_burst_sz)
> +		send_burst(p);
> +
> +	return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_tx_bulk(void *port,
> +							struct rte_mbuf
> **pkts,
> +							uint64_t pkts_mask) {
> +	struct rte_port_kni_writer *p =
> +			(struct rte_port_kni_writer *) port;
> +	uint64_t bsz_mask = p->bsz_mask;
> +	uint32_t tx_buf_count = p->tx_buf_count;
> +	uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
> +					((pkts_mask & bsz_mask) ^
> bsz_mask);
> +
> +	if (expr == 0) {
> +		uint64_t n_pkts = __builtin_popcountll(pkts_mask);
> +		uint32_t n_pkts_ok;
> +
> +		if (tx_buf_count)
> +			send_burst(p);
> +
> +		RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
> +		n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
> +
> +		RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts
> - n_pkts_ok);
> +		for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
> +			struct rte_mbuf *pkt = pkts[n_pkts_ok];
> +
> +			rte_pktmbuf_free(pkt);
> +		}
> +	} else {
> +		for (; pkts_mask;) {
> +			uint32_t pkt_index = __builtin_ctzll(pkts_mask);
> +			uint64_t pkt_mask = 1LLU << pkt_index;
> +			struct rte_mbuf *pkt = pkts[pkt_index];
> +
> +			p->tx_buf[tx_buf_count++] = pkt;
> +			RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> +			pkts_mask &= ~pkt_mask;
> +		}
> +
> +		p->tx_buf_count = tx_buf_count;
> +		if (tx_buf_count >= p->tx_burst_sz)
> +			send_burst(p);
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_flush(void *port) {
> +	struct rte_port_kni_writer *p =
> +			(struct rte_port_kni_writer *) port;
> +
> +	if (p->tx_buf_count > 0)
> +		send_burst(p);
> +
> +	return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_free(void *port) {
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	rte_port_kni_writer_flush(port);
> +	rte_free(port);
> +
> +	return 0;
> +}
> +
> +static int rte_port_kni_writer_stats_read(void *port,
> +	struct rte_port_out_stats *stats, int clear)
> +{
> +	struct rte_port_kni_writer *p =
> +			(struct rte_port_kni_writer *) port;
> +
> +	if (stats != NULL)
> +		memcpy(stats, &p->stats, sizeof(p->stats));
> +
> +	if (clear)
> +		memset(&p->stats, 0, sizeof(p->stats));
> +
> +	return 0;
> +}
> +
> +/*
> + * Summary of port operations
> + */
> +struct rte_port_in_ops rte_port_kni_reader_ops = {
> +	.f_create = rte_port_kni_reader_create,
> +	.f_free = rte_port_kni_reader_free,
> +	.f_rx = rte_port_kni_reader_rx,
> +	.f_stats = rte_port_kni_reader_stats_read,
> +};
> +
> +struct rte_port_out_ops rte_port_kni_writer_ops = {
> +	.f_create = rte_port_kni_writer_create,
> +	.f_free = rte_port_kni_writer_free,
> +	.f_tx = rte_port_kni_writer_tx,
> +	.f_tx_bulk = rte_port_kni_writer_tx_bulk,
> +	.f_flush = rte_port_kni_writer_flush,
> +	.f_stats = rte_port_kni_writer_stats_read,
> +};

Do we need a KNI writer no-drop version as well? Would it be useful?

> diff --git a/lib/librte_port/rte_port_kni.h b/lib/librte_port/rte_port_kni.h
> new file mode 100644
> index 0000000..7623798
> --- /dev/null
> +++ b/lib/librte_port/rte_port_kni.h
> @@ -0,0 +1,81 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.

Please fix the year to 2016.

> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> + */
> +
> +#ifndef __INCLUDE_RTE_PORT_KNI_H__
> +#define __INCLUDE_RTE_PORT_KNI_H__
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/**
> + * @file
> + * RTE Port KNI Interface
> + *
> + * kni_reader: input port built on top of pre-initialized KNI interface
> + * kni_writer: output port built on top of pre-initialized KNI interface
> + *
> + ***/
> +
> +#include <stdint.h>
> +
> +#include <rte_kni.h>
> +
> +#include "rte_port.h"
> +
> +/** kni_reader port parameters */
> +struct rte_port_kni_reader_params {
> +	/** KNI interface reference */
> +	struct rte_kni *kni;
> +};
> +
> +/** kni_reader port operations */
> +extern struct rte_port_in_ops rte_port_kni_reader_ops;
> +
> +
> +/** kni_writer port parameters */
> +struct rte_port_kni_writer_params {
> +	/** KNI interface reference */
> +	struct rte_kni *kni;
> +	/** Burst size to KNI interface. */
> +	uint32_t tx_burst_sz;
> +};
> +
> +/** kni_writer port operations */
> +extern struct rte_port_out_ops rte_port_kni_writer_ops;
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
> --
> 2.7.4

Thank you!

Regards,
Cristian

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

* Re: [PATCH] port: add kni interface support
  2016-06-09 23:42   ` Dumitrescu, Cristian
@ 2016-06-13 10:25     ` Dumitrescu, Cristian
  2016-06-13 10:47     ` Ethan
  1 sibling, 0 replies; 22+ messages in thread
From: Dumitrescu, Cristian @ 2016-06-13 10:25 UTC (permalink / raw)
  To: WeiJie Zhuang; +Cc: dev, Singh, Jasvinder, Yigit, Ferruh

Hi Ethan,

In the process of testing your patch, I actually had to do the rebase on the latest code and fix all the comments that I previously sent as part of my reply. I also did a few other cosmetic changes and fixes required by the latest code (see below), therefore I think it makes sense to send you this code for quick review rather than have you spend time doing the same work. It would also save some iterations on the email list and speed up the integration of this patch.

Please review this code and let us know as soon as possible if you are OK with us sending it as the next version of this patch keeping your initial signoff as well. Thank you!

Changes included in the code below:
1. Rebase on top of latest DPDK head
2. Fixing all the previous code comments
3. Fix the binding of KNI device kernel space thread to CPU core (struct app_pktq_kni_params::force_bind)
4. The KNI requests handled by master pipeline rather than on data path (pipeline_master_be.c: call to rte_kni_handle_request())
5. Fixing the tracking feature for KNI (pipeline_common_fe.c: function app_pipeline_track_pktq_out_to_link())
6. Enhanced config file with two KNI interfaces connected using a Linux kernel bridge (./config/kni.cfg)
7. Cosmetic improvements

Regards,
Cristian


diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
old mode 100644
new mode 100755
index 848244a..555c6bd
--- a/examples/ip_pipeline/app.h
+++ b/examples/ip_pipeline/app.h
@@ -44,6 +44,7 @@
 #include <cmdline_parse.h>
 
 #include <rte_ethdev.h>
+#include <rte_kni.h>
 
 #include "cpu_core_map.h"
 #include "pipeline.h"
@@ -158,6 +159,20 @@ struct app_pktq_tm_params {
 	uint32_t burst_write;
 };
 
+struct app_pktq_kni_params {
+	char *name;
+	uint32_t parsed;
+
+	uint32_t socket_id;
+	uint32_t core_id;
+	uint32_t hyper_th_id;
+	uint32_t force_bind;
+
+	uint32_t mempool_id; /* Position in the app->mempool_params */
+	uint32_t burst_read;
+	uint32_t burst_write;
+};
+
 struct app_pktq_source_params {
 	char *name;
 	uint32_t parsed;
@@ -185,6 +200,7 @@ enum app_pktq_in_type {
 	APP_PKTQ_IN_HWQ,
 	APP_PKTQ_IN_SWQ,
 	APP_PKTQ_IN_TM,
+	APP_PKTQ_IN_KNI,
 	APP_PKTQ_IN_SOURCE,
 };
 
@@ -197,6 +213,7 @@ enum app_pktq_out_type {
 	APP_PKTQ_OUT_HWQ,
 	APP_PKTQ_OUT_SWQ,
 	APP_PKTQ_OUT_TM,
+	APP_PKTQ_OUT_KNI,
 	APP_PKTQ_OUT_SINK,
 };
 
@@ -420,6 +437,8 @@ struct app_eal_params {
 
 #define APP_MAX_PKTQ_TM                          APP_MAX_LINKS
 
+#define APP_MAX_PKTQ_KNI                         APP_MAX_LINKS
+
 #ifndef APP_MAX_PKTQ_SOURCE
 #define APP_MAX_PKTQ_SOURCE                      64
 #endif
@@ -471,6 +490,7 @@ struct app_params {
 	struct app_pktq_hwq_out_params hwq_out_params[APP_MAX_HWQ_OUT];
 	struct app_pktq_swq_params swq_params[APP_MAX_PKTQ_SWQ];
 	struct app_pktq_tm_params tm_params[APP_MAX_PKTQ_TM];
+	struct app_pktq_kni_params kni_params[APP_MAX_PKTQ_KNI];
 	struct app_pktq_source_params source_params[APP_MAX_PKTQ_SOURCE];
 	struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
 	struct app_msgq_params msgq_params[APP_MAX_MSGQ];
@@ -482,6 +502,7 @@ struct app_params {
 	uint32_t n_pktq_hwq_out;
 	uint32_t n_pktq_swq;
 	uint32_t n_pktq_tm;
+	uint32_t n_pktq_kni;
 	uint32_t n_pktq_source;
 	uint32_t n_pktq_sink;
 	uint32_t n_msgq;
@@ -495,6 +516,7 @@ struct app_params {
 	struct app_link_data link_data[APP_MAX_LINKS];
 	struct rte_ring *swq[APP_MAX_PKTQ_SWQ];
 	struct rte_sched_port *tm[APP_MAX_PKTQ_TM];
+	struct rte_kni *kni[APP_MAX_PKTQ_KNI];
 	struct rte_ring *msgq[APP_MAX_MSGQ];
 	struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
 	struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
@@ -758,6 +780,66 @@ app_tm_get_reader(struct app_params *app,
 }
 
 static inline uint32_t
+app_kni_get_readers(struct app_params *app, struct app_pktq_kni_params *kni)
+{
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq = &p->pktq_in[j];
+
+			if ((pktq->type == APP_PKTQ_IN_KNI) &&
+				(pktq->id == pos))
+				n_readers++;
+		}
+	}
+
+	return n_readers;
+}
+
+static inline struct app_pipeline_params *
+app_kni_get_reader(struct app_params *app,
+	struct app_pktq_kni_params *kni,
+	uint32_t *pktq_in_id)
+{
+	struct app_pipeline_params *reader = NULL;
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers = 0, id = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq = &p->pktq_in[j];
+
+			if ((pktq->type == APP_PKTQ_IN_KNI) &&
+				(pktq->id == pos)) {
+				n_readers++;
+				reader = p;
+				id = j;
+			}
+		}
+	}
+
+	if (n_readers != 1)
+		return NULL;
+
+	*pktq_in_id = id;
+	return reader;
+}
+
+static inline uint32_t
 app_source_get_readers(struct app_params *app,
 struct app_pktq_source_params *source)
 {
@@ -954,6 +1036,67 @@ app_tm_get_writer(struct app_params *app,
 }
 
 static inline uint32_t
+app_kni_get_writers(struct app_params *app, struct app_pktq_kni_params *kni)
+{
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
+			RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq = &p->pktq_out[j];
+
+			if ((pktq->type == APP_PKTQ_OUT_KNI) &&
+				(pktq->id == pos))
+				n_writers++;
+		}
+	}
+
+	return n_writers;
+}
+
+static inline struct app_pipeline_params *
+app_kni_get_writer(struct app_params *app,
+	struct app_pktq_kni_params *kni,
+	uint32_t *pktq_out_id)
+{
+	struct app_pipeline_params *writer = NULL;
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers = 0, id = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
+			RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq = &p->pktq_out[j];
+
+			if ((pktq->type == APP_PKTQ_OUT_KNI) &&
+				(pktq->id == pos))
+				n_writers++;
+				writer = p;
+				id = j;
+		}
+	}
+
+	if (n_writers != 1)
+		return NULL;
+
+	*pktq_out_id = id;
+	return writer;
+}
+
+static inline uint32_t
 app_sink_get_writers(struct app_params *app, struct app_pktq_sink_params *sink)
 {
 	uint32_t pos = sink - app->sink_params;
@@ -1051,6 +1194,22 @@ app_get_link_for_tm(struct app_params *app, struct app_pktq_tm_params *p_tm)
 	return &app->link_params[link_param_idx];
 }
 
+static inline struct app_link_params *
+app_get_link_for_kni(struct app_params *app, struct app_pktq_kni_params *p_kni)
+{
+	char link_name[APP_PARAM_NAME_SIZE];
+	uint32_t link_id;
+	ssize_t link_param_idx;
+
+	sscanf(p_kni->name, "KNI%" PRIu32, &link_id);
+	sprintf(link_name, "LINK%" PRIu32, link_id);
+	link_param_idx = APP_PARAM_FIND(app->link_params, link_name);
+	APP_CHECK((link_param_idx >= 0),
+		"Cannot find %s for %s", link_name, p_kni->name);
+
+	return &app->link_params[link_param_idx];
+}
+
 void app_pipeline_params_get(struct app_params *app,
 	struct app_pipeline_params *p_in,
 	struct pipeline_params *p_out);
diff --git a/examples/ip_pipeline/config/kni.cfg b/examples/ip_pipeline/config/kni.cfg
new file mode 100755
index 0000000..55ad966
--- /dev/null
+++ b/examples/ip_pipeline/config/kni.cfg
@@ -0,0 +1,67 @@
+;   BSD LICENSE
+;
+;   Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+;   All rights reserved.
+;
+;   Redistribution and use in source and binary forms, with or without
+;   modification, are permitted provided that the following conditions
+;   are met:
+;
+;     * Redistributions of source code must retain the above copyright
+;       notice, this list of conditions and the following disclaimer.
+;     * Redistributions in binary form must reproduce the above copyright
+;       notice, this list of conditions and the following disclaimer in
+;       the documentation and/or other materials provided with the
+;       distribution.
+;     * Neither the name of Intel Corporation nor the names of its
+;       contributors may be used to endorse or promote products derived
+;       from this software without specific prior written permission.
+;
+;   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+;   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+;   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+;   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+;   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+;   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+;   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+;   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+;   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+;   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+;   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+;             ______________          ______________________ 
+;            |              |  KNI0  |                      |
+; RXQ0.0 --->|              |------->|--+                   |
+;            |              |  KNI1  |  | br0               |
+; TXQ0.0 <---|              |<-------|<-+                   |
+;            | Pass-through |        |     Linux Kernel     |
+;            |     (P1)     |        |     Network Stack    |
+;            |              |  KNI0  |                      |
+; RXQ0.0 --->|              |------->|--+                   |
+;            |              |  KNI1  |  | br0               |
+; TXQ0.0 <---|              |<-------|<-+                   |
+;            |______________|        |______________________|
+;
+; Insert Linux kernel KNI module:
+;    [Linux]$ insmod rte_kni.ko
+;
+; Configure Linux kernel bridge between KNI0 and KNI1 interfaces:
+;    [Linux]$ ifconfig KNI0 up
+;    [Linux]$ ifconfig KNI1 up
+;    [Linux]$ brctl addbr "br0"
+;    [Linux]$ brctl addif br0 KNI0
+;    [Linux]$ brctl addif br0 KNI1
+;    [Linux]$ ifconfig br0 up
+
+[EAL]
+log_level = 0
+
+[PIPELINE0]
+type = MASTER
+core = 0
+
+[PIPELINE1]
+type = PASS-THROUGH
+core = 1
+pktq_in = RXQ0.0 KNI1 RXQ1.0 KNI0
+pktq_out = KNI0 TXQ1.0 KNI1 TXQ0.0
diff --git a/examples/ip_pipeline/config_check.c b/examples/ip_pipeline/config_check.c
old mode 100644
new mode 100755
index 18f57be..01fa030
--- a/examples/ip_pipeline/config_check.c
+++ b/examples/ip_pipeline/config_check.c
@@ -316,6 +316,29 @@ check_tms(struct app_params *app)
 }
 
 static void
+check_knis(struct app_params *app) {
+	uint32_t i;
+
+	for (i = 0; i < app->n_pktq_kni; i++) {
+		struct app_pktq_kni_params *p = &app->kni_params[i];
+		uint32_t n_readers = app_kni_get_readers(app, p);
+		uint32_t n_writers = app_kni_get_writers(app, p);
+
+		APP_CHECK((n_readers != 0),
+			"%s has no reader\n", p->name);
+
+		APP_CHECK((n_readers == 1),
+			"%s has more than one reader\n", p->name);
+
+		APP_CHECK((n_writers != 0),
+			"%s has no writer\n", p->name);
+
+		APP_CHECK((n_writers == 1),
+			"%s has more than one writer\n", p->name);
+	}
+}
+
+static void
 check_sources(struct app_params *app)
 {
 	uint32_t i;
@@ -453,6 +476,7 @@ app_config_check(struct app_params *app)
 	check_txqs(app);
 	check_swqs(app);
 	check_tms(app);
+	check_knis(app);
 	check_sources(app);
 	check_sinks(app);
 	check_msgqs(app);
diff --git a/examples/ip_pipeline/config_parse.c b/examples/ip_pipeline/config_parse.c
old mode 100644
new mode 100755
index 504018e..a286291
--- a/examples/ip_pipeline/config_parse.c
+++ b/examples/ip_pipeline/config_parse.c
@@ -189,6 +189,18 @@ struct app_pktq_tm_params default_tm_params = {
 	.burst_write = 32,
 };
 
+struct app_pktq_kni_params default_kni_params = {
+	.parsed = 0,
+	.socket_id = 0,
+	.core_id = 0,
+	.hyper_th_id = 0,
+	.force_bind = 0,
+
+	.mempool_id = 0,
+	.burst_read = 32,
+	.burst_write = 32,
+};
+
 struct app_pktq_source_params default_source_params = {
 	.parsed = 0,
 	.mempool_id = 0,
@@ -300,6 +312,18 @@ app_print_usage(char *prgname)
 	link_param_pos;							\
 })
 
+#define APP_PARAM_ADD_LINK_FOR_KNI(app, kni_name)				\
+({									\
+	char link_name[APP_PARAM_NAME_SIZE];				\
+	ssize_t link_param_pos;						\
+	uint32_t link_id;						\
+									\
+	sscanf((kni_name), "KNI%" SCNu32, &link_id);			\
+	sprintf(link_name, "LINK%" PRIu32, link_id);			\
+	link_param_pos = APP_PARAM_ADD((app)->link_params, link_name);	\
+	link_param_pos;							\
+})
+
 #define PARSE_CHECK_DUPLICATE_SECTION(obj)				\
 do {									\
 	APP_CHECK(((obj)->parsed == 0),					\
@@ -826,6 +850,10 @@ parse_pipeline_pktq_in(struct app_params *app,
 			type = APP_PKTQ_IN_TM;
 			id = APP_PARAM_ADD(app->tm_params, name);
 			APP_PARAM_ADD_LINK_FOR_TM(app, name);
+		} else if (validate_name(name, "KNI", 1) == 0) {
+			type = APP_PKTQ_IN_KNI;
+			id = APP_PARAM_ADD(app->kni_params, name);
+			APP_PARAM_ADD_LINK_FOR_KNI(app, name);
 		} else if (validate_name(name, "SOURCE", 1) == 0) {
 			type = APP_PKTQ_IN_SOURCE;
 			id = APP_PARAM_ADD(app->source_params, name);
@@ -871,6 +899,10 @@ parse_pipeline_pktq_out(struct app_params *app,
 			type = APP_PKTQ_OUT_TM;
 			id = APP_PARAM_ADD(app->tm_params, name);
 			APP_PARAM_ADD_LINK_FOR_TM(app, name);
+		} else if (validate_name(name, "KNI", 1) == 0) {
+			type = APP_PKTQ_OUT_KNI;
+			id = APP_PARAM_ADD(app->kni_params, name);
+			APP_PARAM_ADD_LINK_FOR_KNI(app, name);
 		} else if (validate_name(name, "SINK", 1) == 0) {
 			type = APP_PKTQ_OUT_SINK;
 			id = APP_PARAM_ADD(app->sink_params, name);
@@ -1816,7 +1848,7 @@ parse_tm(struct app_params *app,
 	param = &app->tm_params[param_idx];
 	PARSE_CHECK_DUPLICATE_SECTION(param);
 
-	APP_PARAM_ADD_LINK_FOR_TXQ(app, section_name);
+	APP_PARAM_ADD_LINK_FOR_TM(app, section_name);
 
 	for (i = 0; i < n_entries; i++) {
 		struct rte_cfgfile_entry *ent = &entries[i];
@@ -1853,6 +1885,87 @@ parse_tm(struct app_params *app,
 }
 
 static void
+parse_kni(struct app_params *app,
+	const char *section_name,
+	struct rte_cfgfile *cfg)
+{
+	struct app_pktq_kni_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->kni_params, section_name);
+	param = &app->kni_params[param_idx];
+	PARSE_CHECK_DUPLICATE_SECTION(param);
+
+	APP_PARAM_ADD_LINK_FOR_KNI(app, section_name);
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		if (strcmp(ent->name, "core") == 0) {
+			int status = parse_pipeline_core(
+				&param->socket_id,
+				&param->core_id,
+				&param->hyper_th_id,
+				ent->value);
+
+			param->force_bind = 1;
+
+			PARSE_ERROR((status == 0), section_name,
+				ent->name);
+			continue;
+		}
+
+		if (strcmp(ent->name, "mempool") == 0) {
+			int status = validate_name(ent->value,
+				"MEMPOOL", 1);
+			ssize_t idx;
+
+			PARSE_ERROR((status == 0), section_name,
+				ent->name);
+
+			idx = APP_PARAM_ADD(app->mempool_params, ent->value);
+			param->mempool_id = idx;
+			continue;
+		}
+
+		if (strcmp(ent->name, "burst_read") == 0) {
+			int status = parser_read_uint32(&param->burst_read,
+				ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+				ent->name);
+			continue;
+		}
+
+		if (strcmp(ent->name, "burst_write") == 0) {
+			int status = parser_read_uint32(&param->burst_write,
+				ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+				ent->name);
+			continue;
+		}
+
+		/* unrecognized */
+		PARSE_ERROR_INVALID(0, section_name, ent->name);
+	}
+
+	param->parsed = 1;
+
+	free(entries);
+}
+
+static void
 parse_source(struct app_params *app,
 	const char *section_name,
 	struct rte_cfgfile *cfg)
@@ -2147,6 +2260,7 @@ static const struct config_section cfg_file_scheme[] = {
 	{"TXQ", 2, parse_txq},
 	{"SWQ", 1, parse_swq},
 	{"TM", 1, parse_tm},
+	{"KNI", 1, parse_kni},
 	{"SOURCE", 1, parse_source},
 	{"SINK", 1, parse_sink},
 	{"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
@@ -2285,6 +2399,7 @@ app_config_parse(struct app_params *app, const char *file_name)
 	APP_PARAM_COUNT(app->hwq_out_params, app->n_pktq_hwq_out);
 	APP_PARAM_COUNT(app->swq_params, app->n_pktq_swq);
 	APP_PARAM_COUNT(app->tm_params, app->n_pktq_tm);
+	APP_PARAM_COUNT(app->kni_params, app->n_pktq_kni);
 	APP_PARAM_COUNT(app->source_params, app->n_pktq_source);
 	APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
 	APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
@@ -2647,6 +2762,46 @@ save_tm_params(struct app_params *app, FILE *f)
 }
 
 static void
+save_kni_params(struct app_params *app, FILE *f)
+{
+	struct app_pktq_kni_params *p;
+	size_t i, count;
+
+	count = RTE_DIM(app->kni_params);
+	for (i = 0; i < count; i++) {
+		p = &app->kni_params[i];
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		/* section name */
+		fprintf(f, "[%s]\n", p->name);
+
+		/* core */
+		if (p->force_bind) {
+			fprintf(f, "; force_bind = 1\n");
+			fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
+				p->socket_id,
+				p->core_id,
+				(p->hyper_th_id) ? "h" : "");
+		} else
+			fprintf(f, "; force_bind = 0\n");
+
+		/* mempool */
+		fprintf(f, "%s = %s\n",
+			"mempool",
+			app->mempool_params[p->mempool_id].name);
+
+		/* burst_read */
+		fprintf(f, "%s = %" PRIu32 "\n", "burst_read", p->burst_read);
+
+		/* burst_write */
+		fprintf(f, "%s = %" PRIu32 "\n", "burst_write", p->burst_read);
+
+		fputc('\n', f);
+	}
+}
+
+static void
 save_source_params(struct app_params *app, FILE *f)
 {
 	struct app_pktq_source_params *p;
@@ -2753,6 +2908,9 @@ save_pipeline_params(struct app_params *app, FILE *f)
 				case APP_PKTQ_IN_TM:
 					name = app->tm_params[pp->id].name;
 					break;
+				case APP_PKTQ_IN_KNI:
+					name = app->kni_params[pp->id].name;
+					break;
 				case APP_PKTQ_IN_SOURCE:
 					name = app->source_params[pp->id].name;
 					break;
@@ -2787,6 +2945,9 @@ save_pipeline_params(struct app_params *app, FILE *f)
 				case APP_PKTQ_OUT_TM:
 					name = app->tm_params[pp->id].name;
 					break;
+				case APP_PKTQ_OUT_KNI:
+					name = app->kni_params[pp->id].name;
+					break;
 				case APP_PKTQ_OUT_SINK:
 					name = app->sink_params[pp->id].name;
 					break;
@@ -2872,6 +3033,7 @@ app_config_save(struct app_params *app, const char *file_name)
 	save_txq_params(app, file);
 	save_swq_params(app, file);
 	save_tm_params(app, file);
+	save_kni_params(app, file);
 	save_source_params(app, file);
 	save_sink_params(app, file);
 	save_msgq_params(app, file);
@@ -2921,6 +3083,11 @@ app_config_init(struct app_params *app)
 			&default_tm_params,
 			sizeof(default_tm_params));
 
+	for (i = 0; i < RTE_DIM(app->kni_params); i++)
+		memcpy(&app->kni_params[i],
+			&default_kni_params,
+			sizeof(default_kni_params));
+
 	for (i = 0; i < RTE_DIM(app->source_params); i++)
 		memcpy(&app->source_params[i],
 			&default_source_params,
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
old mode 100644
new mode 100755
index 7120bab..0c76d91
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -1176,6 +1176,109 @@ app_init_tm(struct app_params *app)
 	}
 }
 
+static int
+kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
+	int ret;
+
+	if (port_id >= rte_eth_dev_count())
+		return -EINVAL;
+
+	if (new_mtu > ETHER_MAX_LEN)
+		return -EINVAL;
+
+	/* Stop specific port */
+	rte_eth_dev_stop(port_id);
+
+	/* Set new MTU */
+	ret = rte_eth_dev_set_mtu(port_id, new_mtu);
+	if (ret < 0)
+		return ret;
+
+	/* Restart specific port */
+	ret = rte_eth_dev_start(port_id);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int
+kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
+	int ret = 0;
+
+	if (port_id >= rte_eth_dev_count())
+		return -EINVAL;
+
+	if (if_up) {
+		rte_eth_dev_stop(port_id);
+		ret = rte_eth_dev_start(port_id);
+	} else
+		rte_eth_dev_stop(port_id);
+
+	return ret;
+}
+
+static void
+app_init_kni(struct app_params *app) {
+	uint32_t i;
+
+	if (app->n_pktq_kni == 0)
+		return;
+
+	rte_kni_init(app->n_pktq_kni);
+
+	for (i = 0; i < app->n_pktq_kni; i++) {
+		struct app_pktq_kni_params *p_kni = &app->kni_params[i];
+		struct app_link_params *p_link;
+		struct rte_eth_dev_info dev_info;
+		struct app_mempool_params *mempool_params;
+		struct rte_mempool *mempool;
+		struct rte_kni_conf conf;
+		struct rte_kni_ops ops;
+
+		/* LINK */
+		p_link = app_get_link_for_kni(app, p_kni);
+		memset(&dev_info, 0, sizeof(dev_info));
+		rte_eth_dev_info_get(p_link->pmd_id, &dev_info);
+
+		/* MEMPOOL */
+		mempool_params = &app->mempool_params[p_kni->mempool_id];
+		mempool = app->mempool[p_kni->mempool_id];
+
+		/* KNI */
+		memset(&conf, 0, sizeof(conf));
+		snprintf(conf.name, RTE_KNI_NAMESIZE, "%s", p_kni->name);
+		conf.force_bind = p_kni->force_bind;
+		if (conf.force_bind) {
+			int lcore_id;
+
+			lcore_id = cpu_core_map_get_lcore_id(app->core_map,
+				p_kni->socket_id,
+				p_kni->core_id,
+				p_kni->hyper_th_id);
+
+			if (lcore_id < 0)
+				rte_panic("%s invalid CPU core\n", p_kni->name);
+
+			conf.core_id = (uint32_t) lcore_id;
+		}
+		conf.group_id = p_link->pmd_id;
+		conf.mbuf_size = mempool_params->buffer_size;
+		conf.addr = dev_info.pci_dev->addr;
+		conf.id = dev_info.pci_dev->id;
+
+		memset(&ops, 0, sizeof(ops));
+		ops.port_id = (uint8_t) p_link->pmd_id;
+		ops.change_mtu = kni_change_mtu;
+		ops.config_network_if = kni_config_network_interface;
+
+		APP_LOG(app, HIGH, "Initializing %s ...", p_kni->name);
+		app->kni[i] = rte_kni_alloc(mempool, &conf, &ops);
+		if (!app->kni[i])
+			rte_panic("%s init error\n", p_kni->name);
+	}
+}
+
 static void
 app_init_msgq(struct app_params *app)
 {
@@ -1285,6 +1388,11 @@ void app_pipeline_params_get(struct app_params *app,
 			out->params.sched.sched = app->tm[in->id];
 			out->burst_size = app->tm_params[in->id].burst_read;
 			break;
+		case APP_PKTQ_IN_KNI:
+			out->type = PIPELINE_PORT_IN_KNI_READER;
+			out->params.kni.kni = app->kni[in->id];
+			out->burst_size = app->kni_params[in->id].burst_read;
+			break;
 		case APP_PKTQ_IN_SOURCE:
 		{
 			uint32_t mempool_id =
@@ -1409,7 +1517,8 @@ void app_pipeline_params_get(struct app_params *app,
 			}
 			break;
 		}
-		case APP_PKTQ_OUT_TM: {
+		case APP_PKTQ_OUT_TM:
+		{
 			struct rte_port_sched_writer_params *params =
 				&out->params.sched;
 
@@ -1419,6 +1528,11 @@ void app_pipeline_params_get(struct app_params *app,
 				app->tm_params[in->id].burst_write;
 			break;
 		}
+		case APP_PKTQ_OUT_KNI:
+			out->type = PIPELINE_PORT_OUT_KNI_WRITER;
+			out->params.kni.kni = app->kni[in->id];
+			out->params.kni.tx_burst_sz = app->kni_params[in->id].burst_write;
+			break;
 		case APP_PKTQ_OUT_SINK:
 		{
 			out->type = PIPELINE_PORT_OUT_SINK;
@@ -1607,6 +1721,7 @@ int app_init(struct app_params *app)
 	app_init_link(app);
 	app_init_swq(app);
 	app_init_tm(app);
+	app_init_kni(app);
 	app_init_msgq(app);
 
 	app_pipeline_common_cmd_push(app);
diff --git a/examples/ip_pipeline/pipeline/pipeline_common_fe.c b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
old mode 100644
new mode 100755
index 70c57e4..cd1d082
--- a/examples/ip_pipeline/pipeline/pipeline_common_fe.c
+++ b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
@@ -130,6 +130,33 @@ app_pipeline_track_pktq_out_to_link(struct app_params *app,
 			break;
 		}
 
+		case APP_PKTQ_OUT_KNI:
+		{
+			struct pipeline_params pp;
+			struct pipeline_type *ptype;
+			struct app_pktq_kni_params *kni;
+			uint32_t pktq_in_id;
+			int status;
+
+			kni = &app->kni_params[pktq_out->id];
+			p = app_kni_get_reader(app, kni, &pktq_in_id);
+			if (p == NULL)
+				return NULL;
+
+			ptype = app_pipeline_type_find(app, p->type);
+			if ((ptype == NULL) || (ptype->fe_ops->f_track == NULL))
+				return NULL;
+
+			app_pipeline_params_get(app, p, &pp);
+			status = ptype->fe_ops->f_track(&pp,
+				pktq_in_id,
+				&pktq_out_id);
+			if (status)
+				return NULL;
+
+			break;
+		}
+
 		case APP_PKTQ_OUT_SINK:
 		default:
 			return NULL;
diff --git a/examples/ip_pipeline/pipeline/pipeline_master_be.c b/examples/ip_pipeline/pipeline/pipeline_master_be.c
old mode 100644
new mode 100755
index 79869a4..ba2a863
--- a/examples/ip_pipeline/pipeline/pipeline_master_be.c
+++ b/examples/ip_pipeline/pipeline/pipeline_master_be.c
@@ -105,6 +105,7 @@ pipeline_run(void *pipeline)
 {
 	struct pipeline_master *p = (struct pipeline_master *) pipeline;
 	struct app_params *app = p->app;
+	uint32_t i;
 	int status;
 
 	/* Application post-init phase */
@@ -144,6 +145,10 @@ pipeline_run(void *pipeline)
 		rte_exit(0, "Bye!\n");
 	}
 
+	/* Handle KNI requests from Linux kernel */
+	for (i = 0; i < app->n_pktq_kni; i++)
+		rte_kni_handle_request(app->kni[i]);
+
 	return 0;
 }
 
diff --git a/examples/ip_pipeline/pipeline_be.h b/examples/ip_pipeline/pipeline_be.h
old mode 100644
new mode 100755
index 5501ab7..972708e
--- a/examples/ip_pipeline/pipeline_be.h
+++ b/examples/ip_pipeline/pipeline_be.h
@@ -40,6 +40,7 @@
 #include <rte_port_ras.h>
 #include <rte_port_sched.h>
 #include <rte_port_source_sink.h>
+#include <rte_port_kni.h>
 #include <rte_pipeline.h>
 
 enum pipeline_port_in_type {
@@ -49,6 +50,7 @@ enum pipeline_port_in_type {
 	PIPELINE_PORT_IN_RING_READER_IPV4_FRAG,
 	PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
 	PIPELINE_PORT_IN_SCHED_READER,
+	PIPELINE_PORT_IN_KNI_READER,
 	PIPELINE_PORT_IN_SOURCE,
 };
 
@@ -61,6 +63,7 @@ struct pipeline_port_in_params {
 		struct rte_port_ring_reader_ipv4_frag_params ring_ipv4_frag;
 		struct rte_port_ring_reader_ipv6_frag_params ring_ipv6_frag;
 		struct rte_port_sched_reader_params sched;
+		struct rte_port_kni_reader_params kni;
 		struct rte_port_source_params source;
 	} params;
 	uint32_t burst_size;
@@ -82,6 +85,8 @@ pipeline_port_in_params_convert(struct pipeline_port_in_params  *p)
 		return (void *) &p->params.ring_ipv6_frag;
 	case PIPELINE_PORT_IN_SCHED_READER:
 		return (void *) &p->params.sched;
+	case PIPELINE_PORT_IN_KNI_READER:
+		return (void *) &p->params.kni;
 	case PIPELINE_PORT_IN_SOURCE:
 		return (void *) &p->params.source;
 	default:
@@ -105,6 +110,8 @@ pipeline_port_in_params_get_ops(struct pipeline_port_in_params  *p)
 		return &rte_port_ring_reader_ipv6_frag_ops;
 	case PIPELINE_PORT_IN_SCHED_READER:
 		return &rte_port_sched_reader_ops;
+	case PIPELINE_PORT_IN_KNI_READER:
+		return &rte_port_kni_reader_ops;
 	case PIPELINE_PORT_IN_SOURCE:
 		return &rte_port_source_ops;
 	default:
@@ -122,6 +129,7 @@ enum pipeline_port_out_type {
 	PIPELINE_PORT_OUT_RING_WRITER_IPV4_RAS,
 	PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
 	PIPELINE_PORT_OUT_SCHED_WRITER,
+	PIPELINE_PORT_OUT_KNI_WRITER,
 	PIPELINE_PORT_OUT_SINK,
 };
 
@@ -137,6 +145,7 @@ struct pipeline_port_out_params {
 		struct rte_port_ring_writer_ipv4_ras_params ring_ipv4_ras;
 		struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
 		struct rte_port_sched_writer_params sched;
+		struct rte_port_kni_writer_params kni;
 		struct rte_port_sink_params sink;
 	} params;
 };
@@ -163,6 +172,8 @@ pipeline_port_out_params_convert(struct pipeline_port_out_params  *p)
 		return (void *) &p->params.ring_ipv6_ras;
 	case PIPELINE_PORT_OUT_SCHED_WRITER:
 		return (void *) &p->params.sched;
+	case PIPELINE_PORT_OUT_KNI_WRITER:
+		return (void *) &p->params.kni;
 	case PIPELINE_PORT_OUT_SINK:
 		return (void *) &p->params.sink;
 	default:
@@ -192,6 +203,8 @@ pipeline_port_out_params_get_ops(struct pipeline_port_out_params  *p)
 		return &rte_port_ring_writer_ipv6_ras_ops;
 	case PIPELINE_PORT_OUT_SCHED_WRITER:
 		return &rte_port_sched_writer_ops;
+	case PIPELINE_PORT_OUT_KNI_WRITER:
+		return &rte_port_kni_writer_ops;
 	case PIPELINE_PORT_OUT_SINK:
 		return &rte_port_sink_ops;
 	default:
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
old mode 100644
new mode 100755
index d4de5af..52d2485
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -57,6 +57,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
 endif
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
+endif
 
 # install includes
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port.h
@@ -68,6 +71,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_ras.h
 endif
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
+endif
 
 # this lib depends upon:
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
@@ -75,5 +81,8 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mbuf
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
+endif
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/rte_port_kni.c b/lib/librte_port/rte_port_kni.c
new file mode 100755
index 0000000..ddc26c9
--- /dev/null
+++ b/lib/librte_port/rte_port_kni.c
@@ -0,0 +1,318 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_kni.h>
+
+#include "rte_port_kni.h"
+
+/*
+ * Port KNI Reader
+ */
+#ifdef RTE_PORT_STATS_COLLECT
+
+#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
+	port->stats.n_pkts_in += val
+#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
+	port->stats.n_pkts_drop += val
+
+#else
+
+#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
+#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
+
+#endif
+
+struct rte_port_kni_reader {
+	struct rte_port_in_stats stats;
+
+	struct rte_kni *kni;
+};
+
+static void *
+rte_port_kni_reader_create(void *params, int socket_id) {
+	struct rte_port_kni_reader_params *conf =
+			(struct rte_port_kni_reader_params *) params;
+	struct rte_port_kni_reader *port;
+
+	/* Check input parameters */
+	if (conf == NULL) {
+		RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	port = rte_zmalloc_socket("PORT", sizeof(*port),
+		RTE_CACHE_LINE_SIZE, socket_id);
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni = conf->kni;
+
+	return port;
+}
+
+static int
+rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t n_pkts) {
+	struct rte_port_kni_reader *p =
+			(struct rte_port_kni_reader *) port;
+	uint16_t rx_pkt_cnt;
+
+	rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
+	RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
+	return rx_pkt_cnt;
+}
+
+static int
+rte_port_kni_reader_free(void *port) {
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rte_free(port);
+
+	return 0;
+}
+
+static int rte_port_kni_reader_stats_read(void *port,
+	struct rte_port_in_stats *stats, int clear)
+{
+	struct rte_port_kni_reader *p =
+			(struct rte_port_kni_reader *) port;
+
+	if (stats != NULL)
+		memcpy(stats, &p->stats, sizeof(p->stats));
+
+	if (clear)
+		memset(&p->stats, 0, sizeof(p->stats));
+
+	return 0;
+}
+
+/*
+ * Port KNI Writer
+ */
+#ifdef RTE_PORT_STATS_COLLECT
+
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
+	port->stats.n_pkts_in += val
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
+	port->stats.n_pkts_drop += val
+
+#else
+
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
+
+#endif
+
+struct rte_port_kni_writer {
+	struct rte_port_out_stats stats;
+
+	struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
+	uint32_t tx_burst_sz;
+	uint16_t tx_buf_count;
+	uint64_t bsz_mask;
+	struct rte_kni *kni;
+};
+
+static void *
+rte_port_kni_writer_create(void *params, int socket_id) {
+	struct rte_port_kni_writer_params *conf =
+			(struct rte_port_kni_writer_params *) params;
+	struct rte_port_kni_writer *port;
+
+	/* Check input parameters */
+	if ((conf == NULL) ||
+		(conf->tx_burst_sz == 0) ||
+		(conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
+		(!rte_is_power_of_2(conf->tx_burst_sz))) {
+		RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n", __func__);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	port = rte_zmalloc_socket("PORT", sizeof(*port),
+		RTE_CACHE_LINE_SIZE, socket_id);
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni = conf->kni;
+	port->tx_burst_sz = conf->tx_burst_sz;
+	port->tx_buf_count = 0;
+	port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
+
+	return port;
+}
+
+static inline void
+send_burst(struct rte_port_kni_writer *p) {
+	uint32_t nb_tx;
+
+	nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
+
+	RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p->tx_buf_count - nb_tx);
+	for ( ; nb_tx < p->tx_buf_count; nb_tx++)
+		rte_pktmbuf_free(p->tx_buf[nb_tx]);
+
+	p->tx_buf_count = 0;
+}
+
+static int
+rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt) {
+	struct rte_port_kni_writer *p =
+		(struct rte_port_kni_writer *) port;
+
+	p->tx_buf[p->tx_buf_count++] = pkt;
+	RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+	if (p->tx_buf_count >= p->tx_burst_sz)
+		send_burst(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_tx_bulk(void *port,
+	struct rte_mbuf **pkts,
+	uint64_t pkts_mask)
+{
+	struct rte_port_kni_writer *p =
+		(struct rte_port_kni_writer *) port;
+	uint64_t bsz_mask = p->bsz_mask;
+	uint32_t tx_buf_count = p->tx_buf_count;
+	uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
+		((pkts_mask & bsz_mask) ^ bsz_mask);
+
+	if (expr == 0) {
+		uint64_t n_pkts = __builtin_popcountll(pkts_mask);
+		uint32_t n_pkts_ok;
+
+		if (tx_buf_count)
+			send_burst(p);
+
+		RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
+		n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
+
+		RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts - n_pkts_ok);
+		for ( ; n_pkts_ok < n_pkts; n_pkts_ok++) {
+			struct rte_mbuf *pkt = pkts[n_pkts_ok];
+
+			rte_pktmbuf_free(pkt);
+		}
+	} else {
+		for ( ; pkts_mask; ) {
+			uint32_t pkt_index = __builtin_ctzll(pkts_mask);
+			uint64_t pkt_mask = 1LLU << pkt_index;
+			struct rte_mbuf *pkt = pkts[pkt_index];
+
+			p->tx_buf[tx_buf_count++] = pkt;
+			RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+			pkts_mask &= ~pkt_mask;
+		}
+
+		p->tx_buf_count = tx_buf_count;
+		if (tx_buf_count >= p->tx_burst_sz)
+			send_burst(p);
+	}
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_flush(void *port)
+{
+	struct rte_port_kni_writer *p =
+		(struct rte_port_kni_writer *) port;
+
+	if (p->tx_buf_count > 0)
+		send_burst(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_free(void *port)
+{
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rte_port_kni_writer_flush(port);
+	rte_free(port);
+
+	return 0;
+}
+
+static int rte_port_kni_writer_stats_read(void *port,
+	struct rte_port_out_stats *stats, int clear)
+{
+	struct rte_port_kni_writer *p =
+		(struct rte_port_kni_writer *) port;
+
+	if (stats != NULL)
+		memcpy(stats, &p->stats, sizeof(p->stats));
+
+	if (clear)
+		memset(&p->stats, 0, sizeof(p->stats));
+
+	return 0;
+}
+
+/*
+ * Summary of port operations
+ */
+struct rte_port_in_ops rte_port_kni_reader_ops = {
+	.f_create = rte_port_kni_reader_create,
+	.f_free = rte_port_kni_reader_free,
+	.f_rx = rte_port_kni_reader_rx,
+	.f_stats = rte_port_kni_reader_stats_read,
+};
+
+struct rte_port_out_ops rte_port_kni_writer_ops = {
+	.f_create = rte_port_kni_writer_create,
+	.f_free = rte_port_kni_writer_free,
+	.f_tx = rte_port_kni_writer_tx,
+	.f_tx_bulk = rte_port_kni_writer_tx_bulk,
+	.f_flush = rte_port_kni_writer_flush,
+	.f_stats = rte_port_kni_writer_stats_read,
+};
diff --git a/lib/librte_port/rte_port_kni.h b/lib/librte_port/rte_port_kni.h
new file mode 100755
index 0000000..5b729c6
--- /dev/null
+++ b/lib/librte_port/rte_port_kni.h
@@ -0,0 +1,82 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_RTE_PORT_KNI_H__
+#define __INCLUDE_RTE_PORT_KNI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * RTE Port KNI Interface
+ *
+ * kni_reader: input port built on top of pre-initialized KNI interface
+ * kni_writer: output port built on top of pre-initialized KNI interface
+ *
+ ***/
+
+#include <stdint.h>
+
+#include <rte_kni.h>
+
+#include "rte_port.h"
+
+/** kni_reader port parameters */
+struct rte_port_kni_reader_params {
+	/** KNI interface reference */
+	struct rte_kni *kni;
+};
+
+/** kni_reader port operations */
+extern struct rte_port_in_ops rte_port_kni_reader_ops;
+
+
+/** kni_writer port parameters */
+struct rte_port_kni_writer_params {
+	/** KNI interface reference */
+	struct rte_kni *kni;
+
+	/** Burst size to KNI interface. */
+	uint32_t tx_burst_sz;
+};
+
+/** kni_writer port operations */
+extern struct rte_port_out_ops rte_port_kni_writer_ops;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

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

* Re: [PATCH] port: add kni interface support
  2016-06-09 23:42   ` Dumitrescu, Cristian
  2016-06-13 10:25     ` Dumitrescu, Cristian
@ 2016-06-13 10:47     ` Ethan
  2016-06-13 13:18       ` Dumitrescu, Cristian
  1 sibling, 1 reply; 22+ messages in thread
From: Ethan @ 2016-06-13 10:47 UTC (permalink / raw)
  To: Dumitrescu, Cristian; +Cc: dev, Singh, Jasvinder, Yigit, Ferruh

Hi Cristian,

I've got your comments. Thank you for review the code from a DPDK newbie.
:-)
I plan to submit a new patch to fix all during this week hopefully.

There are four places I'd like to discuss further:

1. Dedicated lcore for kni kernel thread
First of all, it is a bug to add kni kernel core to the user space core
mask. What I want is just to check if the kni kernel thread has a dedicated
core.
The reason I prefer to allocate a dedicated core to kni kernel thread is
that my application is latency sensitive. I worry the context switch and
cache miss will cause the latency increasing if the kni kernel thread and
application thread share one core.
Anyway, I think I should remove the hard coded check because it will be
more generic. Users who has the similar usage like mine can achieve so
through configuration file.

2. The compiler error of the Macro RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD
Actually I implements the macro similar
to RTE_PORT_RING_READER_STATS_PKTS_IN_ADD first. But the
scripts/checkpatches.sh fails: ERROR:COMPLEX_MACRO: Macros with complex
values should be enclosed in parentheses
I'm not share either I have done something wrong or the checkpatches script
need an update.

3. KNI kernel operations callback
To be  honest, I made reference to the the KNI sample application.
Since there is very little docs tell the difference between link up call
and device start call, I am not sure which one is better here.
Any help will be appreciate. :-)

4. Shall I use DPDK_16.07 in the  librte_port/rte_port_version.map file?



2016-06-10 7:42 GMT+08:00 Dumitrescu, Cristian <
cristian.dumitrescu@intel.com>:

> Hi Ethan,
>
> Great work! There are still several comments below that need to be
> addressed, but I am confident we can close on them quickly. Thank you!
>
> Please rebase the next version on top of the latest code on master branch.
>
> Please also update librte_port/rte_port_version.map file.
>
> Shall I use DPDK_16.07 in the  librte_port/rte_port_version.map file?


>
> > -----Original Message-----
> > From: WeiJie Zhuang [mailto:zhuangwj@gmail.com]
> > Sent: Saturday, May 28, 2016 12:26 PM
> > To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>
> > Cc: dev@dpdk.org; WeiJie Zhuang <zhuangwj@gmail.com>
> > Subject: [PATCH] port: add kni interface support
> >
> > 1. add KNI port type to the packet framework
> > 2. add KNI support to the IP Pipeline sample Application
> >
> > Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
> > ---
> > v2:
> > * Fix check patch error.
> > ---
> >  doc/api/doxy-api-index.md           |   1 +
> >  examples/ip_pipeline/Makefile       |   6 +-
> >  examples/ip_pipeline/app.h          |  74 +++++++++
> >  examples/ip_pipeline/config/kni.cfg |  12 ++
> >  examples/ip_pipeline/config_check.c |  34 ++++
> >  examples/ip_pipeline/config_parse.c | 130 +++++++++++++++
> >  examples/ip_pipeline/init.c         |  79 +++++++++
> >  examples/ip_pipeline/kni/kni.c      |  80 +++++++++
> >  examples/ip_pipeline/kni/kni.h      |  16 ++
> >  examples/ip_pipeline/pipeline_be.h  |  13 ++
> >  examples/ip_pipeline/thread.c       |   9 +
> >  lib/librte_port/Makefile            |   7 +
> >  lib/librte_port/rte_port_kni.c      | 316
> > ++++++++++++++++++++++++++++++++++++
> >  lib/librte_port/rte_port_kni.h      |  81 +++++++++
> >  14 files changed, 856 insertions(+), 2 deletions(-)
> >  create mode 100644 examples/ip_pipeline/config/kni.cfg
> >  create mode 100644 examples/ip_pipeline/kni/kni.c
> >  create mode 100644 examples/ip_pipeline/kni/kni.h
> >  create mode 100644 lib/librte_port/rte_port_kni.c
> >  create mode 100644 lib/librte_port/rte_port_kni.h
> >
> > diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> > index f626386..e38a959 100644
> > --- a/doc/api/doxy-api-index.md
> > +++ b/doc/api/doxy-api-index.md
> > @@ -119,6 +119,7 @@ There are many libraries, so their headers may be
> > grouped by topics:
> >      [reass]            (@ref rte_port_ras.h),
> >      [sched]            (@ref rte_port_sched.h),
> >      [src/sink]         (@ref rte_port_source_sink.h)
> > +    [kni]              (@ref rte_port_kni.h)
> >    * [table]            (@ref rte_table.h):
> >      [lpm IPv4]         (@ref rte_table_lpm.h),
> >      [lpm IPv6]         (@ref rte_table_lpm_ipv6.h),
> > diff --git a/examples/ip_pipeline/Makefile
> b/examples/ip_pipeline/Makefile
> > index 10fe1ba..848c2aa 100644
> > --- a/examples/ip_pipeline/Makefile
> > +++ b/examples/ip_pipeline/Makefile
> > @@ -43,9 +43,10 @@ include $(RTE_SDK)/mk/rte.vars.mk
> >  # binary name
> >  APP = ip_pipeline
> >
> > +VPATH += $(SRCDIR)/kni
> >  VPATH += $(SRCDIR)/pipeline
> >
> > -INC += $(wildcard *.h) $(wildcard pipeline/*.h)
> > +INC += $(wildcard *.h) $(wildcard pipeline/*.h) $(wildcard kni/*.h)
> >
> >  # all source are stored in SRCS-y
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) := main.c
> > @@ -56,6 +57,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread_fe.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
> > +SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += kni.c
> >
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_be.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_fe.c
> > @@ -72,7 +74,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) +=
> > pipeline_flow_actions.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing_be.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing.c
> >
> > -CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline
> > +CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline -I$(SRCDIR)/kni
> >  CFLAGS += -O3
> >  CFLAGS += $(WERROR_FLAGS) -Wno-error=unused-function -Wno-
> > error=unused-variable
> >
>
> I would like to avoid creating the kni subfolder. Please move the
> functions from kni/kni.c to init.c file, just before function
> app_init_kni(), where they should be declared as static functions.
>
> > diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
> > index e775024..a86ce57 100644
> > --- a/examples/ip_pipeline/app.h
> > +++ b/examples/ip_pipeline/app.h
> > @@ -44,7 +44,9 @@
> >  #include <cmdline_parse.h>
> >
> >  #include <rte_ethdev.h>
> > +#include <rte_kni.h>
> >
> > +#include "kni.h"
> >  #include "cpu_core_map.h"
> >  #include "pipeline.h"
> >
> > @@ -99,6 +101,18 @@ struct app_pktq_hwq_out_params {
> >       struct rte_eth_txconf conf;
> >  };
> >
> > +struct app_kni_params {
> > +     char *name;
> > +     uint32_t parsed;
> > +
> > +     uint32_t socket_id;
> > +     uint32_t core_id;
> > +     uint32_t hyper_th_id;
> > +
> > +     uint32_t mempool_id;
>
> Please add the usual comment on the same line: /* Position in the
> app->mempool_params */
>
> > +     uint32_t burst;
>
> Why having a unified value for read and write burst size? Please use
> burst_read and burst_write (both uint32_t) instead and update the
> config_parse.c and init.c code accordingly (small changes).
>
> > +};
> > +
> >  struct app_pktq_swq_params {
> >       char *name;
> >       uint32_t parsed;
> > @@ -172,6 +186,7 @@ enum app_pktq_in_type {
> >       APP_PKTQ_IN_SWQ,
> >       APP_PKTQ_IN_TM,
> >       APP_PKTQ_IN_SOURCE,
> > +     APP_PKTQ_IN_KNI,
> >  };
> >
> >  struct app_pktq_in_params {
> > @@ -184,6 +199,7 @@ enum app_pktq_out_type {
> >       APP_PKTQ_OUT_SWQ,
> >       APP_PKTQ_OUT_TM,
> >       APP_PKTQ_OUT_SINK,
> > +     APP_PKTQ_OUT_KNI,
> >  };
> >
> >  struct app_pktq_out_params {
> > @@ -434,6 +450,10 @@ struct app_eal_params {
> >  #define APP_THREAD_HEADROOM_STATS_COLLECT        1
> >  #endif
> >
> > +#ifndef APP_MAX_KNI
> > +#define APP_MAX_KNI                              8
> > +#endif
> > +
> >  struct app_params {
> >       /* Config */
> >       char app_name[APP_APPNAME_SIZE];
> > @@ -457,6 +477,7 @@ struct app_params {
> >       struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
> >       struct app_msgq_params msgq_params[APP_MAX_MSGQ];
> >       struct app_pipeline_params pipeline_params[APP_MAX_PIPELINES];
> > +     struct app_kni_params kni_params[APP_MAX_KNI];
> >
> >       uint32_t n_mempools;
> >       uint32_t n_links;
> > @@ -468,6 +489,7 @@ struct app_params {
> >       uint32_t n_pktq_sink;
> >       uint32_t n_msgq;
> >       uint32_t n_pipelines;
> > +     uint32_t n_kni;
> >
> >       /* Init */
> >       char *eal_argv[1 + APP_EAL_ARGC];
> > @@ -480,6 +502,7 @@ struct app_params {
> >       struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
> >       struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
> >       struct app_thread_data thread_data[APP_MAX_THREADS];
> > +     struct rte_kni *kni[APP_MAX_KNI];
> >       cmdline_parse_ctx_t cmds[APP_MAX_CMDS + 1];
> >
> >       int eal_argc;
> > @@ -738,6 +761,31 @@ app_msgq_get_readers(struct app_params *app,
> > struct app_msgq_params *msgq)
> >  }
> >
> >  static inline uint32_t
> > +app_kni_get_readers(struct app_params *app, struct app_kni_params
> > *kni)
> > +{
> > +     uint32_t pos = kni - app->kni_params;
> > +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> > +             RTE_DIM(app->pipeline_params));
> > +     uint32_t n_readers = 0, i;
> > +
> > +     for (i = 0; i < n_pipelines; i++) {
> > +             struct app_pipeline_params *p = &app->pipeline_params[i];
> > +             uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p-
> > >pktq_in));
> > +             uint32_t j;
> > +
> > +             for (j = 0; j < n_pktq_in; j++) {
> > +                     struct app_pktq_in_params *pktq = &p->pktq_in[j];
> > +
> > +                     if ((pktq->type == APP_PKTQ_IN_KNI) &&
> > +                             (pktq->id == pos))
> > +                             n_readers++;
> > +             }
> > +     }
> > +
> > +     return n_readers;
> > +}
> > +
> > +static inline uint32_t
> >  app_txq_get_writers(struct app_params *app, struct
> > app_pktq_hwq_out_params *txq)
> >  {
> >       uint32_t pos = txq - app->hwq_out_params;
> > @@ -863,6 +911,32 @@ app_msgq_get_writers(struct app_params *app,
> > struct app_msgq_params *msgq)
> >       return n_writers;
> >  }
> >
> > +static inline uint32_t
> > +app_kni_get_writers(struct app_params *app, struct app_kni_params *kni)
> > +{
> > +     uint32_t pos = kni - app->kni_params;
> > +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> > +             RTE_DIM(app->pipeline_params));
> > +     uint32_t n_writers = 0, i;
> > +
> > +     for (i = 0; i < n_pipelines; i++) {
> > +             struct app_pipeline_params *p = &app->pipeline_params[i];
> > +             uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
> > +                     RTE_DIM(p->pktq_out));
> > +             uint32_t j;
> > +
> > +             for (j = 0; j < n_pktq_out; j++) {
> > +                     struct app_pktq_out_params *pktq = &p-
> > >pktq_out[j];
> > +
> > +                     if ((pktq->type == APP_PKTQ_OUT_KNI) &&
> > +                             (pktq->id == pos))
> > +                             n_writers++;
> > +             }
> > +     }
> > +
> > +     return n_writers;
> > +}
> > +
> >  static inline struct app_link_params *
> >  app_get_link_for_rxq(struct app_params *app, struct
> > app_pktq_hwq_in_params *p)
> >  {
> > diff --git a/examples/ip_pipeline/config/kni.cfg
> > b/examples/ip_pipeline/config/kni.cfg
> > new file mode 100644
> > index 0000000..30466b0
> > --- /dev/null
> > +++ b/examples/ip_pipeline/config/kni.cfg
> > @@ -0,0 +1,12 @@
> > +[KNI0]
> > +core = 2
> > +
> > +[PIPELINE0]
> > +type = MASTER
> > +core = 0
> > +
> > +[PIPELINE1]
> > +type = PASS-THROUGH
> > +core = 1
> > +pktq_in = RXQ0.0 KNI0
> > +pktq_out = KNI0 TXQ0.0
>
> I don't think this new config file config/kni.cfg is really useful, so I
> would like to remove it. We should avoid the proliferation of
> straightforward config files.
>
> > diff --git a/examples/ip_pipeline/config_check.c
> > b/examples/ip_pipeline/config_check.c
> > index fd9ff49..3e300f9 100644
> > --- a/examples/ip_pipeline/config_check.c
> > +++ b/examples/ip_pipeline/config_check.c
> > @@ -426,6 +426,39 @@ check_pipelines(struct app_params *app)
> >       }
> >  }
> >
> > +static void
> > +check_kni(struct app_params *app) {
> > +     uint32_t i;
> > +     uint32_t port_id;
> > +
> > +     for (i = 0; i < app->n_kni; i++) {
> > +             struct app_kni_params *p = &app->kni_params[i];
> > +             uint32_t n_readers = app_kni_get_readers(app, p);
> > +             uint32_t n_writers = app_kni_get_writers(app, p);
> > +
> > +             APP_CHECK((n_readers != 0),
> > +                               "%s has no reader\n", p->name);
> > +
> > +             if (n_readers > 1)
> > +                     APP_LOG(app, LOW,
> > +                                     "%s has more than one reader", p-
> > >name);
> > +
> > +             APP_CHECK((n_writers != 0),
> > +                               "%s has no writer\n", p->name);
> > +
> > +             if (n_writers > 1)
> > +                     APP_LOG(app, LOW,
> > +                                     "%s has more than one writer", p-
> > >name);
> > +
>
> We should remove the next two checks. The reason is that in the latest
> code in config_parse.c (already merged on master branch), we automatically
> add LINKx for every object associated with it , such as RXQx.y, TXQx.y,
> TMx. This is the same for KNI, as KNIx is associated with LINKx. As also
> commented below, we should implement this in config_parse.c. Basically, due
> to this change in config_parse.c, it is always guaranteed that for every
> KNIx object, the LINKx object exists as well, so no need to check this here
> in config_check.c or in init.c.
>
> > +             APP_CHECK(sscanf(p->name, "KNI%" PRIu32, &port_id) == 1,
> > +                               "%s's port id is invalid\n", p->name);
> > +
> > +             APP_CHECK(port_id < app->n_links,
> > +                               "kni %s is not associated with a valid
> link\n",
> > +                               p->name);
> > +     }
> > +}
> > +
> >  int
> >  app_config_check(struct app_params *app)
> >  {
> > @@ -439,6 +472,7 @@ app_config_check(struct app_params *app)
> >       check_sinks(app);
> >       check_msgqs(app);
> >       check_pipelines(app);
> > +     check_kni(app);
> >
> >       return 0;
> >  }
> > diff --git a/examples/ip_pipeline/config_parse.c
> > b/examples/ip_pipeline/config_parse.c
> > index e5efd03..e9cd5a4 100644
> > --- a/examples/ip_pipeline/config_parse.c
> > +++ b/examples/ip_pipeline/config_parse.c
> > @@ -209,6 +209,15 @@ struct app_pipeline_params
> > default_pipeline_params = {
> >       .n_args = 0,
> >  };
> >
> > +struct app_kni_params default_kni_params = {
> > +     .parsed = 0,
> > +     .socket_id = 0,
> > +     .core_id = 0,
> > +     .hyper_th_id = 0,
> > +     .mempool_id = 0,
> > +     .burst = 32,
>
> .burst_read = 32,
> .burst_write = 32,
>
> > +};
> > +
> >  static const char app_usage[] =
> >       "Usage: %s [-f CONFIG_FILE] [-s SCRIPT_FILE] [-p PORT_MASK] "
> >       "[-l LOG_LEVEL] [--preproc PREPROCESSOR] [--preproc-args
> > ARGS]\n"
> > @@ -1169,6 +1178,9 @@ parse_pipeline_pktq_in(struct app_params *app,
> >               } else if (validate_name(name, "SOURCE", 1) == 0) {
> >                       type = APP_PKTQ_IN_SOURCE;
> >                       id = APP_PARAM_ADD(app->source_params, name);
> > +             } else if (validate_name(name, "KNI", 1) == 0) {
> > +                     type = APP_PKTQ_IN_KNI;
> > +                     id = APP_PARAM_ADD(app->kni_params, name);
> >               } else
> >                       return -EINVAL;
> >
> > @@ -1240,6 +1252,9 @@ parse_pipeline_pktq_out(struct app_params *app,
> >               } else if (validate_name(name, "SINK", 1) == 0) {
> >                       type = APP_PKTQ_OUT_SINK;
> >                       id = APP_PARAM_ADD(app->sink_params, name);
> > +             } else if (validate_name(name, "KNI", 1) == 0) {
> > +                     type = APP_PKTQ_OUT_KNI;
> > +                     id = APP_PARAM_ADD(app->kni_params, name);
> >               } else
> >                       return -EINVAL;
> >
> > @@ -2459,6 +2474,76 @@ parse_msgq(struct app_params *app,
> >       free(entries);
> >  }
> >
>
> Please rework the below parse_kni() function based on the latest code
> (rebase). For example, the PARSER_PARAM_ADD_CHECK macro has been removed.
>
> Also, as mentioned above, please add LINKx automatically for KNIx, same as
> the latest code adds LINKx automatically every time RXQx.y, TXQx.y, TMx
> objects are met. This has to be done in several places: once here, in
> function parse_kni(), which is executed for KNI sections, but also in
> parse_pipeline_pktq_in() and parse_pipeline_pktq_out() functions (please
> check latest code).
>
> > +static void
> > +parse_kni(struct app_params *app,
> > +                const char *section_name,
> > +                struct rte_cfgfile *cfg)
> > +{
> > +     struct app_kni_params *param;
> > +     struct rte_cfgfile_entry *entries;
> > +     int n_entries, i;
> > +     ssize_t param_idx;
> > +
> > +     n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
> > +     PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0),
> > section_name);
> > +
> > +     entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
> > +     PARSE_ERROR_MALLOC(entries != NULL);
> > +
> > +     rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
> > +
> > +     param_idx = APP_PARAM_ADD(app->kni_params, section_name);
> > +     PARSER_PARAM_ADD_CHECK(param_idx, app->kni_params,
> > section_name);
> > +
> > +     param = &app->kni_params[param_idx];
> > +
> > +     for (i = 0; i < n_entries; i++) {
> > +             struct rte_cfgfile_entry *ent = &entries[i];
> > +
> > +             if (strcmp(ent->name, "core") == 0) {
> > +                     int status = parse_pipeline_core(
> > +                             &param->socket_id, &param->core_id,
> > +                             &param->hyper_th_id, ent->value);
> > +
> > +                     PARSE_ERROR((status == 0), section_name,
> > +                             ent->name);
> > +                     continue;
> > +             }
> > +
> > +             if (strcmp(ent->name, "mempool") == 0) {
> > +                     int status = validate_name(ent->value,
> > +                             "MEMPOOL", 1);
> > +                     ssize_t idx;
> > +
> > +                     PARSE_ERROR((status == 0), section_name,
> > +                             ent->name);
> > +                     idx = APP_PARAM_ADD(app->mempool_params,
> > +                             ent->value);
> > +                     PARSER_PARAM_ADD_CHECK(idx,
> > +                             app->mempool_params,
> > +                             section_name);
> > +                     param->mempool_id = idx;
> > +                     continue;
> > +             }
> > +
> > +             if (strcmp(ent->name, "burst") == 0) {
> > +                     int status = parser_read_uint32(&param->burst,
> > +                             ent->value);
> > +
> > +                     PARSE_ERROR((status == 0), section_name,
> > +                             ent->name);
> > +                     continue;
> > +             }
>
> As discussed above, we should parse two different entries in KNI section:
> burst_read and burst_write.
>
> > +
> > +             /* unrecognized */
> > +             PARSE_ERROR_INVALID(0, section_name, ent->name);
> > +     }
> > +
> > +     param->parsed = 1;
> > +
> > +     free(entries);
> > +}
> > +
> >  typedef void (*config_section_load)(struct app_params *p,
> >       const char *section_name,
> >       struct rte_cfgfile *cfg);
> > @@ -2483,6 +2568,7 @@ static const struct config_section
> cfg_file_scheme[]
> > = {
> >       {"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
> >       {"MSGQ-RSP-PIPELINE", 1, parse_msgq_rsp_pipeline},
> >       {"MSGQ", 1, parse_msgq},
> > +     {"KNI", 1, parse_kni},
> >  };
> >
> >  static void
> > @@ -2619,6 +2705,7 @@ app_config_parse(struct app_params *app, const
> > char *file_name)
> >       APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
> >       APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
> >       APP_PARAM_COUNT(app->pipeline_params, app->n_pipelines);
> > +     APP_PARAM_COUNT(app->kni_params, app->n_kni);
> >
> >  #ifdef RTE_PORT_PCAP
> >       for (i = 0; i < (int)app->n_pktq_source; i++) {
> > @@ -3025,6 +3112,9 @@ save_pipeline_params(struct app_params *app,
> > FILE *f)
> >                               case APP_PKTQ_IN_SOURCE:
> >                                       name = app->source_params[pp-
> > >id].name;
> >                                       break;
> > +                             case APP_PKTQ_IN_KNI:
> > +                                     name = app->kni_params[pp-
> > >id].name;
> > +                                     break;
> >                               default:
> >                                       APP_CHECK(0, "System error "
> >                                               "occurred while saving "
> > @@ -3059,6 +3149,9 @@ save_pipeline_params(struct app_params *app,
> > FILE *f)
> >                               case APP_PKTQ_OUT_SINK:
> >                                       name = app->sink_params[pp-
> > >id].name;
> >                                       break;
> > +                             case APP_PKTQ_OUT_KNI:
> > +                                     name = app->kni_params[pp-
> > >id].name;
> > +                                     break;
> >                               default:
> >                                       APP_CHECK(0, "System error "
> >                                               "occurred while saving "
> > @@ -3114,6 +3207,37 @@ save_pipeline_params(struct app_params *app,
> > FILE *f)
> >       }
> >  }
> >
> > +static void
> > +save_kni_params(struct app_params *app, FILE *f)
> > +{
> > +     struct app_kni_params *p;
> > +     size_t i, count;
> > +
> > +     count = RTE_DIM(app->kni_params);
> > +     for (i = 0; i < count; i++) {
> > +             p = &app->kni_params[i];
> > +             if (!APP_PARAM_VALID(p))
> > +                     continue;
> > +
> > +             /* section name */
> > +             fprintf(f, "[%s]\n", p->name);
> > +
> > +             /* core */
> > +             fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
> > +                             p->socket_id,
> > +                             p->core_id,
> > +                             (p->hyper_th_id) ? "h" : "");
> > +
> > +             /* mempool */
> > +             fprintf(f, "%s = %" PRIu32 "\n", "mempool_id", p-
> > >mempool_id);
>
> The name of the entry is "mempool" instead of "mempool_id". The value of
> the entry is app->mempool_params[p->mempool_id].name (which is a string)
> instead of p->mempool_id (which is a number).
>
> > +
> > +             /* burst */
> > +             fprintf(f, "%s = %" PRIu32 "\n", "burst", p->burst);
> > +
>
> We should save both p->burst_read and p->burst_write.
>
> > +             fputc('\n', f);
> > +     }
> > +}
> > +
> >  void
> >  app_config_save(struct app_params *app, const char *file_name)
> >  {
> > @@ -3144,6 +3268,7 @@ app_config_save(struct app_params *app, const
> > char *file_name)
> >       save_source_params(app, file);
> >       save_sink_params(app, file);
> >       save_msgq_params(app, file);
> > +     save_kni_params(app, file);
> >
> >       fclose(file);
> >       free(name);
> > @@ -3206,6 +3331,11 @@ app_config_init(struct app_params *app)
> >                       &default_pipeline_params,
> >                       sizeof(default_pipeline_params));
> >
> > +     for (i = 0; i < RTE_DIM(app->kni_params); i++)
> > +             memcpy(&app->kni_params[i],
> > +                     &default_kni_params,
> > +                     sizeof(default_kni_params));
> > +
> >       return 0;
> >  }
> >
> > diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
> > index 02351f6..8ff9118 100644
> > --- a/examples/ip_pipeline/init.c
> > +++ b/examples/ip_pipeline/init.c
>
> This is run-time code, let's have all the KNI code in init.c file enabled
> only when KNI library is part of the build:
> #ifdef RTE_LIBRTE_KNI
> ...
> #endif /* RTE_LIBRTE_KNI */
>
> > @@ -89,6 +89,24 @@ app_init_core_mask(struct app_params *app)
> >               mask |= 1LLU << lcore_id;
> >       }
> >
> > +     for (i = 0; i < app->n_kni; i++) {
> > +             struct app_kni_params *p = &app->kni_params[i];
> > +             int lcore_id;
> > +
> > +             lcore_id = cpu_core_map_get_lcore_id(app->core_map,
> > +                     p->socket_id,
> > +                     p->core_id,
> > +                     p->hyper_th_id);
> > +
> > +             if (lcore_id < 0)
> > +                     rte_panic("Cannot create CPU core mask\n");
> > +
> > +             if (mask & 1LLU << lcore_id)
>
> Please use parenthesis for improved readability: if (mask & (1LLU <<
> lcore_id)).
>
> > +                     rte_panic("KNI interface must use a dedicated
> > lcore\n");
>
> The bigger questions are:
> - Why do we need to dedicate separate CPU core(s) for KNI interface(s)?
> Isn't KNI code running in kernel space, why do we need to dedicate separate
> user space core for it and why do we need to add this core to the
> user-space application core mask?
> - Even if we need to add this to the core mask (maybe I am missing
> something here ...), why do we need to dedicate this core entirely to KNI?
> Can't we have KNI (kernel) code sharing this core with user-space
> application code (e.g. some pipeline instances?)


>
First of all, it is a bug to add KNI kernel core to the user space core
mask. What I want is just to check if the KNI kernel thread has a dedicated
core.
The reason I prefer to allocate a dedicated core to KNI kernel thread is
that my application is latency sensitive. I worry the context switch and
cache miss will cause the latency increasing if the KNI kernel thread and
application thread share one core.
Anyway, I think I should remove the hard coded check because it will be
more generic. Users who has the similar usage like mine can achieve so
through configuration file.


> > +
> > +             mask |= 1LLU << lcore_id;
> > +     }
> > +
> >       app->core_mask = mask;
> >       APP_LOG(app, HIGH, "CPU core mask = 0x%016" PRIx64, app-
> > >core_mask);
> >  }
> > @@ -1236,6 +1254,11 @@ static void app_pipeline_params_get(struct
> > app_params *app,
> >                                       n_bytes_per_pkt;
> >                       }
> >                       break;
> > +             case APP_PKTQ_IN_KNI:
> > +                     out->type = PIPELINE_PORT_IN_KNI;
> > +                     out->params.kni.kni = app->kni[in->id];
> > +                     out->burst_size = app->kni_params[in->id].burst;
> > +                     break;
> >               default:
> >                       break;
> >               }
> > @@ -1374,6 +1397,12 @@ static void app_pipeline_params_get(struct
> > app_params *app,
> >                               out->params.sink.max_n_pkts = 0;
> >                       }
> >                       break;
> > +             case APP_PKTQ_OUT_KNI:
> > +                     out->type = PIPELINE_PORT_OUT_KNI;
> > +                     out->params.kni.kni = app->kni[in->id];
> > +                     out->params.kni.tx_burst_sz =
> > +                                     app->kni_params[in->id].burst;
> > +                     break;
> >               default:
> >                       break;
> >               }
> > @@ -1397,6 +1426,55 @@ static void app_pipeline_params_get(struct
> > app_params *app,
> >  }
> >
> >  static void
> > +app_init_kni(struct app_params *app) {
> > +     uint32_t i;
> > +     struct rte_kni_conf conf;
> > +
>
> Avoid calling rte_kni_init() when there is no KNI device:
>
> if (app->n_kni == 0)
>         return;
>
> > +     rte_kni_init((unsigned int)app->n_kni);
> > +
> > +     for (i = 0; i < app->n_kni; i++) {
> > +             struct app_kni_params *p_kni = &app->kni_params[i];
> > +             uint32_t port_id;
>
> Please rename port_id with link_id, as this index is really the x from
> LINKx objects.
>
> > +             struct app_mempool_params *mempool_params;
> > +             struct rte_mempool *mempool;
> > +
> > +             if (sscanf(p_kni->name, "KNI%" PRIu32, &port_id) != 1)
> > +                     rte_panic("%s's port id is invalid\n",
> p_kni->name);
>
> Same comment as above: we do not need to check the x in KNIx, as LINKx is
> (should be, after you adjust the config_parse.c code) added automatically
> for KNIx.
>
> > +
> > +             mempool_params = &app->mempool_params[p_kni-
> > >mempool_id];
> > +             mempool = app->mempool[p_kni->mempool_id];
> > +
> > +             memset(&conf, 0, sizeof(conf));
> > +             snprintf(conf.name, RTE_KNI_NAMESIZE,
> > +                              "vEth%u", port_id);
> > +             conf.core_id = p_kni->core_id;
>
> The way the conf.core_id is set here is wrong, right?
>
>
> conf.core_id = cpu_core_map_get_lcore_id(app->core_map,
>         p->socket_id,
>         p->core_id,
>         p->hyper_th_id);
>
> > +             conf.force_bind = 1;
> > +
> > +             conf.group_id = (uint16_t) port_id;
> > +             conf.mbuf_size = mempool_params->buffer_size;
> > +
> > +             struct rte_kni_ops ops;
> > +             struct rte_eth_dev_info dev_info;
> > +
>
> Please move these definitions at the top of the for loop block rather than
> having them in the middle of the for loop block.
>
> > +             memset(&dev_info, 0, sizeof(dev_info));
> > +             rte_eth_dev_info_get(app->link_params[port_id].pmd_id,
> > +                                                      &dev_info);
> > +             conf.addr = dev_info.pci_dev->addr;
> > +             conf.id = dev_info.pci_dev->id;
> > +
> > +             memset(&ops, 0, sizeof(ops));
> > +             ops.port_id = app->link_params[port_id].pmd_id;
> > +             ops.change_mtu = kni_change_mtu;
> > +             ops.config_network_if = kni_config_network_interface;
> > +
> > +             app->kni[i] = rte_kni_alloc(mempool,
> > +                     &conf, &ops);
> > +             if (!app->kni[i])
> > +                     rte_panic("Fail to create kni for port: %d\n",
> port_id);
>
> rte_panic("Failed to create %s", p->name);
>
> This should print e.g. "Failed to create KNI5", which users should know it
> is the KNI associated with LINK5.
>
> > +     }
> > +}
> > +
> > +static void
> >  app_init_pipelines(struct app_params *app)
> >  {
> >       uint32_t p_id;
> > @@ -1531,6 +1609,7 @@ int app_init(struct app_params *app)
> >       app_init_swq(app);
> >       app_init_tm(app);
> >       app_init_msgq(app);
> > +     app_init_kni(app);
> >
> >       app_pipeline_common_cmd_push(app);
> >       app_pipeline_thread_cmd_push(app);
> > diff --git a/examples/ip_pipeline/kni/kni.c
> b/examples/ip_pipeline/kni/kni.c
> > new file mode 100644
> > index 0000000..c58e146
>
> Please move the two functions from kni/kni.c to init.c (just before
> app_init_kni() function) and make them static functions, then remove
> kni/kni.h and kni/kni.c files.
>
> > --- /dev/null
> > +++ b/examples/ip_pipeline/kni/kni.c
> > @@ -0,0 +1,80 @@
> > +#include <string.h>
> > +
> > +#include <rte_common.h>
> > +#include <rte_malloc.h>
> > +#include <rte_table_array.h>
> > +#include <rte_kni.h>
> > +#include <rte_ethdev.h>
> > +
> > +#include "rte_port_kni.h"
> > +#include "kni.h"
> > +
> > +int
> > +kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
> > +     int ret = 0;
> > +
> > +     if (port_id >= rte_eth_dev_count() || port_id >=
> > RTE_MAX_ETHPORTS) {
> > +             RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
> > +                             __func__, port_id);
> > +             return -EINVAL;
> > +     }
> > +
> > +     RTE_LOG(INFO, PORT, "%s: Configure network interface of %d
> > %s\n",
> > +                     __func__, port_id, if_up ? "up" : "down");
> > +
> > +     if (if_up != 0) { /* Configure network interface up */
> > +             rte_eth_dev_stop(port_id);
>
> Why do we need to stop the device first before we start it?
>
> > +             ret = rte_eth_dev_start(port_id);
> > +     } else /* Configure network interface down */
> > +             rte_eth_dev_stop(port_id);
>
> Do we need to call rte_eth_dev_start/stop() or do we need to call
> rte_eth_dev_up/down()?
>
> To be  honest, I made reference to the the KNI sample application.
Since there is very little docs tell the difference between device up call
and device start call, I am not sure which one is better here.
Any help will be appreciate. :-)


> > +
> > +     if (ret < 0)
> > +             RTE_LOG(ERR, PORT, "%s: Failed to start port %d\n",
> > +                             __func__, port_id);
> > +
>
> This is a callback function, I think we should completely remove any
> RTE_LOG calls from it, as link can go up and down quite frequently.
>
> > +     return ret;
> > +}
> > +
> > +int
> > +kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
> > +     int ret;
> > +
> > +     if (port_id >= rte_eth_dev_count()) {
> > +             RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
> > +                             __func__, port_id);
> > +             return -EINVAL;
> > +     }
> > +
> > +     if (new_mtu > ETHER_MAX_LEN) {
> > +             RTE_LOG(ERR, PORT,
> > +                             "%s: Fail to reconfigure port %d, the new
> > MTU is too big\n",
> > +                             __func__, port_id);
> > +             return -EINVAL;
> > +     }
> > +
> > +     RTE_LOG(INFO, PORT, "%s: Change MTU of port %d to %u\n",
> > +                     __func__, port_id,
> > +                     new_mtu);
> > +
> > +     /* Stop specific port */
> > +     rte_eth_dev_stop(port_id);
> > +
> > +     /* Set new MTU */
> > +     ret = rte_eth_dev_set_mtu(port_id, new_mtu);
> > +     if (ret < 0) {
> > +             RTE_LOG(ERR, PORT, "%s: Fail to reconfigure port %d\n",
> > +                             __func__, port_id);
> > +             return ret;
> > +     }
> > +
> > +     /* Restart specific port */
> > +     ret = rte_eth_dev_start(port_id);
> > +     if (ret < 0) {
> > +             RTE_LOG(ERR, PORT, "%s: Fail to restart port %d\n",
> > +                             __func__, port_id);
> > +             return ret;
> > +     }
> > +
> > +     return 0;
> > +}
>
> This is a callback function, I think we should completely remove all the
> RTE_LOG calls from it.
>
> > +
> > diff --git a/examples/ip_pipeline/kni/kni.h
> b/examples/ip_pipeline/kni/kni.h
> > new file mode 100644
> > index 0000000..04c8429
> > --- /dev/null
> > +++ b/examples/ip_pipeline/kni/kni.h
> > @@ -0,0 +1,16 @@
> > +#ifndef __INCLUDE_KNI_H__
> > +#define __INCLUDE_KNI_H__
> > +
> > +#include <rte_common.h>
> > +
> > +/* Total octets in ethernet header */
> > +#define KNI_ENET_HEADER_SIZE    14
>
> Are we actually using this macro? I would like to remove it if not needed.
>
> > +
> > +/* Total octets in the FCS */
> > +#define KNI_ENET_FCS_SIZE       4
>
> Are we actually using this macro? I would like to remove it if not needed.
>
> > +
> > +int kni_config_network_interface(uint8_t port_id, uint8_t if_up);
> > +
> > +int kni_change_mtu(uint8_t port_id, unsigned new_mtu);
> > +
> > +#endif
> > diff --git a/examples/ip_pipeline/pipeline_be.h
> > b/examples/ip_pipeline/pipeline_be.h
> > index f4ff262..23f0438 100644
> > --- a/examples/ip_pipeline/pipeline_be.h
> > +++ b/examples/ip_pipeline/pipeline_be.h
> > @@ -40,6 +40,7 @@
> >  #include <rte_port_ras.h>
> >  #include <rte_port_sched.h>
> >  #include <rte_port_source_sink.h>
> > +#include <rte_port_kni.h>
> >  #include <rte_pipeline.h>
> >
> >  enum pipeline_port_in_type {
> > @@ -50,6 +51,7 @@ enum pipeline_port_in_type {
> >       PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
> >       PIPELINE_PORT_IN_SCHED_READER,
> >       PIPELINE_PORT_IN_SOURCE,
> > +     PIPELINE_PORT_IN_KNI,
> >  };
> >
> >  struct pipeline_port_in_params {
> > @@ -62,6 +64,7 @@ struct pipeline_port_in_params {
> >               struct rte_port_ring_reader_ipv6_frag_params
> > ring_ipv6_frag;
> >               struct rte_port_sched_reader_params sched;
> >               struct rte_port_source_params source;
> > +             struct rte_port_kni_reader_params kni;
> >       } params;
> >       uint32_t burst_size;
> >  };
> > @@ -84,6 +87,8 @@ pipeline_port_in_params_convert(struct
> > pipeline_port_in_params  *p)
> >               return (void *) &p->params.sched;
> >       case PIPELINE_PORT_IN_SOURCE:
> >               return (void *) &p->params.source;
> > +     case PIPELINE_PORT_IN_KNI:
> > +             return (void *) &p->params.kni;
> >       default:
> >               return NULL;
> >       }
> > @@ -107,6 +112,8 @@ pipeline_port_in_params_get_ops(struct
> > pipeline_port_in_params  *p)
> >               return &rte_port_sched_reader_ops;
> >       case PIPELINE_PORT_IN_SOURCE:
> >               return &rte_port_source_ops;
> > +     case PIPELINE_PORT_IN_KNI:
> > +             return &rte_port_kni_reader_ops;
> >       default:
> >               return NULL;
> >       }
> > @@ -123,6 +130,7 @@ enum pipeline_port_out_type {
> >       PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
> >       PIPELINE_PORT_OUT_SCHED_WRITER,
> >       PIPELINE_PORT_OUT_SINK,
> > +     PIPELINE_PORT_OUT_KNI,
> >  };
> >
> >  struct pipeline_port_out_params {
> > @@ -138,6 +146,7 @@ struct pipeline_port_out_params {
> >               struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
> >               struct rte_port_sched_writer_params sched;
> >               struct rte_port_sink_params sink;
> > +             struct rte_port_kni_writer_params kni;
> >       } params;
> >  };
> >
> > @@ -165,6 +174,8 @@ pipeline_port_out_params_convert(struct
> > pipeline_port_out_params  *p)
> >               return (void *) &p->params.sched;
> >       case PIPELINE_PORT_OUT_SINK:
> >               return (void *) &p->params.sink;
> > +     case PIPELINE_PORT_OUT_KNI:
> > +             return (void *) &p->params.kni;
> >       default:
> >               return NULL;
> >       }
> > @@ -194,6 +205,8 @@ pipeline_port_out_params_get_ops(struct
> > pipeline_port_out_params  *p)
> >               return &rte_port_sched_writer_ops;
> >       case PIPELINE_PORT_OUT_SINK:
> >               return &rte_port_sink_ops;
> > +     case PIPELINE_PORT_OUT_KNI:
> > +             return &rte_port_kni_writer_ops;
> >       default:
> >               return NULL;
> >       }
> > diff --git a/examples/ip_pipeline/thread.c
> b/examples/ip_pipeline/thread.c
> > index a0f1f12..534864a 100644
> > --- a/examples/ip_pipeline/thread.c
> > +++ b/examples/ip_pipeline/thread.c
> > @@ -239,6 +239,15 @@ app_thread(void *arg)
> >       uint32_t core_id = rte_lcore_id(), i, j;
> >       struct app_thread_data *t = &app->thread_data[core_id];
> >
> > +     for (i = 0; i < app->n_kni; i++) {
> > +             if (core_id == (uint32_t)cpu_core_map_get_lcore_id(
> > +                     app->core_map,
> > +                     app->kni_params[i].socket_id,
> > +                     app->kni_params[i].core_id,
> > +                     app->kni_params[i].hyper_th_id))
> > +                     return 0;
> > +     }
> > +
>
> Same questions as above:
> - Why do we need to dedicate separate CPU core(s) for KNI interface(s)?
> Isn't KNI code running in kernel space, why do we need to dedicate separate
> user space core for it and why do we need to add this core to the
> user-space application core mask?
> - Even if we need to add this to the core mask (maybe I am missing
> something here ...), why do we need to dedicate this core entirely to KNI?
> Can't we have KNI (kernel) code sharing this core with user-space
> application code (e.g. some pipeline instances?)
>
> This is run-time code, let's have it enabled only when KNI library is part
> of the build:
> #ifdef RTE_LIBRTE_KNI
> ...
> #endif /* RTE_LIBRTE_KNI */
>
> >       for (i = 0; ; i++) {
> >               uint32_t n_regular = RTE_MIN(t->n_regular, RTE_DIM(t-
> > >regular));
> >               uint32_t n_custom = RTE_MIN(t->n_custom, RTE_DIM(t-
> > >custom));
> > diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
> > index d4de5af..f18253d 100644
> > --- a/lib/librte_port/Makefile
> > +++ b/lib/librte_port/Makefile
> > @@ -57,6 +57,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
> >  endif
> >  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
> > +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> > +SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
> > +endif
> >
> >  # install includes
> >  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port.h
> > @@ -68,6 +71,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=
> > rte_port_ras.h
> >  endif
> >  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
> >  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
> > +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> > +SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
> > +endif
> >
> >  # this lib depends upon:
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
> > @@ -75,5 +81,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=
> > lib/librte_mbuf
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
> > +DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
> >
> >  include $(RTE_SDK)/mk/rte.lib.mk
> > diff --git a/lib/librte_port/rte_port_kni.c
> b/lib/librte_port/rte_port_kni.c
> > new file mode 100644
> > index 0000000..8c5e404
> > --- /dev/null
> > +++ b/lib/librte_port/rte_port_kni.c
> > @@ -0,0 +1,316 @@
> > +/*-
> > + *   BSD LICENSE
> > + *
> > + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
>
> Please fix the year as 2016.
>
> > + *   All rights reserved.
> > + *
> > + *   Redistribution and use in source and binary forms, with or without
> > + *   modification, are permitted provided that the following conditions
> > + *   are met:
> > + *
> > + *     * Redistributions of source code must retain the above copyright
> > + *       notice, this list of conditions and the following disclaimer.
> > + *     * Redistributions in binary form must reproduce the above
> copyright
> > + *       notice, this list of conditions and the following disclaimer in
> > + *       the documentation and/or other materials provided with the
> > + *       distribution.
> > + *     * Neither the name of Intel Corporation nor the names of its
> > + *       contributors may be used to endorse or promote products derived
> > + *       from this software without specific prior written permission.
> > + *
> > + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> > CONTRIBUTORS
> > + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> > FITNESS FOR
> > + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> > COPYRIGHT
> > + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> > INCIDENTAL,
> > + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> > OF USE,
> > + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> > AND ON ANY
> > + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> > TORT
> > + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> > THE USE
> > + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> > DAMAGE.
> > + */
> > +#include <string.h>
> > +
> > +#include <rte_common.h>
> > +#include <rte_malloc.h>
> > +#include <rte_kni.h>
> > +
> > +#include "rte_port_kni.h"
> > +
> > +/*
> > + * Port KNI Reader
> > + */
> > +#ifdef RTE_PORT_STATS_COLLECT
> > +
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
> > +     {port->stats.n_pkts_in += val}
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
> > +     {port->stats.n_pkts_drop += val}
>
> This actually results in compiler error when built with
> RTE_PORT_STATS_COLLECT = ON.
>
> Please add semicolon and remove curly braces (as in e.g. rte_port_ring.c):
> #define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
>         port->stats.n_pkts_in += val;
> #define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
>         port->stats.n_pkts_drop += val;
>
> > +#else
> > +
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
> > +
> > +#endif
> > +
> > +struct rte_port_kni_reader {
> > +     struct rte_port_in_stats stats;
> > +
> > +     struct rte_kni *kni;
> > +};
> > +
> > +static void *
> > +rte_port_kni_reader_create(void *params, int socket_id) {
> > +     struct rte_port_kni_reader_params *conf =
> > +                     (struct rte_port_kni_reader_params *) params;
> > +     struct rte_port_kni_reader *port;
> > +
> > +     /* Check input parameters */
> > +     if (conf == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Memory allocation */
> > +     port = rte_zmalloc_socket("PORT", sizeof(*port),
> > +             RTE_CACHE_LINE_SIZE, socket_id);
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> > __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Initialization */
> > +     port->kni = conf->kni;
> > +
> > +     return port;
> > +}
> > +
> > +static int
> > +rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t
> > n_pkts) {
> > +     struct rte_port_kni_reader *p =
> > +                     (struct rte_port_kni_reader *) port;
> > +     uint16_t rx_pkt_cnt;
> > +
> > +     rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
> > +     RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
> > +     return rx_pkt_cnt;
> > +}
> > +
> > +static int
> > +rte_port_kni_reader_free(void *port) {
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
> > +             return -EINVAL;
> > +     }
> > +
> > +     rte_free(port);
> > +
> > +     return 0;
> > +}
> > +
> > +static int rte_port_kni_reader_stats_read(void *port,
> > +     struct rte_port_in_stats *stats, int clear)
> > +{
> > +     struct rte_port_kni_reader *p =
> > +                     (struct rte_port_kni_reader *) port;
> > +
> > +     if (stats != NULL)
> > +             memcpy(stats, &p->stats, sizeof(p->stats));
> > +
> > +     if (clear)
> > +             memset(&p->stats, 0, sizeof(p->stats));
> > +
> > +     return 0;
> > +}
> > +
> > +/*
> > + * Port KNI Writer
> > + */
> > +#ifdef RTE_PORT_STATS_COLLECT
> > +
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
> > +     {port->stats.n_pkts_in += val}
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
> > +     {port->stats.n_pkts_drop += val}
> > +
>
> This actually results in compiler error when built with
> RTE_PORT_STATS_COLLECT = ON.
>
> Please add semicolon and remove curly braces (as in e.g. rte_port_ring.c).
>
> Actually I implements the macro similar
to RTE_PORT_RING_READER_STATS_PKTS_IN_ADD first. But the
scripts/checkpatches.sh fails: ERROR:COMPLEX_MACRO: Macros with complex
values should be enclosed in parentheses
I'm not share either I have done something wrong or the checkpatches script
need an update.


> > +#else
> > +
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
> > +
> > +#endif
> > +
> > +struct rte_port_kni_writer {
> > +     struct rte_port_out_stats stats;
> > +
> > +     struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
> > +     uint32_t tx_burst_sz;
> > +     uint16_t tx_buf_count;
>
> Can we actually make tx_buf_count to be uint32_t rather than uint16_t? I
> see some computation between tx_buf_count and some other variable of type
> uint32_t later in the code ...
>
> > +     uint64_t bsz_mask;
> > +     struct rte_kni *kni;
> > +};
> > +
> > +static void *
> > +rte_port_kni_writer_create(void *params, int socket_id) {
> > +     struct rte_port_kni_writer_params *conf =
> > +                     (struct rte_port_kni_writer_params *) params;
> > +     struct rte_port_kni_writer *port;
> > +
> > +     /* Check input parameters */
> > +     if ((conf == NULL) ||
> > +             (conf->tx_burst_sz == 0) ||
> > +             (conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
> > +             (!rte_is_power_of_2(conf->tx_burst_sz))) {
> > +             RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n",
> > __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Memory allocation */
> > +     port = rte_zmalloc_socket("PORT", sizeof(*port),
> > +             RTE_CACHE_LINE_SIZE, socket_id);
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> > __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Initialization */
> > +     port->kni = conf->kni;
> > +     port->tx_burst_sz = conf->tx_burst_sz;
> > +     port->tx_buf_count = 0;
> > +     port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
> > +
> > +     return port;
> > +}
> > +
> > +static inline void
> > +send_burst(struct rte_port_kni_writer *p) {
> > +     uint32_t nb_tx;
> > +
> > +     nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
> > +     rte_kni_handle_request(p->kni);
> > +
> > +     RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p-
> > >tx_buf_count - nb_tx);
> > +     for (; nb_tx < p->tx_buf_count; nb_tx++)
> > +             rte_pktmbuf_free(p->tx_buf[nb_tx]);
> > +
> > +     p->tx_buf_count = 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt) {
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +
> > +     p->tx_buf[p->tx_buf_count++] = pkt;
> > +     RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> > +     if (p->tx_buf_count >= p->tx_burst_sz)
> > +             send_burst(p);
> > +
> > +     return 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_tx_bulk(void *port,
> > +                                                     struct rte_mbuf
> > **pkts,
> > +                                                     uint64_t
> pkts_mask) {
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +     uint64_t bsz_mask = p->bsz_mask;
> > +     uint32_t tx_buf_count = p->tx_buf_count;
> > +     uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
> > +                                     ((pkts_mask & bsz_mask) ^
> > bsz_mask);
> > +
> > +     if (expr == 0) {
> > +             uint64_t n_pkts = __builtin_popcountll(pkts_mask);
> > +             uint32_t n_pkts_ok;
> > +
> > +             if (tx_buf_count)
> > +                     send_burst(p);
> > +
> > +             RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
> > +             n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
> > +
> > +             RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts
> > - n_pkts_ok);
> > +             for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
> > +                     struct rte_mbuf *pkt = pkts[n_pkts_ok];
> > +
> > +                     rte_pktmbuf_free(pkt);
> > +             }
> > +     } else {
> > +             for (; pkts_mask;) {
> > +                     uint32_t pkt_index = __builtin_ctzll(pkts_mask);
> > +                     uint64_t pkt_mask = 1LLU << pkt_index;
> > +                     struct rte_mbuf *pkt = pkts[pkt_index];
> > +
> > +                     p->tx_buf[tx_buf_count++] = pkt;
> > +                     RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> > +                     pkts_mask &= ~pkt_mask;
> > +             }
> > +
> > +             p->tx_buf_count = tx_buf_count;
> > +             if (tx_buf_count >= p->tx_burst_sz)
> > +                     send_burst(p);
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_flush(void *port) {
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +
> > +     if (p->tx_buf_count > 0)
> > +             send_burst(p);
> > +
> > +     return 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_free(void *port) {
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
> > +             return -EINVAL;
> > +     }
> > +
> > +     rte_port_kni_writer_flush(port);
> > +     rte_free(port);
> > +
> > +     return 0;
> > +}
> > +
> > +static int rte_port_kni_writer_stats_read(void *port,
> > +     struct rte_port_out_stats *stats, int clear)
> > +{
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +
> > +     if (stats != NULL)
> > +             memcpy(stats, &p->stats, sizeof(p->stats));
> > +
> > +     if (clear)
> > +             memset(&p->stats, 0, sizeof(p->stats));
> > +
> > +     return 0;
> > +}
> > +
> > +/*
> > + * Summary of port operations
> > + */
> > +struct rte_port_in_ops rte_port_kni_reader_ops = {
> > +     .f_create = rte_port_kni_reader_create,
> > +     .f_free = rte_port_kni_reader_free,
> > +     .f_rx = rte_port_kni_reader_rx,
> > +     .f_stats = rte_port_kni_reader_stats_read,
> > +};
> > +
> > +struct rte_port_out_ops rte_port_kni_writer_ops = {
> > +     .f_create = rte_port_kni_writer_create,
> > +     .f_free = rte_port_kni_writer_free,
> > +     .f_tx = rte_port_kni_writer_tx,
> > +     .f_tx_bulk = rte_port_kni_writer_tx_bulk,
> > +     .f_flush = rte_port_kni_writer_flush,
> > +     .f_stats = rte_port_kni_writer_stats_read,
> > +};
>
> Do we need a KNI writer no-drop version as well? Would it be useful?
>
> > diff --git a/lib/librte_port/rte_port_kni.h
> b/lib/librte_port/rte_port_kni.h
> > new file mode 100644
> > index 0000000..7623798
> > --- /dev/null
> > +++ b/lib/librte_port/rte_port_kni.h
> > @@ -0,0 +1,81 @@
> > +/*-
> > + *   BSD LICENSE
> > + *
> > + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
>
> Please fix the year to 2016.
>
> > + *   All rights reserved.
> > + *
> > + *   Redistribution and use in source and binary forms, with or without
> > + *   modification, are permitted provided that the following conditions
> > + *   are met:
> > + *
> > + *     * Redistributions of source code must retain the above copyright
> > + *       notice, this list of conditions and the following disclaimer.
> > + *     * Redistributions in binary form must reproduce the above
> copyright
> > + *       notice, this list of conditions and the following disclaimer in
> > + *       the documentation and/or other materials provided with the
> > + *       distribution.
> > + *     * Neither the name of Intel Corporation nor the names of its
> > + *       contributors may be used to endorse or promote products derived
> > + *       from this software without specific prior written permission.
> > + *
> > + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> > CONTRIBUTORS
> > + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> > FITNESS FOR
> > + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> > COPYRIGHT
> > + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> > INCIDENTAL,
> > + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> > OF USE,
> > + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> > AND ON ANY
> > + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> > TORT
> > + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> > THE USE
> > + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> > DAMAGE.
> > + */
> > +
> > +#ifndef __INCLUDE_RTE_PORT_KNI_H__
> > +#define __INCLUDE_RTE_PORT_KNI_H__
> > +
> > +#ifdef __cplusplus
> > +extern "C" {
> > +#endif
> > +
> > +/**
> > + * @file
> > + * RTE Port KNI Interface
> > + *
> > + * kni_reader: input port built on top of pre-initialized KNI interface
> > + * kni_writer: output port built on top of pre-initialized KNI interface
> > + *
> > + ***/
> > +
> > +#include <stdint.h>
> > +
> > +#include <rte_kni.h>
> > +
> > +#include "rte_port.h"
> > +
> > +/** kni_reader port parameters */
> > +struct rte_port_kni_reader_params {
> > +     /** KNI interface reference */
> > +     struct rte_kni *kni;
> > +};
> > +
> > +/** kni_reader port operations */
> > +extern struct rte_port_in_ops rte_port_kni_reader_ops;
> > +
> > +
> > +/** kni_writer port parameters */
> > +struct rte_port_kni_writer_params {
> > +     /** KNI interface reference */
> > +     struct rte_kni *kni;
> > +     /** Burst size to KNI interface. */
> > +     uint32_t tx_burst_sz;
> > +};
> > +
> > +/** kni_writer port operations */
> > +extern struct rte_port_out_ops rte_port_kni_writer_ops;
> > +
> > +#ifdef __cplusplus
> > +}
> > +#endif
> > +
> > +#endif
> > --
> > 2.7.4
>
> Thank you!
>
> Regards,
> Cristian
>
>

B.R.

Ethan

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

* Re: [PATCH] port: add kni interface support
  2016-06-13 10:47     ` Ethan
@ 2016-06-13 13:18       ` Dumitrescu, Cristian
  2016-06-16 11:34         ` Ethan
  0 siblings, 1 reply; 22+ messages in thread
From: Dumitrescu, Cristian @ 2016-06-13 13:18 UTC (permalink / raw)
  To: Ethan; +Cc: dev, Singh, Jasvinder, Yigit, Ferruh

Hi Ethan,

Great, we’ll wait for your patch later this week then. I recommend you add any other changes that you might have on top of the latest code that I just send, as this will minimize your work, my work to further code reviews and number of future iterations to merge this patch.

Answers to your questions are inlined below.

Regards,
Cristian

From: zhuangweijie@gmail.com [mailto:zhuangweijie@gmail.com] On Behalf Of Ethan
Sent: Monday, June 13, 2016 11:48 AM
To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>
Cc: dev@dpdk.org; Singh, Jasvinder <jasvinder.singh@intel.com>; Yigit, Ferruh <ferruh.yigit@intel.com>
Subject: Re: [PATCH] port: add kni interface support

Hi Cristian,

I've got your comments. Thank you for review the code from a DPDK newbie. :-)
I plan to submit a new patch to fix all during this week hopefully.

There are four places I'd like to discuss further:

1. Dedicated lcore for kni kernel thread
First of all, it is a bug to add kni kernel core to the user space core mask. What I want is just to check if the kni kernel thread has a dedicated core.
The reason I prefer to allocate a dedicated core to kni kernel thread is that my application is latency sensitive. I worry the context switch and cache miss will cause the latency increasing if the kni kernel thread and application thread share one core.
Anyway, I think I should remove the hard coded check because it will be more generic. Users who has the similar usage like mine can achieve so through configuration file.

[Cristian] I agree with you that the user should be able to specify the core where the kernel thread should run, and this requirement is fully met by the latest code I sent, but implemented in a slightly different way, which I think it is a cleaner way.

In your initial solution, the application redefines the meaning of the core mask as the reunion of cores used by the user space application (cores running the pipelines) and the cores used to run the kernel space KNI threads. This does not make sense to me. The application is in user space and it does not start or manage any kernel threads itself, why should the application worry about the cores running kernel threads? The application should just pick up the user instructions from the config file and send them to the KNI kernel module transparently.

In the code that I just sent, the application preserves the current definition of the core mask, i.e. just the collection of cores running the pipelines. This leads to simpler code that meets all the requirements for kernel threads affinity:
i) The user wants to affinitize the kernel thread to a CPU core that is not used to run any pipeline (this core will run just KNI kernel threads): Core entry in KNI section is set to be different than the core entry of any PIPELINE section in the config file;
ii) The user affinitizes the kernel thread to a CPU core that also runs some of the pipelines (this core will run both user space and kernel space threads): Core entry in KNI section is equal to the core entry in one or several of the PIPELINE sections in the config file;
iii) The user does not affinitize the kernel thread to any CPU core, so the kernel decides the scheduling policy for the KNI threads: Core entry of the KNI section is not present; this results in force_bind KNI parameter to be set to 0.

Makes sense?

2. The compiler error of the Macro RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD
Actually I implements the macro similar to RTE_PORT_RING_READER_STATS_PKTS_IN_ADD first. But the scripts/checkpatches.sh fails: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parentheses
I'm not share either I have done something wrong or the checkpatches script need an update.

[Cristian] Let’s use the same consistent rule to create the stats macros for all the ports, i.e. follow the existing rule used for other ports. You can ignore this check patch issue.

3. KNI kernel operations callback
To be  honest, I made reference to the the KNI sample application.
Since there is very little docs tell the difference between link up call and device start call, I am not sure which one is better here.
Any help will be appreciate. :-)

[Cristian] I suggest you use the ones from the code that I just sent.

4. Shall I use DPDK_16.07 in the  librte_port/rte_port_version.map file?

[Cristian] Yes.


2016-06-10 7:42 GMT+08:00 Dumitrescu, Cristian <cristian.dumitrescu@intel.com<mailto:cristian.dumitrescu@intel.com>>:
Hi Ethan,

Great work! There are still several comments below that need to be addressed, but I am confident we can close on them quickly. Thank you!

Please rebase the next version on top of the latest code on master branch.

Please also update librte_port/rte_port_version.map file.

Shall I use DPDK_16.07 in the  librte_port/rte_port_version.map file?


> -----Original Message-----
> From: WeiJie Zhuang [mailto:zhuangwj@gmail.com<mailto:zhuangwj@gmail.com>]
> Sent: Saturday, May 28, 2016 12:26 PM
> To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com<mailto:cristian.dumitrescu@intel.com>>
> Cc: dev@dpdk.org<mailto:dev@dpdk.org>; WeiJie Zhuang <zhuangwj@gmail.com<mailto:zhuangwj@gmail.com>>
> Subject: [PATCH] port: add kni interface support
>
> 1. add KNI port type to the packet framework
> 2. add KNI support to the IP Pipeline sample Application
>
> Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com<mailto:zhuangwj@gmail.com>>
> ---
> v2:
> * Fix check patch error.
> ---
>  doc/api/doxy-api-index.md<http://doxy-api-index.md>           |   1 +
>  examples/ip_pipeline/Makefile       |   6 +-
>  examples/ip_pipeline/app.h          |  74 +++++++++
>  examples/ip_pipeline/config/kni.cfg |  12 ++
>  examples/ip_pipeline/config_check.c |  34 ++++
>  examples/ip_pipeline/config_parse.c | 130 +++++++++++++++
>  examples/ip_pipeline/init.c         |  79 +++++++++
>  examples/ip_pipeline/kni/kni.c      |  80 +++++++++
>  examples/ip_pipeline/kni/kni.h      |  16 ++
>  examples/ip_pipeline/pipeline_be.h  |  13 ++
>  examples/ip_pipeline/thread.c       |   9 +
>  lib/librte_port/Makefile            |   7 +
>  lib/librte_port/rte_port_kni.c      | 316
> ++++++++++++++++++++++++++++++++++++
>  lib/librte_port/rte_port_kni.h      |  81 +++++++++
>  14 files changed, 856 insertions(+), 2 deletions(-)
>  create mode 100644 examples/ip_pipeline/config/kni.cfg
>  create mode 100644 examples/ip_pipeline/kni/kni.c
>  create mode 100644 examples/ip_pipeline/kni/kni.h
>  create mode 100644 lib/librte_port/rte_port_kni.c
>  create mode 100644 lib/librte_port/rte_port_kni.h
>
> diff --git a/doc/api/doxy-api-index.md<http://doxy-api-index.md> b/doc/api/doxy-api-index.md<http://doxy-api-index.md>
> index f626386..e38a959 100644
> --- a/doc/api/doxy-api-index.md<http://doxy-api-index.md>
> +++ b/doc/api/doxy-api-index.md<http://doxy-api-index.md>
> @@ -119,6 +119,7 @@ There are many libraries, so their headers may be
> grouped by topics:
>      [reass]            (@ref rte_port_ras.h),
>      [sched]            (@ref rte_port_sched.h),
>      [src/sink]         (@ref rte_port_source_sink.h)
> +    [kni]              (@ref rte_port_kni.h)
>    * [table]            (@ref rte_table.h):
>      [lpm IPv4]         (@ref rte_table_lpm.h),
>      [lpm IPv6]         (@ref rte_table_lpm_ipv6.h),
> diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
> index 10fe1ba..848c2aa 100644
> --- a/examples/ip_pipeline/Makefile
> +++ b/examples/ip_pipeline/Makefile
> @@ -43,9 +43,10 @@ include $(RTE_SDK)/mk/rte.vars.mk<http://rte.vars.mk>
>  # binary name
>  APP = ip_pipeline
>
> +VPATH += $(SRCDIR)/kni
>  VPATH += $(SRCDIR)/pipeline
>
> -INC += $(wildcard *.h) $(wildcard pipeline/*.h)
> +INC += $(wildcard *.h) $(wildcard pipeline/*.h) $(wildcard kni/*.h)
>
>  # all source are stored in SRCS-y
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) := main.c
> @@ -56,6 +57,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread_fe.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
> +SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += kni.c
>
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_be.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_fe.c
> @@ -72,7 +74,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) +=
> pipeline_flow_actions.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing_be.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing.c
>
> -CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline
> +CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline -I$(SRCDIR)/kni
>  CFLAGS += -O3
>  CFLAGS += $(WERROR_FLAGS) -Wno-error=unused-function -Wno-
> error=unused-variable
>
I would like to avoid creating the kni subfolder. Please move the functions from kni/kni.c to init.c file, just before function app_init_kni(), where they should be declared as static functions.

> diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
> index e775024..a86ce57 100644
> --- a/examples/ip_pipeline/app.h
> +++ b/examples/ip_pipeline/app.h
> @@ -44,7 +44,9 @@
>  #include <cmdline_parse.h>
>
>  #include <rte_ethdev.h>
> +#include <rte_kni.h>
>
> +#include "kni.h"
>  #include "cpu_core_map.h"
>  #include "pipeline.h"
>
> @@ -99,6 +101,18 @@ struct app_pktq_hwq_out_params {
>       struct rte_eth_txconf conf;
>  };
>
> +struct app_kni_params {
> +     char *name;
> +     uint32_t parsed;
> +
> +     uint32_t socket_id;
> +     uint32_t core_id;
> +     uint32_t hyper_th_id;
> +
> +     uint32_t mempool_id;

Please add the usual comment on the same line: /* Position in the app->mempool_params */

> +     uint32_t burst;

Why having a unified value for read and write burst size? Please use burst_read and burst_write (both uint32_t) instead and update the config_parse.c and init.c code accordingly (small changes).

> +};
> +
>  struct app_pktq_swq_params {
>       char *name;
>       uint32_t parsed;
> @@ -172,6 +186,7 @@ enum app_pktq_in_type {
>       APP_PKTQ_IN_SWQ,
>       APP_PKTQ_IN_TM,
>       APP_PKTQ_IN_SOURCE,
> +     APP_PKTQ_IN_KNI,
>  };
>
>  struct app_pktq_in_params {
> @@ -184,6 +199,7 @@ enum app_pktq_out_type {
>       APP_PKTQ_OUT_SWQ,
>       APP_PKTQ_OUT_TM,
>       APP_PKTQ_OUT_SINK,
> +     APP_PKTQ_OUT_KNI,
>  };
>
>  struct app_pktq_out_params {
> @@ -434,6 +450,10 @@ struct app_eal_params {
>  #define APP_THREAD_HEADROOM_STATS_COLLECT        1
>  #endif
>
> +#ifndef APP_MAX_KNI
> +#define APP_MAX_KNI                              8
> +#endif
> +
>  struct app_params {
>       /* Config */
>       char app_name[APP_APPNAME_SIZE];
> @@ -457,6 +477,7 @@ struct app_params {
>       struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
>       struct app_msgq_params msgq_params[APP_MAX_MSGQ];
>       struct app_pipeline_params pipeline_params[APP_MAX_PIPELINES];
> +     struct app_kni_params kni_params[APP_MAX_KNI];
>
>       uint32_t n_mempools;
>       uint32_t n_links;
> @@ -468,6 +489,7 @@ struct app_params {
>       uint32_t n_pktq_sink;
>       uint32_t n_msgq;
>       uint32_t n_pipelines;
> +     uint32_t n_kni;
>
>       /* Init */
>       char *eal_argv[1 + APP_EAL_ARGC];
> @@ -480,6 +502,7 @@ struct app_params {
>       struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
>       struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
>       struct app_thread_data thread_data[APP_MAX_THREADS];
> +     struct rte_kni *kni[APP_MAX_KNI];
>       cmdline_parse_ctx_t cmds[APP_MAX_CMDS + 1];
>
>       int eal_argc;
> @@ -738,6 +761,31 @@ app_msgq_get_readers(struct app_params *app,
> struct app_msgq_params *msgq)
>  }
>
>  static inline uint32_t
> +app_kni_get_readers(struct app_params *app, struct app_kni_params
> *kni)
> +{
> +     uint32_t pos = kni - app->kni_params;
> +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +             RTE_DIM(app->pipeline_params));
> +     uint32_t n_readers = 0, i;
> +
> +     for (i = 0; i < n_pipelines; i++) {
> +             struct app_pipeline_params *p = &app->pipeline_params[i];
> +             uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p-
> >pktq_in));
> +             uint32_t j;
> +
> +             for (j = 0; j < n_pktq_in; j++) {
> +                     struct app_pktq_in_params *pktq = &p->pktq_in[j];
> +
> +                     if ((pktq->type == APP_PKTQ_IN_KNI) &&
> +                             (pktq->id == pos))
> +                             n_readers++;
> +             }
> +     }
> +
> +     return n_readers;
> +}
> +
> +static inline uint32_t
>  app_txq_get_writers(struct app_params *app, struct
> app_pktq_hwq_out_params *txq)
>  {
>       uint32_t pos = txq - app->hwq_out_params;
> @@ -863,6 +911,32 @@ app_msgq_get_writers(struct app_params *app,
> struct app_msgq_params *msgq)
>       return n_writers;
>  }
>
> +static inline uint32_t
> +app_kni_get_writers(struct app_params *app, struct app_kni_params *kni)
> +{
> +     uint32_t pos = kni - app->kni_params;
> +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +             RTE_DIM(app->pipeline_params));
> +     uint32_t n_writers = 0, i;
> +
> +     for (i = 0; i < n_pipelines; i++) {
> +             struct app_pipeline_params *p = &app->pipeline_params[i];
> +             uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
> +                     RTE_DIM(p->pktq_out));
> +             uint32_t j;
> +
> +             for (j = 0; j < n_pktq_out; j++) {
> +                     struct app_pktq_out_params *pktq = &p-
> >pktq_out[j];
> +
> +                     if ((pktq->type == APP_PKTQ_OUT_KNI) &&
> +                             (pktq->id == pos))
> +                             n_writers++;
> +             }
> +     }
> +
> +     return n_writers;
> +}
> +
>  static inline struct app_link_params *
>  app_get_link_for_rxq(struct app_params *app, struct
> app_pktq_hwq_in_params *p)
>  {
> diff --git a/examples/ip_pipeline/config/kni.cfg
> b/examples/ip_pipeline/config/kni.cfg
> new file mode 100644
> index 0000000..30466b0
> --- /dev/null
> +++ b/examples/ip_pipeline/config/kni.cfg
> @@ -0,0 +1,12 @@
> +[KNI0]
> +core = 2
> +
> +[PIPELINE0]
> +type = MASTER
> +core = 0
> +
> +[PIPELINE1]
> +type = PASS-THROUGH
> +core = 1
> +pktq_in = RXQ0.0 KNI0
> +pktq_out = KNI0 TXQ0.0
I don't think this new config file config/kni.cfg is really useful, so I would like to remove it. We should avoid the proliferation of straightforward config files.

> diff --git a/examples/ip_pipeline/config_check.c
> b/examples/ip_pipeline/config_check.c
> index fd9ff49..3e300f9 100644
> --- a/examples/ip_pipeline/config_check.c
> +++ b/examples/ip_pipeline/config_check.c
> @@ -426,6 +426,39 @@ check_pipelines(struct app_params *app)
>       }
>  }
>
> +static void
> +check_kni(struct app_params *app) {
> +     uint32_t i;
> +     uint32_t port_id;
> +
> +     for (i = 0; i < app->n_kni; i++) {
> +             struct app_kni_params *p = &app->kni_params[i];
> +             uint32_t n_readers = app_kni_get_readers(app, p);
> +             uint32_t n_writers = app_kni_get_writers(app, p);
> +
> +             APP_CHECK((n_readers != 0),
> +                               "%s has no reader\n", p->name);
> +
> +             if (n_readers > 1)
> +                     APP_LOG(app, LOW,
> +                                     "%s has more than one reader", p-
> >name);
> +
> +             APP_CHECK((n_writers != 0),
> +                               "%s has no writer\n", p->name);
> +
> +             if (n_writers > 1)
> +                     APP_LOG(app, LOW,
> +                                     "%s has more than one writer", p-
> >name);
> +
We should remove the next two checks. The reason is that in the latest code in config_parse.c (already merged on master branch), we automatically add LINKx for every object associated with it , such as RXQx.y, TXQx.y, TMx. This is the same for KNI, as KNIx is associated with LINKx. As also commented below, we should implement this in config_parse.c. Basically, due to this change in config_parse.c, it is always guaranteed that for every KNIx object, the LINKx object exists as well, so no need to check this here in config_check.c or in init.c.

> +             APP_CHECK(sscanf(p->name, "KNI%" PRIu32, &port_id) == 1,
> +                               "%s's port id is invalid\n", p->name);
> +
> +             APP_CHECK(port_id < app->n_links,
> +                               "kni %s is not associated with a valid link\n",
> +                               p->name);
> +     }
> +}
> +
>  int
>  app_config_check(struct app_params *app)
>  {
> @@ -439,6 +472,7 @@ app_config_check(struct app_params *app)
>       check_sinks(app);
>       check_msgqs(app);
>       check_pipelines(app);
> +     check_kni(app);
>
>       return 0;
>  }
> diff --git a/examples/ip_pipeline/config_parse.c
> b/examples/ip_pipeline/config_parse.c
> index e5efd03..e9cd5a4 100644
> --- a/examples/ip_pipeline/config_parse.c
> +++ b/examples/ip_pipeline/config_parse.c
> @@ -209,6 +209,15 @@ struct app_pipeline_params
> default_pipeline_params = {
>       .n_args = 0,
>  };
>
> +struct app_kni_params default_kni_params = {
> +     .parsed = 0,
> +     .socket_id = 0,
> +     .core_id = 0,
> +     .hyper_th_id = 0,
> +     .mempool_id = 0,
> +     .burst = 32,
.burst_read = 32,
.burst_write = 32,

> +};
> +
>  static const char app_usage[] =
>       "Usage: %s [-f CONFIG_FILE] [-s SCRIPT_FILE] [-p PORT_MASK] "
>       "[-l LOG_LEVEL] [--preproc PREPROCESSOR] [--preproc-args
> ARGS]\n"
> @@ -1169,6 +1178,9 @@ parse_pipeline_pktq_in(struct app_params *app,
>               } else if (validate_name(name, "SOURCE", 1) == 0) {
>                       type = APP_PKTQ_IN_SOURCE;
>                       id = APP_PARAM_ADD(app->source_params, name);
> +             } else if (validate_name(name, "KNI", 1) == 0) {
> +                     type = APP_PKTQ_IN_KNI;
> +                     id = APP_PARAM_ADD(app->kni_params, name);
>               } else
>                       return -EINVAL;
>
> @@ -1240,6 +1252,9 @@ parse_pipeline_pktq_out(struct app_params *app,
>               } else if (validate_name(name, "SINK", 1) == 0) {
>                       type = APP_PKTQ_OUT_SINK;
>                       id = APP_PARAM_ADD(app->sink_params, name);
> +             } else if (validate_name(name, "KNI", 1) == 0) {
> +                     type = APP_PKTQ_OUT_KNI;
> +                     id = APP_PARAM_ADD(app->kni_params, name);
>               } else
>                       return -EINVAL;
>
> @@ -2459,6 +2474,76 @@ parse_msgq(struct app_params *app,
>       free(entries);
>  }
>
Please rework the below parse_kni() function based on the latest code (rebase). For example, the PARSER_PARAM_ADD_CHECK macro has been removed.

Also, as mentioned above, please add LINKx automatically for KNIx, same as the latest code adds LINKx automatically every time RXQx.y, TXQx.y, TMx objects are met. This has to be done in several places: once here, in function parse_kni(), which is executed for KNI sections, but also in parse_pipeline_pktq_in() and parse_pipeline_pktq_out() functions (please check latest code).

> +static void
> +parse_kni(struct app_params *app,
> +                const char *section_name,
> +                struct rte_cfgfile *cfg)
> +{
> +     struct app_kni_params *param;
> +     struct rte_cfgfile_entry *entries;
> +     int n_entries, i;
> +     ssize_t param_idx;
> +
> +     n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
> +     PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0),
> section_name);
> +
> +     entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
> +     PARSE_ERROR_MALLOC(entries != NULL);
> +
> +     rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
> +
> +     param_idx = APP_PARAM_ADD(app->kni_params, section_name);
> +     PARSER_PARAM_ADD_CHECK(param_idx, app->kni_params,
> section_name);
> +
> +     param = &app->kni_params[param_idx];
> +
> +     for (i = 0; i < n_entries; i++) {
> +             struct rte_cfgfile_entry *ent = &entries[i];
> +
> +             if (strcmp(ent->name, "core") == 0) {
> +                     int status = parse_pipeline_core(
> +                             &param->socket_id, &param->core_id,
> +                             &param->hyper_th_id, ent->value);
> +
> +                     PARSE_ERROR((status == 0), section_name,
> +                             ent->name);
> +                     continue;
> +             }
> +
> +             if (strcmp(ent->name, "mempool") == 0) {
> +                     int status = validate_name(ent->value,
> +                             "MEMPOOL", 1);
> +                     ssize_t idx;
> +
> +                     PARSE_ERROR((status == 0), section_name,
> +                             ent->name);
> +                     idx = APP_PARAM_ADD(app->mempool_params,
> +                             ent->value);
> +                     PARSER_PARAM_ADD_CHECK(idx,
> +                             app->mempool_params,
> +                             section_name);
> +                     param->mempool_id = idx;
> +                     continue;
> +             }
> +
> +             if (strcmp(ent->name, "burst") == 0) {
> +                     int status = parser_read_uint32(&param->burst,
> +                             ent->value);
> +
> +                     PARSE_ERROR((status == 0), section_name,
> +                             ent->name);
> +                     continue;
> +             }
As discussed above, we should parse two different entries in KNI section: burst_read and burst_write.

> +
> +             /* unrecognized */
> +             PARSE_ERROR_INVALID(0, section_name, ent->name);
> +     }
> +
> +     param->parsed = 1;
> +
> +     free(entries);
> +}
> +
>  typedef void (*config_section_load)(struct app_params *p,
>       const char *section_name,
>       struct rte_cfgfile *cfg);
> @@ -2483,6 +2568,7 @@ static const struct config_section cfg_file_scheme[]
> = {
>       {"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
>       {"MSGQ-RSP-PIPELINE", 1, parse_msgq_rsp_pipeline},
>       {"MSGQ", 1, parse_msgq},
> +     {"KNI", 1, parse_kni},
>  };
>
>  static void
> @@ -2619,6 +2705,7 @@ app_config_parse(struct app_params *app, const
> char *file_name)
>       APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
>       APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
>       APP_PARAM_COUNT(app->pipeline_params, app->n_pipelines);
> +     APP_PARAM_COUNT(app->kni_params, app->n_kni);
>
>  #ifdef RTE_PORT_PCAP
>       for (i = 0; i < (int)app->n_pktq_source; i++) {
> @@ -3025,6 +3112,9 @@ save_pipeline_params(struct app_params *app,
> FILE *f)
>                               case APP_PKTQ_IN_SOURCE:
>                                       name = app->source_params[pp-
> >id].name;
>                                       break;
> +                             case APP_PKTQ_IN_KNI:
> +                                     name = app->kni_params[pp-
> >id].name;
> +                                     break;
>                               default:
>                                       APP_CHECK(0, "System error "
>                                               "occurred while saving "
> @@ -3059,6 +3149,9 @@ save_pipeline_params(struct app_params *app,
> FILE *f)
>                               case APP_PKTQ_OUT_SINK:
>                                       name = app->sink_params[pp-
> >id].name;
>                                       break;
> +                             case APP_PKTQ_OUT_KNI:
> +                                     name = app->kni_params[pp-
> >id].name;
> +                                     break;
>                               default:
>                                       APP_CHECK(0, "System error "
>                                               "occurred while saving "
> @@ -3114,6 +3207,37 @@ save_pipeline_params(struct app_params *app,
> FILE *f)
>       }
>  }
>
> +static void
> +save_kni_params(struct app_params *app, FILE *f)
> +{
> +     struct app_kni_params *p;
> +     size_t i, count;
> +
> +     count = RTE_DIM(app->kni_params);
> +     for (i = 0; i < count; i++) {
> +             p = &app->kni_params[i];
> +             if (!APP_PARAM_VALID(p))
> +                     continue;
> +
> +             /* section name */
> +             fprintf(f, "[%s]\n", p->name);
> +
> +             /* core */
> +             fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
> +                             p->socket_id,
> +                             p->core_id,
> +                             (p->hyper_th_id) ? "h" : "");
> +
> +             /* mempool */
> +             fprintf(f, "%s = %" PRIu32 "\n", "mempool_id", p-
> >mempool_id);
The name of the entry is "mempool" instead of "mempool_id". The value of the entry is app->mempool_params[p->mempool_id].name (which is a string) instead of p->mempool_id (which is a number).

> +
> +             /* burst */
> +             fprintf(f, "%s = %" PRIu32 "\n", "burst", p->burst);
> +

We should save both p->burst_read and p->burst_write.

> +             fputc('\n', f);
> +     }
> +}
> +
>  void
>  app_config_save(struct app_params *app, const char *file_name)
>  {
> @@ -3144,6 +3268,7 @@ app_config_save(struct app_params *app, const
> char *file_name)
>       save_source_params(app, file);
>       save_sink_params(app, file);
>       save_msgq_params(app, file);
> +     save_kni_params(app, file);
>
>       fclose(file);
>       free(name);
> @@ -3206,6 +3331,11 @@ app_config_init(struct app_params *app)
>                       &default_pipeline_params,
>                       sizeof(default_pipeline_params));
>
> +     for (i = 0; i < RTE_DIM(app->kni_params); i++)
> +             memcpy(&app->kni_params[i],
> +                     &default_kni_params,
> +                     sizeof(default_kni_params));
> +
>       return 0;
>  }
>
> diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
> index 02351f6..8ff9118 100644
> --- a/examples/ip_pipeline/init.c
> +++ b/examples/ip_pipeline/init.c
This is run-time code, let's have all the KNI code in init.c file enabled only when KNI library is part of the build:
#ifdef RTE_LIBRTE_KNI
...
#endif /* RTE_LIBRTE_KNI */

> @@ -89,6 +89,24 @@ app_init_core_mask(struct app_params *app)
>               mask |= 1LLU << lcore_id;
>       }
>
> +     for (i = 0; i < app->n_kni; i++) {
> +             struct app_kni_params *p = &app->kni_params[i];
> +             int lcore_id;
> +
> +             lcore_id = cpu_core_map_get_lcore_id(app->core_map,
> +                     p->socket_id,
> +                     p->core_id,
> +                     p->hyper_th_id);
> +
> +             if (lcore_id < 0)
> +                     rte_panic("Cannot create CPU core mask\n");
> +
> +             if (mask & 1LLU << lcore_id)

Please use parenthesis for improved readability: if (mask & (1LLU << lcore_id)).

> +                     rte_panic("KNI interface must use a dedicated
> lcore\n");

The bigger questions are:
- Why do we need to dedicate separate CPU core(s) for KNI interface(s)? Isn't KNI code running in kernel space, why do we need to dedicate separate user space core for it and why do we need to add this core to the user-space application core mask?
- Even if we need to add this to the core mask (maybe I am missing something here ...), why do we need to dedicate this core entirely to KNI? Can't we have KNI (kernel) code sharing this core with user-space application code (e.g. some pipeline instances?)

First of all, it is a bug to add KNI kernel core to the user space core mask. What I want is just to check if the KNI kernel thread has a dedicated core.
The reason I prefer to allocate a dedicated core to KNI kernel thread is that my application is latency sensitive. I worry the context switch and cache miss will cause the latency increasing if the KNI kernel thread and application thread share one core.
Anyway, I think I should remove the hard coded check because it will be more generic. Users who has the similar usage like mine can achieve so through configuration file.

> +
> +             mask |= 1LLU << lcore_id;
> +     }
> +
>       app->core_mask = mask;
>       APP_LOG(app, HIGH, "CPU core mask = 0x%016" PRIx64, app-
> >core_mask);
>  }
> @@ -1236,6 +1254,11 @@ static void app_pipeline_params_get(struct
> app_params *app,
>                                       n_bytes_per_pkt;
>                       }
>                       break;
> +             case APP_PKTQ_IN_KNI:
> +                     out->type = PIPELINE_PORT_IN_KNI;
> +                     out->params.kni.kni = app->kni[in->id];
> +                     out->burst_size = app->kni_params[in->id].burst;
> +                     break;
>               default:
>                       break;
>               }
> @@ -1374,6 +1397,12 @@ static void app_pipeline_params_get(struct
> app_params *app,
>                               out->params.sink.max_n_pkts = 0;
>                       }
>                       break;
> +             case APP_PKTQ_OUT_KNI:
> +                     out->type = PIPELINE_PORT_OUT_KNI;
> +                     out->params.kni.kni = app->kni[in->id];
> +                     out->params.kni.tx_burst_sz =
> +                                     app->kni_params[in->id].burst;
> +                     break;
>               default:
>                       break;
>               }
> @@ -1397,6 +1426,55 @@ static void app_pipeline_params_get(struct
> app_params *app,
>  }
>
>  static void
> +app_init_kni(struct app_params *app) {
> +     uint32_t i;
> +     struct rte_kni_conf conf;
> +
Avoid calling rte_kni_init() when there is no KNI device:

if (app->n_kni == 0)
        return;

> +     rte_kni_init((unsigned int)app->n_kni);
> +
> +     for (i = 0; i < app->n_kni; i++) {
> +             struct app_kni_params *p_kni = &app->kni_params[i];
> +             uint32_t port_id;

Please rename port_id with link_id, as this index is really the x from LINKx objects.

> +             struct app_mempool_params *mempool_params;
> +             struct rte_mempool *mempool;
> +
> +             if (sscanf(p_kni->name, "KNI%" PRIu32, &port_id) != 1)
> +                     rte_panic("%s's port id is invalid\n", p_kni->name);

Same comment as above: we do not need to check the x in KNIx, as LINKx is (should be, after you adjust the config_parse.c code) added automatically for KNIx.

> +
> +             mempool_params = &app->mempool_params[p_kni-
> >mempool_id];
> +             mempool = app->mempool[p_kni->mempool_id];
> +
> +             memset(&conf, 0, sizeof(conf));
> +             snprintf(conf.name<http://conf.name>, RTE_KNI_NAMESIZE,
> +                              "vEth%u", port_id);
> +             conf.core_id = p_kni->core_id;

The way the conf.core_id is set here is wrong, right?


conf.core_id = cpu_core_map_get_lcore_id(app->core_map,
        p->socket_id,
        p->core_id,
        p->hyper_th_id);

> +             conf.force_bind = 1;
> +
> +             conf.group_id = (uint16_t) port_id;
> +             conf.mbuf_size = mempool_params->buffer_size;
> +
> +             struct rte_kni_ops ops;
> +             struct rte_eth_dev_info dev_info;
> +

Please move these definitions at the top of the for loop block rather than having them in the middle of the for loop block.

> +             memset(&dev_info, 0, sizeof(dev_info));
> +             rte_eth_dev_info_get(app->link_params[port_id].pmd_id,
> +                                                      &dev_info);
> +             conf.addr = dev_info.pci_dev->addr;
> +             conf.id<http://conf.id> = dev_info.pci_dev->id;
> +
> +             memset(&ops, 0, sizeof(ops));
> +             ops.port_id = app->link_params[port_id].pmd_id;
> +             ops.change_mtu = kni_change_mtu;
> +             ops.config_network_if = kni_config_network_interface;
> +
> +             app->kni[i] = rte_kni_alloc(mempool,
> +                     &conf, &ops);
> +             if (!app->kni[i])
> +                     rte_panic("Fail to create kni for port: %d\n", port_id);

rte_panic("Failed to create %s", p->name);

This should print e.g. "Failed to create KNI5", which users should know it is the KNI associated with LINK5.

> +     }
> +}
> +
> +static void
>  app_init_pipelines(struct app_params *app)
>  {
>       uint32_t p_id;
> @@ -1531,6 +1609,7 @@ int app_init(struct app_params *app)
>       app_init_swq(app);
>       app_init_tm(app);
>       app_init_msgq(app);
> +     app_init_kni(app);
>
>       app_pipeline_common_cmd_push(app);
>       app_pipeline_thread_cmd_push(app);
> diff --git a/examples/ip_pipeline/kni/kni.c b/examples/ip_pipeline/kni/kni.c
> new file mode 100644
> index 0000000..c58e146

Please move the two functions from kni/kni.c to init.c (just before app_init_kni() function) and make them static functions, then remove kni/kni.h and kni/kni.c files.

> --- /dev/null
> +++ b/examples/ip_pipeline/kni/kni.c
> @@ -0,0 +1,80 @@
> +#include <string.h>
> +
> +#include <rte_common.h>
> +#include <rte_malloc.h>
> +#include <rte_table_array.h>
> +#include <rte_kni.h>
> +#include <rte_ethdev.h>
> +
> +#include "rte_port_kni.h"
> +#include "kni.h"
> +
> +int
> +kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
> +     int ret = 0;
> +
> +     if (port_id >= rte_eth_dev_count() || port_id >=
> RTE_MAX_ETHPORTS) {
> +             RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
> +                             __func__, port_id);
> +             return -EINVAL;
> +     }
> +
> +     RTE_LOG(INFO, PORT, "%s: Configure network interface of %d
> %s\n",
> +                     __func__, port_id, if_up ? "up" : "down");
> +
> +     if (if_up != 0) { /* Configure network interface up */
> +             rte_eth_dev_stop(port_id);
Why do we need to stop the device first before we start it?

> +             ret = rte_eth_dev_start(port_id);
> +     } else /* Configure network interface down */
> +             rte_eth_dev_stop(port_id);

Do we need to call rte_eth_dev_start/stop() or do we need to call rte_eth_dev_up/down()?
To be  honest, I made reference to the the KNI sample application.
Since there is very little docs tell the difference between device up call and device start call, I am not sure which one is better here.
Any help will be appreciate. :-)

> +
> +     if (ret < 0)
> +             RTE_LOG(ERR, PORT, "%s: Failed to start port %d\n",
> +                             __func__, port_id);
> +

This is a callback function, I think we should completely remove any RTE_LOG calls from it, as link can go up and down quite frequently.

> +     return ret;
> +}
> +
> +int
> +kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
> +     int ret;
> +
> +     if (port_id >= rte_eth_dev_count()) {
> +             RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
> +                             __func__, port_id);
> +             return -EINVAL;
> +     }
> +
> +     if (new_mtu > ETHER_MAX_LEN) {
> +             RTE_LOG(ERR, PORT,
> +                             "%s: Fail to reconfigure port %d, the new
> MTU is too big\n",
> +                             __func__, port_id);
> +             return -EINVAL;
> +     }
> +
> +     RTE_LOG(INFO, PORT, "%s: Change MTU of port %d to %u\n",
> +                     __func__, port_id,
> +                     new_mtu);
> +
> +     /* Stop specific port */
> +     rte_eth_dev_stop(port_id);
> +
> +     /* Set new MTU */
> +     ret = rte_eth_dev_set_mtu(port_id, new_mtu);
> +     if (ret < 0) {
> +             RTE_LOG(ERR, PORT, "%s: Fail to reconfigure port %d\n",
> +                             __func__, port_id);
> +             return ret;
> +     }
> +
> +     /* Restart specific port */
> +     ret = rte_eth_dev_start(port_id);
> +     if (ret < 0) {
> +             RTE_LOG(ERR, PORT, "%s: Fail to restart port %d\n",
> +                             __func__, port_id);
> +             return ret;
> +     }
> +
> +     return 0;
> +}
This is a callback function, I think we should completely remove all the RTE_LOG calls from it.

> +
> diff --git a/examples/ip_pipeline/kni/kni.h b/examples/ip_pipeline/kni/kni.h
> new file mode 100644
> index 0000000..04c8429
> --- /dev/null
> +++ b/examples/ip_pipeline/kni/kni.h
> @@ -0,0 +1,16 @@
> +#ifndef __INCLUDE_KNI_H__
> +#define __INCLUDE_KNI_H__
> +
> +#include <rte_common.h>
> +
> +/* Total octets in ethernet header */
> +#define KNI_ENET_HEADER_SIZE    14

Are we actually using this macro? I would like to remove it if not needed.

> +
> +/* Total octets in the FCS */
> +#define KNI_ENET_FCS_SIZE       4

Are we actually using this macro? I would like to remove it if not needed.

> +
> +int kni_config_network_interface(uint8_t port_id, uint8_t if_up);
> +
> +int kni_change_mtu(uint8_t port_id, unsigned new_mtu);
> +
> +#endif
> diff --git a/examples/ip_pipeline/pipeline_be.h
> b/examples/ip_pipeline/pipeline_be.h
> index f4ff262..23f0438 100644
> --- a/examples/ip_pipeline/pipeline_be.h
> +++ b/examples/ip_pipeline/pipeline_be.h
> @@ -40,6 +40,7 @@
>  #include <rte_port_ras.h>
>  #include <rte_port_sched.h>
>  #include <rte_port_source_sink.h>
> +#include <rte_port_kni.h>
>  #include <rte_pipeline.h>
>
>  enum pipeline_port_in_type {
> @@ -50,6 +51,7 @@ enum pipeline_port_in_type {
>       PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
>       PIPELINE_PORT_IN_SCHED_READER,
>       PIPELINE_PORT_IN_SOURCE,
> +     PIPELINE_PORT_IN_KNI,
>  };
>
>  struct pipeline_port_in_params {
> @@ -62,6 +64,7 @@ struct pipeline_port_in_params {
>               struct rte_port_ring_reader_ipv6_frag_params
> ring_ipv6_frag;
>               struct rte_port_sched_reader_params sched;
>               struct rte_port_source_params source;
> +             struct rte_port_kni_reader_params kni;
>       } params;
>       uint32_t burst_size;
>  };
> @@ -84,6 +87,8 @@ pipeline_port_in_params_convert(struct
> pipeline_port_in_params  *p)
>               return (void *) &p->params.sched;
>       case PIPELINE_PORT_IN_SOURCE:
>               return (void *) &p->params.source;
> +     case PIPELINE_PORT_IN_KNI:
> +             return (void *) &p->params.kni;
>       default:
>               return NULL;
>       }
> @@ -107,6 +112,8 @@ pipeline_port_in_params_get_ops(struct
> pipeline_port_in_params  *p)
>               return &rte_port_sched_reader_ops;
>       case PIPELINE_PORT_IN_SOURCE:
>               return &rte_port_source_ops;
> +     case PIPELINE_PORT_IN_KNI:
> +             return &rte_port_kni_reader_ops;
>       default:
>               return NULL;
>       }
> @@ -123,6 +130,7 @@ enum pipeline_port_out_type {
>       PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
>       PIPELINE_PORT_OUT_SCHED_WRITER,
>       PIPELINE_PORT_OUT_SINK,
> +     PIPELINE_PORT_OUT_KNI,
>  };
>
>  struct pipeline_port_out_params {
> @@ -138,6 +146,7 @@ struct pipeline_port_out_params {
>               struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
>               struct rte_port_sched_writer_params sched;
>               struct rte_port_sink_params sink;
> +             struct rte_port_kni_writer_params kni;
>       } params;
>  };
>
> @@ -165,6 +174,8 @@ pipeline_port_out_params_convert(struct
> pipeline_port_out_params  *p)
>               return (void *) &p->params.sched;
>       case PIPELINE_PORT_OUT_SINK:
>               return (void *) &p->params.sink;
> +     case PIPELINE_PORT_OUT_KNI:
> +             return (void *) &p->params.kni;
>       default:
>               return NULL;
>       }
> @@ -194,6 +205,8 @@ pipeline_port_out_params_get_ops(struct
> pipeline_port_out_params  *p)
>               return &rte_port_sched_writer_ops;
>       case PIPELINE_PORT_OUT_SINK:
>               return &rte_port_sink_ops;
> +     case PIPELINE_PORT_OUT_KNI:
> +             return &rte_port_kni_writer_ops;
>       default:
>               return NULL;
>       }
> diff --git a/examples/ip_pipeline/thread.c b/examples/ip_pipeline/thread.c
> index a0f1f12..534864a 100644
> --- a/examples/ip_pipeline/thread.c
> +++ b/examples/ip_pipeline/thread.c
> @@ -239,6 +239,15 @@ app_thread(void *arg)
>       uint32_t core_id = rte_lcore_id(), i, j;
>       struct app_thread_data *t = &app->thread_data[core_id];
>
> +     for (i = 0; i < app->n_kni; i++) {
> +             if (core_id == (uint32_t)cpu_core_map_get_lcore_id(
> +                     app->core_map,
> +                     app->kni_params[i].socket_id,
> +                     app->kni_params[i].core_id,
> +                     app->kni_params[i].hyper_th_id))
> +                     return 0;
> +     }
> +
Same questions as above:
- Why do we need to dedicate separate CPU core(s) for KNI interface(s)? Isn't KNI code running in kernel space, why do we need to dedicate separate user space core for it and why do we need to add this core to the user-space application core mask?
- Even if we need to add this to the core mask (maybe I am missing something here ...), why do we need to dedicate this core entirely to KNI? Can't we have KNI (kernel) code sharing this core with user-space application code (e.g. some pipeline instances?)

This is run-time code, let's have it enabled only when KNI library is part of the build:
#ifdef RTE_LIBRTE_KNI
...
#endif /* RTE_LIBRTE_KNI */

>       for (i = 0; ; i++) {
>               uint32_t n_regular = RTE_MIN(t->n_regular, RTE_DIM(t-
> >regular));
>               uint32_t n_custom = RTE_MIN(t->n_custom, RTE_DIM(t-
> >custom));
> diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
> index d4de5af..f18253d 100644
> --- a/lib/librte_port/Makefile
> +++ b/lib/librte_port/Makefile
> @@ -57,6 +57,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
>  endif
>  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
> +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> +SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
> +endif
>
>  # install includes
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port.h
> @@ -68,6 +71,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=
> rte_port_ras.h
>  endif
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
> +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> +SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
> +endif
>
>  # this lib depends upon:
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
> @@ -75,5 +81,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=
> lib/librte_mbuf
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
>
>  include $(RTE_SDK)/mk/rte.lib.mk<http://rte.lib.mk>
> diff --git a/lib/librte_port/rte_port_kni.c b/lib/librte_port/rte_port_kni.c
> new file mode 100644
> index 0000000..8c5e404
> --- /dev/null
> +++ b/lib/librte_port/rte_port_kni.c
> @@ -0,0 +1,316 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
Please fix the year as 2016.

> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> + */
> +#include <string.h>
> +
> +#include <rte_common.h>
> +#include <rte_malloc.h>
> +#include <rte_kni.h>
> +
> +#include "rte_port_kni.h"
> +
> +/*
> + * Port KNI Reader
> + */
> +#ifdef RTE_PORT_STATS_COLLECT
> +
> +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
> +     {port->stats.n_pkts_in += val}
> +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
> +     {port->stats.n_pkts_drop += val}
This actually results in compiler error when built with RTE_PORT_STATS_COLLECT = ON.

Please add semicolon and remove curly braces (as in e.g. rte_port_ring.c):
#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
        port->stats.n_pkts_in += val;
#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
        port->stats.n_pkts_drop += val;

> +#else
> +
> +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
> +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
> +
> +#endif
> +
> +struct rte_port_kni_reader {
> +     struct rte_port_in_stats stats;
> +
> +     struct rte_kni *kni;
> +};
> +
> +static void *
> +rte_port_kni_reader_create(void *params, int socket_id) {
> +     struct rte_port_kni_reader_params *conf =
> +                     (struct rte_port_kni_reader_params *) params;
> +     struct rte_port_kni_reader *port;
> +
> +     /* Check input parameters */
> +     if (conf == NULL) {
> +             RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
> +             return NULL;
> +     }
> +
> +     /* Memory allocation */
> +     port = rte_zmalloc_socket("PORT", sizeof(*port),
> +             RTE_CACHE_LINE_SIZE, socket_id);
> +     if (port == NULL) {
> +             RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> __func__);
> +             return NULL;
> +     }
> +
> +     /* Initialization */
> +     port->kni = conf->kni;
> +
> +     return port;
> +}
> +
> +static int
> +rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t
> n_pkts) {
> +     struct rte_port_kni_reader *p =
> +                     (struct rte_port_kni_reader *) port;
> +     uint16_t rx_pkt_cnt;
> +
> +     rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
> +     RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
> +     return rx_pkt_cnt;
> +}
> +
> +static int
> +rte_port_kni_reader_free(void *port) {
> +     if (port == NULL) {
> +             RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
> +             return -EINVAL;
> +     }
> +
> +     rte_free(port);
> +
> +     return 0;
> +}
> +
> +static int rte_port_kni_reader_stats_read(void *port,
> +     struct rte_port_in_stats *stats, int clear)
> +{
> +     struct rte_port_kni_reader *p =
> +                     (struct rte_port_kni_reader *) port;
> +
> +     if (stats != NULL)
> +             memcpy(stats, &p->stats, sizeof(p->stats));
> +
> +     if (clear)
> +             memset(&p->stats, 0, sizeof(p->stats));
> +
> +     return 0;
> +}
> +
> +/*
> + * Port KNI Writer
> + */
> +#ifdef RTE_PORT_STATS_COLLECT
> +
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
> +     {port->stats.n_pkts_in += val}
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
> +     {port->stats.n_pkts_drop += val}
> +
This actually results in compiler error when built with RTE_PORT_STATS_COLLECT = ON.

Please add semicolon and remove curly braces (as in e.g. rte_port_ring.c).
Actually I implements the macro similar to RTE_PORT_RING_READER_STATS_PKTS_IN_ADD first. But the scripts/checkpatches.sh fails: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parentheses
I'm not share either I have done something wrong or the checkpatches script need an update.

> +#else
> +
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
> +
> +#endif
> +
> +struct rte_port_kni_writer {
> +     struct rte_port_out_stats stats;
> +
> +     struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
> +     uint32_t tx_burst_sz;
> +     uint16_t tx_buf_count;

Can we actually make tx_buf_count to be uint32_t rather than uint16_t? I see some computation between tx_buf_count and some other variable of type uint32_t later in the code ...

> +     uint64_t bsz_mask;
> +     struct rte_kni *kni;
> +};
> +
> +static void *
> +rte_port_kni_writer_create(void *params, int socket_id) {
> +     struct rte_port_kni_writer_params *conf =
> +                     (struct rte_port_kni_writer_params *) params;
> +     struct rte_port_kni_writer *port;
> +
> +     /* Check input parameters */
> +     if ((conf == NULL) ||
> +             (conf->tx_burst_sz == 0) ||
> +             (conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
> +             (!rte_is_power_of_2(conf->tx_burst_sz))) {
> +             RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n",
> __func__);
> +             return NULL;
> +     }
> +
> +     /* Memory allocation */
> +     port = rte_zmalloc_socket("PORT", sizeof(*port),
> +             RTE_CACHE_LINE_SIZE, socket_id);
> +     if (port == NULL) {
> +             RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> __func__);
> +             return NULL;
> +     }
> +
> +     /* Initialization */
> +     port->kni = conf->kni;
> +     port->tx_burst_sz = conf->tx_burst_sz;
> +     port->tx_buf_count = 0;
> +     port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
> +
> +     return port;
> +}
> +
> +static inline void
> +send_burst(struct rte_port_kni_writer *p) {
> +     uint32_t nb_tx;
> +
> +     nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
> +     rte_kni_handle_request(p->kni);
> +
> +     RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p-
> >tx_buf_count - nb_tx);
> +     for (; nb_tx < p->tx_buf_count; nb_tx++)
> +             rte_pktmbuf_free(p->tx_buf[nb_tx]);
> +
> +     p->tx_buf_count = 0;
> +}
> +
> +static int
> +rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt) {
> +     struct rte_port_kni_writer *p =
> +                     (struct rte_port_kni_writer *) port;
> +
> +     p->tx_buf[p->tx_buf_count++] = pkt;
> +     RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> +     if (p->tx_buf_count >= p->tx_burst_sz)
> +             send_burst(p);
> +
> +     return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_tx_bulk(void *port,
> +                                                     struct rte_mbuf
> **pkts,
> +                                                     uint64_t pkts_mask) {
> +     struct rte_port_kni_writer *p =
> +                     (struct rte_port_kni_writer *) port;
> +     uint64_t bsz_mask = p->bsz_mask;
> +     uint32_t tx_buf_count = p->tx_buf_count;
> +     uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
> +                                     ((pkts_mask & bsz_mask) ^
> bsz_mask);
> +
> +     if (expr == 0) {
> +             uint64_t n_pkts = __builtin_popcountll(pkts_mask);
> +             uint32_t n_pkts_ok;
> +
> +             if (tx_buf_count)
> +                     send_burst(p);
> +
> +             RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
> +             n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
> +
> +             RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts
> - n_pkts_ok);
> +             for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
> +                     struct rte_mbuf *pkt = pkts[n_pkts_ok];
> +
> +                     rte_pktmbuf_free(pkt);
> +             }
> +     } else {
> +             for (; pkts_mask;) {
> +                     uint32_t pkt_index = __builtin_ctzll(pkts_mask);
> +                     uint64_t pkt_mask = 1LLU << pkt_index;
> +                     struct rte_mbuf *pkt = pkts[pkt_index];
> +
> +                     p->tx_buf[tx_buf_count++] = pkt;
> +                     RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> +                     pkts_mask &= ~pkt_mask;
> +             }
> +
> +             p->tx_buf_count = tx_buf_count;
> +             if (tx_buf_count >= p->tx_burst_sz)
> +                     send_burst(p);
> +     }
> +
> +     return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_flush(void *port) {
> +     struct rte_port_kni_writer *p =
> +                     (struct rte_port_kni_writer *) port;
> +
> +     if (p->tx_buf_count > 0)
> +             send_burst(p);
> +
> +     return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_free(void *port) {
> +     if (port == NULL) {
> +             RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
> +             return -EINVAL;
> +     }
> +
> +     rte_port_kni_writer_flush(port);
> +     rte_free(port);
> +
> +     return 0;
> +}
> +
> +static int rte_port_kni_writer_stats_read(void *port,
> +     struct rte_port_out_stats *stats, int clear)
> +{
> +     struct rte_port_kni_writer *p =
> +                     (struct rte_port_kni_writer *) port;
> +
> +     if (stats != NULL)
> +             memcpy(stats, &p->stats, sizeof(p->stats));
> +
> +     if (clear)
> +             memset(&p->stats, 0, sizeof(p->stats));
> +
> +     return 0;
> +}
> +
> +/*
> + * Summary of port operations
> + */
> +struct rte_port_in_ops rte_port_kni_reader_ops = {
> +     .f_create = rte_port_kni_reader_create,
> +     .f_free = rte_port_kni_reader_free,
> +     .f_rx = rte_port_kni_reader_rx,
> +     .f_stats = rte_port_kni_reader_stats_read,
> +};
> +
> +struct rte_port_out_ops rte_port_kni_writer_ops = {
> +     .f_create = rte_port_kni_writer_create,
> +     .f_free = rte_port_kni_writer_free,
> +     .f_tx = rte_port_kni_writer_tx,
> +     .f_tx_bulk = rte_port_kni_writer_tx_bulk,
> +     .f_flush = rte_port_kni_writer_flush,
> +     .f_stats = rte_port_kni_writer_stats_read,
> +};
Do we need a KNI writer no-drop version as well? Would it be useful?

> diff --git a/lib/librte_port/rte_port_kni.h b/lib/librte_port/rte_port_kni.h
> new file mode 100644
> index 0000000..7623798
> --- /dev/null
> +++ b/lib/librte_port/rte_port_kni.h
> @@ -0,0 +1,81 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.

Please fix the year to 2016.

> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> + */
> +
> +#ifndef __INCLUDE_RTE_PORT_KNI_H__
> +#define __INCLUDE_RTE_PORT_KNI_H__
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/**
> + * @file
> + * RTE Port KNI Interface
> + *
> + * kni_reader: input port built on top of pre-initialized KNI interface
> + * kni_writer: output port built on top of pre-initialized KNI interface
> + *
> + ***/
> +
> +#include <stdint.h>
> +
> +#include <rte_kni.h>
> +
> +#include "rte_port.h"
> +
> +/** kni_reader port parameters */
> +struct rte_port_kni_reader_params {
> +     /** KNI interface reference */
> +     struct rte_kni *kni;
> +};
> +
> +/** kni_reader port operations */
> +extern struct rte_port_in_ops rte_port_kni_reader_ops;
> +
> +
> +/** kni_writer port parameters */
> +struct rte_port_kni_writer_params {
> +     /** KNI interface reference */
> +     struct rte_kni *kni;
> +     /** Burst size to KNI interface. */
> +     uint32_t tx_burst_sz;
> +};
> +
> +/** kni_writer port operations */
> +extern struct rte_port_out_ops rte_port_kni_writer_ops;
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
> --
> 2.7.4
Thank you!

Regards,
Cristian


B.R.

Ethan


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

* [PATCH v3 1/3] port: add kni interface support
  2016-05-28 11:25 ` [PATCH] port: add kni interface support WeiJie Zhuang
  2016-05-30 14:40   ` Dumitrescu, Cristian
  2016-06-09 23:42   ` Dumitrescu, Cristian
@ 2016-06-16 11:27   ` WeiJie Zhuang
  2016-06-16 11:27     ` [PATCH v3 2/3] port: add kni nodrop writer WeiJie Zhuang
                       ` (2 more replies)
  2016-06-21 10:55   ` [PATCH v4 1/4] port: " Ethan Zhuang
  3 siblings, 3 replies; 22+ messages in thread
From: WeiJie Zhuang @ 2016-06-16 11:27 UTC (permalink / raw)
  To: cristian.dumitrescu; +Cc: dev, jasvinder.singh, ferruh.yigit, WeiJie Zhuang

1. add KNI port type to the packet framework
2. add KNI support to the IP Pipeline sample Application
3. some bug fix

Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
---
v2:
* Fix check patch error.
v3:
* Fix code review comments.
---
 doc/api/doxy-api-index.md                          |   1 +
 examples/ip_pipeline/Makefile                      |   2 +-
 examples/ip_pipeline/app.h                         | 181 +++++++++++-
 examples/ip_pipeline/config/kni.cfg                |  67 +++++
 examples/ip_pipeline/config_check.c                |  26 +-
 examples/ip_pipeline/config_parse.c                | 166 ++++++++++-
 examples/ip_pipeline/init.c                        | 132 ++++++++-
 examples/ip_pipeline/pipeline/pipeline_common_fe.c |  29 ++
 examples/ip_pipeline/pipeline/pipeline_master_be.c |   6 +
 examples/ip_pipeline/pipeline_be.h                 |  27 ++
 lib/librte_port/Makefile                           |   7 +
 lib/librte_port/rte_port_kni.c                     | 325 +++++++++++++++++++++
 lib/librte_port/rte_port_kni.h                     |  82 ++++++
 lib/librte_port/rte_port_version.map               |   8 +
 14 files changed, 1047 insertions(+), 12 deletions(-)
 create mode 100644 examples/ip_pipeline/config/kni.cfg
 create mode 100644 lib/librte_port/rte_port_kni.c
 create mode 100644 lib/librte_port/rte_port_kni.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index f626386..5e7f024 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -118,6 +118,7 @@ There are many libraries, so their headers may be grouped by topics:
     [frag]             (@ref rte_port_frag.h),
     [reass]            (@ref rte_port_ras.h),
     [sched]            (@ref rte_port_sched.h),
+    [kni]              (@ref rte_port_kni.h),
     [src/sink]         (@ref rte_port_source_sink.h)
   * [table]            (@ref rte_table.h):
     [lpm IPv4]         (@ref rte_table_lpm.h),
diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index 5827117..6dc3f52 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -1,6 +1,6 @@
 #   BSD LICENSE
 #
-#   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+#   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
 #   All rights reserved.
 #
 #   Redistribution and use in source and binary forms, with or without
diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
index 7611341..abbd6d4 100644
--- a/examples/ip_pipeline/app.h
+++ b/examples/ip_pipeline/app.h
@@ -44,6 +44,9 @@
 #include <cmdline_parse.h>
 
 #include <rte_ethdev.h>
+#ifdef RTE_LIBRTE_KNI
+#include <rte_kni.h>
+#endif
 
 #include "cpu_core_map.h"
 #include "pipeline.h"
@@ -132,6 +135,20 @@ struct app_pktq_swq_params {
 	uint32_t mempool_indirect_id;
 };
 
+struct app_pktq_kni_params {
+	char *name;
+	uint32_t parsed;
+
+	uint32_t socket_id;
+	uint32_t core_id;
+	uint32_t hyper_th_id;
+	uint32_t force_bind;
+
+	uint32_t mempool_id; /* Position in the app->mempool_params */
+	uint32_t burst_read;
+	uint32_t burst_write;
+};
+
 #ifndef APP_FILE_NAME_SIZE
 #define APP_FILE_NAME_SIZE                       256
 #endif
@@ -185,6 +202,7 @@ enum app_pktq_in_type {
 	APP_PKTQ_IN_HWQ,
 	APP_PKTQ_IN_SWQ,
 	APP_PKTQ_IN_TM,
+	APP_PKTQ_IN_KNI,
 	APP_PKTQ_IN_SOURCE,
 };
 
@@ -197,6 +215,7 @@ enum app_pktq_out_type {
 	APP_PKTQ_OUT_HWQ,
 	APP_PKTQ_OUT_SWQ,
 	APP_PKTQ_OUT_TM,
+	APP_PKTQ_OUT_KNI,
 	APP_PKTQ_OUT_SINK,
 };
 
@@ -420,6 +439,8 @@ struct app_eal_params {
 
 #define APP_MAX_PKTQ_TM                          APP_MAX_LINKS
 
+#define APP_MAX_PKTQ_KNI                         APP_MAX_LINKS
+
 #ifndef APP_MAX_PKTQ_SOURCE
 #define APP_MAX_PKTQ_SOURCE                      64
 #endif
@@ -471,6 +492,7 @@ struct app_params {
 	struct app_pktq_hwq_out_params hwq_out_params[APP_MAX_HWQ_OUT];
 	struct app_pktq_swq_params swq_params[APP_MAX_PKTQ_SWQ];
 	struct app_pktq_tm_params tm_params[APP_MAX_PKTQ_TM];
+	struct app_pktq_kni_params kni_params[APP_MAX_PKTQ_KNI];
 	struct app_pktq_source_params source_params[APP_MAX_PKTQ_SOURCE];
 	struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
 	struct app_msgq_params msgq_params[APP_MAX_MSGQ];
@@ -482,6 +504,7 @@ struct app_params {
 	uint32_t n_pktq_hwq_out;
 	uint32_t n_pktq_swq;
 	uint32_t n_pktq_tm;
+	uint32_t n_pktq_kni;
 	uint32_t n_pktq_source;
 	uint32_t n_pktq_sink;
 	uint32_t n_msgq;
@@ -495,6 +518,9 @@ struct app_params {
 	struct app_link_data link_data[APP_MAX_LINKS];
 	struct rte_ring *swq[APP_MAX_PKTQ_SWQ];
 	struct rte_sched_port *tm[APP_MAX_PKTQ_TM];
+#ifdef RTE_LIBRTE_KNI
+	struct rte_kni *kni[APP_MAX_PKTQ_KNI];
+#endif /* RTE_LIBRTE_KNI */
 	struct rte_ring *msgq[APP_MAX_MSGQ];
 	struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
 	struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
@@ -667,11 +693,11 @@ app_swq_get_reader(struct app_params *app,
 	struct app_pktq_swq_params *swq,
 	uint32_t *pktq_in_id)
 {
-	struct app_pipeline_params *reader;
+	struct app_pipeline_params *reader = NULL;
 	uint32_t pos = swq - app->swq_params;
 	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
 		RTE_DIM(app->pipeline_params));
-	uint32_t n_readers = 0, id, i;
+	uint32_t n_readers = 0, id = 0, i;
 
 	for (i = 0; i < n_pipelines; i++) {
 		struct app_pipeline_params *p = &app->pipeline_params[i];
@@ -727,11 +753,11 @@ app_tm_get_reader(struct app_params *app,
 	struct app_pktq_tm_params *tm,
 	uint32_t *pktq_in_id)
 {
-	struct app_pipeline_params *reader;
+	struct app_pipeline_params *reader = NULL;
 	uint32_t pos = tm - app->tm_params;
 	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
 		RTE_DIM(app->pipeline_params));
-	uint32_t n_readers = 0, id, i;
+	uint32_t n_readers = 0, id = 0, i;
 
 	for (i = 0; i < n_pipelines; i++) {
 		struct app_pipeline_params *p = &app->pipeline_params[i];
@@ -758,6 +784,66 @@ app_tm_get_reader(struct app_params *app,
 }
 
 static inline uint32_t
+app_kni_get_readers(struct app_params *app, struct app_pktq_kni_params *kni)
+{
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq = &p->pktq_in[j];
+
+			if ((pktq->type == APP_PKTQ_IN_KNI) &&
+				(pktq->id == pos))
+				n_readers++;
+		}
+	}
+
+	return n_readers;
+}
+
+static inline struct app_pipeline_params *
+app_kni_get_reader(struct app_params *app,
+				  struct app_pktq_kni_params *kni,
+				  uint32_t *pktq_in_id)
+{
+	struct app_pipeline_params *reader = NULL;
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers = 0, id = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq = &p->pktq_in[j];
+
+			if ((pktq->type == APP_PKTQ_IN_KNI) &&
+				(pktq->id == pos)) {
+				n_readers++;
+				reader = p;
+				id = j;
+			}
+		}
+	}
+
+	if (n_readers != 1)
+		return NULL;
+
+	*pktq_in_id = id;
+	return reader;
+}
+
+static inline uint32_t
 app_source_get_readers(struct app_params *app,
 struct app_pktq_source_params *source)
 {
@@ -861,11 +947,11 @@ app_swq_get_writer(struct app_params *app,
 	struct app_pktq_swq_params *swq,
 	uint32_t *pktq_out_id)
 {
-	struct app_pipeline_params *writer;
+	struct app_pipeline_params *writer = NULL;
 	uint32_t pos = swq - app->swq_params;
 	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
 		RTE_DIM(app->pipeline_params));
-	uint32_t n_writers = 0, id, i;
+	uint32_t n_writers = 0, id = 0, i;
 
 	for (i = 0; i < n_pipelines; i++) {
 		struct app_pipeline_params *p = &app->pipeline_params[i];
@@ -923,11 +1009,11 @@ app_tm_get_writer(struct app_params *app,
 	struct app_pktq_tm_params *tm,
 	uint32_t *pktq_out_id)
 {
-	struct app_pipeline_params *writer;
+	struct app_pipeline_params *writer = NULL;
 	uint32_t pos = tm - app->tm_params;
 	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
 		RTE_DIM(app->pipeline_params));
-	uint32_t n_writers = 0, id, i;
+	uint32_t n_writers = 0, id = 0, i;
 
 	for (i = 0; i < n_pipelines; i++) {
 		struct app_pipeline_params *p = &app->pipeline_params[i];
@@ -939,10 +1025,73 @@ app_tm_get_writer(struct app_params *app,
 			struct app_pktq_out_params *pktq = &p->pktq_out[j];
 
 			if ((pktq->type == APP_PKTQ_OUT_TM) &&
+				(pktq->id == pos)) {
+				n_writers++;
+				writer = p;
+				id = j;
+			}
+		}
+	}
+
+	if (n_writers != 1)
+		return NULL;
+
+	*pktq_out_id = id;
+	return writer;
+}
+
+static inline uint32_t
+app_kni_get_writers(struct app_params *app, struct app_pktq_kni_params *kni)
+{
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
+			RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq = &p->pktq_out[j];
+
+			if ((pktq->type == APP_PKTQ_OUT_KNI) &&
 				(pktq->id == pos))
 				n_writers++;
+		}
+	}
+
+	return n_writers;
+}
+
+static inline struct app_pipeline_params *
+app_kni_get_writer(struct app_params *app,
+				  struct app_pktq_kni_params *kni,
+				  uint32_t *pktq_out_id)
+{
+	struct app_pipeline_params *writer = NULL;
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers = 0, id = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
+			RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq = &p->pktq_out[j];
+
+			if ((pktq->type == APP_PKTQ_OUT_KNI) &&
+				(pktq->id == pos)) {
+				n_writers++;
 				writer = p;
 				id = j;
+			}
 		}
 	}
 
@@ -1051,6 +1200,22 @@ app_get_link_for_tm(struct app_params *app, struct app_pktq_tm_params *p_tm)
 	return &app->link_params[link_param_idx];
 }
 
+static inline struct app_link_params *
+app_get_link_for_kni(struct app_params *app, struct app_pktq_kni_params *p_kni)
+{
+	char link_name[APP_PARAM_NAME_SIZE];
+	uint32_t link_id;
+	ssize_t link_param_idx;
+
+	sscanf(p_kni->name, "KNI%" PRIu32, &link_id);
+	sprintf(link_name, "LINK%" PRIu32, link_id);
+	link_param_idx = APP_PARAM_FIND(app->link_params, link_name);
+	APP_CHECK((link_param_idx >= 0),
+			  "Cannot find %s for %s", link_name, p_kni->name);
+
+	return &app->link_params[link_param_idx];
+}
+
 void app_pipeline_params_get(struct app_params *app,
 	struct app_pipeline_params *p_in,
 	struct pipeline_params *p_out);
diff --git a/examples/ip_pipeline/config/kni.cfg b/examples/ip_pipeline/config/kni.cfg
new file mode 100644
index 0000000..c339080
--- /dev/null
+++ b/examples/ip_pipeline/config/kni.cfg
@@ -0,0 +1,67 @@
+;   BSD LICENSE
+;
+;   Copyright(c) 2016 Intel Corporation.
+;   All rights reserved.
+;
+;   Redistribution and use in source and binary forms, with or without
+;   modification, are permitted provided that the following conditions
+;   are met:
+;
+;     * Redistributions of source code must retain the above copyright
+;       notice, this list of conditions and the following disclaimer.
+;     * Redistributions in binary form must reproduce the above copyright
+;       notice, this list of conditions and the following disclaimer in
+;       the documentation and/or other materials provided with the
+;       distribution.
+;     * Neither the name of Intel Corporation nor the names of its
+;       contributors may be used to endorse or promote products derived
+;       from this software without specific prior written permission.
+;
+;   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+;   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+;   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+;   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+;   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+;   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+;   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+;   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+;   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+;   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+;   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+;             ______________          ______________________
+;            |              |  KNI0  |                      |
+; RXQ0.0 --->|              |------->|--+                   |
+;            |              |  KNI1  |  | br0               |
+; TXQ0.0 <---|              |<-------|<-+                   |
+;            | Pass-through |        |     Linux Kernel     |
+;            |     (P1)     |        |     Network Stack    |
+;            |              |  KNI0  |                      |
+; RXQ0.0 --->|              |------->|--+                   |
+;            |              |  KNI1  |  | br0               |
+; TXQ0.0 <---|              |<-------|<-+                   |
+;            |______________|        |______________________|
+;
+; Insert Linux kernel KNI module:
+;    [Linux]$ insmod rte_kni.ko
+;
+; Configure Linux kernel bridge between KNI0 and KNI1 interfaces:
+;    [Linux]$ ifconfig KNI0 up
+;    [Linux]$ ifconfig KNI1 up
+;    [Linux]$ brctl addbr "br0"
+;    [Linux]$ brctl addif br0 KNI0
+;    [Linux]$ brctl addif br0 KNI1
+;    [Linux]$ ifconfig br0 up
+
+[EAL]
+log_level = 0
+
+[PIPELINE0]
+type = MASTER
+core = 0
+
+[PIPELINE1]
+type = PASS-THROUGH
+core = 1
+pktq_in = RXQ0.0 KNI1 RXQ1.0 KNI0
+pktq_out = KNI0 TXQ1.0 KNI1 TXQ0.0
diff --git a/examples/ip_pipeline/config_check.c b/examples/ip_pipeline/config_check.c
index 18f57be..7e90342 100644
--- a/examples/ip_pipeline/config_check.c
+++ b/examples/ip_pipeline/config_check.c
@@ -1,7 +1,7 @@
 /*-
  *   BSD LICENSE
  *
- *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
  *   All rights reserved.
  *
  *   Redistribution and use in source and binary forms, with or without
@@ -444,6 +444,29 @@ check_pipelines(struct app_params *app)
 	}
 }
 
+static void
+check_knis(struct app_params *app) {
+	uint32_t i;
+
+	for (i = 0; i < app->n_pktq_kni; i++) {
+		struct app_pktq_kni_params *p = &app->kni_params[i];
+		uint32_t n_readers = app_kni_get_readers(app, p);
+		uint32_t n_writers = app_kni_get_writers(app, p);
+
+		APP_CHECK((n_readers != 0),
+			"%s has no reader\n", p->name);
+
+		APP_CHECK((n_readers == 1),
+			"%s has more than one reader\n", p->name);
+
+		APP_CHECK((n_writers != 0),
+			"%s has no writer\n", p->name);
+
+		APP_CHECK((n_writers == 1),
+			"%s has more than one writer\n", p->name);
+	}
+}
+
 int
 app_config_check(struct app_params *app)
 {
@@ -453,6 +476,7 @@ app_config_check(struct app_params *app)
 	check_txqs(app);
 	check_swqs(app);
 	check_tms(app);
+	check_knis(app);
 	check_sources(app);
 	check_sinks(app);
 	check_msgqs(app);
diff --git a/examples/ip_pipeline/config_parse.c b/examples/ip_pipeline/config_parse.c
index 504018e..c55be31 100644
--- a/examples/ip_pipeline/config_parse.c
+++ b/examples/ip_pipeline/config_parse.c
@@ -189,6 +189,18 @@ struct app_pktq_tm_params default_tm_params = {
 	.burst_write = 32,
 };
 
+struct app_pktq_kni_params default_kni_params = {
+	.parsed = 0,
+	.socket_id = 0,
+	.core_id = 0,
+	.hyper_th_id = 0,
+	.force_bind = 0,
+
+	.mempool_id = 0,
+	.burst_read = 32,
+	.burst_write = 32,
+};
+
 struct app_pktq_source_params default_source_params = {
 	.parsed = 0,
 	.mempool_id = 0,
@@ -300,6 +312,18 @@ app_print_usage(char *prgname)
 	link_param_pos;							\
 })
 
+#define APP_PARAM_ADD_LINK_FOR_KNI(app, kni_name)			\
+({									\
+	char link_name[APP_PARAM_NAME_SIZE];				\
+	ssize_t link_param_pos;						\
+	uint32_t link_id;						\
+									\
+	sscanf((kni_name), "KNI%" SCNu32, &link_id);		\
+	sprintf(link_name, "LINK%" PRIu32, link_id);			\
+	link_param_pos = APP_PARAM_ADD((app)->link_params, link_name);	\
+	link_param_pos;							\
+})
+
 #define PARSE_CHECK_DUPLICATE_SECTION(obj)				\
 do {									\
 	APP_CHECK(((obj)->parsed == 0),					\
@@ -826,6 +850,10 @@ parse_pipeline_pktq_in(struct app_params *app,
 			type = APP_PKTQ_IN_TM;
 			id = APP_PARAM_ADD(app->tm_params, name);
 			APP_PARAM_ADD_LINK_FOR_TM(app, name);
+		} else if (validate_name(name, "KNI", 1) == 0) {
+			type = APP_PKTQ_IN_KNI;
+			id = APP_PARAM_ADD(app->kni_params, name);
+			APP_PARAM_ADD_LINK_FOR_KNI(app, name);
 		} else if (validate_name(name, "SOURCE", 1) == 0) {
 			type = APP_PKTQ_IN_SOURCE;
 			id = APP_PARAM_ADD(app->source_params, name);
@@ -871,6 +899,10 @@ parse_pipeline_pktq_out(struct app_params *app,
 			type = APP_PKTQ_OUT_TM;
 			id = APP_PARAM_ADD(app->tm_params, name);
 			APP_PARAM_ADD_LINK_FOR_TM(app, name);
+		} else if (validate_name(name, "KNI", 1) == 0) {
+			type = APP_PKTQ_OUT_KNI;
+			id = APP_PARAM_ADD(app->kni_params, name);
+			APP_PARAM_ADD_LINK_FOR_KNI(app, name);
 		} else if (validate_name(name, "SINK", 1) == 0) {
 			type = APP_PKTQ_OUT_SINK;
 			id = APP_PARAM_ADD(app->sink_params, name);
@@ -1816,7 +1848,7 @@ parse_tm(struct app_params *app,
 	param = &app->tm_params[param_idx];
 	PARSE_CHECK_DUPLICATE_SECTION(param);
 
-	APP_PARAM_ADD_LINK_FOR_TXQ(app, section_name);
+	APP_PARAM_ADD_LINK_FOR_TM(app, section_name);
 
 	for (i = 0; i < n_entries; i++) {
 		struct rte_cfgfile_entry *ent = &entries[i];
@@ -1853,6 +1885,85 @@ parse_tm(struct app_params *app,
 }
 
 static void
+parse_kni(struct app_params *app,
+		  const char *section_name,
+		  struct rte_cfgfile *cfg)
+{
+	struct app_pktq_kni_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->kni_params, section_name);
+	param = &app->kni_params[param_idx];
+	PARSE_CHECK_DUPLICATE_SECTION(param);
+
+	APP_PARAM_ADD_LINK_FOR_KNI(app, section_name);
+
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		if (strcmp(ent->name, "core") == 0) {
+			int status = parse_pipeline_core(
+					&param->socket_id,
+					&param->core_id,
+					&param->hyper_th_id,
+					ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+						ent->name);
+			param->force_bind = 1;
+			continue;
+		}
+
+		if (strcmp(ent->name, "mempool") == 0) {
+			int status = validate_name(ent->value,
+									   "MEMPOOL", 1);
+			ssize_t idx;
+
+			PARSE_ERROR((status == 0), section_name,
+						ent->name);
+
+			idx = APP_PARAM_ADD(app->mempool_params, ent->value);
+			param->mempool_id = idx;
+			continue;
+		}
+
+		if (strcmp(ent->name, "burst_read") == 0) {
+			int status = parser_read_uint32(&param->burst_read,
+											ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+						ent->name);
+			continue;
+		}
+
+		if (strcmp(ent->name, "burst_write") == 0) {
+			int status = parser_read_uint32(&param->burst_write,
+											ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+						ent->name);
+			continue;
+		}
+
+		/* unrecognized */
+		PARSE_ERROR_INVALID(0, section_name, ent->name);
+	}
+
+	free(entries);
+}
+
+static void
 parse_source(struct app_params *app,
 	const char *section_name,
 	struct rte_cfgfile *cfg)
@@ -2147,6 +2258,7 @@ static const struct config_section cfg_file_scheme[] = {
 	{"TXQ", 2, parse_txq},
 	{"SWQ", 1, parse_swq},
 	{"TM", 1, parse_tm},
+	{"KNI", 1, parse_kni},
 	{"SOURCE", 1, parse_source},
 	{"SINK", 1, parse_sink},
 	{"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
@@ -2285,6 +2397,7 @@ app_config_parse(struct app_params *app, const char *file_name)
 	APP_PARAM_COUNT(app->hwq_out_params, app->n_pktq_hwq_out);
 	APP_PARAM_COUNT(app->swq_params, app->n_pktq_swq);
 	APP_PARAM_COUNT(app->tm_params, app->n_pktq_tm);
+	APP_PARAM_COUNT(app->kni_params, app->n_pktq_kni);
 	APP_PARAM_COUNT(app->source_params, app->n_pktq_source);
 	APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
 	APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
@@ -2647,6 +2760,45 @@ save_tm_params(struct app_params *app, FILE *f)
 }
 
 static void
+save_kni_params(struct app_params *app, FILE *f)
+{
+	struct app_pktq_kni_params *p;
+	size_t i, count;
+
+	count = RTE_DIM(app->kni_params);
+	for (i = 0; i < count; i++) {
+		p = &app->kni_params[i];
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		/* section name */
+		fprintf(f, "[%s]\n", p->name);
+
+		/* core */
+		if (p->force_bind) {
+			fprintf(f, "; force_bind = 1\n");
+			fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
+					p->socket_id,
+					p->core_id,
+					(p->hyper_th_id) ? "h" : "");
+		} else
+			fprintf(f, "; force_bind = 0\n");
+
+		/* mempool */
+		fprintf(f, "%s = %s\n", "mempool",
+				app->mempool_params[p->mempool_id].name);
+
+		/* burst_read */
+		fprintf(f, "%s = %" PRIu32 "\n", "burst_read", p->burst_read);
+
+		/* burst_write */
+		fprintf(f, "%s = %" PRIu32 "\n", "burst_write", p->burst_write);
+
+		fputc('\n', f);
+	}
+}
+
+static void
 save_source_params(struct app_params *app, FILE *f)
 {
 	struct app_pktq_source_params *p;
@@ -2753,6 +2905,9 @@ save_pipeline_params(struct app_params *app, FILE *f)
 				case APP_PKTQ_IN_TM:
 					name = app->tm_params[pp->id].name;
 					break;
+				case APP_PKTQ_IN_KNI:
+					name = app->kni_params[pp->id].name;
+					break;
 				case APP_PKTQ_IN_SOURCE:
 					name = app->source_params[pp->id].name;
 					break;
@@ -2787,6 +2942,9 @@ save_pipeline_params(struct app_params *app, FILE *f)
 				case APP_PKTQ_OUT_TM:
 					name = app->tm_params[pp->id].name;
 					break;
+				case APP_PKTQ_OUT_KNI:
+					name = app->kni_params[pp->id].name;
+					break;
 				case APP_PKTQ_OUT_SINK:
 					name = app->sink_params[pp->id].name;
 					break;
@@ -2872,6 +3030,7 @@ app_config_save(struct app_params *app, const char *file_name)
 	save_txq_params(app, file);
 	save_swq_params(app, file);
 	save_tm_params(app, file);
+	save_kni_params(app, file);
 	save_source_params(app, file);
 	save_sink_params(app, file);
 	save_msgq_params(app, file);
@@ -2921,6 +3080,11 @@ app_config_init(struct app_params *app)
 			&default_tm_params,
 			sizeof(default_tm_params));
 
+	for (i = 0; i < RTE_DIM(app->kni_params); i++)
+		memcpy(&app->kni_params[i],
+			   &default_kni_params,
+			   sizeof(default_kni_params));
+
 	for (i = 0; i < RTE_DIM(app->source_params); i++)
 		memcpy(&app->source_params[i],
 			&default_source_params,
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
index 7120bab..d522de4 100644
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -1281,10 +1281,21 @@ void app_pipeline_params_get(struct app_params *app,
 			break;
 		}
 		case APP_PKTQ_IN_TM:
+		{
 			out->type = PIPELINE_PORT_IN_SCHED_READER;
 			out->params.sched.sched = app->tm[in->id];
 			out->burst_size = app->tm_params[in->id].burst_read;
 			break;
+		}
+#ifdef RTE_LIBRTE_KNI
+		case APP_PKTQ_IN_KNI:
+		{
+			out->type = PIPELINE_PORT_IN_KNI_READER;
+			out->params.kni.kni = app->kni[in->id];
+			out->burst_size = app->kni_params[in->id].burst_read;
+			break;
+		}
+#endif /* RTE_LIBRTE_KNI */
 		case APP_PKTQ_IN_SOURCE:
 		{
 			uint32_t mempool_id =
@@ -1409,7 +1420,8 @@ void app_pipeline_params_get(struct app_params *app,
 			}
 			break;
 		}
-		case APP_PKTQ_OUT_TM: {
+		case APP_PKTQ_OUT_TM:
+		{
 			struct rte_port_sched_writer_params *params =
 				&out->params.sched;
 
@@ -1419,6 +1431,16 @@ void app_pipeline_params_get(struct app_params *app,
 				app->tm_params[in->id].burst_write;
 			break;
 		}
+#ifdef RTE_LIBRTE_KNI
+		case APP_PKTQ_OUT_KNI:
+		{
+			out->type = PIPELINE_PORT_OUT_KNI_WRITER;
+			out->params.kni.kni = app->kni[in->id];
+			out->params.kni.tx_burst_sz =
+				app->kni_params[in->id].burst_write;
+			break;
+		}
+#endif /* RTE_LIBRTE_KNI */
 		case APP_PKTQ_OUT_SINK:
 		{
 			out->type = PIPELINE_PORT_OUT_SINK;
@@ -1452,6 +1474,113 @@ void app_pipeline_params_get(struct app_params *app,
 	}
 }
 
+#ifdef RTE_LIBRTE_KNI
+static int
+kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
+	int ret = 0;
+
+	if (port_id >= rte_eth_dev_count())
+		return -EINVAL;
+
+	if (if_up) {
+		rte_eth_dev_stop(port_id);
+		ret = rte_eth_dev_start(port_id);
+	} else
+		rte_eth_dev_stop(port_id);
+
+	return ret;
+}
+
+static int
+kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
+	int ret;
+
+	if (port_id >= rte_eth_dev_count())
+		return -EINVAL;
+
+	if (new_mtu > ETHER_MAX_LEN)
+		return -EINVAL;
+
+	/* Stop specific port */
+	rte_eth_dev_stop(port_id);
+
+	/* Set new MTU */
+	ret = rte_eth_dev_set_mtu(port_id, new_mtu);
+	if (ret < 0)
+		return ret;
+
+	/* Restart specific port */
+	ret = rte_eth_dev_start(port_id);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+#endif /* RTE_LIBRTE_KNI */
+
+static void
+app_init_kni(struct app_params *app) {
+	if (app->n_pktq_kni == 0)
+		return;
+#ifndef RTE_LIBRTE_KNI
+	else
+		rte_panic("Can not init KNI without librte_kni support.\n");
+#else
+	rte_kni_init(app->n_pktq_kni);
+
+	for (uint32_t i = 0; i < app->n_pktq_kni; i++) {
+		struct app_pktq_kni_params *p_kni = &app->kni_params[i];
+		struct app_link_params *p_link;
+		struct rte_eth_dev_info dev_info;
+		struct app_mempool_params *mempool_params;
+		struct rte_mempool *mempool;
+		struct rte_kni_conf conf;
+		struct rte_kni_ops ops;
+
+		/* LINK */
+		p_link = app_get_link_for_kni(app, p_kni);
+		memset(&dev_info, 0, sizeof(dev_info));
+		rte_eth_dev_info_get(p_link->pmd_id, &dev_info);
+
+		/* MEMPOOL */
+		mempool_params = &app->mempool_params[p_kni->mempool_id];
+		mempool = app->mempool[p_kni->mempool_id];
+
+		/* KNI */
+		memset(&conf, 0, sizeof(conf));
+		snprintf(conf.name, RTE_KNI_NAMESIZE, "%s", p_kni->name);
+		conf.force_bind = p_kni->force_bind;
+		if (conf.force_bind) {
+			int lcore_id;
+
+			lcore_id = cpu_core_map_get_lcore_id(app->core_map,
+				p_kni->socket_id,
+				p_kni->core_id,
+				p_kni->hyper_th_id);
+
+			if (lcore_id < 0)
+				rte_panic("%s invalid CPU core\n", p_kni->name);
+
+			conf.core_id = (uint32_t) lcore_id;
+		}
+		conf.group_id = p_link->pmd_id;
+		conf.mbuf_size = mempool_params->buffer_size;
+		conf.addr = dev_info.pci_dev->addr;
+		conf.id = dev_info.pci_dev->id;
+
+		memset(&ops, 0, sizeof(ops));
+		ops.port_id = (uint8_t) p_link->pmd_id;
+		ops.change_mtu = kni_change_mtu;
+		ops.config_network_if = kni_config_network_interface;
+
+		APP_LOG(app, HIGH, "Initializing %s ...", p_kni->name);
+		app->kni[i] = rte_kni_alloc(mempool, &conf, &ops);
+		if (!app->kni[i])
+			rte_panic("%s init error\n", p_kni->name);
+	}
+#endif /* RTE_LIBRTE_KNI */
+}
+
 static void
 app_init_pipelines(struct app_params *app)
 {
@@ -1607,6 +1736,7 @@ int app_init(struct app_params *app)
 	app_init_link(app);
 	app_init_swq(app);
 	app_init_tm(app);
+	app_init_kni(app);
 	app_init_msgq(app);
 
 	app_pipeline_common_cmd_push(app);
diff --git a/examples/ip_pipeline/pipeline/pipeline_common_fe.c b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
index 70c57e4..63723cd 100644
--- a/examples/ip_pipeline/pipeline/pipeline_common_fe.c
+++ b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
@@ -130,6 +130,35 @@ app_pipeline_track_pktq_out_to_link(struct app_params *app,
 			break;
 		}
 
+#ifdef RTE_LIBRTE_KNI
+		case APP_PKTQ_OUT_KNI:
+		{
+			struct pipeline_params pp;
+			struct pipeline_type *ptype;
+			struct app_pktq_kni_params *kni;
+			uint32_t pktq_in_id;
+			int status;
+
+			kni = &app->kni_params[pktq_out->id];
+			p = app_kni_get_reader(app, kni, &pktq_in_id);
+			if (p == NULL)
+				return NULL;
+
+			ptype = app_pipeline_type_find(app, p->type);
+			if ((ptype == NULL) || (ptype->fe_ops->f_track == NULL))
+				return NULL;
+
+			app_pipeline_params_get(app, p, &pp);
+			status = ptype->fe_ops->f_track(&pp,
+				pktq_in_id,
+				&pktq_out_id);
+			if (status)
+				return NULL;
+
+			break;
+		}
+#endif
+
 		case APP_PKTQ_OUT_SINK:
 		default:
 			return NULL;
diff --git a/examples/ip_pipeline/pipeline/pipeline_master_be.c b/examples/ip_pipeline/pipeline/pipeline_master_be.c
index 79869a4..32d4635 100644
--- a/examples/ip_pipeline/pipeline/pipeline_master_be.c
+++ b/examples/ip_pipeline/pipeline/pipeline_master_be.c
@@ -144,6 +144,12 @@ pipeline_run(void *pipeline)
 		rte_exit(0, "Bye!\n");
 	}
 
+#ifdef RTE_LIBRTE_KNI
+	/* Handle KNI requests from Linux kernel */
+	for (uint32_t i = 0; i < app->n_pktq_kni; i++)
+		rte_kni_handle_request(app->kni[i]);
+#endif /* RTE_LIBRTE_KNI */
+
 	return 0;
 }
 
diff --git a/examples/ip_pipeline/pipeline_be.h b/examples/ip_pipeline/pipeline_be.h
index 5501ab7..0ee208c 100644
--- a/examples/ip_pipeline/pipeline_be.h
+++ b/examples/ip_pipeline/pipeline_be.h
@@ -40,6 +40,9 @@
 #include <rte_port_ras.h>
 #include <rte_port_sched.h>
 #include <rte_port_source_sink.h>
+#ifdef RTE_LIBRTE_KNI
+#include <rte_port_kni.h>
+#endif
 #include <rte_pipeline.h>
 
 enum pipeline_port_in_type {
@@ -49,6 +52,7 @@ enum pipeline_port_in_type {
 	PIPELINE_PORT_IN_RING_READER_IPV4_FRAG,
 	PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
 	PIPELINE_PORT_IN_SCHED_READER,
+	PIPELINE_PORT_IN_KNI_READER,
 	PIPELINE_PORT_IN_SOURCE,
 };
 
@@ -61,6 +65,9 @@ struct pipeline_port_in_params {
 		struct rte_port_ring_reader_ipv4_frag_params ring_ipv4_frag;
 		struct rte_port_ring_reader_ipv6_frag_params ring_ipv6_frag;
 		struct rte_port_sched_reader_params sched;
+#ifdef RTE_LIBRTE_KNI
+		struct rte_port_kni_reader_params kni;
+#endif
 		struct rte_port_source_params source;
 	} params;
 	uint32_t burst_size;
@@ -82,6 +89,10 @@ pipeline_port_in_params_convert(struct pipeline_port_in_params  *p)
 		return (void *) &p->params.ring_ipv6_frag;
 	case PIPELINE_PORT_IN_SCHED_READER:
 		return (void *) &p->params.sched;
+#ifdef RTE_LIBRTE_KNI
+	case PIPELINE_PORT_IN_KNI_READER:
+		return (void *) &p->params.kni;
+#endif
 	case PIPELINE_PORT_IN_SOURCE:
 		return (void *) &p->params.source;
 	default:
@@ -105,6 +116,10 @@ pipeline_port_in_params_get_ops(struct pipeline_port_in_params  *p)
 		return &rte_port_ring_reader_ipv6_frag_ops;
 	case PIPELINE_PORT_IN_SCHED_READER:
 		return &rte_port_sched_reader_ops;
+#ifdef RTE_LIBRTE_KNI
+	case PIPELINE_PORT_IN_KNI_READER:
+		return &rte_port_kni_reader_ops;
+#endif
 	case PIPELINE_PORT_IN_SOURCE:
 		return &rte_port_source_ops;
 	default:
@@ -122,6 +137,7 @@ enum pipeline_port_out_type {
 	PIPELINE_PORT_OUT_RING_WRITER_IPV4_RAS,
 	PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
 	PIPELINE_PORT_OUT_SCHED_WRITER,
+	PIPELINE_PORT_OUT_KNI_WRITER,
 	PIPELINE_PORT_OUT_SINK,
 };
 
@@ -137,6 +153,9 @@ struct pipeline_port_out_params {
 		struct rte_port_ring_writer_ipv4_ras_params ring_ipv4_ras;
 		struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
 		struct rte_port_sched_writer_params sched;
+#ifdef RTE_LIBRTE_KNI
+		struct rte_port_kni_writer_params kni;
+#endif
 		struct rte_port_sink_params sink;
 	} params;
 };
@@ -163,6 +182,10 @@ pipeline_port_out_params_convert(struct pipeline_port_out_params  *p)
 		return (void *) &p->params.ring_ipv6_ras;
 	case PIPELINE_PORT_OUT_SCHED_WRITER:
 		return (void *) &p->params.sched;
+#ifdef RTE_LIBRTE_KNI
+	case PIPELINE_PORT_OUT_KNI_WRITER:
+		return (void *) &p->params.kni;
+#endif
 	case PIPELINE_PORT_OUT_SINK:
 		return (void *) &p->params.sink;
 	default:
@@ -192,6 +215,10 @@ pipeline_port_out_params_get_ops(struct pipeline_port_out_params  *p)
 		return &rte_port_ring_writer_ipv6_ras_ops;
 	case PIPELINE_PORT_OUT_SCHED_WRITER:
 		return &rte_port_sched_writer_ops;
+#ifdef RTE_LIBRTE_KNI
+	case PIPELINE_PORT_OUT_KNI_WRITER:
+		return &rte_port_kni_writer_ops;
+#endif
 	case PIPELINE_PORT_OUT_SINK:
 		return &rte_port_sink_ops;
 	default:
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index dc6a601..0fc929b 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -56,6 +56,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_frag.c
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
 endif
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
+endif
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
 
 # install includes
@@ -67,6 +70,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_frag.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_ras.h
 endif
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
+endif
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
 
 # this lib depends upon:
@@ -76,5 +82,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_sched
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/rte_port_kni.c b/lib/librte_port/rte_port_kni.c
new file mode 100644
index 0000000..4cbc345
--- /dev/null
+++ b/lib/librte_port/rte_port_kni.c
@@ -0,0 +1,325 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016 Ethan Zhuang <zhuangwj@gmail.com>.
+ *   Copyright(c) 2016 Intel Corporation.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_kni.h>
+
+#include "rte_port_kni.h"
+
+/*
+ * Port KNI Reader
+ */
+#ifdef RTE_PORT_STATS_COLLECT
+
+#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
+	port->stats.n_pkts_in += val
+#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
+	port->stats.n_pkts_drop += val
+
+#else
+
+#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
+#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
+
+#endif
+
+struct rte_port_kni_reader {
+	struct rte_port_in_stats stats;
+
+	struct rte_kni *kni;
+};
+
+static void *
+rte_port_kni_reader_create(void *params, int socket_id)
+{
+	struct rte_port_kni_reader_params *conf =
+			(struct rte_port_kni_reader_params *) params;
+	struct rte_port_kni_reader *port;
+
+	/* Check input parameters */
+	if (conf == NULL) {
+		RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	port = rte_zmalloc_socket("PORT", sizeof(*port),
+		RTE_CACHE_LINE_SIZE, socket_id);
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni = conf->kni;
+
+	return port;
+}
+
+static int
+rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t n_pkts)
+{
+	struct rte_port_kni_reader *p =
+			(struct rte_port_kni_reader *) port;
+	uint16_t rx_pkt_cnt;
+
+	rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
+	RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
+	return rx_pkt_cnt;
+}
+
+static int
+rte_port_kni_reader_free(void *port)
+{
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rte_free(port);
+
+	return 0;
+}
+
+static int rte_port_kni_reader_stats_read(void *port,
+	struct rte_port_in_stats *stats, int clear)
+{
+	struct rte_port_kni_reader *p =
+			(struct rte_port_kni_reader *) port;
+
+	if (stats != NULL)
+		memcpy(stats, &p->stats, sizeof(p->stats));
+
+	if (clear)
+		memset(&p->stats, 0, sizeof(p->stats));
+
+	return 0;
+}
+
+/*
+ * Port KNI Writer
+ */
+#ifdef RTE_PORT_STATS_COLLECT
+
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
+	port->stats.n_pkts_in += val
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
+	port->stats.n_pkts_drop += val
+
+#else
+
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
+
+#endif
+
+struct rte_port_kni_writer {
+	struct rte_port_out_stats stats;
+
+	struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
+	uint32_t tx_burst_sz;
+	uint32_t tx_buf_count;
+	uint64_t bsz_mask;
+	struct rte_kni *kni;
+};
+
+static void *
+rte_port_kni_writer_create(void *params, int socket_id)
+{
+	struct rte_port_kni_writer_params *conf =
+			(struct rte_port_kni_writer_params *) params;
+	struct rte_port_kni_writer *port;
+
+	/* Check input parameters */
+	if ((conf == NULL) ||
+		(conf->tx_burst_sz == 0) ||
+		(conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
+		(!rte_is_power_of_2(conf->tx_burst_sz))) {
+		RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n", __func__);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	port = rte_zmalloc_socket("PORT", sizeof(*port),
+		RTE_CACHE_LINE_SIZE, socket_id);
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni = conf->kni;
+	port->tx_burst_sz = conf->tx_burst_sz;
+	port->tx_buf_count = 0;
+	port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
+
+	return port;
+}
+
+static inline void
+send_burst(struct rte_port_kni_writer *p)
+{
+	uint32_t nb_tx;
+
+	nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
+
+	RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p->tx_buf_count - nb_tx);
+	for (; nb_tx < p->tx_buf_count; nb_tx++)
+		rte_pktmbuf_free(p->tx_buf[nb_tx]);
+
+	p->tx_buf_count = 0;
+}
+
+static int
+rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt)
+{
+	struct rte_port_kni_writer *p =
+			(struct rte_port_kni_writer *) port;
+
+	p->tx_buf[p->tx_buf_count++] = pkt;
+	RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+	if (p->tx_buf_count >= p->tx_burst_sz)
+		send_burst(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_tx_bulk(void *port,
+	struct rte_mbuf **pkts,
+	uint64_t pkts_mask)
+{
+	struct rte_port_kni_writer *p =
+			(struct rte_port_kni_writer *) port;
+	uint64_t bsz_mask = p->bsz_mask;
+	uint32_t tx_buf_count = p->tx_buf_count;
+	uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
+					((pkts_mask & bsz_mask) ^ bsz_mask);
+
+	if (expr == 0) {
+		uint64_t n_pkts = __builtin_popcountll(pkts_mask);
+		uint32_t n_pkts_ok;
+
+		if (tx_buf_count)
+			send_burst(p);
+
+		RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
+		n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
+
+		RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts - n_pkts_ok);
+		for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
+			struct rte_mbuf *pkt = pkts[n_pkts_ok];
+
+			rte_pktmbuf_free(pkt);
+		}
+	} else {
+		for (; pkts_mask;) {
+			uint32_t pkt_index = __builtin_ctzll(pkts_mask);
+			uint64_t pkt_mask = 1LLU << pkt_index;
+			struct rte_mbuf *pkt = pkts[pkt_index];
+
+			p->tx_buf[tx_buf_count++] = pkt;
+			RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+			pkts_mask &= ~pkt_mask;
+		}
+
+		p->tx_buf_count = tx_buf_count;
+		if (tx_buf_count >= p->tx_burst_sz)
+			send_burst(p);
+	}
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_flush(void *port)
+{
+	struct rte_port_kni_writer *p =
+			(struct rte_port_kni_writer *) port;
+
+	if (p->tx_buf_count > 0)
+		send_burst(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_free(void *port)
+{
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rte_port_kni_writer_flush(port);
+	rte_free(port);
+
+	return 0;
+}
+
+static int rte_port_kni_writer_stats_read(void *port,
+	struct rte_port_out_stats *stats, int clear)
+{
+	struct rte_port_kni_writer *p =
+			(struct rte_port_kni_writer *) port;
+
+	if (stats != NULL)
+		memcpy(stats, &p->stats, sizeof(p->stats));
+
+	if (clear)
+		memset(&p->stats, 0, sizeof(p->stats));
+
+	return 0;
+}
+
+/*
+ * Summary of port operations
+ */
+struct rte_port_in_ops rte_port_kni_reader_ops = {
+	.f_create = rte_port_kni_reader_create,
+	.f_free = rte_port_kni_reader_free,
+	.f_rx = rte_port_kni_reader_rx,
+	.f_stats = rte_port_kni_reader_stats_read,
+};
+
+struct rte_port_out_ops rte_port_kni_writer_ops = {
+	.f_create = rte_port_kni_writer_create,
+	.f_free = rte_port_kni_writer_free,
+	.f_tx = rte_port_kni_writer_tx,
+	.f_tx_bulk = rte_port_kni_writer_tx_bulk,
+	.f_flush = rte_port_kni_writer_flush,
+	.f_stats = rte_port_kni_writer_stats_read,
+};
diff --git a/lib/librte_port/rte_port_kni.h b/lib/librte_port/rte_port_kni.h
new file mode 100644
index 0000000..d4de8c4
--- /dev/null
+++ b/lib/librte_port/rte_port_kni.h
@@ -0,0 +1,82 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016 Ethan Zhuang <zhuangwj@gmail.com>.
+ *   Copyright(c) 2016 Intel Corporation.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_RTE_PORT_KNI_H__
+#define __INCLUDE_RTE_PORT_KNI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * RTE Port KNI Interface
+ *
+ * kni_reader: input port built on top of pre-initialized KNI interface
+ * kni_writer: output port built on top of pre-initialized KNI interface
+ *
+ ***/
+
+#include <stdint.h>
+
+#include <rte_kni.h>
+
+#include "rte_port.h"
+
+/** kni_reader port parameters */
+struct rte_port_kni_reader_params {
+	/** KNI interface reference */
+	struct rte_kni *kni;
+};
+
+/** kni_reader port operations */
+extern struct rte_port_in_ops rte_port_kni_reader_ops;
+
+
+/** kni_writer port parameters */
+struct rte_port_kni_writer_params {
+	/** KNI interface reference */
+	struct rte_kni *kni;
+	/** Burst size to KNI interface. */
+	uint32_t tx_burst_sz;
+};
+
+/** kni_writer port operations */
+extern struct rte_port_out_ops rte_port_kni_writer_ops;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/librte_port/rte_port_version.map b/lib/librte_port/rte_port_version.map
index 7a0b34d..e61b3fa 100644
--- a/lib/librte_port/rte_port_version.map
+++ b/lib/librte_port/rte_port_version.map
@@ -35,3 +35,11 @@ DPDK_2.2 {
 	rte_port_ring_multi_writer_nodrop_ops;
 
 } DPDK_2.1;
+
+DPDK_16.07 {
+	global:
+
+	rte_port_kni_reader_ops;
+	rte_port_kni_writer_ops;
+
+} DPDK_2.2;
-- 
2.7.4

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

* [PATCH v3 2/3] port: add kni nodrop writer
  2016-06-16 11:27   ` [PATCH v3 1/3] " WeiJie Zhuang
@ 2016-06-16 11:27     ` WeiJie Zhuang
  2016-06-18 21:47       ` Dumitrescu, Cristian
  2016-06-16 11:27     ` [PATCH v3 3/3] port: document update WeiJie Zhuang
  2016-06-18 16:44     ` [PATCH v3 1/3] port: add kni interface support Dumitrescu, Cristian
  2 siblings, 1 reply; 22+ messages in thread
From: WeiJie Zhuang @ 2016-06-16 11:27 UTC (permalink / raw)
  To: cristian.dumitrescu; +Cc: dev, jasvinder.singh, ferruh.yigit, WeiJie Zhuang

1. add no drop writing operations to the kni port
2. support dropless kni config in the ip pipeline sample application

Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
---
 examples/ip_pipeline/app.h           |   2 +
 examples/ip_pipeline/config_parse.c  |  31 ++++-
 examples/ip_pipeline/init.c          |  26 ++++-
 examples/ip_pipeline/pipeline_be.h   |   6 +
 lib/librte_port/rte_port_kni.c       | 220 +++++++++++++++++++++++++++++++++++
 lib/librte_port/rte_port_kni.h       |  13 +++
 lib/librte_port/rte_port_version.map |   1 +
 7 files changed, 292 insertions(+), 7 deletions(-)

diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
index abbd6d4..6a6fdd9 100644
--- a/examples/ip_pipeline/app.h
+++ b/examples/ip_pipeline/app.h
@@ -147,6 +147,8 @@ struct app_pktq_kni_params {
 	uint32_t mempool_id; /* Position in the app->mempool_params */
 	uint32_t burst_read;
 	uint32_t burst_write;
+	uint32_t dropless;
+	uint64_t n_retries;
 };
 
 #ifndef APP_FILE_NAME_SIZE
diff --git a/examples/ip_pipeline/config_parse.c b/examples/ip_pipeline/config_parse.c
index c55be31..31a50c2 100644
--- a/examples/ip_pipeline/config_parse.c
+++ b/examples/ip_pipeline/config_parse.c
@@ -199,6 +199,8 @@ struct app_pktq_kni_params default_kni_params = {
 	.mempool_id = 0,
 	.burst_read = 32,
 	.burst_write = 32,
+	.dropless = 0,
+	.n_retries = 0,
 };
 
 struct app_pktq_source_params default_source_params = {
@@ -1927,7 +1929,7 @@ parse_kni(struct app_params *app,
 
 		if (strcmp(ent->name, "mempool") == 0) {
 			int status = validate_name(ent->value,
-									   "MEMPOOL", 1);
+				"MEMPOOL", 1);
 			ssize_t idx;
 
 			PARSE_ERROR((status == 0), section_name,
@@ -1940,7 +1942,7 @@ parse_kni(struct app_params *app,
 
 		if (strcmp(ent->name, "burst_read") == 0) {
 			int status = parser_read_uint32(&param->burst_read,
-											ent->value);
+						ent->value);
 
 			PARSE_ERROR((status == 0), section_name,
 						ent->name);
@@ -1949,7 +1951,25 @@ parse_kni(struct app_params *app,
 
 		if (strcmp(ent->name, "burst_write") == 0) {
 			int status = parser_read_uint32(&param->burst_write,
-											ent->value);
+						ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+						ent->name);
+			continue;
+		}
+
+		if (strcmp(ent->name, "dropless") == 0) {
+			int status = parser_read_arg_bool(ent->value);
+
+			PARSE_ERROR((status != -EINVAL), section_name,
+						ent->name);
+			param->dropless = status;
+			continue;
+		}
+
+		if (strcmp(ent->name, "n_retries") == 0) {
+			int status = parser_read_uint64(&param->n_retries,
+						ent->value);
 
 			PARSE_ERROR((status == 0), section_name,
 						ent->name);
@@ -2794,6 +2814,11 @@ save_kni_params(struct app_params *app, FILE *f)
 		/* burst_write */
 		fprintf(f, "%s = %" PRIu32 "\n", "burst_write", p->burst_write);
 
+		/* dropless */
+		fprintf(f, "%s = %s\n",
+				"dropless",
+				p->dropless ? "yes" : "no");
+
 		fputc('\n', f);
 	}
 }
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
index d522de4..af24f52 100644
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -1434,10 +1434,28 @@ void app_pipeline_params_get(struct app_params *app,
 #ifdef RTE_LIBRTE_KNI
 		case APP_PKTQ_OUT_KNI:
 		{
-			out->type = PIPELINE_PORT_OUT_KNI_WRITER;
-			out->params.kni.kni = app->kni[in->id];
-			out->params.kni.tx_burst_sz =
-				app->kni_params[in->id].burst_write;
+			struct app_pktq_kni_params *p_kni =
+				&app->kni_params[in->id];
+
+			if (p_kni->dropless == 0) {
+				struct rte_port_kni_writer_params *params =
+					&out->params.kni;
+
+				out->type = PIPELINE_PORT_OUT_KNI_WRITER;
+				params->kni = app->kni[in->id];
+				params->tx_burst_sz =
+					app->kni_params[in->id].burst_write;
+			} else {
+				struct rte_port_kni_writer_nodrop_params
+					*params = &out->params.kni_nodrop;
+
+				out->type = PIPELINE_PORT_OUT_KNI_WRITER_NODROP;
+				params->kni = app->kni[in->id];
+				params->tx_burst_sz =
+					app->kni_params[in->id].burst_write;
+				params->n_retries =
+					app->kni_params[in->id].n_retries;
+			}
 			break;
 		}
 #endif /* RTE_LIBRTE_KNI */
diff --git a/examples/ip_pipeline/pipeline_be.h b/examples/ip_pipeline/pipeline_be.h
index 0ee208c..b562472 100644
--- a/examples/ip_pipeline/pipeline_be.h
+++ b/examples/ip_pipeline/pipeline_be.h
@@ -138,6 +138,7 @@ enum pipeline_port_out_type {
 	PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
 	PIPELINE_PORT_OUT_SCHED_WRITER,
 	PIPELINE_PORT_OUT_KNI_WRITER,
+	PIPELINE_PORT_OUT_KNI_WRITER_NODROP,
 	PIPELINE_PORT_OUT_SINK,
 };
 
@@ -155,6 +156,7 @@ struct pipeline_port_out_params {
 		struct rte_port_sched_writer_params sched;
 #ifdef RTE_LIBRTE_KNI
 		struct rte_port_kni_writer_params kni;
+		struct rte_port_kni_writer_nodrop_params kni_nodrop;
 #endif
 		struct rte_port_sink_params sink;
 	} params;
@@ -185,6 +187,8 @@ pipeline_port_out_params_convert(struct pipeline_port_out_params  *p)
 #ifdef RTE_LIBRTE_KNI
 	case PIPELINE_PORT_OUT_KNI_WRITER:
 		return (void *) &p->params.kni;
+	case PIPELINE_PORT_OUT_KNI_WRITER_NODROP:
+		return (void *) &p->params.kni_nodrop;
 #endif
 	case PIPELINE_PORT_OUT_SINK:
 		return (void *) &p->params.sink;
@@ -218,6 +222,8 @@ pipeline_port_out_params_get_ops(struct pipeline_port_out_params  *p)
 #ifdef RTE_LIBRTE_KNI
 	case PIPELINE_PORT_OUT_KNI_WRITER:
 		return &rte_port_kni_writer_ops;
+	case PIPELINE_PORT_OUT_KNI_WRITER_NODROP:
+		return &rte_port_kni_writer_nodrop_ops;
 #endif
 	case PIPELINE_PORT_OUT_SINK:
 		return &rte_port_sink_ops;
diff --git a/lib/librte_port/rte_port_kni.c b/lib/librte_port/rte_port_kni.c
index 4cbc345..08f4ac2 100644
--- a/lib/librte_port/rte_port_kni.c
+++ b/lib/librte_port/rte_port_kni.c
@@ -306,6 +306,217 @@ static int rte_port_kni_writer_stats_read(void *port,
 }
 
 /*
+ * Port KNI Writer Nodrop
+ */
+#ifdef RTE_PORT_STATS_COLLECT
+
+#define RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_IN_ADD(port, val) \
+	port->stats.n_pkts_in += val
+#define RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_DROP_ADD(port, val) \
+	port->stats.n_pkts_drop += val
+
+#else
+
+#define RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_IN_ADD(port, val)
+#define RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_DROP_ADD(port, val)
+
+#endif
+
+struct rte_port_kni_writer_nodrop {
+	struct rte_port_out_stats stats;
+
+	struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
+	uint32_t tx_burst_sz;
+	uint32_t tx_buf_count;
+	uint64_t bsz_mask;
+	uint64_t n_retries;
+	struct rte_kni *kni;
+};
+
+static void *
+rte_port_kni_writer_nodrop_create(void *params, int socket_id)
+{
+	struct rte_port_kni_writer_nodrop_params *conf =
+		(struct rte_port_kni_writer_nodrop_params *) params;
+	struct rte_port_kni_writer_nodrop *port;
+
+	/* Check input parameters */
+	if ((conf == NULL) ||
+		(conf->tx_burst_sz == 0) ||
+		(conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
+		(!rte_is_power_of_2(conf->tx_burst_sz))) {
+		RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n", __func__);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	port = rte_zmalloc_socket("PORT", sizeof(*port),
+		RTE_CACHE_LINE_SIZE, socket_id);
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni = conf->kni;
+	port->tx_burst_sz = conf->tx_burst_sz;
+	port->tx_buf_count = 0;
+	port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
+
+	/*
+	 * When n_retries is 0 it means that we should wait for every packet to
+	 * send no matter how many retries should it take. To limit number of
+	 * branches in fast path, we use UINT64_MAX instead of branching.
+	 */
+	port->n_retries = (conf->n_retries == 0) ? UINT64_MAX : conf->n_retries;
+
+	return port;
+}
+
+static inline void
+send_burst_nodrop(struct rte_port_kni_writer_nodrop *p)
+{
+	uint32_t nb_tx = 0, i;
+
+	nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
+
+	/* We sent all the packets in a first try */
+	if (nb_tx >= p->tx_buf_count) {
+		p->tx_buf_count = 0;
+		return;
+	}
+
+	for (i = 0; i < p->n_retries; i++) {
+		nb_tx += rte_kni_tx_burst(p->kni,
+			p->tx_buf + nb_tx,
+			p->tx_buf_count - nb_tx);
+
+		/* We sent all the packets in more than one try */
+		if (nb_tx >= p->tx_buf_count) {
+			p->tx_buf_count = 0;
+			return;
+		}
+	}
+
+	/* We didn't send the packets in maximum allowed attempts */
+	RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_DROP_ADD(p, p->tx_buf_count - nb_tx);
+	for ( ; nb_tx < p->tx_buf_count; nb_tx++)
+		rte_pktmbuf_free(p->tx_buf[nb_tx]);
+
+	p->tx_buf_count = 0;
+}
+
+static int
+rte_port_kni_writer_nodrop_tx(void *port, struct rte_mbuf *pkt)
+{
+	struct rte_port_kni_writer_nodrop *p =
+			(struct rte_port_kni_writer_nodrop *) port;
+
+	p->tx_buf[p->tx_buf_count++] = pkt;
+	RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+	if (p->tx_buf_count >= p->tx_burst_sz)
+		send_burst_nodrop(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_nodrop_tx_bulk(void *port,
+	struct rte_mbuf **pkts,
+	uint64_t pkts_mask)
+{
+	struct rte_port_kni_writer_nodrop *p =
+			(struct rte_port_kni_writer_nodrop *) port;
+
+	uint64_t bsz_mask = p->bsz_mask;
+	uint32_t tx_buf_count = p->tx_buf_count;
+	uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
+					((pkts_mask & bsz_mask) ^ bsz_mask);
+
+	if (expr == 0) {
+		uint64_t n_pkts = __builtin_popcountll(pkts_mask);
+		uint32_t n_pkts_ok;
+
+		if (tx_buf_count)
+			send_burst_nodrop(p);
+
+		RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_IN_ADD(p, n_pkts);
+		n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
+
+		if (n_pkts_ok >= n_pkts)
+			return 0;
+
+		/*
+		 * If we didn't manage to send all packets in single burst, move
+		 * remaining packets to the buffer and call send burst.
+		 */
+		for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
+			struct rte_mbuf *pkt = pkts[n_pkts_ok];
+			p->tx_buf[p->tx_buf_count++] = pkt;
+		}
+		send_burst_nodrop(p);
+	} else {
+		for ( ; pkts_mask; ) {
+			uint32_t pkt_index = __builtin_ctzll(pkts_mask);
+			uint64_t pkt_mask = 1LLU << pkt_index;
+			struct rte_mbuf *pkt = pkts[pkt_index];
+
+			p->tx_buf[tx_buf_count++] = pkt;
+			RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_IN_ADD(p, 1);
+			pkts_mask &= ~pkt_mask;
+		}
+
+		p->tx_buf_count = tx_buf_count;
+		if (tx_buf_count >= p->tx_burst_sz)
+			send_burst_nodrop(p);
+	}
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_nodrop_flush(void *port)
+{
+	struct rte_port_kni_writer_nodrop *p =
+		(struct rte_port_kni_writer_nodrop *) port;
+
+	if (p->tx_buf_count > 0)
+		send_burst_nodrop(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_nodrop_free(void *port)
+{
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rte_port_kni_writer_nodrop_flush(port);
+	rte_free(port);
+
+	return 0;
+}
+
+static int rte_port_kni_writer_nodrop_stats_read(void *port,
+	struct rte_port_out_stats *stats, int clear)
+{
+	struct rte_port_kni_writer_nodrop *p =
+			(struct rte_port_kni_writer_nodrop *) port;
+
+	if (stats != NULL)
+		memcpy(stats, &p->stats, sizeof(p->stats));
+
+	if (clear)
+		memset(&p->stats, 0, sizeof(p->stats));
+
+	return 0;
+}
+
+
+/*
  * Summary of port operations
  */
 struct rte_port_in_ops rte_port_kni_reader_ops = {
@@ -323,3 +534,12 @@ struct rte_port_out_ops rte_port_kni_writer_ops = {
 	.f_flush = rte_port_kni_writer_flush,
 	.f_stats = rte_port_kni_writer_stats_read,
 };
+
+struct rte_port_out_ops rte_port_kni_writer_nodrop_ops = {
+	.f_create = rte_port_kni_writer_nodrop_create,
+	.f_free = rte_port_kni_writer_nodrop_free,
+	.f_tx = rte_port_kni_writer_nodrop_tx,
+	.f_tx_bulk = rte_port_kni_writer_nodrop_tx_bulk,
+	.f_flush = rte_port_kni_writer_nodrop_flush,
+	.f_stats = rte_port_kni_writer_nodrop_stats_read,
+};
diff --git a/lib/librte_port/rte_port_kni.h b/lib/librte_port/rte_port_kni.h
index d4de8c4..4b60689 100644
--- a/lib/librte_port/rte_port_kni.h
+++ b/lib/librte_port/rte_port_kni.h
@@ -75,6 +75,19 @@ struct rte_port_kni_writer_params {
 /** kni_writer port operations */
 extern struct rte_port_out_ops rte_port_kni_writer_ops;
 
+/** kni_writer_nodrop port parameters */
+struct rte_port_kni_writer_nodrop_params {
+	/** KNI interface reference */
+	struct rte_kni *kni;
+	/** Burst size to KNI interface. */
+	uint32_t tx_burst_sz;
+	/** Maximum number of retries, 0 for no limit */
+	uint32_t n_retries;
+};
+
+/** kni_writer_nodrop port operations */
+extern struct rte_port_out_ops rte_port_kni_writer_nodrop_ops;
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_port/rte_port_version.map b/lib/librte_port/rte_port_version.map
index e61b3fa..c528658 100644
--- a/lib/librte_port/rte_port_version.map
+++ b/lib/librte_port/rte_port_version.map
@@ -41,5 +41,6 @@ DPDK_16.07 {
 
 	rte_port_kni_reader_ops;
 	rte_port_kni_writer_ops;
+	rte_port_kni_writer_nodrop_ops;
 
 } DPDK_2.2;
-- 
2.7.4

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

* [PATCH v3 3/3] port: document update
  2016-06-16 11:27   ` [PATCH v3 1/3] " WeiJie Zhuang
  2016-06-16 11:27     ` [PATCH v3 2/3] port: add kni nodrop writer WeiJie Zhuang
@ 2016-06-16 11:27     ` WeiJie Zhuang
  2016-06-18 16:44     ` [PATCH v3 1/3] port: add kni interface support Dumitrescu, Cristian
  2 siblings, 0 replies; 22+ messages in thread
From: WeiJie Zhuang @ 2016-06-16 11:27 UTC (permalink / raw)
  To: cristian.dumitrescu; +Cc: dev, jasvinder.singh, ferruh.yigit, WeiJie Zhuang

add kni configurations into the document of ip pipeline sample application

Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
---
 doc/guides/sample_app_ug/ip_pipeline.rst | 112 +++++++++++++++++++++++--------
 1 file changed, 83 insertions(+), 29 deletions(-)

diff --git a/doc/guides/sample_app_ug/ip_pipeline.rst b/doc/guides/sample_app_ug/ip_pipeline.rst
index 899fd4a..566106b 100644
--- a/doc/guides/sample_app_ug/ip_pipeline.rst
+++ b/doc/guides/sample_app_ug/ip_pipeline.rst
@@ -1,5 +1,5 @@
 ..  BSD LICENSE
-    Copyright(c) 2015 Intel Corporation. All rights reserved.
+    Copyright(c) 2016 Intel Corporation. All rights reserved.
     All rights reserved.
 
     Redistribution and use in source and binary forms, with or without
@@ -351,33 +351,35 @@ Application resources present in the configuration file
 
 .. table:: Application resource names in the configuration file
 
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Resource type            | Format                      | Examples                                        |
-   +==========================+=============================+=================================================+
-   | Pipeline                 | ``PIPELINE<ID>``            | ``PIPELINE0``, ``PIPELINE1``                    |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Mempool                  | ``MEMPOOL<ID>``             | ``MEMPOOL0``, ``MEMPOOL1``                      |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Link (network interface) | ``LINK<ID>``                | ``LINK0``, ``LINK1``                            |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Link RX queue            | ``RXQ<LINK_ID>.<QUEUE_ID>`` | ``RXQ0.0``, ``RXQ1.5``                          |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Link TX queue            | ``TXQ<LINK_ID>.<QUEUE_ID>`` | ``TXQ0.0``, ``TXQ1.5``                          |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Software queue           | ``SWQ<ID>``                 | ``SWQ0``, ``SWQ1``                              |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Traffic Manager          | ``TM<LINK_ID>``             | ``TM0``, ``TM1``                                |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Source                   | ``SOURCE<ID>``              | ``SOURCE0``, ``SOURCE1``                        |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Sink                     | ``SINK<ID>``                | ``SINK0``, ``SINK1``                            |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Message queue            | ``MSGQ<ID>``                | ``MSGQ0``, ``MSGQ1``,                           |
-   |                          | ``MSGQ-REQ-PIPELINE<ID>``   | ``MSGQ-REQ-PIPELINE2``, ``MSGQ-RSP-PIPELINE2,`` |
-   |                          | ``MSGQ-RSP-PIPELINE<ID>``   | ``MSGQ-REQ-CORE-s0c1``, ``MSGQ-RSP-CORE-s0c1``  |
-   |                          | ``MSGQ-REQ-CORE-<CORE_ID>`` |                                                 |
-   |                          | ``MSGQ-RSP-CORE-<CORE_ID>`` |                                                 |
-   +--------------------------+-----------------------------+-------------------------------------------------+
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Resource type              | Format                      | Examples                                        |
+   +============================+=============================+=================================================+
+   | Pipeline                   | ``PIPELINE<ID>``            | ``PIPELINE0``, ``PIPELINE1``                    |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Mempool                    | ``MEMPOOL<ID>``             | ``MEMPOOL0``, ``MEMPOOL1``                      |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Link (network interface)   | ``LINK<ID>``                | ``LINK0``, ``LINK1``                            |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Link RX queue              | ``RXQ<LINK_ID>.<QUEUE_ID>`` | ``RXQ0.0``, ``RXQ1.5``                          |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Link TX queue              | ``TXQ<LINK_ID>.<QUEUE_ID>`` | ``TXQ0.0``, ``TXQ1.5``                          |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Software queue             | ``SWQ<ID>``                 | ``SWQ0``, ``SWQ1``                              |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Traffic Manager            | ``TM<LINK_ID>``             | ``TM0``, ``TM1``                                |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | KNI (kernel NIC interface) | ``KNI<LINK_ID>``            | ``KNI0``, ``KNI1``                              |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Source                     | ``SOURCE<ID>``              | ``SOURCE0``, ``SOURCE1``                        |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Sink                       | ``SINK<ID>``                | ``SINK0``, ``SINK1``                            |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Message queue              | ``MSGQ<ID>``                | ``MSGQ0``, ``MSGQ1``,                           |
+   |                            | ``MSGQ-REQ-PIPELINE<ID>``   | ``MSGQ-REQ-PIPELINE2``, ``MSGQ-RSP-PIPELINE2,`` |
+   |                            | ``MSGQ-RSP-PIPELINE<ID>``   | ``MSGQ-REQ-CORE-s0c1``, ``MSGQ-RSP-CORE-s0c1``  |
+   |                            | ``MSGQ-REQ-CORE-<CORE_ID>`` |                                                 |
+   |                            | ``MSGQ-RSP-CORE-<CORE_ID>`` |                                                 |
+   +----------------------------+-----------------------------+-------------------------------------------------+
 
 ``LINK`` instances are created implicitly based on the ``PORT_MASK`` application startup argument.
 ``LINK0`` is the first port enabled in the ``PORT_MASK``, port 1 is the next one, etc.
@@ -386,7 +388,7 @@ For example, if bit 5 is the first bit set in the bitmask, then ``LINK0`` is hav
 This mechanism creates a contiguous LINK ID space and isolates the configuration file against changes in the board
 PCIe slots where NICs are plugged in.
 
-``RXQ``, ``TXQ`` and ``TM`` instances have the LINK ID as part of their name.
+``RXQ``, ``TXQ``, ``TM`` and ``KNI`` instances have the LINK ID as part of their name.
 For example, ``RXQ2.1``, ``TXQ2.1`` and ``TM2`` are all associated with ``LINK2``.
 
 
@@ -707,6 +709,58 @@ TM section
    +---------------+---------------------------------------------+----------+----------+---------------+
 
 
+KNI section
+~~~~~~~~~~~
+
+.. _table_ip_pipelines_kni_section:
+
+.. tabularcolumns:: |p{2.5cm}|p{7cm}|p{1.5cm}|p{1.5cm}|p{1.5cm}|
+
+.. table:: Configuration file KNI section
+
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+   | Section       | Description                                  | Optional | Type             | Default value |
+   +===============+==============================================+==========+==================+===============+
+   | core          | CPU core to run the KNI kernel thread.       | YES      | See "CPU Core    | Not set       |
+   |               | When core config is set, the KNI kernel      |          | notation"        |               |
+   |               | thread will be bound to the particular core. |          |                  |               |
+   |               | When core config is not set, the KNI kernel  |          |                  |               |
+   |               | thread will be scheduled by the OS.          |          |                  |               |
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+   | mempool       | Mempool to use for buffer allocation for     | YES      | uint32_t         | MEMPOOL0      |
+   |               | current KNI port. The mempool ID has         |          |                  |               |
+   |               | to be associated with a valid instance       |          |                  |               |
+   |               | defined in the mempool entry of the global   |          |                  |               |
+   |               | section.                                     |          |                  |               |
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+   | burst_read    | Read burst size (number of packets)          | YES      | uint32_t         | 32            |
+   |               |                                              |          | power of 2       |               |
+   |               |                                              |          | 0 < burst < size |               |
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+   | burst_write   | Write burst size (number of packets)         | YES      | uint32_t         | 32            |
+   |               |                                              |          | power of 2       |               |
+   |               |                                              |          | 0 < burst < size |               |
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+   | dropless      | When dropless is set to NO, packets can be   | YES      | YES/NO           | NO            |
+   |               | dropped if not enough free slots are         |          |                  |               |
+   |               | currently available in the queue, so the     |          |                  |               |
+   |               | write operation to the queue is non-         |          |                  |               |
+   |               | blocking.                                    |          |                  |               |
+   |               | When dropless is set to YES, packets cannot  |          |                  |               |
+   |               | be dropped if not enough free slots are      |          |                  |               |
+   |               | currently available in the queue, so the     |          |                  |               |
+   |               | write operation to the queue is blocking, as |          |                  |               |
+   |               | the write operation is retried until enough  |          |                  |               |
+   |               | free slots become available and all the      |          |                  |               |
+   |               | packets are successfully written to the      |          |                  |               |
+   |               | queue.                                       |          |                  |               |
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+   | n_retries     | Number of retries. Valid only when dropless  | YES      | uint64_t         | 0             |
+   |               | is set to YES. When set to 0, it indicates   |          |                  |               |
+   |               | unlimited number of retries.                 |          |                  |               |
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+
+
 SOURCE section
 ~~~~~~~~~~~~~~
 
-- 
2.7.4

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

* Re: [PATCH] port: add kni interface support
  2016-06-13 13:18       ` Dumitrescu, Cristian
@ 2016-06-16 11:34         ` Ethan
  0 siblings, 0 replies; 22+ messages in thread
From: Ethan @ 2016-06-16 11:34 UTC (permalink / raw)
  To: Dumitrescu, Cristian; +Cc: dev, Singh, Jasvinder, Yigit, Ferruh

Hi Cristian,

The new patch has been submitted just now. Please note that I do ignore
some check patch errors this time.

B.R.
Ethan


2016-06-13 21:18 GMT+08:00 Dumitrescu, Cristian <
cristian.dumitrescu@intel.com>:

> Hi Ethan,
>
>
>
> Great, we’ll wait for your patch later this week then. I recommend you add
> any other changes that you might have on top of the latest code that I just
> send, as this will minimize your work, my work to further code reviews and
> number of future iterations to merge this patch.
>
>
>
> Answers to your questions are inlined below.
>
>
>
> Regards,
>
> Cristian
>
>
>
> *From:* zhuangweijie@gmail.com [mailto:zhuangweijie@gmail.com] *On Behalf
> Of *Ethan
> *Sent:* Monday, June 13, 2016 11:48 AM
> *To:* Dumitrescu, Cristian <cristian.dumitrescu@intel.com>
> *Cc:* dev@dpdk.org; Singh, Jasvinder <jasvinder.singh@intel.com>; Yigit,
> Ferruh <ferruh.yigit@intel.com>
> *Subject:* Re: [PATCH] port: add kni interface support
>
>
>
> Hi Cristian,
>
>
>
> I've got your comments. Thank you for review the code from a DPDK newbie.
> :-)
>
> I plan to submit a new patch to fix all during this week hopefully.
>
>
>
> There are four places I'd like to discuss further:
>
>
>
> 1. Dedicated lcore for kni kernel thread
>
> First of all, it is a bug to add kni kernel core to the user space core
> mask. What I want is just to check if the kni kernel thread has a dedicated
> core.
>
> The reason I prefer to allocate a dedicated core to kni kernel thread is
> that my application is latency sensitive. I worry the context switch and
> cache miss will cause the latency increasing if the kni kernel thread and
> application thread share one core.
>
> Anyway, I think I should remove the hard coded check because it will be
> more generic. Users who has the similar usage like mine can achieve so
> through configuration file.
>
>
>
> [Cristian] I agree with you that the user should be able to specify the
> core where the kernel thread should run, and this requirement is fully met
> by the latest code I sent, but implemented in a slightly different way,
> which I think it is a cleaner way.
>
>
>
> In your initial solution, the application redefines the meaning of the
> core mask as the reunion of cores used by the user space application (cores
> running the pipelines) and the cores used to run the kernel space KNI
> threads. This does not make sense to me. The application is in user space
> and it does not start or manage any kernel threads itself, why should the
> application worry about the cores running kernel threads? The application
> should just pick up the user instructions from the config file and send
> them to the KNI kernel module transparently.
>
>
>
> In the code that I just sent, the application preserves the current
> definition of the core mask, i.e. just the collection of cores running the
> pipelines. This leads to simpler code that meets all the requirements for
> kernel threads affinity:
>
> i) The user wants to affinitize the kernel thread to a CPU core that is
> not used to run any pipeline (this core will run just KNI kernel threads):
> Core entry in KNI section is set to be different than the core entry of any
> PIPELINE section in the config file;
>
> ii) The user affinitizes the kernel thread to a CPU core that also runs
> some of the pipelines (this core will run both user space and kernel space
> threads): Core entry in KNI section is equal to the core entry in one or
> several of the PIPELINE sections in the config file;
>
> iii) The user does not affinitize the kernel thread to any CPU core, so
> the kernel decides the scheduling policy for the KNI threads: Core entry of
> the KNI section is not present; this results in force_bind KNI parameter to
> be set to 0.
>
>
>
> Makes sense?
>
>
>
> 2. The compiler error of the Macro RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD
>
> Actually I implements the macro similar
> to RTE_PORT_RING_READER_STATS_PKTS_IN_ADD first. But the
> scripts/checkpatches.sh fails: ERROR:COMPLEX_MACRO: Macros with complex
> values should be enclosed in parentheses
>
> I'm not share either I have done something wrong or the checkpatches
> script need an update.
>
>
>
> [Cristian] Let’s use the same consistent rule to create the stats macros
> for all the ports, i.e. follow the existing rule used for other ports. You
> can ignore this check patch issue.
>
>
>
> 3. KNI kernel operations callback
>
> To be  honest, I made reference to the the KNI sample application.
>
> Since there is very little docs tell the difference between link up call
> and device start call, I am not sure which one is better here.
>
> Any help will be appreciate. :-)
>
>
>
> [Cristian] I suggest you use the ones from the code that I just sent.
>
>
>
> 4. Shall I use DPDK_16.07 in the  librte_port/rte_port_version.map file?
>
>
>
> [Cristian] Yes.
>
>
>
>
>
> 2016-06-10 7:42 GMT+08:00 Dumitrescu, Cristian <
> cristian.dumitrescu@intel.com>:
>
> Hi Ethan,
>
> Great work! There are still several comments below that need to be
> addressed, but I am confident we can close on them quickly. Thank you!
>
> Please rebase the next version on top of the latest code on master branch.
>
> Please also update librte_port/rte_port_version.map file.
>
>
>
> Shall I use DPDK_16.07 in the  librte_port/rte_port_version.map file?
>
>
>
>
> > -----Original Message-----
> > From: WeiJie Zhuang [mailto:zhuangwj@gmail.com]
> > Sent: Saturday, May 28, 2016 12:26 PM
> > To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>
> > Cc: dev@dpdk.org; WeiJie Zhuang <zhuangwj@gmail.com>
> > Subject: [PATCH] port: add kni interface support
> >
> > 1. add KNI port type to the packet framework
> > 2. add KNI support to the IP Pipeline sample Application
> >
> > Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
> > ---
> > v2:
> > * Fix check patch error.
> > ---
> >  doc/api/doxy-api-index.md           |   1 +
> >  examples/ip_pipeline/Makefile       |   6 +-
> >  examples/ip_pipeline/app.h          |  74 +++++++++
> >  examples/ip_pipeline/config/kni.cfg |  12 ++
> >  examples/ip_pipeline/config_check.c |  34 ++++
> >  examples/ip_pipeline/config_parse.c | 130 +++++++++++++++
> >  examples/ip_pipeline/init.c         |  79 +++++++++
> >  examples/ip_pipeline/kni/kni.c      |  80 +++++++++
> >  examples/ip_pipeline/kni/kni.h      |  16 ++
> >  examples/ip_pipeline/pipeline_be.h  |  13 ++
> >  examples/ip_pipeline/thread.c       |   9 +
> >  lib/librte_port/Makefile            |   7 +
> >  lib/librte_port/rte_port_kni.c      | 316
> > ++++++++++++++++++++++++++++++++++++
> >  lib/librte_port/rte_port_kni.h      |  81 +++++++++
> >  14 files changed, 856 insertions(+), 2 deletions(-)
> >  create mode 100644 examples/ip_pipeline/config/kni.cfg
> >  create mode 100644 examples/ip_pipeline/kni/kni.c
> >  create mode 100644 examples/ip_pipeline/kni/kni.h
> >  create mode 100644 lib/librte_port/rte_port_kni.c
> >  create mode 100644 lib/librte_port/rte_port_kni.h
> >
> > diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> > index f626386..e38a959 100644
> > --- a/doc/api/doxy-api-index.md
> > +++ b/doc/api/doxy-api-index.md
> > @@ -119,6 +119,7 @@ There are many libraries, so their headers may be
> > grouped by topics:
> >      [reass]            (@ref rte_port_ras.h),
> >      [sched]            (@ref rte_port_sched.h),
> >      [src/sink]         (@ref rte_port_source_sink.h)
> > +    [kni]              (@ref rte_port_kni.h)
> >    * [table]            (@ref rte_table.h):
> >      [lpm IPv4]         (@ref rte_table_lpm.h),
> >      [lpm IPv6]         (@ref rte_table_lpm_ipv6.h),
> > diff --git a/examples/ip_pipeline/Makefile
> b/examples/ip_pipeline/Makefile
> > index 10fe1ba..848c2aa 100644
> > --- a/examples/ip_pipeline/Makefile
> > +++ b/examples/ip_pipeline/Makefile
> > @@ -43,9 +43,10 @@ include $(RTE_SDK)/mk/rte.vars.mk
> >  # binary name
> >  APP = ip_pipeline
> >
> > +VPATH += $(SRCDIR)/kni
> >  VPATH += $(SRCDIR)/pipeline
> >
> > -INC += $(wildcard *.h) $(wildcard pipeline/*.h)
> > +INC += $(wildcard *.h) $(wildcard pipeline/*.h) $(wildcard kni/*.h)
> >
> >  # all source are stored in SRCS-y
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) := main.c
> > @@ -56,6 +57,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread_fe.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
> > +SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += kni.c
> >
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_be.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_fe.c
> > @@ -72,7 +74,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) +=
> > pipeline_flow_actions.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing_be.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing.c
> >
> > -CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline
> > +CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline -I$(SRCDIR)/kni
> >  CFLAGS += -O3
> >  CFLAGS += $(WERROR_FLAGS) -Wno-error=unused-function -Wno-
> > error=unused-variable
> >
>
> I would like to avoid creating the kni subfolder. Please move the
> functions from kni/kni.c to init.c file, just before function
> app_init_kni(), where they should be declared as static functions.
>
> > diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
> > index e775024..a86ce57 100644
> > --- a/examples/ip_pipeline/app.h
> > +++ b/examples/ip_pipeline/app.h
> > @@ -44,7 +44,9 @@
> >  #include <cmdline_parse.h>
> >
> >  #include <rte_ethdev.h>
> > +#include <rte_kni.h>
> >
> > +#include "kni.h"
> >  #include "cpu_core_map.h"
> >  #include "pipeline.h"
> >
> > @@ -99,6 +101,18 @@ struct app_pktq_hwq_out_params {
> >       struct rte_eth_txconf conf;
> >  };
> >
> > +struct app_kni_params {
> > +     char *name;
> > +     uint32_t parsed;
> > +
> > +     uint32_t socket_id;
> > +     uint32_t core_id;
> > +     uint32_t hyper_th_id;
> > +
> > +     uint32_t mempool_id;
>
> Please add the usual comment on the same line: /* Position in the
> app->mempool_params */
>
> > +     uint32_t burst;
>
> Why having a unified value for read and write burst size? Please use
> burst_read and burst_write (both uint32_t) instead and update the
> config_parse.c and init.c code accordingly (small changes).
>
>
> > +};
> > +
> >  struct app_pktq_swq_params {
> >       char *name;
> >       uint32_t parsed;
> > @@ -172,6 +186,7 @@ enum app_pktq_in_type {
> >       APP_PKTQ_IN_SWQ,
> >       APP_PKTQ_IN_TM,
> >       APP_PKTQ_IN_SOURCE,
> > +     APP_PKTQ_IN_KNI,
> >  };
> >
> >  struct app_pktq_in_params {
> > @@ -184,6 +199,7 @@ enum app_pktq_out_type {
> >       APP_PKTQ_OUT_SWQ,
> >       APP_PKTQ_OUT_TM,
> >       APP_PKTQ_OUT_SINK,
> > +     APP_PKTQ_OUT_KNI,
> >  };
> >
> >  struct app_pktq_out_params {
> > @@ -434,6 +450,10 @@ struct app_eal_params {
> >  #define APP_THREAD_HEADROOM_STATS_COLLECT        1
> >  #endif
> >
> > +#ifndef APP_MAX_KNI
> > +#define APP_MAX_KNI                              8
> > +#endif
> > +
> >  struct app_params {
> >       /* Config */
> >       char app_name[APP_APPNAME_SIZE];
> > @@ -457,6 +477,7 @@ struct app_params {
> >       struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
> >       struct app_msgq_params msgq_params[APP_MAX_MSGQ];
> >       struct app_pipeline_params pipeline_params[APP_MAX_PIPELINES];
> > +     struct app_kni_params kni_params[APP_MAX_KNI];
> >
> >       uint32_t n_mempools;
> >       uint32_t n_links;
> > @@ -468,6 +489,7 @@ struct app_params {
> >       uint32_t n_pktq_sink;
> >       uint32_t n_msgq;
> >       uint32_t n_pipelines;
> > +     uint32_t n_kni;
> >
> >       /* Init */
> >       char *eal_argv[1 + APP_EAL_ARGC];
> > @@ -480,6 +502,7 @@ struct app_params {
> >       struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
> >       struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
> >       struct app_thread_data thread_data[APP_MAX_THREADS];
> > +     struct rte_kni *kni[APP_MAX_KNI];
> >       cmdline_parse_ctx_t cmds[APP_MAX_CMDS + 1];
> >
> >       int eal_argc;
> > @@ -738,6 +761,31 @@ app_msgq_get_readers(struct app_params *app,
> > struct app_msgq_params *msgq)
> >  }
> >
> >  static inline uint32_t
> > +app_kni_get_readers(struct app_params *app, struct app_kni_params
> > *kni)
> > +{
> > +     uint32_t pos = kni - app->kni_params;
> > +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> > +             RTE_DIM(app->pipeline_params));
> > +     uint32_t n_readers = 0, i;
> > +
> > +     for (i = 0; i < n_pipelines; i++) {
> > +             struct app_pipeline_params *p = &app->pipeline_params[i];
> > +             uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p-
> > >pktq_in));
> > +             uint32_t j;
> > +
> > +             for (j = 0; j < n_pktq_in; j++) {
> > +                     struct app_pktq_in_params *pktq = &p->pktq_in[j];
> > +
> > +                     if ((pktq->type == APP_PKTQ_IN_KNI) &&
> > +                             (pktq->id == pos))
> > +                             n_readers++;
> > +             }
> > +     }
> > +
> > +     return n_readers;
> > +}
> > +
> > +static inline uint32_t
> >  app_txq_get_writers(struct app_params *app, struct
> > app_pktq_hwq_out_params *txq)
> >  {
> >       uint32_t pos = txq - app->hwq_out_params;
> > @@ -863,6 +911,32 @@ app_msgq_get_writers(struct app_params *app,
> > struct app_msgq_params *msgq)
> >       return n_writers;
> >  }
> >
> > +static inline uint32_t
> > +app_kni_get_writers(struct app_params *app, struct app_kni_params *kni)
> > +{
> > +     uint32_t pos = kni - app->kni_params;
> > +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> > +             RTE_DIM(app->pipeline_params));
> > +     uint32_t n_writers = 0, i;
> > +
> > +     for (i = 0; i < n_pipelines; i++) {
> > +             struct app_pipeline_params *p = &app->pipeline_params[i];
> > +             uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
> > +                     RTE_DIM(p->pktq_out));
> > +             uint32_t j;
> > +
> > +             for (j = 0; j < n_pktq_out; j++) {
> > +                     struct app_pktq_out_params *pktq = &p-
> > >pktq_out[j];
> > +
> > +                     if ((pktq->type == APP_PKTQ_OUT_KNI) &&
> > +                             (pktq->id == pos))
> > +                             n_writers++;
> > +             }
> > +     }
> > +
> > +     return n_writers;
> > +}
> > +
> >  static inline struct app_link_params *
> >  app_get_link_for_rxq(struct app_params *app, struct
> > app_pktq_hwq_in_params *p)
> >  {
> > diff --git a/examples/ip_pipeline/config/kni.cfg
> > b/examples/ip_pipeline/config/kni.cfg
> > new file mode 100644
> > index 0000000..30466b0
> > --- /dev/null
> > +++ b/examples/ip_pipeline/config/kni.cfg
> > @@ -0,0 +1,12 @@
> > +[KNI0]
> > +core = 2
> > +
> > +[PIPELINE0]
> > +type = MASTER
> > +core = 0
> > +
> > +[PIPELINE1]
> > +type = PASS-THROUGH
> > +core = 1
> > +pktq_in = RXQ0.0 KNI0
> > +pktq_out = KNI0 TXQ0.0
>
> I don't think this new config file config/kni.cfg is really useful, so I
> would like to remove it. We should avoid the proliferation of
> straightforward config files.
>
>
> > diff --git a/examples/ip_pipeline/config_check.c
> > b/examples/ip_pipeline/config_check.c
> > index fd9ff49..3e300f9 100644
> > --- a/examples/ip_pipeline/config_check.c
> > +++ b/examples/ip_pipeline/config_check.c
> > @@ -426,6 +426,39 @@ check_pipelines(struct app_params *app)
> >       }
> >  }
> >
> > +static void
> > +check_kni(struct app_params *app) {
> > +     uint32_t i;
> > +     uint32_t port_id;
> > +
> > +     for (i = 0; i < app->n_kni; i++) {
> > +             struct app_kni_params *p = &app->kni_params[i];
> > +             uint32_t n_readers = app_kni_get_readers(app, p);
> > +             uint32_t n_writers = app_kni_get_writers(app, p);
> > +
> > +             APP_CHECK((n_readers != 0),
> > +                               "%s has no reader\n", p->name);
> > +
> > +             if (n_readers > 1)
> > +                     APP_LOG(app, LOW,
> > +                                     "%s has more than one reader", p-
> > >name);
> > +
> > +             APP_CHECK((n_writers != 0),
> > +                               "%s has no writer\n", p->name);
> > +
> > +             if (n_writers > 1)
> > +                     APP_LOG(app, LOW,
> > +                                     "%s has more than one writer", p-
> > >name);
> > +
>
> We should remove the next two checks. The reason is that in the latest
> code in config_parse.c (already merged on master branch), we automatically
> add LINKx for every object associated with it , such as RXQx.y, TXQx.y,
> TMx. This is the same for KNI, as KNIx is associated with LINKx. As also
> commented below, we should implement this in config_parse.c. Basically, due
> to this change in config_parse.c, it is always guaranteed that for every
> KNIx object, the LINKx object exists as well, so no need to check this here
> in config_check.c or in init.c.
>
>
> > +             APP_CHECK(sscanf(p->name, "KNI%" PRIu32, &port_id) == 1,
> > +                               "%s's port id is invalid\n", p->name);
> > +
> > +             APP_CHECK(port_id < app->n_links,
> > +                               "kni %s is not associated with a valid
> link\n",
> > +                               p->name);
> > +     }
> > +}
> > +
> >  int
> >  app_config_check(struct app_params *app)
> >  {
> > @@ -439,6 +472,7 @@ app_config_check(struct app_params *app)
> >       check_sinks(app);
> >       check_msgqs(app);
> >       check_pipelines(app);
> > +     check_kni(app);
> >
> >       return 0;
> >  }
> > diff --git a/examples/ip_pipeline/config_parse.c
> > b/examples/ip_pipeline/config_parse.c
> > index e5efd03..e9cd5a4 100644
> > --- a/examples/ip_pipeline/config_parse.c
> > +++ b/examples/ip_pipeline/config_parse.c
> > @@ -209,6 +209,15 @@ struct app_pipeline_params
> > default_pipeline_params = {
> >       .n_args = 0,
> >  };
> >
> > +struct app_kni_params default_kni_params = {
> > +     .parsed = 0,
> > +     .socket_id = 0,
> > +     .core_id = 0,
> > +     .hyper_th_id = 0,
> > +     .mempool_id = 0,
> > +     .burst = 32,
>
> .burst_read = 32,
> .burst_write = 32,
>
>
> > +};
> > +
> >  static const char app_usage[] =
> >       "Usage: %s [-f CONFIG_FILE] [-s SCRIPT_FILE] [-p PORT_MASK] "
> >       "[-l LOG_LEVEL] [--preproc PREPROCESSOR] [--preproc-args
> > ARGS]\n"
> > @@ -1169,6 +1178,9 @@ parse_pipeline_pktq_in(struct app_params *app,
> >               } else if (validate_name(name, "SOURCE", 1) == 0) {
> >                       type = APP_PKTQ_IN_SOURCE;
> >                       id = APP_PARAM_ADD(app->source_params, name);
> > +             } else if (validate_name(name, "KNI", 1) == 0) {
> > +                     type = APP_PKTQ_IN_KNI;
> > +                     id = APP_PARAM_ADD(app->kni_params, name);
> >               } else
> >                       return -EINVAL;
> >
> > @@ -1240,6 +1252,9 @@ parse_pipeline_pktq_out(struct app_params *app,
> >               } else if (validate_name(name, "SINK", 1) == 0) {
> >                       type = APP_PKTQ_OUT_SINK;
> >                       id = APP_PARAM_ADD(app->sink_params, name);
> > +             } else if (validate_name(name, "KNI", 1) == 0) {
> > +                     type = APP_PKTQ_OUT_KNI;
> > +                     id = APP_PARAM_ADD(app->kni_params, name);
> >               } else
> >                       return -EINVAL;
> >
> > @@ -2459,6 +2474,76 @@ parse_msgq(struct app_params *app,
> >       free(entries);
> >  }
> >
>
> Please rework the below parse_kni() function based on the latest code
> (rebase). For example, the PARSER_PARAM_ADD_CHECK macro has been removed.
>
> Also, as mentioned above, please add LINKx automatically for KNIx, same as
> the latest code adds LINKx automatically every time RXQx.y, TXQx.y, TMx
> objects are met. This has to be done in several places: once here, in
> function parse_kni(), which is executed for KNI sections, but also in
> parse_pipeline_pktq_in() and parse_pipeline_pktq_out() functions (please
> check latest code).
>
>
> > +static void
> > +parse_kni(struct app_params *app,
> > +                const char *section_name,
> > +                struct rte_cfgfile *cfg)
> > +{
> > +     struct app_kni_params *param;
> > +     struct rte_cfgfile_entry *entries;
> > +     int n_entries, i;
> > +     ssize_t param_idx;
> > +
> > +     n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
> > +     PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0),
> > section_name);
> > +
> > +     entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
> > +     PARSE_ERROR_MALLOC(entries != NULL);
> > +
> > +     rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
> > +
> > +     param_idx = APP_PARAM_ADD(app->kni_params, section_name);
> > +     PARSER_PARAM_ADD_CHECK(param_idx, app->kni_params,
> > section_name);
> > +
> > +     param = &app->kni_params[param_idx];
> > +
> > +     for (i = 0; i < n_entries; i++) {
> > +             struct rte_cfgfile_entry *ent = &entries[i];
> > +
> > +             if (strcmp(ent->name, "core") == 0) {
> > +                     int status = parse_pipeline_core(
> > +                             &param->socket_id, &param->core_id,
> > +                             &param->hyper_th_id, ent->value);
> > +
> > +                     PARSE_ERROR((status == 0), section_name,
> > +                             ent->name);
> > +                     continue;
> > +             }
> > +
> > +             if (strcmp(ent->name, "mempool") == 0) {
> > +                     int status = validate_name(ent->value,
> > +                             "MEMPOOL", 1);
> > +                     ssize_t idx;
> > +
> > +                     PARSE_ERROR((status == 0), section_name,
> > +                             ent->name);
> > +                     idx = APP_PARAM_ADD(app->mempool_params,
> > +                             ent->value);
> > +                     PARSER_PARAM_ADD_CHECK(idx,
> > +                             app->mempool_params,
> > +                             section_name);
> > +                     param->mempool_id = idx;
> > +                     continue;
> > +             }
> > +
> > +             if (strcmp(ent->name, "burst") == 0) {
> > +                     int status = parser_read_uint32(&param->burst,
> > +                             ent->value);
> > +
> > +                     PARSE_ERROR((status == 0), section_name,
> > +                             ent->name);
> > +                     continue;
> > +             }
>
> As discussed above, we should parse two different entries in KNI section:
> burst_read and burst_write.
>
>
> > +
> > +             /* unrecognized */
> > +             PARSE_ERROR_INVALID(0, section_name, ent->name);
> > +     }
> > +
> > +     param->parsed = 1;
> > +
> > +     free(entries);
> > +}
> > +
> >  typedef void (*config_section_load)(struct app_params *p,
> >       const char *section_name,
> >       struct rte_cfgfile *cfg);
> > @@ -2483,6 +2568,7 @@ static const struct config_section
> cfg_file_scheme[]
> > = {
> >       {"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
> >       {"MSGQ-RSP-PIPELINE", 1, parse_msgq_rsp_pipeline},
> >       {"MSGQ", 1, parse_msgq},
> > +     {"KNI", 1, parse_kni},
> >  };
> >
> >  static void
> > @@ -2619,6 +2705,7 @@ app_config_parse(struct app_params *app, const
> > char *file_name)
> >       APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
> >       APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
> >       APP_PARAM_COUNT(app->pipeline_params, app->n_pipelines);
> > +     APP_PARAM_COUNT(app->kni_params, app->n_kni);
> >
> >  #ifdef RTE_PORT_PCAP
> >       for (i = 0; i < (int)app->n_pktq_source; i++) {
> > @@ -3025,6 +3112,9 @@ save_pipeline_params(struct app_params *app,
> > FILE *f)
> >                               case APP_PKTQ_IN_SOURCE:
> >                                       name = app->source_params[pp-
> > >id].name;
> >                                       break;
> > +                             case APP_PKTQ_IN_KNI:
> > +                                     name = app->kni_params[pp-
> > >id].name;
> > +                                     break;
> >                               default:
> >                                       APP_CHECK(0, "System error "
> >                                               "occurred while saving "
> > @@ -3059,6 +3149,9 @@ save_pipeline_params(struct app_params *app,
> > FILE *f)
> >                               case APP_PKTQ_OUT_SINK:
> >                                       name = app->sink_params[pp-
> > >id].name;
> >                                       break;
> > +                             case APP_PKTQ_OUT_KNI:
> > +                                     name = app->kni_params[pp-
> > >id].name;
> > +                                     break;
> >                               default:
> >                                       APP_CHECK(0, "System error "
> >                                               "occurred while saving "
> > @@ -3114,6 +3207,37 @@ save_pipeline_params(struct app_params *app,
> > FILE *f)
> >       }
> >  }
> >
> > +static void
> > +save_kni_params(struct app_params *app, FILE *f)
> > +{
> > +     struct app_kni_params *p;
> > +     size_t i, count;
> > +
> > +     count = RTE_DIM(app->kni_params);
> > +     for (i = 0; i < count; i++) {
> > +             p = &app->kni_params[i];
> > +             if (!APP_PARAM_VALID(p))
> > +                     continue;
> > +
> > +             /* section name */
> > +             fprintf(f, "[%s]\n", p->name);
> > +
> > +             /* core */
> > +             fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
> > +                             p->socket_id,
> > +                             p->core_id,
> > +                             (p->hyper_th_id) ? "h" : "");
> > +
> > +             /* mempool */
> > +             fprintf(f, "%s = %" PRIu32 "\n", "mempool_id", p-
> > >mempool_id);
>
> The name of the entry is "mempool" instead of "mempool_id". The value of
> the entry is app->mempool_params[p->mempool_id].name (which is a string)
> instead of p->mempool_id (which is a number).
>
> > +
> > +             /* burst */
> > +             fprintf(f, "%s = %" PRIu32 "\n", "burst", p->burst);
> > +
>
> We should save both p->burst_read and p->burst_write.
>
>
> > +             fputc('\n', f);
> > +     }
> > +}
> > +
> >  void
> >  app_config_save(struct app_params *app, const char *file_name)
> >  {
> > @@ -3144,6 +3268,7 @@ app_config_save(struct app_params *app, const
> > char *file_name)
> >       save_source_params(app, file);
> >       save_sink_params(app, file);
> >       save_msgq_params(app, file);
> > +     save_kni_params(app, file);
> >
> >       fclose(file);
> >       free(name);
> > @@ -3206,6 +3331,11 @@ app_config_init(struct app_params *app)
> >                       &default_pipeline_params,
> >                       sizeof(default_pipeline_params));
> >
> > +     for (i = 0; i < RTE_DIM(app->kni_params); i++)
> > +             memcpy(&app->kni_params[i],
> > +                     &default_kni_params,
> > +                     sizeof(default_kni_params));
> > +
> >       return 0;
> >  }
> >
> > diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
> > index 02351f6..8ff9118 100644
> > --- a/examples/ip_pipeline/init.c
> > +++ b/examples/ip_pipeline/init.c
>
> This is run-time code, let's have all the KNI code in init.c file enabled
> only when KNI library is part of the build:
> #ifdef RTE_LIBRTE_KNI
> ...
> #endif /* RTE_LIBRTE_KNI */
>
> > @@ -89,6 +89,24 @@ app_init_core_mask(struct app_params *app)
> >               mask |= 1LLU << lcore_id;
> >       }
> >
> > +     for (i = 0; i < app->n_kni; i++) {
> > +             struct app_kni_params *p = &app->kni_params[i];
> > +             int lcore_id;
> > +
> > +             lcore_id = cpu_core_map_get_lcore_id(app->core_map,
> > +                     p->socket_id,
> > +                     p->core_id,
> > +                     p->hyper_th_id);
> > +
> > +             if (lcore_id < 0)
> > +                     rte_panic("Cannot create CPU core mask\n");
> > +
> > +             if (mask & 1LLU << lcore_id)
>
> Please use parenthesis for improved readability: if (mask & (1LLU <<
> lcore_id)).
>
> > +                     rte_panic("KNI interface must use a dedicated
> > lcore\n");
>
> The bigger questions are:
> - Why do we need to dedicate separate CPU core(s) for KNI interface(s)?
> Isn't KNI code running in kernel space, why do we need to dedicate separate
> user space core for it and why do we need to add this core to the
> user-space application core mask?
> - Even if we need to add this to the core mask (maybe I am missing
> something here ...), why do we need to dedicate this core entirely to KNI?
> Can't we have KNI (kernel) code sharing this core with user-space
> application code (e.g. some pipeline instances?)
>
>
>
> First of all, it is a bug to add KNI kernel core to the user space core
> mask. What I want is just to check if the KNI kernel thread has a dedicated
> core.
>
> The reason I prefer to allocate a dedicated core to KNI kernel thread is
> that my application is latency sensitive. I worry the context switch and
> cache miss will cause the latency increasing if the KNI kernel thread and
> application thread share one core.
>
> Anyway, I think I should remove the hard coded check because it will be
> more generic. Users who has the similar usage like mine can achieve so
> through configuration file.
>
>
>
> > +
> > +             mask |= 1LLU << lcore_id;
> > +     }
> > +
> >       app->core_mask = mask;
> >       APP_LOG(app, HIGH, "CPU core mask = 0x%016" PRIx64, app-
> > >core_mask);
> >  }
> > @@ -1236,6 +1254,11 @@ static void app_pipeline_params_get(struct
> > app_params *app,
> >                                       n_bytes_per_pkt;
> >                       }
> >                       break;
> > +             case APP_PKTQ_IN_KNI:
> > +                     out->type = PIPELINE_PORT_IN_KNI;
> > +                     out->params.kni.kni = app->kni[in->id];
> > +                     out->burst_size = app->kni_params[in->id].burst;
> > +                     break;
> >               default:
> >                       break;
> >               }
> > @@ -1374,6 +1397,12 @@ static void app_pipeline_params_get(struct
> > app_params *app,
> >                               out->params.sink.max_n_pkts = 0;
> >                       }
> >                       break;
> > +             case APP_PKTQ_OUT_KNI:
> > +                     out->type = PIPELINE_PORT_OUT_KNI;
> > +                     out->params.kni.kni = app->kni[in->id];
> > +                     out->params.kni.tx_burst_sz =
> > +                                     app->kni_params[in->id].burst;
> > +                     break;
> >               default:
> >                       break;
> >               }
> > @@ -1397,6 +1426,55 @@ static void app_pipeline_params_get(struct
> > app_params *app,
> >  }
> >
> >  static void
> > +app_init_kni(struct app_params *app) {
> > +     uint32_t i;
> > +     struct rte_kni_conf conf;
> > +
>
> Avoid calling rte_kni_init() when there is no KNI device:
>
> if (app->n_kni == 0)
>         return;
>
> > +     rte_kni_init((unsigned int)app->n_kni);
> > +
> > +     for (i = 0; i < app->n_kni; i++) {
> > +             struct app_kni_params *p_kni = &app->kni_params[i];
> > +             uint32_t port_id;
>
> Please rename port_id with link_id, as this index is really the x from
> LINKx objects.
>
> > +             struct app_mempool_params *mempool_params;
> > +             struct rte_mempool *mempool;
> > +
> > +             if (sscanf(p_kni->name, "KNI%" PRIu32, &port_id) != 1)
> > +                     rte_panic("%s's port id is invalid\n",
> p_kni->name);
>
> Same comment as above: we do not need to check the x in KNIx, as LINKx is
> (should be, after you adjust the config_parse.c code) added automatically
> for KNIx.
>
> > +
> > +             mempool_params = &app->mempool_params[p_kni-
> > >mempool_id];
> > +             mempool = app->mempool[p_kni->mempool_id];
> > +
> > +             memset(&conf, 0, sizeof(conf));
> > +             snprintf(conf.name, RTE_KNI_NAMESIZE,
> > +                              "vEth%u", port_id);
> > +             conf.core_id = p_kni->core_id;
>
> The way the conf.core_id is set here is wrong, right?
>
>
> conf.core_id = cpu_core_map_get_lcore_id(app->core_map,
>         p->socket_id,
>         p->core_id,
>         p->hyper_th_id);
>
> > +             conf.force_bind = 1;
> > +
> > +             conf.group_id = (uint16_t) port_id;
> > +             conf.mbuf_size = mempool_params->buffer_size;
> > +
> > +             struct rte_kni_ops ops;
> > +             struct rte_eth_dev_info dev_info;
> > +
>
> Please move these definitions at the top of the for loop block rather than
> having them in the middle of the for loop block.
>
> > +             memset(&dev_info, 0, sizeof(dev_info));
> > +             rte_eth_dev_info_get(app->link_params[port_id].pmd_id,
> > +                                                      &dev_info);
> > +             conf.addr = dev_info.pci_dev->addr;
> > +             conf.id = dev_info.pci_dev->id;
> > +
> > +             memset(&ops, 0, sizeof(ops));
> > +             ops.port_id = app->link_params[port_id].pmd_id;
> > +             ops.change_mtu = kni_change_mtu;
> > +             ops.config_network_if = kni_config_network_interface;
> > +
> > +             app->kni[i] = rte_kni_alloc(mempool,
> > +                     &conf, &ops);
> > +             if (!app->kni[i])
> > +                     rte_panic("Fail to create kni for port: %d\n",
> port_id);
>
> rte_panic("Failed to create %s", p->name);
>
> This should print e.g. "Failed to create KNI5", which users should know it
> is the KNI associated with LINK5.
>
> > +     }
> > +}
> > +
> > +static void
> >  app_init_pipelines(struct app_params *app)
> >  {
> >       uint32_t p_id;
> > @@ -1531,6 +1609,7 @@ int app_init(struct app_params *app)
> >       app_init_swq(app);
> >       app_init_tm(app);
> >       app_init_msgq(app);
> > +     app_init_kni(app);
> >
> >       app_pipeline_common_cmd_push(app);
> >       app_pipeline_thread_cmd_push(app);
> > diff --git a/examples/ip_pipeline/kni/kni.c
> b/examples/ip_pipeline/kni/kni.c
> > new file mode 100644
> > index 0000000..c58e146
>
> Please move the two functions from kni/kni.c to init.c (just before
> app_init_kni() function) and make them static functions, then remove
> kni/kni.h and kni/kni.c files.
>
>
> > --- /dev/null
> > +++ b/examples/ip_pipeline/kni/kni.c
> > @@ -0,0 +1,80 @@
> > +#include <string.h>
> > +
> > +#include <rte_common.h>
> > +#include <rte_malloc.h>
> > +#include <rte_table_array.h>
> > +#include <rte_kni.h>
> > +#include <rte_ethdev.h>
> > +
> > +#include "rte_port_kni.h"
> > +#include "kni.h"
> > +
> > +int
> > +kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
> > +     int ret = 0;
> > +
> > +     if (port_id >= rte_eth_dev_count() || port_id >=
> > RTE_MAX_ETHPORTS) {
> > +             RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
> > +                             __func__, port_id);
> > +             return -EINVAL;
> > +     }
> > +
> > +     RTE_LOG(INFO, PORT, "%s: Configure network interface of %d
> > %s\n",
> > +                     __func__, port_id, if_up ? "up" : "down");
> > +
> > +     if (if_up != 0) { /* Configure network interface up */
> > +             rte_eth_dev_stop(port_id);
>
> Why do we need to stop the device first before we start it?
>
> > +             ret = rte_eth_dev_start(port_id);
> > +     } else /* Configure network interface down */
> > +             rte_eth_dev_stop(port_id);
>
> Do we need to call rte_eth_dev_start/stop() or do we need to call
> rte_eth_dev_up/down()?
>
> To be  honest, I made reference to the the KNI sample application.
>
> Since there is very little docs tell the difference between device up call
> and device start call, I am not sure which one is better here.
>
> Any help will be appreciate. :-)
>
>
>
> > +
> > +     if (ret < 0)
> > +             RTE_LOG(ERR, PORT, "%s: Failed to start port %d\n",
> > +                             __func__, port_id);
> > +
>
> This is a callback function, I think we should completely remove any
> RTE_LOG calls from it, as link can go up and down quite frequently.
>
>
> > +     return ret;
> > +}
> > +
> > +int
> > +kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
> > +     int ret;
> > +
> > +     if (port_id >= rte_eth_dev_count()) {
> > +             RTE_LOG(ERR, PORT, "%s: Invalid port id %d\n",
> > +                             __func__, port_id);
> > +             return -EINVAL;
> > +     }
> > +
> > +     if (new_mtu > ETHER_MAX_LEN) {
> > +             RTE_LOG(ERR, PORT,
> > +                             "%s: Fail to reconfigure port %d, the new
> > MTU is too big\n",
> > +                             __func__, port_id);
> > +             return -EINVAL;
> > +     }
> > +
> > +     RTE_LOG(INFO, PORT, "%s: Change MTU of port %d to %u\n",
> > +                     __func__, port_id,
> > +                     new_mtu);
> > +
> > +     /* Stop specific port */
> > +     rte_eth_dev_stop(port_id);
> > +
> > +     /* Set new MTU */
> > +     ret = rte_eth_dev_set_mtu(port_id, new_mtu);
> > +     if (ret < 0) {
> > +             RTE_LOG(ERR, PORT, "%s: Fail to reconfigure port %d\n",
> > +                             __func__, port_id);
> > +             return ret;
> > +     }
> > +
> > +     /* Restart specific port */
> > +     ret = rte_eth_dev_start(port_id);
> > +     if (ret < 0) {
> > +             RTE_LOG(ERR, PORT, "%s: Fail to restart port %d\n",
> > +                             __func__, port_id);
> > +             return ret;
> > +     }
> > +
> > +     return 0;
> > +}
>
> This is a callback function, I think we should completely remove all the
> RTE_LOG calls from it.
>
> > +
> > diff --git a/examples/ip_pipeline/kni/kni.h
> b/examples/ip_pipeline/kni/kni.h
> > new file mode 100644
> > index 0000000..04c8429
> > --- /dev/null
> > +++ b/examples/ip_pipeline/kni/kni.h
> > @@ -0,0 +1,16 @@
> > +#ifndef __INCLUDE_KNI_H__
> > +#define __INCLUDE_KNI_H__
> > +
> > +#include <rte_common.h>
> > +
> > +/* Total octets in ethernet header */
> > +#define KNI_ENET_HEADER_SIZE    14
>
> Are we actually using this macro? I would like to remove it if not needed.
>
> > +
> > +/* Total octets in the FCS */
> > +#define KNI_ENET_FCS_SIZE       4
>
> Are we actually using this macro? I would like to remove it if not needed.
>
>
> > +
> > +int kni_config_network_interface(uint8_t port_id, uint8_t if_up);
> > +
> > +int kni_change_mtu(uint8_t port_id, unsigned new_mtu);
> > +
> > +#endif
> > diff --git a/examples/ip_pipeline/pipeline_be.h
> > b/examples/ip_pipeline/pipeline_be.h
> > index f4ff262..23f0438 100644
> > --- a/examples/ip_pipeline/pipeline_be.h
> > +++ b/examples/ip_pipeline/pipeline_be.h
> > @@ -40,6 +40,7 @@
> >  #include <rte_port_ras.h>
> >  #include <rte_port_sched.h>
> >  #include <rte_port_source_sink.h>
> > +#include <rte_port_kni.h>
> >  #include <rte_pipeline.h>
> >
> >  enum pipeline_port_in_type {
> > @@ -50,6 +51,7 @@ enum pipeline_port_in_type {
> >       PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
> >       PIPELINE_PORT_IN_SCHED_READER,
> >       PIPELINE_PORT_IN_SOURCE,
> > +     PIPELINE_PORT_IN_KNI,
> >  };
> >
> >  struct pipeline_port_in_params {
> > @@ -62,6 +64,7 @@ struct pipeline_port_in_params {
> >               struct rte_port_ring_reader_ipv6_frag_params
> > ring_ipv6_frag;
> >               struct rte_port_sched_reader_params sched;
> >               struct rte_port_source_params source;
> > +             struct rte_port_kni_reader_params kni;
> >       } params;
> >       uint32_t burst_size;
> >  };
> > @@ -84,6 +87,8 @@ pipeline_port_in_params_convert(struct
> > pipeline_port_in_params  *p)
> >               return (void *) &p->params.sched;
> >       case PIPELINE_PORT_IN_SOURCE:
> >               return (void *) &p->params.source;
> > +     case PIPELINE_PORT_IN_KNI:
> > +             return (void *) &p->params.kni;
> >       default:
> >               return NULL;
> >       }
> > @@ -107,6 +112,8 @@ pipeline_port_in_params_get_ops(struct
> > pipeline_port_in_params  *p)
> >               return &rte_port_sched_reader_ops;
> >       case PIPELINE_PORT_IN_SOURCE:
> >               return &rte_port_source_ops;
> > +     case PIPELINE_PORT_IN_KNI:
> > +             return &rte_port_kni_reader_ops;
> >       default:
> >               return NULL;
> >       }
> > @@ -123,6 +130,7 @@ enum pipeline_port_out_type {
> >       PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
> >       PIPELINE_PORT_OUT_SCHED_WRITER,
> >       PIPELINE_PORT_OUT_SINK,
> > +     PIPELINE_PORT_OUT_KNI,
> >  };
> >
> >  struct pipeline_port_out_params {
> > @@ -138,6 +146,7 @@ struct pipeline_port_out_params {
> >               struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
> >               struct rte_port_sched_writer_params sched;
> >               struct rte_port_sink_params sink;
> > +             struct rte_port_kni_writer_params kni;
> >       } params;
> >  };
> >
> > @@ -165,6 +174,8 @@ pipeline_port_out_params_convert(struct
> > pipeline_port_out_params  *p)
> >               return (void *) &p->params.sched;
> >       case PIPELINE_PORT_OUT_SINK:
> >               return (void *) &p->params.sink;
> > +     case PIPELINE_PORT_OUT_KNI:
> > +             return (void *) &p->params.kni;
> >       default:
> >               return NULL;
> >       }
> > @@ -194,6 +205,8 @@ pipeline_port_out_params_get_ops(struct
> > pipeline_port_out_params  *p)
> >               return &rte_port_sched_writer_ops;
> >       case PIPELINE_PORT_OUT_SINK:
> >               return &rte_port_sink_ops;
> > +     case PIPELINE_PORT_OUT_KNI:
> > +             return &rte_port_kni_writer_ops;
> >       default:
> >               return NULL;
> >       }
> > diff --git a/examples/ip_pipeline/thread.c
> b/examples/ip_pipeline/thread.c
> > index a0f1f12..534864a 100644
> > --- a/examples/ip_pipeline/thread.c
> > +++ b/examples/ip_pipeline/thread.c
> > @@ -239,6 +239,15 @@ app_thread(void *arg)
> >       uint32_t core_id = rte_lcore_id(), i, j;
> >       struct app_thread_data *t = &app->thread_data[core_id];
> >
> > +     for (i = 0; i < app->n_kni; i++) {
> > +             if (core_id == (uint32_t)cpu_core_map_get_lcore_id(
> > +                     app->core_map,
> > +                     app->kni_params[i].socket_id,
> > +                     app->kni_params[i].core_id,
> > +                     app->kni_params[i].hyper_th_id))
> > +                     return 0;
> > +     }
> > +
>
> Same questions as above:
> - Why do we need to dedicate separate CPU core(s) for KNI interface(s)?
> Isn't KNI code running in kernel space, why do we need to dedicate separate
> user space core for it and why do we need to add this core to the
> user-space application core mask?
> - Even if we need to add this to the core mask (maybe I am missing
> something here ...), why do we need to dedicate this core entirely to KNI?
> Can't we have KNI (kernel) code sharing this core with user-space
> application code (e.g. some pipeline instances?)
>
> This is run-time code, let's have it enabled only when KNI library is part
> of the build:
>
> #ifdef RTE_LIBRTE_KNI
> ...
> #endif /* RTE_LIBRTE_KNI */
>
>
> >       for (i = 0; ; i++) {
> >               uint32_t n_regular = RTE_MIN(t->n_regular, RTE_DIM(t-
> > >regular));
> >               uint32_t n_custom = RTE_MIN(t->n_custom, RTE_DIM(t-
> > >custom));
> > diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
> > index d4de5af..f18253d 100644
> > --- a/lib/librte_port/Makefile
> > +++ b/lib/librte_port/Makefile
> > @@ -57,6 +57,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
> >  endif
> >  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
> > +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> > +SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
> > +endif
> >
> >  # install includes
> >  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port.h
> > @@ -68,6 +71,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=
> > rte_port_ras.h
> >  endif
> >  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
> >  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
> > +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> > +SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
> > +endif
> >
> >  # this lib depends upon:
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
> > @@ -75,5 +81,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=
> > lib/librte_mbuf
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
> > +DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
> >
> >  include $(RTE_SDK)/mk/rte.lib.mk
> > diff --git a/lib/librte_port/rte_port_kni.c
> b/lib/librte_port/rte_port_kni.c
> > new file mode 100644
> > index 0000000..8c5e404
> > --- /dev/null
> > +++ b/lib/librte_port/rte_port_kni.c
> > @@ -0,0 +1,316 @@
> > +/*-
> > + *   BSD LICENSE
> > + *
> > + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
>
> Please fix the year as 2016.
>
>
> > + *   All rights reserved.
> > + *
> > + *   Redistribution and use in source and binary forms, with or without
> > + *   modification, are permitted provided that the following conditions
> > + *   are met:
> > + *
> > + *     * Redistributions of source code must retain the above copyright
> > + *       notice, this list of conditions and the following disclaimer.
> > + *     * Redistributions in binary form must reproduce the above
> copyright
> > + *       notice, this list of conditions and the following disclaimer in
> > + *       the documentation and/or other materials provided with the
> > + *       distribution.
> > + *     * Neither the name of Intel Corporation nor the names of its
> > + *       contributors may be used to endorse or promote products derived
> > + *       from this software without specific prior written permission.
> > + *
> > + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> > CONTRIBUTORS
> > + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> > FITNESS FOR
> > + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> > COPYRIGHT
> > + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> > INCIDENTAL,
> > + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> > OF USE,
> > + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> > AND ON ANY
> > + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> > TORT
> > + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> > THE USE
> > + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> > DAMAGE.
> > + */
> > +#include <string.h>
> > +
> > +#include <rte_common.h>
> > +#include <rte_malloc.h>
> > +#include <rte_kni.h>
> > +
> > +#include "rte_port_kni.h"
> > +
> > +/*
> > + * Port KNI Reader
> > + */
> > +#ifdef RTE_PORT_STATS_COLLECT
> > +
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
> > +     {port->stats.n_pkts_in += val}
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
> > +     {port->stats.n_pkts_drop += val}
>
> This actually results in compiler error when built with
> RTE_PORT_STATS_COLLECT = ON.
>
> Please add semicolon and remove curly braces (as in e.g. rte_port_ring.c):
> #define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
>         port->stats.n_pkts_in += val;
> #define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
>         port->stats.n_pkts_drop += val;
>
>
> > +#else
> > +
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
> > +
> > +#endif
> > +
> > +struct rte_port_kni_reader {
> > +     struct rte_port_in_stats stats;
> > +
> > +     struct rte_kni *kni;
> > +};
> > +
> > +static void *
> > +rte_port_kni_reader_create(void *params, int socket_id) {
> > +     struct rte_port_kni_reader_params *conf =
> > +                     (struct rte_port_kni_reader_params *) params;
> > +     struct rte_port_kni_reader *port;
> > +
> > +     /* Check input parameters */
> > +     if (conf == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Memory allocation */
> > +     port = rte_zmalloc_socket("PORT", sizeof(*port),
> > +             RTE_CACHE_LINE_SIZE, socket_id);
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> > __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Initialization */
> > +     port->kni = conf->kni;
> > +
> > +     return port;
> > +}
> > +
> > +static int
> > +rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t
> > n_pkts) {
> > +     struct rte_port_kni_reader *p =
> > +                     (struct rte_port_kni_reader *) port;
> > +     uint16_t rx_pkt_cnt;
> > +
> > +     rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
> > +     RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
> > +     return rx_pkt_cnt;
> > +}
> > +
> > +static int
> > +rte_port_kni_reader_free(void *port) {
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
> > +             return -EINVAL;
> > +     }
> > +
> > +     rte_free(port);
> > +
> > +     return 0;
> > +}
> > +
> > +static int rte_port_kni_reader_stats_read(void *port,
> > +     struct rte_port_in_stats *stats, int clear)
> > +{
> > +     struct rte_port_kni_reader *p =
> > +                     (struct rte_port_kni_reader *) port;
> > +
> > +     if (stats != NULL)
> > +             memcpy(stats, &p->stats, sizeof(p->stats));
> > +
> > +     if (clear)
> > +             memset(&p->stats, 0, sizeof(p->stats));
> > +
> > +     return 0;
> > +}
> > +
> > +/*
> > + * Port KNI Writer
> > + */
> > +#ifdef RTE_PORT_STATS_COLLECT
> > +
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
> > +     {port->stats.n_pkts_in += val}
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
> > +     {port->stats.n_pkts_drop += val}
> > +
>
> This actually results in compiler error when built with
> RTE_PORT_STATS_COLLECT = ON.
>
> Please add semicolon and remove curly braces (as in e.g. rte_port_ring.c).
>
> Actually I implements the macro similar
> to RTE_PORT_RING_READER_STATS_PKTS_IN_ADD first. But the
> scripts/checkpatches.sh fails: ERROR:COMPLEX_MACRO: Macros with complex
> values should be enclosed in parentheses
>
> I'm not share either I have done something wrong or
> the checkpatches script need an update.
>
>
>
> > +#else
> > +
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
> > +
> > +#endif
> > +
> > +struct rte_port_kni_writer {
> > +     struct rte_port_out_stats stats;
> > +
> > +     struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
> > +     uint32_t tx_burst_sz;
> > +     uint16_t tx_buf_count;
>
> Can we actually make tx_buf_count to be uint32_t rather than uint16_t? I
> see some computation between tx_buf_count and some other variable of type
> uint32_t later in the code ...
>
>
> > +     uint64_t bsz_mask;
> > +     struct rte_kni *kni;
> > +};
> > +
> > +static void *
> > +rte_port_kni_writer_create(void *params, int socket_id) {
> > +     struct rte_port_kni_writer_params *conf =
> > +                     (struct rte_port_kni_writer_params *) params;
> > +     struct rte_port_kni_writer *port;
> > +
> > +     /* Check input parameters */
> > +     if ((conf == NULL) ||
> > +             (conf->tx_burst_sz == 0) ||
> > +             (conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
> > +             (!rte_is_power_of_2(conf->tx_burst_sz))) {
> > +             RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n",
> > __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Memory allocation */
> > +     port = rte_zmalloc_socket("PORT", sizeof(*port),
> > +             RTE_CACHE_LINE_SIZE, socket_id);
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> > __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Initialization */
> > +     port->kni = conf->kni;
> > +     port->tx_burst_sz = conf->tx_burst_sz;
> > +     port->tx_buf_count = 0;
> > +     port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
> > +
> > +     return port;
> > +}
> > +
> > +static inline void
> > +send_burst(struct rte_port_kni_writer *p) {
> > +     uint32_t nb_tx;
> > +
> > +     nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
> > +     rte_kni_handle_request(p->kni);
> > +
> > +     RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p-
> > >tx_buf_count - nb_tx);
> > +     for (; nb_tx < p->tx_buf_count; nb_tx++)
> > +             rte_pktmbuf_free(p->tx_buf[nb_tx]);
> > +
> > +     p->tx_buf_count = 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt) {
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +
> > +     p->tx_buf[p->tx_buf_count++] = pkt;
> > +     RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> > +     if (p->tx_buf_count >= p->tx_burst_sz)
> > +             send_burst(p);
> > +
> > +     return 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_tx_bulk(void *port,
> > +                                                     struct rte_mbuf
> > **pkts,
> > +                                                     uint64_t
> pkts_mask) {
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +     uint64_t bsz_mask = p->bsz_mask;
> > +     uint32_t tx_buf_count = p->tx_buf_count;
> > +     uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
> > +                                     ((pkts_mask & bsz_mask) ^
> > bsz_mask);
> > +
> > +     if (expr == 0) {
> > +             uint64_t n_pkts = __builtin_popcountll(pkts_mask);
> > +             uint32_t n_pkts_ok;
> > +
> > +             if (tx_buf_count)
> > +                     send_burst(p);
> > +
> > +             RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
> > +             n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
> > +
> > +             RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts
> > - n_pkts_ok);
> > +             for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
> > +                     struct rte_mbuf *pkt = pkts[n_pkts_ok];
> > +
> > +                     rte_pktmbuf_free(pkt);
> > +             }
> > +     } else {
> > +             for (; pkts_mask;) {
> > +                     uint32_t pkt_index = __builtin_ctzll(pkts_mask);
> > +                     uint64_t pkt_mask = 1LLU << pkt_index;
> > +                     struct rte_mbuf *pkt = pkts[pkt_index];
> > +
> > +                     p->tx_buf[tx_buf_count++] = pkt;
> > +                     RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> > +                     pkts_mask &= ~pkt_mask;
> > +             }
> > +
> > +             p->tx_buf_count = tx_buf_count;
> > +             if (tx_buf_count >= p->tx_burst_sz)
> > +                     send_burst(p);
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_flush(void *port) {
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +
> > +     if (p->tx_buf_count > 0)
> > +             send_burst(p);
> > +
> > +     return 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_free(void *port) {
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
> > +             return -EINVAL;
> > +     }
> > +
> > +     rte_port_kni_writer_flush(port);
> > +     rte_free(port);
> > +
> > +     return 0;
> > +}
> > +
> > +static int rte_port_kni_writer_stats_read(void *port,
> > +     struct rte_port_out_stats *stats, int clear)
> > +{
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +
> > +     if (stats != NULL)
> > +             memcpy(stats, &p->stats, sizeof(p->stats));
> > +
> > +     if (clear)
> > +             memset(&p->stats, 0, sizeof(p->stats));
> > +
> > +     return 0;
> > +}
> > +
> > +/*
> > + * Summary of port operations
> > + */
> > +struct rte_port_in_ops rte_port_kni_reader_ops = {
> > +     .f_create = rte_port_kni_reader_create,
> > +     .f_free = rte_port_kni_reader_free,
> > +     .f_rx = rte_port_kni_reader_rx,
> > +     .f_stats = rte_port_kni_reader_stats_read,
> > +};
> > +
> > +struct rte_port_out_ops rte_port_kni_writer_ops = {
> > +     .f_create = rte_port_kni_writer_create,
> > +     .f_free = rte_port_kni_writer_free,
> > +     .f_tx = rte_port_kni_writer_tx,
> > +     .f_tx_bulk = rte_port_kni_writer_tx_bulk,
> > +     .f_flush = rte_port_kni_writer_flush,
> > +     .f_stats = rte_port_kni_writer_stats_read,
> > +};
>
> Do we need a KNI writer no-drop version as well? Would it be useful?
>
> > diff --git a/lib/librte_port/rte_port_kni.h
> b/lib/librte_port/rte_port_kni.h
> > new file mode 100644
> > index 0000000..7623798
> > --- /dev/null
> > +++ b/lib/librte_port/rte_port_kni.h
> > @@ -0,0 +1,81 @@
> > +/*-
> > + *   BSD LICENSE
> > + *
> > + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
>
> Please fix the year to 2016.
>
>
> > + *   All rights reserved.
> > + *
> > + *   Redistribution and use in source and binary forms, with or without
> > + *   modification, are permitted provided that the following conditions
> > + *   are met:
> > + *
> > + *     * Redistributions of source code must retain the above copyright
> > + *       notice, this list of conditions and the following disclaimer.
> > + *     * Redistributions in binary form must reproduce the above
> copyright
> > + *       notice, this list of conditions and the following disclaimer in
> > + *       the documentation and/or other materials provided with the
> > + *       distribution.
> > + *     * Neither the name of Intel Corporation nor the names of its
> > + *       contributors may be used to endorse or promote products derived
> > + *       from this software without specific prior written permission.
> > + *
> > + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> > CONTRIBUTORS
> > + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> > FITNESS FOR
> > + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> > COPYRIGHT
> > + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> > INCIDENTAL,
> > + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> > OF USE,
> > + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> > AND ON ANY
> > + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> > TORT
> > + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> > THE USE
> > + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> > DAMAGE.
> > + */
> > +
> > +#ifndef __INCLUDE_RTE_PORT_KNI_H__
> > +#define __INCLUDE_RTE_PORT_KNI_H__
> > +
> > +#ifdef __cplusplus
> > +extern "C" {
> > +#endif
> > +
> > +/**
> > + * @file
> > + * RTE Port KNI Interface
> > + *
> > + * kni_reader: input port built on top of pre-initialized KNI interface
> > + * kni_writer: output port built on top of pre-initialized KNI interface
> > + *
> > + ***/
> > +
> > +#include <stdint.h>
> > +
> > +#include <rte_kni.h>
> > +
> > +#include "rte_port.h"
> > +
> > +/** kni_reader port parameters */
> > +struct rte_port_kni_reader_params {
> > +     /** KNI interface reference */
> > +     struct rte_kni *kni;
> > +};
> > +
> > +/** kni_reader port operations */
> > +extern struct rte_port_in_ops rte_port_kni_reader_ops;
> > +
> > +
> > +/** kni_writer port parameters */
> > +struct rte_port_kni_writer_params {
> > +     /** KNI interface reference */
> > +     struct rte_kni *kni;
> > +     /** Burst size to KNI interface. */
> > +     uint32_t tx_burst_sz;
> > +};
> > +
> > +/** kni_writer port operations */
> > +extern struct rte_port_out_ops rte_port_kni_writer_ops;
> > +
> > +#ifdef __cplusplus
> > +}
> > +#endif
> > +
> > +#endif
> > --
> > 2.7.4
>
> Thank you!
>
> Regards,
> Cristian
>
>
>
>
>
> B.R.
>
>
>
> Ethan
>
>
>

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

* Re: [PATCH v3 1/3] port: add kni interface support
  2016-06-16 11:27   ` [PATCH v3 1/3] " WeiJie Zhuang
  2016-06-16 11:27     ` [PATCH v3 2/3] port: add kni nodrop writer WeiJie Zhuang
  2016-06-16 11:27     ` [PATCH v3 3/3] port: document update WeiJie Zhuang
@ 2016-06-18 16:44     ` Dumitrescu, Cristian
  2016-06-21 11:10       ` Ethan
  2 siblings, 1 reply; 22+ messages in thread
From: Dumitrescu, Cristian @ 2016-06-18 16:44 UTC (permalink / raw)
  To: WeiJie Zhuang; +Cc: dev, Singh, Jasvinder, Yigit, Ferruh

Hi Ethan,

Thank you, here are some comments inlined below.

Please reorganize this patch in a slightly different way to look similar to other DPDK patch sets and also ease up the integration work for Thomas:
	Patch 0: I suggest adding a cover letter;
	Patch 1: all librte_port changes (rte_port_kni.h, rte_port_kni.c, Makefile, rte_port_version.map), including the "nodrop" KNI port version
	Patch 2: all ip_pipeline app changes
	Patch 3: ip_pipeline app kni.cfg file
	Patch 4: Documentation changes

> -----Original Message-----
> From: WeiJie Zhuang [mailto:zhuangwj@gmail.com]
> Sent: Thursday, June 16, 2016 12:27 PM
> To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>
> Cc: dev@dpdk.org; Singh, Jasvinder <jasvinder.singh@intel.com>; Yigit,
> Ferruh <ferruh.yigit@intel.com>; WeiJie Zhuang <zhuangwj@gmail.com>
> Subject: [PATCH v3 1/3] port: add kni interface support
> 
> 1. add KNI port type to the packet framework
> 2. add KNI support to the IP Pipeline sample Application
> 3. some bug fix
> 
> Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
> ---
> v2:
> * Fix check patch error.
> v3:
> * Fix code review comments.
> ---
>  doc/api/doxy-api-index.md                          |   1 +
>  examples/ip_pipeline/Makefile                      |   2 +-
>  examples/ip_pipeline/app.h                         | 181 +++++++++++-
>  examples/ip_pipeline/config/kni.cfg                |  67 +++++
>  examples/ip_pipeline/config_check.c                |  26 +-
>  examples/ip_pipeline/config_parse.c                | 166 ++++++++++-
>  examples/ip_pipeline/init.c                        | 132 ++++++++-
>  examples/ip_pipeline/pipeline/pipeline_common_fe.c |  29 ++
>  examples/ip_pipeline/pipeline/pipeline_master_be.c |   6 +
>  examples/ip_pipeline/pipeline_be.h                 |  27 ++
>  lib/librte_port/Makefile                           |   7 +
>  lib/librte_port/rte_port_kni.c                     | 325 +++++++++++++++++++++
>  lib/librte_port/rte_port_kni.h                     |  82 ++++++
>  lib/librte_port/rte_port_version.map               |   8 +
>  14 files changed, 1047 insertions(+), 12 deletions(-)
>  create mode 100644 examples/ip_pipeline/config/kni.cfg
>  create mode 100644 lib/librte_port/rte_port_kni.c
>  create mode 100644 lib/librte_port/rte_port_kni.h
> 
> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> index f626386..5e7f024 100644
> --- a/doc/api/doxy-api-index.md
> +++ b/doc/api/doxy-api-index.md
> @@ -118,6 +118,7 @@ There are many libraries, so their headers may be
> grouped by topics:
>      [frag]             (@ref rte_port_frag.h),
>      [reass]            (@ref rte_port_ras.h),
>      [sched]            (@ref rte_port_sched.h),
> +    [kni]              (@ref rte_port_kni.h),
>      [src/sink]         (@ref rte_port_source_sink.h)
>    * [table]            (@ref rte_table.h):
>      [lpm IPv4]         (@ref rte_table_lpm.h),
> diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
> index 5827117..6dc3f52 100644
> --- a/examples/ip_pipeline/Makefile
> +++ b/examples/ip_pipeline/Makefile
> @@ -1,6 +1,6 @@
>  #   BSD LICENSE
>  #
> -#   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> +#   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
>  #   All rights reserved.
>  #
>  #   Redistribution and use in source and binary forms, with or without
> diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
> index 7611341..abbd6d4 100644
> --- a/examples/ip_pipeline/app.h
> +++ b/examples/ip_pipeline/app.h
> @@ -44,6 +44,9 @@
>  #include <cmdline_parse.h>
> 
>  #include <rte_ethdev.h>
> +#ifdef RTE_LIBRTE_KNI
> +#include <rte_kni.h>
> +#endif
> 
>  #include "cpu_core_map.h"
>  #include "pipeline.h"
> @@ -132,6 +135,20 @@ struct app_pktq_swq_params {
>  	uint32_t mempool_indirect_id;
>  };
> 
> +struct app_pktq_kni_params {
> +	char *name;
> +	uint32_t parsed;
> +
> +	uint32_t socket_id;
> +	uint32_t core_id;
> +	uint32_t hyper_th_id;
> +	uint32_t force_bind;
> +
> +	uint32_t mempool_id; /* Position in the app->mempool_params */
> +	uint32_t burst_read;
> +	uint32_t burst_write;
> +};
> +
>  #ifndef APP_FILE_NAME_SIZE
>  #define APP_FILE_NAME_SIZE                       256
>  #endif
> @@ -185,6 +202,7 @@ enum app_pktq_in_type {
>  	APP_PKTQ_IN_HWQ,
>  	APP_PKTQ_IN_SWQ,
>  	APP_PKTQ_IN_TM,
> +	APP_PKTQ_IN_KNI,
>  	APP_PKTQ_IN_SOURCE,
>  };
> 
> @@ -197,6 +215,7 @@ enum app_pktq_out_type {
>  	APP_PKTQ_OUT_HWQ,
>  	APP_PKTQ_OUT_SWQ,
>  	APP_PKTQ_OUT_TM,
> +	APP_PKTQ_OUT_KNI,
>  	APP_PKTQ_OUT_SINK,
>  };
> 
> @@ -420,6 +439,8 @@ struct app_eal_params {
> 
>  #define APP_MAX_PKTQ_TM                          APP_MAX_LINKS
> 
> +#define APP_MAX_PKTQ_KNI                         APP_MAX_LINKS
> +
>  #ifndef APP_MAX_PKTQ_SOURCE
>  #define APP_MAX_PKTQ_SOURCE                      64
>  #endif
> @@ -471,6 +492,7 @@ struct app_params {
>  	struct app_pktq_hwq_out_params
> hwq_out_params[APP_MAX_HWQ_OUT];
>  	struct app_pktq_swq_params swq_params[APP_MAX_PKTQ_SWQ];
>  	struct app_pktq_tm_params tm_params[APP_MAX_PKTQ_TM];
> +	struct app_pktq_kni_params kni_params[APP_MAX_PKTQ_KNI];
>  	struct app_pktq_source_params
> source_params[APP_MAX_PKTQ_SOURCE];
>  	struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
>  	struct app_msgq_params msgq_params[APP_MAX_MSGQ];
> @@ -482,6 +504,7 @@ struct app_params {
>  	uint32_t n_pktq_hwq_out;
>  	uint32_t n_pktq_swq;
>  	uint32_t n_pktq_tm;
> +	uint32_t n_pktq_kni;
>  	uint32_t n_pktq_source;
>  	uint32_t n_pktq_sink;
>  	uint32_t n_msgq;
> @@ -495,6 +518,9 @@ struct app_params {
>  	struct app_link_data link_data[APP_MAX_LINKS];
>  	struct rte_ring *swq[APP_MAX_PKTQ_SWQ];
>  	struct rte_sched_port *tm[APP_MAX_PKTQ_TM];
> +#ifdef RTE_LIBRTE_KNI
> +	struct rte_kni *kni[APP_MAX_PKTQ_KNI];
> +#endif /* RTE_LIBRTE_KNI */
>  	struct rte_ring *msgq[APP_MAX_MSGQ];
>  	struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
>  	struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
> @@ -667,11 +693,11 @@ app_swq_get_reader(struct app_params *app,
>  	struct app_pktq_swq_params *swq,
>  	uint32_t *pktq_in_id)
>  {
> -	struct app_pipeline_params *reader;
> +	struct app_pipeline_params *reader = NULL;
>  	uint32_t pos = swq - app->swq_params;
>  	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
>  		RTE_DIM(app->pipeline_params));
> -	uint32_t n_readers = 0, id, i;
> +	uint32_t n_readers = 0, id = 0, i;
> 
>  	for (i = 0; i < n_pipelines; i++) {
>  		struct app_pipeline_params *p = &app->pipeline_params[i];
> @@ -727,11 +753,11 @@ app_tm_get_reader(struct app_params *app,
>  	struct app_pktq_tm_params *tm,
>  	uint32_t *pktq_in_id)
>  {
> -	struct app_pipeline_params *reader;
> +	struct app_pipeline_params *reader = NULL;
>  	uint32_t pos = tm - app->tm_params;
>  	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
>  		RTE_DIM(app->pipeline_params));
> -	uint32_t n_readers = 0, id, i;
> +	uint32_t n_readers = 0, id = 0, i;
> 
>  	for (i = 0; i < n_pipelines; i++) {
>  		struct app_pipeline_params *p = &app->pipeline_params[i];
> @@ -758,6 +784,66 @@ app_tm_get_reader(struct app_params *app,
>  }
> 
>  static inline uint32_t
> +app_kni_get_readers(struct app_params *app, struct
> app_pktq_kni_params *kni)
> +{
> +	uint32_t pos = kni - app->kni_params;
> +	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +		RTE_DIM(app->pipeline_params));
> +	uint32_t n_readers = 0, i;
> +
> +	for (i = 0; i < n_pipelines; i++) {
> +		struct app_pipeline_params *p = &app->pipeline_params[i];
> +		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p-
> >pktq_in));
> +		uint32_t j;
> +
> +		for (j = 0; j < n_pktq_in; j++) {
> +			struct app_pktq_in_params *pktq = &p->pktq_in[j];
> +
> +			if ((pktq->type == APP_PKTQ_IN_KNI) &&
> +				(pktq->id == pos))
> +				n_readers++;
> +		}
> +	}
> +
> +	return n_readers;
> +}
> +
> +static inline struct app_pipeline_params *
> +app_kni_get_reader(struct app_params *app,
> +				  struct app_pktq_kni_params *kni,
> +				  uint32_t *pktq_in_id)
> +{
> +	struct app_pipeline_params *reader = NULL;
> +	uint32_t pos = kni - app->kni_params;
> +	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +		RTE_DIM(app->pipeline_params));
> +	uint32_t n_readers = 0, id = 0, i;
> +
> +	for (i = 0; i < n_pipelines; i++) {
> +		struct app_pipeline_params *p = &app->pipeline_params[i];
> +		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p-
> >pktq_in));
> +		uint32_t j;
> +
> +		for (j = 0; j < n_pktq_in; j++) {
> +			struct app_pktq_in_params *pktq = &p->pktq_in[j];
> +
> +			if ((pktq->type == APP_PKTQ_IN_KNI) &&
> +				(pktq->id == pos)) {
> +				n_readers++;
> +				reader = p;
> +				id = j;
> +			}
> +		}
> +	}
> +
> +	if (n_readers != 1)
> +		return NULL;
> +
> +	*pktq_in_id = id;
> +	return reader;
> +}
> +
> +static inline uint32_t
>  app_source_get_readers(struct app_params *app,
>  struct app_pktq_source_params *source)
>  {
> @@ -861,11 +947,11 @@ app_swq_get_writer(struct app_params *app,
>  	struct app_pktq_swq_params *swq,
>  	uint32_t *pktq_out_id)
>  {
> -	struct app_pipeline_params *writer;
> +	struct app_pipeline_params *writer = NULL;
>  	uint32_t pos = swq - app->swq_params;
>  	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
>  		RTE_DIM(app->pipeline_params));
> -	uint32_t n_writers = 0, id, i;
> +	uint32_t n_writers = 0, id = 0, i;
> 
>  	for (i = 0; i < n_pipelines; i++) {
>  		struct app_pipeline_params *p = &app->pipeline_params[i];
> @@ -923,11 +1009,11 @@ app_tm_get_writer(struct app_params *app,
>  	struct app_pktq_tm_params *tm,
>  	uint32_t *pktq_out_id)
>  {
> -	struct app_pipeline_params *writer;
> +	struct app_pipeline_params *writer = NULL;
>  	uint32_t pos = tm - app->tm_params;
>  	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
>  		RTE_DIM(app->pipeline_params));
> -	uint32_t n_writers = 0, id, i;
> +	uint32_t n_writers = 0, id = 0, i;
> 
>  	for (i = 0; i < n_pipelines; i++) {
>  		struct app_pipeline_params *p = &app->pipeline_params[i];
> @@ -939,10 +1025,73 @@ app_tm_get_writer(struct app_params *app,
>  			struct app_pktq_out_params *pktq = &p-
> >pktq_out[j];
> 
>  			if ((pktq->type == APP_PKTQ_OUT_TM) &&
> +				(pktq->id == pos)) {
> +				n_writers++;
> +				writer = p;
> +				id = j;
> +			}
> +		}
> +	}
> +
> +	if (n_writers != 1)
> +		return NULL;
> +
> +	*pktq_out_id = id;
> +	return writer;
> +}
> +
> +static inline uint32_t
> +app_kni_get_writers(struct app_params *app, struct
> app_pktq_kni_params *kni)
> +{
> +	uint32_t pos = kni - app->kni_params;
> +	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +		RTE_DIM(app->pipeline_params));
> +	uint32_t n_writers = 0, i;
> +
> +	for (i = 0; i < n_pipelines; i++) {
> +		struct app_pipeline_params *p = &app->pipeline_params[i];
> +		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
> +			RTE_DIM(p->pktq_out));
> +		uint32_t j;
> +
> +		for (j = 0; j < n_pktq_out; j++) {
> +			struct app_pktq_out_params *pktq = &p-
> >pktq_out[j];
> +
> +			if ((pktq->type == APP_PKTQ_OUT_KNI) &&
>  				(pktq->id == pos))
>  				n_writers++;
> +		}
> +	}
> +
> +	return n_writers;
> +}
> +
> +static inline struct app_pipeline_params *
> +app_kni_get_writer(struct app_params *app,
> +				  struct app_pktq_kni_params *kni,
> +				  uint32_t *pktq_out_id)
> +{
> +	struct app_pipeline_params *writer = NULL;
> +	uint32_t pos = kni - app->kni_params;
> +	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +		RTE_DIM(app->pipeline_params));
> +	uint32_t n_writers = 0, id = 0, i;
> +
> +	for (i = 0; i < n_pipelines; i++) {
> +		struct app_pipeline_params *p = &app->pipeline_params[i];
> +		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
> +			RTE_DIM(p->pktq_out));
> +		uint32_t j;
> +
> +		for (j = 0; j < n_pktq_out; j++) {
> +			struct app_pktq_out_params *pktq = &p-
> >pktq_out[j];
> +
> +			if ((pktq->type == APP_PKTQ_OUT_KNI) &&
> +				(pktq->id == pos)) {
> +				n_writers++;
>  				writer = p;
>  				id = j;
> +			}
>  		}
>  	}
> 
> @@ -1051,6 +1200,22 @@ app_get_link_for_tm(struct app_params *app,
> struct app_pktq_tm_params *p_tm)
>  	return &app->link_params[link_param_idx];
>  }
> 
> +static inline struct app_link_params *
> +app_get_link_for_kni(struct app_params *app, struct
> app_pktq_kni_params *p_kni)
> +{
> +	char link_name[APP_PARAM_NAME_SIZE];
> +	uint32_t link_id;
> +	ssize_t link_param_idx;
> +
> +	sscanf(p_kni->name, "KNI%" PRIu32, &link_id);
> +	sprintf(link_name, "LINK%" PRIu32, link_id);
> +	link_param_idx = APP_PARAM_FIND(app->link_params, link_name);
> +	APP_CHECK((link_param_idx >= 0),
> +			  "Cannot find %s for %s", link_name, p_kni->name);
> +
> +	return &app->link_params[link_param_idx];
> +}
> +
>  void app_pipeline_params_get(struct app_params *app,
>  	struct app_pipeline_params *p_in,
>  	struct pipeline_params *p_out);
> diff --git a/examples/ip_pipeline/config/kni.cfg
> b/examples/ip_pipeline/config/kni.cfg
> new file mode 100644
> index 0000000..c339080
> --- /dev/null
> +++ b/examples/ip_pipeline/config/kni.cfg
> @@ -0,0 +1,67 @@
> +;   BSD LICENSE
> +;
> +;   Copyright(c) 2016 Intel Corporation.
> +;   All rights reserved.
> +;
> +;   Redistribution and use in source and binary forms, with or without
> +;   modification, are permitted provided that the following conditions
> +;   are met:
> +;
> +;     * Redistributions of source code must retain the above copyright
> +;       notice, this list of conditions and the following disclaimer.
> +;     * Redistributions in binary form must reproduce the above copyright
> +;       notice, this list of conditions and the following disclaimer in
> +;       the documentation and/or other materials provided with the
> +;       distribution.
> +;     * Neither the name of Intel Corporation nor the names of its
> +;       contributors may be used to endorse or promote products derived
> +;       from this software without specific prior written permission.
> +;
> +;   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> +;   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> +;   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> +;   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> +;   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> +;   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> +;   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> +;   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> +;   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> +;   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> +;   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> +
> +;             ______________          ______________________
> +;            |              |  KNI0  |                      |
> +; RXQ0.0 --->|              |------->|--+                   |
> +;            |              |  KNI1  |  | br0               |
> +; TXQ0.0 <---|              |<-------|<-+                   |
> +;            | Pass-through |        |     Linux Kernel     |
> +;            |     (P1)     |        |     Network Stack    |
> +;            |              |  KNI0  |                      |
> +; RXQ0.0 --->|              |------->|--+                   |
> +;            |              |  KNI1  |  | br0               |
> +; TXQ0.0 <---|              |<-------|<-+                   |
> +;            |______________|        |______________________|
> +;

There are some errors in the diagram I previously sent you, here is the corrected version (which matches the PIPELINE1 configuration):

;             ______________          ______________________ 
;            |              |  KNI0  |                      |
; RXQ0.0 --->|              |------->|--+                   |
;            |              |  KNI1  |  | br0               |
; TXQ1.0 <---|              |<-------|<-+                   |
;            | Pass-through |        |     Linux Kernel     |
;            |     (P1)     |        |     Network Stack    |
;            |              |  KNI1  |                      |
; RXQ1.0 --->|              |------->|--+                   |
;            |              |  KNI0  |  | br0               |
; TXQ0.0 <---|              |<-------|<-+                   |
;            |______________|        |______________________|

> +; Insert Linux kernel KNI module:
> +;    [Linux]$ insmod rte_kni.ko
> +;
> +; Configure Linux kernel bridge between KNI0 and KNI1 interfaces:
> +;    [Linux]$ ifconfig KNI0 up
> +;    [Linux]$ ifconfig KNI1 up
> +;    [Linux]$ brctl addbr "br0"
> +;    [Linux]$ brctl addif br0 KNI0
> +;    [Linux]$ brctl addif br0 KNI1
> +;    [Linux]$ ifconfig br0 up
> +
> +[EAL]
> +log_level = 0
> +
> +[PIPELINE0]
> +type = MASTER
> +core = 0
> +
> +[PIPELINE1]
> +type = PASS-THROUGH
> +core = 1
> +pktq_in = RXQ0.0 KNI1 RXQ1.0 KNI0
> +pktq_out = KNI0 TXQ1.0 KNI1 TXQ0.0
> diff --git a/examples/ip_pipeline/config_check.c
> b/examples/ip_pipeline/config_check.c
> index 18f57be..7e90342 100644
> --- a/examples/ip_pipeline/config_check.c
> +++ b/examples/ip_pipeline/config_check.c
> @@ -1,7 +1,7 @@
>  /*-
>   *   BSD LICENSE
>   *
> - *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
> + *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
>   *   All rights reserved.
>   *
>   *   Redistribution and use in source and binary forms, with or without
> @@ -444,6 +444,29 @@ check_pipelines(struct app_params *app)
>  	}
>  }
> 
> +static void
> +check_knis(struct app_params *app) {
> +	uint32_t i;
> +
> +	for (i = 0; i < app->n_pktq_kni; i++) {
> +		struct app_pktq_kni_params *p = &app->kni_params[i];
> +		uint32_t n_readers = app_kni_get_readers(app, p);
> +		uint32_t n_writers = app_kni_get_writers(app, p);
> +
> +		APP_CHECK((n_readers != 0),
> +			"%s has no reader\n", p->name);
> +
> +		APP_CHECK((n_readers == 1),
> +			"%s has more than one reader\n", p->name);
> +
> +		APP_CHECK((n_writers != 0),
> +			"%s has no writer\n", p->name);
> +
> +		APP_CHECK((n_writers == 1),
> +			"%s has more than one writer\n", p->name);
> +	}
> +}

I suggest you place this function just after the check_tms() function, in order to keep consistency with our conventions.

> +
>  int
>  app_config_check(struct app_params *app)
>  {
> @@ -453,6 +476,7 @@ app_config_check(struct app_params *app)
>  	check_txqs(app);
>  	check_swqs(app);
>  	check_tms(app);
> +	check_knis(app);
>  	check_sources(app);
>  	check_sinks(app);
>  	check_msgqs(app);
> diff --git a/examples/ip_pipeline/config_parse.c
> b/examples/ip_pipeline/config_parse.c
> index 504018e..c55be31 100644
> --- a/examples/ip_pipeline/config_parse.c
> +++ b/examples/ip_pipeline/config_parse.c
> @@ -189,6 +189,18 @@ struct app_pktq_tm_params default_tm_params = {
>  	.burst_write = 32,
>  };
> 
> +struct app_pktq_kni_params default_kni_params = {
> +	.parsed = 0,
> +	.socket_id = 0,
> +	.core_id = 0,
> +	.hyper_th_id = 0,
> +	.force_bind = 0,
> +
> +	.mempool_id = 0,
> +	.burst_read = 32,
> +	.burst_write = 32,
> +};
> +
>  struct app_pktq_source_params default_source_params = {
>  	.parsed = 0,
>  	.mempool_id = 0,
> @@ -300,6 +312,18 @@ app_print_usage(char *prgname)
>  	link_param_pos;
> 	\
>  })
> 
> +#define APP_PARAM_ADD_LINK_FOR_KNI(app, kni_name)
> 	\
> +({									\
> +	char link_name[APP_PARAM_NAME_SIZE];
> 	\
> +	ssize_t link_param_pos;
> 	\
> +	uint32_t link_id;						\
> +									\
> +	sscanf((kni_name), "KNI%" SCNu32, &link_id);		\
> +	sprintf(link_name, "LINK%" PRIu32, link_id);			\
> +	link_param_pos = APP_PARAM_ADD((app)->link_params,
> link_name);	\
> +	link_param_pos;
> 	\
> +})
> +
>  #define PARSE_CHECK_DUPLICATE_SECTION(obj)
> 	\
>  do {									\
>  	APP_CHECK(((obj)->parsed == 0),
> 	\
> @@ -826,6 +850,10 @@ parse_pipeline_pktq_in(struct app_params *app,
>  			type = APP_PKTQ_IN_TM;
>  			id = APP_PARAM_ADD(app->tm_params, name);
>  			APP_PARAM_ADD_LINK_FOR_TM(app, name);
> +		} else if (validate_name(name, "KNI", 1) == 0) {
> +			type = APP_PKTQ_IN_KNI;
> +			id = APP_PARAM_ADD(app->kni_params, name);
> +			APP_PARAM_ADD_LINK_FOR_KNI(app, name);
>  		} else if (validate_name(name, "SOURCE", 1) == 0) {
>  			type = APP_PKTQ_IN_SOURCE;
>  			id = APP_PARAM_ADD(app->source_params, name);
> @@ -871,6 +899,10 @@ parse_pipeline_pktq_out(struct app_params *app,
>  			type = APP_PKTQ_OUT_TM;
>  			id = APP_PARAM_ADD(app->tm_params, name);
>  			APP_PARAM_ADD_LINK_FOR_TM(app, name);
> +		} else if (validate_name(name, "KNI", 1) == 0) {
> +			type = APP_PKTQ_OUT_KNI;
> +			id = APP_PARAM_ADD(app->kni_params, name);
> +			APP_PARAM_ADD_LINK_FOR_KNI(app, name);
>  		} else if (validate_name(name, "SINK", 1) == 0) {
>  			type = APP_PKTQ_OUT_SINK;
>  			id = APP_PARAM_ADD(app->sink_params, name);
> @@ -1816,7 +1848,7 @@ parse_tm(struct app_params *app,
>  	param = &app->tm_params[param_idx];
>  	PARSE_CHECK_DUPLICATE_SECTION(param);
> 
> -	APP_PARAM_ADD_LINK_FOR_TXQ(app, section_name);
> +	APP_PARAM_ADD_LINK_FOR_TM(app, section_name);
> 
>  	for (i = 0; i < n_entries; i++) {
>  		struct rte_cfgfile_entry *ent = &entries[i];
> @@ -1853,6 +1885,85 @@ parse_tm(struct app_params *app,
>  }
> 
>  static void
> +parse_kni(struct app_params *app,
> +		  const char *section_name,
> +		  struct rte_cfgfile *cfg)
> +{
> +	struct app_pktq_kni_params *param;
> +	struct rte_cfgfile_entry *entries;
> +	int n_entries, i;
> +	ssize_t param_idx;
> +
> +	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
> +	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0),
> section_name);
> +
> +	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
> +	PARSE_ERROR_MALLOC(entries != NULL);
> +
> +	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
> +
> +	param_idx = APP_PARAM_ADD(app->kni_params, section_name);
> +	param = &app->kni_params[param_idx];
> +	PARSE_CHECK_DUPLICATE_SECTION(param);
> +
> +	APP_PARAM_ADD_LINK_FOR_KNI(app, section_name);
> +
> +
> +	for (i = 0; i < n_entries; i++) {
> +		struct rte_cfgfile_entry *ent = &entries[i];
> +
> +		if (strcmp(ent->name, "core") == 0) {
> +			int status = parse_pipeline_core(
> +					&param->socket_id,
> +					&param->core_id,
> +					&param->hyper_th_id,
> +					ent->value);
> +
> +			PARSE_ERROR((status == 0), section_name,
> +						ent->name);
> +			param->force_bind = 1;
> +			continue;
> +		}
> +
> +		if (strcmp(ent->name, "mempool") == 0) {
> +			int status = validate_name(ent->value,
> +
> "MEMPOOL", 1);
> +			ssize_t idx;
> +
> +			PARSE_ERROR((status == 0), section_name,
> +						ent->name);
> +
> +			idx = APP_PARAM_ADD(app->mempool_params,
> ent->value);
> +			param->mempool_id = idx;
> +			continue;
> +		}
> +
> +		if (strcmp(ent->name, "burst_read") == 0) {
> +			int status = parser_read_uint32(&param-
> >burst_read,
> +
> 		ent->value);
> +
> +			PARSE_ERROR((status == 0), section_name,
> +						ent->name);
> +			continue;
> +		}
> +
> +		if (strcmp(ent->name, "burst_write") == 0) {
> +			int status = parser_read_uint32(&param-
> >burst_write,
> +
> 		ent->value);
> +
> +			PARSE_ERROR((status == 0), section_name,
> +						ent->name);
> +			continue;
> +		}
> +
> +		/* unrecognized */
> +		PARSE_ERROR_INVALID(0, section_name, ent->name);
> +	}
> +

Here is one bug for you, you need to make sure you add the following line here:
	param->parsed = 1;

> +	free(entries);
> +}
> +
> +static void
>  parse_source(struct app_params *app,
>  	const char *section_name,
>  	struct rte_cfgfile *cfg)
> @@ -2147,6 +2258,7 @@ static const struct config_section cfg_file_scheme[]
> = {
>  	{"TXQ", 2, parse_txq},
>  	{"SWQ", 1, parse_swq},
>  	{"TM", 1, parse_tm},
> +	{"KNI", 1, parse_kni},
>  	{"SOURCE", 1, parse_source},
>  	{"SINK", 1, parse_sink},
>  	{"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
> @@ -2285,6 +2397,7 @@ app_config_parse(struct app_params *app, const
> char *file_name)
>  	APP_PARAM_COUNT(app->hwq_out_params, app-
> >n_pktq_hwq_out);
>  	APP_PARAM_COUNT(app->swq_params, app->n_pktq_swq);
>  	APP_PARAM_COUNT(app->tm_params, app->n_pktq_tm);
> +	APP_PARAM_COUNT(app->kni_params, app->n_pktq_kni);
>  	APP_PARAM_COUNT(app->source_params, app->n_pktq_source);
>  	APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
>  	APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
> @@ -2647,6 +2760,45 @@ save_tm_params(struct app_params *app, FILE
> *f)
>  }
> 
>  static void
> +save_kni_params(struct app_params *app, FILE *f)
> +{
> +	struct app_pktq_kni_params *p;
> +	size_t i, count;
> +
> +	count = RTE_DIM(app->kni_params);
> +	for (i = 0; i < count; i++) {
> +		p = &app->kni_params[i];
> +		if (!APP_PARAM_VALID(p))
> +			continue;
> +
> +		/* section name */
> +		fprintf(f, "[%s]\n", p->name);
> +
> +		/* core */
> +		if (p->force_bind) {
> +			fprintf(f, "; force_bind = 1\n");
> +			fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
> +					p->socket_id,
> +					p->core_id,
> +					(p->hyper_th_id) ? "h" : "");
> +		} else
> +			fprintf(f, "; force_bind = 0\n");
> +
> +		/* mempool */
> +		fprintf(f, "%s = %s\n", "mempool",
> +				app->mempool_params[p-
> >mempool_id].name);
> +
> +		/* burst_read */
> +		fprintf(f, "%s = %" PRIu32 "\n", "burst_read", p-
> >burst_read);
> +
> +		/* burst_write */
> +		fprintf(f, "%s = %" PRIu32 "\n", "burst_write", p-
> >burst_write);
> +
> +		fputc('\n', f);
> +	}
> +}
> +
> +static void
>  save_source_params(struct app_params *app, FILE *f)
>  {
>  	struct app_pktq_source_params *p;
> @@ -2753,6 +2905,9 @@ save_pipeline_params(struct app_params *app,
> FILE *f)
>  				case APP_PKTQ_IN_TM:
>  					name = app->tm_params[pp-
> >id].name;
>  					break;
> +				case APP_PKTQ_IN_KNI:
> +					name = app->kni_params[pp-
> >id].name;
> +					break;
>  				case APP_PKTQ_IN_SOURCE:
>  					name = app->source_params[pp-
> >id].name;
>  					break;
> @@ -2787,6 +2942,9 @@ save_pipeline_params(struct app_params *app,
> FILE *f)
>  				case APP_PKTQ_OUT_TM:
>  					name = app->tm_params[pp-
> >id].name;
>  					break;
> +				case APP_PKTQ_OUT_KNI:
> +					name = app->kni_params[pp-
> >id].name;
> +					break;
>  				case APP_PKTQ_OUT_SINK:
>  					name = app->sink_params[pp-
> >id].name;
>  					break;
> @@ -2872,6 +3030,7 @@ app_config_save(struct app_params *app, const
> char *file_name)
>  	save_txq_params(app, file);
>  	save_swq_params(app, file);
>  	save_tm_params(app, file);
> +	save_kni_params(app, file);
>  	save_source_params(app, file);
>  	save_sink_params(app, file);
>  	save_msgq_params(app, file);
> @@ -2921,6 +3080,11 @@ app_config_init(struct app_params *app)
>  			&default_tm_params,
>  			sizeof(default_tm_params));
> 
> +	for (i = 0; i < RTE_DIM(app->kni_params); i++)
> +		memcpy(&app->kni_params[i],
> +			   &default_kni_params,
> +			   sizeof(default_kni_params));
> +
>  	for (i = 0; i < RTE_DIM(app->source_params); i++)
>  		memcpy(&app->source_params[i],
>  			&default_source_params,
> diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c

In order to keep up with our existing code conventions, please move the KNI code (functions kni_change_mtu(), kni_config_network_interface(), app_init_kni()) just after function app_init_tm().

> index 7120bab..d522de4 100644
> --- a/examples/ip_pipeline/init.c
> +++ b/examples/ip_pipeline/init.c
> @@ -1281,10 +1281,21 @@ void app_pipeline_params_get(struct
> app_params *app,
>  			break;
>  		}
>  		case APP_PKTQ_IN_TM:
> +		{
>  			out->type = PIPELINE_PORT_IN_SCHED_READER;
>  			out->params.sched.sched = app->tm[in->id];
>  			out->burst_size = app->tm_params[in-
> >id].burst_read;
>  			break;
> +		}
> +#ifdef RTE_LIBRTE_KNI
> +		case APP_PKTQ_IN_KNI:
> +		{
> +			out->type = PIPELINE_PORT_IN_KNI_READER;
> +			out->params.kni.kni = app->kni[in->id];
> +			out->burst_size = app->kni_params[in-
> >id].burst_read;
> +			break;
> +		}
> +#endif /* RTE_LIBRTE_KNI */
>  		case APP_PKTQ_IN_SOURCE:
>  		{
>  			uint32_t mempool_id =
> @@ -1409,7 +1420,8 @@ void app_pipeline_params_get(struct app_params
> *app,
>  			}
>  			break;
>  		}
> -		case APP_PKTQ_OUT_TM: {
> +		case APP_PKTQ_OUT_TM:
> +		{
>  			struct rte_port_sched_writer_params *params =
>  				&out->params.sched;
> 
> @@ -1419,6 +1431,16 @@ void app_pipeline_params_get(struct
> app_params *app,
>  				app->tm_params[in->id].burst_write;
>  			break;
>  		}
> +#ifdef RTE_LIBRTE_KNI
> +		case APP_PKTQ_OUT_KNI:
> +		{
> +			out->type = PIPELINE_PORT_OUT_KNI_WRITER;
> +			out->params.kni.kni = app->kni[in->id];
> +			out->params.kni.tx_burst_sz =
> +				app->kni_params[in->id].burst_write;
> +			break;
> +		}
> +#endif /* RTE_LIBRTE_KNI */
>  		case APP_PKTQ_OUT_SINK:
>  		{
>  			out->type = PIPELINE_PORT_OUT_SINK;
> @@ -1452,6 +1474,113 @@ void app_pipeline_params_get(struct
> app_params *app,
>  	}
>  }
> 
> +#ifdef RTE_LIBRTE_KNI
> +static int
> +kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
> +	int ret = 0;
> +
> +	if (port_id >= rte_eth_dev_count())
> +		return -EINVAL;
> +
> +	if (if_up) {
> +		rte_eth_dev_stop(port_id);
> +		ret = rte_eth_dev_start(port_id);
> +	} else
> +		rte_eth_dev_stop(port_id);
> +
> +	return ret;
> +}
> +

Here is a critical issue that we found out recently during testing the KNI code: the correct PMD API functions to use here are rte_eth_dev_set_link_up/down(); stopping and restarting the device while the device is running can lead to segfault and nondeterministic behaviour.

Here is my proposal:

static int
kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
	int ret = 0;

	if (port_id >= rte_eth_dev_count())
		return -EINVAL;

	ret = (if_up)?
		rte_eth_dev_set_link_up(port_id) :
		rte_eth_dev_set_link_down(port_id);

	return ret;
}

I tested this function successfully.

> +static int
> +kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
> +	int ret;
> +
> +	if (port_id >= rte_eth_dev_count())
> +		return -EINVAL;
> +
> +	if (new_mtu > ETHER_MAX_LEN)
> +		return -EINVAL;
> +
> +	/* Stop specific port */
> +	rte_eth_dev_stop(port_id);
> +
> +	/* Set new MTU */
> +	ret = rte_eth_dev_set_mtu(port_id, new_mtu);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Restart specific port */
> +	ret = rte_eth_dev_start(port_id);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}

Same issue about using the rte_eth_dev_start/stop() functions. Based on function port_mtu_set() of the app/test-pmd application, it looks like rte_eth_dev_set_mtu() function could be called directly with no start/stop or up/down preparations.

Here is my version, although I did not fully test this one:

static int
kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
	int ret;

	if (port_id >= rte_eth_dev_count())
		return -EINVAL;

	if (new_mtu > ETHER_MAX_LEN)
		return -EINVAL;

	/* Set new MTU */
	ret = rte_eth_dev_set_mtu(port_id, new_mtu);
	if (ret < 0)
		return ret;

	return 0;
}

> +#endif /* RTE_LIBRTE_KNI */
> +
> +static void
> +app_init_kni(struct app_params *app) {
> +	if (app->n_pktq_kni == 0)
> +		return;
> +#ifndef RTE_LIBRTE_KNI
> +	else
> +		rte_panic("Can not init KNI without librte_kni support.\n");
> +#else

I would like to avoid #ifdefs in the middle of the function code as much as possible. I suggest we do the following:

#ifndef RTE_LIBRTE_KNI

static void
app_init_kni(struct app_params *app)
{
	if (app->n_pktq_kni == 0)
		return;

	rte_panic("Cannot init KNI without librte_kni support.\n");
}

#else

static void
app_init_kni(struct app_params *app)
{
	//the real function code
}

#endif

> +	rte_kni_init(app->n_pktq_kni);
> +
> +	for (uint32_t i = 0; i < app->n_pktq_kni; i++) {

Please declare the "i" variable at the start of the function, as usual. Although I personally like this idea, this pattern is not used anywhere in DPDK and it might trigger warnings/errors from some C compilers. Btw, any idea whether this is mentioned in any way by the ANSI C/C99 standards?

> +		struct app_pktq_kni_params *p_kni = &app->kni_params[i];
> +		struct app_link_params *p_link;
> +		struct rte_eth_dev_info dev_info;
> +		struct app_mempool_params *mempool_params;
> +		struct rte_mempool *mempool;
> +		struct rte_kni_conf conf;
> +		struct rte_kni_ops ops;
> +
> +		/* LINK */
> +		p_link = app_get_link_for_kni(app, p_kni);
> +		memset(&dev_info, 0, sizeof(dev_info));
> +		rte_eth_dev_info_get(p_link->pmd_id, &dev_info);
> +
> +		/* MEMPOOL */
> +		mempool_params = &app->mempool_params[p_kni-
> >mempool_id];
> +		mempool = app->mempool[p_kni->mempool_id];
> +
> +		/* KNI */
> +		memset(&conf, 0, sizeof(conf));
> +		snprintf(conf.name, RTE_KNI_NAMESIZE, "%s", p_kni-
> >name);
> +		conf.force_bind = p_kni->force_bind;
> +		if (conf.force_bind) {
> +			int lcore_id;
> +
> +			lcore_id = cpu_core_map_get_lcore_id(app-
> >core_map,
> +				p_kni->socket_id,
> +				p_kni->core_id,
> +				p_kni->hyper_th_id);
> +
> +			if (lcore_id < 0)
> +				rte_panic("%s invalid CPU core\n", p_kni-
> >name);
> +
> +			conf.core_id = (uint32_t) lcore_id;
> +		}
> +		conf.group_id = p_link->pmd_id;
> +		conf.mbuf_size = mempool_params->buffer_size;
> +		conf.addr = dev_info.pci_dev->addr;
> +		conf.id = dev_info.pci_dev->id;
> +
> +		memset(&ops, 0, sizeof(ops));
> +		ops.port_id = (uint8_t) p_link->pmd_id;
> +		ops.change_mtu = kni_change_mtu;
> +		ops.config_network_if = kni_config_network_interface;
> +
> +		APP_LOG(app, HIGH, "Initializing %s ...", p_kni->name);
> +		app->kni[i] = rte_kni_alloc(mempool, &conf, &ops);
> +		if (!app->kni[i])
> +			rte_panic("%s init error\n", p_kni->name);
> +	}
> +#endif /* RTE_LIBRTE_KNI */
> +}
> +
>  static void
>  app_init_pipelines(struct app_params *app)
>  {
> @@ -1607,6 +1736,7 @@ int app_init(struct app_params *app)
>  	app_init_link(app);
>  	app_init_swq(app);
>  	app_init_tm(app);
> +	app_init_kni(app);
>  	app_init_msgq(app);
> 
>  	app_pipeline_common_cmd_push(app);
> diff --git a/examples/ip_pipeline/pipeline/pipeline_common_fe.c
> b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
> index 70c57e4..63723cd 100644
> --- a/examples/ip_pipeline/pipeline/pipeline_common_fe.c
> +++ b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
> @@ -130,6 +130,35 @@ app_pipeline_track_pktq_out_to_link(struct
> app_params *app,
>  			break;
>  		}
> 
> +#ifdef RTE_LIBRTE_KNI

As in any other place that just deals with KNI configuration data rather than KNI init data (e.g. in app.h or config_parse.c), we do not need to #ifdef the code fragment below. Please remove this instance of #ifdef RTE_LIBRTE_KNI.

> +		case APP_PKTQ_OUT_KNI:
> +		{
> +			struct pipeline_params pp;
> +			struct pipeline_type *ptype;
> +			struct app_pktq_kni_params *kni;
> +			uint32_t pktq_in_id;
> +			int status;
> +
> +			kni = &app->kni_params[pktq_out->id];
> +			p = app_kni_get_reader(app, kni, &pktq_in_id);
> +			if (p == NULL)
> +				return NULL;
> +
> +			ptype = app_pipeline_type_find(app, p->type);
> +			if ((ptype == NULL) || (ptype->fe_ops->f_track ==
> NULL))
> +				return NULL;
> +
> +			app_pipeline_params_get(app, p, &pp);
> +			status = ptype->fe_ops->f_track(&pp,
> +				pktq_in_id,
> +				&pktq_out_id);
> +			if (status)
> +				return NULL;
> +
> +			break;
> +		}
> +#endif
> +
>  		case APP_PKTQ_OUT_SINK:
>  		default:
>  			return NULL;
> diff --git a/examples/ip_pipeline/pipeline/pipeline_master_be.c
> b/examples/ip_pipeline/pipeline/pipeline_master_be.c
> index 79869a4..32d4635 100644
> --- a/examples/ip_pipeline/pipeline/pipeline_master_be.c
> +++ b/examples/ip_pipeline/pipeline/pipeline_master_be.c
> @@ -144,6 +144,12 @@ pipeline_run(void *pipeline)
>  		rte_exit(0, "Bye!\n");
>  	}
> 
> +#ifdef RTE_LIBRTE_KNI
> +	/* Handle KNI requests from Linux kernel */
> +	for (uint32_t i = 0; i < app->n_pktq_kni; i++)

Same comment as above on definition of the "i" variable.

> +		rte_kni_handle_request(app->kni[i]);
> +#endif /* RTE_LIBRTE_KNI */
> +
>  	return 0;
>  }
> 
> diff --git a/examples/ip_pipeline/pipeline_be.h
> b/examples/ip_pipeline/pipeline_be.h
> index 5501ab7..0ee208c 100644
> --- a/examples/ip_pipeline/pipeline_be.h
> +++ b/examples/ip_pipeline/pipeline_be.h
> @@ -40,6 +40,9 @@
>  #include <rte_port_ras.h>
>  #include <rte_port_sched.h>
>  #include <rte_port_source_sink.h>
> +#ifdef RTE_LIBRTE_KNI
> +#include <rte_port_kni.h>
> +#endif
>  #include <rte_pipeline.h>
> 
>  enum pipeline_port_in_type {
> @@ -49,6 +52,7 @@ enum pipeline_port_in_type {
>  	PIPELINE_PORT_IN_RING_READER_IPV4_FRAG,
>  	PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
>  	PIPELINE_PORT_IN_SCHED_READER,
> +	PIPELINE_PORT_IN_KNI_READER,
>  	PIPELINE_PORT_IN_SOURCE,
>  };
> 
> @@ -61,6 +65,9 @@ struct pipeline_port_in_params {
>  		struct rte_port_ring_reader_ipv4_frag_params
> ring_ipv4_frag;
>  		struct rte_port_ring_reader_ipv6_frag_params
> ring_ipv6_frag;
>  		struct rte_port_sched_reader_params sched;
> +#ifdef RTE_LIBRTE_KNI
> +		struct rte_port_kni_reader_params kni;
> +#endif
>  		struct rte_port_source_params source;
>  	} params;
>  	uint32_t burst_size;
> @@ -82,6 +89,10 @@ pipeline_port_in_params_convert(struct
> pipeline_port_in_params  *p)
>  		return (void *) &p->params.ring_ipv6_frag;
>  	case PIPELINE_PORT_IN_SCHED_READER:
>  		return (void *) &p->params.sched;
> +#ifdef RTE_LIBRTE_KNI
> +	case PIPELINE_PORT_IN_KNI_READER:
> +		return (void *) &p->params.kni;
> +#endif
>  	case PIPELINE_PORT_IN_SOURCE:
>  		return (void *) &p->params.source;
>  	default:
> @@ -105,6 +116,10 @@ pipeline_port_in_params_get_ops(struct
> pipeline_port_in_params  *p)
>  		return &rte_port_ring_reader_ipv6_frag_ops;
>  	case PIPELINE_PORT_IN_SCHED_READER:
>  		return &rte_port_sched_reader_ops;
> +#ifdef RTE_LIBRTE_KNI
> +	case PIPELINE_PORT_IN_KNI_READER:
> +		return &rte_port_kni_reader_ops;
> +#endif
>  	case PIPELINE_PORT_IN_SOURCE:
>  		return &rte_port_source_ops;
>  	default:
> @@ -122,6 +137,7 @@ enum pipeline_port_out_type {
>  	PIPELINE_PORT_OUT_RING_WRITER_IPV4_RAS,
>  	PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
>  	PIPELINE_PORT_OUT_SCHED_WRITER,
> +	PIPELINE_PORT_OUT_KNI_WRITER,
>  	PIPELINE_PORT_OUT_SINK,
>  };
> 
> @@ -137,6 +153,9 @@ struct pipeline_port_out_params {
>  		struct rte_port_ring_writer_ipv4_ras_params ring_ipv4_ras;
>  		struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
>  		struct rte_port_sched_writer_params sched;
> +#ifdef RTE_LIBRTE_KNI
> +		struct rte_port_kni_writer_params kni;
> +#endif
>  		struct rte_port_sink_params sink;
>  	} params;
>  };
> @@ -163,6 +182,10 @@ pipeline_port_out_params_convert(struct
> pipeline_port_out_params  *p)
>  		return (void *) &p->params.ring_ipv6_ras;
>  	case PIPELINE_PORT_OUT_SCHED_WRITER:
>  		return (void *) &p->params.sched;
> +#ifdef RTE_LIBRTE_KNI
> +	case PIPELINE_PORT_OUT_KNI_WRITER:
> +		return (void *) &p->params.kni;
> +#endif
>  	case PIPELINE_PORT_OUT_SINK:
>  		return (void *) &p->params.sink;
>  	default:
> @@ -192,6 +215,10 @@ pipeline_port_out_params_get_ops(struct
> pipeline_port_out_params  *p)
>  		return &rte_port_ring_writer_ipv6_ras_ops;
>  	case PIPELINE_PORT_OUT_SCHED_WRITER:
>  		return &rte_port_sched_writer_ops;
> +#ifdef RTE_LIBRTE_KNI
> +	case PIPELINE_PORT_OUT_KNI_WRITER:
> +		return &rte_port_kni_writer_ops;
> +#endif
>  	case PIPELINE_PORT_OUT_SINK:
>  		return &rte_port_sink_ops;
>  	default:
> diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
> index dc6a601..0fc929b 100644
> --- a/lib/librte_port/Makefile
> +++ b/lib/librte_port/Makefile
> @@ -56,6 +56,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_frag.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
>  endif
>  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
> +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> +SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
> +endif
>  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
> 
>  # install includes
> @@ -67,6 +70,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=
> rte_port_frag.h
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_ras.h
>  endif
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
> +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> +SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
> +endif
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
> 
>  # this lib depends upon:
> @@ -76,5 +82,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=
> lib/librte_mempool
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_sched
> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
> 
>  include $(RTE_SDK)/mk/rte.lib.mk
> diff --git a/lib/librte_port/rte_port_kni.c b/lib/librte_port/rte_port_kni.c
> new file mode 100644
> index 0000000..4cbc345
> --- /dev/null
> +++ b/lib/librte_port/rte_port_kni.c
> @@ -0,0 +1,325 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2016 Ethan Zhuang <zhuangwj@gmail.com>.
> + *   Copyright(c) 2016 Intel Corporation.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> + */
> +#include <string.h>
> +
> +#include <rte_common.h>
> +#include <rte_malloc.h>
> +#include <rte_kni.h>
> +
> +#include "rte_port_kni.h"
> +
> +/*
> + * Port KNI Reader
> + */
> +#ifdef RTE_PORT_STATS_COLLECT
> +
> +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
> +	port->stats.n_pkts_in += val
> +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
> +	port->stats.n_pkts_drop += val
> +
> +#else
> +
> +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
> +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
> +
> +#endif
> +
> +struct rte_port_kni_reader {
> +	struct rte_port_in_stats stats;
> +
> +	struct rte_kni *kni;
> +};
> +
> +static void *
> +rte_port_kni_reader_create(void *params, int socket_id)
> +{
> +	struct rte_port_kni_reader_params *conf =
> +			(struct rte_port_kni_reader_params *) params;
> +	struct rte_port_kni_reader *port;
> +
> +	/* Check input parameters */
> +	if (conf == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
> +		return NULL;
> +	}
> +
> +	/* Memory allocation */
> +	port = rte_zmalloc_socket("PORT", sizeof(*port),
> +		RTE_CACHE_LINE_SIZE, socket_id);
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> __func__);
> +		return NULL;
> +	}
> +
> +	/* Initialization */
> +	port->kni = conf->kni;
> +
> +	return port;
> +}
> +
> +static int
> +rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t
> n_pkts)
> +{
> +	struct rte_port_kni_reader *p =
> +			(struct rte_port_kni_reader *) port;
> +	uint16_t rx_pkt_cnt;
> +
> +	rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
> +	RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
> +	return rx_pkt_cnt;
> +}
> +
> +static int
> +rte_port_kni_reader_free(void *port)
> +{
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	rte_free(port);
> +
> +	return 0;
> +}
> +
> +static int rte_port_kni_reader_stats_read(void *port,
> +	struct rte_port_in_stats *stats, int clear)
> +{
> +	struct rte_port_kni_reader *p =
> +			(struct rte_port_kni_reader *) port;
> +
> +	if (stats != NULL)
> +		memcpy(stats, &p->stats, sizeof(p->stats));
> +
> +	if (clear)
> +		memset(&p->stats, 0, sizeof(p->stats));
> +
> +	return 0;
> +}
> +
> +/*
> + * Port KNI Writer
> + */
> +#ifdef RTE_PORT_STATS_COLLECT
> +
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
> +	port->stats.n_pkts_in += val
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
> +	port->stats.n_pkts_drop += val
> +
> +#else
> +
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
> +
> +#endif
> +
> +struct rte_port_kni_writer {
> +	struct rte_port_out_stats stats;
> +
> +	struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
> +	uint32_t tx_burst_sz;
> +	uint32_t tx_buf_count;
> +	uint64_t bsz_mask;
> +	struct rte_kni *kni;
> +};
> +
> +static void *
> +rte_port_kni_writer_create(void *params, int socket_id)
> +{
> +	struct rte_port_kni_writer_params *conf =
> +			(struct rte_port_kni_writer_params *) params;
> +	struct rte_port_kni_writer *port;
> +
> +	/* Check input parameters */
> +	if ((conf == NULL) ||
> +		(conf->tx_burst_sz == 0) ||
> +		(conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
> +		(!rte_is_power_of_2(conf->tx_burst_sz))) {
> +		RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n",
> __func__);
> +		return NULL;
> +	}
> +
> +	/* Memory allocation */
> +	port = rte_zmalloc_socket("PORT", sizeof(*port),
> +		RTE_CACHE_LINE_SIZE, socket_id);
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> __func__);
> +		return NULL;
> +	}
> +
> +	/* Initialization */
> +	port->kni = conf->kni;
> +	port->tx_burst_sz = conf->tx_burst_sz;
> +	port->tx_buf_count = 0;
> +	port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
> +
> +	return port;
> +}
> +
> +static inline void
> +send_burst(struct rte_port_kni_writer *p)
> +{
> +	uint32_t nb_tx;
> +
> +	nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
> +
> +	RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p-
> >tx_buf_count - nb_tx);
> +	for (; nb_tx < p->tx_buf_count; nb_tx++)
> +		rte_pktmbuf_free(p->tx_buf[nb_tx]);
> +
> +	p->tx_buf_count = 0;
> +}
> +
> +static int
> +rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt)
> +{
> +	struct rte_port_kni_writer *p =
> +			(struct rte_port_kni_writer *) port;
> +
> +	p->tx_buf[p->tx_buf_count++] = pkt;
> +	RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> +	if (p->tx_buf_count >= p->tx_burst_sz)
> +		send_burst(p);
> +
> +	return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_tx_bulk(void *port,
> +	struct rte_mbuf **pkts,
> +	uint64_t pkts_mask)
> +{
> +	struct rte_port_kni_writer *p =
> +			(struct rte_port_kni_writer *) port;
> +	uint64_t bsz_mask = p->bsz_mask;
> +	uint32_t tx_buf_count = p->tx_buf_count;
> +	uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
> +					((pkts_mask & bsz_mask) ^
> bsz_mask);
> +
> +	if (expr == 0) {
> +		uint64_t n_pkts = __builtin_popcountll(pkts_mask);
> +		uint32_t n_pkts_ok;
> +
> +		if (tx_buf_count)
> +			send_burst(p);
> +
> +		RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
> +		n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
> +
> +		RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts
> - n_pkts_ok);
> +		for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
> +			struct rte_mbuf *pkt = pkts[n_pkts_ok];
> +
> +			rte_pktmbuf_free(pkt);
> +		}
> +	} else {
> +		for (; pkts_mask;) {
> +			uint32_t pkt_index = __builtin_ctzll(pkts_mask);
> +			uint64_t pkt_mask = 1LLU << pkt_index;
> +			struct rte_mbuf *pkt = pkts[pkt_index];
> +
> +			p->tx_buf[tx_buf_count++] = pkt;
> +			RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> +			pkts_mask &= ~pkt_mask;
> +		}
> +
> +		p->tx_buf_count = tx_buf_count;
> +		if (tx_buf_count >= p->tx_burst_sz)
> +			send_burst(p);
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_flush(void *port)
> +{
> +	struct rte_port_kni_writer *p =
> +			(struct rte_port_kni_writer *) port;
> +
> +	if (p->tx_buf_count > 0)
> +		send_burst(p);
> +
> +	return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_free(void *port)
> +{
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	rte_port_kni_writer_flush(port);
> +	rte_free(port);
> +
> +	return 0;
> +}
> +
> +static int rte_port_kni_writer_stats_read(void *port,
> +	struct rte_port_out_stats *stats, int clear)
> +{
> +	struct rte_port_kni_writer *p =
> +			(struct rte_port_kni_writer *) port;
> +
> +	if (stats != NULL)
> +		memcpy(stats, &p->stats, sizeof(p->stats));
> +
> +	if (clear)
> +		memset(&p->stats, 0, sizeof(p->stats));
> +
> +	return 0;
> +}
> +
> +/*
> + * Summary of port operations
> + */
> +struct rte_port_in_ops rte_port_kni_reader_ops = {
> +	.f_create = rte_port_kni_reader_create,
> +	.f_free = rte_port_kni_reader_free,
> +	.f_rx = rte_port_kni_reader_rx,
> +	.f_stats = rte_port_kni_reader_stats_read,
> +};
> +
> +struct rte_port_out_ops rte_port_kni_writer_ops = {
> +	.f_create = rte_port_kni_writer_create,
> +	.f_free = rte_port_kni_writer_free,
> +	.f_tx = rte_port_kni_writer_tx,
> +	.f_tx_bulk = rte_port_kni_writer_tx_bulk,
> +	.f_flush = rte_port_kni_writer_flush,
> +	.f_stats = rte_port_kni_writer_stats_read,
> +};
> diff --git a/lib/librte_port/rte_port_kni.h b/lib/librte_port/rte_port_kni.h
> new file mode 100644
> index 0000000..d4de8c4
> --- /dev/null
> +++ b/lib/librte_port/rte_port_kni.h
> @@ -0,0 +1,82 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2016 Ethan Zhuang <zhuangwj@gmail.com>.
> + *   Copyright(c) 2016 Intel Corporation.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> + */
> +
> +#ifndef __INCLUDE_RTE_PORT_KNI_H__
> +#define __INCLUDE_RTE_PORT_KNI_H__
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/**
> + * @file
> + * RTE Port KNI Interface
> + *
> + * kni_reader: input port built on top of pre-initialized KNI interface
> + * kni_writer: output port built on top of pre-initialized KNI interface
> + *
> + ***/
> +
> +#include <stdint.h>
> +
> +#include <rte_kni.h>
> +
> +#include "rte_port.h"
> +
> +/** kni_reader port parameters */
> +struct rte_port_kni_reader_params {
> +	/** KNI interface reference */
> +	struct rte_kni *kni;
> +};
> +
> +/** kni_reader port operations */
> +extern struct rte_port_in_ops rte_port_kni_reader_ops;
> +
> +
> +/** kni_writer port parameters */
> +struct rte_port_kni_writer_params {
> +	/** KNI interface reference */
> +	struct rte_kni *kni;
> +	/** Burst size to KNI interface. */
> +	uint32_t tx_burst_sz;
> +};
> +
> +/** kni_writer port operations */
> +extern struct rte_port_out_ops rte_port_kni_writer_ops;
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
> diff --git a/lib/librte_port/rte_port_version.map
> b/lib/librte_port/rte_port_version.map
> index 7a0b34d..e61b3fa 100644
> --- a/lib/librte_port/rte_port_version.map
> +++ b/lib/librte_port/rte_port_version.map
> @@ -35,3 +35,11 @@ DPDK_2.2 {
>  	rte_port_ring_multi_writer_nodrop_ops;
> 
>  } DPDK_2.1;
> +
> +DPDK_16.07 {
> +	global:
> +
> +	rte_port_kni_reader_ops;
> +	rte_port_kni_writer_ops;
> +
> +} DPDK_2.2;
> --
> 2.7.4

Regards,
Cristian

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

* Re: [PATCH v3 2/3] port: add kni nodrop writer
  2016-06-16 11:27     ` [PATCH v3 2/3] port: add kni nodrop writer WeiJie Zhuang
@ 2016-06-18 21:47       ` Dumitrescu, Cristian
  0 siblings, 0 replies; 22+ messages in thread
From: Dumitrescu, Cristian @ 2016-06-18 21:47 UTC (permalink / raw)
  To: WeiJie Zhuang; +Cc: dev, Singh, Jasvinder, Yigit, Ferruh



> -----Original Message-----
> From: WeiJie Zhuang [mailto:zhuangwj@gmail.com]
> Sent: Thursday, June 16, 2016 12:27 PM
> To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>
> Cc: dev@dpdk.org; Singh, Jasvinder <jasvinder.singh@intel.com>; Yigit,
> Ferruh <ferruh.yigit@intel.com>; WeiJie Zhuang <zhuangwj@gmail.com>
> Subject: [PATCH v3 2/3] port: add kni nodrop writer
> 
> 1. add no drop writing operations to the kni port
> 2. support dropless kni config in the ip pipeline sample application
> 
> Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
> ---
>  examples/ip_pipeline/app.h           |   2 +
>  examples/ip_pipeline/config_parse.c  |  31 ++++-
>  examples/ip_pipeline/init.c          |  26 ++++-
>  examples/ip_pipeline/pipeline_be.h   |   6 +
>  lib/librte_port/rte_port_kni.c       | 220
> +++++++++++++++++++++++++++++++++++
>  lib/librte_port/rte_port_kni.h       |  13 +++
>  lib/librte_port/rte_port_version.map |   1 +
>  7 files changed, 292 insertions(+), 7 deletions(-)
> 
> diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
> index abbd6d4..6a6fdd9 100644
> --- a/examples/ip_pipeline/app.h
> +++ b/examples/ip_pipeline/app.h
> @@ -147,6 +147,8 @@ struct app_pktq_kni_params {
>  	uint32_t mempool_id; /* Position in the app->mempool_params */
>  	uint32_t burst_read;
>  	uint32_t burst_write;
> +	uint32_t dropless;
> +	uint64_t n_retries;
>  };
> 
>  #ifndef APP_FILE_NAME_SIZE
> diff --git a/examples/ip_pipeline/config_parse.c
> b/examples/ip_pipeline/config_parse.c
> index c55be31..31a50c2 100644
> --- a/examples/ip_pipeline/config_parse.c
> +++ b/examples/ip_pipeline/config_parse.c
> @@ -199,6 +199,8 @@ struct app_pktq_kni_params default_kni_params = {
>  	.mempool_id = 0,
>  	.burst_read = 32,
>  	.burst_write = 32,
> +	.dropless = 0,
> +	.n_retries = 0,
>  };
> 
>  struct app_pktq_source_params default_source_params = {
> @@ -1927,7 +1929,7 @@ parse_kni(struct app_params *app,
> 
>  		if (strcmp(ent->name, "mempool") == 0) {
>  			int status = validate_name(ent->value,
> -
> "MEMPOOL", 1);
> +				"MEMPOOL", 1);
>  			ssize_t idx;
> 
>  			PARSE_ERROR((status == 0), section_name,
> @@ -1940,7 +1942,7 @@ parse_kni(struct app_params *app,
> 
>  		if (strcmp(ent->name, "burst_read") == 0) {
>  			int status = parser_read_uint32(&param-
> >burst_read,
> -
> 		ent->value);
> +						ent->value);
> 
>  			PARSE_ERROR((status == 0), section_name,
>  						ent->name);
> @@ -1949,7 +1951,25 @@ parse_kni(struct app_params *app,
> 
>  		if (strcmp(ent->name, "burst_write") == 0) {
>  			int status = parser_read_uint32(&param-
> >burst_write,
> -
> 		ent->value);
> +						ent->value);
> +
> +			PARSE_ERROR((status == 0), section_name,
> +						ent->name);
> +			continue;
> +		}
> +
> +		if (strcmp(ent->name, "dropless") == 0) {
> +			int status = parser_read_arg_bool(ent->value);
> +
> +			PARSE_ERROR((status != -EINVAL), section_name,
> +						ent->name);
> +			param->dropless = status;
> +			continue;
> +		}
> +
> +		if (strcmp(ent->name, "n_retries") == 0) {
> +			int status = parser_read_uint64(&param->n_retries,
> +						ent->value);
> 
>  			PARSE_ERROR((status == 0), section_name,
>  						ent->name);
> @@ -2794,6 +2814,11 @@ save_kni_params(struct app_params *app, FILE
> *f)
>  		/* burst_write */
>  		fprintf(f, "%s = %" PRIu32 "\n", "burst_write", p-
> >burst_write);
> 
> +		/* dropless */
> +		fprintf(f, "%s = %s\n",
> +				"dropless",
> +				p->dropless ? "yes" : "no");

Please also do not forget to save the number of retries parameter as well (struct app_pktq_kni_params::n_retries):

		fprintf(f, "%s = %" PRIu64 "\n", "n_retries", p->n_retries);

I realize that we forgot to save n_retires in function save_txq_params(), can you please add it as part of your patch? We have it for save_swq_params().

> +
>  		fputc('\n', f);
>  	}
>  }
> diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
> index d522de4..af24f52 100644
> --- a/examples/ip_pipeline/init.c
> +++ b/examples/ip_pipeline/init.c
> @@ -1434,10 +1434,28 @@ void app_pipeline_params_get(struct
> app_params *app,
>  #ifdef RTE_LIBRTE_KNI
>  		case APP_PKTQ_OUT_KNI:
>  		{
> -			out->type = PIPELINE_PORT_OUT_KNI_WRITER;
> -			out->params.kni.kni = app->kni[in->id];
> -			out->params.kni.tx_burst_sz =
> -				app->kni_params[in->id].burst_write;
> +			struct app_pktq_kni_params *p_kni =
> +				&app->kni_params[in->id];
> +
> +			if (p_kni->dropless == 0) {
> +				struct rte_port_kni_writer_params *params
> =
> +					&out->params.kni;
> +
> +				out->type =
> PIPELINE_PORT_OUT_KNI_WRITER;
> +				params->kni = app->kni[in->id];
> +				params->tx_burst_sz =
> +					app->kni_params[in-
> >id].burst_write;
> +			} else {
> +				struct rte_port_kni_writer_nodrop_params
> +					*params = &out-
> >params.kni_nodrop;
> +
> +				out->type =
> PIPELINE_PORT_OUT_KNI_WRITER_NODROP;
> +				params->kni = app->kni[in->id];
> +				params->tx_burst_sz =
> +					app->kni_params[in-
> >id].burst_write;
> +				params->n_retries =
> +					app->kni_params[in->id].n_retries;
> +			}
>  			break;
>  		}
>  #endif /* RTE_LIBRTE_KNI */
> diff --git a/examples/ip_pipeline/pipeline_be.h
> b/examples/ip_pipeline/pipeline_be.h
> index 0ee208c..b562472 100644
> --- a/examples/ip_pipeline/pipeline_be.h
> +++ b/examples/ip_pipeline/pipeline_be.h
> @@ -138,6 +138,7 @@ enum pipeline_port_out_type {
>  	PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
>  	PIPELINE_PORT_OUT_SCHED_WRITER,
>  	PIPELINE_PORT_OUT_KNI_WRITER,
> +	PIPELINE_PORT_OUT_KNI_WRITER_NODROP,
>  	PIPELINE_PORT_OUT_SINK,
>  };
> 
> @@ -155,6 +156,7 @@ struct pipeline_port_out_params {
>  		struct rte_port_sched_writer_params sched;
>  #ifdef RTE_LIBRTE_KNI
>  		struct rte_port_kni_writer_params kni;
> +		struct rte_port_kni_writer_nodrop_params kni_nodrop;
>  #endif
>  		struct rte_port_sink_params sink;
>  	} params;
> @@ -185,6 +187,8 @@ pipeline_port_out_params_convert(struct
> pipeline_port_out_params  *p)
>  #ifdef RTE_LIBRTE_KNI
>  	case PIPELINE_PORT_OUT_KNI_WRITER:
>  		return (void *) &p->params.kni;
> +	case PIPELINE_PORT_OUT_KNI_WRITER_NODROP:
> +		return (void *) &p->params.kni_nodrop;
>  #endif
>  	case PIPELINE_PORT_OUT_SINK:
>  		return (void *) &p->params.sink;
> @@ -218,6 +222,8 @@ pipeline_port_out_params_get_ops(struct
> pipeline_port_out_params  *p)
>  #ifdef RTE_LIBRTE_KNI
>  	case PIPELINE_PORT_OUT_KNI_WRITER:
>  		return &rte_port_kni_writer_ops;
> +	case PIPELINE_PORT_OUT_KNI_WRITER_NODROP:
> +		return &rte_port_kni_writer_nodrop_ops;
>  #endif
>  	case PIPELINE_PORT_OUT_SINK:
>  		return &rte_port_sink_ops;
> diff --git a/lib/librte_port/rte_port_kni.c b/lib/librte_port/rte_port_kni.c
> index 4cbc345..08f4ac2 100644
> --- a/lib/librte_port/rte_port_kni.c
> +++ b/lib/librte_port/rte_port_kni.c
> @@ -306,6 +306,217 @@ static int rte_port_kni_writer_stats_read(void
> *port,
>  }
> 
>  /*
> + * Port KNI Writer Nodrop
> + */
> +#ifdef RTE_PORT_STATS_COLLECT
> +
> +#define RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_IN_ADD(port, val)
> \
> +	port->stats.n_pkts_in += val
> +#define RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_DROP_ADD(port,
> val) \
> +	port->stats.n_pkts_drop += val
> +
> +#else
> +
> +#define RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_IN_ADD(port, val)
> +#define RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_DROP_ADD(port,
> val)
> +
> +#endif
> +
> +struct rte_port_kni_writer_nodrop {
> +	struct rte_port_out_stats stats;
> +
> +	struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
> +	uint32_t tx_burst_sz;
> +	uint32_t tx_buf_count;
> +	uint64_t bsz_mask;
> +	uint64_t n_retries;
> +	struct rte_kni *kni;
> +};
> +
> +static void *
> +rte_port_kni_writer_nodrop_create(void *params, int socket_id)
> +{
> +	struct rte_port_kni_writer_nodrop_params *conf =
> +		(struct rte_port_kni_writer_nodrop_params *) params;
> +	struct rte_port_kni_writer_nodrop *port;
> +
> +	/* Check input parameters */
> +	if ((conf == NULL) ||
> +		(conf->tx_burst_sz == 0) ||
> +		(conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
> +		(!rte_is_power_of_2(conf->tx_burst_sz))) {
> +		RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n",
> __func__);
> +		return NULL;
> +	}
> +
> +	/* Memory allocation */
> +	port = rte_zmalloc_socket("PORT", sizeof(*port),
> +		RTE_CACHE_LINE_SIZE, socket_id);
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> __func__);
> +		return NULL;
> +	}
> +
> +	/* Initialization */
> +	port->kni = conf->kni;
> +	port->tx_burst_sz = conf->tx_burst_sz;
> +	port->tx_buf_count = 0;
> +	port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
> +
> +	/*
> +	 * When n_retries is 0 it means that we should wait for every packet
> to
> +	 * send no matter how many retries should it take. To limit number of
> +	 * branches in fast path, we use UINT64_MAX instead of branching.
> +	 */
> +	port->n_retries = (conf->n_retries == 0) ? UINT64_MAX : conf-
> >n_retries;
> +
> +	return port;
> +}
> +
> +static inline void
> +send_burst_nodrop(struct rte_port_kni_writer_nodrop *p)
> +{
> +	uint32_t nb_tx = 0, i;
> +
> +	nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
> +
> +	/* We sent all the packets in a first try */
> +	if (nb_tx >= p->tx_buf_count) {
> +		p->tx_buf_count = 0;
> +		return;
> +	}
> +
> +	for (i = 0; i < p->n_retries; i++) {
> +		nb_tx += rte_kni_tx_burst(p->kni,
> +			p->tx_buf + nb_tx,
> +			p->tx_buf_count - nb_tx);
> +
> +		/* We sent all the packets in more than one try */
> +		if (nb_tx >= p->tx_buf_count) {
> +			p->tx_buf_count = 0;
> +			return;
> +		}
> +	}
> +
> +	/* We didn't send the packets in maximum allowed attempts */
> +	RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_DROP_ADD(p, p-
> >tx_buf_count - nb_tx);
> +	for ( ; nb_tx < p->tx_buf_count; nb_tx++)
> +		rte_pktmbuf_free(p->tx_buf[nb_tx]);
> +
> +	p->tx_buf_count = 0;
> +}
> +
> +static int
> +rte_port_kni_writer_nodrop_tx(void *port, struct rte_mbuf *pkt)
> +{
> +	struct rte_port_kni_writer_nodrop *p =
> +			(struct rte_port_kni_writer_nodrop *) port;
> +
> +	p->tx_buf[p->tx_buf_count++] = pkt;
> +	RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> +	if (p->tx_buf_count >= p->tx_burst_sz)
> +		send_burst_nodrop(p);
> +
> +	return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_nodrop_tx_bulk(void *port,
> +	struct rte_mbuf **pkts,
> +	uint64_t pkts_mask)
> +{
> +	struct rte_port_kni_writer_nodrop *p =
> +			(struct rte_port_kni_writer_nodrop *) port;
> +
> +	uint64_t bsz_mask = p->bsz_mask;
> +	uint32_t tx_buf_count = p->tx_buf_count;
> +	uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
> +					((pkts_mask & bsz_mask) ^
> bsz_mask);
> +
> +	if (expr == 0) {
> +		uint64_t n_pkts = __builtin_popcountll(pkts_mask);
> +		uint32_t n_pkts_ok;
> +
> +		if (tx_buf_count)
> +			send_burst_nodrop(p);
> +
> +		RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_IN_ADD(p,
> n_pkts);
> +		n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
> +
> +		if (n_pkts_ok >= n_pkts)
> +			return 0;
> +
> +		/*
> +		 * If we didn't manage to send all packets in single burst,
> move
> +		 * remaining packets to the buffer and call send burst.
> +		 */
> +		for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
> +			struct rte_mbuf *pkt = pkts[n_pkts_ok];
> +			p->tx_buf[p->tx_buf_count++] = pkt;
> +		}
> +		send_burst_nodrop(p);
> +	} else {
> +		for ( ; pkts_mask; ) {
> +			uint32_t pkt_index = __builtin_ctzll(pkts_mask);
> +			uint64_t pkt_mask = 1LLU << pkt_index;
> +			struct rte_mbuf *pkt = pkts[pkt_index];
> +
> +			p->tx_buf[tx_buf_count++] = pkt;
> +
> 	RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_IN_ADD(p, 1);
> +			pkts_mask &= ~pkt_mask;
> +		}
> +
> +		p->tx_buf_count = tx_buf_count;
> +		if (tx_buf_count >= p->tx_burst_sz)
> +			send_burst_nodrop(p);
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_nodrop_flush(void *port)
> +{
> +	struct rte_port_kni_writer_nodrop *p =
> +		(struct rte_port_kni_writer_nodrop *) port;
> +
> +	if (p->tx_buf_count > 0)
> +		send_burst_nodrop(p);
> +
> +	return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_nodrop_free(void *port)
> +{
> +	if (port == NULL) {
> +		RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	rte_port_kni_writer_nodrop_flush(port);
> +	rte_free(port);
> +
> +	return 0;
> +}
> +
> +static int rte_port_kni_writer_nodrop_stats_read(void *port,
> +	struct rte_port_out_stats *stats, int clear)
> +{
> +	struct rte_port_kni_writer_nodrop *p =
> +			(struct rte_port_kni_writer_nodrop *) port;
> +
> +	if (stats != NULL)
> +		memcpy(stats, &p->stats, sizeof(p->stats));
> +
> +	if (clear)
> +		memset(&p->stats, 0, sizeof(p->stats));
> +
> +	return 0;
> +}
> +
> +
> +/*
>   * Summary of port operations
>   */
>  struct rte_port_in_ops rte_port_kni_reader_ops = {
> @@ -323,3 +534,12 @@ struct rte_port_out_ops rte_port_kni_writer_ops =
> {
>  	.f_flush = rte_port_kni_writer_flush,
>  	.f_stats = rte_port_kni_writer_stats_read,
>  };
> +
> +struct rte_port_out_ops rte_port_kni_writer_nodrop_ops = {
> +	.f_create = rte_port_kni_writer_nodrop_create,
> +	.f_free = rte_port_kni_writer_nodrop_free,
> +	.f_tx = rte_port_kni_writer_nodrop_tx,
> +	.f_tx_bulk = rte_port_kni_writer_nodrop_tx_bulk,
> +	.f_flush = rte_port_kni_writer_nodrop_flush,
> +	.f_stats = rte_port_kni_writer_nodrop_stats_read,
> +};
> diff --git a/lib/librte_port/rte_port_kni.h b/lib/librte_port/rte_port_kni.h
> index d4de8c4..4b60689 100644
> --- a/lib/librte_port/rte_port_kni.h
> +++ b/lib/librte_port/rte_port_kni.h
> @@ -75,6 +75,19 @@ struct rte_port_kni_writer_params {
>  /** kni_writer port operations */
>  extern struct rte_port_out_ops rte_port_kni_writer_ops;
> 
> +/** kni_writer_nodrop port parameters */
> +struct rte_port_kni_writer_nodrop_params {
> +	/** KNI interface reference */
> +	struct rte_kni *kni;
> +	/** Burst size to KNI interface. */
> +	uint32_t tx_burst_sz;
> +	/** Maximum number of retries, 0 for no limit */
> +	uint32_t n_retries;
> +};
> +
> +/** kni_writer_nodrop port operations */
> +extern struct rte_port_out_ops rte_port_kni_writer_nodrop_ops;
> +
>  #ifdef __cplusplus
>  }
>  #endif
> diff --git a/lib/librte_port/rte_port_version.map
> b/lib/librte_port/rte_port_version.map
> index e61b3fa..c528658 100644
> --- a/lib/librte_port/rte_port_version.map
> +++ b/lib/librte_port/rte_port_version.map
> @@ -41,5 +41,6 @@ DPDK_16.07 {
> 
>  	rte_port_kni_reader_ops;
>  	rte_port_kni_writer_ops;
> +	rte_port_kni_writer_nodrop_ops;
> 
>  } DPDK_2.2;
> --
> 2.7.4

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

* [PATCH v4 1/4] port: kni interface support
  2016-05-28 11:25 ` [PATCH] port: add kni interface support WeiJie Zhuang
                     ` (2 preceding siblings ...)
  2016-06-16 11:27   ` [PATCH v3 1/3] " WeiJie Zhuang
@ 2016-06-21 10:55   ` Ethan Zhuang
  2016-06-21 10:55     ` [PATCH v4 2/4] examples/ip_pipeline: " Ethan Zhuang
                       ` (3 more replies)
  3 siblings, 4 replies; 22+ messages in thread
From: Ethan Zhuang @ 2016-06-21 10:55 UTC (permalink / raw)
  To: cristian.dumitrescu; +Cc: dev, jasvinder.singh, ferruh.yigit, WeiJie Zhuang

From: WeiJie Zhuang <zhuangwj@gmail.com>

add KNI port type to the packet framework

Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
---
v2:
* Fix check patch error.
v3:
* Fix code review comments.
v4:
* Reorganize patch sets and fix some comments
---
 lib/librte_port/Makefile             |   7 +
 lib/librte_port/rte_port_kni.c       | 545 +++++++++++++++++++++++++++++++++++
 lib/librte_port/rte_port_kni.h       |  95 ++++++
 lib/librte_port/rte_port_version.map |   9 +
 4 files changed, 656 insertions(+)
 create mode 100644 lib/librte_port/rte_port_kni.c
 create mode 100644 lib/librte_port/rte_port_kni.h

diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index dc6a601..0fc929b 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -56,6 +56,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_frag.c
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
 endif
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
+endif
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
 
 # install includes
@@ -67,6 +70,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_frag.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_ras.h
 endif
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
+endif
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
 
 # this lib depends upon:
@@ -76,5 +82,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_sched
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/rte_port_kni.c b/lib/librte_port/rte_port_kni.c
new file mode 100644
index 0000000..08f4ac2
--- /dev/null
+++ b/lib/librte_port/rte_port_kni.c
@@ -0,0 +1,545 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016 Ethan Zhuang <zhuangwj@gmail.com>.
+ *   Copyright(c) 2016 Intel Corporation.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_kni.h>
+
+#include "rte_port_kni.h"
+
+/*
+ * Port KNI Reader
+ */
+#ifdef RTE_PORT_STATS_COLLECT
+
+#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
+	port->stats.n_pkts_in += val
+#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
+	port->stats.n_pkts_drop += val
+
+#else
+
+#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
+#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
+
+#endif
+
+struct rte_port_kni_reader {
+	struct rte_port_in_stats stats;
+
+	struct rte_kni *kni;
+};
+
+static void *
+rte_port_kni_reader_create(void *params, int socket_id)
+{
+	struct rte_port_kni_reader_params *conf =
+			(struct rte_port_kni_reader_params *) params;
+	struct rte_port_kni_reader *port;
+
+	/* Check input parameters */
+	if (conf == NULL) {
+		RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	port = rte_zmalloc_socket("PORT", sizeof(*port),
+		RTE_CACHE_LINE_SIZE, socket_id);
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni = conf->kni;
+
+	return port;
+}
+
+static int
+rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t n_pkts)
+{
+	struct rte_port_kni_reader *p =
+			(struct rte_port_kni_reader *) port;
+	uint16_t rx_pkt_cnt;
+
+	rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
+	RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
+	return rx_pkt_cnt;
+}
+
+static int
+rte_port_kni_reader_free(void *port)
+{
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rte_free(port);
+
+	return 0;
+}
+
+static int rte_port_kni_reader_stats_read(void *port,
+	struct rte_port_in_stats *stats, int clear)
+{
+	struct rte_port_kni_reader *p =
+			(struct rte_port_kni_reader *) port;
+
+	if (stats != NULL)
+		memcpy(stats, &p->stats, sizeof(p->stats));
+
+	if (clear)
+		memset(&p->stats, 0, sizeof(p->stats));
+
+	return 0;
+}
+
+/*
+ * Port KNI Writer
+ */
+#ifdef RTE_PORT_STATS_COLLECT
+
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
+	port->stats.n_pkts_in += val
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
+	port->stats.n_pkts_drop += val
+
+#else
+
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
+
+#endif
+
+struct rte_port_kni_writer {
+	struct rte_port_out_stats stats;
+
+	struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
+	uint32_t tx_burst_sz;
+	uint32_t tx_buf_count;
+	uint64_t bsz_mask;
+	struct rte_kni *kni;
+};
+
+static void *
+rte_port_kni_writer_create(void *params, int socket_id)
+{
+	struct rte_port_kni_writer_params *conf =
+			(struct rte_port_kni_writer_params *) params;
+	struct rte_port_kni_writer *port;
+
+	/* Check input parameters */
+	if ((conf == NULL) ||
+		(conf->tx_burst_sz == 0) ||
+		(conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
+		(!rte_is_power_of_2(conf->tx_burst_sz))) {
+		RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n", __func__);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	port = rte_zmalloc_socket("PORT", sizeof(*port),
+		RTE_CACHE_LINE_SIZE, socket_id);
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni = conf->kni;
+	port->tx_burst_sz = conf->tx_burst_sz;
+	port->tx_buf_count = 0;
+	port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
+
+	return port;
+}
+
+static inline void
+send_burst(struct rte_port_kni_writer *p)
+{
+	uint32_t nb_tx;
+
+	nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
+
+	RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p->tx_buf_count - nb_tx);
+	for (; nb_tx < p->tx_buf_count; nb_tx++)
+		rte_pktmbuf_free(p->tx_buf[nb_tx]);
+
+	p->tx_buf_count = 0;
+}
+
+static int
+rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt)
+{
+	struct rte_port_kni_writer *p =
+			(struct rte_port_kni_writer *) port;
+
+	p->tx_buf[p->tx_buf_count++] = pkt;
+	RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+	if (p->tx_buf_count >= p->tx_burst_sz)
+		send_burst(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_tx_bulk(void *port,
+	struct rte_mbuf **pkts,
+	uint64_t pkts_mask)
+{
+	struct rte_port_kni_writer *p =
+			(struct rte_port_kni_writer *) port;
+	uint64_t bsz_mask = p->bsz_mask;
+	uint32_t tx_buf_count = p->tx_buf_count;
+	uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
+					((pkts_mask & bsz_mask) ^ bsz_mask);
+
+	if (expr == 0) {
+		uint64_t n_pkts = __builtin_popcountll(pkts_mask);
+		uint32_t n_pkts_ok;
+
+		if (tx_buf_count)
+			send_burst(p);
+
+		RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
+		n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
+
+		RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts - n_pkts_ok);
+		for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
+			struct rte_mbuf *pkt = pkts[n_pkts_ok];
+
+			rte_pktmbuf_free(pkt);
+		}
+	} else {
+		for (; pkts_mask;) {
+			uint32_t pkt_index = __builtin_ctzll(pkts_mask);
+			uint64_t pkt_mask = 1LLU << pkt_index;
+			struct rte_mbuf *pkt = pkts[pkt_index];
+
+			p->tx_buf[tx_buf_count++] = pkt;
+			RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+			pkts_mask &= ~pkt_mask;
+		}
+
+		p->tx_buf_count = tx_buf_count;
+		if (tx_buf_count >= p->tx_burst_sz)
+			send_burst(p);
+	}
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_flush(void *port)
+{
+	struct rte_port_kni_writer *p =
+			(struct rte_port_kni_writer *) port;
+
+	if (p->tx_buf_count > 0)
+		send_burst(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_free(void *port)
+{
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rte_port_kni_writer_flush(port);
+	rte_free(port);
+
+	return 0;
+}
+
+static int rte_port_kni_writer_stats_read(void *port,
+	struct rte_port_out_stats *stats, int clear)
+{
+	struct rte_port_kni_writer *p =
+			(struct rte_port_kni_writer *) port;
+
+	if (stats != NULL)
+		memcpy(stats, &p->stats, sizeof(p->stats));
+
+	if (clear)
+		memset(&p->stats, 0, sizeof(p->stats));
+
+	return 0;
+}
+
+/*
+ * Port KNI Writer Nodrop
+ */
+#ifdef RTE_PORT_STATS_COLLECT
+
+#define RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_IN_ADD(port, val) \
+	port->stats.n_pkts_in += val
+#define RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_DROP_ADD(port, val) \
+	port->stats.n_pkts_drop += val
+
+#else
+
+#define RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_IN_ADD(port, val)
+#define RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_DROP_ADD(port, val)
+
+#endif
+
+struct rte_port_kni_writer_nodrop {
+	struct rte_port_out_stats stats;
+
+	struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
+	uint32_t tx_burst_sz;
+	uint32_t tx_buf_count;
+	uint64_t bsz_mask;
+	uint64_t n_retries;
+	struct rte_kni *kni;
+};
+
+static void *
+rte_port_kni_writer_nodrop_create(void *params, int socket_id)
+{
+	struct rte_port_kni_writer_nodrop_params *conf =
+		(struct rte_port_kni_writer_nodrop_params *) params;
+	struct rte_port_kni_writer_nodrop *port;
+
+	/* Check input parameters */
+	if ((conf == NULL) ||
+		(conf->tx_burst_sz == 0) ||
+		(conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
+		(!rte_is_power_of_2(conf->tx_burst_sz))) {
+		RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n", __func__);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	port = rte_zmalloc_socket("PORT", sizeof(*port),
+		RTE_CACHE_LINE_SIZE, socket_id);
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni = conf->kni;
+	port->tx_burst_sz = conf->tx_burst_sz;
+	port->tx_buf_count = 0;
+	port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
+
+	/*
+	 * When n_retries is 0 it means that we should wait for every packet to
+	 * send no matter how many retries should it take. To limit number of
+	 * branches in fast path, we use UINT64_MAX instead of branching.
+	 */
+	port->n_retries = (conf->n_retries == 0) ? UINT64_MAX : conf->n_retries;
+
+	return port;
+}
+
+static inline void
+send_burst_nodrop(struct rte_port_kni_writer_nodrop *p)
+{
+	uint32_t nb_tx = 0, i;
+
+	nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
+
+	/* We sent all the packets in a first try */
+	if (nb_tx >= p->tx_buf_count) {
+		p->tx_buf_count = 0;
+		return;
+	}
+
+	for (i = 0; i < p->n_retries; i++) {
+		nb_tx += rte_kni_tx_burst(p->kni,
+			p->tx_buf + nb_tx,
+			p->tx_buf_count - nb_tx);
+
+		/* We sent all the packets in more than one try */
+		if (nb_tx >= p->tx_buf_count) {
+			p->tx_buf_count = 0;
+			return;
+		}
+	}
+
+	/* We didn't send the packets in maximum allowed attempts */
+	RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_DROP_ADD(p, p->tx_buf_count - nb_tx);
+	for ( ; nb_tx < p->tx_buf_count; nb_tx++)
+		rte_pktmbuf_free(p->tx_buf[nb_tx]);
+
+	p->tx_buf_count = 0;
+}
+
+static int
+rte_port_kni_writer_nodrop_tx(void *port, struct rte_mbuf *pkt)
+{
+	struct rte_port_kni_writer_nodrop *p =
+			(struct rte_port_kni_writer_nodrop *) port;
+
+	p->tx_buf[p->tx_buf_count++] = pkt;
+	RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+	if (p->tx_buf_count >= p->tx_burst_sz)
+		send_burst_nodrop(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_nodrop_tx_bulk(void *port,
+	struct rte_mbuf **pkts,
+	uint64_t pkts_mask)
+{
+	struct rte_port_kni_writer_nodrop *p =
+			(struct rte_port_kni_writer_nodrop *) port;
+
+	uint64_t bsz_mask = p->bsz_mask;
+	uint32_t tx_buf_count = p->tx_buf_count;
+	uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
+					((pkts_mask & bsz_mask) ^ bsz_mask);
+
+	if (expr == 0) {
+		uint64_t n_pkts = __builtin_popcountll(pkts_mask);
+		uint32_t n_pkts_ok;
+
+		if (tx_buf_count)
+			send_burst_nodrop(p);
+
+		RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_IN_ADD(p, n_pkts);
+		n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
+
+		if (n_pkts_ok >= n_pkts)
+			return 0;
+
+		/*
+		 * If we didn't manage to send all packets in single burst, move
+		 * remaining packets to the buffer and call send burst.
+		 */
+		for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
+			struct rte_mbuf *pkt = pkts[n_pkts_ok];
+			p->tx_buf[p->tx_buf_count++] = pkt;
+		}
+		send_burst_nodrop(p);
+	} else {
+		for ( ; pkts_mask; ) {
+			uint32_t pkt_index = __builtin_ctzll(pkts_mask);
+			uint64_t pkt_mask = 1LLU << pkt_index;
+			struct rte_mbuf *pkt = pkts[pkt_index];
+
+			p->tx_buf[tx_buf_count++] = pkt;
+			RTE_PORT_KNI_WRITER_NODROP_STATS_PKTS_IN_ADD(p, 1);
+			pkts_mask &= ~pkt_mask;
+		}
+
+		p->tx_buf_count = tx_buf_count;
+		if (tx_buf_count >= p->tx_burst_sz)
+			send_burst_nodrop(p);
+	}
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_nodrop_flush(void *port)
+{
+	struct rte_port_kni_writer_nodrop *p =
+		(struct rte_port_kni_writer_nodrop *) port;
+
+	if (p->tx_buf_count > 0)
+		send_burst_nodrop(p);
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_nodrop_free(void *port)
+{
+	if (port == NULL) {
+		RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rte_port_kni_writer_nodrop_flush(port);
+	rte_free(port);
+
+	return 0;
+}
+
+static int rte_port_kni_writer_nodrop_stats_read(void *port,
+	struct rte_port_out_stats *stats, int clear)
+{
+	struct rte_port_kni_writer_nodrop *p =
+			(struct rte_port_kni_writer_nodrop *) port;
+
+	if (stats != NULL)
+		memcpy(stats, &p->stats, sizeof(p->stats));
+
+	if (clear)
+		memset(&p->stats, 0, sizeof(p->stats));
+
+	return 0;
+}
+
+
+/*
+ * Summary of port operations
+ */
+struct rte_port_in_ops rte_port_kni_reader_ops = {
+	.f_create = rte_port_kni_reader_create,
+	.f_free = rte_port_kni_reader_free,
+	.f_rx = rte_port_kni_reader_rx,
+	.f_stats = rte_port_kni_reader_stats_read,
+};
+
+struct rte_port_out_ops rte_port_kni_writer_ops = {
+	.f_create = rte_port_kni_writer_create,
+	.f_free = rte_port_kni_writer_free,
+	.f_tx = rte_port_kni_writer_tx,
+	.f_tx_bulk = rte_port_kni_writer_tx_bulk,
+	.f_flush = rte_port_kni_writer_flush,
+	.f_stats = rte_port_kni_writer_stats_read,
+};
+
+struct rte_port_out_ops rte_port_kni_writer_nodrop_ops = {
+	.f_create = rte_port_kni_writer_nodrop_create,
+	.f_free = rte_port_kni_writer_nodrop_free,
+	.f_tx = rte_port_kni_writer_nodrop_tx,
+	.f_tx_bulk = rte_port_kni_writer_nodrop_tx_bulk,
+	.f_flush = rte_port_kni_writer_nodrop_flush,
+	.f_stats = rte_port_kni_writer_nodrop_stats_read,
+};
diff --git a/lib/librte_port/rte_port_kni.h b/lib/librte_port/rte_port_kni.h
new file mode 100644
index 0000000..4b60689
--- /dev/null
+++ b/lib/librte_port/rte_port_kni.h
@@ -0,0 +1,95 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016 Ethan Zhuang <zhuangwj@gmail.com>.
+ *   Copyright(c) 2016 Intel Corporation.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_RTE_PORT_KNI_H__
+#define __INCLUDE_RTE_PORT_KNI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * RTE Port KNI Interface
+ *
+ * kni_reader: input port built on top of pre-initialized KNI interface
+ * kni_writer: output port built on top of pre-initialized KNI interface
+ *
+ ***/
+
+#include <stdint.h>
+
+#include <rte_kni.h>
+
+#include "rte_port.h"
+
+/** kni_reader port parameters */
+struct rte_port_kni_reader_params {
+	/** KNI interface reference */
+	struct rte_kni *kni;
+};
+
+/** kni_reader port operations */
+extern struct rte_port_in_ops rte_port_kni_reader_ops;
+
+
+/** kni_writer port parameters */
+struct rte_port_kni_writer_params {
+	/** KNI interface reference */
+	struct rte_kni *kni;
+	/** Burst size to KNI interface. */
+	uint32_t tx_burst_sz;
+};
+
+/** kni_writer port operations */
+extern struct rte_port_out_ops rte_port_kni_writer_ops;
+
+/** kni_writer_nodrop port parameters */
+struct rte_port_kni_writer_nodrop_params {
+	/** KNI interface reference */
+	struct rte_kni *kni;
+	/** Burst size to KNI interface. */
+	uint32_t tx_burst_sz;
+	/** Maximum number of retries, 0 for no limit */
+	uint32_t n_retries;
+};
+
+/** kni_writer_nodrop port operations */
+extern struct rte_port_out_ops rte_port_kni_writer_nodrop_ops;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/librte_port/rte_port_version.map b/lib/librte_port/rte_port_version.map
index 7a0b34d..c528658 100644
--- a/lib/librte_port/rte_port_version.map
+++ b/lib/librte_port/rte_port_version.map
@@ -35,3 +35,12 @@ DPDK_2.2 {
 	rte_port_ring_multi_writer_nodrop_ops;
 
 } DPDK_2.1;
+
+DPDK_16.07 {
+	global:
+
+	rte_port_kni_reader_ops;
+	rte_port_kni_writer_ops;
+	rte_port_kni_writer_nodrop_ops;
+
+} DPDK_2.2;
-- 
2.7.4

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

* [PATCH v4 2/4] examples/ip_pipeline: kni interface support
  2016-06-21 10:55   ` [PATCH v4 1/4] port: " Ethan Zhuang
@ 2016-06-21 10:55     ` Ethan Zhuang
  2016-06-21 10:55     ` [PATCH v4 3/4] examples/ip_pipeline: kni example configuration Ethan Zhuang
                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 22+ messages in thread
From: Ethan Zhuang @ 2016-06-21 10:55 UTC (permalink / raw)
  To: cristian.dumitrescu; +Cc: dev, jasvinder.singh, ferruh.yigit, WeiJie Zhuang

From: WeiJie Zhuang <zhuangwj@gmail.com>

1. add KNI support to the IP Pipeline sample Application
2. some bug fix

Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
---
 examples/ip_pipeline/app.h                         | 183 ++++++++++++++++++-
 examples/ip_pipeline/config_check.c                |  26 ++-
 examples/ip_pipeline/config_parse.c                | 203 ++++++++++++++++++++-
 examples/ip_pipeline/init.c                        | 148 ++++++++++++++-
 examples/ip_pipeline/pipeline/pipeline_common_fe.c |  27 +++
 examples/ip_pipeline/pipeline/pipeline_master_be.c |   9 +
 examples/ip_pipeline/pipeline_be.h                 |  33 ++++
 7 files changed, 618 insertions(+), 11 deletions(-)

diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
index 7611341..6a6fdd9 100644
--- a/examples/ip_pipeline/app.h
+++ b/examples/ip_pipeline/app.h
@@ -44,6 +44,9 @@
 #include <cmdline_parse.h>
 
 #include <rte_ethdev.h>
+#ifdef RTE_LIBRTE_KNI
+#include <rte_kni.h>
+#endif
 
 #include "cpu_core_map.h"
 #include "pipeline.h"
@@ -132,6 +135,22 @@ struct app_pktq_swq_params {
 	uint32_t mempool_indirect_id;
 };
 
+struct app_pktq_kni_params {
+	char *name;
+	uint32_t parsed;
+
+	uint32_t socket_id;
+	uint32_t core_id;
+	uint32_t hyper_th_id;
+	uint32_t force_bind;
+
+	uint32_t mempool_id; /* Position in the app->mempool_params */
+	uint32_t burst_read;
+	uint32_t burst_write;
+	uint32_t dropless;
+	uint64_t n_retries;
+};
+
 #ifndef APP_FILE_NAME_SIZE
 #define APP_FILE_NAME_SIZE                       256
 #endif
@@ -185,6 +204,7 @@ enum app_pktq_in_type {
 	APP_PKTQ_IN_HWQ,
 	APP_PKTQ_IN_SWQ,
 	APP_PKTQ_IN_TM,
+	APP_PKTQ_IN_KNI,
 	APP_PKTQ_IN_SOURCE,
 };
 
@@ -197,6 +217,7 @@ enum app_pktq_out_type {
 	APP_PKTQ_OUT_HWQ,
 	APP_PKTQ_OUT_SWQ,
 	APP_PKTQ_OUT_TM,
+	APP_PKTQ_OUT_KNI,
 	APP_PKTQ_OUT_SINK,
 };
 
@@ -420,6 +441,8 @@ struct app_eal_params {
 
 #define APP_MAX_PKTQ_TM                          APP_MAX_LINKS
 
+#define APP_MAX_PKTQ_KNI                         APP_MAX_LINKS
+
 #ifndef APP_MAX_PKTQ_SOURCE
 #define APP_MAX_PKTQ_SOURCE                      64
 #endif
@@ -471,6 +494,7 @@ struct app_params {
 	struct app_pktq_hwq_out_params hwq_out_params[APP_MAX_HWQ_OUT];
 	struct app_pktq_swq_params swq_params[APP_MAX_PKTQ_SWQ];
 	struct app_pktq_tm_params tm_params[APP_MAX_PKTQ_TM];
+	struct app_pktq_kni_params kni_params[APP_MAX_PKTQ_KNI];
 	struct app_pktq_source_params source_params[APP_MAX_PKTQ_SOURCE];
 	struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
 	struct app_msgq_params msgq_params[APP_MAX_MSGQ];
@@ -482,6 +506,7 @@ struct app_params {
 	uint32_t n_pktq_hwq_out;
 	uint32_t n_pktq_swq;
 	uint32_t n_pktq_tm;
+	uint32_t n_pktq_kni;
 	uint32_t n_pktq_source;
 	uint32_t n_pktq_sink;
 	uint32_t n_msgq;
@@ -495,6 +520,9 @@ struct app_params {
 	struct app_link_data link_data[APP_MAX_LINKS];
 	struct rte_ring *swq[APP_MAX_PKTQ_SWQ];
 	struct rte_sched_port *tm[APP_MAX_PKTQ_TM];
+#ifdef RTE_LIBRTE_KNI
+	struct rte_kni *kni[APP_MAX_PKTQ_KNI];
+#endif /* RTE_LIBRTE_KNI */
 	struct rte_ring *msgq[APP_MAX_MSGQ];
 	struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
 	struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
@@ -667,11 +695,11 @@ app_swq_get_reader(struct app_params *app,
 	struct app_pktq_swq_params *swq,
 	uint32_t *pktq_in_id)
 {
-	struct app_pipeline_params *reader;
+	struct app_pipeline_params *reader = NULL;
 	uint32_t pos = swq - app->swq_params;
 	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
 		RTE_DIM(app->pipeline_params));
-	uint32_t n_readers = 0, id, i;
+	uint32_t n_readers = 0, id = 0, i;
 
 	for (i = 0; i < n_pipelines; i++) {
 		struct app_pipeline_params *p = &app->pipeline_params[i];
@@ -727,11 +755,11 @@ app_tm_get_reader(struct app_params *app,
 	struct app_pktq_tm_params *tm,
 	uint32_t *pktq_in_id)
 {
-	struct app_pipeline_params *reader;
+	struct app_pipeline_params *reader = NULL;
 	uint32_t pos = tm - app->tm_params;
 	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
 		RTE_DIM(app->pipeline_params));
-	uint32_t n_readers = 0, id, i;
+	uint32_t n_readers = 0, id = 0, i;
 
 	for (i = 0; i < n_pipelines; i++) {
 		struct app_pipeline_params *p = &app->pipeline_params[i];
@@ -758,6 +786,66 @@ app_tm_get_reader(struct app_params *app,
 }
 
 static inline uint32_t
+app_kni_get_readers(struct app_params *app, struct app_pktq_kni_params *kni)
+{
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq = &p->pktq_in[j];
+
+			if ((pktq->type == APP_PKTQ_IN_KNI) &&
+				(pktq->id == pos))
+				n_readers++;
+		}
+	}
+
+	return n_readers;
+}
+
+static inline struct app_pipeline_params *
+app_kni_get_reader(struct app_params *app,
+				  struct app_pktq_kni_params *kni,
+				  uint32_t *pktq_in_id)
+{
+	struct app_pipeline_params *reader = NULL;
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers = 0, id = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq = &p->pktq_in[j];
+
+			if ((pktq->type == APP_PKTQ_IN_KNI) &&
+				(pktq->id == pos)) {
+				n_readers++;
+				reader = p;
+				id = j;
+			}
+		}
+	}
+
+	if (n_readers != 1)
+		return NULL;
+
+	*pktq_in_id = id;
+	return reader;
+}
+
+static inline uint32_t
 app_source_get_readers(struct app_params *app,
 struct app_pktq_source_params *source)
 {
@@ -861,11 +949,11 @@ app_swq_get_writer(struct app_params *app,
 	struct app_pktq_swq_params *swq,
 	uint32_t *pktq_out_id)
 {
-	struct app_pipeline_params *writer;
+	struct app_pipeline_params *writer = NULL;
 	uint32_t pos = swq - app->swq_params;
 	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
 		RTE_DIM(app->pipeline_params));
-	uint32_t n_writers = 0, id, i;
+	uint32_t n_writers = 0, id = 0, i;
 
 	for (i = 0; i < n_pipelines; i++) {
 		struct app_pipeline_params *p = &app->pipeline_params[i];
@@ -923,11 +1011,11 @@ app_tm_get_writer(struct app_params *app,
 	struct app_pktq_tm_params *tm,
 	uint32_t *pktq_out_id)
 {
-	struct app_pipeline_params *writer;
+	struct app_pipeline_params *writer = NULL;
 	uint32_t pos = tm - app->tm_params;
 	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
 		RTE_DIM(app->pipeline_params));
-	uint32_t n_writers = 0, id, i;
+	uint32_t n_writers = 0, id = 0, i;
 
 	for (i = 0; i < n_pipelines; i++) {
 		struct app_pipeline_params *p = &app->pipeline_params[i];
@@ -939,10 +1027,73 @@ app_tm_get_writer(struct app_params *app,
 			struct app_pktq_out_params *pktq = &p->pktq_out[j];
 
 			if ((pktq->type == APP_PKTQ_OUT_TM) &&
+				(pktq->id == pos)) {
+				n_writers++;
+				writer = p;
+				id = j;
+			}
+		}
+	}
+
+	if (n_writers != 1)
+		return NULL;
+
+	*pktq_out_id = id;
+	return writer;
+}
+
+static inline uint32_t
+app_kni_get_writers(struct app_params *app, struct app_pktq_kni_params *kni)
+{
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
+			RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq = &p->pktq_out[j];
+
+			if ((pktq->type == APP_PKTQ_OUT_KNI) &&
 				(pktq->id == pos))
 				n_writers++;
+		}
+	}
+
+	return n_writers;
+}
+
+static inline struct app_pipeline_params *
+app_kni_get_writer(struct app_params *app,
+				  struct app_pktq_kni_params *kni,
+				  uint32_t *pktq_out_id)
+{
+	struct app_pipeline_params *writer = NULL;
+	uint32_t pos = kni - app->kni_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers = 0, id = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
+			RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq = &p->pktq_out[j];
+
+			if ((pktq->type == APP_PKTQ_OUT_KNI) &&
+				(pktq->id == pos)) {
+				n_writers++;
 				writer = p;
 				id = j;
+			}
 		}
 	}
 
@@ -1051,6 +1202,22 @@ app_get_link_for_tm(struct app_params *app, struct app_pktq_tm_params *p_tm)
 	return &app->link_params[link_param_idx];
 }
 
+static inline struct app_link_params *
+app_get_link_for_kni(struct app_params *app, struct app_pktq_kni_params *p_kni)
+{
+	char link_name[APP_PARAM_NAME_SIZE];
+	uint32_t link_id;
+	ssize_t link_param_idx;
+
+	sscanf(p_kni->name, "KNI%" PRIu32, &link_id);
+	sprintf(link_name, "LINK%" PRIu32, link_id);
+	link_param_idx = APP_PARAM_FIND(app->link_params, link_name);
+	APP_CHECK((link_param_idx >= 0),
+			  "Cannot find %s for %s", link_name, p_kni->name);
+
+	return &app->link_params[link_param_idx];
+}
+
 void app_pipeline_params_get(struct app_params *app,
 	struct app_pipeline_params *p_in,
 	struct pipeline_params *p_out);
diff --git a/examples/ip_pipeline/config_check.c b/examples/ip_pipeline/config_check.c
index 18f57be..af1b628 100644
--- a/examples/ip_pipeline/config_check.c
+++ b/examples/ip_pipeline/config_check.c
@@ -1,7 +1,7 @@
 /*-
  *   BSD LICENSE
  *
- *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
  *   All rights reserved.
  *
  *   Redistribution and use in source and binary forms, with or without
@@ -316,6 +316,29 @@ check_tms(struct app_params *app)
 }
 
 static void
+check_knis(struct app_params *app) {
+	uint32_t i;
+
+	for (i = 0; i < app->n_pktq_kni; i++) {
+		struct app_pktq_kni_params *p = &app->kni_params[i];
+		uint32_t n_readers = app_kni_get_readers(app, p);
+		uint32_t n_writers = app_kni_get_writers(app, p);
+
+		APP_CHECK((n_readers != 0),
+			"%s has no reader\n", p->name);
+
+		APP_CHECK((n_readers == 1),
+			"%s has more than one reader\n", p->name);
+
+		APP_CHECK((n_writers != 0),
+			"%s has no writer\n", p->name);
+
+		APP_CHECK((n_writers == 1),
+			"%s has more than one writer\n", p->name);
+	}
+}
+
+static void
 check_sources(struct app_params *app)
 {
 	uint32_t i;
@@ -453,6 +476,7 @@ app_config_check(struct app_params *app)
 	check_txqs(app);
 	check_swqs(app);
 	check_tms(app);
+	check_knis(app);
 	check_sources(app);
 	check_sinks(app);
 	check_msgqs(app);
diff --git a/examples/ip_pipeline/config_parse.c b/examples/ip_pipeline/config_parse.c
index 504018e..0adca98 100644
--- a/examples/ip_pipeline/config_parse.c
+++ b/examples/ip_pipeline/config_parse.c
@@ -189,6 +189,20 @@ struct app_pktq_tm_params default_tm_params = {
 	.burst_write = 32,
 };
 
+struct app_pktq_kni_params default_kni_params = {
+	.parsed = 0,
+	.socket_id = 0,
+	.core_id = 0,
+	.hyper_th_id = 0,
+	.force_bind = 0,
+
+	.mempool_id = 0,
+	.burst_read = 32,
+	.burst_write = 32,
+	.dropless = 0,
+	.n_retries = 0,
+};
+
 struct app_pktq_source_params default_source_params = {
 	.parsed = 0,
 	.mempool_id = 0,
@@ -300,6 +314,18 @@ app_print_usage(char *prgname)
 	link_param_pos;							\
 })
 
+#define APP_PARAM_ADD_LINK_FOR_KNI(app, kni_name)			\
+({									\
+	char link_name[APP_PARAM_NAME_SIZE];				\
+	ssize_t link_param_pos;						\
+	uint32_t link_id;						\
+									\
+	sscanf((kni_name), "KNI%" SCNu32, &link_id);		\
+	sprintf(link_name, "LINK%" PRIu32, link_id);			\
+	link_param_pos = APP_PARAM_ADD((app)->link_params, link_name);	\
+	link_param_pos;							\
+})
+
 #define PARSE_CHECK_DUPLICATE_SECTION(obj)				\
 do {									\
 	APP_CHECK(((obj)->parsed == 0),					\
@@ -826,6 +852,10 @@ parse_pipeline_pktq_in(struct app_params *app,
 			type = APP_PKTQ_IN_TM;
 			id = APP_PARAM_ADD(app->tm_params, name);
 			APP_PARAM_ADD_LINK_FOR_TM(app, name);
+		} else if (validate_name(name, "KNI", 1) == 0) {
+			type = APP_PKTQ_IN_KNI;
+			id = APP_PARAM_ADD(app->kni_params, name);
+			APP_PARAM_ADD_LINK_FOR_KNI(app, name);
 		} else if (validate_name(name, "SOURCE", 1) == 0) {
 			type = APP_PKTQ_IN_SOURCE;
 			id = APP_PARAM_ADD(app->source_params, name);
@@ -871,6 +901,10 @@ parse_pipeline_pktq_out(struct app_params *app,
 			type = APP_PKTQ_OUT_TM;
 			id = APP_PARAM_ADD(app->tm_params, name);
 			APP_PARAM_ADD_LINK_FOR_TM(app, name);
+		} else if (validate_name(name, "KNI", 1) == 0) {
+			type = APP_PKTQ_OUT_KNI;
+			id = APP_PARAM_ADD(app->kni_params, name);
+			APP_PARAM_ADD_LINK_FOR_KNI(app, name);
 		} else if (validate_name(name, "SINK", 1) == 0) {
 			type = APP_PKTQ_OUT_SINK;
 			id = APP_PARAM_ADD(app->sink_params, name);
@@ -1581,6 +1615,15 @@ parse_txq(struct app_params *app,
 			continue;
 		}
 
+		if (strcmp(ent->name, "n_retries") == 0) {
+			int status = parser_read_uint64(&param->n_retries,
+				ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+				ent->name);
+			continue;
+		}
+
 		/* unrecognized */
 		PARSE_ERROR_INVALID(0, section_name, ent->name);
 	}
@@ -1816,7 +1859,7 @@ parse_tm(struct app_params *app,
 	param = &app->tm_params[param_idx];
 	PARSE_CHECK_DUPLICATE_SECTION(param);
 
-	APP_PARAM_ADD_LINK_FOR_TXQ(app, section_name);
+	APP_PARAM_ADD_LINK_FOR_TM(app, section_name);
 
 	for (i = 0; i < n_entries; i++) {
 		struct rte_cfgfile_entry *ent = &entries[i];
@@ -1853,6 +1896,102 @@ parse_tm(struct app_params *app,
 }
 
 static void
+parse_kni(struct app_params *app,
+		  const char *section_name,
+		  struct rte_cfgfile *cfg)
+{
+	struct app_pktq_kni_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->kni_params, section_name);
+	param = &app->kni_params[param_idx];
+	PARSE_CHECK_DUPLICATE_SECTION(param);
+
+	APP_PARAM_ADD_LINK_FOR_KNI(app, section_name);
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		if (strcmp(ent->name, "core") == 0) {
+			int status = parse_pipeline_core(
+					&param->socket_id,
+					&param->core_id,
+					&param->hyper_th_id,
+					ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+						ent->name);
+			param->force_bind = 1;
+			continue;
+		}
+
+		if (strcmp(ent->name, "mempool") == 0) {
+			int status = validate_name(ent->value,
+				"MEMPOOL", 1);
+			ssize_t idx;
+
+			PARSE_ERROR((status == 0), section_name,
+						ent->name);
+
+			idx = APP_PARAM_ADD(app->mempool_params, ent->value);
+			param->mempool_id = idx;
+			continue;
+		}
+
+		if (strcmp(ent->name, "burst_read") == 0) {
+			int status = parser_read_uint32(&param->burst_read,
+						ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+						ent->name);
+			continue;
+		}
+
+		if (strcmp(ent->name, "burst_write") == 0) {
+			int status = parser_read_uint32(&param->burst_write,
+						ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+						ent->name);
+			continue;
+		}
+
+		if (strcmp(ent->name, "dropless") == 0) {
+			int status = parser_read_arg_bool(ent->value);
+
+			PARSE_ERROR((status != -EINVAL), section_name,
+						ent->name);
+			param->dropless = status;
+			continue;
+		}
+
+		if (strcmp(ent->name, "n_retries") == 0) {
+			int status = parser_read_uint64(&param->n_retries,
+						ent->value);
+
+			PARSE_ERROR((status == 0), section_name,
+						ent->name);
+			continue;
+		}
+
+		/* unrecognized */
+		PARSE_ERROR_INVALID(0, section_name, ent->name);
+	}
+
+	free(entries);
+}
+
+static void
 parse_source(struct app_params *app,
 	const char *section_name,
 	struct rte_cfgfile *cfg)
@@ -2147,6 +2286,7 @@ static const struct config_section cfg_file_scheme[] = {
 	{"TXQ", 2, parse_txq},
 	{"SWQ", 1, parse_swq},
 	{"TM", 1, parse_tm},
+	{"KNI", 1, parse_kni},
 	{"SOURCE", 1, parse_source},
 	{"SINK", 1, parse_sink},
 	{"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
@@ -2285,6 +2425,7 @@ app_config_parse(struct app_params *app, const char *file_name)
 	APP_PARAM_COUNT(app->hwq_out_params, app->n_pktq_hwq_out);
 	APP_PARAM_COUNT(app->swq_params, app->n_pktq_swq);
 	APP_PARAM_COUNT(app->tm_params, app->n_pktq_tm);
+	APP_PARAM_COUNT(app->kni_params, app->n_pktq_kni);
 	APP_PARAM_COUNT(app->source_params, app->n_pktq_source);
 	APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
 	APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
@@ -2582,6 +2723,7 @@ save_txq_params(struct app_params *app, FILE *f)
 		fprintf(f, "%s = %s\n",
 			"dropless",
 			p->dropless ? "yes" : "no");
+		fprintf(f, "%s = %" PRIu64 "\n", "n_retries", p->n_retries);
 
 		fputc('\n', f);
 	}
@@ -2647,6 +2789,53 @@ save_tm_params(struct app_params *app, FILE *f)
 }
 
 static void
+save_kni_params(struct app_params *app, FILE *f)
+{
+	struct app_pktq_kni_params *p;
+	size_t i, count;
+
+	count = RTE_DIM(app->kni_params);
+	for (i = 0; i < count; i++) {
+		p = &app->kni_params[i];
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		/* section name */
+		fprintf(f, "[%s]\n", p->name);
+
+		/* core */
+		if (p->force_bind) {
+			fprintf(f, "; force_bind = 1\n");
+			fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
+					p->socket_id,
+					p->core_id,
+					(p->hyper_th_id) ? "h" : "");
+		} else
+			fprintf(f, "; force_bind = 0\n");
+
+		/* mempool */
+		fprintf(f, "%s = %s\n", "mempool",
+				app->mempool_params[p->mempool_id].name);
+
+		/* burst_read */
+		fprintf(f, "%s = %" PRIu32 "\n", "burst_read", p->burst_read);
+
+		/* burst_write */
+		fprintf(f, "%s = %" PRIu32 "\n", "burst_write", p->burst_write);
+
+		/* dropless */
+		fprintf(f, "%s = %s\n",
+				"dropless",
+				p->dropless ? "yes" : "no");
+
+		/* n_retries */
+		fprintf(f, "%s = %" PRIu64 "\n", "n_retries", p->n_retries);
+
+		fputc('\n', f);
+	}
+}
+
+static void
 save_source_params(struct app_params *app, FILE *f)
 {
 	struct app_pktq_source_params *p;
@@ -2753,6 +2942,9 @@ save_pipeline_params(struct app_params *app, FILE *f)
 				case APP_PKTQ_IN_TM:
 					name = app->tm_params[pp->id].name;
 					break;
+				case APP_PKTQ_IN_KNI:
+					name = app->kni_params[pp->id].name;
+					break;
 				case APP_PKTQ_IN_SOURCE:
 					name = app->source_params[pp->id].name;
 					break;
@@ -2787,6 +2979,9 @@ save_pipeline_params(struct app_params *app, FILE *f)
 				case APP_PKTQ_OUT_TM:
 					name = app->tm_params[pp->id].name;
 					break;
+				case APP_PKTQ_OUT_KNI:
+					name = app->kni_params[pp->id].name;
+					break;
 				case APP_PKTQ_OUT_SINK:
 					name = app->sink_params[pp->id].name;
 					break;
@@ -2872,6 +3067,7 @@ app_config_save(struct app_params *app, const char *file_name)
 	save_txq_params(app, file);
 	save_swq_params(app, file);
 	save_tm_params(app, file);
+	save_kni_params(app, file);
 	save_source_params(app, file);
 	save_sink_params(app, file);
 	save_msgq_params(app, file);
@@ -2921,6 +3117,11 @@ app_config_init(struct app_params *app)
 			&default_tm_params,
 			sizeof(default_tm_params));
 
+	for (i = 0; i < RTE_DIM(app->kni_params); i++)
+		memcpy(&app->kni_params[i],
+			   &default_kni_params,
+			   sizeof(default_kni_params));
+
 	for (i = 0; i < RTE_DIM(app->source_params); i++)
 		memcpy(&app->source_params[i],
 			&default_source_params,
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
index 7120bab..cd167f6 100644
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -1176,6 +1176,111 @@ app_init_tm(struct app_params *app)
 	}
 }
 
+#ifdef RTE_LIBRTE_KNI
+static int
+kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
+	int ret = 0;
+
+	if (port_id >= rte_eth_dev_count())
+		return -EINVAL;
+
+	ret = (if_up) ?
+		rte_eth_dev_set_link_up(port_id) :
+		rte_eth_dev_set_link_down(port_id);
+
+	return ret;
+}
+
+static int
+kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
+	int ret;
+
+	if (port_id >= rte_eth_dev_count())
+		return -EINVAL;
+
+	if (new_mtu > ETHER_MAX_LEN)
+		return -EINVAL;
+
+	/* Set new MTU */
+	ret = rte_eth_dev_set_mtu(port_id, new_mtu);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+#endif /* RTE_LIBRTE_KNI */
+
+#ifndef RTE_LIBRTE_KNI
+static void
+app_init_kni(struct app_params *app) {
+	if (app->n_pktq_kni == 0)
+		return;
+
+	rte_panic("Can not init KNI without librte_kni support.\n");
+}
+#else
+static void
+app_init_kni(struct app_params *app) {
+	uint32_t i;
+
+	if (app->n_pktq_kni == 0)
+		return;
+
+	rte_kni_init(app->n_pktq_kni);
+
+	for (i = 0; i < app->n_pktq_kni; i++) {
+		struct app_pktq_kni_params *p_kni = &app->kni_params[i];
+		struct app_link_params *p_link;
+		struct rte_eth_dev_info dev_info;
+		struct app_mempool_params *mempool_params;
+		struct rte_mempool *mempool;
+		struct rte_kni_conf conf;
+		struct rte_kni_ops ops;
+
+		/* LINK */
+		p_link = app_get_link_for_kni(app, p_kni);
+		memset(&dev_info, 0, sizeof(dev_info));
+		rte_eth_dev_info_get(p_link->pmd_id, &dev_info);
+
+		/* MEMPOOL */
+		mempool_params = &app->mempool_params[p_kni->mempool_id];
+		mempool = app->mempool[p_kni->mempool_id];
+
+		/* KNI */
+		memset(&conf, 0, sizeof(conf));
+		snprintf(conf.name, RTE_KNI_NAMESIZE, "%s", p_kni->name);
+		conf.force_bind = p_kni->force_bind;
+		if (conf.force_bind) {
+			int lcore_id;
+
+			lcore_id = cpu_core_map_get_lcore_id(app->core_map,
+				p_kni->socket_id,
+				p_kni->core_id,
+				p_kni->hyper_th_id);
+
+			if (lcore_id < 0)
+				rte_panic("%s invalid CPU core\n", p_kni->name);
+
+			conf.core_id = (uint32_t) lcore_id;
+		}
+		conf.group_id = p_link->pmd_id;
+		conf.mbuf_size = mempool_params->buffer_size;
+		conf.addr = dev_info.pci_dev->addr;
+		conf.id = dev_info.pci_dev->id;
+
+		memset(&ops, 0, sizeof(ops));
+		ops.port_id = (uint8_t) p_link->pmd_id;
+		ops.change_mtu = kni_change_mtu;
+		ops.config_network_if = kni_config_network_interface;
+
+		APP_LOG(app, HIGH, "Initializing %s ...", p_kni->name);
+		app->kni[i] = rte_kni_alloc(mempool, &conf, &ops);
+		if (!app->kni[i])
+			rte_panic("%s init error\n", p_kni->name);
+	}
+}
+#endif /* RTE_LIBRTE_KNI */
+
 static void
 app_init_msgq(struct app_params *app)
 {
@@ -1281,10 +1386,21 @@ void app_pipeline_params_get(struct app_params *app,
 			break;
 		}
 		case APP_PKTQ_IN_TM:
+		{
 			out->type = PIPELINE_PORT_IN_SCHED_READER;
 			out->params.sched.sched = app->tm[in->id];
 			out->burst_size = app->tm_params[in->id].burst_read;
 			break;
+		}
+#ifdef RTE_LIBRTE_KNI
+		case APP_PKTQ_IN_KNI:
+		{
+			out->type = PIPELINE_PORT_IN_KNI_READER;
+			out->params.kni.kni = app->kni[in->id];
+			out->burst_size = app->kni_params[in->id].burst_read;
+			break;
+		}
+#endif /* RTE_LIBRTE_KNI */
 		case APP_PKTQ_IN_SOURCE:
 		{
 			uint32_t mempool_id =
@@ -1409,7 +1525,8 @@ void app_pipeline_params_get(struct app_params *app,
 			}
 			break;
 		}
-		case APP_PKTQ_OUT_TM: {
+		case APP_PKTQ_OUT_TM:
+		{
 			struct rte_port_sched_writer_params *params =
 				&out->params.sched;
 
@@ -1419,6 +1536,34 @@ void app_pipeline_params_get(struct app_params *app,
 				app->tm_params[in->id].burst_write;
 			break;
 		}
+#ifdef RTE_LIBRTE_KNI
+		case APP_PKTQ_OUT_KNI:
+		{
+			struct app_pktq_kni_params *p_kni =
+				&app->kni_params[in->id];
+
+			if (p_kni->dropless == 0) {
+				struct rte_port_kni_writer_params *params =
+					&out->params.kni;
+
+				out->type = PIPELINE_PORT_OUT_KNI_WRITER;
+				params->kni = app->kni[in->id];
+				params->tx_burst_sz =
+					app->kni_params[in->id].burst_write;
+			} else {
+				struct rte_port_kni_writer_nodrop_params
+					*params = &out->params.kni_nodrop;
+
+				out->type = PIPELINE_PORT_OUT_KNI_WRITER_NODROP;
+				params->kni = app->kni[in->id];
+				params->tx_burst_sz =
+					app->kni_params[in->id].burst_write;
+				params->n_retries =
+					app->kni_params[in->id].n_retries;
+			}
+			break;
+		}
+#endif /* RTE_LIBRTE_KNI */
 		case APP_PKTQ_OUT_SINK:
 		{
 			out->type = PIPELINE_PORT_OUT_SINK;
@@ -1607,6 +1752,7 @@ int app_init(struct app_params *app)
 	app_init_link(app);
 	app_init_swq(app);
 	app_init_tm(app);
+	app_init_kni(app);
 	app_init_msgq(app);
 
 	app_pipeline_common_cmd_push(app);
diff --git a/examples/ip_pipeline/pipeline/pipeline_common_fe.c b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
index 70c57e4..cd1d082 100644
--- a/examples/ip_pipeline/pipeline/pipeline_common_fe.c
+++ b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
@@ -130,6 +130,33 @@ app_pipeline_track_pktq_out_to_link(struct app_params *app,
 			break;
 		}
 
+		case APP_PKTQ_OUT_KNI:
+		{
+			struct pipeline_params pp;
+			struct pipeline_type *ptype;
+			struct app_pktq_kni_params *kni;
+			uint32_t pktq_in_id;
+			int status;
+
+			kni = &app->kni_params[pktq_out->id];
+			p = app_kni_get_reader(app, kni, &pktq_in_id);
+			if (p == NULL)
+				return NULL;
+
+			ptype = app_pipeline_type_find(app, p->type);
+			if ((ptype == NULL) || (ptype->fe_ops->f_track == NULL))
+				return NULL;
+
+			app_pipeline_params_get(app, p, &pp);
+			status = ptype->fe_ops->f_track(&pp,
+				pktq_in_id,
+				&pktq_out_id);
+			if (status)
+				return NULL;
+
+			break;
+		}
+
 		case APP_PKTQ_OUT_SINK:
 		default:
 			return NULL;
diff --git a/examples/ip_pipeline/pipeline/pipeline_master_be.c b/examples/ip_pipeline/pipeline/pipeline_master_be.c
index 79869a4..9a7c8c1 100644
--- a/examples/ip_pipeline/pipeline/pipeline_master_be.c
+++ b/examples/ip_pipeline/pipeline/pipeline_master_be.c
@@ -106,6 +106,9 @@ pipeline_run(void *pipeline)
 	struct pipeline_master *p = (struct pipeline_master *) pipeline;
 	struct app_params *app = p->app;
 	int status;
+#ifdef RTE_LIBRTE_KNI
+	uint32_t i;
+#endif /* RTE_LIBRTE_KNI */
 
 	/* Application post-init phase */
 	if (p->post_init_done == 0) {
@@ -144,6 +147,12 @@ pipeline_run(void *pipeline)
 		rte_exit(0, "Bye!\n");
 	}
 
+#ifdef RTE_LIBRTE_KNI
+	/* Handle KNI requests from Linux kernel */
+	for (i = 0; i < app->n_pktq_kni; i++)
+		rte_kni_handle_request(app->kni[i]);
+#endif /* RTE_LIBRTE_KNI */
+
 	return 0;
 }
 
diff --git a/examples/ip_pipeline/pipeline_be.h b/examples/ip_pipeline/pipeline_be.h
index 5501ab7..b562472 100644
--- a/examples/ip_pipeline/pipeline_be.h
+++ b/examples/ip_pipeline/pipeline_be.h
@@ -40,6 +40,9 @@
 #include <rte_port_ras.h>
 #include <rte_port_sched.h>
 #include <rte_port_source_sink.h>
+#ifdef RTE_LIBRTE_KNI
+#include <rte_port_kni.h>
+#endif
 #include <rte_pipeline.h>
 
 enum pipeline_port_in_type {
@@ -49,6 +52,7 @@ enum pipeline_port_in_type {
 	PIPELINE_PORT_IN_RING_READER_IPV4_FRAG,
 	PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
 	PIPELINE_PORT_IN_SCHED_READER,
+	PIPELINE_PORT_IN_KNI_READER,
 	PIPELINE_PORT_IN_SOURCE,
 };
 
@@ -61,6 +65,9 @@ struct pipeline_port_in_params {
 		struct rte_port_ring_reader_ipv4_frag_params ring_ipv4_frag;
 		struct rte_port_ring_reader_ipv6_frag_params ring_ipv6_frag;
 		struct rte_port_sched_reader_params sched;
+#ifdef RTE_LIBRTE_KNI
+		struct rte_port_kni_reader_params kni;
+#endif
 		struct rte_port_source_params source;
 	} params;
 	uint32_t burst_size;
@@ -82,6 +89,10 @@ pipeline_port_in_params_convert(struct pipeline_port_in_params  *p)
 		return (void *) &p->params.ring_ipv6_frag;
 	case PIPELINE_PORT_IN_SCHED_READER:
 		return (void *) &p->params.sched;
+#ifdef RTE_LIBRTE_KNI
+	case PIPELINE_PORT_IN_KNI_READER:
+		return (void *) &p->params.kni;
+#endif
 	case PIPELINE_PORT_IN_SOURCE:
 		return (void *) &p->params.source;
 	default:
@@ -105,6 +116,10 @@ pipeline_port_in_params_get_ops(struct pipeline_port_in_params  *p)
 		return &rte_port_ring_reader_ipv6_frag_ops;
 	case PIPELINE_PORT_IN_SCHED_READER:
 		return &rte_port_sched_reader_ops;
+#ifdef RTE_LIBRTE_KNI
+	case PIPELINE_PORT_IN_KNI_READER:
+		return &rte_port_kni_reader_ops;
+#endif
 	case PIPELINE_PORT_IN_SOURCE:
 		return &rte_port_source_ops;
 	default:
@@ -122,6 +137,8 @@ enum pipeline_port_out_type {
 	PIPELINE_PORT_OUT_RING_WRITER_IPV4_RAS,
 	PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
 	PIPELINE_PORT_OUT_SCHED_WRITER,
+	PIPELINE_PORT_OUT_KNI_WRITER,
+	PIPELINE_PORT_OUT_KNI_WRITER_NODROP,
 	PIPELINE_PORT_OUT_SINK,
 };
 
@@ -137,6 +154,10 @@ struct pipeline_port_out_params {
 		struct rte_port_ring_writer_ipv4_ras_params ring_ipv4_ras;
 		struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
 		struct rte_port_sched_writer_params sched;
+#ifdef RTE_LIBRTE_KNI
+		struct rte_port_kni_writer_params kni;
+		struct rte_port_kni_writer_nodrop_params kni_nodrop;
+#endif
 		struct rte_port_sink_params sink;
 	} params;
 };
@@ -163,6 +184,12 @@ pipeline_port_out_params_convert(struct pipeline_port_out_params  *p)
 		return (void *) &p->params.ring_ipv6_ras;
 	case PIPELINE_PORT_OUT_SCHED_WRITER:
 		return (void *) &p->params.sched;
+#ifdef RTE_LIBRTE_KNI
+	case PIPELINE_PORT_OUT_KNI_WRITER:
+		return (void *) &p->params.kni;
+	case PIPELINE_PORT_OUT_KNI_WRITER_NODROP:
+		return (void *) &p->params.kni_nodrop;
+#endif
 	case PIPELINE_PORT_OUT_SINK:
 		return (void *) &p->params.sink;
 	default:
@@ -192,6 +219,12 @@ pipeline_port_out_params_get_ops(struct pipeline_port_out_params  *p)
 		return &rte_port_ring_writer_ipv6_ras_ops;
 	case PIPELINE_PORT_OUT_SCHED_WRITER:
 		return &rte_port_sched_writer_ops;
+#ifdef RTE_LIBRTE_KNI
+	case PIPELINE_PORT_OUT_KNI_WRITER:
+		return &rte_port_kni_writer_ops;
+	case PIPELINE_PORT_OUT_KNI_WRITER_NODROP:
+		return &rte_port_kni_writer_nodrop_ops;
+#endif
 	case PIPELINE_PORT_OUT_SINK:
 		return &rte_port_sink_ops;
 	default:
-- 
2.7.4

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

* [PATCH v4 3/4] examples/ip_pipeline: kni example configuration
  2016-06-21 10:55   ` [PATCH v4 1/4] port: " Ethan Zhuang
  2016-06-21 10:55     ` [PATCH v4 2/4] examples/ip_pipeline: " Ethan Zhuang
@ 2016-06-21 10:55     ` Ethan Zhuang
  2016-06-21 10:55     ` [PATCH v4 4/4] doc: kni port support in the packet framework Ethan Zhuang
  2016-06-21 12:03     ` [PATCH v4 1/4] port: kni interface support Dumitrescu, Cristian
  3 siblings, 0 replies; 22+ messages in thread
From: Ethan Zhuang @ 2016-06-21 10:55 UTC (permalink / raw)
  To: cristian.dumitrescu; +Cc: dev, jasvinder.singh, ferruh.yigit, WeiJie Zhuang

From: WeiJie Zhuang <zhuangwj@gmail.com>

config file with two KNI interfaces connected using a Linux kernel bridge

Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
---
 examples/ip_pipeline/config/kni.cfg | 67 +++++++++++++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)
 create mode 100644 examples/ip_pipeline/config/kni.cfg

diff --git a/examples/ip_pipeline/config/kni.cfg b/examples/ip_pipeline/config/kni.cfg
new file mode 100644
index 0000000..cea208b
--- /dev/null
+++ b/examples/ip_pipeline/config/kni.cfg
@@ -0,0 +1,67 @@
+;   BSD LICENSE
+;
+;   Copyright(c) 2016 Intel Corporation.
+;   All rights reserved.
+;
+;   Redistribution and use in source and binary forms, with or without
+;   modification, are permitted provided that the following conditions
+;   are met:
+;
+;     * Redistributions of source code must retain the above copyright
+;       notice, this list of conditions and the following disclaimer.
+;     * Redistributions in binary form must reproduce the above copyright
+;       notice, this list of conditions and the following disclaimer in
+;       the documentation and/or other materials provided with the
+;       distribution.
+;     * Neither the name of Intel Corporation nor the names of its
+;       contributors may be used to endorse or promote products derived
+;       from this software without specific prior written permission.
+;
+;   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+;   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+;   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+;   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+;   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+;   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+;   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+;   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+;   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+;   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+;   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+;
+;             ______________          ______________________
+;            |              |  KNI0  |                      |
+; RXQ0.0 --->|              |------->|--+                   |
+;            |              |  KNI1  |  | br0               |
+; TXQ1.0 <---|              |<-------|<-+                   |
+;            | Pass-through |        |     Linux Kernel     |
+;            |     (P1)     |        |     Network Stack    |
+;            |              |  KNI1  |                      |
+; RXQ1.0 --->|              |------->|--+                   |
+;            |              |  KNI0  |  | br0               |
+; TXQ0.0 <---|              |<-------|<-+                   |
+;            |______________|        |______________________|
+;
+; Insert Linux kernel KNI module:
+;    [Linux]$ insmod rte_kni.ko
+;
+; Configure Linux kernel bridge between KNI0 and KNI1 interfaces:
+;    [Linux]$ ifconfig KNI0 up
+;    [Linux]$ ifconfig KNI1 up
+;    [Linux]$ brctl addbr "br0"
+;    [Linux]$ brctl addif br0 KNI0
+;    [Linux]$ brctl addif br0 KNI1
+;    [Linux]$ ifconfig br0 up
+
+[EAL]
+log_level = 0
+
+[PIPELINE0]
+type = MASTER
+core = 0
+
+[PIPELINE1]
+type = PASS-THROUGH
+core = 1
+pktq_in = RXQ0.0 KNI1 RXQ1.0 KNI0
+pktq_out = KNI0 TXQ1.0 KNI1 TXQ0.0
-- 
2.7.4

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

* [PATCH v4 4/4] doc: kni port support in the packet framework
  2016-06-21 10:55   ` [PATCH v4 1/4] port: " Ethan Zhuang
  2016-06-21 10:55     ` [PATCH v4 2/4] examples/ip_pipeline: " Ethan Zhuang
  2016-06-21 10:55     ` [PATCH v4 3/4] examples/ip_pipeline: kni example configuration Ethan Zhuang
@ 2016-06-21 10:55     ` Ethan Zhuang
  2016-06-21 12:03     ` [PATCH v4 1/4] port: kni interface support Dumitrescu, Cristian
  3 siblings, 0 replies; 22+ messages in thread
From: Ethan Zhuang @ 2016-06-21 10:55 UTC (permalink / raw)
  To: cristian.dumitrescu; +Cc: dev, jasvinder.singh, ferruh.yigit, WeiJie Zhuang

From: WeiJie Zhuang <zhuangwj@gmail.com>

add some descriptions for the kni port in the packet framework

Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
---
 doc/api/doxy-api-index.md                |   1 +
 doc/guides/sample_app_ug/ip_pipeline.rst | 112 +++++++++++++++++++++++--------
 2 files changed, 84 insertions(+), 29 deletions(-)

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index f626386..5e7f024 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -118,6 +118,7 @@ There are many libraries, so their headers may be grouped by topics:
     [frag]             (@ref rte_port_frag.h),
     [reass]            (@ref rte_port_ras.h),
     [sched]            (@ref rte_port_sched.h),
+    [kni]              (@ref rte_port_kni.h),
     [src/sink]         (@ref rte_port_source_sink.h)
   * [table]            (@ref rte_table.h):
     [lpm IPv4]         (@ref rte_table_lpm.h),
diff --git a/doc/guides/sample_app_ug/ip_pipeline.rst b/doc/guides/sample_app_ug/ip_pipeline.rst
index 899fd4a..09cbc17 100644
--- a/doc/guides/sample_app_ug/ip_pipeline.rst
+++ b/doc/guides/sample_app_ug/ip_pipeline.rst
@@ -1,5 +1,5 @@
 ..  BSD LICENSE
-    Copyright(c) 2015 Intel Corporation. All rights reserved.
+    Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
     All rights reserved.
 
     Redistribution and use in source and binary forms, with or without
@@ -351,33 +351,35 @@ Application resources present in the configuration file
 
 .. table:: Application resource names in the configuration file
 
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Resource type            | Format                      | Examples                                        |
-   +==========================+=============================+=================================================+
-   | Pipeline                 | ``PIPELINE<ID>``            | ``PIPELINE0``, ``PIPELINE1``                    |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Mempool                  | ``MEMPOOL<ID>``             | ``MEMPOOL0``, ``MEMPOOL1``                      |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Link (network interface) | ``LINK<ID>``                | ``LINK0``, ``LINK1``                            |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Link RX queue            | ``RXQ<LINK_ID>.<QUEUE_ID>`` | ``RXQ0.0``, ``RXQ1.5``                          |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Link TX queue            | ``TXQ<LINK_ID>.<QUEUE_ID>`` | ``TXQ0.0``, ``TXQ1.5``                          |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Software queue           | ``SWQ<ID>``                 | ``SWQ0``, ``SWQ1``                              |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Traffic Manager          | ``TM<LINK_ID>``             | ``TM0``, ``TM1``                                |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Source                   | ``SOURCE<ID>``              | ``SOURCE0``, ``SOURCE1``                        |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Sink                     | ``SINK<ID>``                | ``SINK0``, ``SINK1``                            |
-   +--------------------------+-----------------------------+-------------------------------------------------+
-   | Message queue            | ``MSGQ<ID>``                | ``MSGQ0``, ``MSGQ1``,                           |
-   |                          | ``MSGQ-REQ-PIPELINE<ID>``   | ``MSGQ-REQ-PIPELINE2``, ``MSGQ-RSP-PIPELINE2,`` |
-   |                          | ``MSGQ-RSP-PIPELINE<ID>``   | ``MSGQ-REQ-CORE-s0c1``, ``MSGQ-RSP-CORE-s0c1``  |
-   |                          | ``MSGQ-REQ-CORE-<CORE_ID>`` |                                                 |
-   |                          | ``MSGQ-RSP-CORE-<CORE_ID>`` |                                                 |
-   +--------------------------+-----------------------------+-------------------------------------------------+
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Resource type              | Format                      | Examples                                        |
+   +============================+=============================+=================================================+
+   | Pipeline                   | ``PIPELINE<ID>``            | ``PIPELINE0``, ``PIPELINE1``                    |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Mempool                    | ``MEMPOOL<ID>``             | ``MEMPOOL0``, ``MEMPOOL1``                      |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Link (network interface)   | ``LINK<ID>``                | ``LINK0``, ``LINK1``                            |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Link RX queue              | ``RXQ<LINK_ID>.<QUEUE_ID>`` | ``RXQ0.0``, ``RXQ1.5``                          |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Link TX queue              | ``TXQ<LINK_ID>.<QUEUE_ID>`` | ``TXQ0.0``, ``TXQ1.5``                          |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Software queue             | ``SWQ<ID>``                 | ``SWQ0``, ``SWQ1``                              |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Traffic Manager            | ``TM<LINK_ID>``             | ``TM0``, ``TM1``                                |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | KNI (kernel NIC interface) | ``KNI<LINK_ID>``            | ``KNI0``, ``KNI1``                              |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Source                     | ``SOURCE<ID>``              | ``SOURCE0``, ``SOURCE1``                        |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Sink                       | ``SINK<ID>``                | ``SINK0``, ``SINK1``                            |
+   +----------------------------+-----------------------------+-------------------------------------------------+
+   | Message queue              | ``MSGQ<ID>``                | ``MSGQ0``, ``MSGQ1``,                           |
+   |                            | ``MSGQ-REQ-PIPELINE<ID>``   | ``MSGQ-REQ-PIPELINE2``, ``MSGQ-RSP-PIPELINE2,`` |
+   |                            | ``MSGQ-RSP-PIPELINE<ID>``   | ``MSGQ-REQ-CORE-s0c1``, ``MSGQ-RSP-CORE-s0c1``  |
+   |                            | ``MSGQ-REQ-CORE-<CORE_ID>`` |                                                 |
+   |                            | ``MSGQ-RSP-CORE-<CORE_ID>`` |                                                 |
+   +----------------------------+-----------------------------+-------------------------------------------------+
 
 ``LINK`` instances are created implicitly based on the ``PORT_MASK`` application startup argument.
 ``LINK0`` is the first port enabled in the ``PORT_MASK``, port 1 is the next one, etc.
@@ -386,7 +388,7 @@ For example, if bit 5 is the first bit set in the bitmask, then ``LINK0`` is hav
 This mechanism creates a contiguous LINK ID space and isolates the configuration file against changes in the board
 PCIe slots where NICs are plugged in.
 
-``RXQ``, ``TXQ`` and ``TM`` instances have the LINK ID as part of their name.
+``RXQ``, ``TXQ``, ``TM`` and ``KNI`` instances have the LINK ID as part of their name.
 For example, ``RXQ2.1``, ``TXQ2.1`` and ``TM2`` are all associated with ``LINK2``.
 
 
@@ -707,6 +709,58 @@ TM section
    +---------------+---------------------------------------------+----------+----------+---------------+
 
 
+KNI section
+~~~~~~~~~~~
+
+.. _table_ip_pipelines_kni_section:
+
+.. tabularcolumns:: |p{2.5cm}|p{7cm}|p{1.5cm}|p{1.5cm}|p{1.5cm}|
+
+.. table:: Configuration file KNI section
+
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+   | Section       | Description                                  | Optional | Type             | Default value |
+   +===============+==============================================+==========+==================+===============+
+   | core          | CPU core to run the KNI kernel thread.       | YES      | See "CPU Core    | Not set       |
+   |               | When core config is set, the KNI kernel      |          | notation"        |               |
+   |               | thread will be bound to the particular core. |          |                  |               |
+   |               | When core config is not set, the KNI kernel  |          |                  |               |
+   |               | thread will be scheduled by the OS.          |          |                  |               |
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+   | mempool       | Mempool to use for buffer allocation for     | YES      | uint32_t         | MEMPOOL0      |
+   |               | current KNI port. The mempool ID has         |          |                  |               |
+   |               | to be associated with a valid instance       |          |                  |               |
+   |               | defined in the mempool entry of the global   |          |                  |               |
+   |               | section.                                     |          |                  |               |
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+   | burst_read    | Read burst size (number of packets)          | YES      | uint32_t         | 32            |
+   |               |                                              |          | power of 2       |               |
+   |               |                                              |          | 0 < burst < size |               |
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+   | burst_write   | Write burst size (number of packets)         | YES      | uint32_t         | 32            |
+   |               |                                              |          | power of 2       |               |
+   |               |                                              |          | 0 < burst < size |               |
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+   | dropless      | When dropless is set to NO, packets can be   | YES      | YES/NO           | NO            |
+   |               | dropped if not enough free slots are         |          |                  |               |
+   |               | currently available in the queue, so the     |          |                  |               |
+   |               | write operation to the queue is non-         |          |                  |               |
+   |               | blocking.                                    |          |                  |               |
+   |               | When dropless is set to YES, packets cannot  |          |                  |               |
+   |               | be dropped if not enough free slots are      |          |                  |               |
+   |               | currently available in the queue, so the     |          |                  |               |
+   |               | write operation to the queue is blocking, as |          |                  |               |
+   |               | the write operation is retried until enough  |          |                  |               |
+   |               | free slots become available and all the      |          |                  |               |
+   |               | packets are successfully written to the      |          |                  |               |
+   |               | queue.                                       |          |                  |               |
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+   | n_retries     | Number of retries. Valid only when dropless  | YES      | uint64_t         | 0             |
+   |               | is set to YES. When set to 0, it indicates   |          |                  |               |
+   |               | unlimited number of retries.                 |          |                  |               |
+   +---------------+----------------------------------------------+----------+------------------+---------------+
+
+
 SOURCE section
 ~~~~~~~~~~~~~~
 
-- 
2.7.4

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

* Re: [PATCH v3 1/3] port: add kni interface support
  2016-06-18 16:44     ` [PATCH v3 1/3] port: add kni interface support Dumitrescu, Cristian
@ 2016-06-21 11:10       ` Ethan
  2016-06-21 11:31         ` Dumitrescu, Cristian
  0 siblings, 1 reply; 22+ messages in thread
From: Ethan @ 2016-06-21 11:10 UTC (permalink / raw)
  To: Dumitrescu, Cristian; +Cc: dev, Singh, Jasvinder, Yigit, Ferruh

Hi Cristian,

New patch has been submitted. All comments are fixed except this one:
"Here is one bug for you, you need to make sure you add the following line
here:
        param->parsed = 1;"
I think the new convention is to set this flag by the
macro PARSE_CHECK_DUPLICATE_SECTION.

BTW, although I use the  --cover-letter and --annotate flags in the
send-email command, it seems no cover letter is created.
I am not very familiar with this. So sorry!


B.R.
Ethan

2016-06-19 0:44 GMT+08:00 Dumitrescu, Cristian <
cristian.dumitrescu@intel.com>:

> Hi Ethan,
>
> Thank you, here are some comments inlined below.
>
> Please reorganize this patch in a slightly different way to look similar
> to other DPDK patch sets and also ease up the integration work for Thomas:
>         Patch 0: I suggest adding a cover letter;
>         Patch 1: all librte_port changes (rte_port_kni.h, rte_port_kni.c,
> Makefile, rte_port_version.map), including the "nodrop" KNI port version
>         Patch 2: all ip_pipeline app changes
>         Patch 3: ip_pipeline app kni.cfg file
>         Patch 4: Documentation changes
>
> > -----Original Message-----
> > From: WeiJie Zhuang [mailto:zhuangwj@gmail.com]
> > Sent: Thursday, June 16, 2016 12:27 PM
> > To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>
> > Cc: dev@dpdk.org; Singh, Jasvinder <jasvinder.singh@intel.com>; Yigit,
> > Ferruh <ferruh.yigit@intel.com>; WeiJie Zhuang <zhuangwj@gmail.com>
> > Subject: [PATCH v3 1/3] port: add kni interface support
> >
> > 1. add KNI port type to the packet framework
> > 2. add KNI support to the IP Pipeline sample Application
> > 3. some bug fix
> >
> > Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
> > ---
> > v2:
> > * Fix check patch error.
> > v3:
> > * Fix code review comments.
> > ---
> >  doc/api/doxy-api-index.md                          |   1 +
> >  examples/ip_pipeline/Makefile                      |   2 +-
> >  examples/ip_pipeline/app.h                         | 181 +++++++++++-
> >  examples/ip_pipeline/config/kni.cfg                |  67 +++++
> >  examples/ip_pipeline/config_check.c                |  26 +-
> >  examples/ip_pipeline/config_parse.c                | 166 ++++++++++-
> >  examples/ip_pipeline/init.c                        | 132 ++++++++-
> >  examples/ip_pipeline/pipeline/pipeline_common_fe.c |  29 ++
> >  examples/ip_pipeline/pipeline/pipeline_master_be.c |   6 +
> >  examples/ip_pipeline/pipeline_be.h                 |  27 ++
> >  lib/librte_port/Makefile                           |   7 +
> >  lib/librte_port/rte_port_kni.c                     | 325
> +++++++++++++++++++++
> >  lib/librte_port/rte_port_kni.h                     |  82 ++++++
> >  lib/librte_port/rte_port_version.map               |   8 +
> >  14 files changed, 1047 insertions(+), 12 deletions(-)
> >  create mode 100644 examples/ip_pipeline/config/kni.cfg
> >  create mode 100644 lib/librte_port/rte_port_kni.c
> >  create mode 100644 lib/librte_port/rte_port_kni.h
> >
> > diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> > index f626386..5e7f024 100644
> > --- a/doc/api/doxy-api-index.md
> > +++ b/doc/api/doxy-api-index.md
> > @@ -118,6 +118,7 @@ There are many libraries, so their headers may be
> > grouped by topics:
> >      [frag]             (@ref rte_port_frag.h),
> >      [reass]            (@ref rte_port_ras.h),
> >      [sched]            (@ref rte_port_sched.h),
> > +    [kni]              (@ref rte_port_kni.h),
> >      [src/sink]         (@ref rte_port_source_sink.h)
> >    * [table]            (@ref rte_table.h):
> >      [lpm IPv4]         (@ref rte_table_lpm.h),
> > diff --git a/examples/ip_pipeline/Makefile
> b/examples/ip_pipeline/Makefile
> > index 5827117..6dc3f52 100644
> > --- a/examples/ip_pipeline/Makefile
> > +++ b/examples/ip_pipeline/Makefile
> > @@ -1,6 +1,6 @@
> >  #   BSD LICENSE
> >  #
> > -#   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> > +#   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> >  #   All rights reserved.
> >  #
> >  #   Redistribution and use in source and binary forms, with or without
> > diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
> > index 7611341..abbd6d4 100644
> > --- a/examples/ip_pipeline/app.h
> > +++ b/examples/ip_pipeline/app.h
> > @@ -44,6 +44,9 @@
> >  #include <cmdline_parse.h>
> >
> >  #include <rte_ethdev.h>
> > +#ifdef RTE_LIBRTE_KNI
> > +#include <rte_kni.h>
> > +#endif
> >
> >  #include "cpu_core_map.h"
> >  #include "pipeline.h"
> > @@ -132,6 +135,20 @@ struct app_pktq_swq_params {
> >       uint32_t mempool_indirect_id;
> >  };
> >
> > +struct app_pktq_kni_params {
> > +     char *name;
> > +     uint32_t parsed;
> > +
> > +     uint32_t socket_id;
> > +     uint32_t core_id;
> > +     uint32_t hyper_th_id;
> > +     uint32_t force_bind;
> > +
> > +     uint32_t mempool_id; /* Position in the app->mempool_params */
> > +     uint32_t burst_read;
> > +     uint32_t burst_write;
> > +};
> > +
> >  #ifndef APP_FILE_NAME_SIZE
> >  #define APP_FILE_NAME_SIZE                       256
> >  #endif
> > @@ -185,6 +202,7 @@ enum app_pktq_in_type {
> >       APP_PKTQ_IN_HWQ,
> >       APP_PKTQ_IN_SWQ,
> >       APP_PKTQ_IN_TM,
> > +     APP_PKTQ_IN_KNI,
> >       APP_PKTQ_IN_SOURCE,
> >  };
> >
> > @@ -197,6 +215,7 @@ enum app_pktq_out_type {
> >       APP_PKTQ_OUT_HWQ,
> >       APP_PKTQ_OUT_SWQ,
> >       APP_PKTQ_OUT_TM,
> > +     APP_PKTQ_OUT_KNI,
> >       APP_PKTQ_OUT_SINK,
> >  };
> >
> > @@ -420,6 +439,8 @@ struct app_eal_params {
> >
> >  #define APP_MAX_PKTQ_TM                          APP_MAX_LINKS
> >
> > +#define APP_MAX_PKTQ_KNI                         APP_MAX_LINKS
> > +
> >  #ifndef APP_MAX_PKTQ_SOURCE
> >  #define APP_MAX_PKTQ_SOURCE                      64
> >  #endif
> > @@ -471,6 +492,7 @@ struct app_params {
> >       struct app_pktq_hwq_out_params
> > hwq_out_params[APP_MAX_HWQ_OUT];
> >       struct app_pktq_swq_params swq_params[APP_MAX_PKTQ_SWQ];
> >       struct app_pktq_tm_params tm_params[APP_MAX_PKTQ_TM];
> > +     struct app_pktq_kni_params kni_params[APP_MAX_PKTQ_KNI];
> >       struct app_pktq_source_params
> > source_params[APP_MAX_PKTQ_SOURCE];
> >       struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
> >       struct app_msgq_params msgq_params[APP_MAX_MSGQ];
> > @@ -482,6 +504,7 @@ struct app_params {
> >       uint32_t n_pktq_hwq_out;
> >       uint32_t n_pktq_swq;
> >       uint32_t n_pktq_tm;
> > +     uint32_t n_pktq_kni;
> >       uint32_t n_pktq_source;
> >       uint32_t n_pktq_sink;
> >       uint32_t n_msgq;
> > @@ -495,6 +518,9 @@ struct app_params {
> >       struct app_link_data link_data[APP_MAX_LINKS];
> >       struct rte_ring *swq[APP_MAX_PKTQ_SWQ];
> >       struct rte_sched_port *tm[APP_MAX_PKTQ_TM];
> > +#ifdef RTE_LIBRTE_KNI
> > +     struct rte_kni *kni[APP_MAX_PKTQ_KNI];
> > +#endif /* RTE_LIBRTE_KNI */
> >       struct rte_ring *msgq[APP_MAX_MSGQ];
> >       struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
> >       struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
> > @@ -667,11 +693,11 @@ app_swq_get_reader(struct app_params *app,
> >       struct app_pktq_swq_params *swq,
> >       uint32_t *pktq_in_id)
> >  {
> > -     struct app_pipeline_params *reader;
> > +     struct app_pipeline_params *reader = NULL;
> >       uint32_t pos = swq - app->swq_params;
> >       uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> >               RTE_DIM(app->pipeline_params));
> > -     uint32_t n_readers = 0, id, i;
> > +     uint32_t n_readers = 0, id = 0, i;
> >
> >       for (i = 0; i < n_pipelines; i++) {
> >               struct app_pipeline_params *p = &app->pipeline_params[i];
> > @@ -727,11 +753,11 @@ app_tm_get_reader(struct app_params *app,
> >       struct app_pktq_tm_params *tm,
> >       uint32_t *pktq_in_id)
> >  {
> > -     struct app_pipeline_params *reader;
> > +     struct app_pipeline_params *reader = NULL;
> >       uint32_t pos = tm - app->tm_params;
> >       uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> >               RTE_DIM(app->pipeline_params));
> > -     uint32_t n_readers = 0, id, i;
> > +     uint32_t n_readers = 0, id = 0, i;
> >
> >       for (i = 0; i < n_pipelines; i++) {
> >               struct app_pipeline_params *p = &app->pipeline_params[i];
> > @@ -758,6 +784,66 @@ app_tm_get_reader(struct app_params *app,
> >  }
> >
> >  static inline uint32_t
> > +app_kni_get_readers(struct app_params *app, struct
> > app_pktq_kni_params *kni)
> > +{
> > +     uint32_t pos = kni - app->kni_params;
> > +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> > +             RTE_DIM(app->pipeline_params));
> > +     uint32_t n_readers = 0, i;
> > +
> > +     for (i = 0; i < n_pipelines; i++) {
> > +             struct app_pipeline_params *p = &app->pipeline_params[i];
> > +             uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p-
> > >pktq_in));
> > +             uint32_t j;
> > +
> > +             for (j = 0; j < n_pktq_in; j++) {
> > +                     struct app_pktq_in_params *pktq = &p->pktq_in[j];
> > +
> > +                     if ((pktq->type == APP_PKTQ_IN_KNI) &&
> > +                             (pktq->id == pos))
> > +                             n_readers++;
> > +             }
> > +     }
> > +
> > +     return n_readers;
> > +}
> > +
> > +static inline struct app_pipeline_params *
> > +app_kni_get_reader(struct app_params *app,
> > +                               struct app_pktq_kni_params *kni,
> > +                               uint32_t *pktq_in_id)
> > +{
> > +     struct app_pipeline_params *reader = NULL;
> > +     uint32_t pos = kni - app->kni_params;
> > +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> > +             RTE_DIM(app->pipeline_params));
> > +     uint32_t n_readers = 0, id = 0, i;
> > +
> > +     for (i = 0; i < n_pipelines; i++) {
> > +             struct app_pipeline_params *p = &app->pipeline_params[i];
> > +             uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p-
> > >pktq_in));
> > +             uint32_t j;
> > +
> > +             for (j = 0; j < n_pktq_in; j++) {
> > +                     struct app_pktq_in_params *pktq = &p->pktq_in[j];
> > +
> > +                     if ((pktq->type == APP_PKTQ_IN_KNI) &&
> > +                             (pktq->id == pos)) {
> > +                             n_readers++;
> > +                             reader = p;
> > +                             id = j;
> > +                     }
> > +             }
> > +     }
> > +
> > +     if (n_readers != 1)
> > +             return NULL;
> > +
> > +     *pktq_in_id = id;
> > +     return reader;
> > +}
> > +
> > +static inline uint32_t
> >  app_source_get_readers(struct app_params *app,
> >  struct app_pktq_source_params *source)
> >  {
> > @@ -861,11 +947,11 @@ app_swq_get_writer(struct app_params *app,
> >       struct app_pktq_swq_params *swq,
> >       uint32_t *pktq_out_id)
> >  {
> > -     struct app_pipeline_params *writer;
> > +     struct app_pipeline_params *writer = NULL;
> >       uint32_t pos = swq - app->swq_params;
> >       uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> >               RTE_DIM(app->pipeline_params));
> > -     uint32_t n_writers = 0, id, i;
> > +     uint32_t n_writers = 0, id = 0, i;
> >
> >       for (i = 0; i < n_pipelines; i++) {
> >               struct app_pipeline_params *p = &app->pipeline_params[i];
> > @@ -923,11 +1009,11 @@ app_tm_get_writer(struct app_params *app,
> >       struct app_pktq_tm_params *tm,
> >       uint32_t *pktq_out_id)
> >  {
> > -     struct app_pipeline_params *writer;
> > +     struct app_pipeline_params *writer = NULL;
> >       uint32_t pos = tm - app->tm_params;
> >       uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> >               RTE_DIM(app->pipeline_params));
> > -     uint32_t n_writers = 0, id, i;
> > +     uint32_t n_writers = 0, id = 0, i;
> >
> >       for (i = 0; i < n_pipelines; i++) {
> >               struct app_pipeline_params *p = &app->pipeline_params[i];
> > @@ -939,10 +1025,73 @@ app_tm_get_writer(struct app_params *app,
> >                       struct app_pktq_out_params *pktq = &p-
> > >pktq_out[j];
> >
> >                       if ((pktq->type == APP_PKTQ_OUT_TM) &&
> > +                             (pktq->id == pos)) {
> > +                             n_writers++;
> > +                             writer = p;
> > +                             id = j;
> > +                     }
> > +             }
> > +     }
> > +
> > +     if (n_writers != 1)
> > +             return NULL;
> > +
> > +     *pktq_out_id = id;
> > +     return writer;
> > +}
> > +
> > +static inline uint32_t
> > +app_kni_get_writers(struct app_params *app, struct
> > app_pktq_kni_params *kni)
> > +{
> > +     uint32_t pos = kni - app->kni_params;
> > +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> > +             RTE_DIM(app->pipeline_params));
> > +     uint32_t n_writers = 0, i;
> > +
> > +     for (i = 0; i < n_pipelines; i++) {
> > +             struct app_pipeline_params *p = &app->pipeline_params[i];
> > +             uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
> > +                     RTE_DIM(p->pktq_out));
> > +             uint32_t j;
> > +
> > +             for (j = 0; j < n_pktq_out; j++) {
> > +                     struct app_pktq_out_params *pktq = &p-
> > >pktq_out[j];
> > +
> > +                     if ((pktq->type == APP_PKTQ_OUT_KNI) &&
> >                               (pktq->id == pos))
> >                               n_writers++;
> > +             }
> > +     }
> > +
> > +     return n_writers;
> > +}
> > +
> > +static inline struct app_pipeline_params *
> > +app_kni_get_writer(struct app_params *app,
> > +                               struct app_pktq_kni_params *kni,
> > +                               uint32_t *pktq_out_id)
> > +{
> > +     struct app_pipeline_params *writer = NULL;
> > +     uint32_t pos = kni - app->kni_params;
> > +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> > +             RTE_DIM(app->pipeline_params));
> > +     uint32_t n_writers = 0, id = 0, i;
> > +
> > +     for (i = 0; i < n_pipelines; i++) {
> > +             struct app_pipeline_params *p = &app->pipeline_params[i];
> > +             uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
> > +                     RTE_DIM(p->pktq_out));
> > +             uint32_t j;
> > +
> > +             for (j = 0; j < n_pktq_out; j++) {
> > +                     struct app_pktq_out_params *pktq = &p-
> > >pktq_out[j];
> > +
> > +                     if ((pktq->type == APP_PKTQ_OUT_KNI) &&
> > +                             (pktq->id == pos)) {
> > +                             n_writers++;
> >                               writer = p;
> >                               id = j;
> > +                     }
> >               }
> >       }
> >
> > @@ -1051,6 +1200,22 @@ app_get_link_for_tm(struct app_params *app,
> > struct app_pktq_tm_params *p_tm)
> >       return &app->link_params[link_param_idx];
> >  }
> >
> > +static inline struct app_link_params *
> > +app_get_link_for_kni(struct app_params *app, struct
> > app_pktq_kni_params *p_kni)
> > +{
> > +     char link_name[APP_PARAM_NAME_SIZE];
> > +     uint32_t link_id;
> > +     ssize_t link_param_idx;
> > +
> > +     sscanf(p_kni->name, "KNI%" PRIu32, &link_id);
> > +     sprintf(link_name, "LINK%" PRIu32, link_id);
> > +     link_param_idx = APP_PARAM_FIND(app->link_params, link_name);
> > +     APP_CHECK((link_param_idx >= 0),
> > +                       "Cannot find %s for %s", link_name, p_kni->name);
> > +
> > +     return &app->link_params[link_param_idx];
> > +}
> > +
> >  void app_pipeline_params_get(struct app_params *app,
> >       struct app_pipeline_params *p_in,
> >       struct pipeline_params *p_out);
> > diff --git a/examples/ip_pipeline/config/kni.cfg
> > b/examples/ip_pipeline/config/kni.cfg
> > new file mode 100644
> > index 0000000..c339080
> > --- /dev/null
> > +++ b/examples/ip_pipeline/config/kni.cfg
> > @@ -0,0 +1,67 @@
> > +;   BSD LICENSE
> > +;
> > +;   Copyright(c) 2016 Intel Corporation.
> > +;   All rights reserved.
> > +;
> > +;   Redistribution and use in source and binary forms, with or without
> > +;   modification, are permitted provided that the following conditions
> > +;   are met:
> > +;
> > +;     * Redistributions of source code must retain the above copyright
> > +;       notice, this list of conditions and the following disclaimer.
> > +;     * Redistributions in binary form must reproduce the above
> copyright
> > +;       notice, this list of conditions and the following disclaimer in
> > +;       the documentation and/or other materials provided with the
> > +;       distribution.
> > +;     * Neither the name of Intel Corporation nor the names of its
> > +;       contributors may be used to endorse or promote products derived
> > +;       from this software without specific prior written permission.
> > +;
> > +;   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> > CONTRIBUTORS
> > +;   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> > NOT
> > +;   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> > FITNESS FOR
> > +;   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> > COPYRIGHT
> > +;   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> > INCIDENTAL,
> > +;   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> > NOT
> > +;   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> > OF USE,
> > +;   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> > AND ON ANY
> > +;   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> > +;   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> > THE USE
> > +;   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> > DAMAGE.
> > +
> > +;             ______________          ______________________
> > +;            |              |  KNI0  |                      |
> > +; RXQ0.0 --->|              |------->|--+                   |
> > +;            |              |  KNI1  |  | br0               |
> > +; TXQ0.0 <---|              |<-------|<-+                   |
> > +;            | Pass-through |        |     Linux Kernel     |
> > +;            |     (P1)     |        |     Network Stack    |
> > +;            |              |  KNI0  |                      |
> > +; RXQ0.0 --->|              |------->|--+                   |
> > +;            |              |  KNI1  |  | br0               |
> > +; TXQ0.0 <---|              |<-------|<-+                   |
> > +;            |______________|        |______________________|
> > +;
>
> There are some errors in the diagram I previously sent you, here is the
> corrected version (which matches the PIPELINE1 configuration):
>
> ;             ______________          ______________________
> ;            |              |  KNI0  |                      |
> ; RXQ0.0 --->|              |------->|--+                   |
> ;            |              |  KNI1  |  | br0               |
> ; TXQ1.0 <---|              |<-------|<-+                   |
> ;            | Pass-through |        |     Linux Kernel     |
> ;            |     (P1)     |        |     Network Stack    |
> ;            |              |  KNI1  |                      |
> ; RXQ1.0 --->|              |------->|--+                   |
> ;            |              |  KNI0  |  | br0               |
> ; TXQ0.0 <---|              |<-------|<-+                   |
> ;            |______________|        |______________________|
>
> > +; Insert Linux kernel KNI module:
> > +;    [Linux]$ insmod rte_kni.ko
> > +;
> > +; Configure Linux kernel bridge between KNI0 and KNI1 interfaces:
> > +;    [Linux]$ ifconfig KNI0 up
> > +;    [Linux]$ ifconfig KNI1 up
> > +;    [Linux]$ brctl addbr "br0"
> > +;    [Linux]$ brctl addif br0 KNI0
> > +;    [Linux]$ brctl addif br0 KNI1
> > +;    [Linux]$ ifconfig br0 up
> > +
> > +[EAL]
> > +log_level = 0
> > +
> > +[PIPELINE0]
> > +type = MASTER
> > +core = 0
> > +
> > +[PIPELINE1]
> > +type = PASS-THROUGH
> > +core = 1
> > +pktq_in = RXQ0.0 KNI1 RXQ1.0 KNI0
> > +pktq_out = KNI0 TXQ1.0 KNI1 TXQ0.0
> > diff --git a/examples/ip_pipeline/config_check.c
> > b/examples/ip_pipeline/config_check.c
> > index 18f57be..7e90342 100644
> > --- a/examples/ip_pipeline/config_check.c
> > +++ b/examples/ip_pipeline/config_check.c
> > @@ -1,7 +1,7 @@
> >  /*-
> >   *   BSD LICENSE
> >   *
> > - *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
> > + *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> >   *   All rights reserved.
> >   *
> >   *   Redistribution and use in source and binary forms, with or without
> > @@ -444,6 +444,29 @@ check_pipelines(struct app_params *app)
> >       }
> >  }
> >
> > +static void
> > +check_knis(struct app_params *app) {
> > +     uint32_t i;
> > +
> > +     for (i = 0; i < app->n_pktq_kni; i++) {
> > +             struct app_pktq_kni_params *p = &app->kni_params[i];
> > +             uint32_t n_readers = app_kni_get_readers(app, p);
> > +             uint32_t n_writers = app_kni_get_writers(app, p);
> > +
> > +             APP_CHECK((n_readers != 0),
> > +                     "%s has no reader\n", p->name);
> > +
> > +             APP_CHECK((n_readers == 1),
> > +                     "%s has more than one reader\n", p->name);
> > +
> > +             APP_CHECK((n_writers != 0),
> > +                     "%s has no writer\n", p->name);
> > +
> > +             APP_CHECK((n_writers == 1),
> > +                     "%s has more than one writer\n", p->name);
> > +     }
> > +}
>
> I suggest you place this function just after the check_tms() function, in
> order to keep consistency with our conventions.
>
> > +
> >  int
> >  app_config_check(struct app_params *app)
> >  {
> > @@ -453,6 +476,7 @@ app_config_check(struct app_params *app)
> >       check_txqs(app);
> >       check_swqs(app);
> >       check_tms(app);
> > +     check_knis(app);
> >       check_sources(app);
> >       check_sinks(app);
> >       check_msgqs(app);
> > diff --git a/examples/ip_pipeline/config_parse.c
> > b/examples/ip_pipeline/config_parse.c
> > index 504018e..c55be31 100644
> > --- a/examples/ip_pipeline/config_parse.c
> > +++ b/examples/ip_pipeline/config_parse.c
> > @@ -189,6 +189,18 @@ struct app_pktq_tm_params default_tm_params = {
> >       .burst_write = 32,
> >  };
> >
> > +struct app_pktq_kni_params default_kni_params = {
> > +     .parsed = 0,
> > +     .socket_id = 0,
> > +     .core_id = 0,
> > +     .hyper_th_id = 0,
> > +     .force_bind = 0,
> > +
> > +     .mempool_id = 0,
> > +     .burst_read = 32,
> > +     .burst_write = 32,
> > +};
> > +
> >  struct app_pktq_source_params default_source_params = {
> >       .parsed = 0,
> >       .mempool_id = 0,
> > @@ -300,6 +312,18 @@ app_print_usage(char *prgname)
> >       link_param_pos;
> >       \
> >  })
> >
> > +#define APP_PARAM_ADD_LINK_FOR_KNI(app, kni_name)
> >       \
> > +({                                                                   \
> > +     char link_name[APP_PARAM_NAME_SIZE];
> >       \
> > +     ssize_t link_param_pos;
> >       \
> > +     uint32_t link_id;                                               \
> > +                                                                     \
> > +     sscanf((kni_name), "KNI%" SCNu32, &link_id);            \
> > +     sprintf(link_name, "LINK%" PRIu32, link_id);                    \
> > +     link_param_pos = APP_PARAM_ADD((app)->link_params,
> > link_name);   \
> > +     link_param_pos;
> >       \
> > +})
> > +
> >  #define PARSE_CHECK_DUPLICATE_SECTION(obj)
> >       \
> >  do {                                                                 \
> >       APP_CHECK(((obj)->parsed == 0),
> >       \
> > @@ -826,6 +850,10 @@ parse_pipeline_pktq_in(struct app_params *app,
> >                       type = APP_PKTQ_IN_TM;
> >                       id = APP_PARAM_ADD(app->tm_params, name);
> >                       APP_PARAM_ADD_LINK_FOR_TM(app, name);
> > +             } else if (validate_name(name, "KNI", 1) == 0) {
> > +                     type = APP_PKTQ_IN_KNI;
> > +                     id = APP_PARAM_ADD(app->kni_params, name);
> > +                     APP_PARAM_ADD_LINK_FOR_KNI(app, name);
> >               } else if (validate_name(name, "SOURCE", 1) == 0) {
> >                       type = APP_PKTQ_IN_SOURCE;
> >                       id = APP_PARAM_ADD(app->source_params, name);
> > @@ -871,6 +899,10 @@ parse_pipeline_pktq_out(struct app_params *app,
> >                       type = APP_PKTQ_OUT_TM;
> >                       id = APP_PARAM_ADD(app->tm_params, name);
> >                       APP_PARAM_ADD_LINK_FOR_TM(app, name);
> > +             } else if (validate_name(name, "KNI", 1) == 0) {
> > +                     type = APP_PKTQ_OUT_KNI;
> > +                     id = APP_PARAM_ADD(app->kni_params, name);
> > +                     APP_PARAM_ADD_LINK_FOR_KNI(app, name);
> >               } else if (validate_name(name, "SINK", 1) == 0) {
> >                       type = APP_PKTQ_OUT_SINK;
> >                       id = APP_PARAM_ADD(app->sink_params, name);
> > @@ -1816,7 +1848,7 @@ parse_tm(struct app_params *app,
> >       param = &app->tm_params[param_idx];
> >       PARSE_CHECK_DUPLICATE_SECTION(param);
> >
> > -     APP_PARAM_ADD_LINK_FOR_TXQ(app, section_name);
> > +     APP_PARAM_ADD_LINK_FOR_TM(app, section_name);
> >
> >       for (i = 0; i < n_entries; i++) {
> >               struct rte_cfgfile_entry *ent = &entries[i];
> > @@ -1853,6 +1885,85 @@ parse_tm(struct app_params *app,
> >  }
> >
> >  static void
> > +parse_kni(struct app_params *app,
> > +               const char *section_name,
> > +               struct rte_cfgfile *cfg)
> > +{
> > +     struct app_pktq_kni_params *param;
> > +     struct rte_cfgfile_entry *entries;
> > +     int n_entries, i;
> > +     ssize_t param_idx;
> > +
> > +     n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
> > +     PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0),
> > section_name);
> > +
> > +     entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
> > +     PARSE_ERROR_MALLOC(entries != NULL);
> > +
> > +     rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
> > +
> > +     param_idx = APP_PARAM_ADD(app->kni_params, section_name);
> > +     param = &app->kni_params[param_idx];
> > +     PARSE_CHECK_DUPLICATE_SECTION(param);
> > +
> > +     APP_PARAM_ADD_LINK_FOR_KNI(app, section_name);
> > +
> > +
> > +     for (i = 0; i < n_entries; i++) {
> > +             struct rte_cfgfile_entry *ent = &entries[i];
> > +
> > +             if (strcmp(ent->name, "core") == 0) {
> > +                     int status = parse_pipeline_core(
> > +                                     &param->socket_id,
> > +                                     &param->core_id,
> > +                                     &param->hyper_th_id,
> > +                                     ent->value);
> > +
> > +                     PARSE_ERROR((status == 0), section_name,
> > +                                             ent->name);
> > +                     param->force_bind = 1;
> > +                     continue;
> > +             }
> > +
> > +             if (strcmp(ent->name, "mempool") == 0) {
> > +                     int status = validate_name(ent->value,
> > +
> > "MEMPOOL", 1);
> > +                     ssize_t idx;
> > +
> > +                     PARSE_ERROR((status == 0), section_name,
> > +                                             ent->name);
> > +
> > +                     idx = APP_PARAM_ADD(app->mempool_params,
> > ent->value);
> > +                     param->mempool_id = idx;
> > +                     continue;
> > +             }
> > +
> > +             if (strcmp(ent->name, "burst_read") == 0) {
> > +                     int status = parser_read_uint32(&param-
> > >burst_read,
> > +
> >               ent->value);
> > +
> > +                     PARSE_ERROR((status == 0), section_name,
> > +                                             ent->name);
> > +                     continue;
> > +             }
> > +
> > +             if (strcmp(ent->name, "burst_write") == 0) {
> > +                     int status = parser_read_uint32(&param-
> > >burst_write,
> > +
> >               ent->value);
> > +
> > +                     PARSE_ERROR((status == 0), section_name,
> > +                                             ent->name);
> > +                     continue;
> > +             }
> > +
> > +             /* unrecognized */
> > +             PARSE_ERROR_INVALID(0, section_name, ent->name);
> > +     }
> > +
>
> Here is one bug for you, you need to make sure you add the following line
> here:
>         param->parsed = 1;
>
> > +     free(entries);
> > +}
> > +
> > +static void
> >  parse_source(struct app_params *app,
> >       const char *section_name,
> >       struct rte_cfgfile *cfg)
> > @@ -2147,6 +2258,7 @@ static const struct config_section
> cfg_file_scheme[]
> > = {
> >       {"TXQ", 2, parse_txq},
> >       {"SWQ", 1, parse_swq},
> >       {"TM", 1, parse_tm},
> > +     {"KNI", 1, parse_kni},
> >       {"SOURCE", 1, parse_source},
> >       {"SINK", 1, parse_sink},
> >       {"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
> > @@ -2285,6 +2397,7 @@ app_config_parse(struct app_params *app, const
> > char *file_name)
> >       APP_PARAM_COUNT(app->hwq_out_params, app-
> > >n_pktq_hwq_out);
> >       APP_PARAM_COUNT(app->swq_params, app->n_pktq_swq);
> >       APP_PARAM_COUNT(app->tm_params, app->n_pktq_tm);
> > +     APP_PARAM_COUNT(app->kni_params, app->n_pktq_kni);
> >       APP_PARAM_COUNT(app->source_params, app->n_pktq_source);
> >       APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
> >       APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
> > @@ -2647,6 +2760,45 @@ save_tm_params(struct app_params *app, FILE
> > *f)
> >  }
> >
> >  static void
> > +save_kni_params(struct app_params *app, FILE *f)
> > +{
> > +     struct app_pktq_kni_params *p;
> > +     size_t i, count;
> > +
> > +     count = RTE_DIM(app->kni_params);
> > +     for (i = 0; i < count; i++) {
> > +             p = &app->kni_params[i];
> > +             if (!APP_PARAM_VALID(p))
> > +                     continue;
> > +
> > +             /* section name */
> > +             fprintf(f, "[%s]\n", p->name);
> > +
> > +             /* core */
> > +             if (p->force_bind) {
> > +                     fprintf(f, "; force_bind = 1\n");
> > +                     fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
> > +                                     p->socket_id,
> > +                                     p->core_id,
> > +                                     (p->hyper_th_id) ? "h" : "");
> > +             } else
> > +                     fprintf(f, "; force_bind = 0\n");
> > +
> > +             /* mempool */
> > +             fprintf(f, "%s = %s\n", "mempool",
> > +                             app->mempool_params[p-
> > >mempool_id].name);
> > +
> > +             /* burst_read */
> > +             fprintf(f, "%s = %" PRIu32 "\n", "burst_read", p-
> > >burst_read);
> > +
> > +             /* burst_write */
> > +             fprintf(f, "%s = %" PRIu32 "\n", "burst_write", p-
> > >burst_write);
> > +
> > +             fputc('\n', f);
> > +     }
> > +}
> > +
> > +static void
> >  save_source_params(struct app_params *app, FILE *f)
> >  {
> >       struct app_pktq_source_params *p;
> > @@ -2753,6 +2905,9 @@ save_pipeline_params(struct app_params *app,
> > FILE *f)
> >                               case APP_PKTQ_IN_TM:
> >                                       name = app->tm_params[pp-
> > >id].name;
> >                                       break;
> > +                             case APP_PKTQ_IN_KNI:
> > +                                     name = app->kni_params[pp-
> > >id].name;
> > +                                     break;
> >                               case APP_PKTQ_IN_SOURCE:
> >                                       name = app->source_params[pp-
> > >id].name;
> >                                       break;
> > @@ -2787,6 +2942,9 @@ save_pipeline_params(struct app_params *app,
> > FILE *f)
> >                               case APP_PKTQ_OUT_TM:
> >                                       name = app->tm_params[pp-
> > >id].name;
> >                                       break;
> > +                             case APP_PKTQ_OUT_KNI:
> > +                                     name = app->kni_params[pp-
> > >id].name;
> > +                                     break;
> >                               case APP_PKTQ_OUT_SINK:
> >                                       name = app->sink_params[pp-
> > >id].name;
> >                                       break;
> > @@ -2872,6 +3030,7 @@ app_config_save(struct app_params *app, const
> > char *file_name)
> >       save_txq_params(app, file);
> >       save_swq_params(app, file);
> >       save_tm_params(app, file);
> > +     save_kni_params(app, file);
> >       save_source_params(app, file);
> >       save_sink_params(app, file);
> >       save_msgq_params(app, file);
> > @@ -2921,6 +3080,11 @@ app_config_init(struct app_params *app)
> >                       &default_tm_params,
> >                       sizeof(default_tm_params));
> >
> > +     for (i = 0; i < RTE_DIM(app->kni_params); i++)
> > +             memcpy(&app->kni_params[i],
> > +                        &default_kni_params,
> > +                        sizeof(default_kni_params));
> > +
> >       for (i = 0; i < RTE_DIM(app->source_params); i++)
> >               memcpy(&app->source_params[i],
> >                       &default_source_params,
> > diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
>
> In order to keep up with our existing code conventions, please move the
> KNI code (functions kni_change_mtu(), kni_config_network_interface(),
> app_init_kni()) just after function app_init_tm().
>
> > index 7120bab..d522de4 100644
> > --- a/examples/ip_pipeline/init.c
> > +++ b/examples/ip_pipeline/init.c
> > @@ -1281,10 +1281,21 @@ void app_pipeline_params_get(struct
> > app_params *app,
> >                       break;
> >               }
> >               case APP_PKTQ_IN_TM:
> > +             {
> >                       out->type = PIPELINE_PORT_IN_SCHED_READER;
> >                       out->params.sched.sched = app->tm[in->id];
> >                       out->burst_size = app->tm_params[in-
> > >id].burst_read;
> >                       break;
> > +             }
> > +#ifdef RTE_LIBRTE_KNI
> > +             case APP_PKTQ_IN_KNI:
> > +             {
> > +                     out->type = PIPELINE_PORT_IN_KNI_READER;
> > +                     out->params.kni.kni = app->kni[in->id];
> > +                     out->burst_size = app->kni_params[in-
> > >id].burst_read;
> > +                     break;
> > +             }
> > +#endif /* RTE_LIBRTE_KNI */
> >               case APP_PKTQ_IN_SOURCE:
> >               {
> >                       uint32_t mempool_id =
> > @@ -1409,7 +1420,8 @@ void app_pipeline_params_get(struct app_params
> > *app,
> >                       }
> >                       break;
> >               }
> > -             case APP_PKTQ_OUT_TM: {
> > +             case APP_PKTQ_OUT_TM:
> > +             {
> >                       struct rte_port_sched_writer_params *params =
> >                               &out->params.sched;
> >
> > @@ -1419,6 +1431,16 @@ void app_pipeline_params_get(struct
> > app_params *app,
> >                               app->tm_params[in->id].burst_write;
> >                       break;
> >               }
> > +#ifdef RTE_LIBRTE_KNI
> > +             case APP_PKTQ_OUT_KNI:
> > +             {
> > +                     out->type = PIPELINE_PORT_OUT_KNI_WRITER;
> > +                     out->params.kni.kni = app->kni[in->id];
> > +                     out->params.kni.tx_burst_sz =
> > +                             app->kni_params[in->id].burst_write;
> > +                     break;
> > +             }
> > +#endif /* RTE_LIBRTE_KNI */
> >               case APP_PKTQ_OUT_SINK:
> >               {
> >                       out->type = PIPELINE_PORT_OUT_SINK;
> > @@ -1452,6 +1474,113 @@ void app_pipeline_params_get(struct
> > app_params *app,
> >       }
> >  }
> >
> > +#ifdef RTE_LIBRTE_KNI
> > +static int
> > +kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
> > +     int ret = 0;
> > +
> > +     if (port_id >= rte_eth_dev_count())
> > +             return -EINVAL;
> > +
> > +     if (if_up) {
> > +             rte_eth_dev_stop(port_id);
> > +             ret = rte_eth_dev_start(port_id);
> > +     } else
> > +             rte_eth_dev_stop(port_id);
> > +
> > +     return ret;
> > +}
> > +
>
> Here is a critical issue that we found out recently during testing the KNI
> code: the correct PMD API functions to use here are
> rte_eth_dev_set_link_up/down(); stopping and restarting the device while
> the device is running can lead to segfault and nondeterministic behaviour.
>
> Here is my proposal:
>
> static int
> kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
>         int ret = 0;
>
>         if (port_id >= rte_eth_dev_count())
>                 return -EINVAL;
>
>         ret = (if_up)?
>                 rte_eth_dev_set_link_up(port_id) :
>                 rte_eth_dev_set_link_down(port_id);
>
>         return ret;
> }
>
> I tested this function successfully.
>
> > +static int
> > +kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
> > +     int ret;
> > +
> > +     if (port_id >= rte_eth_dev_count())
> > +             return -EINVAL;
> > +
> > +     if (new_mtu > ETHER_MAX_LEN)
> > +             return -EINVAL;
> > +
> > +     /* Stop specific port */
> > +     rte_eth_dev_stop(port_id);
> > +
> > +     /* Set new MTU */
> > +     ret = rte_eth_dev_set_mtu(port_id, new_mtu);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     /* Restart specific port */
> > +     ret = rte_eth_dev_start(port_id);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     return 0;
> > +}
>
> Same issue about using the rte_eth_dev_start/stop() functions. Based on
> function port_mtu_set() of the app/test-pmd application, it looks like
> rte_eth_dev_set_mtu() function could be called directly with no start/stop
> or up/down preparations.
>
> Here is my version, although I did not fully test this one:
>
> static int
> kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
>         int ret;
>
>         if (port_id >= rte_eth_dev_count())
>                 return -EINVAL;
>
>         if (new_mtu > ETHER_MAX_LEN)
>                 return -EINVAL;
>
>         /* Set new MTU */
>         ret = rte_eth_dev_set_mtu(port_id, new_mtu);
>         if (ret < 0)
>                 return ret;
>
>         return 0;
> }
>
> > +#endif /* RTE_LIBRTE_KNI */
> > +
> > +static void
> > +app_init_kni(struct app_params *app) {
> > +     if (app->n_pktq_kni == 0)
> > +             return;
> > +#ifndef RTE_LIBRTE_KNI
> > +     else
> > +             rte_panic("Can not init KNI without librte_kni
> support.\n");
> > +#else
>
> I would like to avoid #ifdefs in the middle of the function code as much
> as possible. I suggest we do the following:
>
> #ifndef RTE_LIBRTE_KNI
>
> static void
> app_init_kni(struct app_params *app)
> {
>         if (app->n_pktq_kni == 0)
>                 return;
>
>         rte_panic("Cannot init KNI without librte_kni support.\n");
> }
>
> #else
>
> static void
> app_init_kni(struct app_params *app)
> {
>         //the real function code
> }
>
> #endif
>
> > +     rte_kni_init(app->n_pktq_kni);
> > +
> > +     for (uint32_t i = 0; i < app->n_pktq_kni; i++) {
>
> Please declare the "i" variable at the start of the function, as usual.
> Although I personally like this idea, this pattern is not used anywhere in
> DPDK and it might trigger warnings/errors from some C compilers. Btw, any
> idea whether this is mentioned in any way by the ANSI C/C99 standards?
>
> > +             struct app_pktq_kni_params *p_kni = &app->kni_params[i];
> > +             struct app_link_params *p_link;
> > +             struct rte_eth_dev_info dev_info;
> > +             struct app_mempool_params *mempool_params;
> > +             struct rte_mempool *mempool;
> > +             struct rte_kni_conf conf;
> > +             struct rte_kni_ops ops;
> > +
> > +             /* LINK */
> > +             p_link = app_get_link_for_kni(app, p_kni);
> > +             memset(&dev_info, 0, sizeof(dev_info));
> > +             rte_eth_dev_info_get(p_link->pmd_id, &dev_info);
> > +
> > +             /* MEMPOOL */
> > +             mempool_params = &app->mempool_params[p_kni-
> > >mempool_id];
> > +             mempool = app->mempool[p_kni->mempool_id];
> > +
> > +             /* KNI */
> > +             memset(&conf, 0, sizeof(conf));
> > +             snprintf(conf.name, RTE_KNI_NAMESIZE, "%s", p_kni-
> > >name);
> > +             conf.force_bind = p_kni->force_bind;
> > +             if (conf.force_bind) {
> > +                     int lcore_id;
> > +
> > +                     lcore_id = cpu_core_map_get_lcore_id(app-
> > >core_map,
> > +                             p_kni->socket_id,
> > +                             p_kni->core_id,
> > +                             p_kni->hyper_th_id);
> > +
> > +                     if (lcore_id < 0)
> > +                             rte_panic("%s invalid CPU core\n", p_kni-
> > >name);
> > +
> > +                     conf.core_id = (uint32_t) lcore_id;
> > +             }
> > +             conf.group_id = p_link->pmd_id;
> > +             conf.mbuf_size = mempool_params->buffer_size;
> > +             conf.addr = dev_info.pci_dev->addr;
> > +             conf.id = dev_info.pci_dev->id;
> > +
> > +             memset(&ops, 0, sizeof(ops));
> > +             ops.port_id = (uint8_t) p_link->pmd_id;
> > +             ops.change_mtu = kni_change_mtu;
> > +             ops.config_network_if = kni_config_network_interface;
> > +
> > +             APP_LOG(app, HIGH, "Initializing %s ...", p_kni->name);
> > +             app->kni[i] = rte_kni_alloc(mempool, &conf, &ops);
> > +             if (!app->kni[i])
> > +                     rte_panic("%s init error\n", p_kni->name);
> > +     }
> > +#endif /* RTE_LIBRTE_KNI */
> > +}
> > +
> >  static void
> >  app_init_pipelines(struct app_params *app)
> >  {
> > @@ -1607,6 +1736,7 @@ int app_init(struct app_params *app)
> >       app_init_link(app);
> >       app_init_swq(app);
> >       app_init_tm(app);
> > +     app_init_kni(app);
> >       app_init_msgq(app);
> >
> >       app_pipeline_common_cmd_push(app);
> > diff --git a/examples/ip_pipeline/pipeline/pipeline_common_fe.c
> > b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
> > index 70c57e4..63723cd 100644
> > --- a/examples/ip_pipeline/pipeline/pipeline_common_fe.c
> > +++ b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
> > @@ -130,6 +130,35 @@ app_pipeline_track_pktq_out_to_link(struct
> > app_params *app,
> >                       break;
> >               }
> >
> > +#ifdef RTE_LIBRTE_KNI
>
> As in any other place that just deals with KNI configuration data rather
> than KNI init data (e.g. in app.h or config_parse.c), we do not need to
> #ifdef the code fragment below. Please remove this instance of #ifdef
> RTE_LIBRTE_KNI.
>
> > +             case APP_PKTQ_OUT_KNI:
> > +             {
> > +                     struct pipeline_params pp;
> > +                     struct pipeline_type *ptype;
> > +                     struct app_pktq_kni_params *kni;
> > +                     uint32_t pktq_in_id;
> > +                     int status;
> > +
> > +                     kni = &app->kni_params[pktq_out->id];
> > +                     p = app_kni_get_reader(app, kni, &pktq_in_id);
> > +                     if (p == NULL)
> > +                             return NULL;
> > +
> > +                     ptype = app_pipeline_type_find(app, p->type);
> > +                     if ((ptype == NULL) || (ptype->fe_ops->f_track ==
> > NULL))
> > +                             return NULL;
> > +
> > +                     app_pipeline_params_get(app, p, &pp);
> > +                     status = ptype->fe_ops->f_track(&pp,
> > +                             pktq_in_id,
> > +                             &pktq_out_id);
> > +                     if (status)
> > +                             return NULL;
> > +
> > +                     break;
> > +             }
> > +#endif
> > +
> >               case APP_PKTQ_OUT_SINK:
> >               default:
> >                       return NULL;
> > diff --git a/examples/ip_pipeline/pipeline/pipeline_master_be.c
> > b/examples/ip_pipeline/pipeline/pipeline_master_be.c
> > index 79869a4..32d4635 100644
> > --- a/examples/ip_pipeline/pipeline/pipeline_master_be.c
> > +++ b/examples/ip_pipeline/pipeline/pipeline_master_be.c
> > @@ -144,6 +144,12 @@ pipeline_run(void *pipeline)
> >               rte_exit(0, "Bye!\n");
> >       }
> >
> > +#ifdef RTE_LIBRTE_KNI
> > +     /* Handle KNI requests from Linux kernel */
> > +     for (uint32_t i = 0; i < app->n_pktq_kni; i++)
>
> Same comment as above on definition of the "i" variable.
>
> > +             rte_kni_handle_request(app->kni[i]);
> > +#endif /* RTE_LIBRTE_KNI */
> > +
> >       return 0;
> >  }
> >
> > diff --git a/examples/ip_pipeline/pipeline_be.h
> > b/examples/ip_pipeline/pipeline_be.h
> > index 5501ab7..0ee208c 100644
> > --- a/examples/ip_pipeline/pipeline_be.h
> > +++ b/examples/ip_pipeline/pipeline_be.h
> > @@ -40,6 +40,9 @@
> >  #include <rte_port_ras.h>
> >  #include <rte_port_sched.h>
> >  #include <rte_port_source_sink.h>
> > +#ifdef RTE_LIBRTE_KNI
> > +#include <rte_port_kni.h>
> > +#endif
> >  #include <rte_pipeline.h>
> >
> >  enum pipeline_port_in_type {
> > @@ -49,6 +52,7 @@ enum pipeline_port_in_type {
> >       PIPELINE_PORT_IN_RING_READER_IPV4_FRAG,
> >       PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
> >       PIPELINE_PORT_IN_SCHED_READER,
> > +     PIPELINE_PORT_IN_KNI_READER,
> >       PIPELINE_PORT_IN_SOURCE,
> >  };
> >
> > @@ -61,6 +65,9 @@ struct pipeline_port_in_params {
> >               struct rte_port_ring_reader_ipv4_frag_params
> > ring_ipv4_frag;
> >               struct rte_port_ring_reader_ipv6_frag_params
> > ring_ipv6_frag;
> >               struct rte_port_sched_reader_params sched;
> > +#ifdef RTE_LIBRTE_KNI
> > +             struct rte_port_kni_reader_params kni;
> > +#endif
> >               struct rte_port_source_params source;
> >       } params;
> >       uint32_t burst_size;
> > @@ -82,6 +89,10 @@ pipeline_port_in_params_convert(struct
> > pipeline_port_in_params  *p)
> >               return (void *) &p->params.ring_ipv6_frag;
> >       case PIPELINE_PORT_IN_SCHED_READER:
> >               return (void *) &p->params.sched;
> > +#ifdef RTE_LIBRTE_KNI
> > +     case PIPELINE_PORT_IN_KNI_READER:
> > +             return (void *) &p->params.kni;
> > +#endif
> >       case PIPELINE_PORT_IN_SOURCE:
> >               return (void *) &p->params.source;
> >       default:
> > @@ -105,6 +116,10 @@ pipeline_port_in_params_get_ops(struct
> > pipeline_port_in_params  *p)
> >               return &rte_port_ring_reader_ipv6_frag_ops;
> >       case PIPELINE_PORT_IN_SCHED_READER:
> >               return &rte_port_sched_reader_ops;
> > +#ifdef RTE_LIBRTE_KNI
> > +     case PIPELINE_PORT_IN_KNI_READER:
> > +             return &rte_port_kni_reader_ops;
> > +#endif
> >       case PIPELINE_PORT_IN_SOURCE:
> >               return &rte_port_source_ops;
> >       default:
> > @@ -122,6 +137,7 @@ enum pipeline_port_out_type {
> >       PIPELINE_PORT_OUT_RING_WRITER_IPV4_RAS,
> >       PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
> >       PIPELINE_PORT_OUT_SCHED_WRITER,
> > +     PIPELINE_PORT_OUT_KNI_WRITER,
> >       PIPELINE_PORT_OUT_SINK,
> >  };
> >
> > @@ -137,6 +153,9 @@ struct pipeline_port_out_params {
> >               struct rte_port_ring_writer_ipv4_ras_params ring_ipv4_ras;
> >               struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
> >               struct rte_port_sched_writer_params sched;
> > +#ifdef RTE_LIBRTE_KNI
> > +             struct rte_port_kni_writer_params kni;
> > +#endif
> >               struct rte_port_sink_params sink;
> >       } params;
> >  };
> > @@ -163,6 +182,10 @@ pipeline_port_out_params_convert(struct
> > pipeline_port_out_params  *p)
> >               return (void *) &p->params.ring_ipv6_ras;
> >       case PIPELINE_PORT_OUT_SCHED_WRITER:
> >               return (void *) &p->params.sched;
> > +#ifdef RTE_LIBRTE_KNI
> > +     case PIPELINE_PORT_OUT_KNI_WRITER:
> > +             return (void *) &p->params.kni;
> > +#endif
> >       case PIPELINE_PORT_OUT_SINK:
> >               return (void *) &p->params.sink;
> >       default:
> > @@ -192,6 +215,10 @@ pipeline_port_out_params_get_ops(struct
> > pipeline_port_out_params  *p)
> >               return &rte_port_ring_writer_ipv6_ras_ops;
> >       case PIPELINE_PORT_OUT_SCHED_WRITER:
> >               return &rte_port_sched_writer_ops;
> > +#ifdef RTE_LIBRTE_KNI
> > +     case PIPELINE_PORT_OUT_KNI_WRITER:
> > +             return &rte_port_kni_writer_ops;
> > +#endif
> >       case PIPELINE_PORT_OUT_SINK:
> >               return &rte_port_sink_ops;
> >       default:
> > diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
> > index dc6a601..0fc929b 100644
> > --- a/lib/librte_port/Makefile
> > +++ b/lib/librte_port/Makefile
> > @@ -56,6 +56,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_frag.c
> >  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
> >  endif
> >  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
> > +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> > +SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
> > +endif
> >  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
> >
> >  # install includes
> > @@ -67,6 +70,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=
> > rte_port_frag.h
> >  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_ras.h
> >  endif
> >  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
> > +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> > +SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
> > +endif
> >  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
> >
> >  # this lib depends upon:
> > @@ -76,5 +82,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=
> > lib/librte_mempool
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
> >  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_sched
> > +DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
> >
> >  include $(RTE_SDK)/mk/rte.lib.mk
> > diff --git a/lib/librte_port/rte_port_kni.c
> b/lib/librte_port/rte_port_kni.c
> > new file mode 100644
> > index 0000000..4cbc345
> > --- /dev/null
> > +++ b/lib/librte_port/rte_port_kni.c
> > @@ -0,0 +1,325 @@
> > +/*-
> > + *   BSD LICENSE
> > + *
> > + *   Copyright(c) 2016 Ethan Zhuang <zhuangwj@gmail.com>.
> > + *   Copyright(c) 2016 Intel Corporation.
> > + *   All rights reserved.
> > + *
> > + *   Redistribution and use in source and binary forms, with or without
> > + *   modification, are permitted provided that the following conditions
> > + *   are met:
> > + *
> > + *     * Redistributions of source code must retain the above copyright
> > + *       notice, this list of conditions and the following disclaimer.
> > + *     * Redistributions in binary form must reproduce the above
> copyright
> > + *       notice, this list of conditions and the following disclaimer in
> > + *       the documentation and/or other materials provided with the
> > + *       distribution.
> > + *     * Neither the name of Intel Corporation nor the names of its
> > + *       contributors may be used to endorse or promote products derived
> > + *       from this software without specific prior written permission.
> > + *
> > + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> > CONTRIBUTORS
> > + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> > FITNESS FOR
> > + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> > COPYRIGHT
> > + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> > INCIDENTAL,
> > + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> > OF USE,
> > + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> > AND ON ANY
> > + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> > TORT
> > + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> > THE USE
> > + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> > DAMAGE.
> > + */
> > +#include <string.h>
> > +
> > +#include <rte_common.h>
> > +#include <rte_malloc.h>
> > +#include <rte_kni.h>
> > +
> > +#include "rte_port_kni.h"
> > +
> > +/*
> > + * Port KNI Reader
> > + */
> > +#ifdef RTE_PORT_STATS_COLLECT
> > +
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
> > +     port->stats.n_pkts_in += val
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
> > +     port->stats.n_pkts_drop += val
> > +
> > +#else
> > +
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
> > +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
> > +
> > +#endif
> > +
> > +struct rte_port_kni_reader {
> > +     struct rte_port_in_stats stats;
> > +
> > +     struct rte_kni *kni;
> > +};
> > +
> > +static void *
> > +rte_port_kni_reader_create(void *params, int socket_id)
> > +{
> > +     struct rte_port_kni_reader_params *conf =
> > +                     (struct rte_port_kni_reader_params *) params;
> > +     struct rte_port_kni_reader *port;
> > +
> > +     /* Check input parameters */
> > +     if (conf == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Memory allocation */
> > +     port = rte_zmalloc_socket("PORT", sizeof(*port),
> > +             RTE_CACHE_LINE_SIZE, socket_id);
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> > __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Initialization */
> > +     port->kni = conf->kni;
> > +
> > +     return port;
> > +}
> > +
> > +static int
> > +rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t
> > n_pkts)
> > +{
> > +     struct rte_port_kni_reader *p =
> > +                     (struct rte_port_kni_reader *) port;
> > +     uint16_t rx_pkt_cnt;
> > +
> > +     rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
> > +     RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
> > +     return rx_pkt_cnt;
> > +}
> > +
> > +static int
> > +rte_port_kni_reader_free(void *port)
> > +{
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
> > +             return -EINVAL;
> > +     }
> > +
> > +     rte_free(port);
> > +
> > +     return 0;
> > +}
> > +
> > +static int rte_port_kni_reader_stats_read(void *port,
> > +     struct rte_port_in_stats *stats, int clear)
> > +{
> > +     struct rte_port_kni_reader *p =
> > +                     (struct rte_port_kni_reader *) port;
> > +
> > +     if (stats != NULL)
> > +             memcpy(stats, &p->stats, sizeof(p->stats));
> > +
> > +     if (clear)
> > +             memset(&p->stats, 0, sizeof(p->stats));
> > +
> > +     return 0;
> > +}
> > +
> > +/*
> > + * Port KNI Writer
> > + */
> > +#ifdef RTE_PORT_STATS_COLLECT
> > +
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
> > +     port->stats.n_pkts_in += val
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
> > +     port->stats.n_pkts_drop += val
> > +
> > +#else
> > +
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
> > +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
> > +
> > +#endif
> > +
> > +struct rte_port_kni_writer {
> > +     struct rte_port_out_stats stats;
> > +
> > +     struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
> > +     uint32_t tx_burst_sz;
> > +     uint32_t tx_buf_count;
> > +     uint64_t bsz_mask;
> > +     struct rte_kni *kni;
> > +};
> > +
> > +static void *
> > +rte_port_kni_writer_create(void *params, int socket_id)
> > +{
> > +     struct rte_port_kni_writer_params *conf =
> > +                     (struct rte_port_kni_writer_params *) params;
> > +     struct rte_port_kni_writer *port;
> > +
> > +     /* Check input parameters */
> > +     if ((conf == NULL) ||
> > +             (conf->tx_burst_sz == 0) ||
> > +             (conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
> > +             (!rte_is_power_of_2(conf->tx_burst_sz))) {
> > +             RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n",
> > __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Memory allocation */
> > +     port = rte_zmalloc_socket("PORT", sizeof(*port),
> > +             RTE_CACHE_LINE_SIZE, socket_id);
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> > __func__);
> > +             return NULL;
> > +     }
> > +
> > +     /* Initialization */
> > +     port->kni = conf->kni;
> > +     port->tx_burst_sz = conf->tx_burst_sz;
> > +     port->tx_buf_count = 0;
> > +     port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
> > +
> > +     return port;
> > +}
> > +
> > +static inline void
> > +send_burst(struct rte_port_kni_writer *p)
> > +{
> > +     uint32_t nb_tx;
> > +
> > +     nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
> > +
> > +     RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p-
> > >tx_buf_count - nb_tx);
> > +     for (; nb_tx < p->tx_buf_count; nb_tx++)
> > +             rte_pktmbuf_free(p->tx_buf[nb_tx]);
> > +
> > +     p->tx_buf_count = 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt)
> > +{
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +
> > +     p->tx_buf[p->tx_buf_count++] = pkt;
> > +     RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> > +     if (p->tx_buf_count >= p->tx_burst_sz)
> > +             send_burst(p);
> > +
> > +     return 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_tx_bulk(void *port,
> > +     struct rte_mbuf **pkts,
> > +     uint64_t pkts_mask)
> > +{
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +     uint64_t bsz_mask = p->bsz_mask;
> > +     uint32_t tx_buf_count = p->tx_buf_count;
> > +     uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
> > +                                     ((pkts_mask & bsz_mask) ^
> > bsz_mask);
> > +
> > +     if (expr == 0) {
> > +             uint64_t n_pkts = __builtin_popcountll(pkts_mask);
> > +             uint32_t n_pkts_ok;
> > +
> > +             if (tx_buf_count)
> > +                     send_burst(p);
> > +
> > +             RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
> > +             n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
> > +
> > +             RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts
> > - n_pkts_ok);
> > +             for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
> > +                     struct rte_mbuf *pkt = pkts[n_pkts_ok];
> > +
> > +                     rte_pktmbuf_free(pkt);
> > +             }
> > +     } else {
> > +             for (; pkts_mask;) {
> > +                     uint32_t pkt_index = __builtin_ctzll(pkts_mask);
> > +                     uint64_t pkt_mask = 1LLU << pkt_index;
> > +                     struct rte_mbuf *pkt = pkts[pkt_index];
> > +
> > +                     p->tx_buf[tx_buf_count++] = pkt;
> > +                     RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> > +                     pkts_mask &= ~pkt_mask;
> > +             }
> > +
> > +             p->tx_buf_count = tx_buf_count;
> > +             if (tx_buf_count >= p->tx_burst_sz)
> > +                     send_burst(p);
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_flush(void *port)
> > +{
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +
> > +     if (p->tx_buf_count > 0)
> > +             send_burst(p);
> > +
> > +     return 0;
> > +}
> > +
> > +static int
> > +rte_port_kni_writer_free(void *port)
> > +{
> > +     if (port == NULL) {
> > +             RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
> > +             return -EINVAL;
> > +     }
> > +
> > +     rte_port_kni_writer_flush(port);
> > +     rte_free(port);
> > +
> > +     return 0;
> > +}
> > +
> > +static int rte_port_kni_writer_stats_read(void *port,
> > +     struct rte_port_out_stats *stats, int clear)
> > +{
> > +     struct rte_port_kni_writer *p =
> > +                     (struct rte_port_kni_writer *) port;
> > +
> > +     if (stats != NULL)
> > +             memcpy(stats, &p->stats, sizeof(p->stats));
> > +
> > +     if (clear)
> > +             memset(&p->stats, 0, sizeof(p->stats));
> > +
> > +     return 0;
> > +}
> > +
> > +/*
> > + * Summary of port operations
> > + */
> > +struct rte_port_in_ops rte_port_kni_reader_ops = {
> > +     .f_create = rte_port_kni_reader_create,
> > +     .f_free = rte_port_kni_reader_free,
> > +     .f_rx = rte_port_kni_reader_rx,
> > +     .f_stats = rte_port_kni_reader_stats_read,
> > +};
> > +
> > +struct rte_port_out_ops rte_port_kni_writer_ops = {
> > +     .f_create = rte_port_kni_writer_create,
> > +     .f_free = rte_port_kni_writer_free,
> > +     .f_tx = rte_port_kni_writer_tx,
> > +     .f_tx_bulk = rte_port_kni_writer_tx_bulk,
> > +     .f_flush = rte_port_kni_writer_flush,
> > +     .f_stats = rte_port_kni_writer_stats_read,
> > +};
> > diff --git a/lib/librte_port/rte_port_kni.h
> b/lib/librte_port/rte_port_kni.h
> > new file mode 100644
> > index 0000000..d4de8c4
> > --- /dev/null
> > +++ b/lib/librte_port/rte_port_kni.h
> > @@ -0,0 +1,82 @@
> > +/*-
> > + *   BSD LICENSE
> > + *
> > + *   Copyright(c) 2016 Ethan Zhuang <zhuangwj@gmail.com>.
> > + *   Copyright(c) 2016 Intel Corporation.
> > + *   All rights reserved.
> > + *
> > + *   Redistribution and use in source and binary forms, with or without
> > + *   modification, are permitted provided that the following conditions
> > + *   are met:
> > + *
> > + *     * Redistributions of source code must retain the above copyright
> > + *       notice, this list of conditions and the following disclaimer.
> > + *     * Redistributions in binary form must reproduce the above
> copyright
> > + *       notice, this list of conditions and the following disclaimer in
> > + *       the documentation and/or other materials provided with the
> > + *       distribution.
> > + *     * Neither the name of Intel Corporation nor the names of its
> > + *       contributors may be used to endorse or promote products derived
> > + *       from this software without specific prior written permission.
> > + *
> > + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> > CONTRIBUTORS
> > + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> > FITNESS FOR
> > + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> > COPYRIGHT
> > + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> > INCIDENTAL,
> > + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> > NOT
> > + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> > OF USE,
> > + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> > AND ON ANY
> > + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> > TORT
> > + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> > THE USE
> > + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> > DAMAGE.
> > + */
> > +
> > +#ifndef __INCLUDE_RTE_PORT_KNI_H__
> > +#define __INCLUDE_RTE_PORT_KNI_H__
> > +
> > +#ifdef __cplusplus
> > +extern "C" {
> > +#endif
> > +
> > +/**
> > + * @file
> > + * RTE Port KNI Interface
> > + *
> > + * kni_reader: input port built on top of pre-initialized KNI interface
> > + * kni_writer: output port built on top of pre-initialized KNI interface
> > + *
> > + ***/
> > +
> > +#include <stdint.h>
> > +
> > +#include <rte_kni.h>
> > +
> > +#include "rte_port.h"
> > +
> > +/** kni_reader port parameters */
> > +struct rte_port_kni_reader_params {
> > +     /** KNI interface reference */
> > +     struct rte_kni *kni;
> > +};
> > +
> > +/** kni_reader port operations */
> > +extern struct rte_port_in_ops rte_port_kni_reader_ops;
> > +
> > +
> > +/** kni_writer port parameters */
> > +struct rte_port_kni_writer_params {
> > +     /** KNI interface reference */
> > +     struct rte_kni *kni;
> > +     /** Burst size to KNI interface. */
> > +     uint32_t tx_burst_sz;
> > +};
> > +
> > +/** kni_writer port operations */
> > +extern struct rte_port_out_ops rte_port_kni_writer_ops;
> > +
> > +#ifdef __cplusplus
> > +}
> > +#endif
> > +
> > +#endif
> > diff --git a/lib/librte_port/rte_port_version.map
> > b/lib/librte_port/rte_port_version.map
> > index 7a0b34d..e61b3fa 100644
> > --- a/lib/librte_port/rte_port_version.map
> > +++ b/lib/librte_port/rte_port_version.map
> > @@ -35,3 +35,11 @@ DPDK_2.2 {
> >       rte_port_ring_multi_writer_nodrop_ops;
> >
> >  } DPDK_2.1;
> > +
> > +DPDK_16.07 {
> > +     global:
> > +
> > +     rte_port_kni_reader_ops;
> > +     rte_port_kni_writer_ops;
> > +
> > +} DPDK_2.2;
> > --
> > 2.7.4
>
> Regards,
> Cristian
>
>

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

* Re: [PATCH v3 1/3] port: add kni interface support
  2016-06-21 11:10       ` Ethan
@ 2016-06-21 11:31         ` Dumitrescu, Cristian
  0 siblings, 0 replies; 22+ messages in thread
From: Dumitrescu, Cristian @ 2016-06-21 11:31 UTC (permalink / raw)
  To: Ethan; +Cc: dev, Singh, Jasvinder, Yigit, Ferruh

Hi Ethan,

Thanks very much for sending the new version.

You are absolutely right about the param->parsed issue, sorry, my fault.

I think you need to use the --cover-letter flag for git format-patch command. You can practice by sending the patch set to your email address first before you send it to the list. Do you want to try sending a new revision of this patch set with the cover letter included (preferred) or you want to stop at v3? Please let us know.

Thanks,
Cristian

From: zhuangweijie@gmail.com [mailto:zhuangweijie@gmail.com] On Behalf Of Ethan
Sent: Tuesday, June 21, 2016 12:11 PM
To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>
Cc: dev@dpdk.org; Singh, Jasvinder <jasvinder.singh@intel.com>; Yigit, Ferruh <ferruh.yigit@intel.com>
Subject: Re: [PATCH v3 1/3] port: add kni interface support

Hi Cristian,

New patch has been submitted. All comments are fixed except this one:
"Here is one bug for you, you need to make sure you add the following line here:
        param->parsed = 1;"
I think the new convention is to set this flag by the macro PARSE_CHECK_DUPLICATE_SECTION.

BTW, although I use the  --cover-letter and --annotate flags in the send-email command, it seems no cover letter is created.
I am not very familiar with this. So sorry!


B.R.
Ethan

2016-06-19 0:44 GMT+08:00 Dumitrescu, Cristian <cristian.dumitrescu@intel.com<mailto:cristian.dumitrescu@intel.com>>:
Hi Ethan,

Thank you, here are some comments inlined below.

Please reorganize this patch in a slightly different way to look similar to other DPDK patch sets and also ease up the integration work for Thomas:
        Patch 0: I suggest adding a cover letter;
        Patch 1: all librte_port changes (rte_port_kni.h, rte_port_kni.c, Makefile, rte_port_version.map), including the "nodrop" KNI port version
        Patch 2: all ip_pipeline app changes
        Patch 3: ip_pipeline app kni.cfg file
        Patch 4: Documentation changes

> -----Original Message-----
> From: WeiJie Zhuang [mailto:zhuangwj@gmail.com<mailto:zhuangwj@gmail.com>]
> Sent: Thursday, June 16, 2016 12:27 PM
> To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com<mailto:cristian.dumitrescu@intel.com>>
> Cc: dev@dpdk.org<mailto:dev@dpdk.org>; Singh, Jasvinder <jasvinder.singh@intel.com<mailto:jasvinder.singh@intel.com>>; Yigit,
> Ferruh <ferruh.yigit@intel.com<mailto:ferruh.yigit@intel.com>>; WeiJie Zhuang <zhuangwj@gmail.com<mailto:zhuangwj@gmail.com>>
> Subject: [PATCH v3 1/3] port: add kni interface support
>
> 1. add KNI port type to the packet framework
> 2. add KNI support to the IP Pipeline sample Application
> 3. some bug fix
>
> Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com<mailto:zhuangwj@gmail.com>>
> ---
> v2:
> * Fix check patch error.
> v3:
> * Fix code review comments.
> ---
>  doc/api/doxy-api-index.md<http://doxy-api-index.md>                          |   1 +
>  examples/ip_pipeline/Makefile                      |   2 +-
>  examples/ip_pipeline/app.h                         | 181 +++++++++++-
>  examples/ip_pipeline/config/kni.cfg                |  67 +++++
>  examples/ip_pipeline/config_check.c                |  26 +-
>  examples/ip_pipeline/config_parse.c                | 166 ++++++++++-
>  examples/ip_pipeline/init.c                        | 132 ++++++++-
>  examples/ip_pipeline/pipeline/pipeline_common_fe.c |  29 ++
>  examples/ip_pipeline/pipeline/pipeline_master_be.c |   6 +
>  examples/ip_pipeline/pipeline_be.h                 |  27 ++
>  lib/librte_port/Makefile                           |   7 +
>  lib/librte_port/rte_port_kni.c                     | 325 +++++++++++++++++++++
>  lib/librte_port/rte_port_kni.h                     |  82 ++++++
>  lib/librte_port/rte_port_version.map               |   8 +
>  14 files changed, 1047 insertions(+), 12 deletions(-)
>  create mode 100644 examples/ip_pipeline/config/kni.cfg
>  create mode 100644 lib/librte_port/rte_port_kni.c
>  create mode 100644 lib/librte_port/rte_port_kni.h
>
> diff --git a/doc/api/doxy-api-index.md<http://doxy-api-index.md> b/doc/api/doxy-api-index.md<http://doxy-api-index.md>
> index f626386..5e7f024 100644
> --- a/doc/api/doxy-api-index.md<http://doxy-api-index.md>
> +++ b/doc/api/doxy-api-index.md<http://doxy-api-index.md>
> @@ -118,6 +118,7 @@ There are many libraries, so their headers may be
> grouped by topics:
>      [frag]             (@ref rte_port_frag.h),
>      [reass]            (@ref rte_port_ras.h),
>      [sched]            (@ref rte_port_sched.h),
> +    [kni]              (@ref rte_port_kni.h),
>      [src/sink]         (@ref rte_port_source_sink.h)
>    * [table]            (@ref rte_table.h):
>      [lpm IPv4]         (@ref rte_table_lpm.h),
> diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
> index 5827117..6dc3f52 100644
> --- a/examples/ip_pipeline/Makefile
> +++ b/examples/ip_pipeline/Makefile
> @@ -1,6 +1,6 @@
>  #   BSD LICENSE
>  #
> -#   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> +#   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
>  #   All rights reserved.
>  #
>  #   Redistribution and use in source and binary forms, with or without
> diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
> index 7611341..abbd6d4 100644
> --- a/examples/ip_pipeline/app.h
> +++ b/examples/ip_pipeline/app.h
> @@ -44,6 +44,9 @@
>  #include <cmdline_parse.h>
>
>  #include <rte_ethdev.h>
> +#ifdef RTE_LIBRTE_KNI
> +#include <rte_kni.h>
> +#endif
>
>  #include "cpu_core_map.h"
>  #include "pipeline.h"
> @@ -132,6 +135,20 @@ struct app_pktq_swq_params {
>       uint32_t mempool_indirect_id;
>  };
>
> +struct app_pktq_kni_params {
> +     char *name;
> +     uint32_t parsed;
> +
> +     uint32_t socket_id;
> +     uint32_t core_id;
> +     uint32_t hyper_th_id;
> +     uint32_t force_bind;
> +
> +     uint32_t mempool_id; /* Position in the app->mempool_params */
> +     uint32_t burst_read;
> +     uint32_t burst_write;
> +};
> +
>  #ifndef APP_FILE_NAME_SIZE
>  #define APP_FILE_NAME_SIZE                       256
>  #endif
> @@ -185,6 +202,7 @@ enum app_pktq_in_type {
>       APP_PKTQ_IN_HWQ,
>       APP_PKTQ_IN_SWQ,
>       APP_PKTQ_IN_TM,
> +     APP_PKTQ_IN_KNI,
>       APP_PKTQ_IN_SOURCE,
>  };
>
> @@ -197,6 +215,7 @@ enum app_pktq_out_type {
>       APP_PKTQ_OUT_HWQ,
>       APP_PKTQ_OUT_SWQ,
>       APP_PKTQ_OUT_TM,
> +     APP_PKTQ_OUT_KNI,
>       APP_PKTQ_OUT_SINK,
>  };
>
> @@ -420,6 +439,8 @@ struct app_eal_params {
>
>  #define APP_MAX_PKTQ_TM                          APP_MAX_LINKS
>
> +#define APP_MAX_PKTQ_KNI                         APP_MAX_LINKS
> +
>  #ifndef APP_MAX_PKTQ_SOURCE
>  #define APP_MAX_PKTQ_SOURCE                      64
>  #endif
> @@ -471,6 +492,7 @@ struct app_params {
>       struct app_pktq_hwq_out_params
> hwq_out_params[APP_MAX_HWQ_OUT];
>       struct app_pktq_swq_params swq_params[APP_MAX_PKTQ_SWQ];
>       struct app_pktq_tm_params tm_params[APP_MAX_PKTQ_TM];
> +     struct app_pktq_kni_params kni_params[APP_MAX_PKTQ_KNI];
>       struct app_pktq_source_params
> source_params[APP_MAX_PKTQ_SOURCE];
>       struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
>       struct app_msgq_params msgq_params[APP_MAX_MSGQ];
> @@ -482,6 +504,7 @@ struct app_params {
>       uint32_t n_pktq_hwq_out;
>       uint32_t n_pktq_swq;
>       uint32_t n_pktq_tm;
> +     uint32_t n_pktq_kni;
>       uint32_t n_pktq_source;
>       uint32_t n_pktq_sink;
>       uint32_t n_msgq;
> @@ -495,6 +518,9 @@ struct app_params {
>       struct app_link_data link_data[APP_MAX_LINKS];
>       struct rte_ring *swq[APP_MAX_PKTQ_SWQ];
>       struct rte_sched_port *tm[APP_MAX_PKTQ_TM];
> +#ifdef RTE_LIBRTE_KNI
> +     struct rte_kni *kni[APP_MAX_PKTQ_KNI];
> +#endif /* RTE_LIBRTE_KNI */
>       struct rte_ring *msgq[APP_MAX_MSGQ];
>       struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
>       struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
> @@ -667,11 +693,11 @@ app_swq_get_reader(struct app_params *app,
>       struct app_pktq_swq_params *swq,
>       uint32_t *pktq_in_id)
>  {
> -     struct app_pipeline_params *reader;
> +     struct app_pipeline_params *reader = NULL;
>       uint32_t pos = swq - app->swq_params;
>       uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
>               RTE_DIM(app->pipeline_params));
> -     uint32_t n_readers = 0, id, i;
> +     uint32_t n_readers = 0, id = 0, i;
>
>       for (i = 0; i < n_pipelines; i++) {
>               struct app_pipeline_params *p = &app->pipeline_params[i];
> @@ -727,11 +753,11 @@ app_tm_get_reader(struct app_params *app,
>       struct app_pktq_tm_params *tm,
>       uint32_t *pktq_in_id)
>  {
> -     struct app_pipeline_params *reader;
> +     struct app_pipeline_params *reader = NULL;
>       uint32_t pos = tm - app->tm_params;
>       uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
>               RTE_DIM(app->pipeline_params));
> -     uint32_t n_readers = 0, id, i;
> +     uint32_t n_readers = 0, id = 0, i;
>
>       for (i = 0; i < n_pipelines; i++) {
>               struct app_pipeline_params *p = &app->pipeline_params[i];
> @@ -758,6 +784,66 @@ app_tm_get_reader(struct app_params *app,
>  }
>
>  static inline uint32_t
> +app_kni_get_readers(struct app_params *app, struct
> app_pktq_kni_params *kni)
> +{
> +     uint32_t pos = kni - app->kni_params;
> +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +             RTE_DIM(app->pipeline_params));
> +     uint32_t n_readers = 0, i;
> +
> +     for (i = 0; i < n_pipelines; i++) {
> +             struct app_pipeline_params *p = &app->pipeline_params[i];
> +             uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p-
> >pktq_in));
> +             uint32_t j;
> +
> +             for (j = 0; j < n_pktq_in; j++) {
> +                     struct app_pktq_in_params *pktq = &p->pktq_in[j];
> +
> +                     if ((pktq->type == APP_PKTQ_IN_KNI) &&
> +                             (pktq->id == pos))
> +                             n_readers++;
> +             }
> +     }
> +
> +     return n_readers;
> +}
> +
> +static inline struct app_pipeline_params *
> +app_kni_get_reader(struct app_params *app,
> +                               struct app_pktq_kni_params *kni,
> +                               uint32_t *pktq_in_id)
> +{
> +     struct app_pipeline_params *reader = NULL;
> +     uint32_t pos = kni - app->kni_params;
> +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +             RTE_DIM(app->pipeline_params));
> +     uint32_t n_readers = 0, id = 0, i;
> +
> +     for (i = 0; i < n_pipelines; i++) {
> +             struct app_pipeline_params *p = &app->pipeline_params[i];
> +             uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p-
> >pktq_in));
> +             uint32_t j;
> +
> +             for (j = 0; j < n_pktq_in; j++) {
> +                     struct app_pktq_in_params *pktq = &p->pktq_in[j];
> +
> +                     if ((pktq->type == APP_PKTQ_IN_KNI) &&
> +                             (pktq->id == pos)) {
> +                             n_readers++;
> +                             reader = p;
> +                             id = j;
> +                     }
> +             }
> +     }
> +
> +     if (n_readers != 1)
> +             return NULL;
> +
> +     *pktq_in_id = id;
> +     return reader;
> +}
> +
> +static inline uint32_t
>  app_source_get_readers(struct app_params *app,
>  struct app_pktq_source_params *source)
>  {
> @@ -861,11 +947,11 @@ app_swq_get_writer(struct app_params *app,
>       struct app_pktq_swq_params *swq,
>       uint32_t *pktq_out_id)
>  {
> -     struct app_pipeline_params *writer;
> +     struct app_pipeline_params *writer = NULL;
>       uint32_t pos = swq - app->swq_params;
>       uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
>               RTE_DIM(app->pipeline_params));
> -     uint32_t n_writers = 0, id, i;
> +     uint32_t n_writers = 0, id = 0, i;
>
>       for (i = 0; i < n_pipelines; i++) {
>               struct app_pipeline_params *p = &app->pipeline_params[i];
> @@ -923,11 +1009,11 @@ app_tm_get_writer(struct app_params *app,
>       struct app_pktq_tm_params *tm,
>       uint32_t *pktq_out_id)
>  {
> -     struct app_pipeline_params *writer;
> +     struct app_pipeline_params *writer = NULL;
>       uint32_t pos = tm - app->tm_params;
>       uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
>               RTE_DIM(app->pipeline_params));
> -     uint32_t n_writers = 0, id, i;
> +     uint32_t n_writers = 0, id = 0, i;
>
>       for (i = 0; i < n_pipelines; i++) {
>               struct app_pipeline_params *p = &app->pipeline_params[i];
> @@ -939,10 +1025,73 @@ app_tm_get_writer(struct app_params *app,
>                       struct app_pktq_out_params *pktq = &p-
> >pktq_out[j];
>
>                       if ((pktq->type == APP_PKTQ_OUT_TM) &&
> +                             (pktq->id == pos)) {
> +                             n_writers++;
> +                             writer = p;
> +                             id = j;
> +                     }
> +             }
> +     }
> +
> +     if (n_writers != 1)
> +             return NULL;
> +
> +     *pktq_out_id = id;
> +     return writer;
> +}
> +
> +static inline uint32_t
> +app_kni_get_writers(struct app_params *app, struct
> app_pktq_kni_params *kni)
> +{
> +     uint32_t pos = kni - app->kni_params;
> +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +             RTE_DIM(app->pipeline_params));
> +     uint32_t n_writers = 0, i;
> +
> +     for (i = 0; i < n_pipelines; i++) {
> +             struct app_pipeline_params *p = &app->pipeline_params[i];
> +             uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
> +                     RTE_DIM(p->pktq_out));
> +             uint32_t j;
> +
> +             for (j = 0; j < n_pktq_out; j++) {
> +                     struct app_pktq_out_params *pktq = &p-
> >pktq_out[j];
> +
> +                     if ((pktq->type == APP_PKTQ_OUT_KNI) &&
>                               (pktq->id == pos))
>                               n_writers++;
> +             }
> +     }
> +
> +     return n_writers;
> +}
> +
> +static inline struct app_pipeline_params *
> +app_kni_get_writer(struct app_params *app,
> +                               struct app_pktq_kni_params *kni,
> +                               uint32_t *pktq_out_id)
> +{
> +     struct app_pipeline_params *writer = NULL;
> +     uint32_t pos = kni - app->kni_params;
> +     uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
> +             RTE_DIM(app->pipeline_params));
> +     uint32_t n_writers = 0, id = 0, i;
> +
> +     for (i = 0; i < n_pipelines; i++) {
> +             struct app_pipeline_params *p = &app->pipeline_params[i];
> +             uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
> +                     RTE_DIM(p->pktq_out));
> +             uint32_t j;
> +
> +             for (j = 0; j < n_pktq_out; j++) {
> +                     struct app_pktq_out_params *pktq = &p-
> >pktq_out[j];
> +
> +                     if ((pktq->type == APP_PKTQ_OUT_KNI) &&
> +                             (pktq->id == pos)) {
> +                             n_writers++;
>                               writer = p;
>                               id = j;
> +                     }
>               }
>       }
>
> @@ -1051,6 +1200,22 @@ app_get_link_for_tm(struct app_params *app,
> struct app_pktq_tm_params *p_tm)
>       return &app->link_params[link_param_idx];
>  }
>
> +static inline struct app_link_params *
> +app_get_link_for_kni(struct app_params *app, struct
> app_pktq_kni_params *p_kni)
> +{
> +     char link_name[APP_PARAM_NAME_SIZE];
> +     uint32_t link_id;
> +     ssize_t link_param_idx;
> +
> +     sscanf(p_kni->name, "KNI%" PRIu32, &link_id);
> +     sprintf(link_name, "LINK%" PRIu32, link_id);
> +     link_param_idx = APP_PARAM_FIND(app->link_params, link_name);
> +     APP_CHECK((link_param_idx >= 0),
> +                       "Cannot find %s for %s", link_name, p_kni->name);
> +
> +     return &app->link_params[link_param_idx];
> +}
> +
>  void app_pipeline_params_get(struct app_params *app,
>       struct app_pipeline_params *p_in,
>       struct pipeline_params *p_out);
> diff --git a/examples/ip_pipeline/config/kni.cfg
> b/examples/ip_pipeline/config/kni.cfg
> new file mode 100644
> index 0000000..c339080
> --- /dev/null
> +++ b/examples/ip_pipeline/config/kni.cfg
> @@ -0,0 +1,67 @@
> +;   BSD LICENSE
> +;
> +;   Copyright(c) 2016 Intel Corporation.
> +;   All rights reserved.
> +;
> +;   Redistribution and use in source and binary forms, with or without
> +;   modification, are permitted provided that the following conditions
> +;   are met:
> +;
> +;     * Redistributions of source code must retain the above copyright
> +;       notice, this list of conditions and the following disclaimer.
> +;     * Redistributions in binary form must reproduce the above copyright
> +;       notice, this list of conditions and the following disclaimer in
> +;       the documentation and/or other materials provided with the
> +;       distribution.
> +;     * Neither the name of Intel Corporation nor the names of its
> +;       contributors may be used to endorse or promote products derived
> +;       from this software without specific prior written permission.
> +;
> +;   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> +;   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> +;   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> +;   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> +;   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> +;   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> +;   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> +;   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> +;   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> +;   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> +;   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> +
> +;             ______________          ______________________
> +;            |              |  KNI0  |                      |
> +; RXQ0.0 --->|              |------->|--+                   |
> +;            |              |  KNI1  |  | br0               |
> +; TXQ0.0 <---|              |<-------|<-+                   |
> +;            | Pass-through |        |     Linux Kernel     |
> +;            |     (P1)     |        |     Network Stack    |
> +;            |              |  KNI0  |                      |
> +; RXQ0.0 --->|              |------->|--+                   |
> +;            |              |  KNI1  |  | br0               |
> +; TXQ0.0 <---|              |<-------|<-+                   |
> +;            |______________|        |______________________|
> +;

There are some errors in the diagram I previously sent you, here is the corrected version (which matches the PIPELINE1 configuration):

;             ______________          ______________________
;            |              |  KNI0  |                      |
; RXQ0.0 --->|              |------->|--+                   |
;            |              |  KNI1  |  | br0               |
; TXQ1.0 <---|              |<-------|<-+                   |
;            | Pass-through |        |     Linux Kernel     |
;            |     (P1)     |        |     Network Stack    |
;            |              |  KNI1  |                      |
; RXQ1.0 --->|              |------->|--+                   |
;            |              |  KNI0  |  | br0               |
; TXQ0.0 <---|              |<-------|<-+                   |
;            |______________|        |______________________|

> +; Insert Linux kernel KNI module:
> +;    [Linux]$ insmod rte_kni.ko
> +;
> +; Configure Linux kernel bridge between KNI0 and KNI1 interfaces:
> +;    [Linux]$ ifconfig KNI0 up
> +;    [Linux]$ ifconfig KNI1 up
> +;    [Linux]$ brctl addbr "br0"
> +;    [Linux]$ brctl addif br0 KNI0
> +;    [Linux]$ brctl addif br0 KNI1
> +;    [Linux]$ ifconfig br0 up
> +
> +[EAL]
> +log_level = 0
> +
> +[PIPELINE0]
> +type = MASTER
> +core = 0
> +
> +[PIPELINE1]
> +type = PASS-THROUGH
> +core = 1
> +pktq_in = RXQ0.0 KNI1 RXQ1.0 KNI0
> +pktq_out = KNI0 TXQ1.0 KNI1 TXQ0.0
> diff --git a/examples/ip_pipeline/config_check.c
> b/examples/ip_pipeline/config_check.c
> index 18f57be..7e90342 100644
> --- a/examples/ip_pipeline/config_check.c
> +++ b/examples/ip_pipeline/config_check.c
> @@ -1,7 +1,7 @@
>  /*-
>   *   BSD LICENSE
>   *
> - *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
> + *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
>   *   All rights reserved.
>   *
>   *   Redistribution and use in source and binary forms, with or without
> @@ -444,6 +444,29 @@ check_pipelines(struct app_params *app)
>       }
>  }
>
> +static void
> +check_knis(struct app_params *app) {
> +     uint32_t i;
> +
> +     for (i = 0; i < app->n_pktq_kni; i++) {
> +             struct app_pktq_kni_params *p = &app->kni_params[i];
> +             uint32_t n_readers = app_kni_get_readers(app, p);
> +             uint32_t n_writers = app_kni_get_writers(app, p);
> +
> +             APP_CHECK((n_readers != 0),
> +                     "%s has no reader\n", p->name);
> +
> +             APP_CHECK((n_readers == 1),
> +                     "%s has more than one reader\n", p->name);
> +
> +             APP_CHECK((n_writers != 0),
> +                     "%s has no writer\n", p->name);
> +
> +             APP_CHECK((n_writers == 1),
> +                     "%s has more than one writer\n", p->name);
> +     }
> +}

I suggest you place this function just after the check_tms() function, in order to keep consistency with our conventions.

> +
>  int
>  app_config_check(struct app_params *app)
>  {
> @@ -453,6 +476,7 @@ app_config_check(struct app_params *app)
>       check_txqs(app);
>       check_swqs(app);
>       check_tms(app);
> +     check_knis(app);
>       check_sources(app);
>       check_sinks(app);
>       check_msgqs(app);
> diff --git a/examples/ip_pipeline/config_parse.c
> b/examples/ip_pipeline/config_parse.c
> index 504018e..c55be31 100644
> --- a/examples/ip_pipeline/config_parse.c
> +++ b/examples/ip_pipeline/config_parse.c
> @@ -189,6 +189,18 @@ struct app_pktq_tm_params default_tm_params = {
>       .burst_write = 32,
>  };
>
> +struct app_pktq_kni_params default_kni_params = {
> +     .parsed = 0,
> +     .socket_id = 0,
> +     .core_id = 0,
> +     .hyper_th_id = 0,
> +     .force_bind = 0,
> +
> +     .mempool_id = 0,
> +     .burst_read = 32,
> +     .burst_write = 32,
> +};
> +
>  struct app_pktq_source_params default_source_params = {
>       .parsed = 0,
>       .mempool_id = 0,
> @@ -300,6 +312,18 @@ app_print_usage(char *prgname)
>       link_param_pos;
>       \
>  })
>
> +#define APP_PARAM_ADD_LINK_FOR_KNI(app, kni_name)
>       \
> +({                                                                   \
> +     char link_name[APP_PARAM_NAME_SIZE];
>       \
> +     ssize_t link_param_pos;
>       \
> +     uint32_t link_id;                                               \
> +                                                                     \
> +     sscanf((kni_name), "KNI%" SCNu32, &link_id);            \
> +     sprintf(link_name, "LINK%" PRIu32, link_id);                    \
> +     link_param_pos = APP_PARAM_ADD((app)->link_params,
> link_name);   \
> +     link_param_pos;
>       \
> +})
> +
>  #define PARSE_CHECK_DUPLICATE_SECTION(obj)
>       \
>  do {                                                                 \
>       APP_CHECK(((obj)->parsed == 0),
>       \
> @@ -826,6 +850,10 @@ parse_pipeline_pktq_in(struct app_params *app,
>                       type = APP_PKTQ_IN_TM;
>                       id = APP_PARAM_ADD(app->tm_params, name);
>                       APP_PARAM_ADD_LINK_FOR_TM(app, name);
> +             } else if (validate_name(name, "KNI", 1) == 0) {
> +                     type = APP_PKTQ_IN_KNI;
> +                     id = APP_PARAM_ADD(app->kni_params, name);
> +                     APP_PARAM_ADD_LINK_FOR_KNI(app, name);
>               } else if (validate_name(name, "SOURCE", 1) == 0) {
>                       type = APP_PKTQ_IN_SOURCE;
>                       id = APP_PARAM_ADD(app->source_params, name);
> @@ -871,6 +899,10 @@ parse_pipeline_pktq_out(struct app_params *app,
>                       type = APP_PKTQ_OUT_TM;
>                       id = APP_PARAM_ADD(app->tm_params, name);
>                       APP_PARAM_ADD_LINK_FOR_TM(app, name);
> +             } else if (validate_name(name, "KNI", 1) == 0) {
> +                     type = APP_PKTQ_OUT_KNI;
> +                     id = APP_PARAM_ADD(app->kni_params, name);
> +                     APP_PARAM_ADD_LINK_FOR_KNI(app, name);
>               } else if (validate_name(name, "SINK", 1) == 0) {
>                       type = APP_PKTQ_OUT_SINK;
>                       id = APP_PARAM_ADD(app->sink_params, name);
> @@ -1816,7 +1848,7 @@ parse_tm(struct app_params *app,
>       param = &app->tm_params[param_idx];
>       PARSE_CHECK_DUPLICATE_SECTION(param);
>
> -     APP_PARAM_ADD_LINK_FOR_TXQ(app, section_name);
> +     APP_PARAM_ADD_LINK_FOR_TM(app, section_name);
>
>       for (i = 0; i < n_entries; i++) {
>               struct rte_cfgfile_entry *ent = &entries[i];
> @@ -1853,6 +1885,85 @@ parse_tm(struct app_params *app,
>  }
>
>  static void
> +parse_kni(struct app_params *app,
> +               const char *section_name,
> +               struct rte_cfgfile *cfg)
> +{
> +     struct app_pktq_kni_params *param;
> +     struct rte_cfgfile_entry *entries;
> +     int n_entries, i;
> +     ssize_t param_idx;
> +
> +     n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
> +     PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0),
> section_name);
> +
> +     entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
> +     PARSE_ERROR_MALLOC(entries != NULL);
> +
> +     rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
> +
> +     param_idx = APP_PARAM_ADD(app->kni_params, section_name);
> +     param = &app->kni_params[param_idx];
> +     PARSE_CHECK_DUPLICATE_SECTION(param);
> +
> +     APP_PARAM_ADD_LINK_FOR_KNI(app, section_name);
> +
> +
> +     for (i = 0; i < n_entries; i++) {
> +             struct rte_cfgfile_entry *ent = &entries[i];
> +
> +             if (strcmp(ent->name, "core") == 0) {
> +                     int status = parse_pipeline_core(
> +                                     &param->socket_id,
> +                                     &param->core_id,
> +                                     &param->hyper_th_id,
> +                                     ent->value);
> +
> +                     PARSE_ERROR((status == 0), section_name,
> +                                             ent->name);
> +                     param->force_bind = 1;
> +                     continue;
> +             }
> +
> +             if (strcmp(ent->name, "mempool") == 0) {
> +                     int status = validate_name(ent->value,
> +
> "MEMPOOL", 1);
> +                     ssize_t idx;
> +
> +                     PARSE_ERROR((status == 0), section_name,
> +                                             ent->name);
> +
> +                     idx = APP_PARAM_ADD(app->mempool_params,
> ent->value);
> +                     param->mempool_id = idx;
> +                     continue;
> +             }
> +
> +             if (strcmp(ent->name, "burst_read") == 0) {
> +                     int status = parser_read_uint32(&param-
> >burst_read,
> +
>               ent->value);
> +
> +                     PARSE_ERROR((status == 0), section_name,
> +                                             ent->name);
> +                     continue;
> +             }
> +
> +             if (strcmp(ent->name, "burst_write") == 0) {
> +                     int status = parser_read_uint32(&param-
> >burst_write,
> +
>               ent->value);
> +
> +                     PARSE_ERROR((status == 0), section_name,
> +                                             ent->name);
> +                     continue;
> +             }
> +
> +             /* unrecognized */
> +             PARSE_ERROR_INVALID(0, section_name, ent->name);
> +     }
> +

Here is one bug for you, you need to make sure you add the following line here:
        param->parsed = 1;

> +     free(entries);
> +}
> +
> +static void
>  parse_source(struct app_params *app,
>       const char *section_name,
>       struct rte_cfgfile *cfg)
> @@ -2147,6 +2258,7 @@ static const struct config_section cfg_file_scheme[]
> = {
>       {"TXQ", 2, parse_txq},
>       {"SWQ", 1, parse_swq},
>       {"TM", 1, parse_tm},
> +     {"KNI", 1, parse_kni},
>       {"SOURCE", 1, parse_source},
>       {"SINK", 1, parse_sink},
>       {"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
> @@ -2285,6 +2397,7 @@ app_config_parse(struct app_params *app, const
> char *file_name)
>       APP_PARAM_COUNT(app->hwq_out_params, app-
> >n_pktq_hwq_out);
>       APP_PARAM_COUNT(app->swq_params, app->n_pktq_swq);
>       APP_PARAM_COUNT(app->tm_params, app->n_pktq_tm);
> +     APP_PARAM_COUNT(app->kni_params, app->n_pktq_kni);
>       APP_PARAM_COUNT(app->source_params, app->n_pktq_source);
>       APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
>       APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
> @@ -2647,6 +2760,45 @@ save_tm_params(struct app_params *app, FILE
> *f)
>  }
>
>  static void
> +save_kni_params(struct app_params *app, FILE *f)
> +{
> +     struct app_pktq_kni_params *p;
> +     size_t i, count;
> +
> +     count = RTE_DIM(app->kni_params);
> +     for (i = 0; i < count; i++) {
> +             p = &app->kni_params[i];
> +             if (!APP_PARAM_VALID(p))
> +                     continue;
> +
> +             /* section name */
> +             fprintf(f, "[%s]\n", p->name);
> +
> +             /* core */
> +             if (p->force_bind) {
> +                     fprintf(f, "; force_bind = 1\n");
> +                     fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
> +                                     p->socket_id,
> +                                     p->core_id,
> +                                     (p->hyper_th_id) ? "h" : "");
> +             } else
> +                     fprintf(f, "; force_bind = 0\n");
> +
> +             /* mempool */
> +             fprintf(f, "%s = %s\n", "mempool",
> +                             app->mempool_params[p-
> >mempool_id].name);
> +
> +             /* burst_read */
> +             fprintf(f, "%s = %" PRIu32 "\n", "burst_read", p-
> >burst_read);
> +
> +             /* burst_write */
> +             fprintf(f, "%s = %" PRIu32 "\n", "burst_write", p-
> >burst_write);
> +
> +             fputc('\n', f);
> +     }
> +}
> +
> +static void
>  save_source_params(struct app_params *app, FILE *f)
>  {
>       struct app_pktq_source_params *p;
> @@ -2753,6 +2905,9 @@ save_pipeline_params(struct app_params *app,
> FILE *f)
>                               case APP_PKTQ_IN_TM:
>                                       name = app->tm_params[pp-
> >id].name;
>                                       break;
> +                             case APP_PKTQ_IN_KNI:
> +                                     name = app->kni_params[pp-
> >id].name;
> +                                     break;
>                               case APP_PKTQ_IN_SOURCE:
>                                       name = app->source_params[pp-
> >id].name;
>                                       break;
> @@ -2787,6 +2942,9 @@ save_pipeline_params(struct app_params *app,
> FILE *f)
>                               case APP_PKTQ_OUT_TM:
>                                       name = app->tm_params[pp-
> >id].name;
>                                       break;
> +                             case APP_PKTQ_OUT_KNI:
> +                                     name = app->kni_params[pp-
> >id].name;
> +                                     break;
>                               case APP_PKTQ_OUT_SINK:
>                                       name = app->sink_params[pp-
> >id].name;
>                                       break;
> @@ -2872,6 +3030,7 @@ app_config_save(struct app_params *app, const
> char *file_name)
>       save_txq_params(app, file);
>       save_swq_params(app, file);
>       save_tm_params(app, file);
> +     save_kni_params(app, file);
>       save_source_params(app, file);
>       save_sink_params(app, file);
>       save_msgq_params(app, file);
> @@ -2921,6 +3080,11 @@ app_config_init(struct app_params *app)
>                       &default_tm_params,
>                       sizeof(default_tm_params));
>
> +     for (i = 0; i < RTE_DIM(app->kni_params); i++)
> +             memcpy(&app->kni_params[i],
> +                        &default_kni_params,
> +                        sizeof(default_kni_params));
> +
>       for (i = 0; i < RTE_DIM(app->source_params); i++)
>               memcpy(&app->source_params[i],
>                       &default_source_params,
> diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c

In order to keep up with our existing code conventions, please move the KNI code (functions kni_change_mtu(), kni_config_network_interface(), app_init_kni()) just after function app_init_tm().

> index 7120bab..d522de4 100644
> --- a/examples/ip_pipeline/init.c
> +++ b/examples/ip_pipeline/init.c
> @@ -1281,10 +1281,21 @@ void app_pipeline_params_get(struct
> app_params *app,
>                       break;
>               }
>               case APP_PKTQ_IN_TM:
> +             {
>                       out->type = PIPELINE_PORT_IN_SCHED_READER;
>                       out->params.sched.sched = app->tm[in->id];
>                       out->burst_size = app->tm_params[in-
> >id].burst_read;
>                       break;
> +             }
> +#ifdef RTE_LIBRTE_KNI
> +             case APP_PKTQ_IN_KNI:
> +             {
> +                     out->type = PIPELINE_PORT_IN_KNI_READER;
> +                     out->params.kni.kni = app->kni[in->id];
> +                     out->burst_size = app->kni_params[in-
> >id].burst_read;
> +                     break;
> +             }
> +#endif /* RTE_LIBRTE_KNI */
>               case APP_PKTQ_IN_SOURCE:
>               {
>                       uint32_t mempool_id =
> @@ -1409,7 +1420,8 @@ void app_pipeline_params_get(struct app_params
> *app,
>                       }
>                       break;
>               }
> -             case APP_PKTQ_OUT_TM: {
> +             case APP_PKTQ_OUT_TM:
> +             {
>                       struct rte_port_sched_writer_params *params =
>                               &out->params.sched;
>
> @@ -1419,6 +1431,16 @@ void app_pipeline_params_get(struct
> app_params *app,
>                               app->tm_params[in->id].burst_write;
>                       break;
>               }
> +#ifdef RTE_LIBRTE_KNI
> +             case APP_PKTQ_OUT_KNI:
> +             {
> +                     out->type = PIPELINE_PORT_OUT_KNI_WRITER;
> +                     out->params.kni.kni = app->kni[in->id];
> +                     out->params.kni.tx_burst_sz =
> +                             app->kni_params[in->id].burst_write;
> +                     break;
> +             }
> +#endif /* RTE_LIBRTE_KNI */
>               case APP_PKTQ_OUT_SINK:
>               {
>                       out->type = PIPELINE_PORT_OUT_SINK;
> @@ -1452,6 +1474,113 @@ void app_pipeline_params_get(struct
> app_params *app,
>       }
>  }
>
> +#ifdef RTE_LIBRTE_KNI
> +static int
> +kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
> +     int ret = 0;
> +
> +     if (port_id >= rte_eth_dev_count())
> +             return -EINVAL;
> +
> +     if (if_up) {
> +             rte_eth_dev_stop(port_id);
> +             ret = rte_eth_dev_start(port_id);
> +     } else
> +             rte_eth_dev_stop(port_id);
> +
> +     return ret;
> +}
> +

Here is a critical issue that we found out recently during testing the KNI code: the correct PMD API functions to use here are rte_eth_dev_set_link_up/down(); stopping and restarting the device while the device is running can lead to segfault and nondeterministic behaviour.

Here is my proposal:

static int
kni_config_network_interface(uint8_t port_id, uint8_t if_up) {
        int ret = 0;

        if (port_id >= rte_eth_dev_count())
                return -EINVAL;

        ret = (if_up)?
                rte_eth_dev_set_link_up(port_id) :
                rte_eth_dev_set_link_down(port_id);

        return ret;
}

I tested this function successfully.

> +static int
> +kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
> +     int ret;
> +
> +     if (port_id >= rte_eth_dev_count())
> +             return -EINVAL;
> +
> +     if (new_mtu > ETHER_MAX_LEN)
> +             return -EINVAL;
> +
> +     /* Stop specific port */
> +     rte_eth_dev_stop(port_id);
> +
> +     /* Set new MTU */
> +     ret = rte_eth_dev_set_mtu(port_id, new_mtu);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Restart specific port */
> +     ret = rte_eth_dev_start(port_id);
> +     if (ret < 0)
> +             return ret;
> +
> +     return 0;
> +}

Same issue about using the rte_eth_dev_start/stop() functions. Based on function port_mtu_set() of the app/test-pmd application, it looks like rte_eth_dev_set_mtu() function could be called directly with no start/stop or up/down preparations.

Here is my version, although I did not fully test this one:

static int
kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
        int ret;

        if (port_id >= rte_eth_dev_count())
                return -EINVAL;

        if (new_mtu > ETHER_MAX_LEN)
                return -EINVAL;

        /* Set new MTU */
        ret = rte_eth_dev_set_mtu(port_id, new_mtu);
        if (ret < 0)
                return ret;

        return 0;
}

> +#endif /* RTE_LIBRTE_KNI */
> +
> +static void
> +app_init_kni(struct app_params *app) {
> +     if (app->n_pktq_kni == 0)
> +             return;
> +#ifndef RTE_LIBRTE_KNI
> +     else
> +             rte_panic("Can not init KNI without librte_kni support.\n");
> +#else

I would like to avoid #ifdefs in the middle of the function code as much as possible. I suggest we do the following:

#ifndef RTE_LIBRTE_KNI

static void
app_init_kni(struct app_params *app)
{
        if (app->n_pktq_kni == 0)
                return;

        rte_panic("Cannot init KNI without librte_kni support.\n");
}

#else

static void
app_init_kni(struct app_params *app)
{
        //the real function code
}

#endif

> +     rte_kni_init(app->n_pktq_kni);
> +
> +     for (uint32_t i = 0; i < app->n_pktq_kni; i++) {

Please declare the "i" variable at the start of the function, as usual. Although I personally like this idea, this pattern is not used anywhere in DPDK and it might trigger warnings/errors from some C compilers. Btw, any idea whether this is mentioned in any way by the ANSI C/C99 standards?

> +             struct app_pktq_kni_params *p_kni = &app->kni_params[i];
> +             struct app_link_params *p_link;
> +             struct rte_eth_dev_info dev_info;
> +             struct app_mempool_params *mempool_params;
> +             struct rte_mempool *mempool;
> +             struct rte_kni_conf conf;
> +             struct rte_kni_ops ops;
> +
> +             /* LINK */
> +             p_link = app_get_link_for_kni(app, p_kni);
> +             memset(&dev_info, 0, sizeof(dev_info));
> +             rte_eth_dev_info_get(p_link->pmd_id, &dev_info);
> +
> +             /* MEMPOOL */
> +             mempool_params = &app->mempool_params[p_kni-
> >mempool_id];
> +             mempool = app->mempool[p_kni->mempool_id];
> +
> +             /* KNI */
> +             memset(&conf, 0, sizeof(conf));
> +             snprintf(conf.name<http://conf.name>, RTE_KNI_NAMESIZE, "%s", p_kni-
> >name);
> +             conf.force_bind = p_kni->force_bind;
> +             if (conf.force_bind) {
> +                     int lcore_id;
> +
> +                     lcore_id = cpu_core_map_get_lcore_id(app-
> >core_map,
> +                             p_kni->socket_id,
> +                             p_kni->core_id,
> +                             p_kni->hyper_th_id);
> +
> +                     if (lcore_id < 0)
> +                             rte_panic("%s invalid CPU core\n", p_kni-
> >name);
> +
> +                     conf.core_id = (uint32_t) lcore_id;
> +             }
> +             conf.group_id = p_link->pmd_id;
> +             conf.mbuf_size = mempool_params->buffer_size;
> +             conf.addr = dev_info.pci_dev->addr;
> +             conf.id<http://conf.id> = dev_info.pci_dev->id;
> +
> +             memset(&ops, 0, sizeof(ops));
> +             ops.port_id = (uint8_t) p_link->pmd_id;
> +             ops.change_mtu = kni_change_mtu;
> +             ops.config_network_if = kni_config_network_interface;
> +
> +             APP_LOG(app, HIGH, "Initializing %s ...", p_kni->name);
> +             app->kni[i] = rte_kni_alloc(mempool, &conf, &ops);
> +             if (!app->kni[i])
> +                     rte_panic("%s init error\n", p_kni->name);
> +     }
> +#endif /* RTE_LIBRTE_KNI */
> +}
> +
>  static void
>  app_init_pipelines(struct app_params *app)
>  {
> @@ -1607,6 +1736,7 @@ int app_init(struct app_params *app)
>       app_init_link(app);
>       app_init_swq(app);
>       app_init_tm(app);
> +     app_init_kni(app);
>       app_init_msgq(app);
>
>       app_pipeline_common_cmd_push(app);
> diff --git a/examples/ip_pipeline/pipeline/pipeline_common_fe.c
> b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
> index 70c57e4..63723cd 100644
> --- a/examples/ip_pipeline/pipeline/pipeline_common_fe.c
> +++ b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
> @@ -130,6 +130,35 @@ app_pipeline_track_pktq_out_to_link(struct
> app_params *app,
>                       break;
>               }
>
> +#ifdef RTE_LIBRTE_KNI

As in any other place that just deals with KNI configuration data rather than KNI init data (e.g. in app.h or config_parse.c), we do not need to #ifdef the code fragment below. Please remove this instance of #ifdef RTE_LIBRTE_KNI.

> +             case APP_PKTQ_OUT_KNI:
> +             {
> +                     struct pipeline_params pp;
> +                     struct pipeline_type *ptype;
> +                     struct app_pktq_kni_params *kni;
> +                     uint32_t pktq_in_id;
> +                     int status;
> +
> +                     kni = &app->kni_params[pktq_out->id];
> +                     p = app_kni_get_reader(app, kni, &pktq_in_id);
> +                     if (p == NULL)
> +                             return NULL;
> +
> +                     ptype = app_pipeline_type_find(app, p->type);
> +                     if ((ptype == NULL) || (ptype->fe_ops->f_track ==
> NULL))
> +                             return NULL;
> +
> +                     app_pipeline_params_get(app, p, &pp);
> +                     status = ptype->fe_ops->f_track(&pp,
> +                             pktq_in_id,
> +                             &pktq_out_id);
> +                     if (status)
> +                             return NULL;
> +
> +                     break;
> +             }
> +#endif
> +
>               case APP_PKTQ_OUT_SINK:
>               default:
>                       return NULL;
> diff --git a/examples/ip_pipeline/pipeline/pipeline_master_be.c
> b/examples/ip_pipeline/pipeline/pipeline_master_be.c
> index 79869a4..32d4635 100644
> --- a/examples/ip_pipeline/pipeline/pipeline_master_be.c
> +++ b/examples/ip_pipeline/pipeline/pipeline_master_be.c
> @@ -144,6 +144,12 @@ pipeline_run(void *pipeline)
>               rte_exit(0, "Bye!\n");
>       }
>
> +#ifdef RTE_LIBRTE_KNI
> +     /* Handle KNI requests from Linux kernel */
> +     for (uint32_t i = 0; i < app->n_pktq_kni; i++)

Same comment as above on definition of the "i" variable.

> +             rte_kni_handle_request(app->kni[i]);
> +#endif /* RTE_LIBRTE_KNI */
> +
>       return 0;
>  }
>
> diff --git a/examples/ip_pipeline/pipeline_be.h
> b/examples/ip_pipeline/pipeline_be.h
> index 5501ab7..0ee208c 100644
> --- a/examples/ip_pipeline/pipeline_be.h
> +++ b/examples/ip_pipeline/pipeline_be.h
> @@ -40,6 +40,9 @@
>  #include <rte_port_ras.h>
>  #include <rte_port_sched.h>
>  #include <rte_port_source_sink.h>
> +#ifdef RTE_LIBRTE_KNI
> +#include <rte_port_kni.h>
> +#endif
>  #include <rte_pipeline.h>
>
>  enum pipeline_port_in_type {
> @@ -49,6 +52,7 @@ enum pipeline_port_in_type {
>       PIPELINE_PORT_IN_RING_READER_IPV4_FRAG,
>       PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
>       PIPELINE_PORT_IN_SCHED_READER,
> +     PIPELINE_PORT_IN_KNI_READER,
>       PIPELINE_PORT_IN_SOURCE,
>  };
>
> @@ -61,6 +65,9 @@ struct pipeline_port_in_params {
>               struct rte_port_ring_reader_ipv4_frag_params
> ring_ipv4_frag;
>               struct rte_port_ring_reader_ipv6_frag_params
> ring_ipv6_frag;
>               struct rte_port_sched_reader_params sched;
> +#ifdef RTE_LIBRTE_KNI
> +             struct rte_port_kni_reader_params kni;
> +#endif
>               struct rte_port_source_params source;
>       } params;
>       uint32_t burst_size;
> @@ -82,6 +89,10 @@ pipeline_port_in_params_convert(struct
> pipeline_port_in_params  *p)
>               return (void *) &p->params.ring_ipv6_frag;
>       case PIPELINE_PORT_IN_SCHED_READER:
>               return (void *) &p->params.sched;
> +#ifdef RTE_LIBRTE_KNI
> +     case PIPELINE_PORT_IN_KNI_READER:
> +             return (void *) &p->params.kni;
> +#endif
>       case PIPELINE_PORT_IN_SOURCE:
>               return (void *) &p->params.source;
>       default:
> @@ -105,6 +116,10 @@ pipeline_port_in_params_get_ops(struct
> pipeline_port_in_params  *p)
>               return &rte_port_ring_reader_ipv6_frag_ops;
>       case PIPELINE_PORT_IN_SCHED_READER:
>               return &rte_port_sched_reader_ops;
> +#ifdef RTE_LIBRTE_KNI
> +     case PIPELINE_PORT_IN_KNI_READER:
> +             return &rte_port_kni_reader_ops;
> +#endif
>       case PIPELINE_PORT_IN_SOURCE:
>               return &rte_port_source_ops;
>       default:
> @@ -122,6 +137,7 @@ enum pipeline_port_out_type {
>       PIPELINE_PORT_OUT_RING_WRITER_IPV4_RAS,
>       PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
>       PIPELINE_PORT_OUT_SCHED_WRITER,
> +     PIPELINE_PORT_OUT_KNI_WRITER,
>       PIPELINE_PORT_OUT_SINK,
>  };
>
> @@ -137,6 +153,9 @@ struct pipeline_port_out_params {
>               struct rte_port_ring_writer_ipv4_ras_params ring_ipv4_ras;
>               struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
>               struct rte_port_sched_writer_params sched;
> +#ifdef RTE_LIBRTE_KNI
> +             struct rte_port_kni_writer_params kni;
> +#endif
>               struct rte_port_sink_params sink;
>       } params;
>  };
> @@ -163,6 +182,10 @@ pipeline_port_out_params_convert(struct
> pipeline_port_out_params  *p)
>               return (void *) &p->params.ring_ipv6_ras;
>       case PIPELINE_PORT_OUT_SCHED_WRITER:
>               return (void *) &p->params.sched;
> +#ifdef RTE_LIBRTE_KNI
> +     case PIPELINE_PORT_OUT_KNI_WRITER:
> +             return (void *) &p->params.kni;
> +#endif
>       case PIPELINE_PORT_OUT_SINK:
>               return (void *) &p->params.sink;
>       default:
> @@ -192,6 +215,10 @@ pipeline_port_out_params_get_ops(struct
> pipeline_port_out_params  *p)
>               return &rte_port_ring_writer_ipv6_ras_ops;
>       case PIPELINE_PORT_OUT_SCHED_WRITER:
>               return &rte_port_sched_writer_ops;
> +#ifdef RTE_LIBRTE_KNI
> +     case PIPELINE_PORT_OUT_KNI_WRITER:
> +             return &rte_port_kni_writer_ops;
> +#endif
>       case PIPELINE_PORT_OUT_SINK:
>               return &rte_port_sink_ops;
>       default:
> diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
> index dc6a601..0fc929b 100644
> --- a/lib/librte_port/Makefile
> +++ b/lib/librte_port/Makefile
> @@ -56,6 +56,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_frag.c
>  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_ras.c
>  endif
>  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_sched.c
> +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> +SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_kni.c
> +endif
>  SRCS-$(CONFIG_RTE_LIBRTE_PORT) += rte_port_source_sink.c
>
>  # install includes
> @@ -67,6 +70,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=
> rte_port_frag.h
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_ras.h
>  endif
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_sched.h
> +ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
> +SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_kni.h
> +endif
>  SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
>
>  # this lib depends upon:
> @@ -76,5 +82,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=
> lib/librte_mempool
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
>  DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_sched
> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_kni
>
>  include $(RTE_SDK)/mk/rte.lib.mk<http://rte.lib.mk>
> diff --git a/lib/librte_port/rte_port_kni.c b/lib/librte_port/rte_port_kni.c
> new file mode 100644
> index 0000000..4cbc345
> --- /dev/null
> +++ b/lib/librte_port/rte_port_kni.c
> @@ -0,0 +1,325 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2016 Ethan Zhuang <zhuangwj@gmail.com<mailto:zhuangwj@gmail.com>>.
> + *   Copyright(c) 2016 Intel Corporation.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> + */
> +#include <string.h>
> +
> +#include <rte_common.h>
> +#include <rte_malloc.h>
> +#include <rte_kni.h>
> +
> +#include "rte_port_kni.h"
> +
> +/*
> + * Port KNI Reader
> + */
> +#ifdef RTE_PORT_STATS_COLLECT
> +
> +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val) \
> +     port->stats.n_pkts_in += val
> +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
> +     port->stats.n_pkts_drop += val
> +
> +#else
> +
> +#define RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(port, val)
> +#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val)
> +
> +#endif
> +
> +struct rte_port_kni_reader {
> +     struct rte_port_in_stats stats;
> +
> +     struct rte_kni *kni;
> +};
> +
> +static void *
> +rte_port_kni_reader_create(void *params, int socket_id)
> +{
> +     struct rte_port_kni_reader_params *conf =
> +                     (struct rte_port_kni_reader_params *) params;
> +     struct rte_port_kni_reader *port;
> +
> +     /* Check input parameters */
> +     if (conf == NULL) {
> +             RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
> +             return NULL;
> +     }
> +
> +     /* Memory allocation */
> +     port = rte_zmalloc_socket("PORT", sizeof(*port),
> +             RTE_CACHE_LINE_SIZE, socket_id);
> +     if (port == NULL) {
> +             RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> __func__);
> +             return NULL;
> +     }
> +
> +     /* Initialization */
> +     port->kni = conf->kni;
> +
> +     return port;
> +}
> +
> +static int
> +rte_port_kni_reader_rx(void *port, struct rte_mbuf **pkts, uint32_t
> n_pkts)
> +{
> +     struct rte_port_kni_reader *p =
> +                     (struct rte_port_kni_reader *) port;
> +     uint16_t rx_pkt_cnt;
> +
> +     rx_pkt_cnt = rte_kni_rx_burst(p->kni, pkts, n_pkts);
> +     RTE_PORT_KNI_READER_STATS_PKTS_IN_ADD(p, rx_pkt_cnt);
> +     return rx_pkt_cnt;
> +}
> +
> +static int
> +rte_port_kni_reader_free(void *port)
> +{
> +     if (port == NULL) {
> +             RTE_LOG(ERR, PORT, "%s: port is NULL\n", __func__);
> +             return -EINVAL;
> +     }
> +
> +     rte_free(port);
> +
> +     return 0;
> +}
> +
> +static int rte_port_kni_reader_stats_read(void *port,
> +     struct rte_port_in_stats *stats, int clear)
> +{
> +     struct rte_port_kni_reader *p =
> +                     (struct rte_port_kni_reader *) port;
> +
> +     if (stats != NULL)
> +             memcpy(stats, &p->stats, sizeof(p->stats));
> +
> +     if (clear)
> +             memset(&p->stats, 0, sizeof(p->stats));
> +
> +     return 0;
> +}
> +
> +/*
> + * Port KNI Writer
> + */
> +#ifdef RTE_PORT_STATS_COLLECT
> +
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val) \
> +     port->stats.n_pkts_in += val
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
> +     port->stats.n_pkts_drop += val
> +
> +#else
> +
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(port, val)
> +#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val)
> +
> +#endif
> +
> +struct rte_port_kni_writer {
> +     struct rte_port_out_stats stats;
> +
> +     struct rte_mbuf *tx_buf[2 * RTE_PORT_IN_BURST_SIZE_MAX];
> +     uint32_t tx_burst_sz;
> +     uint32_t tx_buf_count;
> +     uint64_t bsz_mask;
> +     struct rte_kni *kni;
> +};
> +
> +static void *
> +rte_port_kni_writer_create(void *params, int socket_id)
> +{
> +     struct rte_port_kni_writer_params *conf =
> +                     (struct rte_port_kni_writer_params *) params;
> +     struct rte_port_kni_writer *port;
> +
> +     /* Check input parameters */
> +     if ((conf == NULL) ||
> +             (conf->tx_burst_sz == 0) ||
> +             (conf->tx_burst_sz > RTE_PORT_IN_BURST_SIZE_MAX) ||
> +             (!rte_is_power_of_2(conf->tx_burst_sz))) {
> +             RTE_LOG(ERR, PORT, "%s: Invalid input parameters\n",
> __func__);
> +             return NULL;
> +     }
> +
> +     /* Memory allocation */
> +     port = rte_zmalloc_socket("PORT", sizeof(*port),
> +             RTE_CACHE_LINE_SIZE, socket_id);
> +     if (port == NULL) {
> +             RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n",
> __func__);
> +             return NULL;
> +     }
> +
> +     /* Initialization */
> +     port->kni = conf->kni;
> +     port->tx_burst_sz = conf->tx_burst_sz;
> +     port->tx_buf_count = 0;
> +     port->bsz_mask = 1LLU << (conf->tx_burst_sz - 1);
> +
> +     return port;
> +}
> +
> +static inline void
> +send_burst(struct rte_port_kni_writer *p)
> +{
> +     uint32_t nb_tx;
> +
> +     nb_tx = rte_kni_tx_burst(p->kni, p->tx_buf, p->tx_buf_count);
> +
> +     RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, p-
> >tx_buf_count - nb_tx);
> +     for (; nb_tx < p->tx_buf_count; nb_tx++)
> +             rte_pktmbuf_free(p->tx_buf[nb_tx]);
> +
> +     p->tx_buf_count = 0;
> +}
> +
> +static int
> +rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt)
> +{
> +     struct rte_port_kni_writer *p =
> +                     (struct rte_port_kni_writer *) port;
> +
> +     p->tx_buf[p->tx_buf_count++] = pkt;
> +     RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> +     if (p->tx_buf_count >= p->tx_burst_sz)
> +             send_burst(p);
> +
> +     return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_tx_bulk(void *port,
> +     struct rte_mbuf **pkts,
> +     uint64_t pkts_mask)
> +{
> +     struct rte_port_kni_writer *p =
> +                     (struct rte_port_kni_writer *) port;
> +     uint64_t bsz_mask = p->bsz_mask;
> +     uint32_t tx_buf_count = p->tx_buf_count;
> +     uint64_t expr = (pkts_mask & (pkts_mask + 1)) |
> +                                     ((pkts_mask & bsz_mask) ^
> bsz_mask);
> +
> +     if (expr == 0) {
> +             uint64_t n_pkts = __builtin_popcountll(pkts_mask);
> +             uint32_t n_pkts_ok;
> +
> +             if (tx_buf_count)
> +                     send_burst(p);
> +
> +             RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, n_pkts);
> +             n_pkts_ok = rte_kni_tx_burst(p->kni, pkts, n_pkts);
> +
> +             RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(p, n_pkts
> - n_pkts_ok);
> +             for (; n_pkts_ok < n_pkts; n_pkts_ok++) {
> +                     struct rte_mbuf *pkt = pkts[n_pkts_ok];
> +
> +                     rte_pktmbuf_free(pkt);
> +             }
> +     } else {
> +             for (; pkts_mask;) {
> +                     uint32_t pkt_index = __builtin_ctzll(pkts_mask);
> +                     uint64_t pkt_mask = 1LLU << pkt_index;
> +                     struct rte_mbuf *pkt = pkts[pkt_index];
> +
> +                     p->tx_buf[tx_buf_count++] = pkt;
> +                     RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
> +                     pkts_mask &= ~pkt_mask;
> +             }
> +
> +             p->tx_buf_count = tx_buf_count;
> +             if (tx_buf_count >= p->tx_burst_sz)
> +                     send_burst(p);
> +     }
> +
> +     return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_flush(void *port)
> +{
> +     struct rte_port_kni_writer *p =
> +                     (struct rte_port_kni_writer *) port;
> +
> +     if (p->tx_buf_count > 0)
> +             send_burst(p);
> +
> +     return 0;
> +}
> +
> +static int
> +rte_port_kni_writer_free(void *port)
> +{
> +     if (port == NULL) {
> +             RTE_LOG(ERR, PORT, "%s: Port is NULL\n", __func__);
> +             return -EINVAL;
> +     }
> +
> +     rte_port_kni_writer_flush(port);
> +     rte_free(port);
> +
> +     return 0;
> +}
> +
> +static int rte_port_kni_writer_stats_read(void *port,
> +     struct rte_port_out_stats *stats, int clear)
> +{
> +     struct rte_port_kni_writer *p =
> +                     (struct rte_port_kni_writer *) port;
> +
> +     if (stats != NULL)
> +             memcpy(stats, &p->stats, sizeof(p->stats));
> +
> +     if (clear)
> +             memset(&p->stats, 0, sizeof(p->stats));
> +
> +     return 0;
> +}
> +
> +/*
> + * Summary of port operations
> + */
> +struct rte_port_in_ops rte_port_kni_reader_ops = {
> +     .f_create = rte_port_kni_reader_create,
> +     .f_free = rte_port_kni_reader_free,
> +     .f_rx = rte_port_kni_reader_rx,
> +     .f_stats = rte_port_kni_reader_stats_read,
> +};
> +
> +struct rte_port_out_ops rte_port_kni_writer_ops = {
> +     .f_create = rte_port_kni_writer_create,
> +     .f_free = rte_port_kni_writer_free,
> +     .f_tx = rte_port_kni_writer_tx,
> +     .f_tx_bulk = rte_port_kni_writer_tx_bulk,
> +     .f_flush = rte_port_kni_writer_flush,
> +     .f_stats = rte_port_kni_writer_stats_read,
> +};
> diff --git a/lib/librte_port/rte_port_kni.h b/lib/librte_port/rte_port_kni.h
> new file mode 100644
> index 0000000..d4de8c4
> --- /dev/null
> +++ b/lib/librte_port/rte_port_kni.h
> @@ -0,0 +1,82 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2016 Ethan Zhuang <zhuangwj@gmail.com<mailto:zhuangwj@gmail.com>>.
> + *   Copyright(c) 2016 Intel Corporation.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
> NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
> OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
> AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> + */
> +
> +#ifndef __INCLUDE_RTE_PORT_KNI_H__
> +#define __INCLUDE_RTE_PORT_KNI_H__
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/**
> + * @file
> + * RTE Port KNI Interface
> + *
> + * kni_reader: input port built on top of pre-initialized KNI interface
> + * kni_writer: output port built on top of pre-initialized KNI interface
> + *
> + ***/
> +
> +#include <stdint.h>
> +
> +#include <rte_kni.h>
> +
> +#include "rte_port.h"
> +
> +/** kni_reader port parameters */
> +struct rte_port_kni_reader_params {
> +     /** KNI interface reference */
> +     struct rte_kni *kni;
> +};
> +
> +/** kni_reader port operations */
> +extern struct rte_port_in_ops rte_port_kni_reader_ops;
> +
> +
> +/** kni_writer port parameters */
> +struct rte_port_kni_writer_params {
> +     /** KNI interface reference */
> +     struct rte_kni *kni;
> +     /** Burst size to KNI interface. */
> +     uint32_t tx_burst_sz;
> +};
> +
> +/** kni_writer port operations */
> +extern struct rte_port_out_ops rte_port_kni_writer_ops;
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
> diff --git a/lib/librte_port/rte_port_version.map
> b/lib/librte_port/rte_port_version.map
> index 7a0b34d..e61b3fa 100644
> --- a/lib/librte_port/rte_port_version.map
> +++ b/lib/librte_port/rte_port_version.map
> @@ -35,3 +35,11 @@ DPDK_2.2 {
>       rte_port_ring_multi_writer_nodrop_ops;
>
>  } DPDK_2.1;
> +
> +DPDK_16.07 {
> +     global:
> +
> +     rte_port_kni_reader_ops;
> +     rte_port_kni_writer_ops;
> +
> +} DPDK_2.2;
> --
> 2.7.4

Regards,
Cristian


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

* Re: [PATCH v4 1/4] port: kni interface support
  2016-06-21 10:55   ` [PATCH v4 1/4] port: " Ethan Zhuang
                       ` (2 preceding siblings ...)
  2016-06-21 10:55     ` [PATCH v4 4/4] doc: kni port support in the packet framework Ethan Zhuang
@ 2016-06-21 12:03     ` Dumitrescu, Cristian
  2016-06-21 16:09       ` Thomas Monjalon
  3 siblings, 1 reply; 22+ messages in thread
From: Dumitrescu, Cristian @ 2016-06-21 12:03 UTC (permalink / raw)
  To: Ethan Zhuang; +Cc: dev, Singh, Jasvinder, Yigit, Ferruh



> -----Original Message-----
> From: Ethan Zhuang [mailto:zhuangwj@gmail.com]
> Sent: Tuesday, June 21, 2016 11:56 AM
> To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>
> Cc: dev@dpdk.org; Singh, Jasvinder <jasvinder.singh@intel.com>; Yigit,
> Ferruh <ferruh.yigit@intel.com>; WeiJie Zhuang <zhuangwj@gmail.com>
> Subject: [PATCH v4 1/4] port: kni interface support
> 
> From: WeiJie Zhuang <zhuangwj@gmail.com>
> 
> add KNI port type to the packet framework
> 
> Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
> ---
> v2:
> * Fix check patch error.
> v3:
> * Fix code review comments.
> v4:
> * Reorganize patch sets and fix some comments
> ---

Series-acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>

I am acking patches 1 - 4 from v4 series on KNI support for Packet Framework and ip_pipeline app.

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

* Re: [PATCH v4 1/4] port: kni interface support
  2016-06-21 12:03     ` [PATCH v4 1/4] port: kni interface support Dumitrescu, Cristian
@ 2016-06-21 16:09       ` Thomas Monjalon
  0 siblings, 0 replies; 22+ messages in thread
From: Thomas Monjalon @ 2016-06-21 16:09 UTC (permalink / raw)
  To: Ethan Zhuang; +Cc: dev, Dumitrescu, Cristian, Singh, Jasvinder, Yigit, Ferruh

> > From: WeiJie Zhuang <zhuangwj@gmail.com>
> > 
> > add KNI port type to the packet framework
> > 
> > Signed-off-by: WeiJie Zhuang <zhuangwj@gmail.com>
> 
> Series-acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>

Applied, thanks

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

end of thread, other threads:[~2016-06-21 16:09 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-27  5:07 [PATCH] port: add KNI interface support 1. add KNI port type to the packet framework 2. add KNI support to the IP Pipeline sample Application WeiJie Zhuang
2016-05-28 11:25 ` [PATCH] port: add kni interface support WeiJie Zhuang
2016-05-30 14:40   ` Dumitrescu, Cristian
2016-06-01  4:18     ` Ethan
2016-06-09 23:42   ` Dumitrescu, Cristian
2016-06-13 10:25     ` Dumitrescu, Cristian
2016-06-13 10:47     ` Ethan
2016-06-13 13:18       ` Dumitrescu, Cristian
2016-06-16 11:34         ` Ethan
2016-06-16 11:27   ` [PATCH v3 1/3] " WeiJie Zhuang
2016-06-16 11:27     ` [PATCH v3 2/3] port: add kni nodrop writer WeiJie Zhuang
2016-06-18 21:47       ` Dumitrescu, Cristian
2016-06-16 11:27     ` [PATCH v3 3/3] port: document update WeiJie Zhuang
2016-06-18 16:44     ` [PATCH v3 1/3] port: add kni interface support Dumitrescu, Cristian
2016-06-21 11:10       ` Ethan
2016-06-21 11:31         ` Dumitrescu, Cristian
2016-06-21 10:55   ` [PATCH v4 1/4] port: " Ethan Zhuang
2016-06-21 10:55     ` [PATCH v4 2/4] examples/ip_pipeline: " Ethan Zhuang
2016-06-21 10:55     ` [PATCH v4 3/4] examples/ip_pipeline: kni example configuration Ethan Zhuang
2016-06-21 10:55     ` [PATCH v4 4/4] doc: kni port support in the packet framework Ethan Zhuang
2016-06-21 12:03     ` [PATCH v4 1/4] port: kni interface support Dumitrescu, Cristian
2016-06-21 16:09       ` Thomas Monjalon

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.