diff mbox series

app/eventdev: add Tx first option to pipeline mode

Message ID 20220525090052.5157-1-pbhagavatula@marvell.com (mailing list archive)
State Accepted, archived
Delegated to: Jerin Jacob
Headers show
Series app/eventdev: add Tx first option to pipeline mode | expand

Checks

Context Check Description
ci/iol-abi-testing success Testing PASS
ci/iol-x86_64-compile-testing success Testing PASS
ci/iol-x86_64-unit-testing success Testing PASS
ci/iol-aarch64-compile-testing success Testing PASS
ci/iol-aarch64-unit-testing success Testing PASS
ci/github-robot: build success github build: passed
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-mellanox-Performance success Performance Testing PASS
ci/intel-Testing success Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/Intel-compilation success Compilation OK
ci/checkpatch warning coding style issues

Commit Message

Pavan Nikhilesh Bhagavatula May 25, 2022, 9 a.m. UTC
From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add Tx first support to pipeline mode tests, the transmition is done
on all the ethernet ports. This helps in testing eventdev performance
with standalone loopback interfaces.

Example:
	./dpdk-test-eventdev ... -- ... --tx_first 512

	512 defines the number of packets to transmit.

Add an option Tx packet size, the default packet size is 64.

Example:
	./dpdk-test-eventdev ... -- ... --tx_first 512 --tx_pkt_sz 320

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 app/test-eventdev/evt_common.h           |   1 +
 app/test-eventdev/evt_options.c          |  28 ++++
 app/test-eventdev/evt_options.h          |   2 +
 app/test-eventdev/test_pipeline_common.c | 165 ++++++++++++++++++++++-
 app/test-eventdev/test_pipeline_common.h |   2 +
 doc/guides/tools/testeventdev.rst        |  21 ++-
 6 files changed, 214 insertions(+), 5 deletions(-)

Comments

Jerin Jacob June 13, 2022, 5:53 a.m. UTC | #1
On Wed, May 25, 2022 at 2:31 PM <pbhagavatula@marvell.com> wrote:
>
> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
>
> Add Tx first support to pipeline mode tests, the transmition is done

Fixed transmission typo.
Acked-by: Jerin Jacob <jerinj@marvell.com>

Updated the git commit as follows and applied to
dpdk-next-net-eventdev/for-main. Thanks

app/eventdev: add Tx first option to pipeline mode

Add Tx first support to pipeline mode tests, the transmission is done
on all the ethernet ports. This helps in testing eventdev performance
with standalone loopback interfaces.

Example:
./dpdk-test-eventdev ... -- ... --tx_first 512

512 defines the number of packets to transmit.
Add an option Tx packet size, the default packet size is 64.

Example:
./dpdk-test-eventdev ... -- ... --tx_first 512 --tx_pkt_sz 320

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Acked-by: Jerin Jacob <jerinj@marvell.com>

> on all the ethernet ports. This helps in testing eventdev performance
> with standalone loopback interfaces.
>
> Example:
>         ./dpdk-test-eventdev ... -- ... --tx_first 512
>
>         512 defines the number of packets to transmit.
>
> Add an option Tx packet size, the default packet size is 64.
>
> Example:
>         ./dpdk-test-eventdev ... -- ... --tx_first 512 --tx_pkt_sz 320
>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> ---
>  app/test-eventdev/evt_common.h           |   1 +
>  app/test-eventdev/evt_options.c          |  28 ++++
>  app/test-eventdev/evt_options.h          |   2 +
>  app/test-eventdev/test_pipeline_common.c | 165 ++++++++++++++++++++++-
>  app/test-eventdev/test_pipeline_common.h |   2 +
>  doc/guides/tools/testeventdev.rst        |  21 ++-
>  6 files changed, 214 insertions(+), 5 deletions(-)
>
> diff --git a/app/test-eventdev/evt_common.h b/app/test-eventdev/evt_common.h
> index 2f301a7e79..e304998cd7 100644
> --- a/app/test-eventdev/evt_common.h
> +++ b/app/test-eventdev/evt_common.h
> @@ -65,6 +65,7 @@ struct evt_options {
>         uint16_t eth_queues;
>         uint32_t nb_flows;
>         uint32_t tx_first;
> +       uint16_t tx_pkt_sz;
>         uint32_t max_pkt_sz;
>         uint32_t prod_enq_burst_sz;
>         uint32_t deq_tmo_nsec;
> diff --git a/app/test-eventdev/evt_options.c b/app/test-eventdev/evt_options.c
> index d3c704d2b3..fb17f5a159 100644
> --- a/app/test-eventdev/evt_options.c
> +++ b/app/test-eventdev/evt_options.c
> @@ -106,6 +106,26 @@ evt_parse_eth_prod_type(struct evt_options *opt, const char *arg __rte_unused)
>         return 0;
>  }
>
> +static int
> +evt_parse_tx_first(struct evt_options *opt, const char *arg __rte_unused)
> +{
> +       int ret;
> +
> +       ret = parser_read_uint32(&(opt->tx_first), arg);
> +
> +       return ret;
> +}
> +
> +static int
> +evt_parse_tx_pkt_sz(struct evt_options *opt, const char *arg __rte_unused)
> +{
> +       int ret;
> +
> +       ret = parser_read_uint16(&(opt->tx_pkt_sz), arg);
> +
> +       return ret;
> +}
> +
>  static int
>  evt_parse_timer_prod_type(struct evt_options *opt, const char *arg __rte_unused)
>  {
> @@ -376,6 +396,10 @@ usage(char *program)
>                 "\t--vector_size      : Max vector size.\n"
>                 "\t--vector_tmo_ns    : Max vector timeout in nanoseconds\n"
>                 "\t--per_port_pool    : Configure unique pool per ethdev port\n"
> +               "\t--tx_first         : Transmit given number of packets\n"
> +               "                       across all the ethernet devices before\n"
> +               "                       event workers start.\n"
> +               "\t--tx_pkt_sz        : Packet size to use with Tx first."
>                 );
>         printf("available tests:\n");
>         evt_test_dump_names();
> @@ -456,6 +480,8 @@ static struct option lgopts[] = {
>         { EVT_VECTOR_TMO,          1, 0, 0 },
>         { EVT_PER_PORT_POOL,       0, 0, 0 },
>         { EVT_HELP,                0, 0, 0 },
> +       { EVT_TX_FIRST,            1, 0, 0 },
> +       { EVT_TX_PKT_SZ,           1, 0, 0 },
>         { NULL,                    0, 0, 0 }
>  };
>
> @@ -497,6 +523,8 @@ evt_opts_parse_long(int opt_idx, struct evt_options *opt)
>                 { EVT_VECTOR_SZ, evt_parse_vector_size},
>                 { EVT_VECTOR_TMO, evt_parse_vector_tmo_ns},
>                 { EVT_PER_PORT_POOL, evt_parse_per_port_pool},
> +               { EVT_TX_FIRST, evt_parse_tx_first},
> +               { EVT_TX_PKT_SZ, evt_parse_tx_pkt_sz},
>         };
>
>         for (i = 0; i < RTE_DIM(parsermap); i++) {
> diff --git a/app/test-eventdev/evt_options.h b/app/test-eventdev/evt_options.h
> index 2231c58801..4aa06976b7 100644
> --- a/app/test-eventdev/evt_options.h
> +++ b/app/test-eventdev/evt_options.h
> @@ -51,6 +51,8 @@
>  #define EVT_VECTOR_SZ            ("vector_size")
>  #define EVT_VECTOR_TMO           ("vector_tmo_ns")
>  #define EVT_PER_PORT_POOL       ("per_port_pool")
> +#define EVT_TX_FIRST            ("tx_first")
> +#define EVT_TX_PKT_SZ           ("tx_pkt_sz")
>  #define EVT_HELP                 ("help")
>
>  void evt_options_default(struct evt_options *opt);
> diff --git a/app/test-eventdev/test_pipeline_common.c b/app/test-eventdev/test_pipeline_common.c
> index c66656cd39..82fe04258b 100644
> --- a/app/test-eventdev/test_pipeline_common.c
> +++ b/app/test-eventdev/test_pipeline_common.c
> @@ -56,14 +56,170 @@ processed_pkts(struct test_pipeline *t)
>         return total;
>  }
>
> +/* RFC863 discard port */
> +#define UDP_SRC_PORT 9
> +#define UDP_DST_PORT 9
> +
> +/* RFC2544 reserved test subnet 192.18.0.0 */
> +#define IP_SRC_ADDR(x, y) ((192U << 24) | (18 << 16) | ((x) << 8) | (y))
> +#define IP_DST_ADDR(x, y) ((192U << 24) | (18 << 16) | ((x) << 8) | (y))
> +
> +#define IP_DEFTTL  64 /* from RFC 1340. */
> +#define IP_VERSION 0x40
> +#define IP_HDRLEN  0x05 /* default IP header length == five 32-bits words. */
> +#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN)
> +
> +static void
> +setup_pkt_udp_ip_headers(struct rte_ipv4_hdr *ip_hdr,
> +                        struct rte_udp_hdr *udp_hdr, uint16_t pkt_data_len,
> +                        uint8_t port, uint8_t flow)
> +{
> +       uint16_t *ptr16;
> +       uint32_t ip_cksum;
> +       uint16_t pkt_len;
> +
> +       /*
> +        * Initialize UDP header.
> +        */
> +       pkt_len = (uint16_t)(pkt_data_len + sizeof(struct rte_udp_hdr));
> +       udp_hdr->src_port = rte_cpu_to_be_16(UDP_SRC_PORT);
> +       udp_hdr->dst_port = rte_cpu_to_be_16(UDP_DST_PORT);
> +       udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len);
> +       udp_hdr->dgram_cksum = 0; /* No UDP checksum. */
> +
> +       /*
> +        * Initialize IP header.
> +        */
> +       pkt_len = (uint16_t)(pkt_len + sizeof(struct rte_ipv4_hdr));
> +       ip_hdr->version_ihl = IP_VHL_DEF;
> +       ip_hdr->type_of_service = 0;
> +       ip_hdr->fragment_offset = 0;
> +       ip_hdr->time_to_live = IP_DEFTTL;
> +       ip_hdr->next_proto_id = IPPROTO_UDP;
> +       ip_hdr->packet_id = 0;
> +       ip_hdr->total_length = rte_cpu_to_be_16(pkt_len);
> +       ip_hdr->src_addr = rte_cpu_to_be_32(IP_SRC_ADDR(port, 1));
> +       ip_hdr->dst_addr = rte_cpu_to_be_32(IP_DST_ADDR(port + 1, flow));
> +
> +       /*
> +        * Compute IP header checksum.
> +        */
> +       ptr16 = (unaligned_uint16_t *)ip_hdr;
> +       ip_cksum = 0;
> +       ip_cksum += ptr16[0];
> +       ip_cksum += ptr16[1];
> +       ip_cksum += ptr16[2];
> +       ip_cksum += ptr16[3];
> +       ip_cksum += ptr16[4];
> +       ip_cksum += ptr16[6];
> +       ip_cksum += ptr16[7];
> +       ip_cksum += ptr16[8];
> +       ip_cksum += ptr16[9];
> +
> +       /*
> +        * Reduce 32 bit checksum to 16 bits and complement it.
> +        */
> +       ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + (ip_cksum & 0x0000FFFF);
> +       if (ip_cksum > 65535)
> +               ip_cksum -= 65535;
> +       ip_cksum = (~ip_cksum) & 0x0000FFFF;
> +       if (ip_cksum == 0)
> +               ip_cksum = 0xFFFF;
> +       ip_hdr->hdr_checksum = (uint16_t)ip_cksum;
> +}
> +
> +static void
> +pipeline_tx_first(struct test_pipeline *t, struct evt_options *opt)
> +{
> +#define TX_DEF_PACKET_LEN 64
> +       uint16_t eth_port_id = 0;
> +       uint16_t pkt_sz, rc;
> +       uint32_t i;
> +
> +       pkt_sz = opt->tx_pkt_sz;
> +       if (pkt_sz > opt->max_pkt_sz)
> +               pkt_sz = opt->max_pkt_sz;
> +       if (!pkt_sz)
> +               pkt_sz = TX_DEF_PACKET_LEN;
> +
> +       RTE_ETH_FOREACH_DEV(eth_port_id) {
> +               struct rte_ether_addr src_mac;
> +               struct rte_ether_addr dst_mac;
> +               struct rte_ether_hdr eth_hdr;
> +
> +               /* Send to the same dest.mac as port mac */
> +               rte_eth_macaddr_get(eth_port_id, &dst_mac);
> +               rte_eth_random_addr((uint8_t *)&src_mac);
> +
> +               rte_ether_addr_copy(&dst_mac, &eth_hdr.dst_addr);
> +               rte_ether_addr_copy(&src_mac, &eth_hdr.src_addr);
> +               eth_hdr.ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
> +
> +               for (i = 0; i < opt->tx_first; i++) {
> +                       struct rte_udp_hdr *pkt_udp_hdr;
> +                       struct rte_ipv4_hdr ip_hdr;
> +                       struct rte_udp_hdr udp_hdr;
> +                       struct rte_mbuf *mbuf;
> +
> +                       mbuf = rte_pktmbuf_alloc(
> +                               opt->per_port_pool ? t->pool[i] : t->pool[0]);
> +                       if (mbuf == NULL)
> +                               continue;
> +
> +                       setup_pkt_udp_ip_headers(
> +                               &ip_hdr, &udp_hdr,
> +                               pkt_sz - sizeof(struct rte_ether_hdr) -
> +                                       sizeof(struct rte_ipv4_hdr) -
> +                                       sizeof(struct rte_udp_hdr),
> +                               eth_port_id, i);
> +                       mbuf->port = eth_port_id;
> +                       mbuf->data_len = pkt_sz;
> +                       mbuf->pkt_len = pkt_sz;
> +
> +                       /* Copy Ethernet header */
> +                       rte_memcpy(rte_pktmbuf_mtod_offset(mbuf, char *, 0),
> +                                  &eth_hdr, sizeof(struct rte_ether_hdr));
> +
> +                       /* Copy Ipv4 header */
> +                       rte_memcpy(rte_pktmbuf_mtod_offset(
> +                                          mbuf, char *,
> +                                          sizeof(struct rte_ether_hdr)),
> +                                  &ip_hdr, sizeof(struct rte_ipv4_hdr));
> +
> +                       /* Copy UDP header */
> +                       rte_memcpy(
> +                               rte_pktmbuf_mtod_offset(
> +                                       mbuf, char *,
> +                                       sizeof(struct rte_ipv4_hdr) +
> +                                               sizeof(struct rte_ether_hdr)),
> +                               &udp_hdr, sizeof(struct rte_udp_hdr));
> +                       pkt_udp_hdr = rte_pktmbuf_mtod_offset(
> +                               mbuf, struct rte_udp_hdr *,
> +                               sizeof(struct rte_ipv4_hdr) +
> +                                       sizeof(struct rte_ether_hdr));
> +                       pkt_udp_hdr->src_port =
> +                               rte_cpu_to_be_16(UDP_SRC_PORT + i);
> +                       pkt_udp_hdr->dst_port =
> +                               rte_cpu_to_be_16(UDP_SRC_PORT + i);
> +
> +                       rc = rte_eth_tx_burst(eth_port_id, 0, &mbuf, 1);
> +                       if (rc == 0)
> +                               rte_pktmbuf_free(mbuf);
> +               }
> +       }
> +}
> +
>  int
>  pipeline_launch_lcores(struct evt_test *test, struct evt_options *opt,
>                 int (*worker)(void *))
>  {
> -       int ret, lcore_id;
>         struct test_pipeline *t = evt_test_priv(test);
> -
> +       int ret, lcore_id;
>         int port_idx = 0;
> +
> +       if (opt->tx_first)
> +               pipeline_tx_first(t, opt);
> +
>         /* launch workers */
>         RTE_LCORE_FOREACH_WORKER(lcore_id) {
>                 if (!(opt->wlcores[lcore_id]))
> @@ -155,6 +311,11 @@ pipeline_opt_check(struct evt_options *opt, uint64_t nb_queues)
>                 return -1;
>         }
>
> +       if (opt->prod_type != EVT_PROD_TYPE_ETH_RX_ADPTR) {
> +               evt_err("Invalid producer type, only --prod_type_ethdev is supported");
> +               return -1;
> +       }
> +
>         if (evt_has_invalid_stage(opt))
>                 return -1;
>
> diff --git a/app/test-eventdev/test_pipeline_common.h b/app/test-eventdev/test_pipeline_common.h
> index a6443faea4..2b7f3e7d0f 100644
> --- a/app/test-eventdev/test_pipeline_common.h
> +++ b/app/test-eventdev/test_pipeline_common.h
> @@ -12,6 +12,7 @@
>
>  #include <rte_cycles.h>
>  #include <rte_ethdev.h>
> +#include <rte_ether.h>
>  #include <rte_event_eth_rx_adapter.h>
>  #include <rte_event_eth_tx_adapter.h>
>  #include <rte_eventdev.h>
> @@ -22,6 +23,7 @@
>  #include <rte_service.h>
>  #include <rte_service_component.h>
>  #include <rte_spinlock.h>
> +#include <rte_udp.h>
>
>  #include "evt_common.h"
>  #include "evt_options.h"
> diff --git a/doc/guides/tools/testeventdev.rst b/doc/guides/tools/testeventdev.rst
> index f7d813226d..eba0030c44 100644
> --- a/doc/guides/tools/testeventdev.rst
> +++ b/doc/guides/tools/testeventdev.rst
> @@ -195,9 +195,20 @@ The following are the application command-line options:
>
>  * ``--per_port_pool``
>
> -        Configure unique mempool per ethernet device, the size of each pool
> -        is equal to `pool_sz`.
> -        Only applicable for pipeline_atq` and `pipeline_queue` tests.
> +       Configure unique mempool per ethernet device, the size of each pool
> +       is equal to `pool_sz`.
> +       Only applicable for `pipeline_atq` and `pipeline_queue` tests.
> +
> +* ``--tx_first``
> +
> +       Transmit given number of packets across all the ethernet device that
> +       are enabled in the test.
> +       Only applicable for `pipeline_atq` and `pipeline_queue` tests.
> +
> +* ``--tx_pkt_sz``
> +
> +       Packet size to use for `--tx_first`.
> +       Only applicable for `pipeline_atq` and `pipeline_queue` tests.
>
>
>  Eventdev Tests
> @@ -667,6 +678,8 @@ Supported application command line options are following::
>          --vector_size
>          --vector_tmo_ns
>          --per_port_pool
> +        --tx_first
> +        --tx_pkt_sz
>
>
>  .. Note::
> @@ -771,6 +784,8 @@ Supported application command line options are following::
>          --vector_size
>          --vector_tmo_ns
>          --per_port_pool
> +        --tx_first
> +        --tx_pkt_sz
>
>
>  .. Note::
> --
> 2.25.1
>
diff mbox series

Patch

diff --git a/app/test-eventdev/evt_common.h b/app/test-eventdev/evt_common.h
index 2f301a7e79..e304998cd7 100644
--- a/app/test-eventdev/evt_common.h
+++ b/app/test-eventdev/evt_common.h
@@ -65,6 +65,7 @@  struct evt_options {
 	uint16_t eth_queues;
 	uint32_t nb_flows;
 	uint32_t tx_first;
+	uint16_t tx_pkt_sz;
 	uint32_t max_pkt_sz;
 	uint32_t prod_enq_burst_sz;
 	uint32_t deq_tmo_nsec;
diff --git a/app/test-eventdev/evt_options.c b/app/test-eventdev/evt_options.c
index d3c704d2b3..fb17f5a159 100644
--- a/app/test-eventdev/evt_options.c
+++ b/app/test-eventdev/evt_options.c
@@ -106,6 +106,26 @@  evt_parse_eth_prod_type(struct evt_options *opt, const char *arg __rte_unused)
 	return 0;
 }
 
+static int
+evt_parse_tx_first(struct evt_options *opt, const char *arg __rte_unused)
+{
+	int ret;
+
+	ret = parser_read_uint32(&(opt->tx_first), arg);
+
+	return ret;
+}
+
+static int
+evt_parse_tx_pkt_sz(struct evt_options *opt, const char *arg __rte_unused)
+{
+	int ret;
+
+	ret = parser_read_uint16(&(opt->tx_pkt_sz), arg);
+
+	return ret;
+}
+
 static int
 evt_parse_timer_prod_type(struct evt_options *opt, const char *arg __rte_unused)
 {
@@ -376,6 +396,10 @@  usage(char *program)
 		"\t--vector_size      : Max vector size.\n"
 		"\t--vector_tmo_ns    : Max vector timeout in nanoseconds\n"
 		"\t--per_port_pool    : Configure unique pool per ethdev port\n"
+		"\t--tx_first         : Transmit given number of packets\n"
+		"                       across all the ethernet devices before\n"
+		"                       event workers start.\n"
+		"\t--tx_pkt_sz        : Packet size to use with Tx first."
 		);
 	printf("available tests:\n");
 	evt_test_dump_names();
@@ -456,6 +480,8 @@  static struct option lgopts[] = {
 	{ EVT_VECTOR_TMO,          1, 0, 0 },
 	{ EVT_PER_PORT_POOL,       0, 0, 0 },
 	{ EVT_HELP,                0, 0, 0 },
+	{ EVT_TX_FIRST,            1, 0, 0 },
+	{ EVT_TX_PKT_SZ,           1, 0, 0 },
 	{ NULL,                    0, 0, 0 }
 };
 
@@ -497,6 +523,8 @@  evt_opts_parse_long(int opt_idx, struct evt_options *opt)
 		{ EVT_VECTOR_SZ, evt_parse_vector_size},
 		{ EVT_VECTOR_TMO, evt_parse_vector_tmo_ns},
 		{ EVT_PER_PORT_POOL, evt_parse_per_port_pool},
+		{ EVT_TX_FIRST, evt_parse_tx_first},
+		{ EVT_TX_PKT_SZ, evt_parse_tx_pkt_sz},
 	};
 
 	for (i = 0; i < RTE_DIM(parsermap); i++) {
diff --git a/app/test-eventdev/evt_options.h b/app/test-eventdev/evt_options.h
index 2231c58801..4aa06976b7 100644
--- a/app/test-eventdev/evt_options.h
+++ b/app/test-eventdev/evt_options.h
@@ -51,6 +51,8 @@ 
 #define EVT_VECTOR_SZ            ("vector_size")
 #define EVT_VECTOR_TMO           ("vector_tmo_ns")
 #define EVT_PER_PORT_POOL	 ("per_port_pool")
+#define EVT_TX_FIRST		 ("tx_first")
+#define EVT_TX_PKT_SZ		 ("tx_pkt_sz")
 #define EVT_HELP                 ("help")
 
 void evt_options_default(struct evt_options *opt);
diff --git a/app/test-eventdev/test_pipeline_common.c b/app/test-eventdev/test_pipeline_common.c
index c66656cd39..82fe04258b 100644
--- a/app/test-eventdev/test_pipeline_common.c
+++ b/app/test-eventdev/test_pipeline_common.c
@@ -56,14 +56,170 @@  processed_pkts(struct test_pipeline *t)
 	return total;
 }
 
+/* RFC863 discard port */
+#define UDP_SRC_PORT 9
+#define UDP_DST_PORT 9
+
+/* RFC2544 reserved test subnet 192.18.0.0 */
+#define IP_SRC_ADDR(x, y) ((192U << 24) | (18 << 16) | ((x) << 8) | (y))
+#define IP_DST_ADDR(x, y) ((192U << 24) | (18 << 16) | ((x) << 8) | (y))
+
+#define IP_DEFTTL  64 /* from RFC 1340. */
+#define IP_VERSION 0x40
+#define IP_HDRLEN  0x05 /* default IP header length == five 32-bits words. */
+#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN)
+
+static void
+setup_pkt_udp_ip_headers(struct rte_ipv4_hdr *ip_hdr,
+			 struct rte_udp_hdr *udp_hdr, uint16_t pkt_data_len,
+			 uint8_t port, uint8_t flow)
+{
+	uint16_t *ptr16;
+	uint32_t ip_cksum;
+	uint16_t pkt_len;
+
+	/*
+	 * Initialize UDP header.
+	 */
+	pkt_len = (uint16_t)(pkt_data_len + sizeof(struct rte_udp_hdr));
+	udp_hdr->src_port = rte_cpu_to_be_16(UDP_SRC_PORT);
+	udp_hdr->dst_port = rte_cpu_to_be_16(UDP_DST_PORT);
+	udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len);
+	udp_hdr->dgram_cksum = 0; /* No UDP checksum. */
+
+	/*
+	 * Initialize IP header.
+	 */
+	pkt_len = (uint16_t)(pkt_len + sizeof(struct rte_ipv4_hdr));
+	ip_hdr->version_ihl = IP_VHL_DEF;
+	ip_hdr->type_of_service = 0;
+	ip_hdr->fragment_offset = 0;
+	ip_hdr->time_to_live = IP_DEFTTL;
+	ip_hdr->next_proto_id = IPPROTO_UDP;
+	ip_hdr->packet_id = 0;
+	ip_hdr->total_length = rte_cpu_to_be_16(pkt_len);
+	ip_hdr->src_addr = rte_cpu_to_be_32(IP_SRC_ADDR(port, 1));
+	ip_hdr->dst_addr = rte_cpu_to_be_32(IP_DST_ADDR(port + 1, flow));
+
+	/*
+	 * Compute IP header checksum.
+	 */
+	ptr16 = (unaligned_uint16_t *)ip_hdr;
+	ip_cksum = 0;
+	ip_cksum += ptr16[0];
+	ip_cksum += ptr16[1];
+	ip_cksum += ptr16[2];
+	ip_cksum += ptr16[3];
+	ip_cksum += ptr16[4];
+	ip_cksum += ptr16[6];
+	ip_cksum += ptr16[7];
+	ip_cksum += ptr16[8];
+	ip_cksum += ptr16[9];
+
+	/*
+	 * Reduce 32 bit checksum to 16 bits and complement it.
+	 */
+	ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + (ip_cksum & 0x0000FFFF);
+	if (ip_cksum > 65535)
+		ip_cksum -= 65535;
+	ip_cksum = (~ip_cksum) & 0x0000FFFF;
+	if (ip_cksum == 0)
+		ip_cksum = 0xFFFF;
+	ip_hdr->hdr_checksum = (uint16_t)ip_cksum;
+}
+
+static void
+pipeline_tx_first(struct test_pipeline *t, struct evt_options *opt)
+{
+#define TX_DEF_PACKET_LEN 64
+	uint16_t eth_port_id = 0;
+	uint16_t pkt_sz, rc;
+	uint32_t i;
+
+	pkt_sz = opt->tx_pkt_sz;
+	if (pkt_sz > opt->max_pkt_sz)
+		pkt_sz = opt->max_pkt_sz;
+	if (!pkt_sz)
+		pkt_sz = TX_DEF_PACKET_LEN;
+
+	RTE_ETH_FOREACH_DEV(eth_port_id) {
+		struct rte_ether_addr src_mac;
+		struct rte_ether_addr dst_mac;
+		struct rte_ether_hdr eth_hdr;
+
+		/* Send to the same dest.mac as port mac */
+		rte_eth_macaddr_get(eth_port_id, &dst_mac);
+		rte_eth_random_addr((uint8_t *)&src_mac);
+
+		rte_ether_addr_copy(&dst_mac, &eth_hdr.dst_addr);
+		rte_ether_addr_copy(&src_mac, &eth_hdr.src_addr);
+		eth_hdr.ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
+
+		for (i = 0; i < opt->tx_first; i++) {
+			struct rte_udp_hdr *pkt_udp_hdr;
+			struct rte_ipv4_hdr ip_hdr;
+			struct rte_udp_hdr udp_hdr;
+			struct rte_mbuf *mbuf;
+
+			mbuf = rte_pktmbuf_alloc(
+				opt->per_port_pool ? t->pool[i] : t->pool[0]);
+			if (mbuf == NULL)
+				continue;
+
+			setup_pkt_udp_ip_headers(
+				&ip_hdr, &udp_hdr,
+				pkt_sz - sizeof(struct rte_ether_hdr) -
+					sizeof(struct rte_ipv4_hdr) -
+					sizeof(struct rte_udp_hdr),
+				eth_port_id, i);
+			mbuf->port = eth_port_id;
+			mbuf->data_len = pkt_sz;
+			mbuf->pkt_len = pkt_sz;
+
+			/* Copy Ethernet header */
+			rte_memcpy(rte_pktmbuf_mtod_offset(mbuf, char *, 0),
+				   &eth_hdr, sizeof(struct rte_ether_hdr));
+
+			/* Copy Ipv4 header */
+			rte_memcpy(rte_pktmbuf_mtod_offset(
+					   mbuf, char *,
+					   sizeof(struct rte_ether_hdr)),
+				   &ip_hdr, sizeof(struct rte_ipv4_hdr));
+
+			/* Copy UDP header */
+			rte_memcpy(
+				rte_pktmbuf_mtod_offset(
+					mbuf, char *,
+					sizeof(struct rte_ipv4_hdr) +
+						sizeof(struct rte_ether_hdr)),
+				&udp_hdr, sizeof(struct rte_udp_hdr));
+			pkt_udp_hdr = rte_pktmbuf_mtod_offset(
+				mbuf, struct rte_udp_hdr *,
+				sizeof(struct rte_ipv4_hdr) +
+					sizeof(struct rte_ether_hdr));
+			pkt_udp_hdr->src_port =
+				rte_cpu_to_be_16(UDP_SRC_PORT + i);
+			pkt_udp_hdr->dst_port =
+				rte_cpu_to_be_16(UDP_SRC_PORT + i);
+
+			rc = rte_eth_tx_burst(eth_port_id, 0, &mbuf, 1);
+			if (rc == 0)
+				rte_pktmbuf_free(mbuf);
+		}
+	}
+}
+
 int
 pipeline_launch_lcores(struct evt_test *test, struct evt_options *opt,
 		int (*worker)(void *))
 {
-	int ret, lcore_id;
 	struct test_pipeline *t = evt_test_priv(test);
-
+	int ret, lcore_id;
 	int port_idx = 0;
+
+	if (opt->tx_first)
+		pipeline_tx_first(t, opt);
+
 	/* launch workers */
 	RTE_LCORE_FOREACH_WORKER(lcore_id) {
 		if (!(opt->wlcores[lcore_id]))
@@ -155,6 +311,11 @@  pipeline_opt_check(struct evt_options *opt, uint64_t nb_queues)
 		return -1;
 	}
 
+	if (opt->prod_type != EVT_PROD_TYPE_ETH_RX_ADPTR) {
+		evt_err("Invalid producer type, only --prod_type_ethdev is supported");
+		return -1;
+	}
+
 	if (evt_has_invalid_stage(opt))
 		return -1;
 
diff --git a/app/test-eventdev/test_pipeline_common.h b/app/test-eventdev/test_pipeline_common.h
index a6443faea4..2b7f3e7d0f 100644
--- a/app/test-eventdev/test_pipeline_common.h
+++ b/app/test-eventdev/test_pipeline_common.h
@@ -12,6 +12,7 @@ 
 
 #include <rte_cycles.h>
 #include <rte_ethdev.h>
+#include <rte_ether.h>
 #include <rte_event_eth_rx_adapter.h>
 #include <rte_event_eth_tx_adapter.h>
 #include <rte_eventdev.h>
@@ -22,6 +23,7 @@ 
 #include <rte_service.h>
 #include <rte_service_component.h>
 #include <rte_spinlock.h>
+#include <rte_udp.h>
 
 #include "evt_common.h"
 #include "evt_options.h"
diff --git a/doc/guides/tools/testeventdev.rst b/doc/guides/tools/testeventdev.rst
index f7d813226d..eba0030c44 100644
--- a/doc/guides/tools/testeventdev.rst
+++ b/doc/guides/tools/testeventdev.rst
@@ -195,9 +195,20 @@  The following are the application command-line options:
 
 * ``--per_port_pool``
 
-        Configure unique mempool per ethernet device, the size of each pool
-        is equal to `pool_sz`.
-        Only applicable for pipeline_atq` and `pipeline_queue` tests.
+       Configure unique mempool per ethernet device, the size of each pool
+       is equal to `pool_sz`.
+       Only applicable for `pipeline_atq` and `pipeline_queue` tests.
+
+* ``--tx_first``
+
+       Transmit given number of packets across all the ethernet device that
+       are enabled in the test.
+       Only applicable for `pipeline_atq` and `pipeline_queue` tests.
+
+* ``--tx_pkt_sz``
+
+       Packet size to use for `--tx_first`.
+       Only applicable for `pipeline_atq` and `pipeline_queue` tests.
 
 
 Eventdev Tests
@@ -667,6 +678,8 @@  Supported application command line options are following::
         --vector_size
         --vector_tmo_ns
         --per_port_pool
+        --tx_first
+        --tx_pkt_sz
 
 
 .. Note::
@@ -771,6 +784,8 @@  Supported application command line options are following::
         --vector_size
         --vector_tmo_ns
         --per_port_pool
+        --tx_first
+        --tx_pkt_sz
 
 
 .. Note::