[dpdk-dev,v2,1/2] app/testpmd: support the heavywight mode GRO

Message ID 1502960892-112960-2-git-send-email-jiayu.hu@intel.com (mailing list archive)
State Superseded, archived
Headers

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel-compilation fail Compilation issues

Commit Message

Hu, Jiayu Aug. 17, 2017, 9:08 a.m. UTC
  The GRO library provides two reassembly modes: lightweight mode and
heavyweight mode. This patch is to support the heavyweight mode in
csum forwarding engine.

With the command "set port <port_id> gro (heavymode|lightmode) (on|off)",
users can select the lightweight mode or the heavyweight mode to use. With
the command "set gro flush interval <num>", users can set the interval of
flushing GROed packets from reassembly tables for the heavyweight mode.
With the command "show port <port_id> gro", users can display GRO
configuration.

Signed-off-by: Jiayu Hu <jiayu.hu@intel.com>
---
 app/test-pmd/cmdline.c  | 181 ++++++++++++++++++++++++++++++++++++++++++------
 app/test-pmd/config.c   |  72 +++++++++++++++----
 app/test-pmd/csumonly.c |  29 ++++++--
 app/test-pmd/testpmd.c  |  18 ++++-
 app/test-pmd/testpmd.h  |  13 +++-
 5 files changed, 270 insertions(+), 43 deletions(-)
  

Comments

Ferruh Yigit Aug. 21, 2017, 11:03 a.m. UTC | #1
On 8/17/2017 10:08 AM, Jiayu Hu wrote:
> The GRO library provides two reassembly modes: lightweight mode and
> heavyweight mode. This patch is to support the heavyweight mode in
> csum forwarding engine.
> 
> With the command "set port <port_id> gro (heavymode|lightmode) (on|off)",
> users can select the lightweight mode or the heavyweight mode to use. With
> the command "set gro flush interval <num>", users can set the interval of
> flushing GROed packets from reassembly tables for the heavyweight mode.
> With the command "show port <port_id> gro", users can display GRO
> configuration.
> 
> Signed-off-by: Jiayu Hu <jiayu.hu@intel.com>

<...>

>  	lcoreid_t  cpuid_idx;    /**< index of logical core in CPU id table */
> @@ -434,13 +436,21 @@ extern struct ether_addr peer_eth_addrs[RTE_MAX_ETHPORTS];
>  extern uint32_t burst_tx_delay_time; /**< Burst tx delay time(us) for mac-retry. */
>  extern uint32_t burst_tx_retry_num;  /**< Burst tx retry number for mac-retry. */
>  
> +#define GRO_HEAVYMODE 0x1
> +#define GRO_LIGHTMODE 0x2

Why these are not part of the gro library?
Is the concept "lightweight mode and heavyweight mode" part of gro
library or implemented only in testpmd?

> +
>  #define GRO_DEFAULT_FLOW_NUM 4
>  #define GRO_DEFAULT_ITEM_NUM_PER_FLOW DEF_PKT_BURST
> +
> +#define GRO_DEFAULT_FLUSH_INTERVAL 2
> +#define GRO_MAX_FLUSH_INTERVAL 4
> +
>  struct gro_status {
>  	struct rte_gro_param param;
>  	uint8_t enable;
>  };
>  extern struct gro_status gro_ports[RTE_MAX_ETHPORTS];
> +extern uint32_t gro_flush_interval;

<...>
  
Ferruh Yigit Aug. 21, 2017, 11:16 a.m. UTC | #2
On 8/17/2017 10:08 AM, Jiayu Hu wrote:
> The GRO library provides two reassembly modes: lightweight mode and
> heavyweight mode. This patch is to support the heavyweight mode in
> csum forwarding engine.
> 
> With the command "set port <port_id> gro (heavymode|lightmode) (on|off)",
> users can select the lightweight mode or the heavyweight mode to use. With
> the command "set gro flush interval <num>", users can set the interval of
> flushing GROed packets from reassembly tables for the heavyweight mode.
> With the command "show port <port_id> gro", users can display GRO
> configuration.
> 
> Signed-off-by: Jiayu Hu <jiayu.hu@intel.com>

<...>

> +	/* create a gro context for each lcore */
> +	gro_param.gro_types = RTE_GRO_TCP_IPV4;
> +	gro_param.max_flow_num = GRO_MAX_FLUSH_INTERVAL;
> +	gro_param.max_item_per_flow = MAX_PKT_BURST;
> +	for (lc_id = 0; lc_id < nb_lcores; lc_id++) {
> +		gro_param.socket_id = rte_lcore_to_socket_id(
> +				fwd_lcores_cpuids[lc_id]);
> +		fwd_lcores[lc_id]->gro_ctx = rte_gro_ctx_create(&gro_param);

The gro library .map file has wrong function name "rte_gro_ctrl_create"
instead of "rte_gro_ctx_create". So this is giving a build error with
shared library config.

Both "rte_gro_ctrl_create" and "rte_gro_ctrl_destroy" need to be fixed.

> +		if (fwd_lcores[lc_id]->gro_ctx == NULL) {
> +			rte_exit(EXIT_FAILURE,
> +					"rte_gro_ctx_create() failed\n");
> +		}
> +	}
>  }

<...>
  
Hu, Jiayu Aug. 22, 2017, 12:53 a.m. UTC | #3
Hi,

> -----Original Message-----

> From: Yigit, Ferruh

> Sent: Monday, August 21, 2017 7:16 PM

> To: Hu, Jiayu <jiayu.hu@intel.com>; dev@dpdk.org

> Cc: Ananyev, Konstantin <konstantin.ananyev@intel.com>; Tan, Jianfeng

> <jianfeng.tan@intel.com>; thomas@monjalon.net; Wu, Jingjing

> <jingjing.wu@intel.com>; Yao, Lei A <lei.a.yao@intel.com>

> Subject: Re: [PATCH v2 1/2] app/testpmd: support the heavywight mode GRO

> 

> On 8/17/2017 10:08 AM, Jiayu Hu wrote:

> > The GRO library provides two reassembly modes: lightweight mode and

> > heavyweight mode. This patch is to support the heavyweight mode in

> > csum forwarding engine.

> >

> > With the command "set port <port_id> gro (heavymode|lightmode)

> (on|off)",

> > users can select the lightweight mode or the heavyweight mode to use.

> With

> > the command "set gro flush interval <num>", users can set the interval of

> > flushing GROed packets from reassembly tables for the heavyweight mode.

> > With the command "show port <port_id> gro", users can display GRO

> > configuration.

> >

> > Signed-off-by: Jiayu Hu <jiayu.hu@intel.com>

> 

> <...>

> 

> > +	/* create a gro context for each lcore */

> > +	gro_param.gro_types = RTE_GRO_TCP_IPV4;

> > +	gro_param.max_flow_num = GRO_MAX_FLUSH_INTERVAL;

> > +	gro_param.max_item_per_flow = MAX_PKT_BURST;

> > +	for (lc_id = 0; lc_id < nb_lcores; lc_id++) {

> > +		gro_param.socket_id = rte_lcore_to_socket_id(

> > +				fwd_lcores_cpuids[lc_id]);

> > +		fwd_lcores[lc_id]->gro_ctx =

> rte_gro_ctx_create(&gro_param);

> 

> The gro library .map file has wrong function name "rte_gro_ctrl_create"

> instead of "rte_gro_ctx_create". So this is giving a build error with

> shared library config.

> 

> Both "rte_gro_ctrl_create" and "rte_gro_ctrl_destroy" need to be fixed.


Thanks a lot. I will send a patch to fix them.

BRs,
Jiayu

> 

> > +		if (fwd_lcores[lc_id]->gro_ctx == NULL) {

> > +			rte_exit(EXIT_FAILURE,

> > +					"rte_gro_ctx_create() failed\n");

> > +		}

> > +	}

> >  }

> 

> <...>
  
Hu, Jiayu Aug. 22, 2017, 1 a.m. UTC | #4
Hi,
> -----Original Message-----

> From: Yigit, Ferruh

> Sent: Monday, August 21, 2017 7:04 PM

> To: Hu, Jiayu <jiayu.hu@intel.com>; dev@dpdk.org

> Cc: Ananyev, Konstantin <konstantin.ananyev@intel.com>; Tan, Jianfeng

> <jianfeng.tan@intel.com>; thomas@monjalon.net; Wu, Jingjing

> <jingjing.wu@intel.com>; Yao, Lei A <lei.a.yao@intel.com>

> Subject: Re: [PATCH v2 1/2] app/testpmd: support the heavywight mode GRO

> 

> On 8/17/2017 10:08 AM, Jiayu Hu wrote:

> > The GRO library provides two reassembly modes: lightweight mode and

> > heavyweight mode. This patch is to support the heavyweight mode in

> > csum forwarding engine.

> >

> > With the command "set port <port_id> gro (heavymode|lightmode)

> (on|off)",

> > users can select the lightweight mode or the heavyweight mode to use.

> With

> > the command "set gro flush interval <num>", users can set the interval of

> > flushing GROed packets from reassembly tables for the heavyweight mode.

> > With the command "show port <port_id> gro", users can display GRO

> > configuration.

> >

> > Signed-off-by: Jiayu Hu <jiayu.hu@intel.com>

> 

> <...>

> 

> >  	lcoreid_t  cpuid_idx;    /**< index of logical core in CPU id table */

> > @@ -434,13 +436,21 @@ extern struct ether_addr

> peer_eth_addrs[RTE_MAX_ETHPORTS];

> >  extern uint32_t burst_tx_delay_time; /**< Burst tx delay time(us) for mac-

> retry. */

> >  extern uint32_t burst_tx_retry_num;  /**< Burst tx retry number for mac-

> retry. */

> >

> > +#define GRO_HEAVYMODE 0x1

> > +#define GRO_LIGHTMODE 0x2

> 

> Why these are not part of the gro library?

> Is the concept "lightweight mode and heavyweight mode" part of gro

> library or implemented only in testpmd?


Lightweight mode and heavyweight mode are two reassembly methods we
provided in the GRO library. For applications, they are just two kinds of APIs.
Applications can select any of them to merge packets.

In testpmd, we want to show how to use these two reassembly modes, so
I define two macros to present them. Users can select which one to use via
command line.

> 

> > +

> >  #define GRO_DEFAULT_FLOW_NUM 4

> >  #define GRO_DEFAULT_ITEM_NUM_PER_FLOW DEF_PKT_BURST

> > +

> > +#define GRO_DEFAULT_FLUSH_INTERVAL 2

> > +#define GRO_MAX_FLUSH_INTERVAL 4

> > +

> >  struct gro_status {

> >  	struct rte_gro_param param;

> >  	uint8_t enable;

> >  };

> >  extern struct gro_status gro_ports[RTE_MAX_ETHPORTS];

> > +extern uint32_t gro_flush_interval;

> 

> <...>
  
Ferruh Yigit Aug. 22, 2017, 8:30 a.m. UTC | #5
On 8/22/2017 2:00 AM, Hu, Jiayu wrote:
> Hi,
>> -----Original Message-----
>> From: Yigit, Ferruh
>> Sent: Monday, August 21, 2017 7:04 PM
>> To: Hu, Jiayu <jiayu.hu@intel.com>; dev@dpdk.org
>> Cc: Ananyev, Konstantin <konstantin.ananyev@intel.com>; Tan, Jianfeng
>> <jianfeng.tan@intel.com>; thomas@monjalon.net; Wu, Jingjing
>> <jingjing.wu@intel.com>; Yao, Lei A <lei.a.yao@intel.com>
>> Subject: Re: [PATCH v2 1/2] app/testpmd: support the heavywight mode GRO
>>
>> On 8/17/2017 10:08 AM, Jiayu Hu wrote:
>>> The GRO library provides two reassembly modes: lightweight mode and
>>> heavyweight mode. This patch is to support the heavyweight mode in
>>> csum forwarding engine.
>>>
>>> With the command "set port <port_id> gro (heavymode|lightmode)
>> (on|off)",
>>> users can select the lightweight mode or the heavyweight mode to use.
>> With
>>> the command "set gro flush interval <num>", users can set the interval of
>>> flushing GROed packets from reassembly tables for the heavyweight mode.
>>> With the command "show port <port_id> gro", users can display GRO
>>> configuration.
>>>
>>> Signed-off-by: Jiayu Hu <jiayu.hu@intel.com>
>>
>> <...>
>>
>>>  	lcoreid_t  cpuid_idx;    /**< index of logical core in CPU id table */
>>> @@ -434,13 +436,21 @@ extern struct ether_addr
>> peer_eth_addrs[RTE_MAX_ETHPORTS];
>>>  extern uint32_t burst_tx_delay_time; /**< Burst tx delay time(us) for mac-
>> retry. */
>>>  extern uint32_t burst_tx_retry_num;  /**< Burst tx retry number for mac-
>> retry. */
>>>
>>> +#define GRO_HEAVYMODE 0x1
>>> +#define GRO_LIGHTMODE 0x2
>>
>> Why these are not part of the gro library?
>> Is the concept "lightweight mode and heavyweight mode" part of gro
>> library or implemented only in testpmd?
> 
> Lightweight mode and heavyweight mode are two reassembly methods we
> provided in the GRO library. For applications, they are just two kinds of APIs.
> Applications can select any of them to merge packets.

GRO modes are defined in testpmd, and kept in testpmd variables, library
seems not aware of these modes.

What are these two APIs, rte_gro_reassemble() and
rte_gro_reassemble_burst() ?
Perhaps you can detail what Lightweight mode and heavyweight mode are,
doc also don't have much about it.

This still looks like gro library provides common API and testpmd calls
these API with different parameters and calls these lightweight and
heavyweight, if these modes are common use case, I believe they should
be part of library. If not, instead of saying different gro modes, it
can be presented as different gro usage samples in testpmd.

testpmd good for testing dpdk, and good for providing usage sample for
APIs, but I believe it shouldn't have the concepts coded in it,
libraries should have it, that is what end user uses.

> 
> In testpmd, we want to show how to use these two reassembly modes, so
> I define two macros to present them. Users can select which one to use via
> command line.
> 
>>
>>> +
>>>  #define GRO_DEFAULT_FLOW_NUM 4
>>>  #define GRO_DEFAULT_ITEM_NUM_PER_FLOW DEF_PKT_BURST
>>> +
>>> +#define GRO_DEFAULT_FLUSH_INTERVAL 2
>>> +#define GRO_MAX_FLUSH_INTERVAL 4
>>> +
>>>  struct gro_status {
>>>  	struct rte_gro_param param;
>>>  	uint8_t enable;
>>>  };
>>>  extern struct gro_status gro_ports[RTE_MAX_ETHPORTS];
>>> +extern uint32_t gro_flush_interval;
>>
>> <...>
  
Hu, Jiayu Aug. 22, 2017, 2:27 p.m. UTC | #6
Hi Ferruh,

On Tue, Aug 22, 2017 at 09:30:32AM +0100, Ferruh Yigit wrote:
> On 8/22/2017 2:00 AM, Hu, Jiayu wrote:
> > Hi,
> >> -----Original Message-----
> >> From: Yigit, Ferruh
> >> Sent: Monday, August 21, 2017 7:04 PM
> >> To: Hu, Jiayu <jiayu.hu@intel.com>; dev@dpdk.org
> >> Cc: Ananyev, Konstantin <konstantin.ananyev@intel.com>; Tan, Jianfeng
> >> <jianfeng.tan@intel.com>; thomas@monjalon.net; Wu, Jingjing
> >> <jingjing.wu@intel.com>; Yao, Lei A <lei.a.yao@intel.com>
> >> Subject: Re: [PATCH v2 1/2] app/testpmd: support the heavywight mode GRO
> >>
> >> On 8/17/2017 10:08 AM, Jiayu Hu wrote:
> >>> The GRO library provides two reassembly modes: lightweight mode and
> >>> heavyweight mode. This patch is to support the heavyweight mode in
> >>> csum forwarding engine.
> >>>
> >>> With the command "set port <port_id> gro (heavymode|lightmode)
> >> (on|off)",
> >>> users can select the lightweight mode or the heavyweight mode to use.
> >> With
> >>> the command "set gro flush interval <num>", users can set the interval of
> >>> flushing GROed packets from reassembly tables for the heavyweight mode.
> >>> With the command "show port <port_id> gro", users can display GRO
> >>> configuration.
> >>>
> >>> Signed-off-by: Jiayu Hu <jiayu.hu@intel.com>
> >>
> >> <...>
> >>
> >>>  	lcoreid_t  cpuid_idx;    /**< index of logical core in CPU id table */
> >>> @@ -434,13 +436,21 @@ extern struct ether_addr
> >> peer_eth_addrs[RTE_MAX_ETHPORTS];
> >>>  extern uint32_t burst_tx_delay_time; /**< Burst tx delay time(us) for mac-
> >> retry. */
> >>>  extern uint32_t burst_tx_retry_num;  /**< Burst tx retry number for mac-
> >> retry. */
> >>>
> >>> +#define GRO_HEAVYMODE 0x1
> >>> +#define GRO_LIGHTMODE 0x2
> >>
> >> Why these are not part of the gro library?
> >> Is the concept "lightweight mode and heavyweight mode" part of gro
> >> library or implemented only in testpmd?
> > 
> > Lightweight mode and heavyweight mode are two reassembly methods we
> > provided in the GRO library. For applications, they are just two kinds of APIs.
> > Applications can select any of them to merge packets.
> 
> GRO modes are defined in testpmd, and kept in testpmd variables, library
> seems not aware of these modes.
> 
> What are these two APIs, rte_gro_reassemble() and
> rte_gro_reassemble_burst() ?
> Perhaps you can detail what Lightweight mode and heavyweight mode are,
> doc also don't have much about it.
> 
> This still looks like gro library provides common API and testpmd calls
> these API with different parameters and calls these lightweight and
> heavyweight, if these modes are common use case, I believe they should

Yes, the GRO API doesn't show the concept of 'heavyweight' and 'lightweight'.
This concept is only used to describe the supported reassembly modes in the
GRO library.

> be part of library. If not, instead of saying different gro modes, it
> can be presented as different gro usage samples in testpmd.

What does "different gro usage samples" mean? Different forwarding
engines?

> 
> testpmd good for testing dpdk, and good for providing usage sample for
> APIs, but I believe it shouldn't have the concepts coded in it,
> libraries should have it, that is what end user uses.
> 
> > 
> > In testpmd, we want to show how to use these two reassembly modes, so
> > I define two macros to present them. Users can select which one to use via
> > command line.
> > 
> >>
> >>> +
> >>>  #define GRO_DEFAULT_FLOW_NUM 4
> >>>  #define GRO_DEFAULT_ITEM_NUM_PER_FLOW DEF_PKT_BURST
> >>> +
> >>> +#define GRO_DEFAULT_FLUSH_INTERVAL 2
> >>> +#define GRO_MAX_FLUSH_INTERVAL 4
> >>> +
> >>>  struct gro_status {
> >>>  	struct rte_gro_param param;
> >>>  	uint8_t enable;
> >>>  };
> >>>  extern struct gro_status gro_ports[RTE_MAX_ETHPORTS];
> >>> +extern uint32_t gro_flush_interval;
> >>
> >> <...>
  

Patch

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index cd8c358..9876604 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -423,14 +423,22 @@  static void cmd_help_long_parsed(void *parsed_result,
 			"tso show (portid)"
 			"    Display the status of TCP Segmentation Offload.\n\n"
 
-			"gro (on|off) (port_id)"
+			"set port (port_id) gro (heavymode|lightmode) "
+			"(on|off)\n"
 			"    Enable or disable Generic Receive Offload in"
 			" csum forwarding engine.\n\n"
 
+			"show port (port_id) gro\n"
+			"    Display GRO configuration.\n\n"
+
 			"gro set (max_flow_num) (max_item_num_per_flow) (port_id)\n"
 			"    Set max flow number and max packet number per-flow"
 			" for GRO.\n\n"
 
+			"set gro flush interval (num)\n"
+			"    Set the interval of flushing GROed packets from"
+			" reassembly tables.\n\n"
+
 			"set fwd (%s)\n"
 			"    Set packet forwarding mode.\n\n"
 
@@ -3850,41 +3858,168 @@  cmdline_parse_inst_t cmd_tunnel_tso_show = {
 };
 
 /* *** SET GRO FOR A PORT *** */
-struct cmd_gro_result {
+struct cmd_gro_enable_result {
+	cmdline_fixed_string_t cmd_set;
+	cmdline_fixed_string_t cmd_port;
 	cmdline_fixed_string_t cmd_keyword;
-	cmdline_fixed_string_t mode;
-	uint8_t port_id;
+	cmdline_fixed_string_t cmd_mode;
+	cmdline_fixed_string_t cmd_onoff;
+	uint8_t cmd_pid;
+};
+
+static void
+cmd_gro_enable_parsed(void *parsed_result,
+		__attribute__((unused)) struct cmdline *cl,
+		__attribute__((unused)) void *data)
+{
+	struct cmd_gro_enable_result *res;
+
+	res = parsed_result;
+	setup_gro(res->cmd_mode, res->cmd_onoff, res->cmd_pid);
+}
+
+cmdline_parse_token_string_t cmd_gro_enable_set =
+	TOKEN_STRING_INITIALIZER(struct cmd_gro_enable_result,
+			cmd_set, "set");
+cmdline_parse_token_string_t cmd_gro_enable_port =
+	TOKEN_STRING_INITIALIZER(struct cmd_gro_enable_result,
+			cmd_keyword, "port");
+cmdline_parse_token_num_t cmd_gro_enable_pid =
+	TOKEN_NUM_INITIALIZER(struct cmd_gro_enable_result,
+			cmd_pid, UINT8);
+cmdline_parse_token_string_t cmd_gro_enable_keyword =
+	TOKEN_STRING_INITIALIZER(struct cmd_gro_enable_result,
+			cmd_keyword, "gro");
+cmdline_parse_token_string_t cmd_gro_enable_mode =
+	TOKEN_STRING_INITIALIZER(struct cmd_gro_enable_result,
+			cmd_mode, "heavymode#lightmode");
+cmdline_parse_token_string_t cmd_gro_enable_onoff =
+	TOKEN_STRING_INITIALIZER(struct cmd_gro_enable_result,
+			cmd_onoff, "on#off");
+
+cmdline_parse_inst_t cmd_gro_enable = {
+	.f = cmd_gro_enable_parsed,
+	.data = NULL,
+	.help_str = "set port <port_id> gro (heavymode|lightmode) (on|off)",
+	.tokens = {
+		(void *)&cmd_gro_enable_set,
+		(void *)&cmd_gro_enable_port,
+		(void *)&cmd_gro_enable_pid,
+		(void *)&cmd_gro_enable_keyword,
+		(void *)&cmd_gro_enable_mode,
+		(void *)&cmd_gro_enable_onoff,
+		NULL,
+	},
+};
+
+/* *** DISPLAY GRO CONFIGURATION *** */
+struct cmd_gro_show_result {
+	cmdline_fixed_string_t cmd_show;
+	cmdline_fixed_string_t cmd_port;
+	cmdline_fixed_string_t cmd_keyword;
+	uint8_t cmd_pid;
+};
+
+static void
+cmd_gro_show_parsed(void *parsed_result,
+		__attribute__((unused)) struct cmdline *cl,
+		__attribute__((unused)) void *data)
+{
+	struct cmd_gro_show_result *res;
+
+	res = parsed_result;
+	if (!strcmp(res->cmd_keyword, "gro"))
+		show_gro(res->cmd_pid);
+}
+
+cmdline_parse_token_string_t cmd_gro_show_show =
+	TOKEN_STRING_INITIALIZER(struct cmd_gro_show_result,
+			cmd_show, "show");
+cmdline_parse_token_string_t cmd_gro_show_port =
+	TOKEN_STRING_INITIALIZER(struct cmd_gro_show_result,
+			cmd_port, "port");
+cmdline_parse_token_num_t cmd_gro_show_pid =
+	TOKEN_NUM_INITIALIZER(struct cmd_gro_show_result,
+			cmd_pid, UINT8);
+cmdline_parse_token_string_t cmd_gro_show_keyword =
+	TOKEN_STRING_INITIALIZER(struct cmd_gro_show_result,
+			cmd_keyword, "gro");
+
+cmdline_parse_inst_t cmd_gro_show = {
+	.f = cmd_gro_show_parsed,
+	.data = NULL,
+	.help_str = "show port <port_id> gro",
+	.tokens = {
+		(void *)&cmd_gro_show_show,
+		(void *)&cmd_gro_show_port,
+		(void *)&cmd_gro_show_pid,
+		(void *)&cmd_gro_show_keyword,
+		NULL,
+	},
+};
+
+/* *** SET FLUSH INTERVAL FOR THE HEAVYWEIGHT MODE GRO *** */
+struct cmd_gro_flush_result {
+	cmdline_fixed_string_t cmd_set;
+	cmdline_fixed_string_t cmd_keyword;
+	cmdline_fixed_string_t cmd_flush;
+	cmdline_fixed_string_t cmd_interval;
+	uint32_t cmd_num;
 };
 
 static void
-cmd_enable_gro_parsed(void *parsed_result,
+cmd_gro_flush_parsed(void *parsed_result,
 		__attribute__((unused)) struct cmdline *cl,
 		__attribute__((unused)) void *data)
 {
-	struct cmd_gro_result *res;
+	struct cmd_gro_flush_result *res;
 
 	res = parsed_result;
-	setup_gro(res->mode, res->port_id);
+	if (test_done == 0) {
+		printf("Before set flushing interval for heavyweight"
+				" mode GRO, please stop forwarding first\n");
+		return;
+	}
+
+	if (!strcmp(res->cmd_interval, "interval")) {
+		if (res->cmd_num > GRO_MAX_FLUSH_INTERVAL) {
+			printf("The interval value should be in the range"
+					" of 0 to %u. Revert to the default"
+					" value %u\n",
+					GRO_MAX_FLUSH_INTERVAL,
+					GRO_DEFAULT_FLUSH_INTERVAL);
+			gro_flush_interval = GRO_DEFAULT_FLUSH_INTERVAL;
+		} else
+			gro_flush_interval = res->cmd_num;
+	}
 }
 
-cmdline_parse_token_string_t cmd_gro_keyword =
-	TOKEN_STRING_INITIALIZER(struct cmd_gro_result,
+cmdline_parse_token_string_t cmd_gro_flush_set =
+	TOKEN_STRING_INITIALIZER(struct cmd_gro_flush_result,
+			cmd_set, "set");
+cmdline_parse_token_string_t cmd_gro_flush_keyword =
+	TOKEN_STRING_INITIALIZER(struct cmd_gro_flush_result,
 			cmd_keyword, "gro");
-cmdline_parse_token_string_t cmd_gro_mode =
-	TOKEN_STRING_INITIALIZER(struct cmd_gro_result,
-			mode, "on#off");
-cmdline_parse_token_num_t cmd_gro_pid =
-	TOKEN_NUM_INITIALIZER(struct cmd_gro_result,
-			port_id, UINT8);
+cmdline_parse_token_string_t cmd_gro_flush_flush =
+	TOKEN_STRING_INITIALIZER(struct cmd_gro_flush_result,
+			cmd_flush, "flush");
+cmdline_parse_token_string_t cmd_gro_flush_interval =
+	TOKEN_STRING_INITIALIZER(struct cmd_gro_flush_result,
+			cmd_interval, "interval");
+cmdline_parse_token_num_t cmd_gro_flush_num =
+	TOKEN_NUM_INITIALIZER(struct cmd_gro_flush_result,
+			cmd_num, UINT32);
 
-cmdline_parse_inst_t cmd_enable_gro = {
-	.f = cmd_enable_gro_parsed,
+cmdline_parse_inst_t cmd_gro_flush = {
+	.f = cmd_gro_flush_parsed,
 	.data = NULL,
-	.help_str = "gro (on|off) (port_id)",
+	.help_str = "set gro flush interval <num>",
 	.tokens = {
-		(void *)&cmd_gro_keyword,
-		(void *)&cmd_gro_mode,
-		(void *)&cmd_gro_pid,
+		(void *)&cmd_gro_flush_set,
+		(void *)&cmd_gro_flush_keyword,
+		(void *)&cmd_gro_flush_flush,
+		(void *)&cmd_gro_flush_interval,
+		(void *)&cmd_gro_flush_num,
 		NULL,
 	},
 };
@@ -14249,8 +14384,10 @@  cmdline_parse_ctx_t main_ctx[] = {
 	(cmdline_parse_inst_t *)&cmd_tso_show,
 	(cmdline_parse_inst_t *)&cmd_tunnel_tso_set,
 	(cmdline_parse_inst_t *)&cmd_tunnel_tso_show,
-	(cmdline_parse_inst_t *)&cmd_enable_gro,
+	(cmdline_parse_inst_t *)&cmd_gro_enable,
+	(cmdline_parse_inst_t *)&cmd_gro_show,
 	(cmdline_parse_inst_t *)&cmd_gro_set,
+	(cmdline_parse_inst_t *)&cmd_gro_flush,
 	(cmdline_parse_inst_t *)&cmd_link_flow_control_set,
 	(cmdline_parse_inst_t *)&cmd_link_flow_control_set_rx,
 	(cmdline_parse_inst_t *)&cmd_link_flow_control_set_tx,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 3ae3e1c..9ee4e59 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -2420,7 +2420,7 @@  set_tx_pkt_segments(unsigned *seg_lengths, unsigned nb_segs)
 }
 
 void
-setup_gro(const char *mode, uint8_t port_id)
+setup_gro(const char *mode, const char *onoff, uint8_t port_id)
 {
 	if (!rte_eth_dev_is_valid_port(port_id)) {
 		printf("invalid port id %u\n", port_id);
@@ -2431,29 +2431,73 @@  setup_gro(const char *mode, uint8_t port_id)
 				" please stop forwarding first\n");
 		return;
 	}
-	if (strcmp(mode, "on") == 0) {
-		if (gro_ports[port_id].enable) {
-			printf("port %u has enabled GRO\n", port_id);
+	if (strcmp(onoff, "on") == 0) {
+		if (gro_ports[port_id].enable != 0) {
+			printf("Port %u has enabled GRO. Please"
+					" disable GRO first\n", port_id);
 			return;
 		}
-		gro_ports[port_id].enable = 1;
-		gro_ports[port_id].param.gro_types = RTE_GRO_TCP_IPV4;
-
-		if (gro_ports[port_id].param.max_flow_num == 0)
-			gro_ports[port_id].param.max_flow_num =
-				GRO_DEFAULT_FLOW_NUM;
-		if (gro_ports[port_id].param.max_item_per_flow == 0)
-			gro_ports[port_id].param.max_item_per_flow =
-				GRO_DEFAULT_ITEM_NUM_PER_FLOW;
+		if (strcmp(mode, "heavymode") == 0)
+			gro_ports[port_id].enable = GRO_HEAVYMODE;
+		else {
+			gro_ports[port_id].enable = GRO_LIGHTMODE;
+			gro_ports[port_id].param.gro_types = RTE_GRO_TCP_IPV4;
+
+			if (gro_ports[port_id].param.max_flow_num == 0) {
+				gro_ports[port_id].param.max_flow_num =
+					GRO_DEFAULT_FLOW_NUM;
+			}
+			if (gro_ports[port_id].param.max_item_per_flow == 0) {
+				gro_ports[port_id].param.max_item_per_flow =
+					GRO_DEFAULT_ITEM_NUM_PER_FLOW;
+			}
+		}
 	} else {
 		if (gro_ports[port_id].enable == 0) {
-			printf("port %u has disabled GRO\n", port_id);
+			printf("Port %u has disabled GRO\n", port_id);
 			return;
 		}
 		gro_ports[port_id].enable = 0;
 	}
 }
 
+void
+show_gro(uint8_t port_id)
+{
+	struct rte_gro_param *param;
+
+	param = &gro_ports[port_id].param;
+
+	if (!rte_eth_dev_is_valid_port(port_id)) {
+		printf("Invalid port id %u\n", port_id);
+		return;
+	}
+	switch (gro_ports[port_id].enable) {
+	case GRO_HEAVYMODE:
+		printf("GRO mode: heavyweight\n"
+				"Flushing interval: %u\n"
+				"GRO type: TCP/IPv4\n"
+				"Max flow number (fixed): %u\n"
+				"Max item per flow (fixed): %u\n",
+				gro_flush_interval,
+				GRO_MAX_FLUSH_INTERVAL,
+				MAX_PKT_BURST);
+		break;
+	case GRO_LIGHTMODE:
+		printf("GRO mode: lightweight\n"
+				"Flushing interval: N/A\n"
+				"GRO type: TCP/IPv4\n"
+				"Max flow number: %u\n"
+				"Max item per flow: %u\n",
+				param->max_flow_num,
+				param->max_item_per_flow);
+		break;
+	case 0:
+		printf("Port %u doesn't enable GRO\n", port_id);
+		break;
+	}
+}
+
 char*
 list_pkt_forwarding_modes(void)
 {
diff --git a/app/test-pmd/csumonly.c b/app/test-pmd/csumonly.c
index 90c8119..6e98938 100644
--- a/app/test-pmd/csumonly.c
+++ b/app/test-pmd/csumonly.c
@@ -631,6 +631,9 @@  pkt_burst_checksum_forward(struct fwd_stream *fs)
 	struct rte_mbuf *m, *p;
 	struct ether_hdr *eth_hdr;
 	void *l3_hdr = NULL, *outer_l3_hdr = NULL; /* can be IPv4 or IPv6 */
+	void *gro_ctx;
+	uint16_t gro_pkts_num;
+	uint8_t gro_enable;
 	uint16_t nb_rx;
 	uint16_t nb_tx;
 	uint16_t nb_prep;
@@ -657,17 +660,13 @@  pkt_burst_checksum_forward(struct fwd_stream *fs)
 				 nb_pkt_per_burst);
 	if (unlikely(nb_rx == 0))
 		return;
-	if (unlikely(gro_ports[fs->rx_port].enable))
-		nb_rx = rte_gro_reassemble_burst(pkts_burst,
-				nb_rx,
-				&(gro_ports[fs->rx_port].param));
-
 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
 	fs->rx_burst_stats.pkt_burst_spread[nb_rx]++;
 #endif
 	fs->rx_packets += nb_rx;
 	rx_bad_ip_csum = 0;
 	rx_bad_l4_csum = 0;
+	gro_enable = gro_ports[fs->rx_port].enable;
 
 	txp = &ports[fs->tx_port];
 	testpmd_ol_flags = txp->tx_ol_flags;
@@ -851,6 +850,26 @@  pkt_burst_checksum_forward(struct fwd_stream *fs)
 		}
 	}
 
+	if (unlikely(gro_enable == GRO_HEAVYMODE)) {
+		gro_ctx = current_fwd_lcore()->gro_ctx;
+		nb_rx = rte_gro_reassemble(pkts_burst, nb_rx, gro_ctx);
+
+		if (fs->gro_times++ >= gro_flush_interval) {
+			gro_pkts_num = rte_gro_get_pkt_count(gro_ctx);
+			if (gro_pkts_num > MAX_PKT_BURST - nb_rx)
+				gro_pkts_num = MAX_PKT_BURST - nb_rx;
+
+			nb_rx += rte_gro_timeout_flush(gro_ctx, 0,
+					RTE_GRO_TCP_IPV4,
+					&pkts_burst[nb_rx],
+					gro_pkts_num);
+			fs->gro_times = 0;
+		}
+	} else if (unlikely(gro_enable == GRO_LIGHTMODE)) {
+		nb_rx = rte_gro_reassemble_burst(pkts_burst, nb_rx,
+				&(gro_ports[fs->rx_port].param));
+	}
+
 	nb_prep = rte_eth_tx_prepare(fs->tx_port, fs->tx_queue,
 			pkts_burst, nb_rx);
 	if (nb_prep != nb_rx)
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index 7d40139..3946a45 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -90,7 +90,6 @@ 
 #ifdef RTE_LIBRTE_LATENCY_STATS
 #include <rte_latencystats.h>
 #endif
-#include <rte_gro.h>
 
 #include "testpmd.h"
 
@@ -386,6 +385,7 @@  uint8_t bitrate_enabled;
 #endif
 
 struct gro_status gro_ports[RTE_MAX_ETHPORTS];
+uint32_t gro_flush_interval = GRO_DEFAULT_FLUSH_INTERVAL;
 
 /* Forward function declarations */
 static void map_port_queue_stats_mapping_registers(uint8_t pi, struct rte_port *port);
@@ -570,6 +570,7 @@  init_config(void)
 	unsigned int nb_mbuf_per_pool;
 	lcoreid_t  lc_id;
 	uint8_t port_per_socket[RTE_MAX_NUMA_NODES];
+	struct rte_gro_param gro_param;
 
 	memset(port_per_socket,0,RTE_MAX_NUMA_NODES);
 
@@ -671,6 +672,20 @@  init_config(void)
 		rte_exit(EXIT_FAILURE, "FAIL from init_fwd_streams()\n");
 
 	fwd_config_setup();
+
+	/* create a gro context for each lcore */
+	gro_param.gro_types = RTE_GRO_TCP_IPV4;
+	gro_param.max_flow_num = GRO_MAX_FLUSH_INTERVAL;
+	gro_param.max_item_per_flow = MAX_PKT_BURST;
+	for (lc_id = 0; lc_id < nb_lcores; lc_id++) {
+		gro_param.socket_id = rte_lcore_to_socket_id(
+				fwd_lcores_cpuids[lc_id]);
+		fwd_lcores[lc_id]->gro_ctx = rte_gro_ctx_create(&gro_param);
+		if (fwd_lcores[lc_id]->gro_ctx == NULL) {
+			rte_exit(EXIT_FAILURE,
+					"rte_gro_ctx_create() failed\n");
+		}
+	}
 }
 
 
@@ -1165,6 +1180,7 @@  start_packet_forwarding(int with_tx_first)
 		fwd_streams[sm_id]->fwd_dropped = 0;
 		fwd_streams[sm_id]->rx_bad_ip_csum = 0;
 		fwd_streams[sm_id]->rx_bad_l4_csum = 0;
+		fwd_streams[sm_id]->gro_times = 0;
 
 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
 		memset(&fwd_streams[sm_id]->rx_burst_stats, 0,
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index c9d7739..8b71a1f 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -120,6 +120,7 @@  struct fwd_stream {
 	unsigned int fwd_dropped; /**< received packets not forwarded */
 	unsigned int rx_bad_ip_csum ; /**< received packets has bad ip checksum */
 	unsigned int rx_bad_l4_csum ; /**< received packets has bad l4 checksum */
+	unsigned int gro_times;	/**< reassembly times in heavyweight mode */
 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
 	uint64_t     core_cycles; /**< used for RX and TX processing */
 #endif
@@ -206,6 +207,7 @@  struct rte_port {
  */
 struct fwd_lcore {
 	struct rte_mempool *mbp; /**< The mbuf pool to use by this core */
+	void *gro_ctx;		/**< GRO context */
 	streamid_t stream_idx;   /**< index of 1st stream in "fwd_streams" */
 	streamid_t stream_nb;    /**< number of streams in "fwd_streams" */
 	lcoreid_t  cpuid_idx;    /**< index of logical core in CPU id table */
@@ -434,13 +436,21 @@  extern struct ether_addr peer_eth_addrs[RTE_MAX_ETHPORTS];
 extern uint32_t burst_tx_delay_time; /**< Burst tx delay time(us) for mac-retry. */
 extern uint32_t burst_tx_retry_num;  /**< Burst tx retry number for mac-retry. */
 
+#define GRO_HEAVYMODE 0x1
+#define GRO_LIGHTMODE 0x2
+
 #define GRO_DEFAULT_FLOW_NUM 4
 #define GRO_DEFAULT_ITEM_NUM_PER_FLOW DEF_PKT_BURST
+
+#define GRO_DEFAULT_FLUSH_INTERVAL 2
+#define GRO_MAX_FLUSH_INTERVAL 4
+
 struct gro_status {
 	struct rte_gro_param param;
 	uint8_t enable;
 };
 extern struct gro_status gro_ports[RTE_MAX_ETHPORTS];
+extern uint32_t gro_flush_interval;
 
 static inline unsigned int
 lcore_num(void)
@@ -640,7 +650,8 @@  void get_2tuple_filter(uint8_t port_id, uint16_t index);
 void get_5tuple_filter(uint8_t port_id, uint16_t index);
 int rx_queue_id_is_invalid(queueid_t rxq_id);
 int tx_queue_id_is_invalid(queueid_t txq_id);
-void setup_gro(const char *mode, uint8_t port_id);
+void setup_gro(const char *mode, const char *onoff, uint8_t port_id);
+void show_gro(uint8_t port_id);
 
 /* Functions to manage the set of filtered Multicast MAC addresses */
 void mcast_addr_add(uint8_t port_id, struct ether_addr *mc_addr);