1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2018 Red Hat Corp. 3 */ 4 5 #include <stdarg.h> 6 #include <stdio.h> 7 #include <stdbool.h> 8 #include <string.h> 9 #include <errno.h> 10 #include <stdint.h> 11 #include <unistd.h> 12 #include <inttypes.h> 13 14 #include <sys/queue.h> 15 #include <sys/stat.h> 16 17 #include <rte_common.h> 18 #include <rte_log.h> 19 #include <rte_debug.h> 20 #include <rte_cycles.h> 21 #include <rte_memory.h> 22 #include <rte_launch.h> 23 #include <rte_eal.h> 24 #include <rte_per_lcore.h> 25 #include <rte_lcore.h> 26 #include <rte_memcpy.h> 27 #include <rte_mempool.h> 28 #include <rte_mbuf.h> 29 #include <rte_ethdev.h> 30 #include <rte_flow.h> 31 #include <rte_malloc.h> 32 33 #include "testpmd.h" 34 35 struct noisy_config { 36 struct rte_ring *f; 37 uint64_t prev_time; 38 char *vnf_mem; 39 bool do_buffering; 40 bool do_flush; 41 bool do_sim; 42 }; 43 44 struct noisy_config *noisy_cfg[RTE_MAX_ETHPORTS]; 45 46 static inline void 47 do_write(char *vnf_mem) 48 { 49 uint64_t i = rte_rand(); 50 uint64_t w = rte_rand(); 51 52 vnf_mem[i % ((noisy_lkup_mem_sz * 1024 * 1024) / 53 RTE_CACHE_LINE_SIZE)] = w; 54 } 55 56 static inline void 57 do_read(char *vnf_mem) 58 { 59 uint64_t i = rte_rand(); 60 uint64_t r; 61 62 r = vnf_mem[i % ((noisy_lkup_mem_sz * 1024 * 1024) / 63 RTE_CACHE_LINE_SIZE)]; 64 r++; 65 } 66 67 static inline void 68 do_readwrite(char *vnf_mem) 69 { 70 do_read(vnf_mem); 71 do_write(vnf_mem); 72 } 73 74 /* 75 * Simulate route lookups as defined by commandline parameters 76 */ 77 static void 78 sim_memory_lookups(struct noisy_config *ncf, uint16_t nb_pkts) 79 { 80 uint16_t i, j; 81 82 if (!ncf->do_sim) 83 return; 84 85 for (i = 0; i < nb_pkts; i++) { 86 for (j = 0; j < noisy_lkup_num_writes; j++) 87 do_write(ncf->vnf_mem); 88 for (j = 0; j < noisy_lkup_num_reads; j++) 89 do_read(ncf->vnf_mem); 90 for (j = 0; j < noisy_lkup_num_reads_writes; j++) 91 do_readwrite(ncf->vnf_mem); 92 } 93 } 94 95 static uint16_t 96 do_retry(uint16_t nb_rx, uint16_t nb_tx, struct rte_mbuf **pkts, 97 struct fwd_stream *fs) 98 { 99 uint32_t retry = 0; 100 101 while (nb_tx < nb_rx && retry++ < burst_tx_retry_num) { 102 rte_delay_us(burst_tx_delay_time); 103 nb_tx += rte_eth_tx_burst(fs->tx_port, fs->tx_queue, 104 &pkts[nb_tx], nb_rx - nb_tx); 105 } 106 107 return nb_tx; 108 } 109 110 static uint32_t 111 drop_pkts(struct rte_mbuf **pkts, uint16_t nb_rx, uint16_t nb_tx) 112 { 113 if (nb_tx < nb_rx) { 114 do { 115 rte_pktmbuf_free(pkts[nb_tx]); 116 } while (++nb_tx < nb_rx); 117 } 118 119 return nb_rx - nb_tx; 120 } 121 122 /* 123 * Forwarding of packets in noisy VNF mode. Forward packets but perform 124 * memory operations first as specified on cmdline. 125 * 126 * Depending on which commandline parameters are specified we have 127 * different cases to handle: 128 * 129 * 1. No FIFO size was given, so we don't do buffering of incoming 130 * packets. This case is pretty much what iofwd does but in this case 131 * we also do simulation of memory accesses (depending on which 132 * parameters were specified for it). 133 * 2. User wants do buffer packets in a FIFO and sent out overflowing 134 * packets. 135 * 3. User wants a FIFO and specifies a time in ms to flush all packets 136 * out of the FIFO 137 * 4. Cases 2 and 3 combined 138 */ 139 static void 140 pkt_burst_noisy_vnf(struct fwd_stream *fs) 141 { 142 const uint64_t freq_khz = rte_get_timer_hz() / 1000; 143 struct noisy_config *ncf = noisy_cfg[fs->rx_port]; 144 struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; 145 struct rte_mbuf *tmp_pkts[MAX_PKT_BURST]; 146 uint16_t nb_deqd = 0; 147 uint16_t nb_rx = 0; 148 uint16_t nb_tx = 0; 149 uint16_t nb_enqd; 150 unsigned int fifo_free; 151 uint64_t delta_ms; 152 bool needs_flush = false; 153 uint64_t now; 154 155 nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, 156 pkts_burst, nb_pkt_per_burst); 157 inc_rx_burst_stats(fs, nb_rx); 158 if (unlikely(nb_rx == 0)) 159 goto flush; 160 fs->rx_packets += nb_rx; 161 162 if (!ncf->do_buffering) { 163 sim_memory_lookups(ncf, nb_rx); 164 nb_tx = rte_eth_tx_burst(fs->tx_port, fs->tx_queue, 165 pkts_burst, nb_rx); 166 if (unlikely(nb_tx < nb_rx) && fs->retry_enabled) 167 nb_tx += do_retry(nb_rx, nb_tx, pkts_burst, fs); 168 inc_tx_burst_stats(fs, nb_tx); 169 fs->tx_packets += nb_tx; 170 fs->fwd_dropped += drop_pkts(pkts_burst, nb_rx, nb_tx); 171 return; 172 } 173 174 fifo_free = rte_ring_free_count(ncf->f); 175 if (fifo_free >= nb_rx) { 176 nb_enqd = rte_ring_enqueue_burst(ncf->f, 177 (void **) pkts_burst, nb_rx, NULL); 178 if (nb_enqd < nb_rx) 179 fs->fwd_dropped += drop_pkts(pkts_burst, 180 nb_rx, nb_enqd); 181 } else { 182 nb_deqd = rte_ring_dequeue_burst(ncf->f, 183 (void **) tmp_pkts, nb_rx, NULL); 184 nb_enqd = rte_ring_enqueue_burst(ncf->f, 185 (void **) pkts_burst, nb_deqd, NULL); 186 if (nb_deqd > 0) { 187 nb_tx = rte_eth_tx_burst(fs->tx_port, 188 fs->tx_queue, tmp_pkts, 189 nb_deqd); 190 if (unlikely(nb_tx < nb_rx) && fs->retry_enabled) 191 nb_tx += do_retry(nb_rx, nb_tx, tmp_pkts, fs); 192 inc_tx_burst_stats(fs, nb_tx); 193 fs->fwd_dropped += drop_pkts(tmp_pkts, nb_deqd, nb_tx); 194 } 195 } 196 197 sim_memory_lookups(ncf, nb_enqd); 198 199 flush: 200 if (ncf->do_flush) { 201 if (!ncf->prev_time) 202 now = ncf->prev_time = rte_get_timer_cycles(); 203 else 204 now = rte_get_timer_cycles(); 205 delta_ms = (now - ncf->prev_time) / freq_khz; 206 needs_flush = delta_ms >= noisy_tx_sw_buf_flush_time && 207 noisy_tx_sw_buf_flush_time > 0 && !nb_tx; 208 } 209 while (needs_flush && !rte_ring_empty(ncf->f)) { 210 unsigned int sent; 211 nb_deqd = rte_ring_dequeue_burst(ncf->f, (void **)tmp_pkts, 212 MAX_PKT_BURST, NULL); 213 sent = rte_eth_tx_burst(fs->tx_port, fs->tx_queue, 214 tmp_pkts, nb_deqd); 215 if (unlikely(sent < nb_deqd) && fs->retry_enabled) 216 nb_tx += do_retry(nb_rx, nb_tx, tmp_pkts, fs); 217 inc_tx_burst_stats(fs, nb_tx); 218 fs->fwd_dropped += drop_pkts(tmp_pkts, nb_deqd, sent); 219 ncf->prev_time = rte_get_timer_cycles(); 220 } 221 } 222 223 #define NOISY_STRSIZE 256 224 #define NOISY_RING "noisy_ring_%d\n" 225 226 static void 227 noisy_fwd_end(portid_t pi) 228 { 229 rte_ring_free(noisy_cfg[pi]->f); 230 rte_free(noisy_cfg[pi]->vnf_mem); 231 rte_free(noisy_cfg[pi]); 232 } 233 234 static void 235 noisy_fwd_begin(portid_t pi) 236 { 237 struct noisy_config *n; 238 char name[NOISY_STRSIZE]; 239 240 noisy_cfg[pi] = rte_zmalloc("testpmd noisy fifo and timers", 241 sizeof(struct noisy_config), 242 RTE_CACHE_LINE_SIZE); 243 if (noisy_cfg[pi] == NULL) { 244 rte_exit(EXIT_FAILURE, 245 "rte_zmalloc(%d) struct noisy_config) failed\n", 246 (int) pi); 247 } 248 n = noisy_cfg[pi]; 249 n->do_buffering = noisy_tx_sw_bufsz > 0; 250 n->do_sim = noisy_lkup_num_writes + noisy_lkup_num_reads + 251 noisy_lkup_num_reads_writes; 252 n->do_flush = noisy_tx_sw_buf_flush_time > 0; 253 254 if (n->do_buffering) { 255 snprintf(name, NOISY_STRSIZE, NOISY_RING, pi); 256 n->f = rte_ring_create(name, noisy_tx_sw_bufsz, 257 rte_socket_id(), 0); 258 if (!n->f) 259 rte_exit(EXIT_FAILURE, 260 "rte_ring_create(%d), size %d) failed\n", 261 (int) pi, 262 noisy_tx_sw_bufsz); 263 } 264 if (noisy_lkup_mem_sz > 0) { 265 n->vnf_mem = (char *) rte_zmalloc("vnf sim memory", 266 noisy_lkup_mem_sz * 1024 * 1024, 267 RTE_CACHE_LINE_SIZE); 268 if (!n->vnf_mem) 269 rte_exit(EXIT_FAILURE, 270 "rte_zmalloc(%" PRIu64 ") for vnf memory) failed\n", 271 noisy_lkup_mem_sz); 272 } else if (n->do_sim) { 273 rte_exit(EXIT_FAILURE, 274 "--noisy-lkup-memory-size must be > 0\n"); 275 } 276 } 277 278 struct fwd_engine noisy_vnf_engine = { 279 .fwd_mode_name = "noisy", 280 .port_fwd_begin = noisy_fwd_begin, 281 .port_fwd_end = noisy_fwd_end, 282 .packet_fwd = pkt_burst_noisy_vnf, 283 }; 284