diff mbox

[dpdk-dev,RFC,v1,7/9] app/testpmd: add pktgen engine scapy commands

Message ID 20171208082225.44913-8-xuemingl@mellanox.com (mailing list archive)
State RFC, archived
Headers show

Checks

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

Commit Message

Xueming Li Dec. 8, 2017, 8:22 a.m. UTC
new commands:
tx: send packets
rx: receive packets
expect: send and expect back

Signed-off-by: Xueming Li <xuemingl@mellanox.com>

Conflicts:
	app/test-pmd/Makefile
	app/test-pmd/testpmd.h
---
 app/test-pmd/Makefile  |   5 +
 app/test-pmd/cmdline.c |  14 +-
 app/test-pmd/pktgen.c  | 493 ++++++++++++++++++++++++++++++++++++++++++++++++-
 app/test-pmd/testpmd.h |   4 +
 4 files changed, 512 insertions(+), 4 deletions(-)
diff mbox

Patch

diff --git a/app/test-pmd/Makefile b/app/test-pmd/Makefile
index 2610a8d49..462f97418 100644
--- a/app/test-pmd/Makefile
+++ b/app/test-pmd/Makefile
@@ -65,6 +65,11 @@  SRCS-$(CONFIG_RTE_LIBRTE_IEEE1588) += ieee1588fwd.c
 ifeq ($(CONFIG_RTE_LIBRTE_PMD_SOFTNIC)$(CONFIG_RTE_LIBRTE_SCHED),yy)
 SRCS-y += tm.c
 endif
+ifeq ($(CONFIG_RTE_LIBRTE_PYTHON),y)
+LDLIBS += -l$(CONFIG_RTE_LIBRTE_PYTHON_VERSION)
+CFLAGS += -I/usr/include/$(CONFIG_RTE_LIBRTE_PYTHON_VERSION)
+endif
+
 
 ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y)
 
diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index d0eb00c1f..b40fe1ac7 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -105,7 +105,7 @@ 
 #include "cmdline_mtr.h"
 #include "cmdline_tm.h"
 
-static struct cmdline *testpmd_cl;
+struct cmdline *testpmd_cl;
 
 static void cmd_reconfig_device_queue(portid_t id, uint8_t dev, uint8_t queue);
 
@@ -15595,6 +15595,12 @@  cmdline_parse_inst_t cmd_py_run = {
 /* ******************************************************************************** */
 
 extern cmdline_parse_inst_t cmd_pktgen_cmd;
+extern cmdline_parse_inst_t cmd_expect_short;
+extern cmdline_parse_inst_t cmd_expect;
+extern cmdline_parse_inst_t cmd_tx;
+extern cmdline_parse_inst_t cmd_tx_short;
+extern cmdline_parse_inst_t cmd_rx;
+extern cmdline_parse_inst_t cmd_rx_short;
 
 /* list of instructions */
 cmdline_parse_ctx_t main_ctx[] = {
@@ -15604,6 +15610,12 @@  cmdline_parse_ctx_t main_ctx[] = {
 	(cmdline_parse_inst_t *)&cmd_load_from_file,
 #ifdef RTE_LIBRTE_PYTHON
 	(cmdline_parse_inst_t *)&cmd_py_run,
+	(cmdline_parse_inst_t *)&cmd_expect_short,
+	(cmdline_parse_inst_t *)&cmd_expect,
+	(cmdline_parse_inst_t *)&cmd_tx_short,
+	(cmdline_parse_inst_t *)&cmd_tx,
+	(cmdline_parse_inst_t *)&cmd_rx_short,
+	(cmdline_parse_inst_t *)&cmd_rx,
 #endif
 	(cmdline_parse_inst_t *)&cmd_pktgen_cmd,
 	(cmdline_parse_inst_t *)&cmd_showport,
diff --git a/app/test-pmd/pktgen.c b/app/test-pmd/pktgen.c
index f4a1b00a9..423459b37 100644
--- a/app/test-pmd/pktgen.c
+++ b/app/test-pmd/pktgen.c
@@ -83,7 +83,6 @@ 
 #define PKTGEN_ROUND_END 0x1
 #define PKTGEN_TASK_END 0x0
 #define PKTGEN_TASK_START 0x3
-
 struct pktgen_task_stats{
 	uint8_t active;
 	uint16_t round;		/* number txrx */
@@ -566,6 +565,495 @@  struct fwd_engine pktgen_engine = {
 /* Control thread functions                             */
 /********************************************************/
 
+#ifdef RTE_LIBRTE_PYTHON
+
+#define US_TSC(us) ((us) * (rte_get_timer_hz() / 1000000L));
+#define TSC_US(tsc) ((tsc) * 1e6 / rte_get_timer_hz())
+
+struct cmd_pktgen_cmd {
+	cmdline_fixed_string_t cmd;
+	cmdline_fixed_string_t pattern;
+	portid_t port;
+	uint64_t count;
+	uint64_t round;
+	uint16_t timeout; /* unit: ms */
+	uint16_t verbose;
+	struct pktgen_task_stats stats;
+};
+
+struct cmd_pg_txrx_cmd {
+	cmdline_fixed_string_t expect;
+	struct cmd_pktgen_cmd tx;
+	struct cmd_pktgen_cmd rx;
+	cmdline_fixed_string_t field;
+	uint64_t val;
+};
+
+/*
+ * get min/max time and count sum
+ */
+static void
+cmd_pg_port_poll(portid_t port, struct pktgen_task_stats *sum, int tx)
+{
+	struct pktgen_task* task;
+	queueid_t i, n;
+
+	n = tx ? nb_txq : nb_rxq;
+	for (i = 0, sum->count = 0; i < n; i++) {
+		task = tx ? task_tx(port, i) : task_rx(port, i);
+		sum->count += task->stats.count;
+		if (task->stats.round > sum->round)
+			sum->round = task->stats.round;
+		if (task->stats.start) {
+			if (sum->start && task->stats.start < sum->start)
+				sum->start = task->stats.start;
+			if (!sum->start)
+				sum->start = task->stats.start;
+			if (sum->end < task->stats.end)
+				sum->end = task->stats.end;
+		}
+	}
+}
+
+static int
+cmd_pg_scapy_to_mbuf(char *scapy, portid_t port, struct pktgen_task *task)
+{
+	int socket;
+	struct rte_mempool *pool;
+
+	socket = port_numa[port];
+	if (socket == NUMA_NO_CONFIG)
+		socket = ports[port].socket_id;
+	pool = mbuf_pool_find(socket);
+	task->data = rte_python_scapy_to_mbufs(pool, scapy, &task->cnt_mbufs);
+	return !task->data;
+}
+
+static inline int
+cmd_pg_init(void)
+{
+	if (rte_python_init())
+		return -1;
+	if (pktgen_idle_mode != 0) {
+		pg_idle_set(0);
+		rte_delay_ms(1);
+	}
+	if (cur_fwd_eng != &pktgen_engine) {
+		set_pkt_forwarding_mode(pktgen_engine.fwd_mode_name);
+		if (!test_done)
+			stop_packet_forwarding();
+	}
+	if (test_done)
+		start_packet_forwarding(0);
+	/* reset task memory */
+	RTE_ASSERT(pktgen_tx_tasks && pktgen_rx_tasks);
+	memset(pktgen_tx_tasks, 0,
+			nb_ports * nb_txq * sizeof(struct pktgen_task));
+	memset(pktgen_rx_tasks, 0,
+			nb_ports * nb_rxq * sizeof(struct pktgen_task));
+	return 0;
+}
+
+static void
+cmd_pg_cleanup(struct cmd_pg_txrx_cmd *cmd)
+{
+	struct pktgen_task *task;
+	queueid_t q;
+	uint16_t m;
+
+	RTE_ASSERT(pktgen_tx_tasks && pktgen_rx_tasks);
+	/* free all tx queue mbufs */
+	for (q = 0; cmd->tx.count && q < nb_txq; q++) {
+		task = task_tx(cmd->tx.port, q);
+		if (!task->data)
+			continue;
+		m = task->cnt_mbufs;
+		while(m)
+			rte_pktmbuf_free(pg_task_template_get(task, --m));
+		task->data = NULL;
+	}
+}
+
+static void
+cmd_pg_rx(struct cmd_pg_txrx_cmd *cmd)
+{
+	struct pktgen_task *task;
+	int i;
+
+	RTE_ASSERT(cmd);
+	memset(&cmd->rx.stats, 0, sizeof(cmd->rx.stats));
+	for (i = 0; i < nb_rxq; i++) {
+		task = task_rx(cmd->rx.port, i);
+		if (cmd->tx.count)
+			task->data = task_tx(cmd->tx.port, 0);
+		task->count = cmd->rx.count;
+		task->round = cmd->rx.round;
+		task->verbose = cmd->rx.verbose & 0xff;
+		if (cmd->field && strlen(cmd->field)) {
+				task->field = cmd->field;
+				task->val = cmd->val;
+		}
+		task->stats.active = PKTGEN_TASK_START;
+	}
+}
+
+static int
+cmd_pg_tx(struct cmd_pktgen_cmd* cmd, int txrx)
+{
+	struct pktgen_task *task;
+
+	RTE_ASSERT(cmd);
+	task = task_tx(cmd->port, 0);
+	if (cmd_pg_scapy_to_mbuf(cmd->pattern, cmd->port, task))
+		return -1;
+	/* send out using queue 0 */
+	memset(&cmd->stats, 0, sizeof(cmd->stats));
+	if (cmd->count == UINT64_MAX)
+		cmd->count = task->cnt_mbufs;
+	task->count = cmd->count;
+	task->round = cmd->round;
+	task->verbose = cmd->verbose & 0xff;
+	task->txrx = txrx;
+	task->stats.active = PKTGEN_TASK_START;
+	return 0;
+}
+
+static void
+cmd_pg_wait(struct cmdline *cl, struct cmd_pktgen_cmd* cmd,
+		uint64_t timeout, int tx)
+{
+	char c = 0;
+	int flags;
+
+	flags = fcntl(cl->s_in, F_GETFL, 0);
+	RTE_ASSERT(flags >= 0);
+	fcntl(cl->s_in, F_SETFL, flags | O_NONBLOCK);
+	memset(&cmd->stats, 0, sizeof(cmd->stats));
+	while (1) {
+		cmd_pg_port_poll(cmd->port, &cmd->stats, tx);
+		if (cmd->count && cmd->round == cmd->stats.round)
+			break;
+		if (timeout && rte_rdtsc() > timeout)
+			break;
+		/* detect ctrl+c or ctrl+d */
+		if (!timeout && read(cl->s_in, &c, 1) && (c == 3 || c == 4))
+			break;
+		rte_delay_ms(1);
+	}
+	if (!cmd->stats.end)
+		cmd->stats.end = rte_rdtsc();
+	fcntl(cl->s_in, F_SETFL, flags);
+}
+
+/* expect command */
+cmdline_parse_token_string_t cmd_expect_cmd =
+	TOKEN_STRING_INITIALIZER(struct cmd_pg_txrx_cmd, expect, "expect");
+cmdline_parse_token_num_t cmd_expect_tx_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, tx.port, UINT16);
+cmdline_parse_token_num_t cmd_expect_rx_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, rx.port, UINT16);
+cmdline_parse_token_string_t cmd_expect_pattern =
+	TOKEN_STRING_INITIALIZER(struct cmd_pg_txrx_cmd, tx.pattern, NULL);
+cmdline_parse_token_num_t cmd_expect_count =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, tx.count, UINT64);
+cmdline_parse_token_num_t cmd_expect_round =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, tx.round, UINT64);
+cmdline_parse_token_num_t cmd_expect_rx_timeout =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, rx.timeout, UINT64);
+cmdline_parse_token_num_t cmd_expect_verbose =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, tx.verbose, UINT16);
+cmdline_parse_token_string_t cmd_expect_field =
+	TOKEN_STRING_INITIALIZER(struct cmd_pg_txrx_cmd, field, NULL);
+cmdline_parse_token_num_t cmd_expect_val =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, val, UINT64);
+
+static void
+cmd_expect_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	__attribute__((unused)) void *data)
+{
+	struct cmd_pg_txrx_cmd *cmd = parsed_result;
+	struct pktgen_task_stats *stats_tx = &cmd->tx.stats;
+	struct pktgen_task_stats *stats_rx = &cmd->rx.stats;
+	uint64_t timeout = 0;
+
+	if (port_id_is_invalid(cmd->tx.port, ENABLED_WARN) ||
+		port_id_is_invalid(cmd->rx.port, ENABLED_WARN))
+		return;
+	if (cmd_pg_init())
+		return;
+	if (!strcmp(cmd->field, "non") || !strcmp(cmd->field, "0"))
+		cmd->field[0] = 0;
+	cmd->rx.verbose = cmd->tx.verbose & 0xff;
+	cmd->tx.verbose = cmd->tx.verbose >> 8;
+	cmd->rx.round = cmd->tx.round;
+	cmd->rx.count = cmd->tx.count;
+	if (cmd->tx.count == 0)
+		cmd->tx.count = UINT64_MAX;
+	/* prepare task */
+	if (cmd_pg_tx(&cmd->tx, 1))
+		return;
+	if (cmd->rx.count == UINT64_MAX)
+		cmd->rx.count = cmd->tx.count;
+	cmd_pg_rx(cmd);
+	/* wait task */
+	if (cmd->rx.timeout)
+		timeout = rte_rdtsc() + US_TSC(cmd->rx.timeout * 1000) ;
+	pktgen_busy = 1;
+	cmd_pg_wait(cl, &cmd->tx, timeout, 1);
+	cmd_pg_wait(cl, &cmd->rx, timeout, 0);
+	pktgen_busy = 0;
+	/* print stats */
+	float t_tx = TSC_US(stats_tx->end - stats_tx->start);
+	float t_rx = TSC_US(stats_rx->end - stats_rx->start);
+	float t_ttl = TSC_US(RTE_MAX(stats_rx->end, stats_tx->end) -
+			RTE_MIN(stats_rx->start, stats_tx->start));
+	if (cmd->rx.count == 0)
+		stats_rx->round = 1;
+	int failed = (stats_tx->round != cmd->tx.round ||
+			stats_rx->round != cmd->rx.round ||
+			stats_rx->count != cmd->rx.count);
+	if (stats_tx->round == 0)
+		stats_tx->round = 1;
+	if (stats_rx->round == 0)
+		stats_rx->round = 1;
+	uint64_t nb_tx = stats_tx->count + cmd->tx.count * (stats_tx->round - 1);
+	uint64_t nb_rx = stats_rx->count + cmd->rx.count * (stats_rx->round - 1);
+	if (failed || !(verbose_level & 0x40)) /* mute */
+		printf("%s"
+			"tx: %lu/%lu %.3fus %fmpps"
+			"\trx: %lu/%lu %.3fus %fmpps"
+			"\tround: %u/%lu %.3fus"
+			"\ttotal: %.3fus %fmpps\n",
+			failed ? "Failed " : "",
+			nb_tx, cmd->tx.count * cmd->tx.round, t_tx, nb_tx/t_tx,
+			nb_rx, cmd->rx.count * cmd->rx.round, t_rx, nb_rx/t_rx,
+			stats_rx->round, cmd->rx.round, t_ttl / stats_rx->round,
+			t_ttl, nb_rx / t_ttl);
+	/* clean up */
+	cmd_pg_cleanup(cmd);
+}
+
+cmdline_parse_inst_t cmd_expect = {
+	.f = cmd_expect_parsed,
+	.data = NULL,
+	.help_str = "expect <tx_port> <rx_port> <scapy> <count> <round> <timeout(ms)> <verbose> <field> <val>\n"
+			"\t\t\tSend packet and expecting same back",
+	.tokens = {
+		(void *)&cmd_expect_cmd,
+		(void *)&cmd_expect_tx_port,
+		(void *)&cmd_expect_rx_port,
+		(void *)&cmd_expect_pattern,
+		(void *)&cmd_expect_count,
+		(void *)&cmd_expect_round,
+		(void *)&cmd_expect_rx_timeout,
+		(void *)&cmd_expect_verbose,
+		(void *)&cmd_expect_field,
+		(void *)&cmd_expect_val,
+		NULL,
+	},
+};
+
+static void
+cmd_expect_short_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	__attribute__((unused)) void *data)
+{
+	struct cmd_pg_txrx_cmd *res = parsed_result;
+
+	/* memory not clean, have to clear unused fields */
+	res->tx.round = 1;
+	res->tx.count = UINT64_MAX; /* detect from pattern */
+	res->tx.verbose = verbose_level;
+	res->rx.timeout = 20;
+	res->rx.verbose = verbose_level & 0xff;
+	res->field[0] = 0;
+	cmd_expect_parsed(res, cl, data);
+
+}
+
+cmdline_parse_inst_t cmd_expect_short = {
+	.f = cmd_expect_short_parsed,
+	.data = NULL,
+	.help_str = "expect <tx_port> <rx_port> <scapy>: tx 1 and expect 1",
+	.tokens = {
+		(void *)&cmd_expect_cmd,
+		(void *)&cmd_expect_tx_port,
+		(void *)&cmd_expect_rx_port,
+		(void *)&cmd_expect_pattern,
+		NULL,
+	},
+};
+
+/* tx command */
+
+cmdline_parse_token_string_t cmd_tx_cmd =
+	TOKEN_STRING_INITIALIZER(struct cmd_pg_txrx_cmd, tx.cmd, "tx");
+cmdline_parse_token_string_t cmd_tx_pattern =
+	TOKEN_STRING_INITIALIZER(struct cmd_pg_txrx_cmd, tx.pattern, NULL);
+cmdline_parse_token_num_t cmd_tx_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, tx.port, UINT16);
+cmdline_parse_token_num_t cmd_tx_count =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, tx.count, UINT64);
+cmdline_parse_token_num_t cmd_tx_verbose =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, tx.verbose, UINT16);
+
+static void
+cmd_tx_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	__attribute__((unused)) void *data)
+{
+	struct cmd_pg_txrx_cmd *cmd = parsed_result;
+
+	if (port_id_is_invalid(cmd->tx.port, ENABLED_WARN))
+		return;
+	if (cmd_pg_init())
+		return;
+	memset(&cmd->rx, 0, sizeof(cmd->rx));
+	cmd->tx.round = 1;
+	if (cmd_pg_tx(&cmd->tx, 0))
+		return;
+	pktgen_busy = 1;
+	cmd_pg_wait(cl, &cmd->tx, 0, 1);
+	pktgen_busy = 0;
+	double t = TSC_US(cmd->tx.stats.end - cmd->tx.stats.start);
+	printf("%s%lu/%lu packets sent in %.3fus %fmpps\n",
+			cmd->tx.count && cmd->tx.stats.count != cmd->tx.count ?
+					"Failed: " : "",
+			cmd->tx.stats.count, cmd->tx.count, t,
+			cmd->tx.stats.count / t);
+	cmd_pg_cleanup(cmd);
+}
+
+cmdline_parse_inst_t cmd_tx = {
+	.f = cmd_tx_parsed,
+	.data = NULL,
+	.help_str = "tx <port> <scapy> <count> <verbose>",
+	.tokens = {
+		(void *)&cmd_tx_cmd,
+		(void *)&cmd_tx_port,
+		(void *)&cmd_tx_pattern,
+		(void *)&cmd_tx_count,
+		(void *)&cmd_tx_verbose,
+		NULL,
+	},
+};
+
+static void
+cmd_tx_short_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	__attribute__((unused)) void *data)
+{
+	struct cmd_pg_txrx_cmd *cmd = parsed_result;
+
+	cmd->tx.count = 0;
+	cmd->tx.verbose = verbose_level >> 8;
+	cmd_tx_parsed(cmd, cl, data);
+}
+
+cmdline_parse_inst_t cmd_tx_short = {
+	.f = cmd_tx_short_parsed,
+	.data = NULL,
+	.help_str = "tx <port> <scapy>: tx 0 Ether()/IP()/UDP()",
+	.tokens = {
+		(void *)&cmd_tx_cmd,
+		(void *)&cmd_tx_port,
+		(void *)&cmd_tx_pattern,
+		NULL,
+	},
+};
+
+/* rx command */
+cmdline_parse_token_string_t cmd_rx_cmd =
+	TOKEN_STRING_INITIALIZER(struct cmd_pg_txrx_cmd, rx, "rx");
+cmdline_parse_token_num_t cmd_rx_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, rx.port, UINT16);
+cmdline_parse_token_num_t cmd_rx_count =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, rx.count, UINT64);
+cmdline_parse_token_num_t cmd_rx_timeout =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, rx.timeout, UINT16);
+cmdline_parse_token_num_t cmd_rx_verbose =
+	TOKEN_NUM_INITIALIZER(struct cmd_pg_txrx_cmd, rx.verbose, UINT16);
+
+/* Common result structure for rx commands */
+static void
+cmd_rx_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	__attribute__((unused)) void *data)
+{
+	struct cmd_pg_txrx_cmd *cmd = parsed_result;
+	uint64_t timeout = 0;
+
+	if (port_id_is_invalid(cmd->rx.port, ENABLED_WARN))
+		return;
+	if (cmd_pg_init())
+		return;
+	memset(&cmd->tx, 0, sizeof(cmd->tx));
+	cmd->field[0] = 0;
+	cmd->rx.round = 1;
+	cmd_pg_rx(cmd);
+	if (cmd->rx.timeout)
+		timeout =  rte_rdtsc() + US_TSC(cmd->rx.timeout * 1e6);
+	pktgen_busy = 1;
+	cmd_pg_wait(cl, &cmd->rx, timeout, 0);
+	pktgen_busy = 0;
+	/* print stats */
+	float t = TSC_US(cmd->rx.stats.end - cmd->rx.stats.start);
+	printf("%s%lu/%lu packets received in %.3fus %fmpps\n",
+			cmd->rx.count && cmd->rx.stats.count != cmd->rx.count ?
+					"Failed: " : "",
+			cmd->rx.stats.count, cmd->rx.count, t,
+			t ? cmd->rx.stats.count / t : 0);
+	/* clean up */
+	cmd_pg_cleanup(cmd);
+}
+
+cmdline_parse_inst_t cmd_rx = {
+	.f = cmd_rx_parsed,
+	.data = NULL,
+	.help_str = "rx <port> <count> <timeout(s)> <verbose>",
+	.tokens = {
+		(void *)&cmd_rx_cmd,
+		(void *)&cmd_rx_port,
+		(void *)&cmd_rx_count,
+		(void *)&cmd_rx_timeout,
+		(void *)&cmd_rx_verbose,
+		NULL,
+	},
+};
+
+static void
+cmd_rx_short_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	__attribute__((unused)) void *data)
+{
+	struct cmd_pg_txrx_cmd *cmd = parsed_result;
+
+	cmd->rx.count = 0; /* endless */
+	cmd->rx.timeout = 0; /* endless */
+	cmd->rx.verbose = verbose_level;
+	cmd_rx_parsed(cmd, cl, data);
+}
+
+cmdline_parse_inst_t cmd_rx_short = {
+	.f = cmd_rx_short_parsed,
+	.data = NULL,
+	.help_str = "rx <port>",
+	.tokens = {
+		(void *)&cmd_rx_cmd,
+		(void *)&cmd_rx_port,
+		NULL,
+	},
+};
+
+#endif
+
 /* "pktgen loopback" command */
 struct cmd_pktgen_cmd_result {
 	cmdline_fixed_string_t pktgen;
@@ -588,8 +1076,7 @@  cmd_pktgen_cmd_parsed(
 {
 	struct cmd_pktgen_cmd_result *res = parsed_result;
 
-	pktgen_idle_mode = res->mode;
-	printf("PktGen idle mode: %hhu\n", res->mode);
+	pg_idle_set(res->mode);
 }
 
 cmdline_parse_inst_t cmd_pktgen_cmd = {
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index ec83c212d..daa5d4e0c 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -34,6 +34,8 @@ 
 #ifndef _TESTPMD_H_
 #define _TESTPMD_H_
 
+#include <cmdline_parse.h>
+
 #include <rte_pci.h>
 #include <rte_bus_pci.h>
 #include <rte_gro.h>
@@ -365,6 +367,8 @@  extern uint8_t flow_isolate_all; /**< set by "--flow-isolate-all */
 extern uint8_t  mp_anon; /**< set by "--mp-anon" parameter */
 extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */
 extern volatile int test_done; /* stop packet forwarding when set to 1. */
+extern cmdline_parse_ctx_t main_ctx[]; /* command line parser */
+extern struct cmdline *testpmd_cl;
 extern uint8_t lsc_interrupt; /**< disabled by "--no-lsc-interrupt" parameter */
 extern uint8_t rmv_interrupt; /**< disabled by "--no-rmv-interrupt" parameter */
 extern uint32_t event_print_mask;