API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_cpu_bench.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2018 Linaro Limited
3 * Copyright (c) 2018-2026 Nokia
4 */
5
14#include <odp_api.h>
15#include <odp/helper/odph_api.h>
16
17#include <getopt.h>
18#include <inttypes.h>
19#include <signal.h>
20#include <stdlib.h>
21#include <unistd.h>
22
23/* Queues are divided into groups and tests packets are passed only between
24 * queues which belong to the same group. */
25#define MAX_GROUPS 64
26#define QUEUES_PER_GROUP 4
27#define PKTS_PER_QUEUE 256
28
29#define MAX_EVENT_BURST 32
30#define CRC_INIT_VAL 123456789
31#define PASS_PACKETS 10000
32
33/* Default number of entries in the test lookup table */
34#define DEF_LOOKUP_TBL_SIZE (1024 * 1024)
35
36#define MAX_WORKERS \
37 (((ODP_THREAD_COUNT_MAX - 1) > (MAX_GROUPS * QUEUES_PER_GROUP)) ? \
38 (MAX_GROUPS * QUEUES_PER_GROUP) : \
39 (ODP_THREAD_COUNT_MAX - 1))
40
41ODP_STATIC_ASSERT(MAX_WORKERS <= MAX_GROUPS * QUEUES_PER_GROUP,
42 "Not enough queues for all workers");
43
44/* Get rid of path in filename - only for unix-type paths using '/' */
45#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
46 strrchr((file_name), '/') + 1 : (file_name))
47
48/* Test dummy lookup table entry */
49typedef struct {
50 uint64_t idx;
51 uint32_t val0;
52 uint32_t val1;
53} lookup_entry_t;
54
55/* Test packet */
56typedef struct {
57 uint32_t seq;
58 uint32_t crc;
59 uint16_t group;
60} test_hdr_t;
61
62/* Parsed application arguments */
63typedef struct {
64 uint64_t lookup_tbl_size; /* Lookup table size */
65 int accuracy; /* Number of seconds between stats prints */
66 unsigned int cpu_count; /* CPU count */
67 int time; /* Time in seconds to run */
68} appl_args_t;
69
70/* Statistics */
71typedef union ODP_ALIGNED_CACHE {
72 struct {
73 /* Number of processed packets */
74 uint64_t pkts;
75 /* Number of dropped packets */
76 uint64_t dropped_pkts;
77 /* Time spent processing packets */
78 uint64_t nsec;
79 /* Cycles spent processing packets */
80 uint64_t cycles;
81 } s;
82
83 uint8_t padding[ODP_CACHE_LINE_SIZE];
84} stats_t;
85
86/* Thread specific data */
87typedef struct thread_args_t {
88 stats_t stats;
89 uint16_t idx;
90} thread_args_t;
91
92/* Grouping of all global data */
93typedef struct {
94 /* Thread specific arguments */
95 thread_args_t thread[MAX_WORKERS];
96 /* Barriers to synchronize main and workers */
97 odp_barrier_t init_barrier;
98 odp_barrier_t term_barrier;
99 /* Application (parsed) arguments */
100 appl_args_t appl;
101 /* Test queues */
102 odp_queue_t queue[MAX_GROUPS][QUEUES_PER_GROUP];
103 /* Test lookup table */
104 lookup_entry_t *lookup_tbl;
105 /* Break workers loop if set to 1 */
106 odp_atomic_u32_t exit_threads;
107} args_t;
108
109/* Global pointer to args */
110static args_t *gbl_args;
111
112static const uint8_t test_udp_packet[] = {
113 0x00, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x00, 0x01,
114 0x02, 0x03, 0x04, 0x05, 0x08, 0x00, 0x45, 0x00,
115 0x02, 0x1C, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
116 0xF7, 0x7C, 0xC0, 0xA8, 0x00, 0x01, 0xC0, 0xA8,
117 0x00, 0x02, 0x04, 0xD2, 0x1A, 0x82, 0x02, 0x08,
118 0x24, 0x1E, 0xC9, 0x56, 0xB4, 0xD6, 0x4B, 0x64,
119 0xB3, 0x01, 0xA1, 0x97, 0x4D, 0xD1, 0xA4, 0x76,
120 0xF5, 0x7B, 0x27, 0x22, 0x6C, 0xA9, 0xED, 0x29,
121 0x6E, 0x02, 0x80, 0xF7, 0xC4, 0x2D, 0x2A, 0x96,
122 0x2D, 0xF6, 0x02, 0x8E, 0x89, 0x9F, 0x8C, 0xF4,
123 0x0D, 0xC5, 0xE5, 0x1F, 0xA1, 0x52, 0xC3, 0x4B,
124 0x5C, 0x4C, 0xDF, 0x14, 0x05, 0x6A, 0xA8, 0xD7,
125 0xAD, 0x4F, 0x22, 0xA6, 0xB8, 0xF9, 0x52, 0x5A,
126 0xB8, 0xF9, 0xE2, 0x2C, 0x05, 0x2A, 0x6F, 0xF2,
127 0xCA, 0xA1, 0xA7, 0xC3, 0x56, 0xE1, 0xDB, 0xC1,
128 0xDB, 0x86, 0x26, 0x55, 0xAC, 0xBE, 0xE1, 0x3D,
129 0x82, 0x86, 0xB9, 0xDE, 0x3E, 0xD3, 0x11, 0xAB,
130 0x65, 0x6A, 0xED, 0x1B, 0x60, 0xBE, 0x69, 0x71,
131 0xB2, 0xA8, 0x5B, 0xB1, 0x06, 0xE3, 0x48, 0x14,
132 0xC9, 0x13, 0x73, 0xDA, 0xBE, 0xE4, 0x7A, 0x5F,
133 0xC0, 0xE0, 0xCA, 0xF3, 0x7A, 0xCA, 0x3F, 0xC9,
134 0x4A, 0xEE, 0x47, 0x76, 0x67, 0xF0, 0x0D, 0x3F,
135 0x7F, 0x3D, 0x69, 0xEA, 0x39, 0x53, 0x7C, 0xE3,
136 0xED, 0x78, 0x79, 0x47, 0x60, 0x95, 0xCB, 0xDC,
137 0x26, 0x60, 0x46, 0xAC, 0x47, 0xDA, 0x4C, 0x4D,
138 0x0F, 0xE1, 0x68, 0x43, 0xBC, 0xCD, 0x4E, 0xFE,
139 0x2E, 0xD6, 0xC2, 0x6E, 0x63, 0xEA, 0xB3, 0x98,
140 0xCA, 0x8F, 0x7F, 0x05, 0xDF, 0x72, 0x8F, 0x6E,
141 0x3E, 0x6D, 0xC7, 0x94, 0x59, 0x9D, 0x15, 0x5B,
142 0xB8, 0x02, 0x52, 0x4F, 0x68, 0x3A, 0xF1, 0xFF,
143 0xA9, 0xA4, 0x30, 0x29, 0xE0, 0x1C, 0xA0, 0x1B,
144 0x50, 0xAB, 0xFD, 0x06, 0x84, 0xD4, 0x33, 0x51,
145 0x01, 0xB3, 0x5F, 0x49, 0x5F, 0x21, 0xA0, 0xA1,
146 0xC9, 0x08, 0xB3, 0xDF, 0x72, 0x9B, 0x5B, 0x70,
147 0x89, 0x96, 0x08, 0x25, 0x88, 0x1E, 0xED, 0x52,
148 0xDC, 0x98, 0xA0, 0xB8, 0x83, 0x2A, 0xA0, 0x90,
149 0x45, 0xC9, 0x77, 0xD2, 0x19, 0xD7, 0x6B, 0xAB,
150 0x49, 0x67, 0x7C, 0xD1, 0xE0, 0x23, 0xA2, 0x36,
151 0xB2, 0x91, 0x3B, 0x23, 0x3B, 0x03, 0x36, 0xAF,
152 0xAD, 0x81, 0xFA, 0x6F, 0x68, 0xD5, 0xBE, 0x73,
153 0x1D, 0x56, 0x8A, 0xE8, 0x1A, 0xB4, 0xA8, 0x7C,
154 0xF3, 0x82, 0x10, 0xD0, 0xF2, 0x1D, 0x9C, 0xEA,
155 0xAB, 0xE7, 0xEC, 0x53, 0x6D, 0x52, 0xBD, 0x29,
156 0x86, 0x21, 0xCE, 0xAA, 0xF3, 0x68, 0xA6, 0xEC,
157 0x7E, 0xCA, 0x6F, 0xEB, 0xE1, 0x81, 0x80, 0x7C,
158 0xF3, 0xE5, 0x22, 0xA0, 0x91, 0x08, 0xB7, 0x35,
159 0x15, 0x87, 0x0C, 0x77, 0x31, 0x9C, 0x2F, 0x73,
160 0xCE, 0x29, 0x6F, 0xC6, 0xAC, 0x9F, 0x68, 0xB8,
161 0x6A, 0xFC, 0xD3, 0xB5, 0x08, 0x98, 0xAE, 0xE4,
162 0x20, 0x84, 0x24, 0x69, 0xA5, 0xF5, 0x4A, 0x9D,
163 0x44, 0x26, 0x5A, 0xF9, 0x6B, 0x5E, 0x5D, 0xC8,
164 0x6F, 0xD4, 0x62, 0x91, 0xE5, 0x8E, 0x80, 0x05,
165 0xA1, 0x95, 0x09, 0xEA, 0xFE, 0x84, 0x6D, 0xC3,
166 0x0D, 0xD4, 0x32, 0xA4, 0x38, 0xB2, 0xF7, 0x9D,
167 0x58, 0xD3, 0x5D, 0x93, 0x5F, 0x67, 0x86, 0xE1,
168 0xAF, 0xFF, 0xE9, 0xFE, 0xF4, 0x71, 0x63, 0xE3,
169 0x3E, 0xE1, 0x7A, 0x80, 0x5A, 0x23, 0x4F, 0x5B,
170 0x54, 0x21, 0x0E, 0xE2, 0xAF, 0x01, 0x2E, 0xA4,
171 0xF5, 0x1F, 0x59, 0x96, 0x3E, 0x82, 0xF3, 0x44,
172 0xDF, 0xA6, 0x7C, 0x64, 0x5D, 0xC7, 0x79, 0xA1,
173 0x17, 0xE1, 0x06, 0x14, 0x3E, 0x1B, 0x46, 0xCA,
174 0x71, 0xC8, 0x05, 0x62, 0xD0, 0x56, 0x23, 0x9B,
175 0xBA, 0xFE, 0x6D, 0xA8, 0x03, 0x4C, 0x23, 0xD8,
176 0x98, 0x8A, 0xE8, 0x9C, 0x93, 0x8E, 0xB7, 0x24,
177 0x31, 0x2A, 0x81, 0x72, 0x8F, 0x13, 0xD4, 0x7E,
178 0xEB, 0xB1, 0xEE, 0x33, 0xD9, 0xF4, 0x96, 0x5E,
179 0x6C, 0x3D, 0x45, 0x9C, 0xE0, 0x71, 0xA3, 0xFA,
180 0x17, 0x2B, 0xC3, 0x07, 0xD6, 0x86, 0xA2, 0x06,
181 0xC5, 0x33, 0xF0, 0xEA, 0x25, 0x70, 0x68, 0x56,
182 0xD5, 0xB0
183};
184
185static void sig_handler(int signo ODP_UNUSED)
186{
187 if (gbl_args == NULL)
188 return;
189 odp_atomic_store_u32(&gbl_args->exit_threads, 1);
190}
191
192static inline void init_packet(odp_packet_t pkt, uint32_t seq, uint16_t group)
193{
194 odp_una_u32_t *payload;
195 test_hdr_t *hdr;
197
198 param.proto = ODP_PROTO_ETH;
200 param.chksums.all_chksum = 0;
201 if (odp_packet_parse(pkt, 0, &param))
202 ODPH_ABORT("odp_packet_parse() failed\n");
203
204 /* Modify UDP payload and update checksum */
205 payload = odp_packet_offset(pkt, odp_packet_l4_offset(pkt) +
206 ODPH_UDPHDR_LEN, NULL, NULL);
207 *payload = seq;
208 if (odph_udp_chksum_set(pkt))
209 ODPH_ABORT("odph_udp_chksum_set() failed\n");
210
211 /* Test header is stored in user area */
212 hdr = odp_packet_user_area(pkt);
213 hdr->seq = seq;
214 hdr->group = group;
215 hdr->crc = odp_hash_crc32c(odp_packet_data(pkt), odp_packet_len(pkt),
216 CRC_INIT_VAL);
217}
218
219static inline odp_queue_t work_on_event(odp_event_t event)
220{
221 odp_packet_t pkt;
223 odph_udphdr_t *udp_hdr;
224 test_hdr_t *hdr;
225 lookup_entry_t *lookup_entry;
226 odp_una_u32_t *payload;
227 uint32_t crc;
228 uint32_t pkt_len;
229 uint8_t *data;
230 uint32_t new_val;
231 uint32_t old_val;
232
233 if (odp_event_type(event) != ODP_EVENT_PACKET)
234 return ODP_QUEUE_INVALID;
235
236 pkt = odp_packet_from_event(event);
237 hdr = odp_packet_user_area(pkt);
238 pkt_len = odp_packet_len(pkt);
239 data = odp_packet_data(pkt);
240
241 crc = odp_hash_crc32c(data, pkt_len, CRC_INIT_VAL);
242 if (crc != hdr->crc)
243 ODPH_ERR("Error: Invalid packet crc\n");
244
245 param.proto = ODP_PROTO_ETH;
247 param.chksums.all_chksum = 1;
248 if (odp_packet_parse(pkt, 0, &param)) {
249 ODPH_ERR("Error: odp_packet_parse() failed\n");
250 return ODP_QUEUE_INVALID;
251 }
252
253 /* Modify packet data using lookup table value and sequence number, and
254 * update UDP checksum accordingly. */
255 lookup_entry = &gbl_args->lookup_tbl[(crc + hdr->seq) %
256 gbl_args->appl.lookup_tbl_size];
257 udp_hdr = odp_packet_l4_ptr(pkt, NULL);
258 payload = odp_packet_offset(pkt, odp_packet_l4_offset(pkt) +
259 ODPH_UDPHDR_LEN, NULL, NULL);
260 old_val = *payload;
261 *payload += lookup_entry->idx % 2 ? lookup_entry->val1 :
262 lookup_entry->val0;
263 new_val = *payload;
264 udp_hdr->chksum = ~(~udp_hdr->chksum + (-old_val) + new_val);
265
266 payload++;
267 old_val = *payload;
268 *payload += hdr->seq;
269 new_val = *payload;
270 udp_hdr->chksum = ~(~udp_hdr->chksum + (-old_val) + new_val);
271
272 hdr->crc = odp_hash_crc32c(data, pkt_len, CRC_INIT_VAL);
273
274 return gbl_args->queue[hdr->group][hdr->seq++ % QUEUES_PER_GROUP];
275}
276
280static int run_thread(void *arg)
281{
282 thread_args_t *thr_args = arg;
283 stats_t *stats = &thr_args->stats;
284 odp_time_t t1, t2;
285 uint64_t c1, c2;
286
287 odp_barrier_wait(&gbl_args->init_barrier);
288
289 c1 = odp_cpu_cycles();
290 t1 = odp_time_local();
291
292 while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
293 odp_event_t event_tbl[MAX_EVENT_BURST];
294 odp_queue_t dst_queue;
295 int num_events;
296 int i;
297
298 num_events = odp_schedule_multi(NULL, ODP_SCHED_NO_WAIT,
299 event_tbl, MAX_EVENT_BURST);
300 if (num_events <= 0)
301 continue;
302
303 for (i = 0; i < num_events; i++) {
304 odp_event_t event = event_tbl[i];
305
306 dst_queue = work_on_event(event);
307 if (odp_unlikely(dst_queue == ODP_QUEUE_INVALID)) {
308 stats->s.dropped_pkts++;
309 odp_event_free(event);
310 continue;
311 }
312
313 if (odp_unlikely(odp_queue_enq(dst_queue, event))) {
314 ODPH_ERR("Error: odp_queue_enq() failed\n");
315 stats->s.dropped_pkts++;
316 odp_event_free(event);
317 break;
318 }
319
320 stats->s.pkts++;
321 }
322 }
323
324 c2 = odp_cpu_cycles();
325 t2 = odp_time_local();
326
327 stats->s.cycles = c2 - c1;
328 stats->s.nsec = odp_time_diff_ns(t2, t1);
329
330 odp_barrier_wait(&gbl_args->term_barrier);
331
332 /* Free remaining events in queues */
333 while (1) {
334 odp_event_t ev;
335
336 ev = odp_schedule(NULL,
338
339 if (ev == ODP_EVENT_INVALID)
340 break;
341
342 odp_event_free(ev);
343 }
344
345 return 0;
346}
347
348/*
349 * Print usage information
350 */
351static void usage(char *progname)
352{
353 printf("\n"
354 "OpenDataPlane CPU benchmarking application.\n"
355 "\n"
356 "Usage: %s [options]\n"
357 "\n"
358 " E.g. %s -c 4 -t 30\n"
359 "Options:\n"
360 " -c, --count <number> CPU count, 0=all available, default=1\n"
361 " -t, --time <sec> Time in seconds to run\n"
362 " (default is 10 second).\n"
363 " -a, --accuracy <sec> Time in seconds get print statistics\n"
364 " (default is 1 second).\n"
365 " -l, --lookup_tbl <num> Number of entries in dummy lookup table\n"
366 " (default is %d).\n"
367 " -h, --help Display help and exit.\n\n"
368 "\n", NO_PATH(progname), NO_PATH(progname), DEF_LOOKUP_TBL_SIZE);
369}
370
378static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
379{
380 int opt;
381
382 static const struct option longopts[] = {
383 {"accuracy", required_argument, NULL, 'a'},
384 {"cpu", required_argument, NULL, 'c'},
385 {"lookup_tbl", required_argument, NULL, 'l'},
386 {"time", required_argument, NULL, 't'},
387 {"help", no_argument, NULL, 'h'},
388 {NULL, 0, NULL, 0}
389 };
390
391 static const char *shortopts = "+a:c:l:t:h";
392
393 appl_args->accuracy = 1; /* Get and print pps stats second */
394 appl_args->cpu_count = 1;
395 appl_args->lookup_tbl_size = DEF_LOOKUP_TBL_SIZE;
396 appl_args->time = 10; /* Loop forever if time to run is 0 */
397
398 while (1) {
399 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
400
401 if (opt == -1)
402 break; /* No more options */
403
404 switch (opt) {
405 case 'a':
406 appl_args->accuracy = atoi(optarg);
407 break;
408 case 'c':
409 appl_args->cpu_count = atoi(optarg);
410 break;
411 case 'l':
412 appl_args->lookup_tbl_size = atoi(optarg);
413 break;
414 case 't':
415 appl_args->time = atoi(optarg);
416 break;
417 case 'h':
418 usage(argv[0]);
419 exit(EXIT_SUCCESS);
420 break;
421
422 default:
423 break;
424 }
425 }
426
427 if (appl_args->lookup_tbl_size < 1) {
428 printf("At least one lookup table entry required.\n");
429 exit(EXIT_FAILURE);
430 }
431}
432
433/*
434 * Print statistics
435 *
436 * num_workers Number of worker threads
437 * thr_stats Pointers to stats storage
438 * duration Number of seconds to loop
439 */
440static int print_stats(int num_workers, stats_t **thr_stats, int duration,
441 int accuracy)
442{
443 uint64_t pkts;
444 uint64_t dropped;
445 uint64_t pkts_prev = 0;
446 uint64_t nsec = 0;
447 uint64_t cycles = 0;
448 int i;
449 int elapsed = 0;
450 int stats_enabled = 1;
451 int loop_forever = (duration == 0);
452
453 if (accuracy <= 0) {
454 stats_enabled = 0;
455 accuracy = 1;
456 }
457 /* Wait for all threads to be ready*/
458 odp_barrier_wait(&gbl_args->init_barrier);
459
460 do {
461 uint64_t pps;
462
463 sleep(accuracy);
464
465 pkts = 0;
466 dropped = 0;
467 for (i = 0; i < num_workers; i++) {
468 pkts += thr_stats[i]->s.pkts;
469 dropped += thr_stats[i]->s.dropped_pkts;
470 }
471
472 pps = (pkts - pkts_prev) / accuracy;
473
474 if (stats_enabled) {
475 printf("%.2f Mpps, ", pps / 1000000.0);
476
477 printf("%" PRIu64 " dropped\n", dropped);
478 }
479
480 pkts_prev = pkts;
481 elapsed += accuracy;
482 } while (!odp_atomic_load_u32(&gbl_args->exit_threads) &&
483 (loop_forever || (elapsed < duration)));
484
485 odp_atomic_store_u32(&gbl_args->exit_threads, 1);
486 odp_barrier_wait(&gbl_args->term_barrier);
487
488 pkts = 0;
489 dropped = 0;
490 for (i = 0; i < num_workers; i++) {
491 pkts += thr_stats[i]->s.pkts;
492 dropped += thr_stats[i]->s.dropped_pkts;
493 nsec += thr_stats[i]->s.nsec;
494 cycles += thr_stats[i]->s.cycles;
495 }
496
497 printf("\nRESULTS - per thread (Million packets per sec):\n");
498 printf("-----------------------------------------------\n");
499 printf(" avg 1 2 3 4 5 6 7 8 9 10\n");
500 printf("%6.2f ", pkts / (nsec / 1000.0));
501
502 for (i = 0; i < num_workers; i++) {
503 if (i != 0 && (i % 10) == 0)
504 printf("\n ");
505
506 printf("%6.2f ", thr_stats[i]->s.pkts /
507 (thr_stats[i]->s.nsec / 1000.0));
508 }
509 printf("\n\n");
510
511 nsec /= num_workers;
512 printf("RESULTS - total over %i threads:\n", num_workers);
513 printf("----------------------------------\n");
514 printf(" avg packets per sec: %.3f M\n", pkts / (nsec / 1000.0));
515 printf(" avg cycles per packet: %" PRIu64 "\n", cycles / pkts);
516 printf(" dropped packets: %" PRIu64 "\n\n", dropped);
517
518 return pkts > PASS_PACKETS ? 0 : -1;
519}
520
521static void gbl_args_init(args_t *args)
522{
523 memset(args, 0, sizeof(args_t));
524 odp_atomic_init_u32(&args->exit_threads, 0);
525}
526
530int main(int argc, char *argv[])
531{
532 stats_t *stats[MAX_WORKERS];
533 odph_helper_options_t helper_options;
534 odph_thread_t thread_tbl[MAX_WORKERS];
535 odph_thread_common_param_t thr_common;
536 odph_thread_param_t thr_param[MAX_WORKERS];
537 odp_cpumask_t cpumask;
538 odp_pool_capability_t pool_capa;
539 odp_pool_t pool;
540 odp_schedule_config_t schedule_config;
541 odp_shm_t shm;
542 odp_shm_t lookup_tbl_shm;
543 odp_pool_param_t params;
544 odp_instance_t instance;
545 odp_init_t init;
546 char cpumaskstr[ODP_CPUMASK_STR_SIZE];
547 uint32_t num_pkts;
548 uint32_t num_groups;
549 uint32_t num_queues;
550 uint32_t pkts_per_group;
551 uint32_t pkt_len;
552 uint32_t init_val;
553 unsigned int num_workers;
554 unsigned int i, j;
555 int ret = 0;
556
557 /* Let helper collect its own arguments (e.g. --odph_proc) */
558 argc = odph_parse_options(argc, argv);
559 if (odph_options(&helper_options)) {
560 ODPH_ERR("Error: reading ODP helper options failed.\n");
561 exit(EXIT_FAILURE);
562 }
563
564 odp_init_param_init(&init);
565
566 /* List features not to be used (may optimize performance) */
567 init.not_used.feat.cls = 1;
568 init.not_used.feat.compress = 1;
569 init.not_used.feat.crypto = 1;
570 init.not_used.feat.dma = 1;
571 init.not_used.feat.ipsec = 1;
572 init.not_used.feat.ml = 1;
573 init.not_used.feat.stash = 1;
574 init.not_used.feat.timer = 1;
575 init.not_used.feat.tm = 1;
576
577 init.mem_model = helper_options.mem_model;
578
579 /* Signal handler has to be registered before global init in case ODP
580 * implementation creates internal threads/processes. */
581 signal(SIGINT, sig_handler);
582
583 if (odp_init_global(&instance, &init, NULL)) {
584 ODPH_ERR("Error: ODP global init failed\n");
585 return -1;
586 }
587
588 if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
589 ODPH_ERR("Error: ODP local init failed\n");
590 exit(EXIT_FAILURE);
591 }
592
593 shm = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE,
594 0);
595 if (shm == ODP_SHM_INVALID) {
596 ODPH_ERR("Error: shared mem reserve failed.\n");
597 exit(EXIT_FAILURE);
598 }
599
600 gbl_args = odp_shm_addr(shm);
601 if (gbl_args == NULL) {
602 ODPH_ERR("Error: shared mem alloc failed\n");
603 exit(EXIT_FAILURE);
604 }
605 gbl_args_init(gbl_args);
606
607 /* Parse and store the application arguments */
608 parse_args(argc, argv, &gbl_args->appl);
609
610 lookup_tbl_shm = odp_shm_reserve("lookup_tbl_shm",
611 sizeof(lookup_entry_t) *
612 gbl_args->appl.lookup_tbl_size,
613 ODP_CACHE_LINE_SIZE, 0);
614 if (lookup_tbl_shm == ODP_SHM_INVALID) {
615 ODPH_ERR("Error: shared mem reserve failed.\n");
616 exit(EXIT_FAILURE);
617 }
618
619 gbl_args->lookup_tbl = odp_shm_addr(lookup_tbl_shm);
620 if (gbl_args->lookup_tbl == NULL) {
621 ODPH_ERR("Error: lookup table mem alloc failed\n");
622 exit(EXIT_FAILURE);
623 }
624
625 printf("\n");
627
628 /* Default to system CPU count unless user specified */
629 num_workers = MAX_WORKERS;
630 if (gbl_args->appl.cpu_count && gbl_args->appl.cpu_count < MAX_WORKERS)
631 num_workers = gbl_args->appl.cpu_count;
632
633 /* Get default worker cpumask */
634 num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
635 (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
636
637 printf("num worker threads: %i\n", num_workers);
638 printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
639 printf("cpu mask: %s\n", cpumaskstr);
640
641 odp_schedule_config_init(&schedule_config);
642 odp_schedule_config(&schedule_config);
643
644 /* Make sure a single queue can store all the packets in a group */
645 pkts_per_group = QUEUES_PER_GROUP * PKTS_PER_QUEUE;
646 if (schedule_config.queue_size &&
647 schedule_config.queue_size < pkts_per_group)
648 pkts_per_group = schedule_config.queue_size;
649
650 /* Divide queues evenly into groups */
651 if (schedule_config.num_queues < QUEUES_PER_GROUP) {
652 ODPH_ERR("Error: min %d queues required\n", QUEUES_PER_GROUP);
653 return -1;
654 }
655 num_queues = num_workers > schedule_config.num_queues ?
656 schedule_config.num_queues : num_workers;
657 num_groups = (num_queues + QUEUES_PER_GROUP - 1) / QUEUES_PER_GROUP;
658 if (num_groups * QUEUES_PER_GROUP > schedule_config.num_queues)
659 num_groups--;
660 num_queues = num_groups * QUEUES_PER_GROUP;
661
662 for (i = 0; i < num_groups; i++) {
663 for (j = 0; j < QUEUES_PER_GROUP; j++) {
664 odp_queue_t queue;
665 odp_queue_param_t param;
666
667 odp_queue_param_init(&param);
672 param.size = pkts_per_group;
673
674 queue = odp_queue_create(NULL, &param);
675 if (queue == ODP_QUEUE_INVALID) {
676 ODPH_ERR("Error: odp_queue_create() failed\n");
677 return -1;
678 }
679 gbl_args->queue[i][j] = queue;
680 }
681 }
682
683 /* Create packet pool */
684 if (odp_pool_capability(&pool_capa)) {
685 ODPH_ERR("Error: odp_pool_capability() failed\n");
686 exit(EXIT_FAILURE);
687 }
688 num_pkts = pkts_per_group * num_groups;
689 if (num_pkts > pool_capa.pkt.max_num)
690 num_pkts = pool_capa.pkt.max_num;
691
692 pkt_len = sizeof(test_udp_packet);
693 if (pool_capa.pkt.max_len && pkt_len > pool_capa.pkt.max_len)
694 pkt_len = pool_capa.pkt.max_len;
695
696 if (pool_capa.pkt.max_seg_len && pkt_len > pool_capa.pkt.max_seg_len)
697 pkt_len = pool_capa.pkt.max_seg_len;
698
699 if (pkt_len < sizeof(test_udp_packet)) {
700 ODPH_ERR("Error: min %dB single segment packets required\n",
701 (int)sizeof(test_udp_packet));
702 exit(EXIT_FAILURE);
703 }
704
705 if (pool_capa.pkt.max_uarea_size &&
706 pool_capa.pkt.max_uarea_size < sizeof(test_hdr_t)) {
707 ODPH_ERR("Error: min %dB of packet user area required\n",
708 (int)sizeof(test_hdr_t));
709 exit(EXIT_FAILURE);
710 }
711
712 odp_pool_param_init(&params);
713 params.pkt.len = pkt_len;
714 params.pkt.max_len = pkt_len;
715 params.pkt.seg_len = pkt_len;
716 params.pkt.num = num_pkts;
717 params.pkt.max_num = num_pkts;
718 params.pkt.uarea_size = sizeof(test_hdr_t);
719 params.type = ODP_POOL_PACKET;
720 pool = odp_pool_create("pkt_pool", &params);
721 if (pool == ODP_POOL_INVALID) {
722 ODPH_ERR("Error: packet pool create failed\n");
723 exit(EXIT_FAILURE);
724 }
725 odp_pool_print(pool);
726
727 printf("CPU bench args\n--------------\n");
728 printf(" workers: %u\n", num_workers);
729 printf(" queues: %" PRIu32 "\n", num_queues);
730 printf(" pkts: %" PRIu32 "\n", num_pkts);
731 printf(" pkt size: %" PRIu32 " B\n", pkt_len);
732 printf(" lookup entries: %" PRIu64 "\n\n",
733 gbl_args->appl.lookup_tbl_size);
734
735 /* Spread test packets into queues */
736 for (i = 0; i < num_pkts; i++) {
737 odp_packet_t pkt = odp_packet_alloc(pool, pkt_len);
738 odp_event_t ev;
739 odp_queue_t queue;
740 uint16_t group = i % num_groups;
741
742 if (pkt == ODP_PACKET_INVALID) {
743 ODPH_ERR("Error: odp_packet_alloc() failed\n");
744 return -1;
745 }
746
747 odp_packet_copy_from_mem(pkt, 0, pkt_len, test_udp_packet);
748
749 init_packet(pkt, i, group);
750
751 queue = gbl_args->queue[group][i % QUEUES_PER_GROUP];
752
753 ev = odp_packet_to_event(pkt);
754 if (odp_queue_enq(queue, ev)) {
755 ODPH_ERR("Error: odp_queue_enq() failed\n");
756 return -1;
757 }
758 }
759
760 odp_barrier_init(&gbl_args->init_barrier, num_workers + 1);
761 odp_barrier_init(&gbl_args->term_barrier, num_workers + 1);
762
763 /* Initialize lookup table */
764 init_val = CRC_INIT_VAL;
765 for (i = 0; i < gbl_args->appl.lookup_tbl_size; i++) {
766 uint32_t *val0 = &gbl_args->lookup_tbl[i].val0;
767 uint32_t *val1 = &gbl_args->lookup_tbl[i].val1;
768
769 gbl_args->lookup_tbl[i].idx = i;
770
771 *val0 = i;
772 *val0 = odp_hash_crc32c(val0, sizeof(uint32_t), init_val);
773 *val1 = odp_hash_crc32c(val0, sizeof(uint32_t), init_val);
774 init_val = *val1;
775 }
776
777 /* Create worker threads */
778 odph_thread_common_param_init(&thr_common);
779 thr_common.instance = instance;
780 thr_common.cpumask = &cpumask;
781
782 for (i = 0; i < num_workers; i++) {
783 gbl_args->thread[i].idx = i;
784 stats[i] = &gbl_args->thread[i].stats;
785
786 odph_thread_param_init(&thr_param[i]);
787 thr_param[i].start = run_thread;
788 thr_param[i].arg = &gbl_args->thread[i];
789 thr_param[i].thr_type = ODP_THREAD_WORKER;
790 }
791
792 memset(thread_tbl, 0, sizeof(thread_tbl));
793 odph_thread_create(thread_tbl, &thr_common, thr_param, num_workers);
794
795 ret = print_stats(num_workers, stats, gbl_args->appl.time,
796 gbl_args->appl.accuracy);
797
798 /* Master thread waits for other threads to exit */
799 odph_thread_join(thread_tbl, num_workers);
800
801 for (i = 0; i < num_groups; i++) {
802 for (j = 0; j < QUEUES_PER_GROUP; j++) {
803 if (odp_queue_destroy(gbl_args->queue[i][j])) {
804 ODPH_ERR("Error: queue destroy\n");
805 exit(EXIT_FAILURE);
806 }
807 }
808 }
809 gbl_args = NULL;
810 odp_mb_full();
811
812 if (odp_pool_destroy(pool)) {
813 ODPH_ERR("Error: pool destroy\n");
814 exit(EXIT_FAILURE);
815 }
816
817 if (odp_shm_free(shm)) {
818 ODPH_ERR("Error: shm free\n");
819 exit(EXIT_FAILURE);
820 }
821
822 if (odp_shm_free(lookup_tbl_shm)) {
823 ODPH_ERR("Error: shm free\n");
824 exit(EXIT_FAILURE);
825 }
826
827 if (odp_term_local()) {
828 ODPH_ERR("Error: term local\n");
829 exit(EXIT_FAILURE);
830 }
831
832 if (odp_term_global(instance)) {
833 ODPH_ERR("Error: term global\n");
834 exit(EXIT_FAILURE);
835 }
836
837 return ret;
838}
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
uint32_t odp_atomic_load_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable.
void odp_atomic_store_u32(odp_atomic_u32_t *atom, uint32_t val)
Store value to atomic uint32 variable.
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_mb_full(void)
Full memory barrier.
void odp_barrier_wait(odp_barrier_t *barr)
Synchronize thread execution on barrier.
#define ODP_ALIGNED_CACHE
Defines type/struct/variable to be cache line size aligned.
#define odp_unlikely(x)
Branch unlikely taken.
Definition spec/hints.h:64
#define ODP_UNUSED
Intentionally unused variables of functions.
Definition spec/hints.h:54
uint64_t odp_cpu_cycles(void)
Current CPU cycle count.
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
int odp_cpumask_first(const odp_cpumask_t *mask)
Find first set CPU in mask.
int32_t odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int32_t size)
Format a string from CPU mask.
#define ODP_CPUMASK_STR_SIZE
The maximum number of characters needed to record any CPU mask as a string (output of odp_cpumask_to_...
void odp_event_free(odp_event_t event)
Free event.
odp_event_type_t odp_event_type(odp_event_t event)
Event type of an event.
#define ODP_EVENT_INVALID
Invalid event.
uint32_t odp_hash_crc32c(const void *data, uint32_t data_len, uint32_t init_val)
Calculate CRC-32C.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
#define ODP_STATIC_ASSERT(cond, msg)
Compile time assertion macro.
int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
Thread local ODP initialization.
int odp_init_global(odp_instance_t *instance, const odp_init_t *params, const odp_platform_init_t *platform_params)
Global ODP initialization.
int odp_term_local(void)
Thread local ODP termination.
int odp_term_global(odp_instance_t instance)
Global ODP termination.
uint64_t odp_instance_t
ODP instance ID.
odp_event_t odp_packet_to_event(odp_packet_t pkt)
Convert packet handle to event.
uint32_t odp_packet_l4_offset(odp_packet_t pkt)
Layer 4 start offset.
void * odp_packet_data(odp_packet_t pkt)
Packet data pointer.
void * odp_packet_offset(odp_packet_t pkt, uint32_t offset, uint32_t *len, odp_packet_seg_t *seg)
Packet offset pointer.
uint32_t odp_packet_len(odp_packet_t pkt)
Packet data length.
odp_packet_t odp_packet_alloc(odp_pool_t pool, uint32_t len)
Allocate a packet from a packet pool.
odp_packet_t odp_packet_from_event(odp_event_t ev)
Get packet handle from event.
int odp_packet_parse(odp_packet_t pkt, uint32_t offset, const odp_packet_parse_param_t *param)
Parse packet.
int odp_packet_copy_from_mem(odp_packet_t pkt, uint32_t offset, uint32_t len, const void *src)
Copy data from memory to packet.
#define ODP_PACKET_INVALID
Invalid packet.
void * odp_packet_user_area(odp_packet_t pkt)
User area address.
void * odp_packet_l4_ptr(odp_packet_t pkt, uint32_t *len)
Layer 4 start pointer.
@ ODP_PROTO_ETH
Ethernet (including VLAN)
@ ODP_PROTO_LAYER_ALL
All layers.
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
int odp_pool_capability(odp_pool_capability_t *capa)
Query pool capabilities.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
void odp_pool_print(odp_pool_t pool)
Print pool info.
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_PACKET
Packet pool.
void odp_queue_param_init(odp_queue_param_t *param)
Initialize queue params.
#define ODP_QUEUE_INVALID
Invalid queue.
int odp_queue_enq(odp_queue_t queue, odp_event_t ev)
Enqueue an event to a queue.
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
Queue create.
int odp_queue_destroy(odp_queue_t queue)
Destroy ODP queue.
@ ODP_QUEUE_TYPE_SCHED
Scheduled queue.
#define ODP_SCHED_SYNC_PARALLEL
Parallel scheduled queues.
int odp_schedule_multi(odp_queue_t *from, uint64_t wait, odp_event_t events[], int num)
Schedule multiple events.
void odp_schedule_config_init(odp_schedule_config_t *config)
Initialize schedule configuration options.
#define ODP_SCHED_NO_WAIT
Do not wait.
int odp_schedule_default_prio(void)
Default scheduling priority level.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
uint64_t odp_schedule_wait_time(uint64_t ns)
Schedule wait time.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
#define ODP_SHM_INVALID
Invalid shared memory block.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
uint32_t odp_una_u32_t
Unaligned uint32_t type.
void odp_sys_info_print(void)
Print system info.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
odp_time_t odp_time_local(void)
Current local time.
#define ODP_TIME_MSEC_IN_NS
A millisecond in nanoseconds.
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
Packet parse parameters.
odp_proto_chksums_t chksums
Flags to control payload data checksums checks up to the selected parse layer.
odp_proto_layer_t last_layer
Continue parsing until this layer.
odp_proto_t proto
Protocol header at parse starting point.
uint32_t max_num
Maximum number of buffers of any size.
uint32_t max_uarea_size
Maximum user area size in bytes.
struct odp_pool_capability_t::@134 pkt
Packet pool capabilities
uint32_t max_seg_len
Maximum packet segment data length in bytes.
uint32_t max_len
Maximum packet data length in bytes.
uint32_t uarea_size
Minimum user area size in bytes.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@139 pkt
Parameters for packet pools.
uint32_t max_len
Maximum packet length that will be allocated from the pool.
uint32_t max_num
Maximum number of packets.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...
ODP Queue parameters.
odp_schedule_param_t sched
Scheduler parameters.
uint32_t size
Queue size.
odp_queue_type_t type
Queue type.
Schedule configuration.
uint32_t num_queues
Maximum number of scheduled queues to be supported.
uint32_t queue_size
Maximum number of events required to be stored simultaneously in scheduled queue.
odp_schedule_group_t group
Thread group.
odp_schedule_prio_t prio
Priority level.
odp_schedule_sync_t sync
Synchronization method.
uint32_t tm
Traffic Manager APIs, e.g., odp_tm_xxx()
uint32_t stash
Stash APIs, e.g., odp_stash_xxx()
uint32_t crypto
Crypto APIs, e.g., odp_crypto_xxx()
uint32_t ipsec
IPsec APIs, e.g., odp_ipsec_xxx()
uint32_t dma
DMA APIs, e.g., odp_dma_xxx()
uint32_t timer
Timer APIs, e.g., odp_timer_xxx(), odp_timeout_xxx()
uint32_t cls
Classifier APIs, e.g., odp_cls_xxx(), odp_cos_xxx()
uint32_t ml
Machine Learning APIs, e.g., odp_ml_xxx()
struct odp_feature_t::@174 feat
Individual feature bits.
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()
uint32_t all_chksum
All checksum bits.