[v3,6/8] app/testpmd: add common fwd wrapper

Message ID 20210917080121.329373-7-xuemingl@nvidia.com (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers
Series ethdev: introduce shared Rx queue |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Xueming Li Sept. 17, 2021, 8:01 a.m. UTC
  From: Xiaoyu Min <jackmin@nvidia.com>

Added common forwarding wrapper function for all fwd engines
which do the following in common:

- record core cycles
- call rte_eth_rx_burst(...,nb_pkt_per_burst)
- update received packets
- handle received mbufs with callback function

For better performance, the function is defined as macro.

Signed-off-by: Xiaoyu Min <jackmin@nvidia.com>
Signed-off-by: Xueming Li <xuemingl@nvidia.com>
---
 app/test-pmd/5tswap.c   | 25 +++++--------------------
 app/test-pmd/csumonly.c | 25 ++++++-------------------
 app/test-pmd/flowgen.c  | 20 +++++---------------
 app/test-pmd/icmpecho.c | 30 ++++++++----------------------
 app/test-pmd/iofwd.c    | 24 +++++-------------------
 app/test-pmd/macfwd.c   | 24 +++++-------------------
 app/test-pmd/macswap.c  | 23 +++++------------------
 app/test-pmd/rxonly.c   | 32 ++++++++------------------------
 app/test-pmd/testpmd.h  | 19 +++++++++++++++++++
 9 files changed, 66 insertions(+), 156 deletions(-)
  

Comments

Jerin Jacob Sept. 17, 2021, 11:24 a.m. UTC | #1
On Fri, Sep 17, 2021 at 1:33 PM Xueming Li <xuemingl@nvidia.com> wrote:
>
> From: Xiaoyu Min <jackmin@nvidia.com>
>
> Added common forwarding wrapper function for all fwd engines
> which do the following in common:
>
> - record core cycles
> - call rte_eth_rx_burst(...,nb_pkt_per_burst)
> - update received packets
> - handle received mbufs with callback function
>
> For better performance, the function is defined as macro.
>
> Signed-off-by: Xiaoyu Min <jackmin@nvidia.com>
> Signed-off-by: Xueming Li <xuemingl@nvidia.com>
> ---
>  app/test-pmd/5tswap.c   | 25 +++++--------------------
>  app/test-pmd/csumonly.c | 25 ++++++-------------------
>  app/test-pmd/flowgen.c  | 20 +++++---------------
>  app/test-pmd/icmpecho.c | 30 ++++++++----------------------
>  app/test-pmd/iofwd.c    | 24 +++++-------------------
>  app/test-pmd/macfwd.c   | 24 +++++-------------------
>  app/test-pmd/macswap.c  | 23 +++++------------------
>  app/test-pmd/rxonly.c   | 32 ++++++++------------------------
>  app/test-pmd/testpmd.h  | 19 +++++++++++++++++++
>  9 files changed, 66 insertions(+), 156 deletions(-)
>
> diff --git a/app/test-pmd/5tswap.c b/app/test-pmd/5tswap.c
> index e8cef9623b..8fe940294f 100644
> --- a/app/test-pmd/5tswap.c
> +++ b/app/test-pmd/5tswap.c
> @@ -82,18 +82,16 @@ swap_udp(struct rte_udp_hdr *udp_hdr)
>   * Parses each layer and swaps it. When the next layer doesn't match it stops.
>   */

> +PKT_BURST_FWD(_5tuple_swap_stream);

Please make _5tuple_swap_stream aka "cb" as inline function to  make sure
compiler doesn't generate yet another function pointer.

>  struct fwd_engine mac_swap_engine = {
>         .fwd_mode_name  = "macswap",
>         .port_fwd_begin = NULL,
>         .port_fwd_end   = NULL,
> -       .packet_fwd     = pkt_burst_mac_swap,

See below

> +       .packet_fwd     = pkt_burst_fwd,
>
> +#define PKT_BURST_FWD(cb)                                       \

Probably it can pass prefix too like PKT_BURST_FWD(cb, prefix)
to make a unique function and call PKT_BURST_FWD(_5tuple_swap_stream,
mac_swap) for better readability
and avoid diff above section.


> +static void                                                     \
> +pkt_burst_fwd(struct fwd_stream *fs)

pkt_burst_fwd##prefix(struct fwd_stream *fs)
                            \
> +{                                                               \
> +       struct rte_mbuf *pkts_burst[nb_pkt_per_burst];          \
> +       uint16_t nb_rx;                                         \
> +       uint64_t start_tsc = 0;                                 \
> +                                                               \
> +       get_start_cycles(&start_tsc);                           \
> +       nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue,     \
> +                       pkts_burst, nb_pkt_per_burst);          \
> +       inc_rx_burst_stats(fs, nb_rx);                          \
> +       if (unlikely(nb_rx == 0))                               \
> +               return;                                         \
> +       fs->rx_packets += nb_rx;                                \
> +       cb(fs, nb_rx, pkts_burst);                              \
> +       get_end_cycles(fs, start_tsc);                          \
> +}
> +
>  /*
>   * Work-around of a compilation error with ICC on invocations of the
>   * rte_be_to_cpu_16() function.
> --
> 2.33.0
>
  

Patch

diff --git a/app/test-pmd/5tswap.c b/app/test-pmd/5tswap.c
index e8cef9623b..8fe940294f 100644
--- a/app/test-pmd/5tswap.c
+++ b/app/test-pmd/5tswap.c
@@ -82,18 +82,16 @@  swap_udp(struct rte_udp_hdr *udp_hdr)
  * Parses each layer and swaps it. When the next layer doesn't match it stops.
  */
 static void
-pkt_burst_5tuple_swap(struct fwd_stream *fs)
+_5tuple_swap_stream(struct fwd_stream *fs, uint16_t nb_rx,
+		struct rte_mbuf **pkts_burst)
 {
-	struct rte_mbuf  *pkts_burst[MAX_PKT_BURST];
 	struct rte_port  *txp;
 	struct rte_mbuf *mb;
 	uint16_t next_proto;
 	uint64_t ol_flags;
 	uint16_t proto;
-	uint16_t nb_rx;
 	uint16_t nb_tx;
 	uint32_t retry;
-
 	int i;
 	union {
 		struct rte_ether_hdr *eth;
@@ -105,20 +103,6 @@  pkt_burst_5tuple_swap(struct fwd_stream *fs)
 		uint8_t *byte;
 	} h;
 
-	uint64_t start_tsc = 0;
-
-	get_start_cycles(&start_tsc);
-
-	/*
-	 * Receive a burst of packets and forward them.
-	 */
-	nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, pkts_burst,
-				 nb_pkt_per_burst);
-	inc_rx_burst_stats(fs, nb_rx);
-	if (unlikely(nb_rx == 0))
-		return;
-
-	fs->rx_packets += nb_rx;
 	txp = &ports[fs->tx_port];
 	ol_flags = ol_flags_init(txp->dev_conf.txmode.offloads);
 	vlan_qinq_set(pkts_burst, nb_rx, ol_flags,
@@ -182,12 +166,13 @@  pkt_burst_5tuple_swap(struct fwd_stream *fs)
 			rte_pktmbuf_free(pkts_burst[nb_tx]);
 		} while (++nb_tx < nb_rx);
 	}
-	get_end_cycles(fs, start_tsc);
 }
 
+PKT_BURST_FWD(_5tuple_swap_stream);
+
 struct fwd_engine five_tuple_swap_fwd_engine = {
 	.fwd_mode_name  = "5tswap",
 	.port_fwd_begin = NULL,
 	.port_fwd_end   = NULL,
-	.packet_fwd     = pkt_burst_5tuple_swap,
+	.packet_fwd     = pkt_burst_fwd,
 };
diff --git a/app/test-pmd/csumonly.c b/app/test-pmd/csumonly.c
index 38cc256533..9bfc7d10dc 100644
--- a/app/test-pmd/csumonly.c
+++ b/app/test-pmd/csumonly.c
@@ -763,7 +763,7 @@  pkt_copy_split(const struct rte_mbuf *pkt)
 }
 
 /*
- * Receive a burst of packets, and for each packet:
+ * For each packet in received mbuf:
  *  - parse packet, and try to recognize a supported packet type (1)
  *  - if it's not a supported packet type, don't touch the packet, else:
  *  - reprocess the checksum of all supported layers. This is done in SW
@@ -792,9 +792,9 @@  pkt_copy_split(const struct rte_mbuf *pkt)
  * OUTER_IP is only useful for tunnel packets.
  */
 static void
-pkt_burst_checksum_forward(struct fwd_stream *fs)
+checksum_forward_stream(struct fwd_stream *fs, uint16_t nb_rx,
+		struct rte_mbuf **pkts_burst)
 {
-	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
 	struct rte_mbuf *gso_segments[GSO_MAX_PKT_BURST];
 	struct rte_gso_ctx *gso_ctx;
 	struct rte_mbuf **tx_pkts_burst;
@@ -805,7 +805,6 @@  pkt_burst_checksum_forward(struct fwd_stream *fs)
 	void **gro_ctx;
 	uint16_t gro_pkts_num;
 	uint8_t gro_enable;
-	uint16_t nb_rx;
 	uint16_t nb_tx;
 	uint16_t nb_prep;
 	uint16_t i;
@@ -820,18 +819,6 @@  pkt_burst_checksum_forward(struct fwd_stream *fs)
 	uint16_t nb_segments = 0;
 	int ret;
 
-	uint64_t start_tsc = 0;
-
-	get_start_cycles(&start_tsc);
-
-	/* receive a burst of packet */
-	nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, pkts_burst,
-				 nb_pkt_per_burst);
-	inc_rx_burst_stats(fs, nb_rx);
-	if (unlikely(nb_rx == 0))
-		return;
-
-	fs->rx_packets += nb_rx;
 	rx_bad_ip_csum = 0;
 	rx_bad_l4_csum = 0;
 	rx_bad_outer_l4_csum = 0;
@@ -1138,13 +1125,13 @@  pkt_burst_checksum_forward(struct fwd_stream *fs)
 			rte_pktmbuf_free(tx_pkts_burst[nb_tx]);
 		} while (++nb_tx < nb_rx);
 	}
-
-	get_end_cycles(fs, start_tsc);
 }
 
+PKT_BURST_FWD(checksum_forward_stream);
+
 struct fwd_engine csum_fwd_engine = {
 	.fwd_mode_name  = "csum",
 	.port_fwd_begin = NULL,
 	.port_fwd_end   = NULL,
-	.packet_fwd     = pkt_burst_checksum_forward,
+	.packet_fwd     = pkt_burst_fwd,
 };
diff --git a/app/test-pmd/flowgen.c b/app/test-pmd/flowgen.c
index 0d3664a64d..aa45948b4c 100644
--- a/app/test-pmd/flowgen.c
+++ b/app/test-pmd/flowgen.c
@@ -61,10 +61,10 @@  RTE_DEFINE_PER_LCORE(int, _next_flow);
  * still do so in order to maintain traffic statistics.
  */
 static void
-pkt_burst_flow_gen(struct fwd_stream *fs)
+flow_gen_stream(struct fwd_stream *fs, uint16_t nb_rx,
+		struct rte_mbuf **pkts_burst)
 {
 	unsigned pkt_size = tx_pkt_length - 4;	/* Adjust FCS */
-	struct rte_mbuf  *pkts_burst[MAX_PKT_BURST];
 	struct rte_mempool *mbp;
 	struct rte_mbuf  *pkt = NULL;
 	struct rte_ether_hdr *eth_hdr;
@@ -72,7 +72,6 @@  pkt_burst_flow_gen(struct fwd_stream *fs)
 	struct rte_udp_hdr *udp_hdr;
 	uint16_t vlan_tci, vlan_tci_outer;
 	uint64_t ol_flags = 0;
-	uint16_t nb_rx;
 	uint16_t nb_tx;
 	uint16_t nb_dropped;
 	uint16_t nb_pkt;
@@ -80,17 +79,9 @@  pkt_burst_flow_gen(struct fwd_stream *fs)
 	uint16_t i;
 	uint32_t retry;
 	uint64_t tx_offloads;
-	uint64_t start_tsc = 0;
 	int next_flow = RTE_PER_LCORE(_next_flow);
 
-	get_start_cycles(&start_tsc);
-
-	/* Receive a burst of packets and discard them. */
-	nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, pkts_burst,
-				 nb_pkt_per_burst);
 	inc_rx_burst_stats(fs, nb_rx);
-	fs->rx_packets += nb_rx;
-
 	for (i = 0; i < nb_rx; i++)
 		rte_pktmbuf_free(pkts_burst[i]);
 
@@ -195,12 +186,11 @@  pkt_burst_flow_gen(struct fwd_stream *fs)
 			rte_pktmbuf_free(pkts_burst[nb_tx]);
 		} while (++nb_tx < nb_pkt);
 	}
-
 	RTE_PER_LCORE(_next_flow) = next_flow;
-
-	get_end_cycles(fs, start_tsc);
 }
 
+PKT_BURST_FWD(flow_gen_stream);
+
 static void
 flowgen_begin(portid_t pi)
 {
@@ -211,5 +201,5 @@  struct fwd_engine flow_gen_engine = {
 	.fwd_mode_name  = "flowgen",
 	.port_fwd_begin = flowgen_begin,
 	.port_fwd_end   = NULL,
-	.packet_fwd     = pkt_burst_flow_gen,
+	.packet_fwd     = pkt_burst_fwd,
 };
diff --git a/app/test-pmd/icmpecho.c b/app/test-pmd/icmpecho.c
index 8948f28eb5..467ba330aa 100644
--- a/app/test-pmd/icmpecho.c
+++ b/app/test-pmd/icmpecho.c
@@ -267,13 +267,13 @@  ipv4_hdr_cksum(struct rte_ipv4_hdr *ip_h)
 	(((rte_be_to_cpu_32((ipv4_addr)) >> 24) & 0x000000FF) == 0xE0)
 
 /*
- * Receive a burst of packets, lookup for ICMP echo requests, and, if any,
- * send back ICMP echo replies.
+ * Lookup for ICMP echo requests in received mbuf and, if any,
+ * send back ICMP echo replies to corresponding Tx port.
  */
 static void
-reply_to_icmp_echo_rqsts(struct fwd_stream *fs)
+reply_to_icmp_echo_rqsts_stream(struct fwd_stream *fs, uint16_t nb_rx,
+		struct rte_mbuf **pkts_burst)
 {
-	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
 	struct rte_mbuf *pkt;
 	struct rte_ether_hdr *eth_h;
 	struct rte_vlan_hdr *vlan_h;
@@ -283,7 +283,6 @@  reply_to_icmp_echo_rqsts(struct fwd_stream *fs)
 	struct rte_ether_addr eth_addr;
 	uint32_t retry;
 	uint32_t ip_addr;
-	uint16_t nb_rx;
 	uint16_t nb_tx;
 	uint16_t nb_replies;
 	uint16_t eth_type;
@@ -291,22 +290,9 @@  reply_to_icmp_echo_rqsts(struct fwd_stream *fs)
 	uint16_t arp_op;
 	uint16_t arp_pro;
 	uint32_t cksum;
-	uint8_t  i;
+	uint16_t  i;
 	int l2_len;
-	uint64_t start_tsc = 0;
 
-	get_start_cycles(&start_tsc);
-
-	/*
-	 * First, receive a burst of packets.
-	 */
-	nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, pkts_burst,
-				 nb_pkt_per_burst);
-	inc_rx_burst_stats(fs, nb_rx);
-	if (unlikely(nb_rx == 0))
-		return;
-
-	fs->rx_packets += nb_rx;
 	nb_replies = 0;
 	for (i = 0; i < nb_rx; i++) {
 		if (likely(i < nb_rx - 1))
@@ -509,13 +495,13 @@  reply_to_icmp_echo_rqsts(struct fwd_stream *fs)
 			} while (++nb_tx < nb_replies);
 		}
 	}
-
-	get_end_cycles(fs, start_tsc);
 }
 
+PKT_BURST_FWD(reply_to_icmp_echo_rqsts_stream);
+
 struct fwd_engine icmp_echo_engine = {
 	.fwd_mode_name  = "icmpecho",
 	.port_fwd_begin = NULL,
 	.port_fwd_end   = NULL,
-	.packet_fwd     = reply_to_icmp_echo_rqsts,
+	.packet_fwd     = pkt_burst_fwd,
 };
diff --git a/app/test-pmd/iofwd.c b/app/test-pmd/iofwd.c
index 83d098adcb..dbd78167b4 100644
--- a/app/test-pmd/iofwd.c
+++ b/app/test-pmd/iofwd.c
@@ -44,25 +44,11 @@ 
  * to packets data.
  */
 static void
-pkt_burst_io_forward(struct fwd_stream *fs)
+io_forward_stream(struct fwd_stream *fs, uint16_t nb_rx,
+		  struct rte_mbuf **pkts_burst)
 {
-	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
-	uint16_t nb_rx;
 	uint16_t nb_tx;
 	uint32_t retry;
-	uint64_t start_tsc = 0;
-
-	get_start_cycles(&start_tsc);
-
-	/*
-	 * Receive a burst of packets and forward them.
-	 */
-	nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue,
-			pkts_burst, nb_pkt_per_burst);
-	inc_rx_burst_stats(fs, nb_rx);
-	if (unlikely(nb_rx == 0))
-		return;
-	fs->rx_packets += nb_rx;
 
 	nb_tx = rte_eth_tx_burst(fs->tx_port, fs->tx_queue,
 			pkts_burst, nb_rx);
@@ -85,13 +71,13 @@  pkt_burst_io_forward(struct fwd_stream *fs)
 			rte_pktmbuf_free(pkts_burst[nb_tx]);
 		} while (++nb_tx < nb_rx);
 	}
-
-	get_end_cycles(fs, start_tsc);
 }
 
+PKT_BURST_FWD(io_forward_stream);
+
 struct fwd_engine io_fwd_engine = {
 	.fwd_mode_name  = "io",
 	.port_fwd_begin = NULL,
 	.port_fwd_end   = NULL,
-	.packet_fwd     = pkt_burst_io_forward,
+	.packet_fwd     = pkt_burst_fwd,
 };
diff --git a/app/test-pmd/macfwd.c b/app/test-pmd/macfwd.c
index 0568ea794d..b0728c7597 100644
--- a/app/test-pmd/macfwd.c
+++ b/app/test-pmd/macfwd.c
@@ -44,32 +44,18 @@ 
  * before forwarding them.
  */
 static void
-pkt_burst_mac_forward(struct fwd_stream *fs)
+mac_forward_stream(struct fwd_stream *fs, uint16_t nb_rx,
+		struct rte_mbuf **pkts_burst)
 {
-	struct rte_mbuf  *pkts_burst[MAX_PKT_BURST];
 	struct rte_port  *txp;
 	struct rte_mbuf  *mb;
 	struct rte_ether_hdr *eth_hdr;
 	uint32_t retry;
-	uint16_t nb_rx;
 	uint16_t nb_tx;
 	uint16_t i;
 	uint64_t ol_flags = 0;
 	uint64_t tx_offloads;
-	uint64_t start_tsc = 0;
 
-	get_start_cycles(&start_tsc);
-
-	/*
-	 * Receive a burst of packets and forward them.
-	 */
-	nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, pkts_burst,
-				 nb_pkt_per_burst);
-	inc_rx_burst_stats(fs, nb_rx);
-	if (unlikely(nb_rx == 0))
-		return;
-
-	fs->rx_packets += nb_rx;
 	txp = &ports[fs->tx_port];
 	tx_offloads = txp->dev_conf.txmode.offloads;
 	if (tx_offloads	& DEV_TX_OFFLOAD_VLAN_INSERT)
@@ -116,13 +102,13 @@  pkt_burst_mac_forward(struct fwd_stream *fs)
 			rte_pktmbuf_free(pkts_burst[nb_tx]);
 		} while (++nb_tx < nb_rx);
 	}
-
-	get_end_cycles(fs, start_tsc);
 }
 
+PKT_BURST_FWD(mac_forward_stream);
+
 struct fwd_engine mac_fwd_engine = {
 	.fwd_mode_name  = "mac",
 	.port_fwd_begin = NULL,
 	.port_fwd_end   = NULL,
-	.packet_fwd     = pkt_burst_mac_forward,
+	.packet_fwd     = pkt_burst_fwd,
 };
diff --git a/app/test-pmd/macswap.c b/app/test-pmd/macswap.c
index 310bca06af..cc208944d7 100644
--- a/app/test-pmd/macswap.c
+++ b/app/test-pmd/macswap.c
@@ -50,27 +50,13 @@ 
  * addresses of packets before forwarding them.
  */
 static void
-pkt_burst_mac_swap(struct fwd_stream *fs)
+mac_swap_stream(struct fwd_stream *fs, uint16_t nb_rx,
+		struct rte_mbuf **pkts_burst)
 {
-	struct rte_mbuf  *pkts_burst[MAX_PKT_BURST];
 	struct rte_port  *txp;
-	uint16_t nb_rx;
 	uint16_t nb_tx;
 	uint32_t retry;
-	uint64_t start_tsc = 0;
 
-	get_start_cycles(&start_tsc);
-
-	/*
-	 * Receive a burst of packets and forward them.
-	 */
-	nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, pkts_burst,
-				 nb_pkt_per_burst);
-	inc_rx_burst_stats(fs, nb_rx);
-	if (unlikely(nb_rx == 0))
-		return;
-
-	fs->rx_packets += nb_rx;
 	txp = &ports[fs->tx_port];
 
 	do_macswap(pkts_burst, nb_rx, txp);
@@ -95,12 +81,13 @@  pkt_burst_mac_swap(struct fwd_stream *fs)
 			rte_pktmbuf_free(pkts_burst[nb_tx]);
 		} while (++nb_tx < nb_rx);
 	}
-	get_end_cycles(fs, start_tsc);
 }
 
+PKT_BURST_FWD(mac_swap_stream);
+
 struct fwd_engine mac_swap_engine = {
 	.fwd_mode_name  = "macswap",
 	.port_fwd_begin = NULL,
 	.port_fwd_end   = NULL,
-	.packet_fwd     = pkt_burst_mac_swap,
+	.packet_fwd     = pkt_burst_fwd,
 };
diff --git a/app/test-pmd/rxonly.c b/app/test-pmd/rxonly.c
index c78fc4609a..a7354596b5 100644
--- a/app/test-pmd/rxonly.c
+++ b/app/test-pmd/rxonly.c
@@ -41,37 +41,21 @@ 
 #include "testpmd.h"
 
 /*
- * Received a burst of packets.
+ * Process a burst of received packets from same stream.
  */
 static void
-pkt_burst_receive(struct fwd_stream *fs)
+rxonly_forward_stream(struct fwd_stream *fs, uint16_t nb_rx,
+		      struct rte_mbuf **pkts_burst)
 {
-	struct rte_mbuf  *pkts_burst[MAX_PKT_BURST];
-	uint16_t nb_rx;
-	uint16_t i;
-	uint64_t start_tsc = 0;
-
-	get_start_cycles(&start_tsc);
-
-	/*
-	 * Receive a burst of packets.
-	 */
-	nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, pkts_burst,
-				 nb_pkt_per_burst);
-	inc_rx_burst_stats(fs, nb_rx);
-	if (unlikely(nb_rx == 0))
-		return;
-
-	fs->rx_packets += nb_rx;
-	for (i = 0; i < nb_rx; i++)
-		rte_pktmbuf_free(pkts_burst[i]);
-
-	get_end_cycles(fs, start_tsc);
+	RTE_SET_USED(fs);
+	rte_pktmbuf_free_bulk(pkts_burst, nb_rx);
 }
 
+PKT_BURST_FWD(rxonly_forward_stream)
+
 struct fwd_engine rx_only_engine = {
 	.fwd_mode_name  = "rxonly",
 	.port_fwd_begin = NULL,
 	.port_fwd_end   = NULL,
-	.packet_fwd     = pkt_burst_receive,
+	.packet_fwd     = pkt_burst_fwd,
 };
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index f121a2da90..4792bef03b 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -1028,6 +1028,25 @@  void add_tx_dynf_callback(portid_t portid);
 void remove_tx_dynf_callback(portid_t portid);
 int update_jumbo_frame_offload(portid_t portid);
 
+#define PKT_BURST_FWD(cb)                                       \
+static void                                                     \
+pkt_burst_fwd(struct fwd_stream *fs)                            \
+{                                                               \
+	struct rte_mbuf *pkts_burst[nb_pkt_per_burst];          \
+	uint16_t nb_rx;                                         \
+	uint64_t start_tsc = 0;                                 \
+								\
+	get_start_cycles(&start_tsc);                           \
+	nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue,     \
+			pkts_burst, nb_pkt_per_burst);          \
+	inc_rx_burst_stats(fs, nb_rx);                          \
+	if (unlikely(nb_rx == 0))                               \
+		return;                                         \
+	fs->rx_packets += nb_rx;                                \
+	cb(fs, nb_rx, pkts_burst);                              \
+	get_end_cycles(fs, start_tsc);                          \
+}
+
 /*
  * Work-around of a compilation error with ICC on invocations of the
  * rte_be_to_cpu_16() function.