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