> -----Original Message-----
> From: Ananyev, Konstantin
> Sent: Monday, November 09, 2015 1:45 PM
> To: De Lara Guarch, Pablo; dev@dpdk.org
> Subject: RE: [dpdk-dev] [PATCHv5 1/2] testpmd: add ability to split outgoing
> packets
>
>
>
> > -----Original Message-----
> > From: De Lara Guarch, Pablo
> > Sent: Monday, November 09, 2015 1:39 PM
> > To: Ananyev, Konstantin; dev@dpdk.org
> > Subject: RE: [dpdk-dev] [PATCHv5 1/2] testpmd: add ability to split
> outgoing packets
> >
> > Hi Konstantin,
> >
> > > -----Original Message-----
> > > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Konstantin
> > > Ananyev
> > > Sent: Monday, November 09, 2015 9:20 AM
> > > To: dev@dpdk.org
> > > Subject: [dpdk-dev] [PATCHv5 1/2] testpmd: add ability to split outgoing
> > > packets
> > >
> > > For CSUM forwarding mode add ability to copy & split outgoing packet
> > > into the new mbuf that consists of multiple segments.
> > > For TXONLY and CSUM forwarding modes add ability to make number of
> > > segments in the outgoing packet to vary on a per packet basis.
> > > Number of segments and size of each segment is controlled by
> > > 'set txpkts' command.
> > > Split policy is controlled by 'set txsplit' command.
> > > Possible values are: on | off | rand.
> > > Tha allows to increase test coverage for TX PMD codepaths.
> > >
> > > Signed-off-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
> > > ---
> > > app/test-pmd/cmdline.c | 57 +++++++++-
> > > app/test-pmd/config.c | 61 +++++++++++
> > > app/test-pmd/csumonly.c | 163
> > > +++++++++++++++++++++++++++-
> > > app/test-pmd/testpmd.c | 3 +
> > > app/test-pmd/testpmd.h | 10 ++
> > > app/test-pmd/txonly.c | 13 ++-
> > > doc/guides/testpmd_app_ug/testpmd_funcs.rst | 11 +-
> > > 7 files changed, 309 insertions(+), 9 deletions(-)
> > >
> >
> > [...]
> >
> > > diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> > > b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> > > index 4fb1e0b..4608b3f 100644
> > > --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> > > +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> > > @@ -211,7 +211,7 @@ show config
> > > Displays the configuration of the application.
> > > The configuration comes from the command-line, the runtime or the
> > > application defaults::
> > >
> > > - testpmd> show config (rxtx|cores|fwd)
> > > + testpmd> show config (rxtx|cores|fwd|txpkts)
> > >
> > > The available information categories are:
> > >
> > > @@ -221,6 +221,8 @@ The available information categories are:
> > >
> > > * ``fwd``: Packet forwarding configuration.
> > >
> > > +* ``txpkts``: Packets to TX configuration.
> > > +
> > > For example:
> > >
> > > .. code-block:: console
> > > @@ -396,6 +398,13 @@ Set the length of each segment of the TX-ONLY
> > > packets::
> > >
> > > Where x[,y]* represents a CSV list of values, without white space.
> > >
> > > +set txsplit
> > > +~~~~~~~~~~~
> > > +
> > > +Set the length of each segment of the TX packets, applicable for TX-
> ONLY
> > > and CSUM forwarding modes::
> > > +
> > > + testpmd> set txsplit (off|on|rand)
> > > +
> > > set corelist
> > > ~~~~~~~~~~~~
> > >
> > > --
> > > 1.8.5.3
> >
> > Description of "set txsplit" does not look correct. Could you fix it?
>
> Ups, sure will fix it.
>
> > It would be nice to describe the three different options (off, on, rand) as
> well.
>
> Ok, will do.
> Any other comments before I'll re-spin it?
Just a typo ("packts") in config.c
+ tx_pkt_split tx_pkt_split = TX_PKT_SPLIT_OFF;
+ /**< Split policy for packts to TX. */
> Thanks
> Konstantin
>
>
> >
> > Thanks,
> > Pablo
@@ -199,7 +199,7 @@ static void cmd_help_long_parsed(void *parsed_result,
"clear port (info|stats|xstats|fdir|stat_qmap) (port_id|all)\n"
" Clear information for port_id, or all.\n\n"
- "show config (rxtx|cores|fwd)\n"
+ "show config (rxtx|cores|fwd|txpkts)\n"
" Display the given configuration.\n\n"
"read rxd (port_id) (queue_id) (rxd_id)\n"
@@ -246,7 +246,12 @@ static void cmd_help_long_parsed(void *parsed_result,
"set txpkts (x[,y]*)\n"
" Set the length of each segment of TXONLY"
- " packets.\n\n"
+ " and optionally CSUM packets.\n\n"
+
+ "set txsplit (off|on|rand)\n"
+ " Set the split policy for the TX packets."
+ " Right now only applicable for CSUM and TXONLY"
+ " modes\n\n"
"set corelist (x[,y]*)\n"
" Set the list of forwarding cores.\n\n"
@@ -2621,6 +2626,47 @@ cmdline_parse_inst_t cmd_set_txpkts = {
},
};
+/* *** SET COPY AND SPLIT POLICY ON TX PACKETS *** */
+
+struct cmd_set_txsplit_result {
+ cmdline_fixed_string_t cmd_keyword;
+ cmdline_fixed_string_t txsplit;
+ cmdline_fixed_string_t mode;
+};
+
+static void
+cmd_set_txsplit_parsed(void *parsed_result,
+ __attribute__((unused)) struct cmdline *cl,
+ __attribute__((unused)) void *data)
+{
+ struct cmd_set_txsplit_result *res;
+
+ res = parsed_result;
+ set_tx_pkt_split(res->mode);
+}
+
+cmdline_parse_token_string_t cmd_set_txsplit_keyword =
+ TOKEN_STRING_INITIALIZER(struct cmd_set_txsplit_result,
+ cmd_keyword, "set");
+cmdline_parse_token_string_t cmd_set_txsplit_name =
+ TOKEN_STRING_INITIALIZER(struct cmd_set_txsplit_result,
+ txsplit, "txsplit");
+cmdline_parse_token_string_t cmd_set_txsplit_mode =
+ TOKEN_STRING_INITIALIZER(struct cmd_set_txsplit_result,
+ mode, NULL);
+
+cmdline_parse_inst_t cmd_set_txsplit = {
+ .f = cmd_set_txsplit_parsed,
+ .data = NULL,
+ .help_str = "set txsplit on|off|rand",
+ .tokens = {
+ (void *)&cmd_set_txsplit_keyword,
+ (void *)&cmd_set_txsplit_name,
+ (void *)&cmd_set_txsplit_mode,
+ NULL,
+ },
+};
+
/* *** ADD/REMOVE ALL VLAN IDENTIFIERS TO/FROM A PORT VLAN RX FILTER *** */
struct cmd_rx_vlan_filter_all_result {
cmdline_fixed_string_t rx_vlan;
@@ -5233,6 +5279,8 @@ static void cmd_showcfg_parsed(void *parsed_result,
fwd_lcores_config_display();
else if (!strcmp(res->what, "fwd"))
fwd_config_display();
+ else if (!strcmp(res->what, "txpkts"))
+ show_tx_pkt_segments();
}
cmdline_parse_token_string_t cmd_showcfg_show =
@@ -5241,12 +5289,12 @@ cmdline_parse_token_string_t cmd_showcfg_port =
TOKEN_STRING_INITIALIZER(struct cmd_showcfg_result, cfg, "config");
cmdline_parse_token_string_t cmd_showcfg_what =
TOKEN_STRING_INITIALIZER(struct cmd_showcfg_result, what,
- "rxtx#cores#fwd");
+ "rxtx#cores#fwd#txpkts");
cmdline_parse_inst_t cmd_showcfg = {
.f = cmd_showcfg_parsed,
.data = NULL,
- .help_str = "show config rxtx|cores|fwd",
+ .help_str = "show config rxtx|cores|fwd|txpkts",
.tokens = {
(void *)&cmd_showcfg_show,
(void *)&cmd_showcfg_port,
@@ -9574,6 +9622,7 @@ cmdline_parse_ctx_t main_ctx[] = {
(cmdline_parse_inst_t *)&cmd_reset,
(cmdline_parse_inst_t *)&cmd_set_numbers,
(cmdline_parse_inst_t *)&cmd_set_txpkts,
+ (cmdline_parse_inst_t *)&cmd_set_txsplit,
(cmdline_parse_inst_t *)&cmd_set_fwd_list,
(cmdline_parse_inst_t *)&cmd_set_fwd_mask,
(cmdline_parse_inst_t *)&cmd_set_fwd_mode,
@@ -97,6 +97,24 @@
static char *flowtype_to_str(uint16_t flow_type);
+static const struct {
+ enum tx_pkt_split split;
+ const char *name;
+} tx_split_name[] = {
+ {
+ .split = TX_PKT_SPLIT_OFF,
+ .name = "off",
+ },
+ {
+ .split = TX_PKT_SPLIT_ON,
+ .name = "on",
+ },
+ {
+ .split = TX_PKT_SPLIT_RND,
+ .name = "rand",
+ },
+};
+
struct rss_type_info {
char str[32];
uint64_t rss_type;
@@ -1582,6 +1600,49 @@ set_nb_pkt_per_burst(uint16_t nb)
(unsigned int) nb_pkt_per_burst);
}
+static const char *
+tx_split_get_name(enum tx_pkt_split split)
+{
+ uint32_t i;
+
+ for (i = 0; i != RTE_DIM(tx_split_name); i++) {
+ if (tx_split_name[i].split == split)
+ return tx_split_name[i].name;
+ }
+ return NULL;
+}
+
+void
+set_tx_pkt_split(const char *name)
+{
+ uint32_t i;
+
+ for (i = 0; i != RTE_DIM(tx_split_name); i++) {
+ if (strcmp(tx_split_name[i].name, name) == 0) {
+ tx_pkt_split = tx_split_name[i].split;
+ return;
+ }
+ }
+ printf("unknown value: \"%s\"\n", name);
+}
+
+void
+show_tx_pkt_segments(void)
+{
+ uint32_t i, n;
+ const char *split;
+
+ n = tx_pkt_nb_segs;
+ split = tx_split_get_name(tx_pkt_split);
+
+ printf("Number of segments: %u\n", n);
+ printf("Segment sizes: ");
+ for (i = 0; i != n - 1; i++)
+ printf("%hu,", tx_pkt_seg_lengths[i]);
+ printf("%hu\n", tx_pkt_seg_lengths[i]);
+ printf("Split packet: %s\n", split);
+}
+
void
set_tx_pkt_segments(unsigned *seg_lengths, unsigned nb_segs)
{
@@ -457,6 +457,155 @@ process_outer_cksums(void *outer_l3_hdr, struct testpmd_offload_info *info,
}
/*
+ * Helper function.
+ * Performs actual copying.
+ * Returns number of segments in the destination mbuf on success,
+ * or negative error code on failure.
+ */
+static int
+mbuf_copy_split(const struct rte_mbuf *ms, struct rte_mbuf *md[],
+ uint16_t seglen[], uint8_t nb_seg)
+{
+ uint32_t dlen, slen, tlen;
+ uint32_t i, len;
+ const struct rte_mbuf *m;
+ const uint8_t *src;
+ uint8_t *dst;
+
+ dlen = 0;
+ slen = 0;
+ tlen = 0;
+
+ dst = NULL;
+ src = NULL;
+
+ m = ms;
+ i = 0;
+ while (ms != NULL && i != nb_seg) {
+
+ if (slen == 0) {
+ slen = rte_pktmbuf_data_len(ms);
+ src = rte_pktmbuf_mtod(ms, const uint8_t *);
+ }
+
+ if (dlen == 0) {
+ dlen = RTE_MIN(seglen[i], slen);
+ md[i]->data_len = dlen;
+ md[i]->next = (i + 1 == nb_seg) ? NULL : md[i + 1];
+ dst = rte_pktmbuf_mtod(md[i], uint8_t *);
+ }
+
+ len = RTE_MIN(slen, dlen);
+ memcpy(dst, src, len);
+ tlen += len;
+ slen -= len;
+ dlen -= len;
+ src += len;
+ dst += len;
+
+ if (slen == 0)
+ ms = ms->next;
+ if (dlen == 0)
+ i++;
+ }
+
+ if (ms != NULL)
+ return -ENOBUFS;
+ else if (tlen != m->pkt_len)
+ return -EINVAL;
+
+ md[0]->nb_segs = nb_seg;
+ md[0]->pkt_len = tlen;
+ md[0]->vlan_tci = m->vlan_tci;
+ md[0]->vlan_tci_outer = m->vlan_tci_outer;
+ md[0]->ol_flags = m->ol_flags;
+ md[0]->tx_offload = m->tx_offload;
+
+ return nb_seg;
+}
+
+/*
+ * Allocate a new mbuf with up to tx_pkt_nb_segs segments.
+ * Copy packet contents and offload information into then new segmented mbuf.
+ */
+static struct rte_mbuf *
+pkt_copy_split(const struct rte_mbuf *pkt)
+{
+ int32_t n, rc;
+ uint32_t i, len, nb_seg;
+ struct rte_mempool *mp;
+ uint16_t seglen[RTE_MAX_SEGS_PER_PKT];
+ struct rte_mbuf *p, *md[RTE_MAX_SEGS_PER_PKT];
+
+ mp = current_fwd_lcore()->mbp;
+
+ if (tx_pkt_split == TX_PKT_SPLIT_RND)
+ nb_seg = random() % tx_pkt_nb_segs + 1;
+ else
+ nb_seg = tx_pkt_nb_segs;
+
+ memcpy(seglen, tx_pkt_seg_lengths, nb_seg * sizeof(seglen[0]));
+
+ /* calculate number of segments to use and their length. */
+ len = 0;
+ for (i = 0; i != nb_seg && len < pkt->pkt_len; i++) {
+ len += seglen[i];
+ md[i] = NULL;
+ }
+
+ n = pkt->pkt_len - len;
+
+ /* update size of the last segment to fit rest of the packet */
+ if (n >= 0) {
+ seglen[i - 1] += n;
+ len += n;
+ }
+
+ nb_seg = i;
+ while (i != 0) {
+ p = rte_pktmbuf_alloc(mp);
+ if (p == NULL) {
+ RTE_LOG(ERR, USER1,
+ "failed to allocate %u-th of %u mbuf "
+ "from mempool: %s\n",
+ nb_seg - i, nb_seg, mp->name);
+ break;
+ }
+
+ md[--i] = p;
+ if (rte_pktmbuf_tailroom(md[i]) < seglen[i]) {
+ RTE_LOG(ERR, USER1, "mempool %s, %u-th segment: "
+ "expected seglen: %u, "
+ "actual mbuf tailroom: %u\n",
+ mp->name, i, seglen[i],
+ rte_pktmbuf_tailroom(md[i]));
+ break;
+ }
+ }
+
+ /* all mbufs successfully allocated, do copy */
+ if (i == 0) {
+ rc = mbuf_copy_split(pkt, md, seglen, nb_seg);
+ if (rc < 0)
+ RTE_LOG(ERR, USER1,
+ "mbuf_copy_split for %p(len=%u, nb_seg=%hhu) "
+ "into %u segments failed with error code: %d\n",
+ pkt, pkt->pkt_len, pkt->nb_segs, nb_seg, rc);
+
+ /* figure out how many mbufs to free. */
+ i = RTE_MAX(rc, 0);
+ }
+
+ /* free unused mbufs */
+ for (; i != nb_seg; i++) {
+ rte_pktmbuf_free_seg(md[i]);
+ md[i] = NULL;
+ }
+
+ return md[0];
+}
+
+/*
* Receive a burst of packets, and for each packet:
* - 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:
@@ -486,7 +635,7 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
{
struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
struct rte_port *txp;
- struct rte_mbuf *m;
+ struct rte_mbuf *m, *p;
struct ether_hdr *eth_hdr;
void *l3_hdr = NULL, *outer_l3_hdr = NULL; /* can be IPv4 or IPv6 */
uint16_t nb_rx;
@@ -627,6 +776,16 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
m->tso_segsz = info.tso_segsz;
m->ol_flags = ol_flags;
+ /* Do split & copy for the packet. */
+ if (tx_pkt_split != TX_PKT_SPLIT_OFF) {
+ p = pkt_copy_split(m);
+ if (p != NULL) {
+ rte_pktmbuf_free(m);
+ m = p;
+ pkts_burst[i] = m;
+ }
+ }
+
/* if verbose mode is enabled, dump debug info */
if (verbose_level > 0) {
struct {
@@ -648,6 +807,8 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
const char *name;
printf("-----------------\n");
+ printf("mbuf=%p, pkt_len=%u, nb_segs=%hhu:\n",
+ m, m->pkt_len, m->nb_segs);
/* dump rx parsed packet info */
printf("rx: l2_len=%d ethertype=%x l3_len=%d "
"l4_proto=%d l4_len=%d\n",
@@ -173,6 +173,9 @@ uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = {
};
uint8_t tx_pkt_nb_segs = 1; /**< Number of segments in TXONLY packets */
+enum tx_pkt_split tx_pkt_split = TX_PKT_SPLIT_OFF;
+/**< Split policy for packts to TX. */
+
uint16_t nb_pkt_per_burst = DEF_PKT_BURST; /**< Number of packets per burst. */
uint16_t mb_mempool_cache = DEF_MBUF_CACHE; /**< Size of mbuf mempool cache. */
@@ -361,6 +361,14 @@ extern uint16_t tx_pkt_length; /**< Length of TXONLY packet */
extern uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT]; /**< Seg. lengths */
extern uint8_t tx_pkt_nb_segs; /**< Number of segments in TX packets */
+enum tx_pkt_split {
+ TX_PKT_SPLIT_OFF,
+ TX_PKT_SPLIT_ON,
+ TX_PKT_SPLIT_RND,
+};
+
+extern enum tx_pkt_split tx_pkt_split;
+
extern uint16_t nb_pkt_per_burst;
extern uint16_t mb_mempool_cache;
extern int8_t rx_pthresh;
@@ -509,6 +517,8 @@ void set_qmap(portid_t port_id, uint8_t is_rx, uint16_t queue_id, uint8_t map_va
void set_verbose_level(uint16_t vb_level);
void set_tx_pkt_segments(unsigned *seg_lengths, unsigned nb_segs);
+void show_tx_pkt_segments(void);
+void set_tx_pkt_split(const char *name);
void set_nb_pkt_per_burst(uint16_t pkt_burst);
char *list_pkt_forwarding_modes(void);
void set_pkt_forwarding_mode(const char *fwd_mode);
@@ -210,6 +210,7 @@ pkt_burst_transmit(struct fwd_stream *fs)
uint64_t end_tsc;
uint64_t core_cycles;
#endif
+ uint32_t nb_segs, pkt_len;
#ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
start_tsc = rte_rdtsc();
@@ -233,7 +234,12 @@ pkt_burst_transmit(struct fwd_stream *fs)
}
pkt->data_len = tx_pkt_seg_lengths[0];
pkt_seg = pkt;
- for (i = 1; i < tx_pkt_nb_segs; i++) {
+ if (tx_pkt_split == TX_PKT_SPLIT_RND)
+ nb_segs = random() % tx_pkt_nb_segs + 1;
+ else
+ nb_segs = tx_pkt_nb_segs;
+ pkt_len = pkt->data_len;
+ for (i = 1; i < nb_segs; i++) {
pkt_seg->next = tx_mbuf_alloc(mbp);
if (pkt_seg->next == NULL) {
pkt->nb_segs = i;
@@ -242,6 +248,7 @@ pkt_burst_transmit(struct fwd_stream *fs)
}
pkt_seg = pkt_seg->next;
pkt_seg->data_len = tx_pkt_seg_lengths[i];
+ pkt_len += pkt_seg->data_len;
}
pkt_seg->next = NULL; /* Last segment of packet. */
@@ -266,8 +273,8 @@ pkt_burst_transmit(struct fwd_stream *fs)
* Complete first mbuf of packet and append it to the
* burst of packets to be transmitted.
*/
- pkt->nb_segs = tx_pkt_nb_segs;
- pkt->pkt_len = tx_pkt_length;
+ pkt->nb_segs = nb_segs;
+ pkt->pkt_len = pkt_len;
pkt->ol_flags = ol_flags;
pkt->vlan_tci = vlan_tci;
pkt->vlan_tci_outer = vlan_tci_outer;
@@ -211,7 +211,7 @@ show config
Displays the configuration of the application.
The configuration comes from the command-line, the runtime or the application defaults::
- testpmd> show config (rxtx|cores|fwd)
+ testpmd> show config (rxtx|cores|fwd|txpkts)
The available information categories are:
@@ -221,6 +221,8 @@ The available information categories are:
* ``fwd``: Packet forwarding configuration.
+* ``txpkts``: Packets to TX configuration.
+
For example:
.. code-block:: console
@@ -396,6 +398,13 @@ Set the length of each segment of the TX-ONLY packets::
Where x[,y]* represents a CSV list of values, without white space.
+set txsplit
+~~~~~~~~~~~
+
+Set the length of each segment of the TX packets, applicable for TX-ONLY and CSUM forwarding modes::
+
+ testpmd> set txsplit (off|on|rand)
+
set corelist
~~~~~~~~~~~~