Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2010-2014 Intel Corporation
3 : : */
4 : :
5 : : #include <stdarg.h>
6 : : #include <string.h>
7 : : #include <stdio.h>
8 : : #include <errno.h>
9 : : #include <stdint.h>
10 : : #include <unistd.h>
11 : : #include <inttypes.h>
12 : :
13 : : #include <sys/queue.h>
14 : : #include <sys/stat.h>
15 : :
16 : : #include <rte_common.h>
17 : : #include <rte_byteorder.h>
18 : : #include <rte_log.h>
19 : : #include <rte_debug.h>
20 : : #include <rte_cycles.h>
21 : : #include <rte_memory.h>
22 : : #include <rte_memcpy.h>
23 : : #include <rte_launch.h>
24 : : #include <rte_eal.h>
25 : : #include <rte_per_lcore.h>
26 : : #include <rte_lcore.h>
27 : : #include <rte_branch_prediction.h>
28 : : #include <rte_mempool.h>
29 : : #include <rte_mbuf.h>
30 : : #include <rte_interrupts.h>
31 : : #include <rte_ether.h>
32 : : #include <rte_ethdev.h>
33 : : #include <rte_ip.h>
34 : : #include <rte_tcp.h>
35 : : #include <rte_udp.h>
36 : : #include <rte_string_fns.h>
37 : : #include <rte_flow.h>
38 : :
39 : : #include "testpmd.h"
40 : :
41 : : struct tx_timestamp {
42 : : rte_be32_t signature;
43 : : rte_be16_t pkt_idx;
44 : : rte_be16_t queue_idx;
45 : : rte_be64_t ts;
46 : : };
47 : :
48 : : /* use RFC863 Discard Protocol */
49 : : uint16_t tx_udp_src_port = 9;
50 : : uint16_t tx_udp_dst_port = 9;
51 : :
52 : : /* use RFC5735 / RFC2544 reserved network test addresses */
53 : : uint32_t tx_ip_src_addr = (198U << 24) | (18 << 16) | (0 << 8) | 1;
54 : : uint32_t tx_ip_dst_addr = (198U << 24) | (18 << 16) | (0 << 8) | 2;
55 : :
56 : : #define IP_DEFTTL 64 /* from RFC 1340. */
57 : :
58 : : static struct rte_ipv4_hdr pkt_ip_hdr; /**< IP header of transmitted packets. */
59 : : RTE_DEFINE_PER_LCORE(uint8_t, _src_port_var); /**< Source port variation */
60 : : static struct rte_udp_hdr pkt_udp_hdr; /**< UDP header of tx packets. */
61 : :
62 : : static uint64_t timestamp_mask; /**< Timestamp dynamic flag mask */
63 : : static int32_t timestamp_off; /**< Timestamp dynamic field offset */
64 : : static bool timestamp_enable; /**< Timestamp enable */
65 : : static uint64_t timestamp_initial[RTE_MAX_ETHPORTS];
66 : :
67 : : static void
68 : 0 : copy_buf_to_pkt_segs(void* buf, unsigned len, struct rte_mbuf *pkt,
69 : : unsigned offset)
70 : : {
71 : : struct rte_mbuf *seg;
72 : : void *seg_buf;
73 : : unsigned copy_len;
74 : :
75 : : seg = pkt;
76 : 0 : while (offset >= seg->data_len) {
77 : 0 : offset -= seg->data_len;
78 : 0 : seg = seg->next;
79 : : }
80 : 0 : copy_len = seg->data_len - offset;
81 : 0 : seg_buf = rte_pktmbuf_mtod_offset(seg, char *, offset);
82 : 0 : while (len > copy_len) {
83 : 0 : rte_memcpy(seg_buf, buf, (size_t) copy_len);
84 : 0 : len -= copy_len;
85 : 0 : buf = ((char*) buf + copy_len);
86 : 0 : seg = seg->next;
87 : 0 : seg_buf = rte_pktmbuf_mtod(seg, char *);
88 : 0 : copy_len = seg->data_len;
89 : : }
90 : 0 : rte_memcpy(seg_buf, buf, (size_t) len);
91 : 0 : }
92 : :
93 : : static inline void
94 : 0 : copy_buf_to_pkt(void* buf, unsigned len, struct rte_mbuf *pkt, unsigned offset)
95 : : {
96 : 0 : if (offset + len <= pkt->data_len) {
97 : 0 : rte_memcpy(rte_pktmbuf_mtod_offset(pkt, char *, offset),
98 : : buf, (size_t) len);
99 : 0 : return;
100 : : }
101 : 0 : copy_buf_to_pkt_segs(buf, len, pkt, offset);
102 : : }
103 : :
104 : : static void
105 : 0 : setup_pkt_udp_ip_headers(struct rte_ipv4_hdr *ip_hdr,
106 : : struct rte_udp_hdr *udp_hdr,
107 : : uint16_t pkt_data_len)
108 : : {
109 : : uint16_t pkt_len;
110 : :
111 : : /*
112 : : * Initialize UDP header.
113 : : */
114 : 0 : pkt_len = (uint16_t) (pkt_data_len + sizeof(struct rte_udp_hdr));
115 : 0 : udp_hdr->src_port = rte_cpu_to_be_16(tx_udp_src_port);
116 : 0 : udp_hdr->dst_port = rte_cpu_to_be_16(tx_udp_dst_port);
117 : 0 : udp_hdr->dgram_len = RTE_CPU_TO_BE_16(pkt_len);
118 : 0 : udp_hdr->dgram_cksum = 0; /* No UDP checksum. */
119 : :
120 : : /*
121 : : * Initialize IP header.
122 : : */
123 : 0 : pkt_len = (uint16_t) (pkt_len + sizeof(struct rte_ipv4_hdr));
124 : 0 : ip_hdr->version_ihl = RTE_IPV4_VHL_DEF;
125 : 0 : ip_hdr->type_of_service = 0;
126 : 0 : ip_hdr->fragment_offset = 0;
127 : 0 : ip_hdr->time_to_live = IP_DEFTTL;
128 : 0 : ip_hdr->next_proto_id = IPPROTO_UDP;
129 : 0 : ip_hdr->packet_id = 0;
130 : 0 : ip_hdr->total_length = RTE_CPU_TO_BE_16(pkt_len);
131 : 0 : ip_hdr->src_addr = rte_cpu_to_be_32(tx_ip_src_addr);
132 : 0 : ip_hdr->dst_addr = rte_cpu_to_be_32(tx_ip_dst_addr);
133 : :
134 : : /*
135 : : * Compute IP header checksum.
136 : : */
137 : 0 : ip_hdr->hdr_checksum = rte_ipv4_cksum_simple(ip_hdr);
138 : 0 : }
139 : :
140 : : static inline void
141 : 0 : update_pkt_header(struct rte_mbuf *pkt, uint32_t total_pkt_len)
142 : : {
143 : : struct rte_ipv4_hdr *ip_hdr;
144 : : struct rte_udp_hdr *udp_hdr;
145 : : uint16_t pkt_data_len;
146 : : uint16_t pkt_len;
147 : :
148 : 0 : pkt_data_len = (uint16_t) (total_pkt_len - (
149 : : sizeof(struct rte_ether_hdr) +
150 : : sizeof(struct rte_ipv4_hdr) +
151 : : sizeof(struct rte_udp_hdr)));
152 : : /* update UDP packet length */
153 : 0 : udp_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_udp_hdr *,
154 : : sizeof(struct rte_ether_hdr) +
155 : : sizeof(struct rte_ipv4_hdr));
156 : 0 : pkt_len = (uint16_t) (pkt_data_len + sizeof(struct rte_udp_hdr));
157 : 0 : udp_hdr->dgram_len = RTE_CPU_TO_BE_16(pkt_len);
158 : :
159 : : /* update IP packet length and checksum */
160 : 0 : ip_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_ipv4_hdr *,
161 : : sizeof(struct rte_ether_hdr));
162 : 0 : ip_hdr->hdr_checksum = 0;
163 : 0 : pkt_len = (uint16_t) (pkt_len + sizeof(struct rte_ipv4_hdr));
164 : 0 : ip_hdr->total_length = RTE_CPU_TO_BE_16(pkt_len);
165 : 0 : ip_hdr->hdr_checksum = rte_ipv4_cksum(ip_hdr);
166 : 0 : }
167 : :
168 : : static inline bool
169 : 0 : pkt_burst_prepare(struct rte_mbuf *pkt, struct rte_mempool *mbp,
170 : : struct rte_ether_hdr *eth_hdr, const uint16_t vlan_tci,
171 : : const uint16_t vlan_tci_outer, const uint64_t ol_flags,
172 : : const uint16_t idx, struct fwd_stream *fs)
173 : : {
174 : : struct rte_mbuf *pkt_segs[RTE_MAX_SEGS_PER_PKT];
175 : : struct rte_mbuf *pkt_seg;
176 : : uint32_t nb_segs, pkt_len;
177 : : uint8_t i;
178 : :
179 : 0 : if (unlikely(tx_pkt_split == TX_PKT_SPLIT_RND))
180 : 0 : nb_segs = rte_rand() % tx_pkt_nb_segs + 1;
181 : : else
182 : 0 : nb_segs = tx_pkt_nb_segs;
183 : :
184 : 0 : if (nb_segs > 1) {
185 : 0 : if (rte_mempool_get_bulk(mbp, (void **)pkt_segs, nb_segs - 1))
186 : : return false;
187 : : }
188 : :
189 : : rte_pktmbuf_reset_headroom(pkt);
190 : 0 : pkt->data_len = tx_pkt_seg_lengths[0];
191 : 0 : pkt->ol_flags &= RTE_MBUF_F_EXTERNAL;
192 : 0 : pkt->ol_flags |= ol_flags;
193 : 0 : pkt->vlan_tci = vlan_tci;
194 : 0 : pkt->vlan_tci_outer = vlan_tci_outer;
195 : 0 : pkt->l2_len = sizeof(struct rte_ether_hdr);
196 : 0 : pkt->l3_len = sizeof(struct rte_ipv4_hdr);
197 : :
198 : 0 : pkt_len = pkt->data_len;
199 : : pkt_seg = pkt;
200 : 0 : for (i = 1; i < nb_segs; i++) {
201 : 0 : pkt_seg->next = pkt_segs[i - 1];
202 : : pkt_seg = pkt_seg->next;
203 : 0 : pkt_seg->data_len = tx_pkt_seg_lengths[i];
204 : 0 : pkt_len += pkt_seg->data_len;
205 : : }
206 : 0 : pkt_seg->next = NULL; /* Last segment of packet. */
207 : : /*
208 : : * Copy headers in first packet segment(s).
209 : : */
210 : 0 : copy_buf_to_pkt(eth_hdr, sizeof(*eth_hdr), pkt, 0);
211 : 0 : copy_buf_to_pkt(&pkt_ip_hdr, sizeof(pkt_ip_hdr), pkt,
212 : : sizeof(struct rte_ether_hdr));
213 : 0 : copy_buf_to_pkt(&pkt_udp_hdr, sizeof(pkt_udp_hdr), pkt,
214 : : sizeof(struct rte_ether_hdr) +
215 : : sizeof(struct rte_ipv4_hdr));
216 : 0 : if (txonly_multi_flow) {
217 : 0 : uint16_t src_var = RTE_PER_LCORE(_src_port_var);
218 : : struct rte_udp_hdr *udp_hdr;
219 : : uint16_t src_port;
220 : :
221 : 0 : udp_hdr = rte_pktmbuf_mtod_offset(pkt,
222 : : struct rte_udp_hdr *,
223 : : sizeof(struct rte_ether_hdr) +
224 : : sizeof(struct rte_ipv4_hdr));
225 : : /*
226 : : * Generate multiple flows by varying UDP source port.
227 : : * This enables packets are well distributed by RSS in
228 : : * receiver side if any and txonly mode can be a decent
229 : : * packet generator for developer's quick performance
230 : : * regression test.
231 : : *
232 : : * Only ports in the range 49152 (0xC000) and 65535 (0xFFFF)
233 : : * will be used, with the least significant byte representing
234 : : * the lcore ID. As such, the most significant byte will cycle
235 : : * through 0xC0 and 0xFF.
236 : : */
237 : 0 : src_port = ((src_var++ | 0xC0) << 8) + rte_lcore_id();
238 : 0 : udp_hdr->src_port = rte_cpu_to_be_16(src_port);
239 : 0 : RTE_PER_LCORE(_src_port_var) = src_var;
240 : : }
241 : :
242 : 0 : if (unlikely(tx_pkt_split == TX_PKT_SPLIT_RND) || txonly_multi_flow)
243 : 0 : update_pkt_header(pkt, pkt_len);
244 : :
245 : 0 : if (unlikely(timestamp_enable)) {
246 : 0 : uint64_t skew = fs->ts_skew;
247 : : struct tx_timestamp timestamp_mark;
248 : :
249 : 0 : if (unlikely(!skew)) {
250 : : struct rte_eth_dev_info dev_info;
251 : : unsigned int txqs_n;
252 : : uint64_t phase;
253 : : int ret;
254 : :
255 : 0 : ret = eth_dev_info_get_print_err(fs->tx_port, &dev_info);
256 : 0 : if (ret != 0) {
257 : 0 : TESTPMD_LOG(ERR,
258 : : "Failed to get device info for port %d,"
259 : : "could not finish timestamp init",
260 : : fs->tx_port);
261 : 0 : return false;
262 : : }
263 : 0 : txqs_n = dev_info.nb_tx_queues;
264 : 0 : phase = tx_pkt_times_inter * fs->tx_queue /
265 : : (txqs_n ? txqs_n : 1);
266 : : /*
267 : : * Initialize the scheduling time phase shift
268 : : * depending on queue index.
269 : : */
270 : 0 : skew = timestamp_initial[fs->tx_port] +
271 : : tx_pkt_times_inter + phase;
272 : 0 : fs->ts_skew = skew;
273 : : }
274 : 0 : timestamp_mark.pkt_idx = rte_cpu_to_be_16(idx);
275 : 0 : timestamp_mark.queue_idx = rte_cpu_to_be_16(fs->tx_queue);
276 : 0 : timestamp_mark.signature = rte_cpu_to_be_32(0xBEEFC0DE);
277 : 0 : if (unlikely(!idx)) {
278 : 0 : skew += tx_pkt_times_inter;
279 : 0 : pkt->ol_flags |= timestamp_mask;
280 : 0 : *RTE_MBUF_DYNFIELD
281 : 0 : (pkt, timestamp_off, uint64_t *) = skew;
282 : 0 : fs->ts_skew = skew;
283 : 0 : timestamp_mark.ts = rte_cpu_to_be_64(skew);
284 : 0 : } else if (tx_pkt_times_intra) {
285 : 0 : skew += tx_pkt_times_intra;
286 : 0 : pkt->ol_flags |= timestamp_mask;
287 : 0 : *RTE_MBUF_DYNFIELD
288 : 0 : (pkt, timestamp_off, uint64_t *) = skew;
289 : 0 : fs->ts_skew = skew;
290 : 0 : timestamp_mark.ts = rte_cpu_to_be_64(skew);
291 : : } else {
292 : 0 : timestamp_mark.ts = RTE_BE64(0);
293 : : }
294 : 0 : copy_buf_to_pkt(×tamp_mark, sizeof(timestamp_mark), pkt,
295 : : sizeof(struct rte_ether_hdr) +
296 : : sizeof(struct rte_ipv4_hdr) +
297 : : sizeof(pkt_udp_hdr));
298 : : }
299 : : /*
300 : : * Complete first mbuf of packet and append it to the
301 : : * burst of packets to be transmitted.
302 : : */
303 : 0 : pkt->nb_segs = nb_segs;
304 : 0 : pkt->pkt_len = pkt_len;
305 : :
306 : 0 : return true;
307 : : }
308 : :
309 : : /*
310 : : * Transmit a burst of multi-segments packets.
311 : : */
312 : : static bool
313 : 0 : pkt_burst_transmit(struct fwd_stream *fs)
314 : : {
315 : : struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
316 : : struct rte_port *txp;
317 : : struct rte_mbuf *pkt;
318 : : struct rte_mempool *mbp;
319 : : struct rte_ether_hdr eth_hdr;
320 : : uint16_t nb_tx;
321 : : uint16_t nb_pkt;
322 : : uint16_t vlan_tci, vlan_tci_outer;
323 : : uint64_t ol_flags = 0;
324 : : uint64_t tx_offloads;
325 : :
326 : 0 : mbp = current_fwd_lcore()->mbp;
327 : 0 : txp = &ports[fs->tx_port];
328 : 0 : tx_offloads = txp->dev_conf.txmode.offloads;
329 : 0 : vlan_tci = txp->tx_vlan_id;
330 : 0 : vlan_tci_outer = txp->tx_vlan_id_outer;
331 : 0 : if (tx_offloads & RTE_ETH_TX_OFFLOAD_VLAN_INSERT)
332 : : ol_flags = RTE_MBUF_F_TX_VLAN;
333 : 0 : if (tx_offloads & RTE_ETH_TX_OFFLOAD_QINQ_INSERT)
334 : 0 : ol_flags |= RTE_MBUF_F_TX_QINQ;
335 : 0 : if (tx_offloads & RTE_ETH_TX_OFFLOAD_MACSEC_INSERT)
336 : 0 : ol_flags |= RTE_MBUF_F_TX_MACSEC;
337 : :
338 : : /*
339 : : * Initialize Ethernet header.
340 : : */
341 : 0 : rte_ether_addr_copy(&peer_eth_addrs[fs->peer_addr], ð_hdr.dst_addr);
342 : : rte_ether_addr_copy(&ports[fs->tx_port].eth_addr, ð_hdr.src_addr);
343 : 0 : eth_hdr.ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
344 : :
345 : 0 : if (rte_mempool_get_bulk(mbp, (void **)pkts_burst,
346 : : nb_pkt_per_burst) == 0) {
347 : 0 : for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) {
348 : 0 : if (unlikely(!pkt_burst_prepare(pkts_burst[nb_pkt], mbp,
349 : : ð_hdr, vlan_tci,
350 : : vlan_tci_outer,
351 : : ol_flags,
352 : : nb_pkt, fs))) {
353 : 0 : rte_mempool_put_bulk(mbp,
354 : 0 : (void **)&pkts_burst[nb_pkt],
355 : 0 : nb_pkt_per_burst - nb_pkt);
356 : : break;
357 : : }
358 : : }
359 : : } else {
360 : 0 : for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) {
361 : 0 : pkt = rte_mbuf_raw_alloc(mbp);
362 : 0 : if (pkt == NULL)
363 : : break;
364 : 0 : if (unlikely(!pkt_burst_prepare(pkt, mbp, ð_hdr,
365 : : vlan_tci,
366 : : vlan_tci_outer,
367 : : ol_flags,
368 : : nb_pkt, fs))) {
369 : 0 : rte_pktmbuf_free(pkt);
370 : 0 : break;
371 : : }
372 : 0 : pkts_burst[nb_pkt] = pkt;
373 : : }
374 : : }
375 : :
376 : 0 : if (nb_pkt == 0)
377 : : return false;
378 : :
379 : 0 : nb_tx = common_fwd_stream_transmit(fs, pkts_burst, nb_pkt);
380 : :
381 : 0 : if (txonly_multi_flow)
382 : 0 : RTE_PER_LCORE(_src_port_var) -= nb_pkt - nb_tx;
383 : :
384 : 0 : if (unlikely(nb_tx < nb_pkt)) {
385 : 0 : if (verbose_level > 0 && fs->fwd_dropped == 0)
386 : 0 : printf("port %d tx_queue %d - drop "
387 : : "(nb_pkt:%u - nb_tx:%u)=%u packets\n",
388 : 0 : fs->tx_port, fs->tx_queue,
389 : : (unsigned) nb_pkt, (unsigned) nb_tx,
390 : 0 : (unsigned) (nb_pkt - nb_tx));
391 : : }
392 : :
393 : : return true;
394 : : }
395 : :
396 : : static int
397 : 0 : tx_only_begin(portid_t pi)
398 : : {
399 : : uint16_t pkt_hdr_len, pkt_data_len;
400 : : int dynf;
401 : :
402 : : pkt_hdr_len = (uint16_t)(sizeof(struct rte_ether_hdr) +
403 : : sizeof(struct rte_ipv4_hdr) +
404 : : sizeof(struct rte_udp_hdr));
405 : 0 : pkt_data_len = tx_pkt_length - pkt_hdr_len;
406 : :
407 : 0 : if ((tx_pkt_split == TX_PKT_SPLIT_RND || txonly_multi_flow) &&
408 : 0 : tx_pkt_seg_lengths[0] < pkt_hdr_len) {
409 : 0 : TESTPMD_LOG(ERR,
410 : : "Random segment number or multiple flow is enabled, "
411 : : "but tx_pkt_seg_lengths[0] %u < %u (needed)\n",
412 : : tx_pkt_seg_lengths[0], pkt_hdr_len);
413 : 0 : return -EINVAL;
414 : : }
415 : :
416 : 0 : setup_pkt_udp_ip_headers(&pkt_ip_hdr, &pkt_udp_hdr, pkt_data_len);
417 : :
418 : 0 : timestamp_enable = false;
419 : 0 : timestamp_mask = 0;
420 : 0 : timestamp_off = -1;
421 : 0 : dynf = rte_mbuf_dynflag_lookup
422 : : (RTE_MBUF_DYNFLAG_TX_TIMESTAMP_NAME, NULL);
423 : 0 : if (dynf >= 0)
424 : 0 : timestamp_mask = 1ULL << dynf;
425 : 0 : dynf = rte_mbuf_dynfield_lookup
426 : : (RTE_MBUF_DYNFIELD_TIMESTAMP_NAME, NULL);
427 : 0 : if (dynf >= 0)
428 : 0 : timestamp_off = dynf;
429 : 0 : timestamp_enable = tx_pkt_times_inter &&
430 : 0 : timestamp_mask &&
431 : 0 : timestamp_off >= 0 &&
432 : 0 : !rte_eth_read_clock(pi, ×tamp_initial[pi]);
433 : :
434 : 0 : if (timestamp_enable) {
435 : : pkt_hdr_len += sizeof(struct tx_timestamp);
436 : :
437 : 0 : if (tx_pkt_split == TX_PKT_SPLIT_RND) {
438 : 0 : if (tx_pkt_seg_lengths[0] < pkt_hdr_len) {
439 : 0 : TESTPMD_LOG(ERR,
440 : : "Time stamp and random segment number are enabled, "
441 : : "but tx_pkt_seg_lengths[0] %u < %u (needed)\n",
442 : : tx_pkt_seg_lengths[0], pkt_hdr_len);
443 : 0 : return -EINVAL;
444 : : }
445 : : } else {
446 : : uint16_t total = 0;
447 : : uint8_t i;
448 : :
449 : 0 : for (i = 0; i < tx_pkt_nb_segs; i++) {
450 : 0 : total += tx_pkt_seg_lengths[i];
451 : 0 : if (total >= pkt_hdr_len)
452 : : break;
453 : : }
454 : :
455 : 0 : if (total < pkt_hdr_len) {
456 : 0 : TESTPMD_LOG(ERR,
457 : : "Not enough Tx segment space for time stamp info, "
458 : : "total %u < %u (needed)\n",
459 : : total, pkt_hdr_len);
460 : 0 : return -EINVAL;
461 : : }
462 : : }
463 : : }
464 : :
465 : : /* Make sure all settings are visible on forwarding cores.*/
466 : : rte_wmb();
467 : 0 : return 0;
468 : : }
469 : :
470 : : static void
471 : 0 : tx_only_stream_init(struct fwd_stream *fs)
472 : : {
473 : 0 : fs->disabled = ports[fs->tx_port].txq[fs->tx_queue].state ==
474 : : RTE_ETH_QUEUE_STATE_STOPPED;
475 : 0 : }
476 : :
477 : : struct fwd_engine tx_only_engine = {
478 : : .fwd_mode_name = "txonly",
479 : : .port_fwd_begin = tx_only_begin,
480 : : .stream_init = tx_only_stream_init,
481 : : .packet_fwd = pkt_burst_transmit,
482 : : };
|