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