test/event: add asymmetric cases for crypto adapter
[dpdk.git] / app / test / test_hash_readwrite.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Intel Corporation
3  */
4
5 #include <inttypes.h>
6 #include <locale.h>
7
8 #include <rte_cycles.h>
9 #include <rte_hash.h>
10 #include <rte_hash_crc.h>
11 #include <rte_jhash.h>
12 #include <rte_launch.h>
13 #include <rte_malloc.h>
14 #include <rte_random.h>
15 #include <rte_spinlock.h>
16
17 #include "test.h"
18
19 #define RTE_RWTEST_FAIL 0
20
21 #define TOTAL_ENTRY (5*1024*1024)
22 #define TOTAL_INSERT (4.5*1024*1024)
23 #define TOTAL_INSERT_EXT (5*1024*1024)
24
25 #define NUM_TEST 3
26 unsigned int core_cnt[NUM_TEST] = {2, 4, 8};
27
28 unsigned int worker_core_ids[RTE_MAX_LCORE];
29 struct perf {
30         uint32_t single_read;
31         uint32_t single_write;
32         uint32_t read_only[NUM_TEST];
33         uint32_t write_only[NUM_TEST];
34         uint32_t read_write_r[NUM_TEST];
35         uint32_t read_write_w[NUM_TEST];
36 };
37
38 static struct perf htm_results, non_htm_results;
39
40 struct {
41         uint32_t *keys;
42         uint8_t *found;
43         uint32_t num_insert;
44         uint32_t rounded_tot_insert;
45         struct rte_hash *h;
46 } tbl_rw_test_param;
47
48 static uint64_t gcycles;
49 static uint64_t ginsertions;
50
51 static uint64_t gread_cycles;
52 static uint64_t gwrite_cycles;
53
54 static uint64_t greads;
55 static uint64_t gwrites;
56
57 static int
58 test_hash_readwrite_worker(__rte_unused void *arg)
59 {
60         uint64_t i, offset;
61         uint32_t lcore_id = rte_lcore_id();
62         uint64_t begin, cycles;
63         int *ret;
64
65         ret = rte_malloc(NULL, sizeof(int) *
66                                 tbl_rw_test_param.num_insert, 0);
67         for (i = 0; i < rte_lcore_count(); i++) {
68                 if (worker_core_ids[i] == lcore_id)
69                         break;
70         }
71         offset = tbl_rw_test_param.num_insert * i;
72
73         printf("Core #%d inserting and reading %d: %'"PRId64" - %'"PRId64"\n",
74                lcore_id, tbl_rw_test_param.num_insert,
75                offset, offset + tbl_rw_test_param.num_insert - 1);
76
77         begin = rte_rdtsc_precise();
78
79         for (i = offset; i < offset + tbl_rw_test_param.num_insert; i++) {
80
81                 if (rte_hash_lookup(tbl_rw_test_param.h,
82                                 tbl_rw_test_param.keys + i) > 0)
83                         break;
84
85                 ret[i - offset] = rte_hash_add_key(tbl_rw_test_param.h,
86                                      tbl_rw_test_param.keys + i);
87                 if (ret[i - offset] < 0)
88                         break;
89
90                 /* lookup a random key */
91                 uint32_t rand = rte_rand() % (i + 1 - offset);
92
93                 if (rte_hash_lookup(tbl_rw_test_param.h,
94                                 tbl_rw_test_param.keys + rand) != ret[rand])
95                         break;
96
97
98                 if (rte_hash_del_key(tbl_rw_test_param.h,
99                                 tbl_rw_test_param.keys + rand) != ret[rand])
100                         break;
101
102                 ret[rand] = rte_hash_add_key(tbl_rw_test_param.h,
103                                         tbl_rw_test_param.keys + rand);
104                 if (ret[rand] < 0)
105                         break;
106
107                 if (rte_hash_lookup(tbl_rw_test_param.h,
108                         tbl_rw_test_param.keys + rand) != ret[rand])
109                         break;
110         }
111
112         cycles = rte_rdtsc_precise() - begin;
113         __atomic_fetch_add(&gcycles, cycles, __ATOMIC_RELAXED);
114         __atomic_fetch_add(&ginsertions, i - offset, __ATOMIC_RELAXED);
115
116         for (; i < offset + tbl_rw_test_param.num_insert; i++)
117                 tbl_rw_test_param.keys[i] = RTE_RWTEST_FAIL;
118
119         rte_free(ret);
120         return 0;
121 }
122
123 static int
124 init_params(int use_ext, int use_htm, int rw_lf, int use_jhash)
125 {
126         unsigned int i;
127
128         uint32_t *keys = NULL;
129         uint8_t *found = NULL;
130         struct rte_hash *handle;
131
132         struct rte_hash_parameters hash_params = {
133                 .entries = TOTAL_ENTRY,
134                 .key_len = sizeof(uint32_t),
135                 .hash_func_init_val = 0,
136                 .socket_id = rte_socket_id(),
137         };
138         if (use_jhash)
139                 hash_params.hash_func = rte_jhash;
140         else
141                 hash_params.hash_func = rte_hash_crc;
142
143         hash_params.extra_flag = RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD;
144         if (use_htm)
145                 hash_params.extra_flag |=
146                         RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT;
147         if (rw_lf)
148                 hash_params.extra_flag |=
149                         RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF;
150         else
151                 hash_params.extra_flag |=
152                         RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY;
153
154         if (use_ext)
155                 hash_params.extra_flag |=
156                         RTE_HASH_EXTRA_FLAGS_EXT_TABLE;
157         else
158                 hash_params.extra_flag &=
159                        ~RTE_HASH_EXTRA_FLAGS_EXT_TABLE;
160
161         hash_params.name = "tests";
162
163         handle = rte_hash_create(&hash_params);
164         if (handle == NULL) {
165                 printf("hash creation failed");
166                 return -1;
167         }
168
169         tbl_rw_test_param.h = handle;
170         keys = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_ENTRY, 0);
171
172         if (keys == NULL) {
173                 printf("RTE_MALLOC failed\n");
174                 goto err;
175         }
176
177         found = rte_zmalloc(NULL, sizeof(uint8_t) * TOTAL_ENTRY, 0);
178         if (found == NULL) {
179                 printf("RTE_ZMALLOC failed\n");
180                 goto err;
181         }
182
183         tbl_rw_test_param.keys = keys;
184         tbl_rw_test_param.found = found;
185
186         for (i = 0; i < TOTAL_ENTRY; i++)
187                 keys[i] = i;
188
189         return 0;
190
191 err:
192         rte_free(keys);
193         rte_hash_free(handle);
194
195         return -1;
196 }
197
198 static int
199 test_hash_readwrite_functional(int use_htm, int use_rw_lf, int use_ext)
200 {
201         unsigned int i;
202         const void *next_key;
203         void *next_data;
204         uint32_t iter = 0;
205
206         uint32_t duplicated_keys = 0;
207         uint32_t lost_keys = 0;
208         int use_jhash = 1;
209         int worker_cnt = rte_lcore_count() - 1;
210         uint32_t tot_insert = 0;
211
212         __atomic_store_n(&gcycles, 0, __ATOMIC_RELAXED);
213         __atomic_store_n(&ginsertions, 0, __ATOMIC_RELAXED);
214
215         if (init_params(use_ext, use_htm, use_rw_lf, use_jhash) != 0)
216                 goto err;
217
218         if (use_ext)
219                 tot_insert = TOTAL_INSERT_EXT;
220         else
221                 tot_insert = TOTAL_INSERT;
222
223         tbl_rw_test_param.num_insert =
224                 tot_insert / worker_cnt;
225
226         tbl_rw_test_param.rounded_tot_insert =
227                 tbl_rw_test_param.num_insert * worker_cnt;
228
229         printf("\nHTM = %d, RW-LF = %d, EXT-Table = %d\n",
230                 use_htm, use_rw_lf, use_ext);
231         printf("++++++++Start function tests:+++++++++\n");
232
233         /* Fire all threads. */
234         rte_eal_mp_remote_launch(test_hash_readwrite_worker,
235                                  NULL, SKIP_MAIN);
236         rte_eal_mp_wait_lcore();
237
238         while (rte_hash_iterate(tbl_rw_test_param.h, &next_key,
239                         &next_data, &iter) >= 0) {
240                 /* Search for the key in the list of keys added .*/
241                 i = *(const uint32_t *)next_key;
242                 tbl_rw_test_param.found[i]++;
243         }
244
245         for (i = 0; i < tbl_rw_test_param.rounded_tot_insert; i++) {
246                 if (tbl_rw_test_param.keys[i] != RTE_RWTEST_FAIL) {
247                         if (tbl_rw_test_param.found[i] > 1) {
248                                 duplicated_keys++;
249                                 break;
250                         }
251                         if (tbl_rw_test_param.found[i] == 0) {
252                                 lost_keys++;
253                                 printf("key %d is lost\n", i);
254                                 break;
255                         }
256                 }
257         }
258
259         if (duplicated_keys > 0) {
260                 printf("%d key duplicated\n", duplicated_keys);
261                 goto err_free;
262         }
263
264         if (lost_keys > 0) {
265                 printf("%d key lost\n", lost_keys);
266                 goto err_free;
267         }
268
269         printf("No key corrupted during read-write test.\n");
270
271         unsigned long long int cycles_per_insertion =
272                 __atomic_load_n(&gcycles, __ATOMIC_RELAXED) /
273                 __atomic_load_n(&ginsertions, __ATOMIC_RELAXED);
274
275         printf("cycles per insertion and lookup: %llu\n", cycles_per_insertion);
276
277         rte_free(tbl_rw_test_param.found);
278         rte_free(tbl_rw_test_param.keys);
279         rte_hash_free(tbl_rw_test_param.h);
280         printf("+++++++++Complete function tests+++++++++\n");
281         return 0;
282
283 err_free:
284         rte_free(tbl_rw_test_param.found);
285         rte_free(tbl_rw_test_param.keys);
286         rte_hash_free(tbl_rw_test_param.h);
287 err:
288         return -1;
289 }
290
291 static int
292 test_rw_reader(void *arg)
293 {
294         uint64_t i;
295         uint64_t begin, cycles;
296         uint64_t read_cnt = (uint64_t)((uintptr_t)arg);
297
298         begin = rte_rdtsc_precise();
299         for (i = 0; i < read_cnt; i++) {
300                 void *data = arg;
301                 rte_hash_lookup_data(tbl_rw_test_param.h,
302                                 tbl_rw_test_param.keys + i,
303                                 &data);
304                 if (i != (uint64_t)(uintptr_t)data) {
305                         printf("lookup find wrong value %"PRIu64","
306                                 "%"PRIu64"\n", i,
307                                 (uint64_t)(uintptr_t)data);
308                         break;
309                 }
310         }
311
312         cycles = rte_rdtsc_precise() - begin;
313         __atomic_fetch_add(&gread_cycles, cycles, __ATOMIC_RELAXED);
314         __atomic_fetch_add(&greads, i, __ATOMIC_RELAXED);
315         return 0;
316 }
317
318 static int
319 test_rw_writer(void *arg)
320 {
321         uint64_t i;
322         uint32_t lcore_id = rte_lcore_id();
323         uint64_t begin, cycles;
324         int ret;
325         uint64_t start_coreid = (uint64_t)(uintptr_t)arg;
326         uint64_t offset;
327
328         for (i = 0; i < rte_lcore_count(); i++) {
329                 if (worker_core_ids[i] == lcore_id)
330                         break;
331         }
332
333         offset = TOTAL_INSERT / 2 + (i - (start_coreid)) *
334                                 tbl_rw_test_param.num_insert;
335         begin = rte_rdtsc_precise();
336         for (i = offset; i < offset + tbl_rw_test_param.num_insert; i++) {
337                 ret = rte_hash_add_key_data(tbl_rw_test_param.h,
338                                 tbl_rw_test_param.keys + i,
339                                 (void *)((uintptr_t)i));
340                 if (ret < 0) {
341                         printf("writer failed %"PRIu64"\n", i);
342                         break;
343                 }
344         }
345
346         cycles = rte_rdtsc_precise() - begin;
347         __atomic_fetch_add(&gwrite_cycles, cycles, __ATOMIC_RELAXED);
348         __atomic_fetch_add(&gwrites, tbl_rw_test_param.num_insert,
349                                                         __ATOMIC_RELAXED);
350         return 0;
351 }
352
353 static int
354 test_hash_readwrite_perf(struct perf *perf_results, int use_htm,
355                                                         int reader_faster)
356 {
357         unsigned int n;
358         int ret;
359         int start_coreid;
360         uint64_t i, read_cnt;
361
362         const void *next_key;
363         void *next_data;
364         uint32_t iter;
365         int use_jhash = 0;
366
367         uint32_t duplicated_keys = 0;
368         uint32_t lost_keys = 0;
369
370         uint64_t start = 0, end = 0;
371
372         __atomic_store_n(&gwrites, 0, __ATOMIC_RELAXED);
373         __atomic_store_n(&greads, 0, __ATOMIC_RELAXED);
374
375         __atomic_store_n(&gread_cycles, 0, __ATOMIC_RELAXED);
376         __atomic_store_n(&gwrite_cycles, 0, __ATOMIC_RELAXED);
377
378         if (init_params(0, use_htm, 0, use_jhash) != 0)
379                 goto err;
380
381         /*
382          * Do a readers finish faster or writers finish faster test.
383          * When readers finish faster, we timing the readers, and when writers
384          * finish faster, we timing the writers.
385          * Divided by 10 or 2 is just experimental values to vary the workload
386          * of readers.
387          */
388         if (reader_faster) {
389                 printf("++++++Start perf test: reader++++++++\n");
390                 read_cnt = TOTAL_INSERT / 10;
391         } else {
392                 printf("++++++Start perf test: writer++++++++\n");
393                 read_cnt = TOTAL_INSERT / 2;
394         }
395
396         /* We first test single thread performance */
397         start = rte_rdtsc_precise();
398         /* Insert half of the keys */
399         for (i = 0; i < TOTAL_INSERT / 2; i++) {
400                 ret = rte_hash_add_key_data(tbl_rw_test_param.h,
401                                      tbl_rw_test_param.keys + i,
402                                         (void *)((uintptr_t)i));
403                 if (ret < 0) {
404                         printf("Failed to insert half of keys\n");
405                         goto err_free;
406                 }
407         }
408         end = rte_rdtsc_precise() - start;
409         perf_results->single_write = end / i;
410
411         start = rte_rdtsc_precise();
412
413         for (i = 0; i < read_cnt; i++) {
414                 void *data;
415                 rte_hash_lookup_data(tbl_rw_test_param.h,
416                                 tbl_rw_test_param.keys + i,
417                                 &data);
418                 if (i != (uint64_t)(uintptr_t)data) {
419                         printf("lookup find wrong value"
420                                         " %"PRIu64",%"PRIu64"\n", i,
421                                         (uint64_t)(uintptr_t)data);
422                         break;
423                 }
424         }
425         end = rte_rdtsc_precise() - start;
426         perf_results->single_read = end / i;
427
428         for (n = 0; n < NUM_TEST; n++) {
429                 unsigned int tot_worker_lcore = rte_lcore_count() - 1;
430                 if (tot_worker_lcore < core_cnt[n] * 2)
431                         goto finish;
432
433                 __atomic_store_n(&greads, 0, __ATOMIC_RELAXED);
434                 __atomic_store_n(&gread_cycles, 0, __ATOMIC_RELAXED);
435                 __atomic_store_n(&gwrites, 0, __ATOMIC_RELAXED);
436                 __atomic_store_n(&gwrite_cycles, 0, __ATOMIC_RELAXED);
437
438                 rte_hash_reset(tbl_rw_test_param.h);
439
440                 tbl_rw_test_param.num_insert = TOTAL_INSERT / 2 / core_cnt[n];
441                 tbl_rw_test_param.rounded_tot_insert = TOTAL_INSERT / 2 +
442                                                 tbl_rw_test_param.num_insert *
443                                                 core_cnt[n];
444
445                 for (i = 0; i < TOTAL_INSERT / 2; i++) {
446                         ret = rte_hash_add_key_data(tbl_rw_test_param.h,
447                                         tbl_rw_test_param.keys + i,
448                                         (void *)((uintptr_t)i));
449                         if (ret < 0) {
450                                 printf("Failed to insert half of keys\n");
451                                 goto err_free;
452                         }
453                 }
454
455                 /* Then test multiple thread case but only all reads or
456                  * all writes
457                  */
458
459                 /* Test only reader cases */
460                 for (i = 0; i < core_cnt[n]; i++)
461                         rte_eal_remote_launch(test_rw_reader,
462                                         (void *)(uintptr_t)read_cnt,
463                                         worker_core_ids[i]);
464
465                 rte_eal_mp_wait_lcore();
466
467                 start_coreid = i;
468                 /* Test only writer cases */
469                 for (; i < core_cnt[n] * 2; i++)
470                         rte_eal_remote_launch(test_rw_writer,
471                                         (void *)((uintptr_t)start_coreid),
472                                         worker_core_ids[i]);
473
474                 rte_eal_mp_wait_lcore();
475
476                 if (reader_faster) {
477                         unsigned long long int cycles_per_insertion =
478                                 __atomic_load_n(&gread_cycles, __ATOMIC_RELAXED) /
479                                 __atomic_load_n(&greads, __ATOMIC_RELAXED);
480                         perf_results->read_only[n] = cycles_per_insertion;
481                         printf("Reader only: cycles per lookup: %llu\n",
482                                                         cycles_per_insertion);
483                 }
484
485                 else {
486                         unsigned long long int cycles_per_insertion =
487                                 __atomic_load_n(&gwrite_cycles, __ATOMIC_RELAXED) /
488                                 __atomic_load_n(&gwrites, __ATOMIC_RELAXED);
489                         perf_results->write_only[n] = cycles_per_insertion;
490                         printf("Writer only: cycles per writes: %llu\n",
491                                                         cycles_per_insertion);
492                 }
493
494                 __atomic_store_n(&greads, 0, __ATOMIC_RELAXED);
495                 __atomic_store_n(&gread_cycles, 0, __ATOMIC_RELAXED);
496                 __atomic_store_n(&gwrites, 0, __ATOMIC_RELAXED);
497                 __atomic_store_n(&gwrite_cycles, 0, __ATOMIC_RELAXED);
498
499                 rte_hash_reset(tbl_rw_test_param.h);
500
501                 for (i = 0; i < TOTAL_INSERT / 2; i++) {
502                         ret = rte_hash_add_key_data(tbl_rw_test_param.h,
503                                         tbl_rw_test_param.keys + i,
504                                         (void *)((uintptr_t)i));
505                         if (ret < 0) {
506                                 printf("Failed to insert half of keys\n");
507                                 goto err_free;
508                         }
509                 }
510
511                 start_coreid = core_cnt[n];
512
513                 if (reader_faster) {
514                         for (i = core_cnt[n]; i < core_cnt[n] * 2; i++)
515                                 rte_eal_remote_launch(test_rw_writer,
516                                         (void *)((uintptr_t)start_coreid),
517                                         worker_core_ids[i]);
518                         for (i = 0; i < core_cnt[n]; i++)
519                                 rte_eal_remote_launch(test_rw_reader,
520                                         (void *)(uintptr_t)read_cnt,
521                                         worker_core_ids[i]);
522                 } else {
523                         for (i = 0; i < core_cnt[n]; i++)
524                                 rte_eal_remote_launch(test_rw_reader,
525                                         (void *)(uintptr_t)read_cnt,
526                                         worker_core_ids[i]);
527                         for (; i < core_cnt[n] * 2; i++)
528                                 rte_eal_remote_launch(test_rw_writer,
529                                         (void *)((uintptr_t)start_coreid),
530                                         worker_core_ids[i]);
531                 }
532
533                 rte_eal_mp_wait_lcore();
534
535                 iter = 0;
536                 memset(tbl_rw_test_param.found, 0, TOTAL_ENTRY);
537                 while (rte_hash_iterate(tbl_rw_test_param.h,
538                                 &next_key, &next_data, &iter) >= 0) {
539                         /* Search for the key in the list of keys added .*/
540                         i = *(const uint32_t *)next_key;
541                         tbl_rw_test_param.found[i]++;
542                 }
543
544                 for (i = 0; i < tbl_rw_test_param.rounded_tot_insert; i++) {
545                         if (tbl_rw_test_param.keys[i] != RTE_RWTEST_FAIL) {
546                                 if (tbl_rw_test_param.found[i] > 1) {
547                                         duplicated_keys++;
548                                         break;
549                                 }
550                                 if (tbl_rw_test_param.found[i] == 0) {
551                                         lost_keys++;
552                                         printf("key %"PRIu64" is lost\n", i);
553                                         break;
554                                 }
555                         }
556                 }
557
558                 if (duplicated_keys > 0) {
559                         printf("%d key duplicated\n", duplicated_keys);
560                         goto err_free;
561                 }
562
563                 if (lost_keys > 0) {
564                         printf("%d key lost\n", lost_keys);
565                         goto err_free;
566                 }
567
568                 printf("No key corrupted during read-write test.\n");
569
570                 if (reader_faster) {
571                         unsigned long long int cycles_per_insertion =
572                                 __atomic_load_n(&gread_cycles, __ATOMIC_RELAXED) /
573                                 __atomic_load_n(&greads, __ATOMIC_RELAXED);
574                         perf_results->read_write_r[n] = cycles_per_insertion;
575                         printf("Read-write cycles per lookup: %llu\n",
576                                                         cycles_per_insertion);
577                 }
578
579                 else {
580                         unsigned long long int cycles_per_insertion =
581                                 __atomic_load_n(&gwrite_cycles, __ATOMIC_RELAXED) /
582                                 __atomic_load_n(&gwrites, __ATOMIC_RELAXED);
583                         perf_results->read_write_w[n] = cycles_per_insertion;
584                         printf("Read-write cycles per writes: %llu\n",
585                                                         cycles_per_insertion);
586                 }
587         }
588
589 finish:
590         rte_free(tbl_rw_test_param.found);
591         rte_free(tbl_rw_test_param.keys);
592         rte_hash_free(tbl_rw_test_param.h);
593         return 0;
594
595 err_free:
596         rte_free(tbl_rw_test_param.found);
597         rte_free(tbl_rw_test_param.keys);
598         rte_hash_free(tbl_rw_test_param.h);
599
600 err:
601         return -1;
602 }
603
604 static int
605 test_hash_rw_perf_main(void)
606 {
607         /*
608          * Variables used to choose different tests.
609          * use_htm indicates if hardware transactional memory should be used.
610          * reader_faster indicates if the reader threads should finish earlier
611          * than writer threads. This is to timing either reader threads or
612          * writer threads for performance numbers.
613          */
614         int use_htm, reader_faster;
615         unsigned int i = 0, core_id = 0;
616
617         if (rte_lcore_count() < 3) {
618                 printf("Not enough cores for hash_readwrite_autotest, expecting at least 3\n");
619                 return TEST_SKIPPED;
620         }
621
622         RTE_LCORE_FOREACH_WORKER(core_id) {
623                 worker_core_ids[i] = core_id;
624                 i++;
625         }
626
627         setlocale(LC_NUMERIC, "");
628
629         if (rte_tm_supported()) {
630                 printf("Hardware transactional memory (lock elision) "
631                         "is supported\n");
632
633                 printf("Test read-write with Hardware transactional memory\n");
634
635                 use_htm = 1;
636
637                 reader_faster = 1;
638                 if (test_hash_readwrite_perf(&htm_results, use_htm,
639                                                         reader_faster) < 0)
640                         return -1;
641
642                 reader_faster = 0;
643                 if (test_hash_readwrite_perf(&htm_results, use_htm,
644                                                         reader_faster) < 0)
645                         return -1;
646         } else {
647                 printf("Hardware transactional memory (lock elision) "
648                         "is NOT supported\n");
649         }
650
651         printf("Test read-write without Hardware transactional memory\n");
652         use_htm = 0;
653
654         reader_faster = 1;
655         if (test_hash_readwrite_perf(&non_htm_results, use_htm,
656                                                         reader_faster) < 0)
657                 return -1;
658         reader_faster = 0;
659         if (test_hash_readwrite_perf(&non_htm_results, use_htm,
660                                                         reader_faster) < 0)
661                 return -1;
662
663         printf("================\n");
664         printf("Results summary:\n");
665         printf("================\n");
666
667         printf("HTM:\n");
668         printf("  single read: %u\n", htm_results.single_read);
669         printf("  single write: %u\n", htm_results.single_write);
670         printf("non HTM:\n");
671         printf("  single read: %u\n", non_htm_results.single_read);
672         printf("  single write: %u\n", non_htm_results.single_write);
673         for (i = 0; i < NUM_TEST; i++) {
674                 printf("+++ core_cnt: %u +++\n", core_cnt[i]);
675                 printf("HTM:\n");
676                 printf("  read only: %u\n", htm_results.read_only[i]);
677                 printf("  write only: %u\n", htm_results.write_only[i]);
678                 printf("  read-write read: %u\n", htm_results.read_write_r[i]);
679                 printf("  read-write write: %u\n", htm_results.read_write_w[i]);
680
681                 printf("non HTM:\n");
682                 printf("  read only: %u\n", non_htm_results.read_only[i]);
683                 printf("  write only: %u\n", non_htm_results.write_only[i]);
684                 printf("  read-write read: %u\n",
685                         non_htm_results.read_write_r[i]);
686                 printf("  read-write write: %u\n",
687                         non_htm_results.read_write_w[i]);
688         }
689
690         return 0;
691 }
692
693 static int
694 test_hash_rw_func_main(void)
695 {
696         /*
697          * Variables used to choose different tests.
698          * use_htm indicates if hardware transactional memory should be used.
699          * reader_faster indicates if the reader threads should finish earlier
700          * than writer threads. This is to timing either reader threads or
701          * writer threads for performance numbers.
702          */
703         unsigned int i = 0, core_id = 0;
704
705         if (rte_lcore_count() < 3) {
706                 printf("Not enough cores for hash_readwrite_autotest, expecting at least 3\n");
707                 return TEST_SKIPPED;
708         }
709
710         RTE_LCORE_FOREACH_WORKER(core_id) {
711                 worker_core_ids[i] = core_id;
712                 i++;
713         }
714
715         setlocale(LC_NUMERIC, "");
716
717         if (rte_tm_supported()) {
718                 printf("Hardware transactional memory (lock elision) "
719                         "is supported\n");
720
721                 printf("Test read-write with Hardware transactional memory\n");
722
723                 /* htm = 1, rw_lf = 0, ext = 0 */
724                 if (test_hash_readwrite_functional(1, 0, 0) < 0)
725                         return -1;
726
727                 /* htm = 1, rw_lf = 1, ext = 0 */
728                 if (test_hash_readwrite_functional(1, 1, 0) < 0)
729                         return -1;
730
731                 /* htm = 1, rw_lf = 0, ext = 1 */
732                 if (test_hash_readwrite_functional(1, 0, 1) < 0)
733                         return -1;
734
735                 /* htm = 1, rw_lf = 1, ext = 1 */
736                 if (test_hash_readwrite_functional(1, 1, 1) < 0)
737                         return -1;
738         } else {
739                 printf("Hardware transactional memory (lock elision) "
740                         "is NOT supported\n");
741         }
742
743         printf("Test read-write without Hardware transactional memory\n");
744         /* htm = 0, rw_lf = 0, ext = 0 */
745         if (test_hash_readwrite_functional(0, 0, 0) < 0)
746                 return -1;
747
748         /* htm = 0, rw_lf = 1, ext = 0 */
749         if (test_hash_readwrite_functional(0, 1, 0) < 0)
750                 return -1;
751
752         /* htm = 0, rw_lf = 0, ext = 1 */
753         if (test_hash_readwrite_functional(0, 0, 1) < 0)
754                 return -1;
755
756         /* htm = 0, rw_lf = 1, ext = 1 */
757         if (test_hash_readwrite_functional(0, 1, 1) < 0)
758                 return -1;
759
760         return 0;
761 }
762
763 REGISTER_TEST_COMMAND(hash_readwrite_func_autotest, test_hash_rw_func_main);
764 REGISTER_TEST_COMMAND(hash_readwrite_perf_autotest, test_hash_rw_perf_main);