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