From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <cristian.dumitrescu@intel.com>
Received: from mga09.intel.com (mga09.intel.com [134.134.136.24])
 by dpdk.org (Postfix) with ESMTP id 500F92BC3
 for <dev@dpdk.org>; Mon, 13 Jun 2016 12:25:19 +0200 (CEST)
Received: from fmsmga003.fm.intel.com ([10.253.24.29])
 by orsmga102.jf.intel.com with ESMTP; 13 Jun 2016 03:25:18 -0700
X-ExtLoop1: 1
X-IronPort-AV: E=Sophos;i="5.26,466,1459839600"; d="scan'208";a="718196089"
Received: from irsmsx102.ger.corp.intel.com ([163.33.3.155])
 by FMSMGA003.fm.intel.com with ESMTP; 13 Jun 2016 03:25:17 -0700
Received: from irsmsx108.ger.corp.intel.com ([169.254.11.183]) by
 IRSMSX102.ger.corp.intel.com ([169.254.2.10]) with mapi id 14.03.0248.002;
 Mon, 13 Jun 2016 11:25:16 +0100
From: "Dumitrescu, Cristian" <cristian.dumitrescu@intel.com>
To: WeiJie Zhuang <zhuangwj@gmail.com>
CC: "dev@dpdk.org" <dev@dpdk.org>, "Singh, Jasvinder"
 <jasvinder.singh@intel.com>, "Yigit, Ferruh" <ferruh.yigit@intel.com>
Thread-Topic: [PATCH] port: add kni interface support
Thread-Index: AQHRuNPzqUvoK3y1DESiX5+hyUDcX5/hyG9ggAV8CeA=
Date: Mon, 13 Jun 2016 10:25:15 +0000
Message-ID: <3EB4FA525960D640B5BDFFD6A3D8912647A02427@IRSMSX108.ger.corp.intel.com>
References: <1464325653-6912-1-git-send-email-zhuangwj@gmail.com>
 <1464434757-10860-1-git-send-email-zhuangwj@gmail.com>
 <3EB4FA525960D640B5BDFFD6A3D8912647A00F57@IRSMSX108.ger.corp.intel.com>
In-Reply-To: <3EB4FA525960D640B5BDFFD6A3D8912647A00F57@IRSMSX108.ger.corp.intel.com>
Accept-Language: en-US
Content-Language: en-US
X-MS-Has-Attach: 
X-MS-TNEF-Correlator: 
x-titus-metadata-40: eyJDYXRlZ29yeUxhYmVscyI6IiIsIk1ldGFkYXRhIjp7Im5zIjoiaHR0cDpcL1wvd3d3LnRpdHVzLmNvbVwvbnNcL0ludGVsMyIsImlkIjoiYmU1MWVmNjMtZjFmZC00MjdjLTk4ZDEtYzM0NDZiOTg5ZGU5IiwicHJvcHMiOlt7Im4iOiJDVFBDbGFzc2lmaWNhdGlvbiIsInZhbHMiOlt7InZhbHVlIjoiQ1RQX0lDIn1dfV19LCJTdWJqZWN0TGFiZWxzIjpbXSwiVE1DVmVyc2lvbiI6IjE1LjkuNi42IiwiVHJ1c3RlZExhYmVsSGFzaCI6ImNNbW9SUld3TXNHaEVDNWV2eW5JUnNzXC9aczJlZGNqZ2hsZVZxZ3dzMWJBPSJ9
x-ctpclassification: CTP_IC
x-originating-ip: [163.33.239.182]
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: quoted-printable
MIME-Version: 1.0
Subject: Re: [dpdk-dev] [PATCH] port: add kni interface support
X-BeenThere: dev@dpdk.org
X-Mailman-Version: 2.1.15
Precedence: list
List-Id: patches and discussions about DPDK <dev.dpdk.org>
List-Unsubscribe: <http://dpdk.org/ml/options/dev>,
 <mailto:dev-request@dpdk.org?subject=unsubscribe>
List-Archive: <http://dpdk.org/ml/archives/dev/>
List-Post: <mailto:dev@dpdk.org>
List-Help: <mailto:dev-request@dpdk.org?subject=help>
List-Subscribe: <http://dpdk.org/ml/listinfo/dev>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
X-List-Received-Date: Mon, 13 Jun 2016 10:25:20 -0000

Hi Ethan,

In the process of testing your patch, I actually had to do the rebase on th=
e 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 l=
atest code (see below), therefore I think it makes sense to send you this c=
ode for quick review rather than have you spend time doing the same work. I=
t would also save some iterations on the email list and speed up the integr=
ation of this patch.

Please review this code and let us know as soon as possible if you are OK w=
ith us sending it as the next version of this patch keeping your initial si=
gnoff 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 ap=
p_pktq_kni_params::force_bind)
4. The KNI requests handled by master pipeline rather than on data path (pi=
peline_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 ker=
nel 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>
=20
 #include <rte_ethdev.h>
+#include <rte_kni.h>
=20
 #include "cpu_core_map.h"
 #include "pipeline.h"
@@ -158,6 +159,20 @@ struct app_pktq_tm_params {
 	uint32_t burst_write;
 };
=20
+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,
 };
=20
@@ -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,
 };
=20
@@ -420,6 +437,8 @@ struct app_eal_params {
=20
 #define APP_MAX_PKTQ_TM                          APP_MAX_LINKS
=20
+#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,
 }
=20
 static inline uint32_t
+app_kni_get_readers(struct app_params *app, struct app_pktq_kni_params *kn=
i)
+{
+	uint32_t pos =3D kni - app->kni_params;
+	uint32_t n_pipelines =3D RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers =3D 0, i;
+
+	for (i =3D 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p =3D &app->pipeline_params[i];
+		uint32_t n_pktq_in =3D RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j =3D 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq =3D &p->pktq_in[j];
+
+			if ((pktq->type =3D=3D APP_PKTQ_IN_KNI) &&
+				(pktq->id =3D=3D 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 =3D NULL;
+	uint32_t pos =3D kni - app->kni_params;
+	uint32_t n_pipelines =3D RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers =3D 0, id =3D 0, i;
+
+	for (i =3D 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p =3D &app->pipeline_params[i];
+		uint32_t n_pktq_in =3D RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j =3D 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq =3D &p->pktq_in[j];
+
+			if ((pktq->type =3D=3D APP_PKTQ_IN_KNI) &&
+				(pktq->id =3D=3D pos)) {
+				n_readers++;
+				reader =3D p;
+				id =3D j;
+			}
+		}
+	}
+
+	if (n_readers !=3D 1)
+		return NULL;
+
+	*pktq_in_id =3D 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,
 }
=20
 static inline uint32_t
+app_kni_get_writers(struct app_params *app, struct app_pktq_kni_params *kn=
i)
+{
+	uint32_t pos =3D kni - app->kni_params;
+	uint32_t n_pipelines =3D RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers =3D 0, i;
+
+	for (i =3D 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p =3D &app->pipeline_params[i];
+		uint32_t n_pktq_out =3D RTE_MIN(p->n_pktq_out,
+			RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j =3D 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq =3D &p->pktq_out[j];
+
+			if ((pktq->type =3D=3D APP_PKTQ_OUT_KNI) &&
+				(pktq->id =3D=3D 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 =3D NULL;
+	uint32_t pos =3D kni - app->kni_params;
+	uint32_t n_pipelines =3D RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers =3D 0, id =3D 0, i;
+
+	for (i =3D 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p =3D &app->pipeline_params[i];
+		uint32_t n_pktq_out =3D RTE_MIN(p->n_pktq_out,
+			RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j =3D 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq =3D &p->pktq_out[j];
+
+			if ((pktq->type =3D=3D APP_PKTQ_OUT_KNI) &&
+				(pktq->id =3D=3D pos))
+				n_writers++;
+				writer =3D p;
+				id =3D j;
+		}
+	}
+
+	if (n_writers !=3D 1)
+		return NULL;
+
+	*pktq_out_id =3D id;
+	return writer;
+}
+
+static inline uint32_t
 app_sink_get_writers(struct app_params *app, struct app_pktq_sink_params *=
sink)
 {
 	uint32_t pos =3D sink - app->sink_params;
@@ -1051,6 +1194,22 @@ app_get_link_for_tm(struct app_params *app, struct a=
pp_pktq_tm_params *p_tm)
 	return &app->link_params[link_param_idx];
 }
=20
+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 =3D APP_PARAM_FIND(app->link_params, link_name);
+	APP_CHECK((link_param_idx >=3D 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/con=
fig/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.
+
+;             ______________          ______________________=20
+;            |              |  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 =3D 0
+
+[PIPELINE0]
+type =3D MASTER
+core =3D 0
+
+[PIPELINE1]
+type =3D PASS-THROUGH
+core =3D 1
+pktq_in =3D RXQ0.0 KNI1 RXQ1.0 KNI0
+pktq_out =3D KNI0 TXQ1.0 KNI1 TXQ0.0
diff --git a/examples/ip_pipeline/config_check.c b/examples/ip_pipeline/con=
fig_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)
 }
=20
 static void
+check_knis(struct app_params *app) {
+	uint32_t i;
+
+	for (i =3D 0; i < app->n_pktq_kni; i++) {
+		struct app_pktq_kni_params *p =3D &app->kni_params[i];
+		uint32_t n_readers =3D app_kni_get_readers(app, p);
+		uint32_t n_writers =3D app_kni_get_writers(app, p);
+
+		APP_CHECK((n_readers !=3D 0),
+			"%s has no reader\n", p->name);
+
+		APP_CHECK((n_readers =3D=3D 1),
+			"%s has more than one reader\n", p->name);
+
+		APP_CHECK((n_writers !=3D 0),
+			"%s has no writer\n", p->name);
+
+		APP_CHECK((n_writers =3D=3D 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/con=
fig_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 =3D {
 	.burst_write =3D 32,
 };
=20
+struct app_pktq_kni_params default_kni_params =3D {
+	.parsed =3D 0,
+	.socket_id =3D 0,
+	.core_id =3D 0,
+	.hyper_th_id =3D 0,
+	.force_bind =3D 0,
+
+	.mempool_id =3D 0,
+	.burst_read =3D 32,
+	.burst_write =3D 32,
+};
+
 struct app_pktq_source_params default_source_params =3D {
 	.parsed =3D 0,
 	.mempool_id =3D 0,
@@ -300,6 +312,18 @@ app_print_usage(char *prgname)
 	link_param_pos;							\
 })
=20
+#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 =3D APP_PARAM_ADD((app)->link_params, link_name);	\
+	link_param_pos;							\
+})
+
 #define PARSE_CHECK_DUPLICATE_SECTION(obj)				\
 do {									\
 	APP_CHECK(((obj)->parsed =3D=3D 0),					\
@@ -826,6 +850,10 @@ parse_pipeline_pktq_in(struct app_params *app,
 			type =3D APP_PKTQ_IN_TM;
 			id =3D APP_PARAM_ADD(app->tm_params, name);
 			APP_PARAM_ADD_LINK_FOR_TM(app, name);
+		} else if (validate_name(name, "KNI", 1) =3D=3D 0) {
+			type =3D APP_PKTQ_IN_KNI;
+			id =3D APP_PARAM_ADD(app->kni_params, name);
+			APP_PARAM_ADD_LINK_FOR_KNI(app, name);
 		} else if (validate_name(name, "SOURCE", 1) =3D=3D 0) {
 			type =3D APP_PKTQ_IN_SOURCE;
 			id =3D APP_PARAM_ADD(app->source_params, name);
@@ -871,6 +899,10 @@ parse_pipeline_pktq_out(struct app_params *app,
 			type =3D APP_PKTQ_OUT_TM;
 			id =3D APP_PARAM_ADD(app->tm_params, name);
 			APP_PARAM_ADD_LINK_FOR_TM(app, name);
+		} else if (validate_name(name, "KNI", 1) =3D=3D 0) {
+			type =3D APP_PKTQ_OUT_KNI;
+			id =3D APP_PARAM_ADD(app->kni_params, name);
+			APP_PARAM_ADD_LINK_FOR_KNI(app, name);
 		} else if (validate_name(name, "SINK", 1) =3D=3D 0) {
 			type =3D APP_PKTQ_OUT_SINK;
 			id =3D APP_PARAM_ADD(app->sink_params, name);
@@ -1816,7 +1848,7 @@ parse_tm(struct app_params *app,
 	param =3D &app->tm_params[param_idx];
 	PARSE_CHECK_DUPLICATE_SECTION(param);
=20
-	APP_PARAM_ADD_LINK_FOR_TXQ(app, section_name);
+	APP_PARAM_ADD_LINK_FOR_TM(app, section_name);
=20
 	for (i =3D 0; i < n_entries; i++) {
 		struct rte_cfgfile_entry *ent =3D &entries[i];
@@ -1853,6 +1885,87 @@ parse_tm(struct app_params *app,
 }
=20
 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 =3D rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries =3D malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries !=3D NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx =3D APP_PARAM_ADD(app->kni_params, section_name);
+	param =3D &app->kni_params[param_idx];
+	PARSE_CHECK_DUPLICATE_SECTION(param);
+
+	APP_PARAM_ADD_LINK_FOR_KNI(app, section_name);
+
+	for (i =3D 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent =3D &entries[i];
+
+		if (strcmp(ent->name, "core") =3D=3D 0) {
+			int status =3D parse_pipeline_core(
+				&param->socket_id,
+				&param->core_id,
+				&param->hyper_th_id,
+				ent->value);
+
+			param->force_bind =3D 1;
+
+			PARSE_ERROR((status =3D=3D 0), section_name,
+				ent->name);
+			continue;
+		}
+
+		if (strcmp(ent->name, "mempool") =3D=3D 0) {
+			int status =3D validate_name(ent->value,
+				"MEMPOOL", 1);
+			ssize_t idx;
+
+			PARSE_ERROR((status =3D=3D 0), section_name,
+				ent->name);
+
+			idx =3D APP_PARAM_ADD(app->mempool_params, ent->value);
+			param->mempool_id =3D idx;
+			continue;
+		}
+
+		if (strcmp(ent->name, "burst_read") =3D=3D 0) {
+			int status =3D parser_read_uint32(&param->burst_read,
+				ent->value);
+
+			PARSE_ERROR((status =3D=3D 0), section_name,
+				ent->name);
+			continue;
+		}
+
+		if (strcmp(ent->name, "burst_write") =3D=3D 0) {
+			int status =3D parser_read_uint32(&param->burst_write,
+				ent->value);
+
+			PARSE_ERROR((status =3D=3D 0), section_name,
+				ent->name);
+			continue;
+		}
+
+		/* unrecognized */
+		PARSE_ERROR_INVALID(0, section_name, ent->name);
+	}
+
+	param->parsed =3D 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[] =
=3D {
 	{"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)
 }
=20
 static void
+save_kni_params(struct app_params *app, FILE *f)
+{
+	struct app_pktq_kni_params *p;
+	size_t i, count;
+
+	count =3D RTE_DIM(app->kni_params);
+	for (i =3D 0; i < count; i++) {
+		p =3D &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 =3D 1\n");
+			fprintf(f, "core =3D s%" PRIu32 "c%" PRIu32 "%s\n",
+				p->socket_id,
+				p->core_id,
+				(p->hyper_th_id) ? "h" : "");
+		} else
+			fprintf(f, "; force_bind =3D 0\n");
+
+		/* mempool */
+		fprintf(f, "%s =3D %s\n",
+			"mempool",
+			app->mempool_params[p->mempool_id].name);
+
+		/* burst_read */
+		fprintf(f, "%s =3D %" PRIu32 "\n", "burst_read", p->burst_read);
+
+		/* burst_write */
+		fprintf(f, "%s =3D %" 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 =3D app->tm_params[pp->id].name;
 					break;
+				case APP_PKTQ_IN_KNI:
+					name =3D app->kni_params[pp->id].name;
+					break;
 				case APP_PKTQ_IN_SOURCE:
 					name =3D 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 =3D app->tm_params[pp->id].name;
 					break;
+				case APP_PKTQ_OUT_KNI:
+					name =3D app->kni_params[pp->id].name;
+					break;
 				case APP_PKTQ_OUT_SINK:
 					name =3D app->sink_params[pp->id].name;
 					break;
@@ -2872,6 +3033,7 @@ app_config_save(struct app_params *app, const char *f=
ile_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));
=20
+	for (i =3D 0; i < RTE_DIM(app->kni_params); i++)
+		memcpy(&app->kni_params[i],
+			&default_kni_params,
+			sizeof(default_kni_params));
+
 	for (i =3D 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)
 	}
 }
=20
+static int
+kni_change_mtu(uint8_t port_id, unsigned new_mtu) {
+	int ret;
+
+	if (port_id >=3D 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 =3D rte_eth_dev_set_mtu(port_id, new_mtu);
+	if (ret < 0)
+		return ret;
+
+	/* Restart specific port */
+	ret =3D 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 =3D 0;
+
+	if (port_id >=3D rte_eth_dev_count())
+		return -EINVAL;
+
+	if (if_up) {
+		rte_eth_dev_stop(port_id);
+		ret =3D 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 =3D=3D 0)
+		return;
+
+	rte_kni_init(app->n_pktq_kni);
+
+	for (i =3D 0; i < app->n_pktq_kni; i++) {
+		struct app_pktq_kni_params *p_kni =3D &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 =3D 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 =3D &app->mempool_params[p_kni->mempool_id];
+		mempool =3D 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 =3D p_kni->force_bind;
+		if (conf.force_bind) {
+			int lcore_id;
+
+			lcore_id =3D 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 =3D (uint32_t) lcore_id;
+		}
+		conf.group_id =3D p_link->pmd_id;
+		conf.mbuf_size =3D mempool_params->buffer_size;
+		conf.addr =3D dev_info.pci_dev->addr;
+		conf.id =3D dev_info.pci_dev->id;
+
+		memset(&ops, 0, sizeof(ops));
+		ops.port_id =3D (uint8_t) p_link->pmd_id;
+		ops.change_mtu =3D kni_change_mtu;
+		ops.config_network_if =3D kni_config_network_interface;
+
+		APP_LOG(app, HIGH, "Initializing %s ...", p_kni->name);
+		app->kni[i] =3D 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 =3D app->tm[in->id];
 			out->burst_size =3D app->tm_params[in->id].burst_read;
 			break;
+		case APP_PKTQ_IN_KNI:
+			out->type =3D PIPELINE_PORT_IN_KNI_READER;
+			out->params.kni.kni =3D app->kni[in->id];
+			out->burst_size =3D app->kni_params[in->id].burst_read;
+			break;
 		case APP_PKTQ_IN_SOURCE:
 		{
 			uint32_t mempool_id =3D
@@ -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 =3D
 				&out->params.sched;
=20
@@ -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 =3D PIPELINE_PORT_OUT_KNI_WRITER;
+			out->params.kni.kni =3D app->kni[in->id];
+			out->params.kni.tx_burst_sz =3D app->kni_params[in->id].burst_write;
+			break;
 		case APP_PKTQ_OUT_SINK:
 		{
 			out->type =3D 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);
=20
 	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;
 		}
=20
+		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 =3D &app->kni_params[pktq_out->id];
+			p =3D app_kni_get_reader(app, kni, &pktq_in_id);
+			if (p =3D=3D NULL)
+				return NULL;
+
+			ptype =3D app_pipeline_type_find(app, p->type);
+			if ((ptype =3D=3D NULL) || (ptype->fe_ops->f_track =3D=3D NULL))
+				return NULL;
+
+			app_pipeline_params_get(app, p, &pp);
+			status =3D 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 =3D (struct pipeline_master *) pipeline;
 	struct app_params *app =3D p->app;
+	uint32_t i;
 	int status;
=20
 	/* Application post-init phase */
@@ -144,6 +145,10 @@ pipeline_run(void *pipeline)
 		rte_exit(0, "Bye!\n");
 	}
=20
+	/* Handle KNI requests from Linux kernel */
+	for (i =3D 0; i < app->n_pktq_kni; i++)
+		rte_kni_handle_request(app->kni[i]);
+
 	return 0;
 }
=20
diff --git a/examples/ip_pipeline/pipeline_be.h b/examples/ip_pipeline/pipe=
line_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>
=20
 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,
 };
=20
@@ -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_p=
arams  *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,
 };
=20
@@ -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_o=
ut_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_o=
ut_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) +=3D rte_port_ras.c
 endif
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) +=3D rte_port_sched.c
 SRCS-$(CONFIG_RTE_LIBRTE_PORT) +=3D rte_port_source_sink.c
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+SRCS-$(CONFIG_RTE_LIBRTE_PORT) +=3D rte_port_kni.c
+endif
=20
 # install includes
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=3D rte_port.h
@@ -68,6 +71,9 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=3D rte_port_r=
as.h
 endif
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=3D rte_port_sched.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=3D rte_port_source_sink.h
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include +=3D rte_port_kni.h
+endif
=20
 # this lib depends upon:
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) :=3D lib/librte_eal
@@ -75,5 +81,8 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=3D lib/librte_mbuf
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=3D lib/librte_mempool
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=3D lib/librte_ether
 DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=3D lib/librte_ip_frag
+ifeq ($(CONFIG_RTE_LIBRTE_KNI),y)
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) +=3D lib/librte_kni
+endif
=20
 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 +=3D val
+#define RTE_PORT_KNI_READER_STATS_PKTS_DROP_ADD(port, val) \
+	port->stats.n_pkts_drop +=3D 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 =3D
+			(struct rte_port_kni_reader_params *) params;
+	struct rte_port_kni_reader *port;
+
+	/* Check input parameters */
+	if (conf =3D=3D NULL) {
+		RTE_LOG(ERR, PORT, "%s: params is NULL\n", __func__);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	port =3D rte_zmalloc_socket("PORT", sizeof(*port),
+		RTE_CACHE_LINE_SIZE, socket_id);
+	if (port =3D=3D NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni =3D 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 =3D
+			(struct rte_port_kni_reader *) port;
+	uint16_t rx_pkt_cnt;
+
+	rx_pkt_cnt =3D 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 =3D=3D 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 =3D
+			(struct rte_port_kni_reader *) port;
+
+	if (stats !=3D 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 +=3D val
+#define RTE_PORT_KNI_WRITER_STATS_PKTS_DROP_ADD(port, val) \
+	port->stats.n_pkts_drop +=3D 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 =3D
+			(struct rte_port_kni_writer_params *) params;
+	struct rte_port_kni_writer *port;
+
+	/* Check input parameters */
+	if ((conf =3D=3D NULL) ||
+		(conf->tx_burst_sz =3D=3D 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 =3D rte_zmalloc_socket("PORT", sizeof(*port),
+		RTE_CACHE_LINE_SIZE, socket_id);
+	if (port =3D=3D NULL) {
+		RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__);
+		return NULL;
+	}
+
+	/* Initialization */
+	port->kni =3D conf->kni;
+	port->tx_burst_sz =3D conf->tx_burst_sz;
+	port->tx_buf_count =3D 0;
+	port->bsz_mask =3D 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 =3D 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 =3D 0;
+}
+
+static int
+rte_port_kni_writer_tx(void *port, struct rte_mbuf *pkt) {
+	struct rte_port_kni_writer *p =3D
+		(struct rte_port_kni_writer *) port;
+
+	p->tx_buf[p->tx_buf_count++] =3D pkt;
+	RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+	if (p->tx_buf_count >=3D 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 =3D
+		(struct rte_port_kni_writer *) port;
+	uint64_t bsz_mask =3D p->bsz_mask;
+	uint32_t tx_buf_count =3D p->tx_buf_count;
+	uint64_t expr =3D (pkts_mask & (pkts_mask + 1)) |
+		((pkts_mask & bsz_mask) ^ bsz_mask);
+
+	if (expr =3D=3D 0) {
+		uint64_t n_pkts =3D __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 =3D 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 =3D pkts[n_pkts_ok];
+
+			rte_pktmbuf_free(pkt);
+		}
+	} else {
+		for ( ; pkts_mask; ) {
+			uint32_t pkt_index =3D __builtin_ctzll(pkts_mask);
+			uint64_t pkt_mask =3D 1LLU << pkt_index;
+			struct rte_mbuf *pkt =3D pkts[pkt_index];
+
+			p->tx_buf[tx_buf_count++] =3D pkt;
+			RTE_PORT_KNI_WRITER_STATS_PKTS_IN_ADD(p, 1);
+			pkts_mask &=3D ~pkt_mask;
+		}
+
+		p->tx_buf_count =3D tx_buf_count;
+		if (tx_buf_count >=3D p->tx_burst_sz)
+			send_burst(p);
+	}
+
+	return 0;
+}
+
+static int
+rte_port_kni_writer_flush(void *port)
+{
+	struct rte_port_kni_writer *p =3D
+		(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 =3D=3D 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 =3D
+		(struct rte_port_kni_writer *) port;
+
+	if (stats !=3D 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 =3D {
+	.f_create =3D rte_port_kni_reader_create,
+	.f_free =3D rte_port_kni_reader_free,
+	.f_rx =3D rte_port_kni_reader_rx,
+	.f_stats =3D rte_port_kni_reader_stats_read,
+};
+
+struct rte_port_out_ops rte_port_kni_writer_ops =3D {
+	.f_create =3D rte_port_kni_writer_create,
+	.f_free =3D rte_port_kni_writer_free,
+	.f_tx =3D rte_port_kni_writer_tx,
+	.f_tx_bulk =3D rte_port_kni_writer_tx_bulk,
+	.f_flush =3D rte_port_kni_writer_flush,
+	.f_stats =3D 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