From patchwork Fri Dec 8 08:22:22 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Xueming Li X-Patchwork-Id: 32025 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 20FD31B1A0; Fri, 8 Dec 2017 09:23:16 +0100 (CET) Received: from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129]) by dpdk.org (Postfix) with ESMTP id 9630E1AEF0 for ; Fri, 8 Dec 2017 09:23:11 +0100 (CET) Received: from Internal Mail-Server by MTLPINE1 (envelope-from xuemingl@mellanox.com) with ESMTPS (AES256-SHA encrypted); 8 Dec 2017 10:23:08 +0200 Received: from dev-r630-06.mtbc.labs.mlnx (dev-r630-06.mtbc.labs.mlnx [10.12.205.180]) by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id vB88N80a001036; Fri, 8 Dec 2017 10:23:08 +0200 Received: from dev-r630-06.mtbc.labs.mlnx (localhost [127.0.0.1]) by dev-r630-06.mtbc.labs.mlnx (8.14.7/8.14.7) with ESMTP id vB88N7wq045198; Fri, 8 Dec 2017 16:23:07 +0800 Received: (from xuemingl@localhost) by dev-r630-06.mtbc.labs.mlnx (8.14.7/8.14.7/Submit) id vB88N7lV045197; Fri, 8 Dec 2017 16:23:07 +0800 From: Xueming Li To: Wu Jingjing , Harry van Haaren Cc: Xueming Li , dev@dpdk.org Date: Fri, 8 Dec 2017 16:22:22 +0800 Message-Id: <20171208082225.44913-7-xuemingl@mellanox.com> X-Mailer: git-send-email 2.13.3 In-Reply-To: <20171208082225.44913-1-xuemingl@mellanox.com> References: <20171208082225.44913-1-xuemingl@mellanox.com> In-Reply-To: <20171019140649.26668-2-xuemingl@mellanox.com> References: <20171019140649.26668-2-xuemingl@mellanox.com> Subject: [dpdk-dev] [RFC v1 6/9] app/testpmd: add pktgen forwarding engine X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Add new "pktgen" forwarding engine that send and receive packets acoording to pktgen_task instruction. Signed-off-by: Xueming Li --- app/test-pmd/Makefile | 1 + app/test-pmd/cmdline.c | 3 + app/test-pmd/pktgen.c | 605 +++++++++++++++++++++++++++++++++++++++++++++++++ app/test-pmd/testpmd.c | 1 + app/test-pmd/testpmd.h | 1 + 5 files changed, 611 insertions(+) create mode 100644 app/test-pmd/pktgen.c diff --git a/app/test-pmd/Makefile b/app/test-pmd/Makefile index d21308fcd..2610a8d49 100644 --- a/app/test-pmd/Makefile +++ b/app/test-pmd/Makefile @@ -55,6 +55,7 @@ SRCS-y += iofwd.c SRCS-y += macfwd.c SRCS-y += macswap.c SRCS-y += flowgen.c +SRCS-y += pktgen.c SRCS-y += rxonly.c SRCS-y += txonly.c SRCS-y += csumonly.c diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index 6654a62cb..d0eb00c1f 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -15594,6 +15594,8 @@ cmdline_parse_inst_t cmd_py_run = { /* ******************************************************************************** */ +extern cmdline_parse_inst_t cmd_pktgen_cmd; + /* list of instructions */ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_help_brief, @@ -15603,6 +15605,7 @@ cmdline_parse_ctx_t main_ctx[] = { #ifdef RTE_LIBRTE_PYTHON (cmdline_parse_inst_t *)&cmd_py_run, #endif + (cmdline_parse_inst_t *)&cmd_pktgen_cmd, (cmdline_parse_inst_t *)&cmd_showport, (cmdline_parse_inst_t *)&cmd_showqueue, (cmdline_parse_inst_t *)&cmd_showportall, diff --git a/app/test-pmd/pktgen.c b/app/test-pmd/pktgen.c new file mode 100644 index 000000000..f4a1b00a9 --- /dev/null +++ b/app/test-pmd/pktgen.c @@ -0,0 +1,605 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef RTE_LIBRTE_PYTHON +#include +#endif + +#include "testpmd.h" + +#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 */ + uint64_t count; + uint64_t start; /* start tsc */ + uint64_t end; /* end tsc */ +}; + +struct pktgen_task { + void *data; /* rx: tx; tx: mbuf[] */ + uint64_t count; + char *field; /* field to match */ + uint64_t val; /* field value */ + uint16_t round; /* number txrx */ + uint16_t cnt_mbufs; /* number of templates */ + uint8_t verbose:7; + uint8_t txrx:1; /* txrx type task */ + struct pktgen_task_stats stats; +} __rte_cache_aligned; + +static void *pktgen_tx_tasks; +static void *pktgen_rx_tasks; +static int pktgen_idle_mode = 2; /* 0-drop 1-loopback 2-forward 3-switch */ +static int pktgen_busy = 0; + +static inline struct rte_mbuf * +pg_task_template_get(struct pktgen_task* task, uint32_t idx) +{ + struct rte_mbuf **mbufs = task->data; + + return mbufs ? mbufs[idx % task->cnt_mbufs] : NULL; +} + +static inline struct pktgen_task * +task_tx(portid_t port, queueid_t queue) +{ + RTE_ASSERT(pktgen_tx_tasks); + struct pktgen_task (*tasks)[nb_txq] = pktgen_tx_tasks; + return &tasks[port][queue]; +} + +static inline struct pktgen_task * +task_rx(portid_t port, queueid_t queue) +{ + RTE_ASSERT(pktgen_rx_tasks); + struct pktgen_task (*tasks)[nb_rxq] = pktgen_rx_tasks; + return &tasks[port][queue]; +} + +/********************************************************/ +/* Forwarding thread functions */ +/********************************************************/ + +static inline void +pg_dump_mbuf_header(struct rte_mbuf *mbuf, struct fwd_stream* fs, int is_rx) { + char buf[256]; + + printf("%s P:%hu Q:%hu len:%hu ptype:0x%x ol_flags:0x%lx rss:0x%08x fdir:0x%x\n", + is_rx ? "RX" : "TX", + is_rx ? fs->rx_port : fs->tx_port, + is_rx ? fs->rx_queue : fs->tx_queue, + mbuf->data_len, mbuf->packet_type, + mbuf->ol_flags, + mbuf->hash.rss, mbuf->hash.fdir.hi + ); + if (mbuf->packet_type) { + rte_get_ptype_name(mbuf->packet_type, buf, sizeof(buf)); + printf(" ptype: %s\n", buf); + } + if (mbuf->tx_offload) + printf(" header len:%d/%d/%d/%d/%d tso len:%hu\n", + mbuf->outer_l2_len, mbuf->outer_l3_len, + mbuf->l2_len, mbuf->l3_len, mbuf->l4_len, + mbuf->tso_segsz); + else if (mbuf->ol_flags){ + rte_get_rx_ol_flag_list(mbuf->ol_flags, buf, sizeof(buf)); + printf(" ol_flags: %s\n", buf); + } +} + +static inline void +pg_debug(struct rte_mbuf *mbuf, uint8_t level, struct fwd_stream* fs, int is_rx) +{ + /* xxxx xxxx + * || L- 1: summary 2: repr 3:show + * |L----- mbuf header + * L------ hex dump + */ + if (level & 0x10) + pg_dump_mbuf_header(mbuf, fs, is_rx); +#ifdef RTE_LIBRTE_PYTHON + if (level) + rte_python_scapy_dump(mbuf, level); +#else + (void) mbuf; +#endif +} + +static int +pg_mbuf_field_expect(struct fwd_stream* fs, struct pktgen_task *task, + struct rte_mbuf *mbuf) +{ + #define OFF(field) offsetof(struct rte_mbuf, field) + unsigned int i; + uint64_t val; + static struct { + const char *name; + uint8_t offset; + uint8_t shift; + uint64_t mask; + } fields [] = { + {"port", OFF(port), 0, UINT16_MAX}, + {"ptype", OFF(packet_type), 0, UINT32_MAX}, + {"rss", OFF(hash.rss), 0, UINT32_MAX}, + {"fdir", OFF(hash.fdir.hi), 0, UINT32_MAX}, + /* ignore rss bit */ + {"ol_flags", OFF(ol_flags), 0, UINT64_MAX & + ~PKT_RX_RSS_HASH}, + }; + + RTE_ASSERT(task && mbuf); + if (!task->field || strlen(task->field) == 0) + return 0; + if (!strcmp(task->field, "queue")) { + if (fs->rx_queue != task->val) { + printf("Failed: queue expect: 0x%lu received:0x%hu\n", + task->val, fs->rx_queue); + return 1; + } else + return 0; + } + for (i = 0; i < RTE_DIM(fields); i++) { + if (strcmp(task->field, fields[i].name)) + continue; + val = *((uint64_t *)(void *)((char *)(void *)mbuf + fields[i].offset)); + val &= fields[i].mask; + if ((val != (task->val & fields[i].mask))) { + printf("Failed: %s mask: 0x%lx expect: 0x%lx received: 0x%lx\n", + fields[i].name, fields[i].mask, + task->val & fields[i].mask, val); + return 1; + } else + return 0; + } + printf("Failed: unknown field '%s', valid names: queue,", task->field); + for (i = 0; i < RTE_DIM(fields); i++) + printf("%s%s", fields[i].name, + i == RTE_DIM(fields) - 1 ? ",non(0)\n" : ","); + return 0; +} + +static int +pg_mbuf_expect(struct fwd_stream* fs, struct pktgen_task *task, + struct rte_mbuf *mbuf, uint32_t idx) +{ + int r = 0; + struct rte_mbuf *exp; + + RTE_ASSERT(task && task->data && mbuf); + exp = pg_task_template_get(task->data, idx); /* tx->mbuf */ + if (!exp) { + RTE_LOG(ERR, USER1, "packet tempalte not found, timeout?\n"); + return -1; + } + r |= pg_mbuf_field_expect(fs, task, mbuf); + if (exp->data_len != mbuf->data_len) { + printf("Failed: packet length not same: %hu/%hu", + mbuf->data_len, exp->data_len); + r |= 2; + } else if (memcmp( + rte_pktmbuf_mtod(mbuf, void *), + rte_pktmbuf_mtod(exp, void *), + mbuf->data_len)) { + printf("Failed: packet not same:\n"); + r |= 4; +#ifdef RTE_LIBRTE_PYTHON + rte_python_scapy_hexdiff( + rte_pktmbuf_mtod(exp, void *), exp->data_len, + rte_pktmbuf_mtod(mbuf, void *), mbuf->data_len); +#endif + } + return r; +} + +static inline void +pg_mbuf_switch(struct rte_mbuf **pkts, uint16_t nb_to_tx) +{ + uint32_t i; + struct ether_hdr *eth; + struct ether_addr addr; + + for (i = 0; i < nb_to_tx; i++) { + eth = rte_pktmbuf_mtod(pkts[i], struct ether_hdr *); + ether_addr_copy(ð->d_addr, &addr); + ether_addr_copy(ð->s_addr, ð->d_addr); + ether_addr_copy(&addr, ð->s_addr); + } +} + +static inline uint16_t +pg_tx_burst(struct fwd_stream* fs, int mode, + struct rte_mbuf **pkts_burst, uint16_t nb_to_tx) +{ + uint32_t retry; + uint16_t nb_tx, i; + portid_t port = mode == 1 ? fs->rx_port : fs->tx_port; + queueid_t queue = mode == 1 ? fs->rx_queue : fs->tx_queue; + + if (unlikely(mode == 3)) + pg_mbuf_switch(pkts_burst, nb_to_tx); + nb_tx = rte_eth_tx_burst(port, queue, pkts_burst, + nb_to_tx); + /* Retry if necessary */ + if (unlikely(nb_tx < nb_to_tx) && fs->retry_enabled) + { + retry = 0; + while (nb_tx < nb_to_tx && retry++ < burst_tx_retry_num) + { + rte_delay_us(burst_tx_delay_time); + nb_tx += rte_eth_tx_burst(port, queue, + &pkts_burst[nb_tx], nb_to_tx - nb_tx); + } + } + /* Drop packets failed to send */ + if (unlikely(nb_tx < nb_to_tx)) + { + fs->fwd_dropped += (nb_to_tx - nb_tx); + i = nb_tx; + do { + rte_pktmbuf_free(pkts_burst[i]); + } while (++i < nb_to_tx); + } + return nb_tx; +} + +static inline int +pg_tx_fill(struct rte_mbuf **pkts_burst, uint64_t nb_to_tx, + struct pktgen_task *task, struct fwd_stream* fs) +{ + uint32_t i; + struct rte_mbuf *exp; + + exp = pg_task_template_get(task, task->stats.count); + RTE_ASSERT(exp && exp->pool); + RTE_ASSERT(task->cnt_mbufs > 0); + if (rte_pktmbuf_alloc_bulk(exp->pool, + pkts_burst, nb_to_tx)) + return -1; + for (i = 0; i < nb_to_tx; i++) { + exp = pg_task_template_get(task, task->stats.count + i); + rte_memcpy(rte_pktmbuf_mtod(pkts_burst[i], void *), + rte_pktmbuf_mtod(exp, void *), + exp->data_len); + pkts_burst[i]->pkt_len = exp->pkt_len; + pkts_burst[i]->data_len = exp->data_len; + pkts_burst[i]->ol_flags = exp->ol_flags; + pg_debug(pkts_burst[i], task->verbose, fs, 0); + } + return 0; +} + +static inline int +pg_start(struct pktgen_task *task, uint64_t start_tsc) +{ + /* even round end, has to check tx stats */ + if (unlikely(task->stats.active == PKTGEN_TASK_END)) + return -1; + if (!task->stats.start) + task->stats.start = start_tsc; + return 0; +} + +static inline int +pg_round_end(struct pktgen_task *task) +{ + /* if not txrx task, keep busy */ + if (unlikely(task->txrx)) + task->stats.active = PKTGEN_ROUND_END; + task->stats.round += 1; + return task->round == task->stats.round; +} + +static inline void +pg_end(struct pktgen_task *task) +{ + task->stats.active = PKTGEN_TASK_END; + task->stats.end = rte_rdtsc(); +} + +/* return -1 if nothing to do */ +static inline int +pg_tx(struct fwd_stream* fs, struct pktgen_task *task, + uint64_t start_tsc) +{ + struct rte_mbuf *pkts_burst[nb_pkt_per_burst]; + uint64_t nb_to_tx = 0; + uint64_t nb_tx; + + if (pg_start(task, start_tsc)) + return -1; + if (unlikely(task->stats.active != PKTGEN_TASK_START)) + return -1; + if (task->count) { + nb_to_tx = task->count - task->stats.count; + if (nb_to_tx > nb_pkt_per_burst) + nb_to_tx = nb_pkt_per_burst; + } else + nb_to_tx = nb_pkt_per_burst; + if (likely((nb_to_tx && task->data))) { + if (unlikely((pg_tx_fill(pkts_burst, nb_to_tx, task, fs)))) + return -1; + nb_tx = pg_tx_burst(fs, 2, pkts_burst, nb_to_tx); + fs->tx_packets += nb_tx; + task->stats.count += nb_tx; + } + if (task->stats.count == task->count) { + if (pg_round_end(task)) /* end of taks? */ + pg_end(task); + } + return 0; +} + +/* return -1 if nothing to do */ +static inline int +pg_rx(struct fwd_stream* fs, struct pktgen_task *task, uint64_t start_tsc) +{ + struct rte_mbuf *pkts_burst[nb_pkt_per_burst]; + uint16_t nb_to_rx; + uint16_t nb_rx; + uint32_t i; + uint8_t verbose; + struct pktgen_task *tx_task; + int r; + + if (pg_start(task, start_tsc)) + return -1; + if (task->count) { + nb_to_rx = task->count - task->stats.count; + if (nb_to_rx > nb_pkt_per_burst) + nb_to_rx = nb_pkt_per_burst; + } else /* endless rx */ + nb_to_rx = nb_pkt_per_burst; + if (nb_to_rx && task->stats.active == PKTGEN_TASK_START) { + nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, + pkts_burst, nb_pkt_per_burst); +#ifdef RTE_TEST_PMD_RECORD_BURST_STATS + fs->rx_burst_stats.pkt_burst_spread[nb_rx]++; +#endif + if (unlikely(nb_rx == 0)) + return 0; + for (i = 0; i < nb_rx; i++) { + verbose = task->verbose; + if (task->data) { + r = pg_mbuf_expect(fs, task, pkts_burst[i], task->stats.count + i); + if (r < 0) /* task timeout */ + return r; + else if (r) /* compare failed, simple dump */ + verbose |= 0x10; + } + pg_debug(pkts_burst[i], verbose, fs, 1); + rte_pktmbuf_free(pkts_burst[i]); + } + fs->rx_packets += nb_rx; + task->stats.count += nb_rx; + } + if (task->count && task->stats.count >= task->count) { + tx_task = task->data; + if (task->stats.active == PKTGEN_TASK_START && pg_round_end(task)) + pg_end(task); + else if (tx_task && tx_task->stats.active==PKTGEN_ROUND_END) { + /* has tx task, next round */ + tx_task->stats.active = PKTGEN_TASK_START; + tx_task->stats.count = 0; + task->stats.active = PKTGEN_TASK_START; + task->stats.count = 0; + } + } + return 0; +} + +static void +pg_idle_set(int mode) +{ + const char *names[] = { + "drop(0)", + "loopback(1)", + "io_forward(2)", + "mac_switch(3)"}; + if (pktgen_idle_mode != mode) + printf("PktGen idle mode changed from %s to %s\n", + names[pktgen_idle_mode], names[mode]); + pktgen_idle_mode = mode; +} + +static inline int +pg_rx_idle(struct fwd_stream* fs) +{ + struct rte_mbuf *pkts_burst[nb_pkt_per_burst]; + uint16_t nb_rx; + uint32_t i; + + nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, + pkts_burst, nb_pkt_per_burst); +#ifdef RTE_TEST_PMD_RECORD_BURST_STATS + fs->rx_burst_stats.pkt_burst_spread[nb_rx]++; +#endif + if (unlikely(nb_rx == 0)) + return 0; + fs->rx_packets += nb_rx; + if (verbose_level & 0xff) + for (i = 0; i < nb_rx; i++) + pg_debug(pkts_burst[i], verbose_level & 0xff, fs, 1); + if (pktgen_idle_mode) /* no drop */ + pg_tx_burst(fs, pktgen_idle_mode, pkts_burst, nb_rx); + else /* drop */ + for (i = 0; i < nb_rx; i++) + rte_pktmbuf_free(pkts_burst[i]); + return 0; +} + +/* + * TX and RX pacets according to traffic generator command. + */ +static void +pg_fwd(struct fwd_stream *fs) +{ + uint64_t start; +#ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES + uint64_t end_tsc; + uint64_t core_cycles; +#endif + + start = rte_rdtsc(); + if (likely(pktgen_busy)) { + pg_tx(fs, task_tx(fs->tx_port, fs->tx_queue), start); + pg_rx(fs, task_rx(fs->rx_port, fs->rx_queue), start); + } else + pg_rx_idle(fs); +#ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES + end_tsc = rte_rdtsc(); + core_cycles = (end_tsc - start_tsc); + fs->core_cycles += fs->core_cycles + core_cycles; +#endif +} + +static void +pktgen_begin(portid_t pi __rte_unused) +{ + if (!pktgen_tx_tasks) + pktgen_tx_tasks = rte_malloc(NULL, + sizeof(struct pktgen_task) * nb_ports * nb_txq, 0); + if (!pktgen_tx_tasks) + RTE_LOG(ERR, USER1, "out of memory?\n"); + if (!pktgen_rx_tasks) + pktgen_rx_tasks = rte_malloc(NULL, + sizeof(struct pktgen_task) * nb_ports * nb_rxq, 0); + if (!pktgen_rx_tasks) + RTE_LOG(ERR, USER1, "out of memory?\n"); +} + +static void +pktgen_end(portid_t pi __rte_unused) +{ + if (pktgen_tx_tasks) + rte_free(pktgen_tx_tasks); + pktgen_tx_tasks = NULL; + if (pktgen_rx_tasks) + rte_free(pktgen_rx_tasks); + pktgen_rx_tasks = NULL; +} + +struct fwd_engine pktgen_engine = { + .fwd_mode_name = "pktgen", + .port_fwd_begin = pktgen_begin, + .port_fwd_end = pktgen_end, + .packet_fwd = pg_fwd, +}; + +/********************************************************/ +/* Control thread functions */ +/********************************************************/ + +/* "pktgen loopback" command */ +struct cmd_pktgen_cmd_result { + cmdline_fixed_string_t pktgen; + cmdline_fixed_string_t cmd; + uint8_t mode; +}; + +cmdline_parse_token_string_t cmd_pktgen_cmd_pktgen = + TOKEN_STRING_INITIALIZER(struct cmd_pktgen_cmd_result, pktgen, "pktgen"); +cmdline_parse_token_string_t cmd_pktgen_cmd_cmd = + TOKEN_STRING_INITIALIZER(struct cmd_pktgen_cmd_result, cmd, "idle"); +cmdline_parse_token_string_t cmd_pktgen_cmd_mode = + TOKEN_NUM_INITIALIZER(struct cmd_pktgen_cmd_result, mode, UINT8); + +static void +cmd_pktgen_cmd_parsed( + void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_pktgen_cmd_result *res = parsed_result; + + pktgen_idle_mode = res->mode; + printf("PktGen idle mode: %hhu\n", res->mode); +} + +cmdline_parse_inst_t cmd_pktgen_cmd = { + .f = cmd_pktgen_cmd_parsed, + .data = NULL, + .help_str = "pktgen idle : 0-drop 1-loopback 2-forward 3-switch", + .tokens = { + (void *)&cmd_pktgen_cmd_pktgen, + (void *)&cmd_pktgen_cmd_cmd, + (void *)&cmd_pktgen_cmd_mode, + NULL, + }, +}; diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index c3ab44849..2a50e522b 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -159,6 +159,7 @@ struct fwd_engine * fwd_engines[] = { &mac_fwd_engine, &mac_swap_engine, &flow_gen_engine, + &pktgen_engine, &rx_only_engine, &tx_only_engine, &csum_fwd_engine, diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 1639d27e7..ec83c212d 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -296,6 +296,7 @@ extern struct fwd_engine io_fwd_engine; extern struct fwd_engine mac_fwd_engine; extern struct fwd_engine mac_swap_engine; extern struct fwd_engine flow_gen_engine; +extern struct fwd_engine pktgen_engine; extern struct fwd_engine rx_only_engine; extern struct fwd_engine tx_only_engine; extern struct fwd_engine csum_fwd_engine;