diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index be1a98b9bc..39a1e772ed 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -294,6 +294,10 @@ static void cmd_help_long_parsed(void *parsed_result, " Right now only applicable for CSUM and TXONLY" " modes\n\n" + "set txtimes (x, y)\n" + " Set the scheduling on timestamps" + " timings for the TXOMLY mode\n\n" + "set corelist (x[,y]*)\n" " Set the list of forwarding cores.\n\n" @@ -3937,6 +3941,52 @@ cmdline_parse_inst_t cmd_set_txsplit = { }, }; +/* *** SET TIMES FOR TXONLY PACKETS SCHEDULING ON TIMESTAMPS *** */ + +struct cmd_set_txtimes_result { + cmdline_fixed_string_t cmd_keyword; + cmdline_fixed_string_t txtimes; + cmdline_fixed_string_t tx_times; +}; + +static void +cmd_set_txtimes_parsed(void *parsed_result, + __rte_unused struct cmdline *cl, + __rte_unused void *data) +{ + struct cmd_set_txtimes_result *res; + unsigned int tx_times[2] = {0, 0}; + unsigned int n_times; + + res = parsed_result; + n_times = parse_item_list(res->tx_times, "tx times", + 2, tx_times, 0); + if (n_times == 2) + set_tx_pkt_times(tx_times); +} + +cmdline_parse_token_string_t cmd_set_txtimes_keyword = + TOKEN_STRING_INITIALIZER(struct cmd_set_txtimes_result, + cmd_keyword, "set"); +cmdline_parse_token_string_t cmd_set_txtimes_name = + TOKEN_STRING_INITIALIZER(struct cmd_set_txtimes_result, + txtimes, "txtimes"); +cmdline_parse_token_string_t cmd_set_txtimes_value = + TOKEN_STRING_INITIALIZER(struct cmd_set_txtimes_result, + tx_times, NULL); + +cmdline_parse_inst_t cmd_set_txtimes = { + .f = cmd_set_txtimes_parsed, + .data = NULL, + .help_str = "set txtimes ,", + .tokens = { + (void *)&cmd_set_txtimes_keyword, + (void *)&cmd_set_txtimes_name, + (void *)&cmd_set_txtimes_value, + 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; @@ -7425,6 +7475,8 @@ static void cmd_showcfg_parsed(void *parsed_result, pkt_fwd_config_display(&cur_fwd_config); else if (!strcmp(res->what, "txpkts")) show_tx_pkt_segments(); + else if (!strcmp(res->what, "txtimes")) + show_tx_pkt_times(); } cmdline_parse_token_string_t cmd_showcfg_show = @@ -7433,12 +7485,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#txpkts"); + "rxtx#cores#fwd#txpkts#txtimes"); cmdline_parse_inst_t cmd_showcfg = { .f = cmd_showcfg_parsed, .data = NULL, - .help_str = "show config rxtx|cores|fwd|txpkts", + .help_str = "show config rxtx|cores|fwd|txpkts|txtimes", .tokens = { (void *)&cmd_showcfg_show, (void *)&cmd_showcfg_port, @@ -18603,7 +18655,8 @@ cmdline_parse_token_string_t cmd_config_per_port_tx_offload_result_offload = "sctp_cksum#tcp_tso#udp_tso#outer_ipv4_cksum#" "qinq_insert#vxlan_tnl_tso#gre_tnl_tso#" "ipip_tnl_tso#geneve_tnl_tso#macsec_insert#" - "mt_lockfree#multi_segs#mbuf_fast_free#security"); + "mt_lockfree#multi_segs#mbuf_fast_free#security#" + "send_on_timestamp"); cmdline_parse_token_string_t cmd_config_per_port_tx_offload_result_on_off = TOKEN_STRING_INITIALIZER (struct cmd_config_per_port_tx_offload_result, @@ -18688,7 +18741,8 @@ cmdline_parse_inst_t cmd_config_per_port_tx_offload = { "sctp_cksum|tcp_tso|udp_tso|outer_ipv4_cksum|" "qinq_insert|vxlan_tnl_tso|gre_tnl_tso|" "ipip_tnl_tso|geneve_tnl_tso|macsec_insert|" - "mt_lockfree|multi_segs|mbuf_fast_free|security on|off", + "mt_lockfree|multi_segs|mbuf_fast_free|security|" + "send_on_timestamp on|off", .tokens = { (void *)&cmd_config_per_port_tx_offload_result_port, (void *)&cmd_config_per_port_tx_offload_result_config, @@ -19360,6 +19414,7 @@ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_set_log, (cmdline_parse_inst_t *)&cmd_set_txpkts, (cmdline_parse_inst_t *)&cmd_set_txsplit, + (cmdline_parse_inst_t *)&cmd_set_txtimes, (cmdline_parse_inst_t *)&cmd_set_fwd_list, (cmdline_parse_inst_t *)&cmd_set_fwd_mask, (cmdline_parse_inst_t *)&cmd_set_fwd_mode, diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 4d937df5e9..fcbe6b6f74 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -1052,6 +1052,15 @@ port_offload_cap_display(portid_t port_id) printf("off\n"); } + if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_SEND_ON_TIMESTAMP) { + printf("Tx scheduling on timestamp: "); + if (ports[port_id].dev_conf.txmode.offloads & + DEV_TX_OFFLOAD_SEND_ON_TIMESTAMP) + printf("on\n"); + else + printf("off\n"); + } + } int @@ -3016,6 +3025,58 @@ set_tx_pkt_segments(unsigned *seg_lengths, unsigned nb_segs) tx_pkt_nb_segs = (uint8_t) nb_segs; } +void +show_tx_pkt_times(void) +{ + printf("Interburst gap: %u\n", tx_pkt_times_inter); + printf("Intraburst gap: %u\n", tx_pkt_times_intra); +} + +void +set_tx_pkt_times(unsigned int *tx_times) +{ + uint16_t port_id; + int offload_found = 0; + int offset; + int flag; + + static const struct rte_mbuf_dynfield desc_offs = { + .name = RTE_MBUF_DYNFIELD_TIMESTAMP_NAME, + .size = sizeof(uint64_t), + .align = __alignof__(uint64_t), + }; + static const struct rte_mbuf_dynflag desc_flag = { + .name = RTE_MBUF_DYNFLAG_TX_TIMESTAMP_NAME, + }; + + RTE_ETH_FOREACH_DEV(port_id) { + struct rte_eth_dev_info dev_info = { 0 }; + int ret; + + ret = rte_eth_dev_info_get(port_id, &dev_info); + if (ret == 0 && dev_info.tx_offload_capa & + DEV_TX_OFFLOAD_SEND_ON_TIMESTAMP) { + offload_found = 1; + break; + } + } + if (!offload_found) { + printf("No device supporting Tx timestamp scheduling found, " + "dynamic flag and field not registered\n"); + return; + } + offset = rte_mbuf_dynfield_register(&desc_offs); + if (offset < 0 && rte_errno != EEXIST) + printf("Dynamic timestamp field registration error: %d", + rte_errno); + flag = rte_mbuf_dynflag_register(&desc_flag); + if (flag < 0 && rte_errno != EEXIST) + printf("Dynamic timestamp flag registration error: %d", + rte_errno); + tx_pkt_times_inter = tx_times[0]; + tx_pkt_times_intra = tx_times[1]; +} + void setup_gro(const char *onoff, portid_t port_id) { diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index 92e00cf7ac..626cb7b2d9 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -220,6 +220,12 @@ enum tx_pkt_split tx_pkt_split = TX_PKT_SPLIT_OFF; uint8_t txonly_multi_flow; /**< Whether multiple flows are generated in TXONLY mode. */ +uint32_t tx_pkt_times_inter; +/**< Timings for send scheduling in TXONLY mode, time between bursts. */ + +uint32_t tx_pkt_times_intra; +/**< Timings for send scheduling in TXONLY mode, time between packets. */ + 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. */ diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index c528db4500..2d0409f4fb 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -420,6 +420,8 @@ extern struct rte_fdir_conf fdir_conf; 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 */ +extern uint32_t tx_pkt_times_intra; +extern uint32_t tx_pkt_times_inter; enum tx_pkt_split { TX_PKT_SPLIT_OFF, @@ -772,6 +774,8 @@ void set_xstats_hide_zero(uint8_t on_off); 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_times(unsigned int *tx_times); +void show_tx_pkt_times(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); diff --git a/app/test-pmd/txonly.c b/app/test-pmd/txonly.c index 076ccaf8f0..97f4a452da 100644 --- a/app/test-pmd/txonly.c +++ b/app/test-pmd/txonly.c @@ -53,6 +53,12 @@ uint32_t tx_ip_dst_addr = (198U << 24) | (18 << 16) | (0 << 8) | 2; static struct rte_ipv4_hdr pkt_ip_hdr; /**< IP header of transmitted packets. */ RTE_DEFINE_PER_LCORE(uint8_t, _ip_var); /**< IP address variation */ static struct rte_udp_hdr pkt_udp_hdr; /**< UDP header of tx packets. */ +RTE_DEFINE_PER_LCORE(uint64_t, timestamp_qskew); + /**< Timestamp offset per queue */ +static uint64_t timestamp_mask; /**< Timestamp dynamic flag mask */ +static int32_t timestamp_off; /**< Timestamp dynamic field offset */ +static bool timestamp_enable; /**< Timestamp enable */ +static uint64_t timestamp_initial[RTE_MAX_ETHPORTS]; static void copy_buf_to_pkt_segs(void* buf, unsigned len, struct rte_mbuf *pkt, @@ -150,7 +156,8 @@ setup_pkt_udp_ip_headers(struct rte_ipv4_hdr *ip_hdr, static inline bool pkt_burst_prepare(struct rte_mbuf *pkt, struct rte_mempool *mbp, struct rte_ether_hdr *eth_hdr, const uint16_t vlan_tci, - const uint16_t vlan_tci_outer, const uint64_t ol_flags) + const uint16_t vlan_tci_outer, const uint64_t ol_flags, + const uint16_t idx, const struct fwd_stream *fs) { struct rte_mbuf *pkt_segs[RTE_MAX_SEGS_PER_PKT]; struct rte_mbuf *pkt_seg; @@ -213,6 +220,53 @@ pkt_burst_prepare(struct rte_mbuf *pkt, struct rte_mempool *mbp, copy_buf_to_pkt(&pkt_udp_hdr, sizeof(pkt_udp_hdr), pkt, sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr)); + if (unlikely(timestamp_enable)) { + uint64_t skew = RTE_PER_LCORE(timestamp_qskew); + struct { + rte_be32_t signature; + rte_be16_t pkt_idx; + rte_be16_t queue_idx; + rte_be64_t ts; + } timestamp_mark; + + if (unlikely(!skew)) { + struct rte_eth_dev *dev = &rte_eth_devices[fs->tx_port]; + unsigned int txqs_n = dev->data->nb_tx_queues; + uint64_t phase = tx_pkt_times_inter * fs->tx_queue / + (txqs_n ? txqs_n : 1); + /* + * Initialize the scheduling time phase shift + * depending on queue index. + */ + skew = timestamp_initial[fs->tx_port] + + tx_pkt_times_inter + phase; + RTE_PER_LCORE(timestamp_qskew) = skew; + } + timestamp_mark.pkt_idx = rte_cpu_to_be_16(idx); + timestamp_mark.queue_idx = rte_cpu_to_be_16(fs->tx_queue); + timestamp_mark.signature = rte_cpu_to_be_32(0xBEEFC0DE); + if (unlikely(!idx)) { + skew += tx_pkt_times_inter; + pkt->ol_flags |= timestamp_mask; + *RTE_MBUF_DYNFIELD + (pkt, timestamp_off, uint64_t *) = skew; + RTE_PER_LCORE(timestamp_qskew) = skew; + timestamp_mark.ts = rte_cpu_to_be_64(skew); + } else if (tx_pkt_times_intra) { + skew += tx_pkt_times_intra; + pkt->ol_flags |= timestamp_mask; + *RTE_MBUF_DYNFIELD + (pkt, timestamp_off, uint64_t *) = skew; + RTE_PER_LCORE(timestamp_qskew) = skew; + timestamp_mark.ts = rte_cpu_to_be_64(skew); + } else { + timestamp_mark.ts = RTE_BE64(0); + } + copy_buf_to_pkt(×tamp_mark, sizeof(timestamp_mark), pkt, + sizeof(struct rte_ether_hdr) + + sizeof(struct rte_ipv4_hdr) + + sizeof(pkt_udp_hdr)); + } /* * Complete first mbuf of packet and append it to the * burst of packets to be transmitted. @@ -275,7 +329,8 @@ pkt_burst_transmit(struct fwd_stream *fs) if (unlikely(!pkt_burst_prepare(pkts_burst[nb_pkt], mbp, ð_hdr, vlan_tci, vlan_tci_outer, - ol_flags))) { + ol_flags, + nb_pkt, fs))) { rte_mempool_put_bulk(mbp, (void **)&pkts_burst[nb_pkt], nb_pkt_per_burst - nb_pkt); @@ -290,7 +345,8 @@ pkt_burst_transmit(struct fwd_stream *fs) if (unlikely(!pkt_burst_prepare(pkt, mbp, ð_hdr, vlan_tci, vlan_tci_outer, - ol_flags))) { + ol_flags, + nb_pkt, fs))) { rte_pktmbuf_free(pkt); break; } @@ -302,6 +358,7 @@ pkt_burst_transmit(struct fwd_stream *fs) return; nb_tx = rte_eth_tx_burst(fs->tx_port, fs->tx_queue, pkts_burst, nb_pkt); + /* * Retry if necessary */ @@ -342,15 +399,33 @@ pkt_burst_transmit(struct fwd_stream *fs) } static void -tx_only_begin(__rte_unused portid_t pi) +tx_only_begin(portid_t pi) { uint16_t pkt_data_len; + int dynf; pkt_data_len = (uint16_t) (tx_pkt_length - ( sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_udp_hdr))); setup_pkt_udp_ip_headers(&pkt_ip_hdr, &pkt_udp_hdr, pkt_data_len); + + timestamp_enable = false; + timestamp_mask = 0; + timestamp_off = -1; + RTE_PER_LCORE(timestamp_qskew) = 0; + dynf = rte_mbuf_dynflag_lookup + (RTE_MBUF_DYNFLAG_TX_TIMESTAMP_NAME, NULL); + if (dynf >= 0) + timestamp_mask = 1ULL << dynf; + dynf = rte_mbuf_dynfield_lookup + (RTE_MBUF_DYNFIELD_TIMESTAMP_NAME, NULL); + if (dynf >= 0) + timestamp_off = dynf; + timestamp_enable = tx_pkt_times_inter && + timestamp_mask && + timestamp_off >= 0 && + !rte_eth_read_clock(pi, ×tamp_initial[pi]); } struct fwd_engine tx_only_engine = { diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst index 47bfbfe948..5cbcf78a13 100644 --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst @@ -266,7 +266,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|txpkts) + testpmd> show config (rxtx|cores|fwd|txpkts|txtimes) The available information categories are: @@ -278,6 +278,8 @@ The available information categories are: * ``txpkts``: Packets to TX configuration. +* ``txtimes``: Burst time pattern for Tx only mode. + For example: .. code-block:: console @@ -722,6 +724,40 @@ Set the length of each segment of the TX-ONLY packets or length of packet for FL Where x[,y]* represents a CSV list of values, without white space. +set txtimes +~~~~~~~~~~~ + +Configure the timing burst pattern for Tx only mode. This command enables +the packet send scheduling on dynamic timestamp mbuf field and configures +timing pattern in Tx only mode. In this mode, if scheduling is enabled +application provides timestamps in the packets being sent. It is possible +to configure delay (in unspecified device clock units) between bursts +and between the packets within the burst:: + + testpmd> set txtimes (inter),(intra) + +where: + +* ``inter`` is the delay between the bursts in the device clock units. + If ``intra`` is zero, this is the time between the beginnings of the + first packets in the neighbour bursts, if ``intra`` is not zero, + ``inter`` specifies the time between the beginning of the first packet + of the current burst and the beginning of the last packet of the + previous burst. If ``inter`` parameter is zero the send scheduling + on timestamps is disabled (default). + +* ``intra`` is the delay between the packets within the burst specified + in the device clock units. The number of packets in the burst is defined + by regular burst setting. If ``intra`` parameter is zero no timestamps + provided in the packets excepting the first one in the burst. + +As the result the bursts of packet will be transmitted with specific +delays between the packets within the burst and specific delay between +the bursts. The rte_eth_read_clock() must be supported by the device(s) +and is supposed to be engaged to get the current device clock value +and provide the reference for the timestamps. If there is no supported +rte_eth_read_clock() there will be no send scheduling provided on the port. + set txsplit ~~~~~~~~~~~